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 김민혁 4주차 과제 STEP1 #54

Open
wants to merge 55 commits into
base: kyleidea1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
04c907f
Initial commit
MyStoryG Jun 15, 2024
9f4d9a7
docs: add project overview of STEP1 to README
kyleidea1 Jul 1, 2024
b2b33de
feat(view): Add activity_search.xml view for SearchView
kyleidea1 Jul 1, 2024
8eac791
feat(view): Add SearchMainActivity.kt
kyleidea1 Jul 1, 2024
a21a7c2
feat(view): Add DBHelper.kt which is local storage of places.
kyleidea1 Jul 2, 2024
f5e9623
feat: Added 9 data into Place.db
kyleidea1 Jul 2, 2024
b3a1339
docs: Modified README.md
kyleidea1 Jul 2, 2024
deb1a3c
feat: Create STEP1 branch and move existing work to it
kyleidea1 Jul 2, 2024
d3adda3
docs: Modified README.md for STEP2
kyleidea1 Jul 2, 2024
b959286
feat(db): add SearchHistoryDBHelper and initial place data
kyleidea1 Jul 5, 2024
a03fdc7
refactor(db): extract Place data class to separate file
kyleidea1 Jul 5, 2024
84ad89b
feat(ui): add SearchHistoryRecyclerViewAdapter for displaying search …
kyleidea1 Jul 5, 2024
496dd1d
fix(SearchActivity): Fix filtering logic to correctly filter search r…
kyleidea1 Jul 5, 2024
a62f21e
refactor: Split DBHelper into PlaceDBHelper and SearchHistoryDBHelper
kyleidea1 Jul 5, 2024
b043982
Refactor XML layout to fix visibility bug in SearchActivity
kyleidea1 Jul 5, 2024
7c26fd4
Add: Extracted Korean strings into string resources and updated decla…
kyleidea1 Jul 5, 2024
e404119
Add: Extracted Korean strings into string resources and updated decla…
kyleidea1 Jul 5, 2024
61d5adc
Refactor: Changed variable names to enhance consistency
kyleidea1 Jul 5, 2024
62e3846
docs: Modified README.md
kyleidea1 Jul 5, 2024
bbf77e9
Initial commit
MyStoryG Jul 7, 2024
9dd9533
Added missing files
kyleidea1 Jul 8, 2024
f62e568
chore(project): import code from android-map-keyword project
kyleidea1 Jul 9, 2024
811b9bc
docs: Added README.md of android-search-STEP1
kyleidea1 Jul 9, 2024
7499b1c
임시저장
kyleidea1 Jul 9, 2024
f898541
임시저장
kyleidea1 Jul 9, 2024
bb225cd
feat: Add Kakao Local API dependencies to Gradle files
kyleidea1 Jul 9, 2024
a5f25d9
feat(dto): Add DTO classes for Kakao API response
kyleidea1 Jul 10, 2024
cf5667f
feat(network): Implement KakaoLocalApi interface for API communication
kyleidea1 Jul 10, 2024
0d7f0e6
feat(repository): Create KakaoRepository for data retrieval from Kaka…
kyleidea1 Jul 10, 2024
81059af
feat: Added KyleMaps application class with Retrofit instance
kyleidea1 Jul 10, 2024
ec020cd
feat: added application icon image resources
kyleidea1 Jul 10, 2024
e4f5aa3
feat: added retrofit instance into KyleMaps.kt
kyleidea1 Jul 10, 2024
bebe6de
feat: remove activity_main_search
kyleidea1 Jul 12, 2024
e4839e6
remove: PlaceDBHelper.kt
kyleidea1 Jul 12, 2024
d7d4f93
feat: Added dto package and its elements
kyleidea1 Jul 12, 2024
70486ba
chore: added dependencies to use Kakao APIs
kyleidea1 Jul 12, 2024
a0be820
feat: Added KakaoRepository.kt
kyleidea1 Jul 12, 2024
cde3ac6
feat: Added KyleMaps.kt
kyleidea1 Jul 12, 2024
4f8a9f6
feat: Added drawable resources
kyleidea1 Jul 12, 2024
43a339a
feat: Added MapActivity and its layout
kyleidea1 Jul 12, 2024
2fb4d24
feat: Modified activity_search.xml and DBHelpers
kyleidea1 Jul 12, 2024
6b912fa
docs: updated README.md
kyleidea1 Jul 12, 2024
7bcbe37
commit before fixing bug
kyleidea1 Jul 16, 2024
a5a9a26
finished STEP0
kyleidea1 Jul 16, 2024
2c28c03
docs: updated README.md
kyleidea1 Jul 16, 2024
7260c3e
temporal save point
kyleidea1 Jul 17, 2024
0f1c076
feat: updated result recycler view's item click event
kyleidea1 Jul 17, 2024
2a4502e
feat: added marker when selected an item of result recycler view
kyleidea1 Jul 17, 2024
f002a98
feat: implemented function that remembering last place's position whe…
kyleidea1 Jul 17, 2024
a30c46c
feat: added activity appears when error occurs
kyleidea1 Jul 18, 2024
807dc2d
feat: added bottom sheet dialog and its components
kyleidea1 Jul 18, 2024
34cf47c
style: get rid of paddings of bottomsheet layout
kyleidea1 Jul 18, 2024
4594193
Merge branch 'kyleidea1' into STEP1
kyleidea1 Jul 18, 2024
7c5707f
fix: fixed bug that MapActivities doesn't destory
kyleidea1 Jul 18, 2024
873df85
Merge remote-tracking branch 'origin/STEP1' into STEP1
kyleidea1 Jul 18, 2024
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
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# android-map-lacation STEP0
# android-map-location STEP1

## 개요

`android-map-search STEP2`의 작업 사항을 옮겨 온 브랜치입니다.
`android-map-location STEP1`에서는 지도 화면과 검색 화면 간의 네비게이션을 추가하고, 검색 기능을 확장하여 저장된 검색어 목록을 관리합니다.

## 기능

- **지도 화면 표시**: 앱을 처음 실행하면 지도 화면이 표시됩니다.
- **검색 화면 이동**: 검색창을 선택하면 검색 화면으로 이동합니다.
- **뒤로 가기로 지도 화면 복귀**: 검색 화면에서 뒤로 가기를 하면 지도 화면으로 돌아옵니다.
- **검색 기능 확장**: 카카오로컬 API를 사용하여 검색어를 입력하면 검색 결과를 표시합니다.
- 검색 결과는 15개 이상 표시됩니다.
- 검색 결과 목록은 세로 스크롤이 가능합니다.
- 검색어는 `X` 버튼을 눌러서 삭제할 수 있습니다.
- **검색 결과 선택**: 검색 결과 목록에서 항목을 선택하면 선택된 항목이 검색어 저장 목록에 추가됩니다.
- 저장된 검색어 목록은 가로 스크롤이 가능합니다.
- 저장된 검색어는 `X` 버튼을 눌러서 삭제할 수 있습니다.
- 저장된 검색어는 앱을 재실행해도 유지됩니다.

## 기능 요구 사항

- 저장된 검색어를 선택하면 해당 검색어의 검색 결과가 표시됩니다.
- 검색 결과 목록 중 하나의 항목을 선택하면 해당 항목의 위치를 지도에 표시합니다.
- 앱 종료 시 마지막 위치를 저장하여 다시 앱 실행 시 해당 위치로 포커스합니다.
- 카카오지도 onMapError() 호출 시 에러 화면을 보여줍니다.

## 프로그래밍 요구 사항

- BottomSheet를 사용합니다.
- 카카오 API 사용을 위한 앱 키를 외부에 노출하지 않습니다.
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".AuthenticationErrorActivity" />
<activity android:name=".SearchActivity" />

</application>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package campus.tech.kakao.map

import android.os.Bundle
import android.widget.ImageView
import android.widget.ProgressBar
import androidx.appcompat.app.AppCompatActivity

class AuthenticationErrorActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_authentication_error)

val retryIcon: ImageView = findViewById(R.id.retry_icon)
val loadingIndicator: ProgressBar = findViewById(R.id.loading_indicator)

retryIcon.setOnClickListener {
loadingIndicator.visibility = ProgressBar.VISIBLE
}
}
}
96 changes: 91 additions & 5 deletions app/src/main/java/campus/tech/kakao/map/MapActivity.kt
Original file line number Diff line number Diff line change
@@ -1,42 +1,127 @@
package campus.tech.kakao.map

import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomsheet.BottomSheetDialog
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.label.LabelOptions
import com.kakao.vectormap.label.LabelStyle
import com.kakao.vectormap.label.LabelStyles

class MapActivity : AppCompatActivity() {

private lateinit var mapView: MapView
private lateinit var searchButton: LinearLayout
private lateinit var bottomSheet: BottomSheetDialog

private var name: String? = null
private var address: String? = null
private var kakaoMap: KakaoMap? = null
private var latitude: String? = null
private var longitude: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
Log.d("here", "I'm in MapActivity")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)

mapView = findViewById(R.id.map_view)
searchButton = findViewById(R.id.search_button)
val bottomSheetLayout = layoutInflater.inflate(R.layout.layout_map_bottom_sheet_dialog, null)
bottomSheet = BottomSheetDialog(this)
Copy link

Choose a reason for hiding this comment

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

미리 셋팅해야할게 있는게 아니라면
bottomSheet가 필요할때 객체를 생성하는건 어떨까요?


val defLat = "35.231627"
val defLng = "129.084020"
val lastLatLng = getSharedPreferences("lastLatLng", MODE_PRIVATE)
Copy link

Choose a reason for hiding this comment

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

sharedPreference에선 double형을 읽고 쓸수가 없어서 string type으로 관리하셨군요
잘 하셨는데 읽고 쓰는부분을 캡슐화하면 어떨까요?
함수도 좋고 클래스도 좋습니다. 내부에서 string으로 관리하는지는 상관하지 않고
사용하는곳에서는 double로 간단히 쓸수 있게끔요

latitude = lastLatLng.getString("lastLat", defLat) // 기본 위치: 부산대.
longitude = lastLatLng.getString("lastLng", defLng) // 기본 위치: 부산대.
Log.d("sharedPreference", "saveLatLng밖에꺼: $latitude")
Log.d("sharedPreference", "saveLatLng밖에꺼: $longitude")

// Intent에서 값을 가져옴
intent?.let {
latitude = it.getStringExtra("mapY") ?: latitude
longitude = it.getStringExtra("mapX") ?: longitude
Copy link

Choose a reason for hiding this comment

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

intent extra에 들어가는 key값들은 보내는쪽과 받는쪽 둘다 사용하기에 상수로 관리하시면 좋습니다
나중에 바뀔일이 생기면 추적하기 힘들어져서요

name = it.getStringExtra("name")
address = it.getStringExtra("address")
}

Log.d("latlngcheck1", "Intent에서 가져온 값: $latitude, $longitude, $name")

mapView.start(
object : MapLifeCycleCallback() {
override fun onMapDestroy() {}
override fun onMapError(error: Exception) {}
override fun onMapDestroy() {
}

override fun onMapError(error: Exception) {
Log.e("MapActivity", "Map error: ${error.message}")
val errorIntent = Intent(this@MapActivity, AuthenticationErrorActivity::class.java)
startActivity(errorIntent)
finish()
}

},
object : KakaoMapReadyCallback() {
override fun onMapReady(kakaoMap: KakaoMap) {}
override fun onMapReady(kakaoMap: KakaoMap) {
Log.d("latlngcheck2", "onMapReady에서: $latitude, $longitude")
[email protected] = kakaoMap
name?.let {
addMarker(kakaoMap, latitude ?: defLat, longitude ?: defLng, it)
saveLatLng(latitude ?: defLat, longitude ?: defLng) // 마커 찍은 위치를 sharedPreference에 저장
val tvBottomSheetPlaceName = bottomSheetLayout.findViewById<TextView>(R.id.bottomSheetPlaceName)
val tvBottomSheetPlaceAddress = bottomSheetLayout.findViewById<TextView>(R.id.bottomsheetPlaceAddress)
tvBottomSheetPlaceName.text = name
tvBottomSheetPlaceAddress.text = address
bottomSheet.setContentView(bottomSheetLayout)
bottomSheet.show()
}
}

override fun getPosition(): LatLng {
Log.d("latlngcheck3", "getPosition에서: $latitude, $longitude")
val lat = (latitude ?: defLat).toDouble()
val lng = (longitude ?: defLng).toDouble()
return LatLng.from(lat, lng)
}
}
)

searchButton.setOnClickListener {
val intent = Intent(this@MapActivity, SearchActivity::class.java)
startActivity(intent)
val mapToSearchIntent = Intent(this@MapActivity, SearchActivity::class.java)
startActivity(mapToSearchIntent)
finish()
}
}

private fun addMarker(kakaoMap: KakaoMap, latitude: String, longitude: String, name: String) {
Log.d("addMarker", "addMarker: $latitude, $longitude, $name")
val styles = kakaoMap.labelManager?.addLabelStyles(LabelStyles.from(LabelStyle.from(R.drawable.location_marker).setTextStyles(25, Color.BLACK)))
val options = LabelOptions.from(LatLng.from(latitude.toDouble(), longitude.toDouble())).setStyles(styles)
val layer = kakaoMap.labelManager?.layer
val label = layer?.addLabel(options)
label?.changeText(name)
}

private fun saveLatLng(latitude: String, longitude: String) {
val sharedPreference = getSharedPreferences("lastLatLng", MODE_PRIVATE)
sharedPreference.edit().apply {
putString("lastLat", latitude)
putString("lastLng", longitude)
apply()
}
Log.d("sharedPreference", "saveLatLng: ${sharedPreference.getString("lastLat", "default_lat")}")
Log.d("sharedPreference", "saveLatLng: ${sharedPreference.getString("lastLng", "default_lng")}")
}

override fun onResume() {
super.onResume()
mapView.resume()
Expand All @@ -46,4 +131,5 @@ class MapActivity : AppCompatActivity() {
super.onPause()
mapView.pause()
}

}
27 changes: 25 additions & 2 deletions app/src/main/java/campus/tech/kakao/map/SearchActivity.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package campus.tech.kakao.map

import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.widget.ImageButton
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
Expand All @@ -23,9 +25,14 @@ class SearchActivity : AppCompatActivity() {
private lateinit var searchHistoryList: MutableList<Place>
private lateinit var kakaoRepository: KakaoRepository
private lateinit var backButton: ImageButton
private lateinit var mapX: String
private lateinit var mapY: String
private lateinit var name: String
private lateinit var address: String

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("here", "I'm in SearchActivity")
setContentView(R.layout.activity_search)

searchView = findViewById(R.id.search_view)
Expand All @@ -45,6 +52,8 @@ class SearchActivity : AppCompatActivity() {
onItemClick = { place ->
searchHistoryDB.insertSearchHistory(place)
updateSearchHistoryRecyclerView(place)
updateMapPosition(place)
goBackToMap()
}
)
resultRecyclerView.adapter = resultRecyclerViewAdapter
Expand Down Expand Up @@ -113,7 +122,21 @@ class SearchActivity : AppCompatActivity() {
}

private fun goBackToMap() {
val intent = Intent(this, MapActivity::class.java)
startActivity(intent)
val searchToMapIntent = Intent(this, MapActivity::class.java)
searchToMapIntent.putExtra("mapX",mapX)
searchToMapIntent.putExtra("mapY",mapY)
searchToMapIntent.putExtra("name",name)
searchToMapIntent.putExtra("address", address)
Log.d("goBackToMap", "goBackToMap: $mapX, $mapY")
finish()
startActivity(searchToMapIntent)
}

private fun updateMapPosition(place: Place) {
mapX = place.x
mapY = place.y
name = place.place_name
address = place.address_name
Log.d("goBackToMap", "updateMapPosition: $mapX, $mapY")
}
}
13 changes: 13 additions & 0 deletions app/src/main/res/drawable/bottom_sheet_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

<solid
android:color="@color/white"/>

<padding
android:left="10dp"
android:right="10dp"
android:top="10dp"
android:bottom="10dp" />

</shape>
Binary file added app/src/main/res/drawable/ic_retry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/location_marker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions app/src/main/res/layout/activity_authentication_error.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".AuthenticationErrorActivity">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/error_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="지도 인증을 실패 했습니다.\n다시 시도해 주세요."
android:textSize="16sp"
android:gravity="center"
android:padding="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/error_details"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

<TextView
android:id="@+id/error_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MapAuthException(401): Unauthorized"
android:textSize="14sp"
android:gravity="center"
android:padding="8dp"
app:layout_constraintTop_toBottomOf="@id/error_message"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

<ImageView
android:id="@+id/retry_icon"
android:layout_width="48dp"
android:layout_height="48dp"
app:srcCompat="@drawable/ic_retry"
android:padding="16dp"
app:layout_constraintTop_toBottomOf="@id/error_details"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

<ProgressBar
android:id="@+id/loading_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/retry_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:indeterminate="true"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
41 changes: 41 additions & 0 deletions app/src/main/res/layout/layout_map_bottom_sheet_dialog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bottom_sheet_layout"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@drawable/bottom_sheet_background"
app:behavior_hideable="false"
app:behavior_peekHeight="150dp"
android:orientation="vertical"
android:padding="10dp"
android:clickable="false"
android:focusable="false"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/bottomSheetPlaceName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="부산대학교"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<TextView
android:id="@+id/bottomsheetPlaceAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="부산 금정구 장전동 40"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="@id/bottomSheetPlaceName"
app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>