providing image gallery via DI graph
This commit is contained in:
parent
bd885823bd
commit
415ea4b150
|
@ -15,6 +15,7 @@ import app.dapk.st.graph.AppModule
|
|||
import app.dapk.st.home.HomeModule
|
||||
import app.dapk.st.login.LoginModule
|
||||
import app.dapk.st.messenger.MessengerModule
|
||||
import app.dapk.st.messenger.gallery.ImageGalleryModule
|
||||
import app.dapk.st.notifications.NotificationsModule
|
||||
import app.dapk.st.profile.ProfileModule
|
||||
import app.dapk.st.push.PushModule
|
||||
|
@ -81,6 +82,7 @@ class SmallTalkApplication : Application(), ModuleProvider {
|
|||
TaskRunnerModule::class -> appModule.domainModules.taskRunnerModule
|
||||
CoreAndroidModule::class -> appModule.coreAndroidModule
|
||||
ShareEntryModule::class -> featureModules.shareEntryModule
|
||||
ImageGalleryModule::class -> featureModules.imageGalleryModule
|
||||
else -> throw IllegalArgumentException("Unknown: $klass")
|
||||
} as T
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import app.dapk.st.matrix.sync.internal.request.ApiToDeviceEvent
|
|||
import app.dapk.st.matrix.sync.internal.room.MessageDecrypter
|
||||
import app.dapk.st.messenger.MessengerActivity
|
||||
import app.dapk.st.messenger.MessengerModule
|
||||
import app.dapk.st.messenger.gallery.ImageGalleryModule
|
||||
import app.dapk.st.navigator.IntentFactory
|
||||
import app.dapk.st.navigator.MessageAttachment
|
||||
import app.dapk.st.notifications.MatrixPushHandler
|
||||
|
@ -217,6 +218,10 @@ internal class FeatureModules internal constructor(
|
|||
ShareEntryModule(matrixModules.sync, matrixModules.room)
|
||||
}
|
||||
|
||||
val imageGalleryModule by unsafeLazy {
|
||||
ImageGalleryModule(context.contentResolver, coroutineDispatchers)
|
||||
}
|
||||
|
||||
val pushModule by unsafeLazy {
|
||||
domainModules.pushModule
|
||||
}
|
||||
|
|
|
@ -3,21 +3,17 @@ package app.dapk.st.messenger.gallery
|
|||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.provider.MediaStore.Images
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
// https://github.com/signalapp/Signal-Android/blob/e22ddb8f96f8801f0abe622b5261abc6cb396d94/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaRepository.java
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.withIoContext
|
||||
|
||||
class FetchMediaFoldersUseCase(
|
||||
private val contentResolver: ContentResolver,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) {
|
||||
|
||||
suspend fun fetchFolders(): List<Folder> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
return dispatchers.withIoContext {
|
||||
val projection = arrayOf(Images.Media._ID, Images.Media.BUCKET_ID, Images.Media.BUCKET_DISPLAY_NAME, Images.Media.DATE_MODIFIED)
|
||||
val selection = "${isNotPending()} AND ${Images.Media.BUCKET_ID} AND ${Images.Media.MIME_TYPE} NOT LIKE ?"
|
||||
val sortBy = "${Images.Media.BUCKET_DISPLAY_NAME} COLLATE NOCASE ASC, ${Images.Media.DATE_MODIFIED} DESC"
|
||||
|
@ -39,7 +35,6 @@ class FetchMediaFoldersUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
data class Folder(
|
||||
|
@ -56,59 +51,3 @@ data class Folder(
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class FetchMediaUseCase(private val contentResolver: ContentResolver) {
|
||||
|
||||
private val projection = arrayOf(
|
||||
Images.Media._ID,
|
||||
Images.Media.MIME_TYPE,
|
||||
Images.Media.DATE_MODIFIED,
|
||||
Images.Media.ORIENTATION,
|
||||
Images.Media.WIDTH,
|
||||
Images.Media.HEIGHT,
|
||||
Images.Media.SIZE
|
||||
)
|
||||
|
||||
suspend fun getMediaInBucket(bucketId: String): List<Media> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
|
||||
val media = mutableListOf<Media>()
|
||||
val selection = Images.Media.BUCKET_ID + " = ? AND " + isNotPending() + " AND " + Images.Media.MIME_TYPE + " NOT LIKE ?"
|
||||
val selectionArgs = arrayOf(bucketId, "%image/svg%")
|
||||
val sortBy = Images.Media.DATE_MODIFIED + " DESC"
|
||||
val contentUri = Images.Media.EXTERNAL_CONTENT_URI
|
||||
contentResolver.query(contentUri, projection, selection, selectionArgs, sortBy).use { cursor ->
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
val rowId = cursor.getLong(cursor.getColumnIndexOrThrow(projection[0]))
|
||||
val uri = ContentUris.withAppendedId(contentUri, rowId)
|
||||
val mimetype = cursor.getString(cursor.getColumnIndexOrThrow(Images.Media.MIME_TYPE))
|
||||
val date = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.DATE_MODIFIED))
|
||||
val orientation = cursor.getInt(cursor.getColumnIndexOrThrow(Images.Media.ORIENTATION))
|
||||
val width = cursor.getInt(cursor.getColumnIndexOrThrow(getWidthColumn(orientation)))
|
||||
val height = cursor.getInt(cursor.getColumnIndexOrThrow(getHeightColumn(orientation)))
|
||||
val size = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE))
|
||||
media.add(Media(rowId, uri, mimetype, width, height, size, date))
|
||||
}
|
||||
}
|
||||
media
|
||||
}
|
||||
}
|
||||
|
||||
private fun getWidthColumn(orientation: Int) = if (orientation == 0 || orientation == 180) Images.Media.WIDTH else Images.Media.HEIGHT
|
||||
|
||||
private fun getHeightColumn(orientation: Int) = if (orientation == 0 || orientation == 180) Images.Media.HEIGHT else Images.Media.WIDTH
|
||||
|
||||
}
|
||||
|
||||
data class Media(
|
||||
val id: Long,
|
||||
val uri: Uri,
|
||||
val mimeType: String,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val size: Long,
|
||||
val dateModifiedEpochMillis: Long,
|
||||
)
|
||||
|
||||
private fun isNotPending() = if (Build.VERSION.SDK_INT <= 28) Images.Media.DATA + " NOT NULL" else MediaStore.MediaColumns.IS_PENDING + " != 1"
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package app.dapk.st.messenger.gallery
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.withIoContext
|
||||
|
||||
class FetchMediaUseCase(private val contentResolver: ContentResolver, private val dispatchers: CoroutineDispatchers) {
|
||||
|
||||
private val projection = arrayOf(
|
||||
MediaStore.Images.Media._ID,
|
||||
MediaStore.Images.Media.MIME_TYPE,
|
||||
MediaStore.Images.Media.DATE_MODIFIED,
|
||||
MediaStore.Images.Media.ORIENTATION,
|
||||
MediaStore.Images.Media.WIDTH,
|
||||
MediaStore.Images.Media.HEIGHT,
|
||||
MediaStore.Images.Media.SIZE
|
||||
)
|
||||
|
||||
private val selection = MediaStore.Images.Media.BUCKET_ID + " = ? AND " + isNotPending() + " AND " + MediaStore.Images.Media.MIME_TYPE + " NOT LIKE ?"
|
||||
|
||||
suspend fun getMediaInBucket(bucketId: String): List<Media> {
|
||||
|
||||
return dispatchers.withIoContext {
|
||||
val media = mutableListOf<Media>()
|
||||
val selectionArgs = arrayOf(bucketId, "%image/svg%")
|
||||
val sortBy = MediaStore.Images.Media.DATE_MODIFIED + " DESC"
|
||||
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
contentResolver.query(contentUri, projection, selection, selectionArgs, sortBy).use { cursor ->
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
val rowId = cursor.getLong(cursor.getColumnIndexOrThrow(projection[0]))
|
||||
val uri = ContentUris.withAppendedId(contentUri, rowId)
|
||||
val mimetype = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.MIME_TYPE))
|
||||
val date = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED))
|
||||
val orientation = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.ORIENTATION))
|
||||
val width = cursor.getInt(cursor.getColumnIndexOrThrow(getWidthColumn(orientation)))
|
||||
val height = cursor.getInt(cursor.getColumnIndexOrThrow(getHeightColumn(orientation)))
|
||||
val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))
|
||||
media.add(Media(rowId, uri, mimetype, width, height, size, date))
|
||||
}
|
||||
}
|
||||
media
|
||||
}
|
||||
}
|
||||
|
||||
private fun getWidthColumn(orientation: Int) = if (orientation == 0 || orientation == 180) MediaStore.Images.Media.WIDTH else MediaStore.Images.Media.HEIGHT
|
||||
|
||||
private fun getHeightColumn(orientation: Int) =
|
||||
if (orientation == 0 || orientation == 180) MediaStore.Images.Media.HEIGHT else MediaStore.Images.Media.WIDTH
|
||||
|
||||
}
|
||||
|
||||
data class Media(
|
||||
val id: Long,
|
||||
val uri: Uri,
|
||||
val mimeType: String,
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val size: Long,
|
||||
val dateModifiedEpochMillis: Long,
|
||||
)
|
||||
|
|
@ -12,21 +12,18 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import app.dapk.st.core.DapkActivity
|
||||
import app.dapk.st.core.Lce
|
||||
import app.dapk.st.core.PermissionResult
|
||||
import app.dapk.st.core.*
|
||||
import app.dapk.st.core.extensions.unsafeLazy
|
||||
import app.dapk.st.messenger.MessengerModule
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ImageGalleryActivity : DapkActivity() {
|
||||
|
||||
private val module by unsafeLazy { module<ImageGalleryModule>() }
|
||||
private val viewModel by viewModel { module.imageGalleryViewModel() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val viewModel = ImageGalleryViewModel(
|
||||
FetchMediaFoldersUseCase(contentResolver),
|
||||
FetchMediaUseCase(contentResolver),
|
||||
)
|
||||
|
||||
val permissionState = mutableStateOf<Lce<PermissionResult>>(Lce.Loading())
|
||||
|
||||
lifecycleScope.launch {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package app.dapk.st.messenger.gallery
|
||||
|
||||
import android.content.ContentResolver
|
||||
import app.dapk.st.core.CoroutineDispatchers
|
||||
import app.dapk.st.core.ProvidableModule
|
||||
|
||||
class ImageGalleryModule(
|
||||
private val contentResolver: ContentResolver,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) : ProvidableModule {
|
||||
|
||||
fun imageGalleryViewModel() = ImageGalleryViewModel(
|
||||
FetchMediaFoldersUseCase(contentResolver, dispatchers),
|
||||
FetchMediaUseCase(contentResolver, dispatchers),
|
||||
)
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package app.dapk.st.messenger.gallery
|
||||
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
|
||||
fun isNotPending() = if (Build.VERSION.SDK_INT <= 28) MediaStore.Images.Media.DATA + " NOT NULL" else MediaStore.MediaColumns.IS_PENDING + " != 1"
|
Loading…
Reference in New Issue