Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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"

}
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@

<activity android:name=".ui.login.LoginActivity"
android:exported="false" />

<activity
android:name=".ui.gallery.GalleryActivity"
android:exported="false" />
</application>

</manifest>
66 changes: 66 additions & 0 deletions app/src/main/java/com/dining/coach/ui/gallery/GalleryActivity.kt
Original file line number Diff line number Diff line change
@@ -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<ActivityGalleryBinding>(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 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위의 필드 requiredPermissions에 Manifest.permission.WRITE_EXTERNAL_STORAGE 도 필요하지 않을까요?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

커스텀 갤러리 사용 목적이 바코드 인식으로 업로드 관련이라 저장하는 기능은 필요 없을 것 같아 추가하지않았습니다!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figma에 보시면 ios-식단 기록 화면에 식단 목록이 보여야 하는데, 저장하는 기능도 필요하지 않을까요?

ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}

}
Original file line number Diff line number Diff line change
@@ -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<Photo, GalleryRecyclerViewAdapter.ImageViewHolder>(diffCallback) {

companion object {
val diffCallback = object : DiffUtil.ItemCallback<Photo>() {
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)
}
}
}
}
23 changes: 23 additions & 0 deletions app/src/main/java/com/dining/coach/ui/gallery/GalleryViewModel.kt
Original file line number Diff line number Diff line change
@@ -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)


}
16 changes: 16 additions & 0 deletions app/src/main/res/layout/activity_gallery.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.gallery.GalleryActivity">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/galleryRv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
15 changes: 15 additions & 0 deletions app/src/main/res/layout/row_gallery_image.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="120dp">

<ImageView
android:id="@+id/rowGalleryImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:ignore="ContentDescription" />


</androidx.constraintlayout.widget.ConstraintLayout>
4 changes: 4 additions & 0 deletions data/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
}
Original file line number Diff line number Diff line change
@@ -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<String>, 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
)
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,4 +18,10 @@ object UseCaseModule {
@Singleton
fun provideCheckIsLoginUseCase(repository: UserRepository): CheckIsLoginUseCase =
CheckIsLoginUseCase(repository)

@Provides
@Singleton
fun provideGalleryImageFetchUseCase(repository: GalleryRepository): GalleryImageFetchUseCase =
GalleryImageFetchUseCase(repository)

}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,4 +17,9 @@ interface LocalDataSourceModule {
@Singleton
@Binds
fun bindsUserLocalDataSource(implements: UserLocalDataSourceImpl): UserLocalDataSource

@Singleton
@Binds
fun bindsGalleryLocalDataSource(implements: GalleryLocalDataSourceImpl): GalleryLocalDataSource

}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -15,4 +17,8 @@ interface RepositoryModule {
@Singleton
@Binds
fun bindsUserRepository(implements: UserRepositoryImpl): UserRepository

@Singleton
@Binds
fun bindsGalleryRepository(implements: GalleryRepositoryImpl): GalleryRepository
}
17 changes: 16 additions & 1 deletion data/src/main/java/com/diningcoach/data/extensions/Extensions.kt
Original file line number Diff line number Diff line change
@@ -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,
)
15 changes: 15 additions & 0 deletions data/src/main/java/com/diningcoach/data/model/PhotoModel.kt
Original file line number Diff line number Diff line change
@@ -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,
)
Loading