diff --git a/app/build.gradle b/app/build.gradle
index 0ab674e..b757107 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -52,6 +52,7 @@ dependencies {
def pagingVersion = "3.1.1"
def navigationVersion= "2.5.3"
def retrofitVersion = "2.9.0"
+ def glideVersion = "4.13.2"
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
@@ -96,4 +97,8 @@ dependencies {
// logging
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
+ // glide
+ implementation "com.github.bumptech.glide:glide:$glideVersion"
+ annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
+
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6e421d9..25bfe0a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -52,6 +52,10 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/dining/coach/ui/gallery/GalleryActivity.kt b/app/src/main/java/com/dining/coach/ui/gallery/GalleryActivity.kt
new file mode 100644
index 0000000..f48b033
--- /dev/null
+++ b/app/src/main/java/com/dining/coach/ui/gallery/GalleryActivity.kt
@@ -0,0 +1,66 @@
+package com.dining.coach.ui.gallery
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.viewModels
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.lifecycleScope
+import com.dining.coach.R
+import com.dining.coach.base.BaseActivity
+import com.dining.coach.databinding.ActivityGalleryBinding
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class GalleryActivity : BaseActivity(R.layout.activity_gallery) {
+ private val viewModel: GalleryViewModel by viewModels()
+ private val requiredPermissions = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
+ private lateinit var galleryRecyclerViewAdapter:GalleryRecyclerViewAdapter
+
+ override fun createActivity() = viewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setRecyclerView()
+ }
+ private fun setRecyclerView(){
+ wrapGridRecyclerView(bind.galleryRv, 4)
+ galleryRecyclerViewAdapter = GalleryRecyclerViewAdapter()
+ bind.galleryRv.adapter = galleryRecyclerViewAdapter
+
+ fetchGalleryAdapter()
+ }
+
+ private fun fetchGalleryAdapter(){
+ if (hasAllPermissions()) {
+ lifecycleScope.launch {
+ viewModel.galleryPager.collectLatest { pagingData ->
+ galleryRecyclerViewAdapter.submitData(pagingData)
+ }
+ }
+ } else {
+ registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { isGranted ->
+ if (isGranted.all { it.value }) {
+ fetchGalleryAdapter()
+ } else {
+ // TODO("replace permission denied process")
+ Toast.makeText(this, "permission error", Toast.LENGTH_SHORT).show()
+ finish()
+ }
+ }.launch(requiredPermissions)
+ }
+ }
+
+
+ private fun hasAllPermissions() = requiredPermissions.all {
+ ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
+ }
+
+}
diff --git a/app/src/main/java/com/dining/coach/ui/gallery/GalleryRecyclerViewAdapter.kt b/app/src/main/java/com/dining/coach/ui/gallery/GalleryRecyclerViewAdapter.kt
new file mode 100644
index 0000000..86f48da
--- /dev/null
+++ b/app/src/main/java/com/dining/coach/ui/gallery/GalleryRecyclerViewAdapter.kt
@@ -0,0 +1,54 @@
+package com.dining.coach.ui.gallery
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.paging.PagingDataAdapter
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.dining.coach.databinding.RowGalleryImageBinding
+import com.diningcoach.domain.model.Photo
+
+class GalleryRecyclerViewAdapter :
+ PagingDataAdapter(diffCallback) {
+
+ companion object {
+ val diffCallback = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: Photo, newItem: Photo) =
+ oldItem.uri == newItem.uri
+
+ override fun areContentsTheSame(oldItem: Photo, newItem: Photo) =
+ oldItem == newItem
+ }
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): ImageViewHolder = ImageViewHolder(
+ RowGalleryImageBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+
+ override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
+ val item = getItem(position)
+ item?.let {
+ holder.bind(it)
+ }
+ }
+
+ inner class ImageViewHolder(
+ private val binding: RowGalleryImageBinding
+ ) : RecyclerView.ViewHolder(binding.root) {
+ fun bind(item: Photo) {
+ binding.run {
+ Glide.with(binding.root.context)
+ .load(item.uri)
+ .into(binding.rowGalleryImageView)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/dining/coach/ui/gallery/GalleryViewModel.kt b/app/src/main/java/com/dining/coach/ui/gallery/GalleryViewModel.kt
new file mode 100644
index 0000000..69b5fd9
--- /dev/null
+++ b/app/src/main/java/com/dining/coach/ui/gallery/GalleryViewModel.kt
@@ -0,0 +1,23 @@
+package com.dining.coach.ui.gallery
+
+import androidx.lifecycle.viewModelScope
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.cachedIn
+import com.dining.coach.base.BaseViewModel
+import com.diningcoach.domain.usecase.gallery.GalleryImageFetchUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+@HiltViewModel
+class GalleryViewModel @Inject constructor(
+ private val galleryImageFetch: GalleryImageFetchUseCase
+): BaseViewModel() {
+ val galleryPager = Pager(
+ config = PagingConfig(pageSize = 50)
+ ) {
+ galleryImageFetch()
+ }.flow.cachedIn(viewModelScope)
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_gallery.xml b/app/src/main/res/layout/activity_gallery.xml
new file mode 100644
index 0000000..853885c
--- /dev/null
+++ b/app/src/main/res/layout/activity_gallery.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/row_gallery_image.xml b/app/src/main/res/layout/row_gallery_image.xml
new file mode 100644
index 0000000..bc38bb4
--- /dev/null
+++ b/app/src/main/res/layout/row_gallery_image.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/build.gradle b/data/build.gradle
index 23b7817..b95cb59 100644
--- a/data/build.gradle
+++ b/data/build.gradle
@@ -38,6 +38,7 @@ dependencies {
def retrofitVersion = "2.9.0"
def hiltVersion = "2.45"
+ def pagingVersion = "3.1.1"
// Retrofit2
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
@@ -51,4 +52,7 @@ dependencies {
implementation "com.google.dagger:hilt-android:$hiltVersion"
kapt "com.google.dagger:hilt-compiler:$hiltVersion"
kapt "androidx.hilt:hilt-compiler:1.0.0"
+
+ // paging
+ implementation "androidx.paging:paging-runtime-ktx:$pagingVersion"
}
\ No newline at end of file
diff --git a/data/src/main/java/com/diningcoach/data/di/manager/local/MediaStoreManager.kt b/data/src/main/java/com/diningcoach/data/di/manager/local/MediaStoreManager.kt
new file mode 100644
index 0000000..bb8e979
--- /dev/null
+++ b/data/src/main/java/com/diningcoach/data/di/manager/local/MediaStoreManager.kt
@@ -0,0 +1,58 @@
+package com.diningcoach.data.di.manager.local
+
+import android.content.ContentResolver
+import android.content.Context
+import android.database.Cursor
+import android.os.Build
+import android.os.Bundle
+import android.provider.MediaStore
+import dagger.hilt.android.qualifiers.ApplicationContext
+import javax.inject.Inject
+
+class MediaStoreManager @Inject constructor(
+ @ApplicationContext context: Context
+) {
+ private val contentResolver = context.contentResolver
+
+ fun fetchImages(projection:Array, limit: Int, offset: Int): Cursor?{
+ val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
+ val selection =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) MediaStore.Images.Media.SIZE + " > 0"
+ else null
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ return contentResolver.query(
+ contentUri,
+ projection,
+ Bundle().apply {
+ // Limit & Offset
+ putInt(ContentResolver.QUERY_ARG_LIMIT, limit)
+ putInt(ContentResolver.QUERY_ARG_OFFSET, offset)
+
+ // Sort function
+ putStringArray(
+ ContentResolver.QUERY_ARG_SORT_COLUMNS,
+ arrayOf(MediaStore.Images.Media.DATE_TAKEN)
+ )
+ putInt(
+ ContentResolver.QUERY_ARG_SORT_DIRECTION,
+ ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
+ )
+
+ // Selection
+ putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
+ }, null
+ )
+ } else {
+ val sortOrder =
+ "${MediaStore.Images.Media.DATE_TAKEN} DESC, ${MediaStore.Images.Media._ID} DESC LIMIT $limit OFFSET $offset"
+ return contentResolver.query(
+ contentUri,
+ projection,
+ selection,
+ null,
+ sortOrder
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/data/src/main/java/com/diningcoach/data/di/module/UseCaseModule.kt b/data/src/main/java/com/diningcoach/data/di/module/UseCaseModule.kt
index 855f0e6..507a458 100644
--- a/data/src/main/java/com/diningcoach/data/di/module/UseCaseModule.kt
+++ b/data/src/main/java/com/diningcoach/data/di/module/UseCaseModule.kt
@@ -1,6 +1,8 @@
package com.diningcoach.data.di.module
+import com.diningcoach.domain.repository.GalleryRepository
import com.diningcoach.domain.repository.UserRepository
+import com.diningcoach.domain.usecase.gallery.GalleryImageFetchUseCase
import com.diningcoach.domain.usecase.user.CheckIsLoginUseCase
import dagger.Module
import dagger.Provides
@@ -16,4 +18,10 @@ object UseCaseModule {
@Singleton
fun provideCheckIsLoginUseCase(repository: UserRepository): CheckIsLoginUseCase =
CheckIsLoginUseCase(repository)
+
+ @Provides
+ @Singleton
+ fun provideGalleryImageFetchUseCase(repository: GalleryRepository): GalleryImageFetchUseCase =
+ GalleryImageFetchUseCase(repository)
+
}
\ No newline at end of file
diff --git a/data/src/main/java/com/diningcoach/data/di/module/repository/LocalDataSourceModule.kt b/data/src/main/java/com/diningcoach/data/di/module/repository/LocalDataSourceModule.kt
index 9f4be9b..c4a45e6 100644
--- a/data/src/main/java/com/diningcoach/data/di/module/repository/LocalDataSourceModule.kt
+++ b/data/src/main/java/com/diningcoach/data/di/module/repository/LocalDataSourceModule.kt
@@ -1,5 +1,7 @@
package com.diningcoach.data.di.module.repository
+import com.diningcoach.data.repository.gallery.local.GalleryLocalDataSource
+import com.diningcoach.data.repository.gallery.local.GalleryLocalDataSourceImpl
import com.diningcoach.data.repository.user.local.UserLocalDataSource
import com.diningcoach.data.repository.user.local.UserLocalDataSourceImpl
import dagger.Binds
@@ -15,4 +17,9 @@ interface LocalDataSourceModule {
@Singleton
@Binds
fun bindsUserLocalDataSource(implements: UserLocalDataSourceImpl): UserLocalDataSource
+
+ @Singleton
+ @Binds
+ fun bindsGalleryLocalDataSource(implements: GalleryLocalDataSourceImpl): GalleryLocalDataSource
+
}
\ No newline at end of file
diff --git a/data/src/main/java/com/diningcoach/data/di/module/repository/RepositoryModule.kt b/data/src/main/java/com/diningcoach/data/di/module/repository/RepositoryModule.kt
index cb17815..9810caa 100644
--- a/data/src/main/java/com/diningcoach/data/di/module/repository/RepositoryModule.kt
+++ b/data/src/main/java/com/diningcoach/data/di/module/repository/RepositoryModule.kt
@@ -1,6 +1,8 @@
package com.diningcoach.data.di.module.repository
+import com.diningcoach.data.repository.gallery.GalleryRepositoryImpl
import com.diningcoach.data.repository.user.UserRepositoryImpl
+import com.diningcoach.domain.repository.GalleryRepository
import com.diningcoach.domain.repository.UserRepository
import dagger.Binds
import dagger.Module
@@ -15,4 +17,8 @@ interface RepositoryModule {
@Singleton
@Binds
fun bindsUserRepository(implements: UserRepositoryImpl): UserRepository
+
+ @Singleton
+ @Binds
+ fun bindsGalleryRepository(implements: GalleryRepositoryImpl): GalleryRepository
}
\ No newline at end of file
diff --git a/data/src/main/java/com/diningcoach/data/extensions/Extensions.kt b/data/src/main/java/com/diningcoach/data/extensions/Extensions.kt
index 5d3c76b..5fa1606 100644
--- a/data/src/main/java/com/diningcoach/data/extensions/Extensions.kt
+++ b/data/src/main/java/com/diningcoach/data/extensions/Extensions.kt
@@ -1,10 +1,25 @@
package com.diningcoach.data.extensions
+import com.diningcoach.data.model.PhotoModel
import com.diningcoach.data.model.UserModel
+import com.diningcoach.domain.model.Photo
import com.diningcoach.domain.model.User
+import java.util.*
fun UserModel.toUser(): User {
return User(
- id, accessToken, refreshToken
+ id, accessToken, refreshToken
)
}
+
+fun PhotoModel.toPhoto() = Photo(
+ uri,
+ name,
+ fullName,
+ mimeType,
+ Date(addedDate),
+ folder,
+ size,
+ width,
+ height,
+)
diff --git a/data/src/main/java/com/diningcoach/data/model/PhotoModel.kt b/data/src/main/java/com/diningcoach/data/model/PhotoModel.kt
new file mode 100644
index 0000000..4ff5506
--- /dev/null
+++ b/data/src/main/java/com/diningcoach/data/model/PhotoModel.kt
@@ -0,0 +1,15 @@
+package com.diningcoach.data.model
+
+import android.net.Uri
+
+data class PhotoModel(
+ val uri: Uri,
+ val name: String,
+ val fullName: String,
+ val mimeType: String,
+ val addedDate: Long,
+ val folder: String,
+ val size: Long,
+ val width: Int,
+ val height: Int,
+)
diff --git a/data/src/main/java/com/diningcoach/data/repository/gallery/GalleryRepositoryImpl.kt b/data/src/main/java/com/diningcoach/data/repository/gallery/GalleryRepositoryImpl.kt
new file mode 100644
index 0000000..0b1de67
--- /dev/null
+++ b/data/src/main/java/com/diningcoach/data/repository/gallery/GalleryRepositoryImpl.kt
@@ -0,0 +1,38 @@
+package com.diningcoach.data.repository.gallery
+
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import com.diningcoach.data.extensions.toPhoto
+import com.diningcoach.data.repository.gallery.local.GalleryLocalDataSource
+import com.diningcoach.domain.model.Photo
+import com.diningcoach.domain.repository.GalleryRepository
+import javax.inject.Inject
+
+class GalleryRepositoryImpl @Inject constructor(
+ private val galleryLocalDataSource: GalleryLocalDataSource,
+): GalleryRepository {
+ override fun getGalleryImagePagingSource():PagingSource {
+ return object: PagingSource() {
+ override fun getRefreshKey(state: PagingState): Int? {
+ return state.anchorPosition?.let { anchorPosition ->
+ val anchorPage = state.closestPageToPosition(anchorPosition)
+ anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
+ }
+ }
+ override suspend fun load(params: LoadParams): LoadResult {
+ try {
+ val pageNumber = params.key ?: 0
+ val response = galleryLocalDataSource.fetchGalleryImages(params.loadSize, pageNumber*params.loadSize)
+ return LoadResult.Page(
+ data = response.map { it.toPhoto() },
+ prevKey = if(pageNumber==0) null else pageNumber-1,
+ nextKey = if(response.isEmpty()) null else pageNumber+1
+ )
+ } catch (e: Exception) {
+ // TODO("error process")
+ throw e
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/data/src/main/java/com/diningcoach/data/repository/gallery/local/GalleryLocalDataSource.kt b/data/src/main/java/com/diningcoach/data/repository/gallery/local/GalleryLocalDataSource.kt
new file mode 100644
index 0000000..e8158f8
--- /dev/null
+++ b/data/src/main/java/com/diningcoach/data/repository/gallery/local/GalleryLocalDataSource.kt
@@ -0,0 +1,7 @@
+package com.diningcoach.data.repository.gallery.local
+
+import com.diningcoach.data.model.PhotoModel
+
+interface GalleryLocalDataSource {
+ fun fetchGalleryImages(limit: Int, offset: Int): List
+}
\ No newline at end of file
diff --git a/data/src/main/java/com/diningcoach/data/repository/gallery/local/GalleryLocalDataSourceImpl.kt b/data/src/main/java/com/diningcoach/data/repository/gallery/local/GalleryLocalDataSourceImpl.kt
new file mode 100644
index 0000000..8e6a789
--- /dev/null
+++ b/data/src/main/java/com/diningcoach/data/repository/gallery/local/GalleryLocalDataSourceImpl.kt
@@ -0,0 +1,49 @@
+package com.diningcoach.data.repository.gallery.local
+
+import android.net.Uri
+import android.provider.MediaStore.Images.Media
+import com.diningcoach.data.di.manager.local.MediaStoreManager
+import com.diningcoach.data.model.PhotoModel
+import javax.inject.Inject
+
+class GalleryLocalDataSourceImpl @Inject constructor(
+ private val mediaStoreManager: MediaStoreManager
+) : GalleryLocalDataSource {
+
+ override fun fetchGalleryImages(limit: Int, offset: Int): List {
+ val contentUri = Media.EXTERNAL_CONTENT_URI
+ val projection = arrayOf(
+ Media._ID,
+ Media.TITLE,
+ Media.DISPLAY_NAME,
+ Media.MIME_TYPE,
+ Media.DATE_TAKEN,
+ Media.BUCKET_DISPLAY_NAME,
+ Media.SIZE,
+ Media.WIDTH,
+ Media.HEIGHT,
+ )
+ val galleryImage = mutableListOf()
+ mediaStoreManager.fetchImages(projection, limit, offset)?.use{ cursor ->
+ while (cursor.moveToNext()) {
+ galleryImage.add(
+ PhotoModel(
+ uri = Uri.withAppendedPath(
+ contentUri,
+ cursor.getLong(cursor.getColumnIndexOrThrow(Media._ID)).toString()
+ ),
+ name = cursor.getString(cursor.getColumnIndexOrThrow(Media.TITLE)),
+ fullName = cursor.getString(cursor.getColumnIndexOrThrow(Media.DISPLAY_NAME)),
+ mimeType = cursor.getString(cursor.getColumnIndexOrThrow(Media.MIME_TYPE)),
+ addedDate = cursor.getLong(cursor.getColumnIndexOrThrow(Media.DATE_TAKEN)),
+ folder = cursor.getString(cursor.getColumnIndexOrThrow(Media.BUCKET_DISPLAY_NAME)),
+ size = cursor.getLong(cursor.getColumnIndexOrThrow(Media.SIZE)),
+ width = cursor.getInt(cursor.getColumnIndexOrThrow(Media.WIDTH)),
+ height = cursor.getInt(cursor.getColumnIndexOrThrow(Media.HEIGHT)),
+ )
+ )
+ }
+ }
+ return galleryImage
+ }
+}
\ No newline at end of file
diff --git a/domain/build.gradle b/domain/build.gradle
index 4acb4ca..e3b9830 100644
--- a/domain/build.gradle
+++ b/domain/build.gradle
@@ -37,6 +37,7 @@ dependencies {
def lifecycleVersion = "2.6.1"
def retrofitVersion = "2.9.0"
def hiltVersion = "2.45"
+ def pagingVersion = "3.1.1"
// Coroutine
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
@@ -61,4 +62,8 @@ dependencies {
implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
// Retrofit2 - log
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
+
+ // paging
+ implementation "androidx.paging:paging-common:$pagingVersion"
+
}
\ No newline at end of file
diff --git a/domain/src/main/java/com/diningcoach/domain/model/Photo.kt b/domain/src/main/java/com/diningcoach/domain/model/Photo.kt
new file mode 100644
index 0000000..b5fcb84
--- /dev/null
+++ b/domain/src/main/java/com/diningcoach/domain/model/Photo.kt
@@ -0,0 +1,16 @@
+package com.diningcoach.domain.model
+
+import android.net.Uri
+import java.util.Date
+
+data class Photo(
+ val uri: Uri,
+ val name: String,
+ val fullName: String,
+ val mimeType: String,
+ val addedDate: Date,
+ val folder: String,
+ val size: Long,
+ val width: Int,
+ val height: Int,
+)
diff --git a/domain/src/main/java/com/diningcoach/domain/repository/GalleryRepository.kt b/domain/src/main/java/com/diningcoach/domain/repository/GalleryRepository.kt
new file mode 100644
index 0000000..c1b5440
--- /dev/null
+++ b/domain/src/main/java/com/diningcoach/domain/repository/GalleryRepository.kt
@@ -0,0 +1,8 @@
+package com.diningcoach.domain.repository
+
+import androidx.paging.PagingSource
+import com.diningcoach.domain.model.Photo
+
+interface GalleryRepository {
+ fun getGalleryImagePagingSource(): PagingSource
+}
\ No newline at end of file
diff --git a/domain/src/main/java/com/diningcoach/domain/usecase/gallery/GalleryImageFetchUseCase.kt b/domain/src/main/java/com/diningcoach/domain/usecase/gallery/GalleryImageFetchUseCase.kt
new file mode 100644
index 0000000..60dd804
--- /dev/null
+++ b/domain/src/main/java/com/diningcoach/domain/usecase/gallery/GalleryImageFetchUseCase.kt
@@ -0,0 +1,12 @@
+package com.diningcoach.domain.usecase.gallery
+
+import androidx.paging.PagingSource
+import com.diningcoach.domain.model.Photo
+import com.diningcoach.domain.repository.GalleryRepository
+import javax.inject.Inject
+
+class GalleryImageFetchUseCase @Inject constructor(
+ private val repository: GalleryRepository
+) {
+ operator fun invoke(): PagingSource =repository.getGalleryImagePagingSource()
+}
\ No newline at end of file