diff --git a/README.md b/README.md
index 91dbd079..5fff9842 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,8 @@
# android-map-location
+## 1단계 기능 구현 목록
+- [x] 저장된 검색어 선택 시 검색 결과 표시
+- [x] 검색 목록에서 항목 선택 시 해당 항목의 위치 표시
+ - [x] 핀과 이름을 표시
+ - [x] BottomSheet를 통해 정보 표시
+- [x] 앱 종료 시 마지막 위치로 이동
+- [x] 맵을 불러오는데 실패하였을 때 대응 화면 표시
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 250fba37..17a77e31 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -57,6 +57,7 @@ dependencies {
implementation("com.kakao.maps.open:android:2.9.5")
implementation("androidx.activity:activity:1.8.0")
implementation("androidx.test:core-ktx:1.5.0")
+ implementation("androidx.activity:activity-ktx:1.9.0")
testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk-android:1.13.11")
testImplementation("io.mockk:mockk-agent:1.13.11")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e1d5170f..e921fd5c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,11 +17,11 @@
android:theme="@style/Theme.Map"
tools:targetApi="31">
diff --git a/app/src/main/java/campus/tech/kakao/map/DatabaseListener.kt b/app/src/main/java/campus/tech/kakao/map/DatabaseListener.kt
index 463815b4..346724e2 100644
--- a/app/src/main/java/campus/tech/kakao/map/DatabaseListener.kt
+++ b/app/src/main/java/campus/tech/kakao/map/DatabaseListener.kt
@@ -1,6 +1,11 @@
package campus.tech.kakao.map
+import campus.tech.kakao.map.domain.model.Location
+
interface DatabaseListener {
- fun deleteHistory(historyName: String)
- fun insertHistory(historyName: String)
+ fun deleteHistory(oldHistory: Location)
+ fun insertHistory(newHistory: Location)
+ fun searchHistory(locName: String, isExactMatch: Boolean)
+ fun showMap(newHistory: Location)
+ fun insertLastLocation(location: Location)
}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/Location.kt b/app/src/main/java/campus/tech/kakao/map/Location.kt
deleted file mode 100644
index 0d893493..00000000
--- a/app/src/main/java/campus/tech/kakao/map/Location.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package campus.tech.kakao.map
-
-data class Location(
- val name: String,
- val category: String,
- val address: String
-) {
- companion object {
- const val CAFE: String = "카페"
- const val PHARMACY: String = "약국"
- const val RESTAURANT: String = "식당"
- const val NORMAL: String = "일반"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/MapActivity.kt b/app/src/main/java/campus/tech/kakao/map/MapActivity.kt
deleted file mode 100644
index ed89ee3e..00000000
--- a/app/src/main/java/campus/tech/kakao/map/MapActivity.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package campus.tech.kakao.map
-
-import android.content.Intent
-import android.os.Bundle
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import com.kakao.vectormap.KakaoMap
-import com.kakao.vectormap.KakaoMapReadyCallback
-import com.kakao.vectormap.MapLifeCycleCallback
-import com.kakao.vectormap.MapView
-import java.lang.Exception
-
-class MapActivity : AppCompatActivity() {
- private lateinit var kakaoMap: MapView
- private lateinit var searchBox: TextView
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_map)
- kakaoMap = findViewById(R.id.kakao_map)
- searchBox = findViewById(R.id.search_box)
-
- kakaoMap.start(object : MapLifeCycleCallback() {
- override fun onMapDestroy() {
- }
-
- override fun onMapError(p0: Exception?) {
- }
-
- }, object : KakaoMapReadyCallback(){
- override fun onMapReady(p0: KakaoMap) {
- }
- })
-
- searchBox.setOnClickListener {
- startActivity(Intent(this, SearchActivity::class.java))
- }
- }
-
- override fun onResume() {
- super.onResume()
- kakaoMap.resume()
- }
-
- override fun onPause() {
- super.onPause()
- kakaoMap.pause()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/MapApplication.kt b/app/src/main/java/campus/tech/kakao/map/MapApplication.kt
index ea0b1006..f86a2d1e 100644
--- a/app/src/main/java/campus/tech/kakao/map/MapApplication.kt
+++ b/app/src/main/java/campus/tech/kakao/map/MapApplication.kt
@@ -1,11 +1,25 @@
package campus.tech.kakao.map
import android.app.Application
+import campus.tech.kakao.map.data.repository.HistoryRepositoryImpl
+import campus.tech.kakao.map.data.repository.LastLocationRepositoryImpl
+import campus.tech.kakao.map.data.repository.ResultRepositoryImpl
+import campus.tech.kakao.map.data.source.MapDbHelper
+import campus.tech.kakao.map.data.source.RetrofitServiceClient
import com.kakao.vectormap.KakaoMapSdk
-class MapApplication: Application() {
+class MapApplication : Application() {
+ lateinit var dbHelper: MapDbHelper
+ lateinit var resultRepositoryImpl: ResultRepositoryImpl
+ lateinit var historyRepositoryImpl: HistoryRepositoryImpl
+ lateinit var lastLocationRepositoryImpl: LastLocationRepositoryImpl
+
override fun onCreate() {
super.onCreate()
KakaoMapSdk.init(this, getString(R.string.kakao_api_key))
+ dbHelper = MapDbHelper(this)
+ resultRepositoryImpl = ResultRepositoryImpl(RetrofitServiceClient.retrofitService)
+ historyRepositoryImpl = HistoryRepositoryImpl(dbHelper)
+ lastLocationRepositoryImpl = LastLocationRepositoryImpl(dbHelper)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/MapContract.kt b/app/src/main/java/campus/tech/kakao/map/MapContract.kt
index dd2a18c2..0d3e8b43 100644
--- a/app/src/main/java/campus/tech/kakao/map/MapContract.kt
+++ b/app/src/main/java/campus/tech/kakao/map/MapContract.kt
@@ -3,11 +3,14 @@ package campus.tech.kakao.map
import android.provider.BaseColumns
object MapContract {
- object MapEntry : BaseColumns {
- const val TABLE_NAME = "map"
+ object MapEntry {
const val TABLE_NAME_HISTORY = "history"
+ const val TABLE_NAME_LAST_LOCATION = "last_location"
+ const val COLUMN_NAME_ID = "id"
const val COLUMN_NAME_NAME = "name"
const val COLUMN_NAME_CATEGORY = "category"
const val COLUMN_NAME_ADDRESS = "address"
+ const val COLUMN_NAME_X = "x"
+ const val COLUMN_NAME_Y = "y"
}
}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/MapDbHelper.kt b/app/src/main/java/campus/tech/kakao/map/MapDbHelper.kt
deleted file mode 100644
index 03153561..00000000
--- a/app/src/main/java/campus/tech/kakao/map/MapDbHelper.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package campus.tech.kakao.map
-
-import android.content.ContentValues
-import android.content.Context
-import android.database.sqlite.SQLiteDatabase
-import android.database.sqlite.SQLiteOpenHelper
-import android.provider.BaseColumns
-
-class MapDbHelper(mContext: Context) :
- SQLiteOpenHelper(mContext, DATABASE_NAME, null, DATABASE_VERSION) {
- override fun onCreate(db: SQLiteDatabase?) {
- db?.execSQL(SQL_CREATE_ENTRIES)
- db?.execSQL(SQL_CREATE_ENTRIES_HISTORY)
-// initializeDb(db)
- }
-
- override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
- db?.execSQL(SQL_DELETE_ENTRIES)
- db?.execSQL(SQL_DELETE_ENTRIES_HISTORY)
- onCreate(db)
- }
-
- private fun initializeDb(db: SQLiteDatabase?) {
- for (idx in 1..10) {
- val exampleCafeValue = ContentValues()
- exampleCafeValue.put(MapContract.MapEntry.COLUMN_NAME_NAME, "카페$idx")
- exampleCafeValue.put(MapContract.MapEntry.COLUMN_NAME_CATEGORY, Location.CAFE)
- exampleCafeValue.put(MapContract.MapEntry.COLUMN_NAME_ADDRESS, "서울 성동구 성수동 $idx")
- db?.insert(MapContract.MapEntry.TABLE_NAME, null, exampleCafeValue)
-
- val examplePharValue = ContentValues()
- examplePharValue.put(MapContract.MapEntry.COLUMN_NAME_NAME, "약국$idx")
- examplePharValue.put(MapContract.MapEntry.COLUMN_NAME_CATEGORY, Location.PHARMACY)
- examplePharValue.put(MapContract.MapEntry.COLUMN_NAME_ADDRESS, "서울 성동구 성수동 $idx")
- db?.insert(MapContract.MapEntry.TABLE_NAME, null, examplePharValue)
- }
- }
-
- fun clearDb(db: SQLiteDatabase?) {
- db?.execSQL(SQL_DELETE_ENTRIES)
- db?.execSQL(SQL_CREATE_ENTRIES)
- }
-
- companion object {
- const val DATABASE_NAME = "map.db"
- const val DATABASE_VERSION = 1
-
- private const val SQL_CREATE_ENTRIES = """
- CREATE TABLE ${MapContract.MapEntry.TABLE_NAME} (
- ${BaseColumns._ID} INTEGER PRIMARY KEY AUTOINCREMENT,
- ${MapContract.MapEntry.COLUMN_NAME_NAME} TEXT,
- ${MapContract.MapEntry.COLUMN_NAME_CATEGORY} TEXT,
- ${MapContract.MapEntry.COLUMN_NAME_ADDRESS} TEXT);
- """
-
- private const val SQL_DELETE_ENTRIES =
- "DROP TABLE IF EXISTS ${MapContract.MapEntry.TABLE_NAME}"
-
- private const val SQL_CREATE_ENTRIES_HISTORY = """
- CREATE TABLE ${MapContract.MapEntry.TABLE_NAME_HISTORY} (
- ${BaseColumns._ID} INTEGER PRIMARY KEY AUTOINCREMENT,
- ${MapContract.MapEntry.COLUMN_NAME_NAME} TEXT);
- """
-
- private const val SQL_DELETE_ENTRIES_HISTORY =
- "DROP TABLE IF EXISTS ${MapContract.MapEntry.TABLE_NAME_HISTORY}"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/MapModel.kt b/app/src/main/java/campus/tech/kakao/map/MapModel.kt
deleted file mode 100644
index e1b24d5a..00000000
--- a/app/src/main/java/campus/tech/kakao/map/MapModel.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-package campus.tech.kakao.map
-
-import android.content.ContentValues
-import android.content.Context
-import android.database.Cursor
-import android.util.Log
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import retrofit2.Call
-import retrofit2.Callback
-import retrofit2.Response
-
-class MapModel(dbHelper: MapDbHelper) {
- private val helper: MapDbHelper = dbHelper
- private val retrofit: RetrofitService = RetrofitServiceClient.getRetrofit("https://dapi.kakao.com/")
-
- private val _searchResult = MutableLiveData(getAllLocation())
- val searchResult: LiveData> = _searchResult
- private val _searchHistory = MutableLiveData(getAllHistory())
- val searchHistory: LiveData> = _searchHistory
-
- fun searchByKeywordFromServer(keyword: String, isExactMatch: Boolean) {
- clearDb()
- request(keyword)
- }
-
- fun insertLocation(location: Location) {
- val writableDb = helper.writableDatabase
- val content = ContentValues()
- content.put(MapContract.MapEntry.COLUMN_NAME_NAME, location.name)
- content.put(MapContract.MapEntry.COLUMN_NAME_CATEGORY, location.category)
- content.put(MapContract.MapEntry.COLUMN_NAME_ADDRESS, location.address)
-
- writableDb.insert(MapContract.MapEntry.TABLE_NAME, null, content)
- }
-
- fun getSearchedLocation(locName: String, isExactMatch: Boolean): List {
- val readableDb = helper.readableDatabase
-
- val selection = "${MapContract.MapEntry.COLUMN_NAME_NAME} LIKE ?"
- val selectionArgs = arrayOf("%${locName}%")
- val cursor = readableDb.query(
- MapContract.MapEntry.TABLE_NAME,
- null,
- selection,
- selectionArgs,
- null,
- null,
- null
- )
- return getLocationResult(cursor)
- }
-
- fun getAllLocation(): List {
- val readableDb = helper.readableDatabase
- val cursor = readableDb.query(
- MapContract.MapEntry.TABLE_NAME,
- null,
- null,
- null,
- null,
- null,
- null
- )
- return getLocationResult(cursor)
- }
-
- private fun getLocationResult(cursor: Cursor): List {
- val res = mutableListOf()
- while (cursor.moveToNext()) {
- res.add(getLocation(cursor))
- }
- cursor.close()
- return res
- }
-
- private fun getLocation(cursor: Cursor): Location {
- val name =
- cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_NAME))
- val category =
- cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_CATEGORY))
- val address =
- cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_ADDRESS))
-
- return Location(name, category, address)
- }
-
- private fun getLocation(document: Document): Location {
- val name = document.placeName
- val category =
- if (document.categoryGroupName != "") {
- document.categoryGroupName
- } else {
- Location.NORMAL
- }
-
- val address =
- if (document.roadAddressName != "") {
- document.roadAddressName
- } else {
- document.addressName
- }
-
-
- return Location(name, category, address)
- }
-
- fun insertHistory(locName: String) {
-
- if (isHistoryExist(locName))
- deleteHistory(locName)
- val writeableDb = helper.writableDatabase
- val content = ContentValues()
- content.put(MapContract.MapEntry.COLUMN_NAME_NAME, locName)
- writeableDb.insert(MapContract.MapEntry.TABLE_NAME_HISTORY, null, content)
- }
-
- private fun isHistoryExist(locName: String): Boolean {
- val readableDb = helper.readableDatabase
- val selection = "${MapContract.MapEntry.COLUMN_NAME_NAME} = ?"
- val selectionArgs = arrayOf(locName)
- val cursor = readableDb.query(
- MapContract.MapEntry.TABLE_NAME_HISTORY,
- null,
- selection,
- selectionArgs,
- null,
- null,
- null
- )
- val isExist: Boolean = cursor.moveToNext()
- cursor.close()
- return isExist
- }
-
- fun deleteHistory(locName: String) {
- val writeableDb = helper.writableDatabase
- val selection = "${MapContract.MapEntry.COLUMN_NAME_NAME} = ?"
- val selectionArgs = arrayOf(locName)
-
- writeableDb.delete(MapContract.MapEntry.TABLE_NAME_HISTORY, selection, selectionArgs)
- }
-
- fun getAllHistory(): List {
- val readableDb = helper.readableDatabase
- val cursor = readableDb.query(
- MapContract.MapEntry.TABLE_NAME_HISTORY,
- null,
- null,
- null,
- null,
- null,
- null
- )
-
- val res = mutableListOf()
- while (cursor.moveToNext()) {
- res.add(cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_NAME)))
- }
- cursor.close()
- return res
- }
-
- private fun request(keyword: String, page: Int = 1) {
- val authorization = "KakaoAK ${BuildConfig.KAKAO_REST_API_KEY}"
- retrofit.requestLocationByKeyword(authorization, keyword, page = page).enqueue(object : Callback {
- override fun onResponse(call: Call, response: Response) {
- if (response.isSuccessful) {
- val body = response.body()
- body?.let {
- updateDb(body, page)
- }
- }
- _searchResult.value = getAllLocation()
- }
-
- override fun onFailure(call: Call, response: Throwable) {
- Log.d("Model", "Fail")
- }
- })
- }
-
- private fun clearDb() {
- helper.clearDb(helper.writableDatabase)
- }
-
- private fun updateDb(serverResult: ServerResult, page: Int) {
- val res = mutableListOf()
- serverResult.docList.forEach { document ->
- val location = getLocation(document)
- insertLocation(location)
- }
- if (!serverResult.meta.isEnd) {
- requestNextPage(serverResult, page)
- }
- }
-
- private fun requestNextPage(serverResult: ServerResult, page: Int) {
- val keyword = serverResult.meta.sameName.keyword
- val nextPage = page + 1
- request(keyword, nextPage)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/MapViewModel.kt b/app/src/main/java/campus/tech/kakao/map/MapViewModel.kt
deleted file mode 100644
index 6555379f..00000000
--- a/app/src/main/java/campus/tech/kakao/map/MapViewModel.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package campus.tech.kakao.map
-
-import android.content.Context
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModel
-
-class MapViewModel(dbHelper: MapDbHelper) : ViewModel() {
- private val model: MapModel = MapModel(dbHelper)
- private val _searchResult = MutableLiveData>()
- val searchResult: LiveData> = _searchResult
- private val _searchHistory = MutableLiveData>()
- val searchHistory: LiveData> = _searchHistory
-
- private val resultObserver = Observer> {
- _searchResult.value = it
- }
- private val historyObserver = Observer> {
- _searchHistory.value = it
- }
-
- init {
- observeData()
- }
-
- override fun onCleared() {
- super.onCleared()
- model.searchResult.removeObserver(resultObserver)
- model.searchHistory.removeObserver(historyObserver)
- }
-
- fun insertLocation(location: Location) {
- model.insertLocation(location)
- }
-
- fun searchLocation(locName: String, isExactMatch: Boolean) {
- _searchResult.value = model.getSearchedLocation(locName, isExactMatch)
- }
-
- fun getAllLocation(): List {
- return model.getAllLocation()
- }
-
- fun deleteHistory(historyName: String) {
- model.deleteHistory(historyName)
- _searchHistory.value = model.getAllHistory()
- }
-
- fun insertHistory(historyName: String) {
- model.insertHistory(historyName)
- _searchHistory.value = model.getAllHistory()
- }
-
- fun getAllHistory(): List {
- return model.getAllHistory()
- }
-
- fun searchByKeywordFromServer(keyword: String, isExactMatch: Boolean) {
- model.searchByKeywordFromServer(keyword, isExactMatch)
- }
-
- private fun observeData() {
- model.searchResult.observeForever(resultObserver)
- model.searchHistory.observeForever(historyObserver)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/RetrofitServiceClient.kt b/app/src/main/java/campus/tech/kakao/map/RetrofitServiceClient.kt
deleted file mode 100644
index 97ee81a3..00000000
--- a/app/src/main/java/campus/tech/kakao/map/RetrofitServiceClient.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package campus.tech.kakao.map
-
-import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
-
-object RetrofitServiceClient {
- private lateinit var retrofitService: RetrofitService
-
- fun getRetrofit(baseUrl: String): RetrofitService {
- retrofitService = Retrofit.Builder()
- .baseUrl(baseUrl)
- .addConverterFactory(GsonConverterFactory.create())
- .build()
- .create(RetrofitService::class.java)
- return retrofitService
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/ServerResult.kt b/app/src/main/java/campus/tech/kakao/map/data/ServerResult.kt
similarity index 97%
rename from app/src/main/java/campus/tech/kakao/map/ServerResult.kt
rename to app/src/main/java/campus/tech/kakao/map/data/ServerResult.kt
index 056605f5..d1d98e19 100644
--- a/app/src/main/java/campus/tech/kakao/map/ServerResult.kt
+++ b/app/src/main/java/campus/tech/kakao/map/data/ServerResult.kt
@@ -1,4 +1,4 @@
-package campus.tech.kakao.map
+package campus.tech.kakao.map.data
import com.google.gson.annotations.SerializedName
diff --git a/app/src/main/java/campus/tech/kakao/map/data/repository/HistoryRepositoryImpl.kt b/app/src/main/java/campus/tech/kakao/map/data/repository/HistoryRepositoryImpl.kt
new file mode 100644
index 00000000..5919cd2d
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/repository/HistoryRepositoryImpl.kt
@@ -0,0 +1,98 @@
+package campus.tech.kakao.map.data.repository
+
+import android.content.ContentValues
+import android.database.Cursor
+import campus.tech.kakao.map.MapContract
+import campus.tech.kakao.map.data.source.MapDbHelper
+import campus.tech.kakao.map.domain.model.Location
+import campus.tech.kakao.map.domain.repository.HistoryRepository
+
+class HistoryRepositoryImpl(
+ private val helper: MapDbHelper
+) : HistoryRepository {
+ override fun insertHistory(newHistory: Location) {
+ if (isHistoryExist(newHistory))
+ deleteHistory(newHistory)
+ val writeableDb = helper.writableDatabase
+ val content = historyToContent(newHistory)
+
+ writeableDb.insert(MapContract.MapEntry.TABLE_NAME_HISTORY, null, content)
+ }
+
+ override fun deleteHistory(oldHistory: Location) {
+ val writeableDb = helper.writableDatabase
+ val selection = "${MapContract.MapEntry.COLUMN_NAME_ID} = ?"
+ val selectionArgs = arrayOf(oldHistory.id)
+
+ writeableDb.delete(MapContract.MapEntry.TABLE_NAME_HISTORY, selection, selectionArgs)
+ }
+
+ override fun getAllHistory(): List {
+ val readableDb = helper.readableDatabase
+ val cursor = readableDb.query(
+ MapContract.MapEntry.TABLE_NAME_HISTORY,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ )
+
+ val res = mutableListOf()
+ while (cursor.moveToNext()) {
+ res.add(cursorToHistory(cursor))
+ }
+ cursor.close()
+ return res
+ }
+
+ private fun isHistoryExist(newHistory: Location): Boolean {
+ val readableDb = helper.readableDatabase
+ val selection = "${MapContract.MapEntry.COLUMN_NAME_ID} = ?"
+ val selectionArgs = arrayOf(newHistory.id)
+ val cursor = readableDb.query(
+ MapContract.MapEntry.TABLE_NAME_HISTORY,
+ null,
+ selection,
+ selectionArgs,
+ null,
+ null,
+ null
+ )
+ val isExist: Boolean = cursor.moveToNext()
+ cursor.close()
+ return isExist
+ }
+
+ private fun historyToContent(location: Location): ContentValues {
+ val content = ContentValues()
+ content.put(MapContract.MapEntry.COLUMN_NAME_ID, location.id)
+ content.put(MapContract.MapEntry.COLUMN_NAME_NAME, location.name)
+ content.put(MapContract.MapEntry.COLUMN_NAME_CATEGORY, location.category)
+ content.put(MapContract.MapEntry.COLUMN_NAME_ADDRESS, location.address)
+ content.put(MapContract.MapEntry.COLUMN_NAME_X, location.x)
+ content.put(MapContract.MapEntry.COLUMN_NAME_Y, location.y)
+
+ return content
+ }
+
+ private fun cursorToHistory(cursor: Cursor): Location {
+ val id =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_ID))
+ val name =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_NAME))
+ val category =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_CATEGORY))
+ val address =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_ADDRESS))
+ val x =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_X))
+ .toDouble()
+ val y =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_Y))
+ .toDouble()
+
+ return Location(id, name, category, address, x, y)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/data/repository/LastLocationRepositoryImpl.kt b/app/src/main/java/campus/tech/kakao/map/data/repository/LastLocationRepositoryImpl.kt
new file mode 100644
index 00000000..31de396b
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/repository/LastLocationRepositoryImpl.kt
@@ -0,0 +1,73 @@
+package campus.tech.kakao.map.data.repository
+
+import android.content.ContentValues
+import android.database.Cursor
+import android.util.Log
+import campus.tech.kakao.map.MapContract
+import campus.tech.kakao.map.data.source.MapDbHelper
+import campus.tech.kakao.map.domain.model.Location
+import campus.tech.kakao.map.domain.repository.LastLocationRepository
+
+class LastLocationRepositoryImpl(
+ private val helper: MapDbHelper
+) : LastLocationRepository {
+ override fun insertLastLocation(location: Location) {
+ val writeableDb = helper.writableDatabase
+ val content = locationToContent(location)
+ clearLastLocation()
+ writeableDb.insert(MapContract.MapEntry.TABLE_NAME_LAST_LOCATION, null, content)
+ }
+
+ override fun clearLastLocation() {
+ val writeableDb = helper.writableDatabase
+ helper.clearLastLocation(writeableDb)
+ }
+
+ override fun getLastLocation(): Location? {
+ val readableDb = helper.readableDatabase
+ val cursor = readableDb.query(
+ MapContract.MapEntry.TABLE_NAME_LAST_LOCATION,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ )
+ return if (cursor.moveToNext())
+ cursorToLocation(cursor)
+ else
+ null
+ }
+
+ private fun locationToContent(location: Location): ContentValues {
+ val content = ContentValues()
+ content.put(MapContract.MapEntry.COLUMN_NAME_ID, location.id)
+ content.put(MapContract.MapEntry.COLUMN_NAME_NAME, location.name)
+ content.put(MapContract.MapEntry.COLUMN_NAME_CATEGORY, location.category)
+ content.put(MapContract.MapEntry.COLUMN_NAME_ADDRESS, location.address)
+ content.put(MapContract.MapEntry.COLUMN_NAME_X, location.x)
+ content.put(MapContract.MapEntry.COLUMN_NAME_Y, location.y)
+
+ return content
+ }
+
+ private fun cursorToLocation(cursor: Cursor): Location {
+ val id =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_ID))
+ val name =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_NAME))
+ val category =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_CATEGORY))
+ val address =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_ADDRESS))
+ val x =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_X))
+ .toDouble()
+ val y =
+ cursor.getString(cursor.getColumnIndexOrThrow(MapContract.MapEntry.COLUMN_NAME_Y))
+ .toDouble()
+
+ return Location(id, name, category, address, x, y)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/data/repository/ResultRepositoryImpl.kt b/app/src/main/java/campus/tech/kakao/map/data/repository/ResultRepositoryImpl.kt
new file mode 100644
index 00000000..e0c7e824
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/repository/ResultRepositoryImpl.kt
@@ -0,0 +1,78 @@
+package campus.tech.kakao.map.data.repository
+
+import android.app.Application
+import android.widget.Toast
+import campus.tech.kakao.map.BuildConfig
+import campus.tech.kakao.map.data.Document
+import campus.tech.kakao.map.data.ServerResult
+import campus.tech.kakao.map.data.source.RetrofitService
+import campus.tech.kakao.map.domain.model.Location
+import campus.tech.kakao.map.domain.repository.ResultRepository
+
+class ResultRepositoryImpl(
+ private val retrofit: RetrofitService
+) : ResultRepository {
+ private val result = mutableListOf()
+
+ override suspend fun search(keyword: String): List {
+ clearResult()
+ request(keyword)
+ return result
+ }
+
+ override fun getAllResult(): List {
+ return result
+ }
+
+ private suspend fun request(keyword: String, currPage: Int = 1, tryCount: Int = 0) {
+ if (keyword.isNotEmpty()) {
+ val authorization = "KakaoAK ${BuildConfig.KAKAO_REST_API_KEY}"
+ val response = retrofit.requestLocationByKeyword(authorization, keyword, currPage)
+ if (response.isSuccessful) {
+ val resBody = response.body()
+ resBody?.let { serverResult ->
+ serverResult.docList.forEach { document ->
+ result.add(documentToLocation(document))
+ }
+ if (!serverResult.meta.isEnd)
+ requestNextPage(serverResult, currPage)
+ }
+ } else if (tryCount < 3) {
+ request(keyword, currPage, tryCount + 1)
+ }
+ }
+ }
+
+ private fun clearResult() {
+ result.clear()
+ }
+
+ private suspend fun requestNextPage(serverResult: ServerResult, page: Int) {
+ val keyword = serverResult.meta.sameName.keyword
+ val nextPage = page + 1
+ request(keyword, nextPage, 0)
+ }
+
+ private fun documentToLocation(document: Document): Location {
+ val id = document.id
+ val name = document.placeName
+ val category =
+ if (document.categoryGroupName != "") {
+ document.categoryGroupName
+ } else {
+ Location.NORMAL
+ }
+
+ val address =
+ if (document.roadAddressName != "") {
+ document.roadAddressName
+ } else {
+ document.addressName
+ }
+
+ val x = document.x.toDouble()
+ val y = document.y.toDouble()
+
+ return Location(id, name, category, address, x, y)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/data/source/MapDbHelper.kt b/app/src/main/java/campus/tech/kakao/map/data/source/MapDbHelper.kt
new file mode 100644
index 00000000..6639438c
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/source/MapDbHelper.kt
@@ -0,0 +1,56 @@
+package campus.tech.kakao.map.data.source
+
+import android.content.Context
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
+import campus.tech.kakao.map.MapContract
+
+class MapDbHelper(mContext: Context) :
+ SQLiteOpenHelper(mContext, DATABASE_NAME, null, DATABASE_VERSION) {
+ override fun onCreate(db: SQLiteDatabase?) {
+ db?.execSQL(SQL_CREATE_ENTRIES_HISTORY)
+ db?.execSQL(SQL_CREATE_ENTRIES_LAST_LOCATION)
+ }
+
+ override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
+ db?.execSQL(SQL_DELETE_ENTRIES_HISTORY)
+ db?.execSQL(SQL_DELETE_ENTRIES_LAST_LOCATION)
+ onCreate(db)
+ }
+
+ fun clearLastLocation(db: SQLiteDatabase?) {
+ db?.execSQL(SQL_DELETE_ENTRIES_LAST_LOCATION)
+ db?.execSQL(SQL_CREATE_ENTRIES_LAST_LOCATION)
+ }
+
+ companion object {
+ const val DATABASE_NAME = "map.db"
+ const val DATABASE_VERSION = 1
+
+ private const val SQL_CREATE_ENTRIES_HISTORY = """
+ CREATE TABLE ${MapContract.MapEntry.TABLE_NAME_HISTORY} (
+ ${MapContract.MapEntry.COLUMN_NAME_ID} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_NAME} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_CATEGORY} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_ADDRESS} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_X} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_Y} TEXT);
+ """
+
+ private const val SQL_DELETE_ENTRIES_HISTORY =
+ "DROP TABLE IF EXISTS ${MapContract.MapEntry.TABLE_NAME_HISTORY}"
+
+ private const val SQL_CREATE_ENTRIES_LAST_LOCATION = """
+ CREATE TABLE ${MapContract.MapEntry.TABLE_NAME_LAST_LOCATION} (
+ ${MapContract.MapEntry.COLUMN_NAME_ID} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_NAME} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_CATEGORY} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_ADDRESS} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_X} TEXT,
+ ${MapContract.MapEntry.COLUMN_NAME_Y} TEXT);
+ """
+
+ private const val SQL_DELETE_ENTRIES_LAST_LOCATION =
+ "DROP TABLE IF EXISTS ${MapContract.MapEntry.TABLE_NAME_LAST_LOCATION}"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/RetrofitService.kt b/app/src/main/java/campus/tech/kakao/map/data/source/RetrofitService.kt
similarity index 76%
rename from app/src/main/java/campus/tech/kakao/map/RetrofitService.kt
rename to app/src/main/java/campus/tech/kakao/map/data/source/RetrofitService.kt
index b397852a..6829f239 100644
--- a/app/src/main/java/campus/tech/kakao/map/RetrofitService.kt
+++ b/app/src/main/java/campus/tech/kakao/map/data/source/RetrofitService.kt
@@ -1,6 +1,8 @@
-package campus.tech.kakao.map
+package campus.tech.kakao.map.data.source
+import campus.tech.kakao.map.data.ServerResult
import retrofit2.Call
+import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
@@ -15,9 +17,9 @@ interface RetrofitService {
) : Call
@GET("v2/local/search/keyword")
- fun requestLocationByKeyword(
+ suspend fun requestLocationByKeyword(
@Header("Authorization") authorization: String,
@Query("query") keyword: String,
@Query("page") page: Int = 1
- ) : Call
+ ) : Response
}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/data/source/RetrofitServiceClient.kt b/app/src/main/java/campus/tech/kakao/map/data/source/RetrofitServiceClient.kt
new file mode 100644
index 00000000..18768a14
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/data/source/RetrofitServiceClient.kt
@@ -0,0 +1,13 @@
+package campus.tech.kakao.map.data.source
+
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+
+object RetrofitServiceClient {
+ val retrofitService: RetrofitService =
+ Retrofit.Builder()
+ .baseUrl("https://dapi.kakao.com/")
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+ .create(RetrofitService::class.java)
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/domain/model/Location.kt b/app/src/main/java/campus/tech/kakao/map/domain/model/Location.kt
new file mode 100644
index 00000000..99e6e976
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/domain/model/Location.kt
@@ -0,0 +1,17 @@
+package campus.tech.kakao.map.domain.model
+
+import java.io.Serializable
+
+data class Location(
+ val id: String,
+ val name: String,
+ val category: String,
+ val address: String,
+ val x: Double,
+ val y: Double
+) : Serializable {
+ companion object {
+ const val LOCATION: String = "LOCATION"
+ const val NORMAL: String = "일반"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/domain/repository/HistoryRepository.kt b/app/src/main/java/campus/tech/kakao/map/domain/repository/HistoryRepository.kt
new file mode 100644
index 00000000..a6ad2b2d
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/domain/repository/HistoryRepository.kt
@@ -0,0 +1,9 @@
+package campus.tech.kakao.map.domain.repository
+
+import campus.tech.kakao.map.domain.model.Location
+
+interface HistoryRepository {
+ fun insertHistory(newHistory: Location)
+ fun deleteHistory(oldHistory: Location)
+ fun getAllHistory(): List
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/domain/repository/LastLocationRepository.kt b/app/src/main/java/campus/tech/kakao/map/domain/repository/LastLocationRepository.kt
new file mode 100644
index 00000000..f06d77f4
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/domain/repository/LastLocationRepository.kt
@@ -0,0 +1,9 @@
+package campus.tech.kakao.map.domain.repository
+
+import campus.tech.kakao.map.domain.model.Location
+
+interface LastLocationRepository {
+ fun insertLastLocation(location: Location)
+ fun clearLastLocation()
+ fun getLastLocation(): Location?
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/domain/repository/ResultRepository.kt b/app/src/main/java/campus/tech/kakao/map/domain/repository/ResultRepository.kt
new file mode 100644
index 00000000..5a3fcf69
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/domain/repository/ResultRepository.kt
@@ -0,0 +1,8 @@
+package campus.tech.kakao.map.domain.repository
+
+import campus.tech.kakao.map.domain.model.Location
+
+interface ResultRepository {
+ suspend fun search(keyword: String): List
+ fun getAllResult(): List
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/HistoryRecyclerAdapter.kt b/app/src/main/java/campus/tech/kakao/map/presentation/HistoryRecyclerAdapter.kt
similarity index 70%
rename from app/src/main/java/campus/tech/kakao/map/HistoryRecyclerAdapter.kt
rename to app/src/main/java/campus/tech/kakao/map/presentation/HistoryRecyclerAdapter.kt
index a0b135a3..007f7d45 100644
--- a/app/src/main/java/campus/tech/kakao/map/HistoryRecyclerAdapter.kt
+++ b/app/src/main/java/campus/tech/kakao/map/presentation/HistoryRecyclerAdapter.kt
@@ -1,19 +1,18 @@
-package campus.tech.kakao.map
+package campus.tech.kakao.map.presentation
-import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import campus.tech.kakao.map.DatabaseListener
+import campus.tech.kakao.map.domain.model.Location
+import campus.tech.kakao.map.R
class HistoryRecyclerAdapter(
- var history: List,
+ var history: List,
val layoutInflater: LayoutInflater,
val databaseListener: DatabaseListener
) : RecyclerView.Adapter() {
@@ -24,9 +23,14 @@ class HistoryRecyclerAdapter(
init {
clear.setOnClickListener {
if (bindingAdapterPosition != RecyclerView.NO_POSITION) {
- databaseListener.deleteHistory(name.text.toString())
+ databaseListener.deleteHistory(history[bindingAdapterPosition])
}
}
+
+ itemView.setOnClickListener {
+ databaseListener.searchHistory(name.text.toString(), false)
+ databaseListener.insertHistory(history[bindingAdapterPosition])
+ }
}
}
@@ -36,7 +40,7 @@ class HistoryRecyclerAdapter(
}
override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) {
- holder.name.text = history[position]
+ holder.name.text = history[position].name
}
override fun getItemCount(): Int {
diff --git a/app/src/main/java/campus/tech/kakao/map/presentation/MapActivity.kt b/app/src/main/java/campus/tech/kakao/map/presentation/MapActivity.kt
new file mode 100644
index 00000000..43553795
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/presentation/MapActivity.kt
@@ -0,0 +1,136 @@
+package campus.tech.kakao.map.presentation
+
+import android.content.Intent
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isVisible
+import androidx.lifecycle.Observer
+import campus.tech.kakao.map.domain.model.Location
+import campus.tech.kakao.map.R
+import campus.tech.kakao.map.data.repository.HistoryRepositoryImpl
+import campus.tech.kakao.map.data.repository.LastLocationRepositoryImpl
+import campus.tech.kakao.map.data.repository.ResultRepositoryImpl
+import campus.tech.kakao.map.data.source.MapDbHelper
+import campus.tech.kakao.map.data.source.RetrofitServiceClient
+import campus.tech.kakao.map.domain.repository.HistoryRepository
+import campus.tech.kakao.map.domain.repository.ResultRepository
+import com.kakao.vectormap.KakaoMap
+import com.kakao.vectormap.KakaoMapReadyCallback
+import com.kakao.vectormap.LatLng
+import com.kakao.vectormap.MapLifeCycleCallback
+import com.kakao.vectormap.MapView
+import com.kakao.vectormap.camera.CameraUpdateFactory
+import com.kakao.vectormap.label.LabelLayerOptions
+import com.kakao.vectormap.label.LabelManager
+import com.kakao.vectormap.label.LabelOptions
+import com.kakao.vectormap.label.LabelStyle
+import com.kakao.vectormap.label.LabelStyles
+
+
+class MapActivity : AppCompatActivity() {
+ private val TAG = "KAKAOMAP"
+ private lateinit var kakaoMapView: MapView
+ private lateinit var kakaoMap: KakaoMap
+ private lateinit var searchBox: TextView
+ private lateinit var infoSheetLayout: LinearLayout
+ private lateinit var infoSheetName: TextView
+ private lateinit var infoSheetAddress: TextView
+ private lateinit var errorLayout: ConstraintLayout
+ private lateinit var errorCode: TextView
+
+ private lateinit var viewModel: MapViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_map)
+ kakaoMapView = findViewById(R.id.kakao_map_view)
+ searchBox = findViewById(R.id.search_box)
+ infoSheetLayout = findViewById(R.id.info_sheet)
+ infoSheetName = findViewById(R.id.info_sheet_name)
+ infoSheetAddress = findViewById(R.id.info_sheet_address)
+ errorLayout = findViewById(R.id.error_layout)
+ errorCode = findViewById(R.id.error_code)
+
+ val dbhelper = MapDbHelper(this)
+ val resultRepo = ResultRepositoryImpl(RetrofitServiceClient.retrofitService)
+ val historyRepo = HistoryRepositoryImpl(dbhelper)
+ val lastRepo = LastLocationRepositoryImpl(dbhelper)
+ viewModel = MapViewModel(dbhelper, resultRepo, historyRepo, lastRepo)
+
+ kakaoMapView.start(object : MapLifeCycleCallback() {
+ override fun onMapDestroy() {
+ // TODO: 필요시 구현 예정
+ }
+
+ override fun onMapError(exception: Exception?) {
+ hideInfo(exception)
+ }
+
+ }, object : KakaoMapReadyCallback() {
+ override fun onMapReady(kakaoMap: KakaoMap) {
+ this@MapActivity.kakaoMap = kakaoMap
+ val target = viewModel.getLastLocation()
+ if (::kakaoMap.isInitialized && target != null) {
+ moveToTargetLocation(kakaoMap, target)
+ showInfo(target)
+ }
+ Log.d(TAG, "onMapReady")
+ }
+ })
+
+ searchBox.setOnClickListener {
+ startActivity(Intent(this, SearchActivity::class.java))
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ kakaoMapView.resume()
+ val target = viewModel.getLastLocation()
+ if (::kakaoMap.isInitialized && target != null) {
+ moveToTargetLocation(kakaoMap, target)
+ showInfo(target)
+ }
+ Log.d(TAG, "onResume")
+ }
+
+ override fun onPause() {
+ super.onPause()
+ kakaoMapView.pause()
+ Log.d("KAKAOMAP", "onPause")
+ }
+
+ private fun moveToTargetLocation(kakaoMap: KakaoMap, target: Location) {
+ val cameraUpdate = CameraUpdateFactory.newCenterPosition(LatLng.from(target.y, target.x))
+ kakaoMap.moveCamera(cameraUpdate)
+ val labelManager = kakaoMap.labelManager
+ labelManager?.let { setPin(labelManager, target) }
+ }
+
+ private fun showInfo(target: Location) {
+ infoSheetLayout.isVisible = true
+ infoSheetName.text = target.name
+ infoSheetAddress.text = target.address
+ }
+
+ private fun hideInfo(exception: Exception?) {
+ errorCode.text = exception?.message
+ errorLayout.isVisible = true
+ kakaoMapView.isVisible = false
+ searchBox.isVisible = false
+ }
+
+ private fun setPin(labelManager: LabelManager, target: Location) {
+ labelManager.removeAllLabelLayer()
+ val style = labelManager
+ .addLabelStyles(LabelStyles.from(LabelStyle.from(R.drawable.location_label).setTextStyles(30, Color.BLACK)))
+ labelManager.layer
+ ?.addLabel(LabelOptions.from(LatLng.from(target.y, target.x)).setStyles(style).setTexts(target.name))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/presentation/MapViewModel.kt b/app/src/main/java/campus/tech/kakao/map/presentation/MapViewModel.kt
new file mode 100644
index 00000000..a0f9e6cb
--- /dev/null
+++ b/app/src/main/java/campus/tech/kakao/map/presentation/MapViewModel.kt
@@ -0,0 +1,82 @@
+package campus.tech.kakao.map.presentation
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.initializer
+import androidx.lifecycle.viewmodel.viewModelFactory
+import campus.tech.kakao.map.MapApplication
+import campus.tech.kakao.map.domain.model.Location
+import campus.tech.kakao.map.data.source.MapDbHelper
+import campus.tech.kakao.map.domain.repository.HistoryRepository
+import campus.tech.kakao.map.domain.repository.LastLocationRepository
+import campus.tech.kakao.map.domain.repository.ResultRepository
+import kotlinx.coroutines.launch
+
+class MapViewModel(
+ dbHelper: MapDbHelper,
+ private val resultRepository: ResultRepository,
+ private val historyRepository: HistoryRepository,
+ private val lastLocationRepository: LastLocationRepository
+) : ViewModel() {
+ private val _searchResult = MutableLiveData>()
+ val searchResult: LiveData> = _searchResult
+ private val _searchHistory = MutableLiveData>()
+ val searchHistory: LiveData> = _searchHistory
+ private val _lastLocation = MutableLiveData()
+ val lastLocation: LiveData = _lastLocation
+
+ fun searchKeyword(keyword: String) {
+ viewModelScope.launch {
+ resultRepository.search(keyword)
+ _searchResult.value = resultRepository.getAllResult()
+ }
+ }
+
+ fun getAllResult(): List {
+ return resultRepository.getAllResult()
+ }
+
+ fun insertHistory(newHistory: Location) {
+ historyRepository.insertHistory(newHistory)
+ _searchHistory.value = historyRepository.getAllHistory()
+ }
+
+ fun deleteHistory(oldHistory: Location) {
+ historyRepository.deleteHistory(oldHistory)
+ _searchHistory.value = historyRepository.getAllHistory()
+ }
+
+ fun getAllHistory(): List {
+ return historyRepository.getAllHistory()
+ }
+
+ fun insertLastLocation(location: Location) {
+ lastLocationRepository.insertLastLocation(location)
+ _lastLocation.value = lastLocationRepository.getLastLocation()
+ Log.d("ViewModel", _lastLocation.value.toString())
+ }
+
+ fun getLastLocation(): Location? {
+ _lastLocation.value = lastLocationRepository.getLastLocation()
+ return lastLocationRepository.getLastLocation()
+ }
+
+ companion object {
+ val Factory: ViewModelProvider.Factory = viewModelFactory {
+ initializer {
+ val application = (this[APPLICATION_KEY] as MapApplication)
+ MapViewModel(
+ application.dbHelper,
+ application.resultRepositoryImpl,
+ application.historyRepositoryImpl,
+ application.lastLocationRepositoryImpl
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/campus/tech/kakao/map/ResultRecyclerAdapter.kt b/app/src/main/java/campus/tech/kakao/map/presentation/ResultRecyclerAdapter.kt
similarity index 77%
rename from app/src/main/java/campus/tech/kakao/map/ResultRecyclerAdapter.kt
rename to app/src/main/java/campus/tech/kakao/map/presentation/ResultRecyclerAdapter.kt
index c09a1221..1f88ee05 100644
--- a/app/src/main/java/campus/tech/kakao/map/ResultRecyclerAdapter.kt
+++ b/app/src/main/java/campus/tech/kakao/map/presentation/ResultRecyclerAdapter.kt
@@ -1,12 +1,14 @@
-package campus.tech.kakao.map
+package campus.tech.kakao.map.presentation
-import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import campus.tech.kakao.map.DatabaseListener
+import campus.tech.kakao.map.domain.model.Location
+import campus.tech.kakao.map.R
class ResultRecyclerAdapter(
var searchResult: List,
@@ -21,7 +23,10 @@ class ResultRecyclerAdapter(
init {
itemView.setOnClickListener {
if (bindingAdapterPosition != RecyclerView.NO_POSITION) {
- databaseListener.insertHistory(name.text.toString())
+ val clickedResult = searchResult[bindingAdapterPosition]
+ databaseListener.insertHistory(clickedResult)
+ databaseListener.insertLastLocation(clickedResult)
+ databaseListener.showMap(clickedResult)
}
}
}
diff --git a/app/src/main/java/campus/tech/kakao/map/SearchActivity.kt b/app/src/main/java/campus/tech/kakao/map/presentation/SearchActivity.kt
similarity index 52%
rename from app/src/main/java/campus/tech/kakao/map/SearchActivity.kt
rename to app/src/main/java/campus/tech/kakao/map/presentation/SearchActivity.kt
index d9e09994..6b1a4b54 100644
--- a/app/src/main/java/campus/tech/kakao/map/SearchActivity.kt
+++ b/app/src/main/java/campus/tech/kakao/map/presentation/SearchActivity.kt
@@ -1,19 +1,29 @@
-package campus.tech.kakao.map
+package campus.tech.kakao.map.presentation
+import android.content.Intent
import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
import android.widget.EditText
import android.widget.ImageButton
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
-import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import campus.tech.kakao.map.DatabaseListener
+import campus.tech.kakao.map.domain.model.Location
+import campus.tech.kakao.map.R
+import campus.tech.kakao.map.data.repository.HistoryRepositoryImpl
+import campus.tech.kakao.map.data.repository.LastLocationRepositoryImpl
+import campus.tech.kakao.map.data.repository.ResultRepositoryImpl
+import campus.tech.kakao.map.data.source.MapDbHelper
+import campus.tech.kakao.map.data.source.RetrofitServiceClient
class SearchActivity : AppCompatActivity(), DatabaseListener {
+// private val viewModel: MapViewModel by viewModels()
private lateinit var viewModel: MapViewModel
-
private lateinit var searchBox: EditText
private lateinit var searchHistoryView: RecyclerView
private lateinit var searchResultView: RecyclerView
@@ -22,27 +32,27 @@ class SearchActivity : AppCompatActivity(), DatabaseListener {
private lateinit var searchResultAdapter: ResultRecyclerAdapter
private lateinit var searchHistoryAdapter: HistoryRecyclerAdapter
-
+ private val searchBoxWatcher = getSearchBoxWatcher()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
val dbHelper = MapDbHelper(this)
- viewModel = MapViewModel(dbHelper)
+ val retrofit = RetrofitServiceClient.retrofitService
+ viewModel = MapViewModel(
+ dbHelper,
+ ResultRepositoryImpl(retrofit) ,
+ HistoryRepositoryImpl(dbHelper),
+ LastLocationRepositoryImpl(dbHelper)
+ )
searchBox = findViewById(R.id.search_box)
searchHistoryView = findViewById(R.id.search_history)
searchResultView = findViewById(R.id.search_result)
message = findViewById(R.id.message)
clear = findViewById(R.id.clear)
- searchBox.doAfterTextChanged { text ->
- if (text.isNullOrEmpty()) {
- hideResult()
- } else {
- search(text.toString(), false)
- }
- }
+ searchBox.addTextChangedListener(searchBoxWatcher)
clear.setOnClickListener {
searchBox.text.clear()
@@ -53,12 +63,31 @@ class SearchActivity : AppCompatActivity(), DatabaseListener {
observeData()
}
- override fun deleteHistory(historyName: String) {
- viewModel.deleteHistory(historyName)
+ override fun deleteHistory(oldHistory: Location) {
+ viewModel.deleteHistory(oldHistory)
+ }
+
+ override fun insertHistory(newHistory: Location) {
+ viewModel.insertHistory(newHistory)
+ }
+
+ override fun showMap(newHistory: Location) {
+ finish()
+ }
+
+ override fun insertLastLocation(location: Location) {
+ viewModel.insertLastLocation(location)
}
- override fun insertHistory(historyName: String) {
- viewModel.insertHistory(historyName)
+ override fun searchHistory(locName: String, isExactMatch: Boolean) {
+ searchBox.removeTextChangedListener(searchBoxWatcher)
+ searchBox.setText(locName)
+ searchBox.addTextChangedListener(searchBoxWatcher)
+ viewModel.searchKeyword(locName)
+ }
+
+ private fun search(locName: String) {
+ viewModel.searchKeyword(locName)
}
private fun hideResult() {
@@ -69,13 +98,10 @@ class SearchActivity : AppCompatActivity(), DatabaseListener {
searchResultView.isVisible = true
message.isVisible = false
}
- private fun search(locName: String, isExactMatch: Boolean) {
- viewModel.searchByKeywordFromServer(locName, isExactMatch)
- }
private fun initSearchResultView() {
searchResultAdapter =
- ResultRecyclerAdapter(viewModel.searchResult.value!!, layoutInflater, this)
+ ResultRecyclerAdapter(viewModel.getAllResult(), layoutInflater, this)
searchResultView.adapter = searchResultAdapter
searchResultView.layoutManager =
LinearLayoutManager(this@SearchActivity, LinearLayoutManager.VERTICAL, false)
@@ -96,7 +122,7 @@ class SearchActivity : AppCompatActivity(), DatabaseListener {
})
viewModel.searchResult.observe(this, Observer {
searchResultAdapter.searchResult = it
- if (it.isNotEmpty() && (searchBox.text.toString() != "")) {
+ if (it.isNotEmpty() && searchBox.text.isNotEmpty()) {
showResult()
} else {
hideResult()
@@ -104,4 +130,24 @@ class SearchActivity : AppCompatActivity(), DatabaseListener {
searchResultAdapter.refreshList()
})
}
+
+ private fun getSearchBoxWatcher(): TextWatcher {
+ return object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
+ // TODO: 필요시 구현 예정
+ }
+
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
+ // TODO: 필요시 구현 예정
+ }
+
+ override fun afterTextChanged(s: Editable?) {
+ if (s.isNullOrEmpty()) {
+ hideResult()
+ } else {
+ search(s.toString())
+ }
+ }
+ }
+ }
}
diff --git a/app/src/main/res/drawable-hdpi/location_label.png b/app/src/main/res/drawable-hdpi/location_label.png
new file mode 100644
index 00000000..d45cf1d4
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/location_label.png differ
diff --git a/app/src/main/res/drawable-mdpi/location_label.png b/app/src/main/res/drawable-mdpi/location_label.png
new file mode 100644
index 00000000..4c9bff81
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/location_label.png differ
diff --git a/app/src/main/res/drawable-xhdpi/location_label.png b/app/src/main/res/drawable-xhdpi/location_label.png
new file mode 100644
index 00000000..7ab21db1
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/location_label.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/location_label.png b/app/src/main/res/drawable-xxhdpi/location_label.png
new file mode 100644
index 00000000..77b37906
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/location_label.png differ
diff --git a/app/src/main/res/drawable/background_bottom_sheet.xml b/app/src/main/res/drawable/background_bottom_sheet.xml
new file mode 100644
index 00000000..ec121cbc
--- /dev/null
+++ b/app/src/main/res/drawable/background_bottom_sheet.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/failed.png b/app/src/main/res/drawable/failed.png
new file mode 100644
index 00000000..83e9b7c7
Binary files /dev/null and b/app/src/main/res/drawable/failed.png differ
diff --git a/app/src/main/res/drawable/refresh_24.xml b/app/src/main/res/drawable/refresh_24.xml
new file mode 100644
index 00000000..86504d0e
--- /dev/null
+++ b/app/src/main/res/drawable/refresh_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_map.xml b/app/src/main/res/layout/activity_map.xml
index d7eefca7..4bbec1e5 100644
--- a/app/src/main/res/layout/activity_map.xml
+++ b/app/src/main/res/layout/activity_map.xml
@@ -1,25 +1,104 @@
-
-
+
-
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+ android:layout_height="match_parent">
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml
index e6d47e36..c3f4d956 100644
--- a/app/src/main/res/layout/activity_search.xml
+++ b/app/src/main/res/layout/activity_search.xml
@@ -5,7 +5,7 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".SearchActivity">
+ tools:context=".presentation.SearchActivity">
검색 결과가 없습니다.
검색어를 입력해 주세요.
삭제
+ 지도 인증을 실패했습니다.\n다시 시도해 주세요.
\ No newline at end of file