From e611cbc86f57f0817570a3af20b8474789401649 Mon Sep 17 00:00:00 2001 From: Rohit Verma Date: Fri, 3 Jan 2025 23:10:04 +0530 Subject: [PATCH] update UI states and refactor code Also, add repository as Single Source of Truth for performing operations on images --- .../data/ImageRepositoryImpl.kt | 29 ++++++ .../customselector/domain/ImageRepository.kt | 15 ++++ .../domain/use_case/ImageUseCase.kt | 89 +++++++++++++++++++ .../ui/screens/CustomSelectorScreen.kt | 8 +- .../ui/screens/CustomSelectorState.kt | 13 --- .../ui/screens/CustomSelectorViewModel.kt | 49 +++++++--- .../customselector/ui/screens/Folder.kt | 2 + .../customselector/ui/screens/ImagesPane.kt | 12 +-- .../ui/screens/ViewImageScreen.kt | 4 +- .../ui/selector/CustomSelectorActivity.kt | 19 ++-- .../ui/states/CustomSelectorUiState.kt | 17 ++++ .../customselector/ui/states/ImageUiState.kt | 20 +++++ .../utils/CustomSelectorViewModelFactory.kt | 21 +++++ .../commons/di/CommonsApplicationModule.java | 13 +++ 14 files changed, 262 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/data/ImageRepositoryImpl.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/domain/ImageRepository.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/domain/use_case/ImageUseCase.kt delete mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorState.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/states/CustomSelectorUiState.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/states/ImageUiState.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/utils/CustomSelectorViewModelFactory.kt diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/data/ImageRepositoryImpl.kt b/app/src/main/java/fr/free/nrw/commons/customselector/data/ImageRepositoryImpl.kt new file mode 100644 index 000000000..6f64293e0 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/data/ImageRepositoryImpl.kt @@ -0,0 +1,29 @@ +package fr.free.nrw.commons.customselector.data + +import fr.free.nrw.commons.customselector.database.NotForUploadStatus +import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao +import fr.free.nrw.commons.customselector.domain.ImageRepository +import fr.free.nrw.commons.customselector.domain.model.Image +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class ImageRepositoryImpl @Inject constructor( + private val mediaReader: MediaReader, + private val notForUploadStatusDao: NotForUploadStatusDao +): ImageRepository { + override suspend fun getImagesFromDevice(): Flow { + return mediaReader.getImages() + } + + override suspend fun markAsNotForUpload(imageSHA: String) { + notForUploadStatusDao.insert(NotForUploadStatus(imageSHA)) + } + + override suspend fun unmarkAsNotForUpload(imageSHA: String) { + notForUploadStatusDao.deleteWithImageSHA1(imageSHA) + } + + override suspend fun isNotForUpload(imageSHA: String): Boolean { + return notForUploadStatusDao.find(imageSHA) > 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/domain/ImageRepository.kt b/app/src/main/java/fr/free/nrw/commons/customselector/domain/ImageRepository.kt new file mode 100644 index 000000000..eab566895 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/domain/ImageRepository.kt @@ -0,0 +1,15 @@ +package fr.free.nrw.commons.customselector.domain + +import fr.free.nrw.commons.customselector.domain.model.Image +import kotlinx.coroutines.flow.Flow + +interface ImageRepository { + + suspend fun getImagesFromDevice(): Flow + + suspend fun markAsNotForUpload(imageSHA: String) + + suspend fun unmarkAsNotForUpload(imageSHA: String) + + suspend fun isNotForUpload(imageSHA: String): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/domain/use_case/ImageUseCase.kt b/app/src/main/java/fr/free/nrw/commons/customselector/domain/use_case/ImageUseCase.kt new file mode 100644 index 000000000..2eb24e4ef --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/domain/use_case/ImageUseCase.kt @@ -0,0 +1,89 @@ +package fr.free.nrw.commons.customselector.domain.use_case + +import android.content.Context +import android.net.Uri +import androidx.exifinterface.media.ExifInterface +import fr.free.nrw.commons.customselector.ui.selector.ImageLoader +import fr.free.nrw.commons.filepicker.PickedFiles +import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.upload.FileProcessor +import fr.free.nrw.commons.upload.FileUtilsWrapper +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okio.FileNotFoundException +import timber.log.Timber +import java.io.IOException +import java.net.UnknownHostException +import javax.inject.Inject + +class ImageUseCase @Inject constructor( + private val fileUtilsWrapper: FileUtilsWrapper, + private val fileProcessor: FileProcessor, + private val mediaClient: MediaClient, + private val context: Context +) { + private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO + + /** + * Retrieves the SHA1 hash of an image from its URI. + * + * @param uri The URI of the image. + * @return The SHA1 hash of the image, or an empty string if the image is not found. + */ + suspend fun getImageSHA1(uri: Uri): String = withContext(ioDispatcher) { + try { + val inputStream = context.contentResolver.openInputStream(uri) + fileUtilsWrapper.getSHA1(inputStream) + } catch (e: FileNotFoundException) { + Timber.e(e) + "" + } + } + + /** + * Generates a modified SHA1 hash of an image after redacting sensitive EXIF tags. + * + * @param imageUri The URI of the image to process. + * @return The modified SHA1 hash of the image. + */ + suspend fun generateModifiedSHA1(imageUri: Uri): String = withContext(ioDispatcher) { + val uploadableFile = PickedFiles.pickedExistingPicture(context, imageUri) + val exifInterface: ExifInterface? = try { + ExifInterface(uploadableFile.file!!) + } catch (e: IOException) { + Timber.e(e) + null + } + fileProcessor.redactExifTags(exifInterface, fileProcessor.getExifTagsToRedact()) + + val sha1 = fileUtilsWrapper.getSHA1( + fileUtilsWrapper.getFileInputStream(uploadableFile.filePath)) + uploadableFile.file.delete() + sha1 + } + + /** + * Checks whether a file with the given SHA1 hash exists on Wikimedia Commons. + * + * @param sha1 The SHA1 hash of the file to check. + * @return An ImageLoader.Result indicating the existence of the file on Commons. + */ + suspend fun checkWhetherFileExistsOnCommonsUsingSHA1( + sha1: String + ): ImageLoader.Result = withContext(ioDispatcher) { + return@withContext try { + if (mediaClient.checkFileExistsUsingSha(sha1).blockingGet()) { + ImageLoader.Result.TRUE + } else { + ImageLoader.Result.FALSE + } + } catch (e: UnknownHostException) { + Timber.e(e, "Network Connection Error") + ImageLoader.Result.ERROR + } catch (e: Exception) { + e.printStackTrace() + ImageLoader.Result.ERROR + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorScreen.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorScreen.kt index de73ffc2a..0b5ee070f 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorScreen.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorScreen.kt @@ -56,12 +56,14 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.customselector.ui.components.CustomSelectorBottomBar import fr.free.nrw.commons.customselector.ui.components.CustomSelectorTopBar import fr.free.nrw.commons.customselector.ui.components.PartialStorageAccessDialog +import fr.free.nrw.commons.customselector.ui.states.CustomSelectorUiState +import fr.free.nrw.commons.customselector.ui.states.ImageUiState import fr.free.nrw.commons.ui.theme.CommonsTheme @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun CustomSelectorScreen( - uiState: CustomSelectorState, + uiState: CustomSelectorUiState, onEvent: (CustomSelectorEvent)-> Unit, selectedImageIds: ()-> Set, onViewImage: (id: Long)-> Unit, @@ -112,7 +114,7 @@ fun CustomSelectorScreen( @Composable fun FoldersPane( - uiState: CustomSelectorState, + uiState: CustomSelectorUiState, onFolderClick: (Folder)-> Unit, onUnselectAll: ()-> Unit, adaptiveInfo: WindowAdaptiveInfo, @@ -270,7 +272,7 @@ private fun FolderItemPreview() { private fun CustomSelectorScreenPreview() { CommonsTheme { CustomSelectorScreen( - uiState = CustomSelectorState(), + uiState = CustomSelectorUiState(), onViewImage = { }, onEvent = { }, selectedImageIds = { emptySet() }, diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorState.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorState.kt deleted file mode 100644 index 11d88bf9b..000000000 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorState.kt +++ /dev/null @@ -1,13 +0,0 @@ -package fr.free.nrw.commons.customselector.ui.screens - -import fr.free.nrw.commons.customselector.model.Image - -data class CustomSelectorState( - val isLoading: Boolean = false, - val folders: List = emptyList(), - val filteredImages: List = emptyList(), - val selectedImageIds: Set = emptySet() -) { - val inSelectionMode: Boolean - get() = selectedImageIds.isNotEmpty() -} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorViewModel.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorViewModel.kt index 842ee8ac6..58b29e624 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorViewModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/CustomSelectorViewModel.kt @@ -1,44 +1,67 @@ package fr.free.nrw.commons.customselector.ui.screens +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import fr.free.nrw.commons.customselector.data.MediaReader -import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.customselector.domain.ImageRepository +import fr.free.nrw.commons.customselector.domain.model.Image +import fr.free.nrw.commons.customselector.domain.use_case.ImageUseCase +import fr.free.nrw.commons.customselector.ui.states.CustomSelectorUiState +import fr.free.nrw.commons.customselector.ui.states.ImageUiState +import fr.free.nrw.commons.customselector.ui.states.toImageUiState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import javax.inject.Inject -class CustomSelectorViewModel(private val mediaReader: MediaReader): ViewModel() { +typealias imageId = Long +typealias imageSHA = String - private val _uiState = MutableStateFlow(CustomSelectorState()) +class CustomSelectorViewModel @Inject constructor( + private val imageRepository: ImageRepository, + private val imageUseCase: ImageUseCase +): ViewModel() { + + private val _uiState = MutableStateFlow(CustomSelectorUiState()) val uiState = _uiState.asStateFlow() + private val cacheSHA1 = mutableMapOf() + + private val allImages = mutableListOf() private val foldersMap = mutableMapOf>() init { - _uiState.update { it.copy(isLoading = true) } viewModelScope.launch { - mediaReader.getImages().collect { image-> + imageRepository.getImagesFromDevice().collect { image -> val bucketId = image.bucketId + + allImages.add(image.toImageUiState()) foldersMap.getOrPut(bucketId) { mutableListOf() }.add(image) } - val foldersList = foldersMap.map { (bucketId, images)-> + val folders = foldersMap.map { (bucketId, images)-> val firstImage = images.first() Folder( - bucketId = bucketId, bucketName = firstImage.bucketName, - preview = firstImage.uri, itemsCount = images.size + bucketId = bucketId, + bucketName = firstImage.bucketName, + preview = firstImage.uri, + itemsCount = images.size, + images = images ) } - _uiState.update { it.copy(isLoading = false, folders = foldersList) } + _uiState.update { it.copy(isLoading = false, folders = folders) } } } fun onEvent(e: CustomSelectorEvent) { when(e) { - is CustomSelectorEvent.OnFolderClick-> { + is CustomSelectorEvent.OnFolderClick -> { _uiState.update { - it.copy(filteredImages = foldersMap[e.bucketId]?.toList() ?: emptyList()) + it.copy( + filteredImages = foldersMap[e.bucketId]?.map { + img -> img.toImageUiState() + } ?: emptyList() + ) } } @@ -46,7 +69,7 @@ class CustomSelectorViewModel(private val mediaReader: MediaReader): ViewModel() _uiState.update { state -> val updatedSelectedIds = if (state.selectedImageIds.contains(e.imageId)) { state.selectedImageIds - e.imageId // Remove if already selected - } else{ + } else { state.selectedImageIds + e.imageId // Add if not selected } state.copy(selectedImageIds = updatedSelectedIds) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/Folder.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/Folder.kt index 840e9d6bf..303edad3c 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/Folder.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/Folder.kt @@ -2,6 +2,7 @@ package fr.free.nrw.commons.customselector.ui.screens import android.net.Uri import android.os.Parcelable +import fr.free.nrw.commons.customselector.domain.model.Image import kotlinx.parcelize.Parcelize @Parcelize @@ -9,5 +10,6 @@ data class Folder( val bucketId: Long, val bucketName: String, val preview: Uri, + val images: List, val itemsCount: Int ): Parcelable diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/ImagesPane.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/ImagesPane.kt index 3a036239e..381119ed2 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/ImagesPane.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/ImagesPane.kt @@ -39,6 +39,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset @@ -54,18 +55,20 @@ import androidx.compose.ui.unit.toIntRect import androidx.window.core.layout.WindowWidthSizeClass import coil.compose.rememberAsyncImagePainter import fr.free.nrw.commons.R -import fr.free.nrw.commons.customselector.domain.model.Image import fr.free.nrw.commons.customselector.ui.components.CustomSelectorBottomBar import fr.free.nrw.commons.customselector.ui.components.CustomSelectorTopBar import fr.free.nrw.commons.customselector.ui.components.PartialStorageAccessDialog +import fr.free.nrw.commons.customselector.ui.states.CustomSelectorUiState +import fr.free.nrw.commons.customselector.ui.states.ImageUiState import fr.free.nrw.commons.ui.theme.CommonsTheme +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @OptIn(ExperimentalFoundationApi::class) @Composable fun ImagesPane( - uiState: CustomSelectorState, + uiState: CustomSelectorUiState, selectedFolder: Folder, selectedImages: () -> Set, onNavigateBack: () -> Unit, @@ -74,7 +77,6 @@ fun ImagesPane( adaptiveInfo: WindowAdaptiveInfo, hasPartialAccess: Boolean = false ) { -// val inSelectionMode by remember { derivedStateOf { selectedImages().isNotEmpty() } } val lazyGridState = rememberLazyGridState() var autoScrollSpeed by remember { mutableFloatStateOf(0f) } val isCompatWidth by remember(adaptiveInfo.windowSizeClass) { @@ -255,7 +257,7 @@ private fun ImageItemPreview() { */ fun Modifier.imageGridDragHandler( gridState: LazyGridState, - imageList: List, + imageList: List, selectedImageIds: () -> Set, autoScrollThreshold: Float, setSelectedImageIds: (Set) -> Unit, @@ -337,7 +339,7 @@ fun Modifier.imageGridDragHandler( * @param pointerKey The ending index of the range. * @return A set of image IDs within the specified range. */ -fun List.getImageIdsInRange(initialKey: Int, pointerKey: Int): Set { +fun List.getImageIdsInRange(initialKey: Int, pointerKey: Int): Set { val setOfKeys = mutableSetOf() if (initialKey < pointerKey) { (initialKey..pointerKey).forEach { diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/ViewImageScreen.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/ViewImageScreen.kt index a9acf2590..0cb3bfcb8 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/ViewImageScreen.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/screens/ViewImageScreen.kt @@ -27,13 +27,13 @@ import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest -import fr.free.nrw.commons.customselector.domain.model.Image +import fr.free.nrw.commons.customselector.ui.states.ImageUiState import kotlin.math.abs @Composable fun ViewImageScreen( currentImageIndex: Int, - imageList: List, + imageList: List, ) { var imageScale by remember { mutableFloatStateOf(1f) } var imageOffset by remember { mutableStateOf(Offset.Zero) } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt index efff0a07c..d1999eabc 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt @@ -27,21 +27,20 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import fr.free.nrw.commons.R -import fr.free.nrw.commons.customselector.data.MediaReader import fr.free.nrw.commons.customselector.database.NotForUploadStatus import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao +import fr.free.nrw.commons.customselector.domain.model.Image import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants import fr.free.nrw.commons.customselector.helper.FolderDeletionHelper import fr.free.nrw.commons.customselector.listeners.FolderClickListener import fr.free.nrw.commons.customselector.listeners.ImageSelectListener -import fr.free.nrw.commons.customselector.domain.model.Image import fr.free.nrw.commons.customselector.ui.screens.CustomSelectorScreen import fr.free.nrw.commons.customselector.ui.screens.ViewImageScreen +import fr.free.nrw.commons.customselector.utils.CustomSelectorViewModelFactory import fr.free.nrw.commons.databinding.ActivityCustomSelectorBinding import fr.free.nrw.commons.databinding.CustomSelectorBottomLayoutBinding import fr.free.nrw.commons.databinding.CustomSelectorToolbarBinding @@ -191,17 +190,11 @@ class CustomSelectorActivity : // setContentView(view) prefs = applicationContext.getSharedPreferences("CustomSelector", MODE_PRIVATE) - viewModel = - ViewModelProvider(this, customSelectorViewModelFactory).get( - CustomSelectorViewModel::class.java, - ) - - val mediaReader = MediaReader(this) setContent { - val csViewModel = viewModel { - fr.free.nrw.commons.customselector.ui.screens.CustomSelectorViewModel(mediaReader) - } + val csViewModel = ViewModelProvider(this, customSelectorViewModelFactory).get( + fr.free.nrw.commons.customselector.ui.screens.CustomSelectorViewModel::class.java + ) val uiState by csViewModel.uiState.collectAsStateWithLifecycle() @@ -265,7 +258,7 @@ class CustomSelectorActivity : override fun onResume() { super.onResume() - fetchData() +// fetchData() } /** diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/states/CustomSelectorUiState.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/states/CustomSelectorUiState.kt new file mode 100644 index 000000000..346801a71 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/states/CustomSelectorUiState.kt @@ -0,0 +1,17 @@ +package fr.free.nrw.commons.customselector.ui.states + +import fr.free.nrw.commons.customselector.ui.screens.Folder +import fr.free.nrw.commons.customselector.ui.screens.imageId + +typealias isNotForUpload = Boolean + +data class CustomSelectorUiState( + val isLoading: Boolean = true, + val folders: List = emptyList(), + val filteredImages: List = emptyList(), + val selectedImageIds: Set = emptySet(), + val imagesNotForUpload: Map = emptyMap() +) { + val inSelectionMode: Boolean + get() = selectedImageIds.isNotEmpty() +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/states/ImageUiState.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/states/ImageUiState.kt new file mode 100644 index 000000000..e986c8387 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/states/ImageUiState.kt @@ -0,0 +1,20 @@ +package fr.free.nrw.commons.customselector.ui.states + +import android.net.Uri +import fr.free.nrw.commons.customselector.domain.model.Image + +data class ImageUiState( + val id: Long, + val name: String, + val uri: Uri, + val bucketId: Long, + val isNotForUpload: Boolean = false, + val isUploaded: Boolean = false +) + +fun Image.toImageUiState() = ImageUiState( + id = id, + name = name, + uri = uri, + bucketId = bucketId +) \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/utils/CustomSelectorViewModelFactory.kt b/app/src/main/java/fr/free/nrw/commons/customselector/utils/CustomSelectorViewModelFactory.kt new file mode 100644 index 000000000..10d78c7dd --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/utils/CustomSelectorViewModelFactory.kt @@ -0,0 +1,21 @@ +package fr.free.nrw.commons.customselector.utils + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import fr.free.nrw.commons.customselector.domain.ImageRepository +import fr.free.nrw.commons.customselector.domain.use_case.ImageUseCase +import fr.free.nrw.commons.customselector.ui.screens.CustomSelectorViewModel +import javax.inject.Inject + +class CustomSelectorViewModelFactory @Inject constructor( + private val imageRepository: ImageRepository, + private val imageUseCase: ImageUseCase +): ViewModelProvider.Factory { + override fun create( + modelClass: Class + ): CustomSelectorViewModel { + return CustomSelectorViewModel( + imageRepository, imageUseCase + ) as CustomSelectorViewModel + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index cd7324c63..3e5716350 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -17,8 +17,12 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.contributions.ContributionDao; +import fr.free.nrw.commons.customselector.data.ImageRepositoryImpl; +import fr.free.nrw.commons.customselector.data.MediaReader; import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao; import fr.free.nrw.commons.customselector.database.UploadedStatusDao; +import fr.free.nrw.commons.customselector.domain.ImageRepository; +import fr.free.nrw.commons.customselector.domain.use_case.ImageUseCase; import fr.free.nrw.commons.customselector.ui.selector.ImageFileLoader; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.db.AppDatabase; @@ -317,4 +321,13 @@ public class CommonsApplicationModule { public ContentResolver providesContentResolver(Context context){ return context.getContentResolver(); } + + @Provides + public ImageRepository providesImageRepository( + MediaReader mediaReader, + NotForUploadStatusDao notForUploadStatusDao, + ImageUseCase imageUseCase + ) { + return new ImageRepositoryImpl(mediaReader, notForUploadStatusDao); + } }