Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

부산대 Android_이지은 5주차 1단계 #60

Open
wants to merge 11 commits into
base: jieunyume
Choose a base branch
from
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
31 changes: 4 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,4 @@
# android-map-location
## step1 기능 목록
1. 저장된 검색어 목록에 기능 추가하기
- 저장된 검색어 중 하나를 선택하면 해당 검색어의 검색 결과 목록이 표시된다.
2. 검색 결과 목록에 기능 추가하기
- 검색 결과 목록 중 하나의 항목을 선택하면 해당 항목의 위치를 지도에 표시한다.
3. 마지막 위치 저장하기
- 앱 종료 시 마지막 위치를 SharedPreference 저장하여 다시 앱 실행 시 해당 위치로 포커스 한다.
4. 에러 처리하기
- 카카오지도 onMapError() 호출 시 에러 화면을 보여준다. "지도 인증을 실패했습니다. 다시 시도해주세요. '에러이름(에러코드): 에러메세지'
## step2 기능 목록
- 테스트 코드 - JUnit과 mockito를 이용하여 단위 테스트 코드를 작성한다.
- data source 테스트
- local
- SavedLocation 저장 테스트
- SavedLocation 삭제 테스트
- SavedLocation 조회 테스트
- 마지막 위치 저장 테스트
- UI 테스트 코드
- 검색 페이지
- 검색 결과 목록 테스트
- 검색어 저장 목록 테스트
- clearButton 테스트
- 지도 페이지
- BottomSheet 테스트
- KakaoMap Label 테스트
- 지도 에러 화면 테스트
# android-map-refactoring
## 기능 목록
- 데이터베이스를 Room으로 변경한다.
- Hilt를 사용하여 의존성 주입을 적용한다.
9 changes: 9 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("com.google.dagger.hilt.android")
}

android {
Expand Down Expand Up @@ -79,4 +81,11 @@ dependencies {
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
androidTestImplementation("androidx.test.espresso:espresso-intents:3.3.0")
// room
implementation("androidx.room:room-runtime:2.6.1")
kapt("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.4.3")
// hilt
implementation("com.google.dagger:hilt-android:2.48.1")
kapt("com.google.dagger:hilt-compiler:2.48.1")
}
2 changes: 2 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package campus.tech.kakao.map
import android.app.Application
import campus.tech.kakao.map.model.datasource.SharedPreferences
import com.kakao.vectormap.KakaoMapSdk
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class App : Application(){
companion object{
lateinit var sharedPreferencesManager : SharedPreferences
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/LocationModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package campus.tech.kakao.map

import android.content.Context
import androidx.room.Room
import campus.tech.kakao.map.model.datasource.LastLocationlSharedPreferences
import campus.tech.kakao.map.model.datasource.LocationApi
import campus.tech.kakao.map.model.datasource.SavedLocationDao
import campus.tech.kakao.map.model.datasource.SavedLocationDatabase
import campus.tech.kakao.map.model.repository.DefaultLocationRepository
import campus.tech.kakao.map.model.repository.DefaultSavedLocationRepository
import campus.tech.kakao.map.model.repository.LocationRepository
import campus.tech.kakao.map.model.repository.SavedLocationRepository
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
class LocationModule {
@Singleton
@Provides
fun provideLocationApi(): LocationApi {
return LocationApi()
}

@Singleton
@Provides
fun provideLastLocationlSharedPreferences(): LastLocationlSharedPreferences {
return LastLocationlSharedPreferences()
}
}
26 changes: 26 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package campus.tech.kakao.map

import campus.tech.kakao.map.model.repository.DefaultLocationRepository
import campus.tech.kakao.map.model.repository.DefaultSavedLocationRepository
import campus.tech.kakao.map.model.repository.LocationRepository
import campus.tech.kakao.map.model.repository.SavedLocationRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
// ViewModelComponent로 설정해도 될까요? Repository가 ViewModel에서 쓰이니까 ViewModelComponent를 쓰면 되겠다고 생각했는데요..
// 적절한 Component를 선택하는 기준이 뭔지 모르겠습니다!
Comment on lines +16 to +17

Choose a reason for hiding this comment

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

본인이 정하기 나름대로 사용하시면 됩니다. 만약 Repository 자체가 전역에서 사용이 될 수 있다고 한다면 SingletonComponent 를 쓰는 것이 적당하겠고, 특정 ViewModel 에서만 사용이 된다면 ViewModelComponent 를 사용하시면 됩니다.

보통 이런 부분은 소규모 과제에서 체감되기 쉽지 않아요. 조금 더 많은 기능을 구현하다보면 자연스럽게 깨닫는 부분이 오지 않을까 싶어요 :)

abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindDefaultLocationRepository(impl: DefaultLocationRepository) : LocationRepository

@Binds
@Singleton
abstract fun bindDefaultSavedLocationRepository(impl: DefaultSavedLocationRepository) : SavedLocationRepository
}
39 changes: 39 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/SavedLocationModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package campus.tech.kakao.map

import android.content.Context
import androidx.room.Room
import campus.tech.kakao.map.model.datasource.LastLocationlSharedPreferences
import campus.tech.kakao.map.model.datasource.LocationApi
import campus.tech.kakao.map.model.datasource.SavedLocationDao
import campus.tech.kakao.map.model.datasource.SavedLocationDatabase
import campus.tech.kakao.map.model.repository.DefaultLocationRepository
import campus.tech.kakao.map.model.repository.DefaultSavedLocationRepository
import campus.tech.kakao.map.model.repository.LocationRepository
import campus.tech.kakao.map.model.repository.SavedLocationRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object SavedLocationModule {
@Singleton
@Provides
fun provideSavedLocationDatabase(@ApplicationContext context: Context): SavedLocationDatabase {
return Room.databaseBuilder(
context,
SavedLocationDatabase::class.java,
SavedLocationDatabase.DATABASE_NAME
).build()
}

@Singleton
@Provides
fun provideSavedLocationDao(savedLocationDatabase: SavedLocationDatabase): SavedLocationDao {
return savedLocationDatabase.savedLocationDao()
}
}
17 changes: 0 additions & 17 deletions app/src/main/java/campus/tech/kakao/map/model/Contract.kt

This file was deleted.

8 changes: 6 additions & 2 deletions app/src/main/java/campus/tech/kakao/map/model/Location.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package campus.tech.kakao.map.model

import java.io.Serializable

data class Location(
val id: Long,
val title: String,
val address: String,
val category: String,
val longitude: Double,
val latitude: Double
){
): Serializable
{
companion object {
fun LocationDto.toLocation(): Location {
return Location(title, address, category, x.toDouble(), y.toDouble())
return Location(id.toLong(), title, address, category, x.toDouble(), y.toDouble())

}
}
Expand Down
40 changes: 0 additions & 40 deletions app/src/main/java/campus/tech/kakao/map/model/LocationDbHelper.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package campus.tech.kakao.map.model

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "savedLocations")
data class SavedLocation(
@PrimaryKey(autoGenerate = true)
val id: Long = 0,
val title: String
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package campus.tech.kakao.map.model.repository
package campus.tech.kakao.map.model.datasource

import campus.tech.kakao.map.model.SearchFromKeywordResponse
import retrofit2.Response
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package campus.tech.kakao.map.model.datasource

import campus.tech.kakao.map.App
import campus.tech.kakao.map.model.Location

class LastLocationlSharedPreferences() {

fun putLastLocation(location: Location) {
App.sharedPreferencesManager.putString("id", location.id.toString())
App.sharedPreferencesManager.putString("longitude", location.longitude.toString())
App.sharedPreferencesManager.putString("latitude", location.latitude.toString())
App.sharedPreferencesManager.putString("title", location.title.toString())
App.sharedPreferencesManager.putString("address", location.address.toString())
App.sharedPreferencesManager.putString("category", location.category.toString())
Comment on lines +9 to +14

Choose a reason for hiding this comment

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

SharedPreferences 도 App 에 직접 접근하는 것이 아닌, hilt 를 통해서 주입받을 수 있도록 해주세요!

Choose a reason for hiding this comment

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

또한 DataStore 로 대체하는 작업을 해보시는 것은 어떨까요?

}

fun getLastLocation(): Location? {
val id = App.sharedPreferencesManager.getString("id", "").toString()
if(id == "") return null
val title = App.sharedPreferencesManager.getString("title", "")
val longitude = App.sharedPreferencesManager.getString("longitude", "").toString().toDouble()
val latitude = App.sharedPreferencesManager.getString("latitude", "").toString().toDouble()
val address = App.sharedPreferencesManager.getString("address", "").toString()
val category = App.sharedPreferencesManager.getString("category", "").toString()
return Location(id.toLong(), title, address, category, longitude, latitude)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package campus.tech.kakao.map.model.datasource

import campus.tech.kakao.map.model.SearchFromKeywordResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class LocationApi {

Choose a reason for hiding this comment

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

또한 LocationApi 는 kakao api 라는 remote data source 를 사용하는 것이니, LocationRemoteDataSource 정도로 사용해보시면 어떨까요?

companion object{
private const val RESULT_SIZE = 15
}

private val client = RetrofitInstance.getInstance().create(KakaoAPI::class.java)

Choose a reason for hiding this comment

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

KakaoApi 를 주입받으면 조금 더 좋은 코드가 될 수 있겠네요 :)


suspend fun getLocations(keyword: String): SearchFromKeywordResponse? {
return withContext(Dispatchers.IO){

Choose a reason for hiding this comment

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

dispatchers 또한 하나의 주입을 받는 대상이라고 인식하시는 것이 조금 더 좋습니다!

https://developer.android.com/kotlin/coroutines/coroutines-best-practices

client.searchFromKeyword(keyword, RESULT_SIZE).body()
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package campus.tech.kakao.map.model.repository
package campus.tech.kakao.map.model.datasource

import android.os.Looper
import campus.tech.kakao.map.BuildConfig
Expand Down
Loading