diff --git a/README.md b/README.md index f091e3fc..c05db8f0 100644 --- a/README.md +++ b/README.md @@ -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 테스트 - - 지도 에러 화면 테스트 \ No newline at end of file +# android-map-refactoring +## 기능 목록 +- 데이터베이스를 Room으로 변경한다. +- Hilt를 사용하여 의존성 주입을 적용한다. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 90173142..db9a5559 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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 { @@ -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") } diff --git a/app/src/main/java/campus/tech/kakao/map/App.kt b/app/src/main/java/campus/tech/kakao/map/App.kt index 5f688ad8..c8a6b838 100644 --- a/app/src/main/java/campus/tech/kakao/map/App.kt +++ b/app/src/main/java/campus/tech/kakao/map/App.kt @@ -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 diff --git a/app/src/main/java/campus/tech/kakao/map/LocationModule.kt b/app/src/main/java/campus/tech/kakao/map/LocationModule.kt new file mode 100644 index 00000000..3abe72f3 --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/LocationModule.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/RepositoryModule.kt b/app/src/main/java/campus/tech/kakao/map/RepositoryModule.kt new file mode 100644 index 00000000..42160a4d --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/RepositoryModule.kt @@ -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를 선택하는 기준이 뭔지 모르겠습니다! +abstract class RepositoryModule { + @Binds + @Singleton + abstract fun bindDefaultLocationRepository(impl: DefaultLocationRepository) : LocationRepository + + @Binds + @Singleton + abstract fun bindDefaultSavedLocationRepository(impl: DefaultSavedLocationRepository) : SavedLocationRepository +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/SavedLocationModule.kt b/app/src/main/java/campus/tech/kakao/map/SavedLocationModule.kt new file mode 100644 index 00000000..6aff4c39 --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/SavedLocationModule.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/Contract.kt b/app/src/main/java/campus/tech/kakao/map/model/Contract.kt deleted file mode 100644 index ffe4a09b..00000000 --- a/app/src/main/java/campus/tech/kakao/map/model/Contract.kt +++ /dev/null @@ -1,17 +0,0 @@ -package campus.tech.kakao.map.model - -import android.provider.BaseColumns - -object Contract { - object LocationEntry: BaseColumns{ - const val TABLE_NAME = "locations" - const val COLUMN_NAME_TITLE = "name" - const val COLUMN_NAME_ADDRESS = "address" - const val COLUMN_NAME_CATEGORY = "category" - } - - object SavedLocationEntry: BaseColumns{ - const val TABLE_NAME = "saved_locations" - const val COLUMN_NAME_TITLE = "name" - } -} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/Location.kt b/app/src/main/java/campus/tech/kakao/map/model/Location.kt index a43c7125..f97c2093 100644 --- a/app/src/main/java/campus/tech/kakao/map/model/Location.kt +++ b/app/src/main/java/campus/tech/kakao/map/model/Location.kt @@ -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()) } } diff --git a/app/src/main/java/campus/tech/kakao/map/model/LocationDbHelper.kt b/app/src/main/java/campus/tech/kakao/map/model/LocationDbHelper.kt deleted file mode 100644 index b74b29ac..00000000 --- a/app/src/main/java/campus/tech/kakao/map/model/LocationDbHelper.kt +++ /dev/null @@ -1,40 +0,0 @@ -package campus.tech.kakao.map.model - -import android.content.Context -import android.database.sqlite.SQLiteDatabase -import android.database.sqlite.SQLiteOpenHelper -import campus.tech.kakao.map.model.Contract.LocationEntry -import campus.tech.kakao.map.model.Contract.SavedLocationEntry -class LocationDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { - - private val SQL_CREATE_LOCATION_TABLE = - "CREATE TABLE ${LocationEntry.TABLE_NAME} (" + - "${LocationEntry.COLUMN_NAME_TITLE} TEXT primary key," + - "${LocationEntry.COLUMN_NAME_ADDRESS} TEXT,"+ - "${LocationEntry.COLUMN_NAME_CATEGORY} TEXT"+ - ")" - - private val SQL_DELETE_LOCATION_TABLE = "DROP TABLE IF EXISTS ${LocationEntry.TABLE_NAME}" - - private val SQL_CREATE_SAVED_LOCATION_TABLE = - "CREATE TABLE ${SavedLocationEntry.TABLE_NAME} (" + - "${SavedLocationEntry.COLUMN_NAME_TITLE} TEXT primary key"+ - ")" - - private val SQL_DELETE_SAVED_LOCATION_TABLE = - "DROP TABLE IF EXISTS ${SavedLocationEntry.TABLE_NAME}" - - override fun onCreate(db: SQLiteDatabase) { - db.execSQL(SQL_CREATE_LOCATION_TABLE) - db.execSQL(SQL_CREATE_SAVED_LOCATION_TABLE) - } - override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { - db.execSQL(SQL_DELETE_LOCATION_TABLE) - db.execSQL(SQL_DELETE_SAVED_LOCATION_TABLE) - onCreate(db) - } - companion object { - const val DATABASE_VERSION = 1 - const val DATABASE_NAME = "map.db" - } -} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/SavedLocation.kt b/app/src/main/java/campus/tech/kakao/map/model/SavedLocation.kt index d09e0cb2..32ab8e81 100644 --- a/app/src/main/java/campus/tech/kakao/map/model/SavedLocation.kt +++ b/app/src/main/java/campus/tech/kakao/map/model/SavedLocation.kt @@ -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 ) \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/repository/KakaoAPI.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/KakaoAPI.kt similarity index 89% rename from app/src/main/java/campus/tech/kakao/map/model/repository/KakaoAPI.kt rename to app/src/main/java/campus/tech/kakao/map/model/datasource/KakaoAPI.kt index 6e2b2dac..05d7d10f 100644 --- a/app/src/main/java/campus/tech/kakao/map/model/repository/KakaoAPI.kt +++ b/app/src/main/java/campus/tech/kakao/map/model/datasource/KakaoAPI.kt @@ -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 diff --git a/app/src/main/java/campus/tech/kakao/map/model/datasource/LastLocationlDataSource.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/LastLocationlDataSource.kt deleted file mode 100644 index e2013e90..00000000 --- a/app/src/main/java/campus/tech/kakao/map/model/datasource/LastLocationlDataSource.kt +++ /dev/null @@ -1,27 +0,0 @@ -package campus.tech.kakao.map.model.datasource - -import campus.tech.kakao.map.App -import campus.tech.kakao.map.model.Location - -class LastLocationlDataSource() { - - fun putLastLocation(location: Location) { - if (location != null) { - 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()) - } - } - - fun getLastLocation(): Location? { - val title = App.sharedPreferencesManager.getString("title", "") - if(title == "") return null - 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(title, address, category, longitude, latitude) - } -} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/datasource/LastLocationlSharedPreferences.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/LastLocationlSharedPreferences.kt new file mode 100644 index 00000000..3b9fefcb --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/model/datasource/LastLocationlSharedPreferences.kt @@ -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()) + } + + 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/datasource/LocationApi.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/LocationApi.kt new file mode 100644 index 00000000..398d7b98 --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/model/datasource/LocationApi.kt @@ -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 { + companion object{ + private const val RESULT_SIZE = 15 + } + + private val client = RetrofitInstance.getInstance().create(KakaoAPI::class.java) + + suspend fun getLocations(keyword: String): SearchFromKeywordResponse? { + return withContext(Dispatchers.IO){ + client.searchFromKeyword(keyword, RESULT_SIZE).body() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/datasource/LocationDataSource.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/LocationDataSource.kt deleted file mode 100644 index c2228444..00000000 --- a/app/src/main/java/campus/tech/kakao/map/model/datasource/LocationDataSource.kt +++ /dev/null @@ -1,32 +0,0 @@ -package campus.tech.kakao.map.model.datasource - -import android.util.Log -import campus.tech.kakao.map.model.Location -import campus.tech.kakao.map.model.Location.Companion.toLocation -import campus.tech.kakao.map.model.LocationDto -import campus.tech.kakao.map.model.repository.KakaoAPI -import campus.tech.kakao.map.model.repository.RetrofitInstance -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -class LocationDataSource { - companion object{ - private const val RESULT_SIZE = 15 - } - - private val client = RetrofitInstance.getInstance().create(KakaoAPI::class.java) - - suspend fun getLocations(keyword: String): List { - return withContext(Dispatchers.IO){ - val response = client.searchFromKeyword(keyword, RESULT_SIZE) - val locationDtos: List = response.body()?.documents ?: emptyList() - Log.d("jieun", "locationDtos: " + locationDtos) - toLocations(locationDtos) - } - } - - private fun toLocations(locationDtos: List) = - locationDtos.map { - it.toLocation() - } -} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/repository/RetrofitInstance.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/RetrofitInstance.kt similarity index 97% rename from app/src/main/java/campus/tech/kakao/map/model/repository/RetrofitInstance.kt rename to app/src/main/java/campus/tech/kakao/map/model/datasource/RetrofitInstance.kt index f75e11b2..733814e9 100644 --- a/app/src/main/java/campus/tech/kakao/map/model/repository/RetrofitInstance.kt +++ b/app/src/main/java/campus/tech/kakao/map/model/datasource/RetrofitInstance.kt @@ -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 diff --git a/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDao.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDao.kt new file mode 100644 index 00000000..c0428511 --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDao.kt @@ -0,0 +1,19 @@ +package campus.tech.kakao.map.model.datasource + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import campus.tech.kakao.map.model.SavedLocation + +@Dao +interface SavedLocationDao { + @Insert + suspend fun insert(savedLocation: SavedLocation): Long + + @Query("SELECT * FROM savedLocations") + suspend fun getAll(): List + + @Delete + suspend fun delete(savedLocation: SavedLocation) +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDataSource.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDataSource.kt deleted file mode 100644 index 9310c1e8..00000000 --- a/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDataSource.kt +++ /dev/null @@ -1,56 +0,0 @@ -package campus.tech.kakao.map.model.datasource - -import android.content.ContentValues -import android.util.Log -import campus.tech.kakao.map.model.Contract.SavedLocationEntry -import campus.tech.kakao.map.model.LocationDbHelper -import campus.tech.kakao.map.model.SavedLocation - -class SavedLocationDataSource(private val dbHelper : LocationDbHelper) { - - fun addSavedLocation(title: String): Long { - val db = dbHelper.writableDatabase - val values = ContentValues().apply { - put(SavedLocationEntry.COLUMN_NAME_TITLE, title) - } - Log.d("jieun", "insertSavedLocation 저장완료") - return db.insert(SavedLocationEntry.TABLE_NAME, null, values) - } - - fun getSavedLocationAll(): MutableList { - val db = dbHelper.readableDatabase - - val projection = arrayOf( - SavedLocationEntry.COLUMN_NAME_TITLE - ) - val sortOrder = "${SavedLocationEntry.COLUMN_NAME_TITLE} ASC" - val cursor = db.query( - SavedLocationEntry.TABLE_NAME, - projection, - null, - null, - null, - null, - sortOrder - ) - - val results = mutableListOf() - with(cursor) { - while (moveToNext()) { - val title = getString(getColumnIndexOrThrow(SavedLocationEntry.COLUMN_NAME_TITLE)) - results.add(SavedLocation(title)) - } - } - cursor.close() - return results - } - - fun deleteSavedLocation(title: String): Int { - val db = dbHelper.writableDatabase - - val selection = "${SavedLocationEntry.COLUMN_NAME_TITLE} = ?" - val selectionArgs = arrayOf(title) - - return db.delete(SavedLocationEntry.TABLE_NAME, selection, selectionArgs) - } -} diff --git a/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDatabase.kt b/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDatabase.kt new file mode 100644 index 00000000..a3257d6e --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/model/datasource/SavedLocationDatabase.kt @@ -0,0 +1,16 @@ +package campus.tech.kakao.map.model.datasource + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import campus.tech.kakao.map.model.SavedLocation + +@Database(entities = [SavedLocation::class], version = 1) +abstract class SavedLocationDatabase: RoomDatabase() { + abstract fun savedLocationDao(): SavedLocationDao + + companion object { + val DATABASE_NAME = "savedLocationDatabase" + } +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/repository/DefaultLocationRepository.kt b/app/src/main/java/campus/tech/kakao/map/model/repository/DefaultLocationRepository.kt new file mode 100644 index 00000000..917601bb --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/model/repository/DefaultLocationRepository.kt @@ -0,0 +1,34 @@ +package campus.tech.kakao.map.model.repository + +import campus.tech.kakao.map.model.Location +import campus.tech.kakao.map.model.Location.Companion.toLocation +import campus.tech.kakao.map.model.LocationDto +import campus.tech.kakao.map.model.datasource.LastLocationlSharedPreferences +import campus.tech.kakao.map.model.datasource.LocationApi +import javax.inject.Inject +import javax.inject.Qualifier +import javax.inject.Singleton + +@Singleton +class DefaultLocationRepository @Inject constructor( + private val locationApi: LocationApi, + private val lastLocationlSharedPreferences: LastLocationlSharedPreferences +): LocationRepository { + override suspend fun getLocationAll(query: String): List { + val searchFromKeywordResponse = locationApi.getLocations(query) + val locationDtos: List = searchFromKeywordResponse?.documents ?: emptyList() + return toLocations(locationDtos) + } + + private fun toLocations(locationDtos: List) = + locationDtos.map { + it.toLocation() + } + override fun addLastLocation(location: Location){ + lastLocationlSharedPreferences.putLastLocation(location) + } + + override fun getLastLocation(): Location? { + return lastLocationlSharedPreferences.getLastLocation() + } +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/repository/DefaultSavedLocationRepository.kt b/app/src/main/java/campus/tech/kakao/map/model/repository/DefaultSavedLocationRepository.kt new file mode 100644 index 00000000..7319cb1f --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/model/repository/DefaultSavedLocationRepository.kt @@ -0,0 +1,24 @@ +package campus.tech.kakao.map.model.repository + +import campus.tech.kakao.map.model.SavedLocation +import campus.tech.kakao.map.model.datasource.SavedLocationDao +import campus.tech.kakao.map.model.datasource.SavedLocationDatabase +import javax.inject.Inject + +class DefaultSavedLocationRepository @Inject constructor( + private val savedLocationDao: SavedLocationDao +): SavedLocationRepository { + override suspend fun getSavedLocationAll(): MutableList { + val results = savedLocationDao.getAll() + return if(results.isNotEmpty()) results.toMutableList() else mutableListOf() + } + + override suspend fun addSavedLocation(savedLocation: SavedLocation) { + savedLocationDao.insert(savedLocation) + } + + override suspend fun deleteSavedLocation(savedLocation: SavedLocation) { + savedLocationDao.delete(savedLocation) + } + +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/repository/LastLocationRepository.kt b/app/src/main/java/campus/tech/kakao/map/model/repository/LastLocationRepository.kt deleted file mode 100644 index 01024b00..00000000 --- a/app/src/main/java/campus/tech/kakao/map/model/repository/LastLocationRepository.kt +++ /dev/null @@ -1,16 +0,0 @@ -package campus.tech.kakao.map.model.repository - -import campus.tech.kakao.map.model.Location -import campus.tech.kakao.map.model.datasource.LastLocationlDataSource - -class LastLocationRepository( - private val locationLocalDataSource: LastLocationlDataSource -) { - fun putLastLocation(location: Location){ - locationLocalDataSource.putLastLocation(location) - } - - fun getLastLocation(): Location? { - return locationLocalDataSource.getLastLocation() - } -} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/repository/LocationRepository.kt b/app/src/main/java/campus/tech/kakao/map/model/repository/LocationRepository.kt index b8846592..9f91916e 100644 --- a/app/src/main/java/campus/tech/kakao/map/model/repository/LocationRepository.kt +++ b/app/src/main/java/campus/tech/kakao/map/model/repository/LocationRepository.kt @@ -1,12 +1,9 @@ package campus.tech.kakao.map.model.repository import campus.tech.kakao.map.model.Location -import campus.tech.kakao.map.model.datasource.LocationDataSource -class LocationRepository( - private val locationRemoteDataSource: LocationDataSource -) { - suspend fun getLocationRemote(query: String): List { - return locationRemoteDataSource.getLocations(query) - } +interface LocationRepository { + suspend fun getLocationAll(query: String): List + fun addLastLocation(location: Location) + fun getLastLocation(): Location? } \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/model/repository/SavedLocationRepository.kt b/app/src/main/java/campus/tech/kakao/map/model/repository/SavedLocationRepository.kt index e4996d97..8f5203f3 100644 --- a/app/src/main/java/campus/tech/kakao/map/model/repository/SavedLocationRepository.kt +++ b/app/src/main/java/campus/tech/kakao/map/model/repository/SavedLocationRepository.kt @@ -1,25 +1,9 @@ package campus.tech.kakao.map.model.repository import campus.tech.kakao.map.model.SavedLocation -import campus.tech.kakao.map.model.datasource.SavedLocationDataSource - -class SavedLocationRepository( - private val locationLocalRepository: SavedLocationDataSource -) { - fun getSavedLocationAll(): MutableList { - val results = locationLocalRepository.getSavedLocationAll() - return if(results.isNotEmpty()) results else mutableListOf() - } - - fun addSavedLocation(title: String) { - locationLocalRepository.addSavedLocation(title) - } - - fun deleteSavedLocation(title: String): Boolean { - if (locationLocalRepository.deleteSavedLocation(title) == 1) { - return true - } - return false - } +interface SavedLocationRepository { + suspend fun getSavedLocationAll(): MutableList + suspend fun addSavedLocation(savedLocation: SavedLocation) + suspend fun deleteSavedLocation(savedLocation: SavedLocation) } \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/view/map/MapActivity.kt b/app/src/main/java/campus/tech/kakao/map/view/map/MapActivity.kt index c1f66493..d40a9b7c 100644 --- a/app/src/main/java/campus/tech/kakao/map/view/map/MapActivity.kt +++ b/app/src/main/java/campus/tech/kakao/map/view/map/MapActivity.kt @@ -7,13 +7,14 @@ import android.util.Log import android.view.View import android.widget.EditText import android.widget.TextView +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import campus.tech.kakao.map.R import campus.tech.kakao.map.model.Location -import campus.tech.kakao.map.model.datasource.LastLocationlDataSource -import campus.tech.kakao.map.model.repository.LastLocationRepository +import campus.tech.kakao.map.model.datasource.LastLocationlSharedPreferences import campus.tech.kakao.map.view.search.MainActivity +import campus.tech.kakao.map.viewmodel.LocationViewModel import com.google.android.material.bottomsheet.BottomSheetBehavior import com.kakao.vectormap.KakaoMap import com.kakao.vectormap.KakaoMapReadyCallback @@ -23,9 +24,13 @@ import com.kakao.vectormap.MapView import com.kakao.vectormap.label.LabelOptions import com.kakao.vectormap.label.LabelStyle import com.kakao.vectormap.label.LabelStyles +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject - +@AndroidEntryPoint class MapActivity : AppCompatActivity() { + private val locationViewModel: LocationViewModel by viewModels() + private val searchEditText by lazy { findViewById(R.id.SearchEditTextInMap) } private val mapView by lazy { findViewById(R.id.map_view) } private val bottomSheetLayout by lazy { findViewById(R.id.bottom_sheet_layout) } @@ -34,9 +39,6 @@ class MapActivity : AppCompatActivity() { private val errorMessageTextView by lazy { findViewById(R.id.errorMessageTextView) } private val bottomSheetBehavior: BottomSheetBehavior by lazy { BottomSheetBehavior.from(bottomSheetLayout) } - private val lastLocationLocalDataSource: LastLocationlDataSource by lazy { LastLocationlDataSource() } - private val lastLocationRepository: LastLocationRepository by lazy { LastLocationRepository(lastLocationLocalDataSource) } - companion object{ private val DEFAULT_LONGITUDE = 127.115587 private val DEFAULT_LATITUDE = 37.406960 @@ -78,14 +80,13 @@ class MapActivity : AppCompatActivity() { showErrorMessage(error) } }, object : KakaoMapReadyCallback() { - val location = getCoordinates() + val location = getLocation() override fun onMapReady(kakaoMap: KakaoMap) { // 인증 후 API 가 정상적으로 실행될 때 호출됨 - Log.d("jieun", "onMapReady coordinates: " + location.toString()) + Log.d("jieun", "onMapReady location: " + location.toString()) if (location != null) { showLabel(location, kakaoMap) showBottomSheet(location) - lastLocationRepository.putLastLocation(location) -// Log.d("jieun", "onMapReady setSharedData: " + getSharedData("pref")) + locationViewModel.addLastLocation(location) } else{ hideBottomSheet() } @@ -139,26 +140,21 @@ class MapActivity : AppCompatActivity() { bottom_sheet_address.text = location.address } - private fun getCoordinates(): Location? { - var location = getCoordinatesByIntent() + private fun getLocation(): Location? { + var location = getLocationByIntent() if(location == null) { - location = lastLocationRepository.getLastLocation() + location = locationViewModel.getLastLocation() } return location } - private fun getCoordinatesByIntent(): Location? { - if (intent.hasExtra("title") && intent.hasExtra("longitude") - && intent.hasExtra("latitude") && intent.hasExtra("address")) { - val title = intent.getStringExtra("title") - val longitude = intent.getDoubleExtra("longitude", 0.0) - val latitude = intent.getDoubleExtra("latitude", 0.0) - val address = intent.getStringExtra("address").toString() - val category = intent.getStringExtra("category").toString() - if (title != null) { - return Location(title, address, category, longitude, latitude) - } else return null - } else return null + private fun getLocationByIntent(): Location? { + if (intent.hasExtra("location")) { + val location = intent.getSerializableExtra("location") as Location + Log.d("jieun","getLocationByIntent location "+location.toString()) + return location + } + return null } } \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/view/search/MainActivity.kt b/app/src/main/java/campus/tech/kakao/map/view/search/MainActivity.kt index f0d1bca5..30e83964 100644 --- a/app/src/main/java/campus/tech/kakao/map/view/search/MainActivity.kt +++ b/app/src/main/java/campus/tech/kakao/map/view/search/MainActivity.kt @@ -8,49 +8,37 @@ import android.view.View import android.widget.EditText import android.widget.ImageView import android.widget.TextView +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import campus.tech.kakao.map.model.datasource.SavedLocationDataSource -import campus.tech.kakao.map.model.datasource.LocationDataSource +import campus.tech.kakao.map.model.datasource.LocationApi import campus.tech.kakao.map.R import campus.tech.kakao.map.model.Location import campus.tech.kakao.map.model.SavedLocation -import campus.tech.kakao.map.model.LocationDbHelper +import campus.tech.kakao.map.model.datasource.SavedLocationDatabase +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 campus.tech.kakao.map.view.map.MapActivity -import campus.tech.kakao.map.viewmodel.ViewModelFactory.LocationViewModelFactory -import campus.tech.kakao.map.viewmodel.ViewModelFactory.SavedLocationViewModelFactory import campus.tech.kakao.map.viewmodel.LocationViewModel import campus.tech.kakao.map.viewmodel.SavedLocationViewModel +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject +@AndroidEntryPoint class MainActivity : AppCompatActivity(), OnItemSelectedListener { - private val locationViewModel: LocationViewModel by lazy { - ViewModelProvider(this, LocationViewModelFactory(locationRepository)) - .get(LocationViewModel::class.java) - } + private val locationViewModel: LocationViewModel by viewModels() + private val savedLocationViewModel: SavedLocationViewModel by viewModels() + + private val locationAdapter: LocationAdapter by lazy { LocationAdapter(this) } private val locationRecyclerView: RecyclerView by lazy { findViewById(R.id.locationRecyclerView) } - private val savedLocationViewModel: SavedLocationViewModel by lazy { - ViewModelProvider(this, SavedLocationViewModelFactory(savedLocationRepository)) - .get(SavedLocationViewModel::class.java) - } - private val savedLocationAdapter: SavedLocationAdapter by lazy { SavedLocationAdapter(this) } private val savedLocationRecyclerView: RecyclerView by lazy { findViewById(R.id.savedLocationRecyclerView) } - - private val locationDbHelper: LocationDbHelper by lazy { LocationDbHelper(this) } - private val locationLocalDataSource: SavedLocationDataSource by lazy { SavedLocationDataSource(locationDbHelper) } - private val locationRemoteDataSource: LocationDataSource by lazy { LocationDataSource() } - private val locationRepository: LocationRepository by lazy { LocationRepository(locationRemoteDataSource) } - private val savedLocationRepository: SavedLocationRepository by lazy { SavedLocationRepository(locationLocalDataSource) } - private val clearButton: ImageView by lazy { findViewById(R.id.clearButton) } private val searchEditText: EditText by lazy { findViewById(R.id.SearchEditTextInMain) } private val noResultTextView: TextView by lazy { findViewById(R.id.NoResultTextView) } @@ -122,19 +110,15 @@ class MainActivity : AppCompatActivity(), OnItemSelectedListener { } override fun onLocationViewClicked(location: Location) { - savedLocationViewModel.addSavedLocation(location.title) + savedLocationViewModel.addSavedLocation(location.id, location.title) val intent = Intent(this@MainActivity, MapActivity::class.java) - intent.putExtra("title", location.title) - intent.putExtra("address", location.address) - intent.putExtra("category", location.category) - intent.putExtra("longitude", location.longitude) - intent.putExtra("latitude", location.latitude) + intent.putExtra("location", location) startActivity(intent) } - override fun onSavedLocationXButtonClicked(item: SavedLocation) { - savedLocationViewModel.deleteSavedLocation(item) + override fun onSavedLocationXButtonClicked(savedLocation: SavedLocation) { + savedLocationViewModel.deleteSavedLocation(savedLocation) } override fun onSavedLocationViewClicked(title: String) { diff --git a/app/src/main/java/campus/tech/kakao/map/view/search/SavedLocationAdapter.kt b/app/src/main/java/campus/tech/kakao/map/view/search/SavedLocationAdapter.kt index 97335afb..f852c358 100644 --- a/app/src/main/java/campus/tech/kakao/map/view/search/SavedLocationAdapter.kt +++ b/app/src/main/java/campus/tech/kakao/map/view/search/SavedLocationAdapter.kt @@ -1,5 +1,6 @@ package campus.tech.kakao.map.view.search +import android.util.Log import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -17,7 +18,7 @@ class SavedLocationAdapter( ) : ListAdapter( object: DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: SavedLocation, newItem: SavedLocation): Boolean { - return oldItem.title == newItem.title + return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: SavedLocation, newItem: SavedLocation): Boolean { return oldItem == newItem @@ -39,7 +40,8 @@ class SavedLocationAdapter( itemSelectedListener.onSavedLocationViewClicked(getItem(bindingAdapterPosition).title) } savedLocationXButton.setOnClickListener { - itemSelectedListener.onSavedLocationXButtonClicked(getItem(bindingAdapterPosition) as SavedLocation) + val savedLocation = getItem(bindingAdapterPosition) as SavedLocation + itemSelectedListener.onSavedLocationXButtonClicked(savedLocation) } } } diff --git a/app/src/main/java/campus/tech/kakao/map/viewmodel/LocationViewModel.kt b/app/src/main/java/campus/tech/kakao/map/viewmodel/LocationViewModel.kt index 5e4b8ca6..1709e627 100644 --- a/app/src/main/java/campus/tech/kakao/map/viewmodel/LocationViewModel.kt +++ b/app/src/main/java/campus/tech/kakao/map/viewmodel/LocationViewModel.kt @@ -5,10 +5,14 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import campus.tech.kakao.map.model.Location +import campus.tech.kakao.map.model.repository.DefaultLocationRepository import campus.tech.kakao.map.model.repository.LocationRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import javax.inject.Inject -class LocationViewModel( +@HiltViewModel +class LocationViewModel @Inject constructor( private val locationRepository: LocationRepository ) : ViewModel() { private val _locations = MutableLiveData>() @@ -27,9 +31,16 @@ class LocationViewModel( fun searchLocationsFromKakaoAPI(query: String, handleNoResultMessage: (Int) -> Unit) { viewModelScope.launch { - _searchedLocations.value = locationRepository.getLocationRemote(query) + _searchedLocations.value = locationRepository.getLocationAll(query) handleNoResultMessage(getSearchedLocationsSize()) } } + fun addLastLocation(location: Location){ + locationRepository.addLastLocation(location) + } + + fun getLastLocation(): Location? { + return locationRepository.getLastLocation() + } } \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/viewmodel/SavedLocationViewModel.kt b/app/src/main/java/campus/tech/kakao/map/viewmodel/SavedLocationViewModel.kt index 4d193c09..d35ff11c 100644 --- a/app/src/main/java/campus/tech/kakao/map/viewmodel/SavedLocationViewModel.kt +++ b/app/src/main/java/campus/tech/kakao/map/viewmodel/SavedLocationViewModel.kt @@ -3,35 +3,49 @@ package campus.tech.kakao.map.viewmodel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import campus.tech.kakao.map.model.SavedLocation +import campus.tech.kakao.map.model.repository.DefaultSavedLocationRepository import campus.tech.kakao.map.model.repository.SavedLocationRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject -class SavedLocationViewModel( +@HiltViewModel +class SavedLocationViewModel @Inject constructor( private val savedLocationRepository: SavedLocationRepository ) : ViewModel() { private val _savedLocation = MutableLiveData>() val savedLocation: LiveData> get() = _savedLocation fun setSavedLocation() { - _savedLocation.value = savedLocationRepository.getSavedLocationAll() + viewModelScope.launch { + _savedLocation.value = savedLocationRepository.getSavedLocationAll() + } } - fun addSavedLocation(title: String) { - val savedLocation = SavedLocation(title) - if(_savedLocation.value?.contains(savedLocation) == false){ - savedLocationRepository.addSavedLocation(title) - val currentList = _savedLocation.value ?: return - if (currentList.add(savedLocation)) { - _savedLocation.value = currentList + fun addSavedLocation(id: Long, title: String) { + val savedLocation = SavedLocation(id, title) + if (_savedLocation.value?.contains(savedLocation) == false) { + viewModelScope.launch { + savedLocationRepository.addSavedLocation(savedLocation) + val currentList = _savedLocation.value + if (currentList != null) { + currentList.add(savedLocation) + _savedLocation.value = currentList + } } } } - fun deleteSavedLocation(savedLocation: SavedLocation) { - if (savedLocationRepository.deleteSavedLocation(savedLocation.title)) { - val currentList = _savedLocation.value ?: return - if (currentList.remove(savedLocation)) { + fun deleteSavedLocation(savedLocation: SavedLocation) { + viewModelScope.launch { + savedLocationRepository.deleteSavedLocation(savedLocation) + val currentList = _savedLocation.value + if (currentList != null) { + currentList.remove(savedLocation) _savedLocation.value = currentList } } + } } diff --git a/app/src/main/java/campus/tech/kakao/map/viewmodel/ViewModelFactory.kt b/app/src/main/java/campus/tech/kakao/map/viewmodel/ViewModelFactory.kt deleted file mode 100644 index 4667404c..00000000 --- a/app/src/main/java/campus/tech/kakao/map/viewmodel/ViewModelFactory.kt +++ /dev/null @@ -1,33 +0,0 @@ -package campus.tech.kakao.map.viewmodel - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.CreationExtras -import campus.tech.kakao.map.model.repository.LocationRepository -import campus.tech.kakao.map.model.repository.SavedLocationRepository - -class ViewModelFactory { - class LocationViewModelFactory( - private val locationRepository: LocationRepository - ) : ViewModelProvider.Factory { - override fun create(modelClass: Class, extras: CreationExtras): T { - if (modelClass.isAssignableFrom(LocationViewModel::class.java)) { - return LocationViewModel(locationRepository) as T - } else { - throw IllegalArgumentException() - } - } - } - - class SavedLocationViewModelFactory( - private val savedLocationRepository: SavedLocationRepository - ) : ViewModelProvider.Factory { - override fun create(modelClass: Class, extras: CreationExtras): T { - if (modelClass.isAssignableFrom(SavedLocationViewModel::class.java)) { - return SavedLocationViewModel(savedLocationRepository) as T - } else { - throw IllegalArgumentException() - } - } - } -} diff --git a/app/src/test/java/campus/tech/kakao/map/SavedLocationDataSourceTest.kt b/app/src/test/java/campus/tech/kakao/map/SavedLocationApiTest.kt similarity index 93% rename from app/src/test/java/campus/tech/kakao/map/SavedLocationDataSourceTest.kt rename to app/src/test/java/campus/tech/kakao/map/SavedLocationApiTest.kt index 4726ee2c..f0224a09 100644 --- a/app/src/test/java/campus/tech/kakao/map/SavedLocationDataSourceTest.kt +++ b/app/src/test/java/campus/tech/kakao/map/SavedLocationApiTest.kt @@ -3,9 +3,7 @@ package campus.tech.kakao.map import android.database.sqlite.SQLiteDatabase import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import campus.tech.kakao.map.model.LocationDbHelper import campus.tech.kakao.map.model.SavedLocation -import campus.tech.kakao.map.model.datasource.SavedLocationDataSource import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before @@ -13,7 +11,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class SavedLocationDataSourceTest { +class SavedLocationApiTest { private lateinit var locationLocalDataSource: SavedLocationDataSource private lateinit var locationDbHelper: LocationDbHelper private lateinit var sqLiteDatabase: SQLiteDatabase diff --git a/build.gradle.kts b/build.gradle.kts index 6465d171..d6b55841 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ plugins { id("com.android.application") version "8.3.1" apply false id("org.jetbrains.kotlin.android") version "1.9.0" apply false id("org.jlleitschuh.gradle.ktlint") version "12.1.0" apply false + id("com.google.dagger.hilt.android") version "2.48.1" apply false } allprojects {