Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

부산대 Android_이지은 3주차 1단계 #98

Open
wants to merge 7 commits into
base: jieunyume
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
# android-map-search
## step0 - 기본 코드 준비
## step1 - 카카오 로컬 API
1. 카카오 로컬 API 사용하기
- 검색어에 맞는 검색 결과가 15개 이상 표시된다.
- API 키는 외부에 노출되지 않는다.
20 changes: 16 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties

plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
Expand All @@ -14,6 +16,9 @@ android {
versionCode = 1
versionName = "1.0"

resValue("string", "KAKAO_API_KEY", getApiKey("KAKAO_API_KEY"))
resValue("string", "KAKAO_REST_API_KEY", getApiKey("KAKAO_REST_API_KEY"))
JieunYume marked this conversation as resolved.
Show resolved Hide resolved

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

Expand All @@ -39,6 +44,8 @@ android {
}
}

fun getApiKey(key: String): String = gradleLocalProperties(rootDir, providers).getProperty(key)

dependencies {

implementation("androidx.core:core-ktx:1.12.0")
Expand All @@ -47,11 +54,16 @@ dependencies {
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation("androidx.datastore:datastore-preferences:1.0.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("androidx.activity:activity:1.8.0")
implementation("com.android.identity:identity-credential-android:20231002")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

implementation("androidx.activity:activity-ktx:1.1.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")

implementation("com.kakao.sdk:v2-all:2.20.3")
// retrofit
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
}
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Map"
tools:targetApi="31">
tools:targetApi="31"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:exported="true">
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/Contract.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package campus.tech.kakao.map

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"
}
}
13 changes: 13 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/Location.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package campus.tech.kakao.map

data class Location(
val title: String,
val address: String,
val category: String
){
companion object {
fun toLocation(locationDto: LocationDto): Location {
return Location(locationDto.place_name, locationDto.address_name, locationDto.category_group_name)
}
}
}
49 changes: 49 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/LocationAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package campus.tech.kakao.map

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import campus.tech.kakao.map.LocationAdapter.LocationHolder

class LocationAdapter(
private val itemSelectedListener: OnItemSelectedListener
) : ListAdapter<Location, LocationHolder>(
object : DiffUtil.ItemCallback<Location>() {
override fun areItemsTheSame(oldItem: Location, newItem: Location): Boolean {
return oldItem.title == newItem.title
}
override fun areContentsTheSame(oldItem: Location, newItem: Location): Boolean {
return oldItem == newItem
}
}) {
inner class LocationHolder(
itemView: View,
itemSelectedListener: OnItemSelectedListener
) : RecyclerView.ViewHolder(itemView) {
val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
val addressTextView: TextView = itemView.findViewById(R.id.addressTextView)
val categoryTextView: TextView = itemView.findViewById(R.id.categoryTextView)
JieunYume marked this conversation as resolved.
Show resolved Hide resolved

init {
itemView.setOnClickListener {
itemSelectedListener.insertSavedLocation(getItem(bindingAdapterPosition).title)
}
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocationHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_location, parent, false)
return LocationHolder(view, itemSelectedListener)
}

override fun onBindViewHolder(holder: LocationHolder, position: Int) {
val location = getItem(position)
holder.titleTextView.text = location.title
holder.addressTextView.text = location.address
holder.categoryTextView.text = location.category
}
}
42 changes: 42 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/LocationDbHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package campus.tech.kakao.map

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.util.Log
import campus.tech.kakao.map.Contract.LocationEntry
import campus.tech.kakao.map.Contract.SavedLocationEntry


private const 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 const val SQL_DELETE_LOCATION_TABLE = "DROP TABLE IF EXISTS ${LocationEntry.TABLE_NAME}"

private const val SQL_CREATE_SAVED_LOCATION_TABLE =
"CREATE TABLE ${SavedLocationEntry.TABLE_NAME} (" +
"${SavedLocationEntry.COLUMN_NAME_TITLE} TEXT primary key"+
")"

private const val SQL_DELETE_SAVED_LOCATION_TABLE =
"DROP TABLE IF EXISTS ${SavedLocationEntry.TABLE_NAME}"
class LocationDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {

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"
}
}
138 changes: 138 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/LocationLocalRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package campus.tech.kakao.map

import android.content.ContentValues
import android.util.Log
import campus.tech.kakao.map.Contract.LocationEntry
import campus.tech.kakao.map.Contract.SavedLocationEntry

class LocationLocalRepository(private val dbHelper : LocationDbHelper) {

fun insertLocation(title: String, address: String, category: String): Long {
val db = dbHelper.writableDatabase

val values = ContentValues().apply {
put(LocationEntry.COLUMN_NAME_TITLE, title)
put(LocationEntry.COLUMN_NAME_ADDRESS, address)
put(LocationEntry.COLUMN_NAME_CATEGORY, category)
}

return db.insert(LocationEntry.TABLE_NAME, null, values)
}

fun getLocationAll(): MutableList<Location> {
val db = dbHelper.readableDatabase

val projection = arrayOf(
LocationEntry.COLUMN_NAME_TITLE,
LocationEntry.COLUMN_NAME_ADDRESS,
LocationEntry.COLUMN_NAME_CATEGORY
)

val sortOrder = "${LocationEntry.COLUMN_NAME_TITLE} ASC"

val cursor = db.query(
LocationEntry.TABLE_NAME,
projection,
null,
null,
null,
null,
sortOrder
)

val results = mutableListOf<Location>()
with(cursor) {
while (moveToNext()) {
val title = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_TITLE))
val address = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_ADDRESS))
val category = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_CATEGORY))
results.add(Location(title, address, category))
}
}
cursor.close()
return results
}

fun insertSavedLocation(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<SavedLocation> {
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<SavedLocation>()
with(cursor) {
while (moveToNext()) {
val title = getString(getColumnIndexOrThrow(SavedLocationEntry.COLUMN_NAME_TITLE))
results.add(SavedLocation(title))
}
}
cursor.close()
return results
}

fun deleteSavedLocation(title: String) {
val db = dbHelper.writableDatabase

val selection = "${SavedLocationEntry.COLUMN_NAME_TITLE} = ?"
val selectionArgs = arrayOf(title)

db.delete(SavedLocationEntry.TABLE_NAME, selection, selectionArgs)
}

fun searchLocations(query: String): List<Location> {
if(query.isBlank()){
return emptyList()
}
val db = dbHelper.readableDatabase

val projection = arrayOf(
LocationEntry.COLUMN_NAME_TITLE,
LocationEntry.COLUMN_NAME_ADDRESS,
LocationEntry.COLUMN_NAME_CATEGORY
)

val selection = "${LocationEntry.COLUMN_NAME_TITLE} LIKE '%' || ? || '%' OR ${LocationEntry.COLUMN_NAME_ADDRESS} LIKE '%' || ? || '%' OR ${LocationEntry.COLUMN_NAME_CATEGORY} LIKE '%' || ? || '%'"
val selectionArgs = arrayOf(query, query, query)

val cursor = db.query(
LocationEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
null
)
val results = mutableListOf<Location>()
with(cursor) {
while (moveToNext()) {
val title = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_TITLE))
val address = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_ADDRESS))
val category = getString(getColumnIndexOrThrow(LocationEntry.COLUMN_NAME_CATEGORY))
results.add(Location(title, address, category))
}
}
cursor.close()
return results
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package campus.tech.kakao.map

import android.util.Log
import campus.tech.kakao.map.retrofit.KakaoAPI
import campus.tech.kakao.map.retrofit.RetrofitInstance

class LocationRemoteRepository {
JieunYume marked this conversation as resolved.
Show resolved Hide resolved
private val RESULT_SIZE = 15
JieunYume marked this conversation as resolved.
Show resolved Hide resolved
private val client = RetrofitInstance.getInstance().create(KakaoAPI::class.java)

suspend fun getLocations(restApiKey:String, keyword: String): List<Location> {
val response = client.searchFromKeyword(restApiKey, keyword, RESULT_SIZE)
val locationDtos: List<LocationDto> = response.body()?.documents ?: emptyList()
Log.d("jieun", "locationDtos: " + locationDtos)

return toLocations(locationDtos)
JieunYume marked this conversation as resolved.
Show resolved Hide resolved
}

private fun toLocations(locationDtos: List<LocationDto>) =
locationDtos.map {
Location.toLocation(it)
}
}
Loading