diff --git a/README.md b/README.md index a7767508..54e4a0f5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ - 코드 복사 - API 받아오기 -1단계 +1단계 ✅ + : 이전 단계와 기능은 비슷하나 카카오로컬 API를 사용한다. - 검색어를 입력하면 검색 결과가 15개 이상 표시된다. - 검색 결과 목록은 세로 스크롤이 된다. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ea1318a5..d5acde28 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,8 +1,18 @@ +import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties +import java.util.Properties + +fun getApiKey(key: String): String { + return gradleLocalProperties(rootDir, providers).getProperty(key) ?: "" +} + plugins { id("com.android.application") id("org.jetbrains.kotlin.android") } +val properties = Properties() +properties.load(project.rootProject.file("local.properties").inputStream()) + android { namespace = "campus.tech.kakao.map" compileSdk = 34 @@ -15,6 +25,10 @@ android { versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + buildConfigField("String", "API_KEY", "\"${getApiKey("API_KEY")}\"") + buildConfigField("String", "MAP_API_KEY", "\"${getApiKey("MAP_API_KEY")}\"") + } buildTypes { @@ -36,6 +50,7 @@ android { buildFeatures { viewBinding = true + buildConfig = true } } @@ -51,6 +66,9 @@ dependencies { implementation("com.google.code.gson:gson:2.9.0") implementation("com.squareup.retrofit2:retrofit:2.11.0") implementation("com.squareup.retrofit2:converter-gson:2.11.0") + implementation("com.kakao.maps.open:android:2.9.5") + implementation("com.kakao.sdk:v2-all:2.20.3") + implementation("androidx.activity:activity:1.9.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 576393e5..525ede22 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,10 @@ + + + + + @@ -21,6 +29,10 @@ + + - - + + \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/KakaoLocalApi.kt b/app/src/main/java/campus/tech/kakao/map/Model/KakaoLocalApi.kt similarity index 90% rename from app/src/main/java/campus/tech/kakao/map/KakaoLocalApi.kt rename to app/src/main/java/campus/tech/kakao/map/Model/KakaoLocalApi.kt index 6b8f6b28..e2c39c2a 100644 --- a/app/src/main/java/campus/tech/kakao/map/KakaoLocalApi.kt +++ b/app/src/main/java/campus/tech/kakao/map/Model/KakaoLocalApi.kt @@ -1,4 +1,4 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.Model import retrofit2.Call import retrofit2.http.GET diff --git a/app/src/main/java/campus/tech/kakao/map/LocationData.kt b/app/src/main/java/campus/tech/kakao/map/Model/LocationData.kt similarity index 73% rename from app/src/main/java/campus/tech/kakao/map/LocationData.kt rename to app/src/main/java/campus/tech/kakao/map/Model/LocationData.kt index 0cdb9904..dc781959 100644 --- a/app/src/main/java/campus/tech/kakao/map/LocationData.kt +++ b/app/src/main/java/campus/tech/kakao/map/Model/LocationData.kt @@ -1,4 +1,4 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.Model data class LocationData ( val name: String, diff --git a/app/src/main/java/campus/tech/kakao/map/Model/MyApplication.kt b/app/src/main/java/campus/tech/kakao/map/Model/MyApplication.kt new file mode 100644 index 00000000..2b65012a --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/Model/MyApplication.kt @@ -0,0 +1,20 @@ +package campus.tech.kakao.map.Model + +import android.app.Application +import android.util.Log +import campus.tech.kakao.map.BuildConfig +import com.kakao.vectormap.KakaoMapSdk + +class MyApplication: Application() { + override fun onCreate() { + super.onCreate() + + try { + KakaoMapSdk.init(this, BuildConfig.MAP_API_KEY) + Log.d("KakaoMapSDK", "SDK initialized successfully") + } catch (e: Exception) { + Log.e("KakaoMapSDK", "error", e) + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/RetrofitClient.kt b/app/src/main/java/campus/tech/kakao/map/Model/RetrofitClient.kt similarity index 92% rename from app/src/main/java/campus/tech/kakao/map/RetrofitClient.kt rename to app/src/main/java/campus/tech/kakao/map/Model/RetrofitClient.kt index 139467ce..95465ac3 100644 --- a/app/src/main/java/campus/tech/kakao/map/RetrofitClient.kt +++ b/app/src/main/java/campus/tech/kakao/map/Model/RetrofitClient.kt @@ -1,4 +1,4 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.Model import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory diff --git a/app/src/main/java/campus/tech/kakao/map/SearchCallback.kt b/app/src/main/java/campus/tech/kakao/map/Model/SearchCallback.kt similarity index 75% rename from app/src/main/java/campus/tech/kakao/map/SearchCallback.kt rename to app/src/main/java/campus/tech/kakao/map/Model/SearchCallback.kt index 03347cbb..3db3bcee 100644 --- a/app/src/main/java/campus/tech/kakao/map/SearchCallback.kt +++ b/app/src/main/java/campus/tech/kakao/map/Model/SearchCallback.kt @@ -1,11 +1,13 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.Model import android.util.Log +import campus.tech.kakao.map.view.MainActivity +import campus.tech.kakao.map.viewmodel.MainViewModel import retrofit2.Call import retrofit2.Callback import retrofit2.Response -class SearchCallback(private val activity: MainActivity) : Callback { +class SearchCallback(private val activity: MainViewModel) : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { val result = response.body() diff --git a/app/src/main/java/campus/tech/kakao/map/SearchResult.kt b/app/src/main/java/campus/tech/kakao/map/Model/SearchResult.kt similarity index 82% rename from app/src/main/java/campus/tech/kakao/map/SearchResult.kt rename to app/src/main/java/campus/tech/kakao/map/Model/SearchResult.kt index 2ddd35d9..426b5cc0 100644 --- a/app/src/main/java/campus/tech/kakao/map/SearchResult.kt +++ b/app/src/main/java/campus/tech/kakao/map/Model/SearchResult.kt @@ -1,4 +1,4 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.Model data class SearchResult( val documents: List diff --git a/app/src/main/java/campus/tech/kakao/map/MainActivity.kt b/app/src/main/java/campus/tech/kakao/map/view/MainActivity.kt similarity index 83% rename from app/src/main/java/campus/tech/kakao/map/MainActivity.kt rename to app/src/main/java/campus/tech/kakao/map/view/MainActivity.kt index a51a9044..5e50669a 100644 --- a/app/src/main/java/campus/tech/kakao/map/MainActivity.kt +++ b/app/src/main/java/campus/tech/kakao/map/view/MainActivity.kt @@ -1,4 +1,4 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.view import android.content.Context import android.os.Bundle @@ -9,16 +9,27 @@ import android.view.View import android.widget.Button import android.widget.EditText import android.widget.TextView +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import campus.tech.kakao.map.BuildConfig +import campus.tech.kakao.map.viewmodel.DataDbHelper +import campus.tech.kakao.map.viewmodel.LocationAdapter +import campus.tech.kakao.map.Model.LocationData +import campus.tech.kakao.map.Model.RetrofitClient +import campus.tech.kakao.map.Model.Place +import campus.tech.kakao.map.R +import campus.tech.kakao.map.Model.SearchCallback +import campus.tech.kakao.map.Model.SearchResult +import campus.tech.kakao.map.viewmodel.MainViewModel +import campus.tech.kakao.map.viewmodel.SearchViewAdapter import com.google.gson.Gson import com.google.gson.reflect.TypeToken import retrofit2.Call -import campus.tech.kakao.map.SearchResult class MainActivity : AppCompatActivity() { - private val API_KEY = "KakaoAK 276613de22bf074fb398b9eed102f89b" private lateinit var db: DataDbHelper private lateinit var recyclerView: RecyclerView @@ -31,16 +42,26 @@ class MainActivity : AppCompatActivity() { private var locationList = ArrayList() private var searchList = ArrayList() + private val mainViewModel: MainViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + mainViewModel.setUiStateChangedListener {uiState -> + locationList.clear() + locationList.addAll(uiState.locationList) + updateRecyclerView() + resultView.isVisible = uiState.isShowText + } + initialize() loadSearchList() setCancelBtn() setSearchView() setRecyclerView() setSearchListener() + } private fun initialize() { @@ -113,7 +134,7 @@ class MainActivity : AppCompatActivity() { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - searchLocations(s.toString()) + mainViewModel.searchLocations(s.toString()) } override fun afterTextChanged(s: Editable?) {} @@ -139,33 +160,10 @@ class MainActivity : AppCompatActivity() { Log.d("MainActivity", "Item clicked: ${locationData.name}") } - private fun searchLocations(key: String) { - val apiService = RetrofitClient.instance - val call: Call = apiService.searchPlaces(API_KEY, key) - call.enqueue(campus.tech.kakao.map.SearchCallback(this)) - } - - fun updateSearchResults(results: List) { - locationList.clear() - for (result in results) { - locationList.add(LocationData(result.place_name, result.address_name, result.category_group_name)) - } - updateRecyclerView() - isShowText() - } - private fun updateRecyclerView() { locationAdapter.notifyDataSetChanged() } - private fun isShowText() { - if (locationList.isEmpty()) { - resultView.visibility = View.VISIBLE - } else { - resultView.visibility = View.GONE - } - } - override fun onDestroy() { super.onDestroy() db.close() diff --git a/app/src/main/java/campus/tech/kakao/map/view/MapActivity.kt b/app/src/main/java/campus/tech/kakao/map/view/MapActivity.kt new file mode 100644 index 00000000..88637105 --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/view/MapActivity.kt @@ -0,0 +1,67 @@ +package campus.tech.kakao.map.view + +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import campus.tech.kakao.map.R +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 + +class MapActivity : AppCompatActivity() { + + private lateinit var mapView: MapView + private lateinit var inputText: View + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_map) + + mapView = findViewById(R.id.map_view) + inputText = findViewById(R.id.MapinputText) + + + mapView.start( + object : MapLifeCycleCallback() { + override fun onMapDestroy() {} + override fun onMapError(error: Exception) { + Log.e("MapError", "${error.message}", error) + } + }, + object : KakaoMapReadyCallback() { + override fun onMapReady(kakaoMap: KakaoMap) { + // 맵 초기화 코드 + val mapCenter = LatLng.from(37.566, 126.978) // 서울시청 좌표 + kakaoMap.moveCamera(CameraUpdateFactory.newCenterPosition(mapCenter)) + kakaoMap.moveCamera(CameraUpdateFactory.zoomTo(15)) + } + } + ) + + inputText.setOnClickListener { + val intent = Intent(this@MapActivity, MainActivity::class.java) + startActivity(intent) + } + } + + override fun onResume() { + super.onResume() + mapView.resume() + } + + override fun onPause() { + super.onPause() + mapView.pause() + } + + override fun onDestroy() { + super.onDestroy() + mapView.finish() + } + +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/DataDbHelper.kt b/app/src/main/java/campus/tech/kakao/map/viewmodel/DataDbHelper.kt similarity index 95% rename from app/src/main/java/campus/tech/kakao/map/DataDbHelper.kt rename to app/src/main/java/campus/tech/kakao/map/viewmodel/DataDbHelper.kt index d619dd24..abcd04df 100644 --- a/app/src/main/java/campus/tech/kakao/map/DataDbHelper.kt +++ b/app/src/main/java/campus/tech/kakao/map/viewmodel/DataDbHelper.kt @@ -1,4 +1,4 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.viewmodel import android.content.Context import android.database.sqlite.SQLiteDatabase diff --git a/app/src/main/java/campus/tech/kakao/map/LocationAdapter.kt b/app/src/main/java/campus/tech/kakao/map/viewmodel/LocationAdapter.kt similarity index 92% rename from app/src/main/java/campus/tech/kakao/map/LocationAdapter.kt rename to app/src/main/java/campus/tech/kakao/map/viewmodel/LocationAdapter.kt index 7c0b7826..a54154d3 100644 --- a/app/src/main/java/campus/tech/kakao/map/LocationAdapter.kt +++ b/app/src/main/java/campus/tech/kakao/map/viewmodel/LocationAdapter.kt @@ -1,10 +1,12 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.viewmodel import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView +import campus.tech.kakao.map.Model.LocationData +import campus.tech.kakao.map.R class LocationAdapter( private val locationList: List, diff --git a/app/src/main/java/campus/tech/kakao/map/viewmodel/MainViewModel.kt b/app/src/main/java/campus/tech/kakao/map/viewmodel/MainViewModel.kt new file mode 100644 index 00000000..b57a3340 --- /dev/null +++ b/app/src/main/java/campus/tech/kakao/map/viewmodel/MainViewModel.kt @@ -0,0 +1,62 @@ +package campus.tech.kakao.map.viewmodel + +import retrofit2.Call +import androidx.lifecycle.ViewModel +import campus.tech.kakao.map.BuildConfig +import campus.tech.kakao.map.Model.LocationData +import campus.tech.kakao.map.Model.Place +import campus.tech.kakao.map.Model.RetrofitClient +import campus.tech.kakao.map.Model.SearchCallback +import campus.tech.kakao.map.Model.SearchResult + +class MainViewModel: ViewModel() { + + private var listener: (UiState) -> Unit = {} + private lateinit var call: Call + + private var uiState: UiState = UiState( + locationList = emptyList(), + isShowText = false + ) + + fun setUiStateChangedListener(listener: (UiState) -> Unit) { + this.listener = listener + } + + fun searchLocations(key: String) { + val apiService = RetrofitClient.instance + call = apiService.searchPlaces(BuildConfig.API_KEY, key) + call.enqueue(SearchCallback(this)) + } + + fun updateSearchResults(results: List) { + val locationList = mutableListOf() + for (result in results) { + locationList.add( + LocationData( + result.place_name, + result.address_name, + result.category_group_name + ) + ) + } + uiState = UiState( + locationList = locationList, + isShowText = locationList.isEmpty() + ) + + listener(uiState) + } + + override fun onCleared() { + super.onCleared() + if (::call.isInitialized) { + call.cancel() + } + } + + data class UiState( + val locationList: List, + val isShowText: Boolean + ) +} \ No newline at end of file diff --git a/app/src/main/java/campus/tech/kakao/map/SearchViewAdapter.kt b/app/src/main/java/campus/tech/kakao/map/viewmodel/SearchViewAdapter.kt similarity index 90% rename from app/src/main/java/campus/tech/kakao/map/SearchViewAdapter.kt rename to app/src/main/java/campus/tech/kakao/map/viewmodel/SearchViewAdapter.kt index 2aa51ce5..9bccf0ea 100644 --- a/app/src/main/java/campus/tech/kakao/map/SearchViewAdapter.kt +++ b/app/src/main/java/campus/tech/kakao/map/viewmodel/SearchViewAdapter.kt @@ -1,4 +1,4 @@ -package campus.tech.kakao.map +package campus.tech.kakao.map.viewmodel import android.view.LayoutInflater import android.view.View @@ -6,6 +6,8 @@ import android.view.ViewGroup import android.widget.Button import android.widget.TextView import androidx.recyclerview.widget.RecyclerView +import campus.tech.kakao.map.Model.LocationData +import campus.tech.kakao.map.R class SearchViewAdapter( private val searchList: ArrayList, diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fc40717b..5c567b0f 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,7 +6,7 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity"> + tools:context=".view.MainActivity"> + diff --git a/app/src/main/res/layout/activity_map.xml b/app/src/main/res/layout/activity_map.xml new file mode 100644 index 00000000..edfd3572 --- /dev/null +++ b/app/src/main/res/layout/activity_map.xml @@ -0,0 +1,48 @@ + + + + + + + + +