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주차 2단계(리팩토링 완료) #72

Open
wants to merge 33 commits into
base: jieunyume
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a749e88
docs: README.md 작성
JieunYume Jul 16, 2024
80c47d6
refactor: SavedLocationHolder의 뷰 변수를 by lazy로 변경
JieunYume Jul 17, 2024
3abbc9b
feat: 저장된 검색어의 제목(title)을 전달하는 콜백 함수 구현
JieunYume Jul 17, 2024
b0527b5
feat: itemView에 콜백 함수 적용
JieunYume Jul 17, 2024
adca161
feat: MainActivity에서 콜백 함수 구현해서 LocationViewModel의 searchLocationsFro…
JieunYume Jul 17, 2024
6d7449b
refactor: handleNoResultMessage() 함수를 만들어서 중복 코드 제거
JieunYume Jul 17, 2024
e533b25
feat: searchEditText의 text를 저장된 검색어의 제목(title)로 설정
JieunYume Jul 17, 2024
2eb7b23
feat: searchEditText의 커서 위치 변경
JieunYume Jul 17, 2024
0b3db81
refactor: 콜백 함수 이름을 네이밍 컨벤션에 맞게 변경
JieunYume Jul 17, 2024
193fa3c
refactor: LocationLocalDataSource에서 중복 코드를 함수로 분리
JieunYume Jul 17, 2024
681b945
feat: 키워드로 장소 검색하는 api에서 x, y 값 받아오기
JieunYume Jul 17, 2024
88faf63
feat: 검색 결과 목록 중 하나의 항목을 선택하면 MapActivity로 전환되게 구현
JieunYume Jul 17, 2024
66c2ece
feat: mapView의 좌표를 검색 결과 목록 중 선택된 항목의 좌표로 설정
JieunYume Jul 17, 2024
4f44f65
feat: 검색 결과 목록 중 선택된 항목의 좌표에 KakaoMap label을 추가
JieunYume Jul 18, 2024
97a4556
feat: SharedPreferences에 마지막 좌표 저장 및 불러오기 기능 구현
JieunYume Jul 18, 2024
e1786ed
feat: onMapError() 호출 시 에러 화면 노출
JieunYume Jul 18, 2024
c3d8a4a
feat: MapActivity에서 BottomSheet를 사용하여 위치 정보 노출
JieunYume Jul 18, 2024
038d548
refactor: 뷰 변수들을 by lazy로 정의하도록 변경
JieunYume Jul 18, 2024
116e601
refactor: onMapError()에서 에러 메세지를 띄울 때 스레드를 runOnUiThread로 변경
JieunYume Jul 18, 2024
8b2cd64
refactor: 저장된 데이터가 없을 때 null 처리로 BottomSheet가 뜨지 않게 설정
JieunYume Jul 18, 2024
b99401c
refactor: onMapReady에서 함수 분리
JieunYume Jul 18, 2024
8e0f341
docs: update README.md
JieunYume Jul 19, 2024
5bb2978
feat: mockito 의존성 주입
JieunYume Jul 19, 2024
9cd5962
test: MapAcitivty UI 테스트
JieunYume Jul 19, 2024
2a94f4b
test: MainActivity UI 테스트
JieunYume Jul 19, 2024
985f813
test: LocationLocalDataSource 단위 테스트
JieunYume Jul 19, 2024
bc7d87d
test: LocationLocalDataSource에서 위치 검색 기능 삭제
JieunYume Jul 19, 2024
322ff94
refactor: Location의 lat과 lng의 자료형을 String에서 Double로 변경
JieunYume Jul 23, 2024
f5b77ec
refactor: 2주차 과제 코드 삭제
JieunYume Jul 23, 2024
b5df635
refactor: KakaoSdk의 초기화 코드를 MapActivity에서 App 클래스로 이동
JieunYume Jul 23, 2024
1e95911
refactor: onMapReady() 내에서 runOnUiThread() 삭제
JieunYume Jul 23, 2024
5472947
refactor: SharedPreference를 싱글톤으로 사용하고, MapActivity에서 ViewModel을 통해 사…
JieunYume Jul 24, 2024
af188b9
refactor: DataSource와 Repository 이름 변경
JieunYume Jul 24, 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
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
# android-map-location
# android-map-location
## step1 기능 목록
1. 저장된 검색어 목록에 기능 추가하기
- 저장된 검색어 중 하나를 선택하면 해당 검색어의 검색 결과 목록이 표시된다.
2. 검색 결과 목록에 기능 추가하기
- 검색 결과 목록 중 하나의 항목을 선택하면 해당 항목의 위치를 지도에 표시한다.
3. 마지막 위치 저장하기
- 앱 종료 시 마지막 위치를 SharedPreference 저장하여 다시 앱 실행 시 해당 위치로 포커스 한다.
4. 에러 처리하기
- 카카오지도 onMapError() 호출 시 에러 화면을 보여준다. "지도 인증을 실패했습니다. 다시 시도해주세요. '에러이름(에러코드): 에러메세지'
## step2 기능 목록
- 테스트 코드 - JUnit과 mockito를 이용하여 단위 테스트 코드를 작성한다.
- data source 테스트
- local
- SavedLocation 저장 테스트
- SavedLocation 삭제 테스트
- SavedLocation 조회 테스트
- 마지막 위치 저장 테스트
- UI 테스트 코드
- 검색 페이지
- 검색 결과 목록 테스트
- 검색어 저장 목록 테스트
- clearButton 테스트
- 지도 페이지
- BottomSheet 테스트
- KakaoMap Label 테스트
- 지도 에러 화면 테스트
8 changes: 8 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,16 @@ dependencies {
implementation("androidx.datastore:datastore-preferences:1.0.0")
implementation("com.android.identity:identity-credential-android:20231002")
implementation("androidx.activity:activity:1.9.0")
implementation("androidx.test:core-ktx:1.6.1")
testImplementation("androidx.test.ext:junit:1.1.1")
testImplementation ("org.robolectric:robolectric:4.9")

testImplementation("junit:junit:4.13.2")
testImplementation("org.mockito:mockito-core:5.8.0")
androidTestImplementation("org.mockito:mockito-core:5.8.0")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.2.0")

implementation("androidx.activity:activity-ktx:1.1.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
Expand All @@ -71,4 +78,5 @@ dependencies {
// retrofit
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
androidTestImplementation("androidx.test.espresso:espresso-intents:3.3.0")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package campus.tech.kakao.map

import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import campus.tech.kakao.map.model.Location
import campus.tech.kakao.map.view.map.MapActivity
import campus.tech.kakao.map.view.search.LocationAdapter
import campus.tech.kakao.map.view.search.MainActivity
import org.hamcrest.Matchers.allOf
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.random.Random

@RunWith(AndroidJUnit4::class)
class MainActivityUiTest {
@get: Rule
var activityScenarioRule = ActivityScenarioRule(MainActivity::class.java)

@Test
fun `검색창에_검색어를_입력하고_clearButton을_클릭하면_입력한_검색어가_삭제된다`() {
onView(withId(R.id.SearchEditTextInMain)).perform(replaceText("카페")).check(matches(withText("카페")))
onView(withId(R.id.clearButton)).perform(click())
onView(withId(R.id.SearchEditTextInMain)).check(matches(withText("")))
}

@Test
fun `리싸이클러뷰가_화면에_표시된다`() {
onView(withId(R.id.locationRecyclerView)).check(matches(isDisplayed()))
}

@Test
fun `검색창에_부산대를_입력하고_리싸이클러뷰의_0번_아이템을_클릭하면_지도와_BottomSheet가_화면에_표시된다`() {
Intents.init()
onView(withId(R.id.SearchEditTextInMain)).perform(replaceText("부산대"))
Thread.sleep(3000)
onView(withId(R.id.locationRecyclerView))
.perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))

intended(hasExtra("title", "부산대학교 부산캠퍼스"))
intended(hasComponent(MapActivity::class.java.name))
onView(withId(R.id.bottom_sheet_layout)).check(matches(isDisplayed()))
onView(withId(R.id.map_view)).check(matches(isDisplayed()))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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 androidx.test.ext.junit.runners.AndroidJUnit4
import campus.tech.kakao.map.view.map.MapActivity
import com.kakao.vectormap.KakaoMap
import com.kakao.vectormap.KakaoMapSdk
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock

@RunWith(AndroidJUnit4::class)
class MapActivityUiTest {
@get: Rule
var activityRule = ActivityScenarioRule(MapActivity::class.java)

@Test
fun `지도에서_에러_발생_시_에러_메세지가_나타난다`() { // 테스트를 통과하지 않습니다.. 지도 에러를 발생시키는 부분에서 오류가 나는 것 같아요ㅠㅠ 어렵습니다...
activityRule.scenario.onActivity { activity ->
KakaoMapSdk.init(activity, "fakeKey");

Choose a reason for hiding this comment

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

MapActivity 에서 이미 init 을 했는데, 한번더 하기 때문에 에러를 발생시키는 것이 아닐까 추측해봐요! 에러 메시지를 첨부해주시면 함께 볼 수 있겠네요!

}
onView(withId(R.id.errorMessageTextView)).check(matches(isDisplayed()))
}
}
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/campus/tech/kakao/map/App.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package campus.tech.kakao.map

import android.app.Application
import campus.tech.kakao.map.model.datasource.SharedPreferences
import com.kakao.vectormap.KakaoMapSdk

class App : Application(){
companion object{
lateinit var sharedPreferencesManager : SharedPreferences
}
override fun onCreate() {
sharedPreferencesManager = SharedPreferences(applicationContext)
KakaoMapSdk.init(this, BuildConfig.KAKAO_API_KEY)
super.onCreate()

}
}
7 changes: 5 additions & 2 deletions app/src/main/java/campus/tech/kakao/map/model/Location.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package campus.tech.kakao.map.model
data class Location(
val title: String,
val address: String,
val category: String
val category: String,
val longitude: Double,
val latitude: Double
){
companion object {
fun LocationDto.toLocation(): Location {
return Location(title, address, category)
return Location(title, address, category, x.toDouble(), y.toDouble())

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@ data class LocationDto(
@SerializedName("category_group_name")
val category: String,
@SerializedName("address_name")
val address: String
val address: String,
@SerializedName("x")
val x: String,
@SerializedName("y")
val y: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package campus.tech.kakao.map.model.datasource

import campus.tech.kakao.map.App
import campus.tech.kakao.map.model.Location

class LastLocationlDataSource() {

fun putLastLocation(location: Location) {
if (location != null) {
App.sharedPreferencesManager.putString("longitude", location.longitude.toString())
App.sharedPreferencesManager.putString("latitude", location.latitude.toString())
App.sharedPreferencesManager.putString("title", location.title.toString())
App.sharedPreferencesManager.putString("address", location.address.toString())
App.sharedPreferencesManager.putString("category", location.category.toString())
}
}

fun getLastLocation(): Location? {
val title = App.sharedPreferencesManager.getString("title", "")
if(title == "") return null
val longitude = App.sharedPreferencesManager.getString("longitude", "").toString().toDouble()
val latitude = App.sharedPreferencesManager.getString("latitude", "").toString().toDouble()
val address = App.sharedPreferencesManager.getString("address", "").toString()
val category = App.sharedPreferencesManager.getString("category", "").toString()
return Location(title, address, category, longitude, latitude)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import campus.tech.kakao.map.model.repository.RetrofitInstance
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class LocationRemoteDataSource {
class LocationDataSource {
companion object{
private const val RESULT_SIZE = 15
}

private val client = RetrofitInstance.getInstance().create(KakaoAPI::class.java)

suspend fun getLocations(keyword: String): List<Location> {

return withContext(Dispatchers.IO){
val response = client.searchFromKeyword(keyword, RESULT_SIZE)
val locationDtos: List<LocationDto> = response.body()?.documents ?: emptyList()
Expand Down

This file was deleted.

Loading