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

[강원대 안드로이드 주민철] 4주차 과제 스텝2 #82

Open
wants to merge 21 commits into
base: joominchul
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8ae483b
Rename[]: 이동 3주차 과제 내용
joominchul Jul 15, 2024
7c76c97
Refactor[]: 변경 warning 부분
joominchul Jul 15, 2024
ba17eaf
Docs[README.md]: 추가 요구 사항 정리
joominchul Jul 15, 2024
4c2c111
Feat[]: 추가 선택 항목 위치, 정보를 지도에 표시
joominchul Jul 16, 2024
c69b181
Refactor[]: 변경 warning 요소
joominchul Jul 16, 2024
2613a7d
Design[bottom_sheet]: 변경 패딩
joominchul Jul 16, 2024
daefd35
Feat[]: 추가 저장된 검색어 선택 시 해당 검색어 검색 결과 표시
joominchul Jul 16, 2024
4bc4784
Feat[]: 추가 카카오지도 onMapError() 호출 시 에러 화면 표시
joominchul Jul 17, 2024
47c2fca
Docs[README.md]: 2단계 요구 사항 작성
joominchul Jul 18, 2024
3c24b64
Test[FunTest]: 추가 검색어를_입력하면_검색_결과_표시 함수
joominchul Jul 18, 2024
2c5abdf
Test[SearchActivityUITest]: 추가 입력한_검색어_X_눌러_삭제 함수
joominchul Jul 19, 2024
504a272
Test[SearchActivityUITest]: 추가 검색_결과_클릭시 함수
joominchul Jul 19, 2024
e3da3c0
Test[SearchActivityUITest]: 변경 검색_결과_클릭시 함수 이름
joominchul Jul 19, 2024
a6c8a27
Test[SearchActivityUITest]: 추가 검색창_표시_확인
joominchul Jul 19, 2024
5bb33ef
Test[SearchActivityUITest]: 추가 X_버튼_확인
joominchul Jul 19, 2024
bacca26
Test[MapActivityUITest]: 추가 검색창_보이는지_확인 함수
joominchul Jul 19, 2024
3c189cb
Merge branch 'joominchul' into feat-joominchul-step2
joominchul Jul 19, 2024
236eb18
Fix[MainViewModel]: 수정 wordfromDocument
joominchul Jul 19, 2024
42b46d9
Feat[FunTest]: 추가 검색어_저장_되는지_확인
joominchul Jul 19, 2024
ef22501
Merge remote-tracking branch 'origin/feat-joominchul-step2' into feat…
joominchul Jul 19, 2024
a88c1a7
Feat[FunTest]: 추가 검색어_저장_되는지_확인 함수에 삭제 확인 기능
joominchul Jul 19, 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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
# android-map-location

## 1단계 - 카카오맵 API 심화
### 기능 요구 사항
- 저장된 검색어를 선택하면 해당 검색어의 검색 결과가 표시된다.
- 검색 결과 목록 중 하나의 항목을 선택하면 해당 항목의 위치를 지도에 표시한다.
- 앱 종료 시 마지막 위치를 저장하여 다시 앱 실행 시 해당 위치로 포커스 한다.
- 카카오지도 onMapError() 호출 시 에러 화면을 보여준다.
### 프로그래밍 요구 사항
- BottomSheet를 사용한다.
- 카카오 API 사용을 위한 앱 키를 외부에 노출하지 않는다.
- 가능한 MVVM 아키텍처 패턴을 적용하도록 한다.
- 코드 컨벤션을 준수하며 프로그래밍한다.

## 2단계 - 테스트
### 기능 요구 사항
- 테스트 코드를 작성한다.
### 프로그래밍 요구 사항
- 카카오 API 사용을 위한 앱 키를 외부에 노출하지 않는다.
- 가능한 MVVM 아키텍처 패턴을 적용하도록 한다.
- 코드 컨벤션을 준수하며 프로그래밍한다.
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ android {
viewBinding = true
buildConfig = true
}
testOptions {
animationsDisabled = true
}
}

dependencies {
Expand Down Expand Up @@ -75,5 +78,6 @@ dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test:rules:1.5.0")
androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.5.1")
}
fun getApiKey(key: String): String = gradleLocalProperties(rootDir, providers).getProperty(key)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package campus.tech.kakao.map

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import org.junit.Rule
import org.junit.Test


class MapActivityUITest {
@get:Rule
val mapActivityRule = ActivityScenarioRule(MapActivity::class.java)

@Test
fun 검색창_보이는지_확인(){
val mapSearch = onView(withId(R.id.map_search))
mapSearch.check(matches(isDisplayed()))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package campus.tech.kakao.map

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.core.AllOf.allOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class SearchActivityUITest {
@get:Rule
val searchActivityRule = ActivityScenarioRule(SearchActivity::class.java)

@Test
fun 검색창_표시_확인(){
val search = onView(withId(R.id.search))
search.check(matches(isDisplayed()))
}

@Test
fun X_버튼_확인(){
val clear = onView(withId(R.id.search_clear))
clear.check(matches(isDisplayed()))
}
@Test
fun 입력한_검색어_X_눌러_삭제() {
val query = "박물관"
val search = onView(allOf(withId(R.id.search), withParent(withId(R.id.search_main))))
val clear = onView(withId(R.id.search_clear))
search.perform(replaceText(query))
clear.perform(click())
search.check(matches(withText("")))
}

@Test
fun 검색_결과_클릭시_검색_결과_나오는지_확인(){
val query = "박물관"
val search = onView(allOf(withId(R.id.search), withParent(withId(R.id.search_main))))
search.perform(replaceText(query))
val searchResult = onView(withId(R.id.search_result_recycler_view))
searchResult.check(matches(isDisplayed()))
}

}
11 changes: 0 additions & 11 deletions app/src/main/java/campus/tech/kakao/map/MainActivity.kt

This file was deleted.

17 changes: 13 additions & 4 deletions app/src/main/java/campus/tech/kakao/map/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package campus.tech.kakao.map
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import campus.tech.kakao.map.DBHelper.SearchWordDbHelper
import campus.tech.kakao.map.DTO.Document
import campus.tech.kakao.map.DTO.SearchWord
import campus.tech.kakao.map.dbHelper.SearchWordDbHelper
import campus.tech.kakao.map.dto.Document
import campus.tech.kakao.map.dto.MapPositionContract
import campus.tech.kakao.map.dto.SearchWord
import campus.tech.kakao.map.MyApplication.Companion.mapPosition
import campus.tech.kakao.map.RetrofitData.Companion.getInstance

class MainViewModel(application: Application): AndroidViewModel(application) {
Expand All @@ -21,7 +23,7 @@ class MainViewModel(application: Application): AndroidViewModel(application) {
}

private fun wordfromDocument(document: Document): SearchWord {
return SearchWord(document.placeName, document.categoryGroupName, document.addressName)
return SearchWord(document.placeName, document.addressName, document.categoryGroupName)
}
fun deleteWord(word: SearchWord){
wordDbHelper.deleteWord(word)
Expand All @@ -39,4 +41,11 @@ class MainViewModel(application: Application): AndroidViewModel(application) {
super.onCleared()
wordDbHelper.close()
}

fun getMapInfo(document: Document){
mapPosition.setPreferences(MapPositionContract.PREFERENCE_KEY_LATITUDE, document.latitude)
mapPosition.setPreferences(MapPositionContract.PREFERENCE_KEY_LONGITUDE, document.longitude)
mapPosition.setPreferences(MapPositionContract.PREFERENCE_KEY_PLACENAME, document.placeName)
mapPosition.setPreferences(MapPositionContract.PREFERENCE_KEY_ADDRESSNAME, document.addressName)
}
}
101 changes: 100 additions & 1 deletion app/src/main/java/campus/tech/kakao/map/MapActivity.kt
Original file line number Diff line number Diff line change
@@ -1,54 +1,153 @@
package campus.tech.kakao.map

import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import campus.tech.kakao.map.MyApplication.Companion.mapPosition
import com.google.android.material.bottomsheet.BottomSheetBehavior
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.CameraUpdate
import com.kakao.vectormap.camera.CameraUpdateFactory
import com.kakao.vectormap.label.Label
import com.kakao.vectormap.label.LabelLayer
import com.kakao.vectormap.label.LabelOptions
import com.kakao.vectormap.label.LabelStyle
import com.kakao.vectormap.label.LabelStyles
import java.lang.Exception

class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
private var map: KakaoMap? = null
private lateinit var searchBar: LinearLayout
private var latitude = 37.402005
private var longitude = 127.108621
private lateinit var placeName:String
private lateinit var addressName:String
private var styles: LabelStyles? = null
private lateinit var options:LabelOptions
private var layer: LabelLayer? = null
private var label: Label? = null
private lateinit var bitmapImage: Bitmap
private lateinit var markerImage: Bitmap
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
private val bottomSheet by lazy { findViewById<LinearLayout>(R.id.bottom_sheet) }
private val bottomSheetName by lazy { findViewById<TextView>(R.id.name) }
private val bottomSheetAddress by lazy { findViewById<TextView>(R.id.address) }
companion object{
var documentClicked = false
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.map_view)
setMapInfo()
mapView.start(object : MapLifeCycleCallback() {
override fun onMapDestroy() {

}

override fun onMapError(p0: Exception?) {
Log.e("MapActivity", "onMapError: ${p0?.message}", p0)
setContentView(R.layout.map_error)
val errorText = findViewById<TextView>(R.id.map_error_text)
errorText.text = p0?.message
}

}, object: KakaoMapReadyCallback() {
override fun onMapReady(kakaoMap: KakaoMap) {
map = kakaoMap
}

override fun getPosition(): LatLng {
return LatLng.from(latitude, longitude)
}

override fun getZoomLevel(): Int {
return 17
}
})
searchBar = findViewById(R.id.search_bar)
searchBar.setOnClickListener {
val intent = Intent(this@MapActivity, SearchActivity::class.java)
startActivity(intent)
}
initBottomSheet()
}

override fun onResume() {
super.onResume()
setMapInfo()
mapView.resume()
if(documentClicked){
makeMarker()
setBottomSheet()
documentClicked = false
}
else{
layer?.remove(label)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
}
val cameraUpdate: CameraUpdate = CameraUpdateFactory.newCenterPosition(LatLng.from(latitude, longitude))
map?.moveCamera(cameraUpdate)

}

override fun onPause() {
super.onPause()
mapView.pause()
}

private fun setMapInfo(){
latitude = mapPosition.getPreferences("latitude","37.406960").toDouble()
longitude = mapPosition.getPreferences("longitude","127.110030").toDouble()
placeName = mapPosition.getPreferences("placeName","")
addressName = mapPosition.getPreferences("addressName","")
}

private fun makeMarker(){
bitmapImage = BitmapFactory.decodeResource(resources, R.drawable.marker)
markerImage = Bitmap.createScaledBitmap(bitmapImage, 100, 100, true)
styles = map?.labelManager?.addLabelStyles(LabelStyles.from(LabelStyle.from(markerImage).setTextStyles(40, Color.BLACK)))
if(styles != null){
options = LabelOptions.from(LatLng.from(latitude, longitude)).setStyles(styles).setTexts(placeName)
layer = map?.labelManager?.layer
if(label != null){
layer?.remove(label)
}
label = layer?.addLabel(options)
}
else{
Log.e("MapActivity", "makeMarker: styles is null")
}
}

private fun initBottomSheet(){
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback(){
override fun onStateChanged(bottomSheet: View, newState: Int) {
}

override fun onSlide(bottomSheet: View, slideOffset: Float) {
}

})
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
}

private fun setBottomSheet(){
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
bottomSheetName.text = placeName
bottomSheetAddress.text = addressName
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/MyApplication.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package campus.tech.kakao.map

import android.app.Application
import campus.tech.kakao.map.dto.MapPositionPreferences

import com.kakao.vectormap.KakaoMapSdk

class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
mapPosition = MapPositionPreferences(this)
KakaoMapSdk.init(this, BuildConfig.KAKAO_API_KEY)
}

companion object{
lateinit var mapPosition : MapPositionPreferences
}
}
17 changes: 11 additions & 6 deletions app/src/main/java/campus/tech/kakao/map/RetrofitData.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package campus.tech.kakao.map

import androidx.lifecycle.MutableLiveData
import campus.tech.kakao.map.DTO.Document
import campus.tech.kakao.map.DTO.PlaceResponse
import campus.tech.kakao.map.DTO.UrlContract
import campus.tech.kakao.map.DTO.UrlContract.AUTHORIZATION
import campus.tech.kakao.map.dto.Document
import campus.tech.kakao.map.dto.PlaceResponse
import campus.tech.kakao.map.dto.UrlContract
import campus.tech.kakao.map.dto.UrlContract.AUTHORIZATION
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
Expand All @@ -28,8 +28,13 @@ class RetrofitData private constructor() {
if (response.isSuccessful) {
val documentList = mutableListOf<Document>()
val body = response.body()
body?.documents?.forEach {
documentList.add(Document(it.placeName, it.categoryGroupName, it.addressName))
body?.documents?.forEach {document ->
documentList.add(Document(
document.placeName,
document.categoryGroupName,
document.addressName,
document.longitude,
document.latitude))
}
_documents.value = documentList
}
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/campus/tech/kakao/map/RetrofitService.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package campus.tech.kakao.map

import campus.tech.kakao.map.DTO.PlaceResponse
import campus.tech.kakao.map.dto.PlaceResponse

import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Header
Expand Down
Loading