generated from ajou4095/template-android
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [Chore]: 갤러리 관련 업무 * [Feat]: 갤러리 ui 구현 & 기초 세팅 * [Chore]: 라이브러리,권한 추가 * [Feat]: 갤러리 페이징 기능 구현 * [Fix]: 오류 수정 * [Style]: 패키지명 수정, 코드 포맷 변경 * [Chore]: 코드 포맷 변경 * [Fix]: 오류 수정
- Loading branch information
Showing
20 changed files
with
707 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
...kotlin/ac/dnd/bookkeeping/android/data/remote/local/gallery/GalleryImageRepositoryImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package ac.dnd.bookkeeping.android.data.remote.local.gallery | ||
|
||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryFolder | ||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryImage | ||
import ac.dnd.bookkeeping.android.domain.repository.GalleryImageRepository | ||
import android.annotation.SuppressLint | ||
import android.content.ContentResolver | ||
import android.content.Context | ||
import android.database.Cursor | ||
import android.net.Uri | ||
import android.os.Build | ||
import android.provider.MediaStore | ||
import androidx.core.os.bundleOf | ||
import dagger.hilt.android.qualifiers.ApplicationContext | ||
import java.io.File | ||
import javax.inject.Inject | ||
|
||
class GalleryImageRepositoryImpl @Inject constructor( | ||
@ApplicationContext private val context: Context | ||
) : GalleryImageRepository { | ||
|
||
private val uriExternal: Uri by lazy { | ||
MediaStore.Images.Media.getContentUri( | ||
MediaStore.VOLUME_EXTERNAL | ||
) | ||
} | ||
|
||
private val projection = arrayOf( | ||
MediaStore.Images.ImageColumns.DATA, | ||
MediaStore.Images.ImageColumns.DISPLAY_NAME, | ||
MediaStore.Images.ImageColumns.DATE_TAKEN, | ||
MediaStore.Images.ImageColumns._ID | ||
) | ||
|
||
private val contentResolver by lazy { context.contentResolver } | ||
private val sortedOrder = MediaStore.Images.ImageColumns.DATE_TAKEN | ||
|
||
override fun getPhotoList( | ||
page: Int, | ||
loadSize: Int, | ||
currentLocation: String? | ||
): List<GalleryImage> { | ||
val galleryImageList = mutableListOf<GalleryImage>() | ||
|
||
var selection: String? = null | ||
var selectionArgs: Array<String>? = null | ||
if (currentLocation != null) { | ||
selection = "${MediaStore.Images.Media.DATA} LIKE ?" | ||
selectionArgs = arrayOf("%$currentLocation%") | ||
} | ||
|
||
val offset = (page - 1) * loadSize | ||
val query = getQuery(offset, loadSize, selection, selectionArgs) | ||
|
||
query?.use { cursor -> | ||
while (cursor.moveToNext()) { | ||
val id = | ||
cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID)) | ||
val name = | ||
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DISPLAY_NAME)) | ||
val filePath = | ||
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATA)) | ||
val date = | ||
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATE_TAKEN)) | ||
val image = GalleryImage( | ||
id = id, | ||
filePath = filePath, | ||
name = name, | ||
date = date ?: "", | ||
size = 0, | ||
) | ||
galleryImageList.add(image) | ||
} | ||
} | ||
return galleryImageList | ||
} | ||
|
||
override fun getFolderList(): List<GalleryFolder> { | ||
val folderList: ArrayList<GalleryFolder> = arrayListOf(GalleryFolder("최근 항목", null)) | ||
val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI | ||
val projection = arrayOf(MediaStore.Images.Media.DATA) | ||
val cursor = context.contentResolver.query(uri, projection, null, null, null) | ||
if (cursor != null) { | ||
while (cursor.moveToNext()) { | ||
val columnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA) | ||
val filePath = File(cursor.getString(columnIndex)).parent | ||
filePath?.let { | ||
val folder = GalleryFolder(filePath.split("/").last(), filePath) | ||
folderList.find { | ||
it.location == filePath | ||
} ?: folderList.add(folder) | ||
} | ||
} | ||
cursor.close() | ||
} | ||
return folderList | ||
} | ||
|
||
@SuppressLint("Recycle") | ||
private fun getQuery( | ||
offset: Int, | ||
limit: Int, | ||
selection: String?, | ||
selectionArgs: Array<String>?, | ||
): Cursor? { | ||
return if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { | ||
val bundle = bundleOf( | ||
ContentResolver.QUERY_ARG_OFFSET to offset, | ||
ContentResolver.QUERY_ARG_LIMIT to limit, | ||
ContentResolver.QUERY_ARG_SORT_COLUMNS to arrayOf(MediaStore.Files.FileColumns.DATE_MODIFIED), | ||
ContentResolver.QUERY_ARG_SORT_DIRECTION to ContentResolver.QUERY_SORT_DIRECTION_DESCENDING, | ||
ContentResolver.QUERY_ARG_SQL_SELECTION to selection, | ||
ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS to selectionArgs, | ||
) | ||
contentResolver.query(uriExternal, projection, bundle, null) | ||
} else { | ||
contentResolver.query( | ||
uriExternal, | ||
projection, | ||
selection, | ||
selectionArgs, | ||
"$sortedOrder DESC LIMIT $limit OFFSET $offset", | ||
) | ||
} | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
...c/main/kotlin/ac/dnd/bookkeeping/android/data/remote/local/gallery/GalleryPagingSource.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package ac.dnd.bookkeeping.android.data.remote.local.gallery | ||
|
||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryImage | ||
import ac.dnd.bookkeeping.android.domain.repository.GalleryImageRepository | ||
import androidx.paging.PagingSource | ||
import androidx.paging.PagingState | ||
|
||
class GalleryPagingSource( | ||
private val imageRepository: GalleryImageRepository, | ||
private val currentLocation: String? | ||
) : PagingSource<Int, GalleryImage>() { | ||
|
||
override fun getRefreshKey(state: PagingState<Int, GalleryImage>): Int? { | ||
return state.anchorPosition?.let { anchorPosition -> | ||
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) | ||
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) | ||
} | ||
} | ||
|
||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GalleryImage> { | ||
return try { | ||
val position = params.key ?: STARTING_PAGE_IDX | ||
val data = imageRepository.getPhotoList( | ||
page = position, | ||
loadSize = params.loadSize, | ||
currentLocation = currentLocation | ||
) | ||
val endOfPaginationReached = data.isEmpty() | ||
val prevKey = if (position == STARTING_PAGE_IDX) null else position - 1 | ||
val nextKey = | ||
if (endOfPaginationReached) null else position + (params.loadSize / PAGING_SIZE) | ||
LoadResult.Page(data, prevKey, nextKey) | ||
|
||
} catch (e: Exception) { | ||
LoadResult.Error(e) | ||
} | ||
} | ||
|
||
companion object { | ||
const val STARTING_PAGE_IDX = 1 | ||
const val PAGING_SIZE = 30 | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
...c/main/kotlin/ac/dnd/bookkeeping/android/data/repository/gallery/GalleryRepositoryImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package ac.dnd.bookkeeping.android.data.repository.gallery | ||
|
||
import ac.dnd.bookkeeping.android.data.remote.local.gallery.GalleryPagingSource | ||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryFolder | ||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryImage | ||
import ac.dnd.bookkeeping.android.domain.repository.GalleryImageRepository | ||
import ac.dnd.bookkeeping.android.domain.repository.GalleryRepository | ||
import androidx.paging.Pager | ||
import androidx.paging.PagingConfig | ||
import androidx.paging.PagingData | ||
import kotlinx.coroutines.flow.Flow | ||
import javax.inject.Inject | ||
|
||
class GalleryRepositoryImpl @Inject constructor( | ||
private val galleryImageRepository: GalleryImageRepository | ||
) : GalleryRepository { | ||
override fun getPagingGalleryList(folder: GalleryFolder): Flow<PagingData<GalleryImage>> { | ||
return Pager( | ||
config = PagingConfig( | ||
pageSize = GalleryPagingSource.PAGING_SIZE, | ||
enablePlaceholders = true | ||
), | ||
pagingSourceFactory = { | ||
GalleryPagingSource( | ||
imageRepository = galleryImageRepository, | ||
currentLocation = folder.location, | ||
) | ||
}, | ||
).flow | ||
} | ||
|
||
override fun getFolderList(): List<GalleryFolder> { | ||
return galleryImageRepository.getFolderList() | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/model/gallery/GalleryFolder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package ac.dnd.bookkeeping.android.domain.model.gallery | ||
|
||
class GalleryFolder( | ||
val name: String, | ||
val location: String?, | ||
) |
9 changes: 9 additions & 0 deletions
9
domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/model/gallery/GalleryImage.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ac.dnd.bookkeeping.android.domain.model.gallery | ||
|
||
data class GalleryImage( | ||
val id: Long, | ||
val filePath: String, | ||
val name: String, | ||
val date: String, | ||
val size: Int, | ||
) |
15 changes: 15 additions & 0 deletions
15
...in/src/main/kotlin/ac/dnd/bookkeeping/android/domain/repository/GalleryImageRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package ac.dnd.bookkeeping.android.domain.repository | ||
|
||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryFolder | ||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryImage | ||
|
||
interface GalleryImageRepository { | ||
|
||
fun getPhotoList( | ||
page: Int, | ||
loadSize: Int, | ||
currentLocation: String? = null | ||
): List<GalleryImage> | ||
|
||
fun getFolderList(): List<GalleryFolder> | ||
} |
16 changes: 16 additions & 0 deletions
16
domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/repository/GalleryRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package ac.dnd.bookkeeping.android.domain.repository | ||
|
||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryFolder | ||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryImage | ||
import androidx.paging.PagingData | ||
import kotlinx.coroutines.flow.Flow | ||
|
||
interface GalleryRepository { | ||
|
||
fun getPagingGalleryList( | ||
folder: GalleryFolder | ||
): Flow<PagingData<GalleryImage>> | ||
|
||
fun getFolderList(): List<GalleryFolder> | ||
|
||
} |
13 changes: 13 additions & 0 deletions
13
...src/main/kotlin/ac/dnd/bookkeeping/android/domain/usecase/gallery/GetFolderListUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package ac.dnd.bookkeeping.android.domain.usecase.gallery | ||
|
||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryFolder | ||
import ac.dnd.bookkeeping.android.domain.repository.GalleryRepository | ||
import javax.inject.Inject | ||
|
||
class GetFolderListUseCase @Inject constructor( | ||
private val galleryRepository: GalleryRepository | ||
) { | ||
operator fun invoke(): List<GalleryFolder> { | ||
return galleryRepository.getFolderList() | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
.../src/main/kotlin/ac/dnd/bookkeeping/android/domain/usecase/gallery/GetPhotoListUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package ac.dnd.bookkeeping.android.domain.usecase.gallery | ||
|
||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryFolder | ||
import ac.dnd.bookkeeping.android.domain.model.gallery.GalleryImage | ||
import ac.dnd.bookkeeping.android.domain.repository.GalleryRepository | ||
import androidx.paging.PagingData | ||
import kotlinx.coroutines.flow.Flow | ||
import javax.inject.Inject | ||
|
||
class GetPhotoListUseCase @Inject constructor( | ||
private val galleryRepository: GalleryRepository | ||
) { | ||
operator fun invoke( | ||
currentFolder: GalleryFolder | ||
): Flow<PagingData<GalleryImage>> { | ||
return galleryRepository.getPagingGalleryList(currentFolder) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.