-
Notifications
You must be signed in to change notification settings - Fork 32
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 박정훈 5주차 과제 Step2 #85
base: pjhn
Are you sure you want to change the base?
Changes from all commits
63c009a
d5d1653
eeafb37
846345d
d3bce44
c339be1
2e23dd5
af63296
98b6866
60023fe
e2eb681
e7e1299
7fb759c
68187f2
6003179
05b182c
8edf866
657e362
692a002
2e55d82
7dda882
e4c2b83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,18 @@ | ||
# android-map-refactoring | ||
# android-map-location | ||
|
||
카카오 맵 클론 코딩 | ||
카카오로컬 API 사용 | ||
|
||
## 기능 요구 사항 | ||
- 저장된 검색어를 선택하면 해당 검색어의 검색 결과가 표시된다. | ||
- 검색 결과 목록 중 하나의 항목을 선택하면 해당 항목의 위치를 지도에 표시한다. | ||
- 앱 종료 시 마지막 위치를 저장하여 다시 앱 실행 시 해당 위치로 포커스 한다. | ||
- 카카오지도 onMapError() 호출 시 에러 화면을 보여준다. | ||
- | ||
## 프로그래밍 요구 사항 | ||
- BottomSheet를 사용한다. | ||
- 카카오 API 사용을 위한 앱 키를 외부에 노출하지 않는다. | ||
- 가능한 MVVM 아키텍처 패턴을 적용하도록 한다. | ||
- 코드 컨벤션을 준수하며 프로그래밍한다. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
diff a/app/build.gradle.kts b/app/build.gradle.kts (rejected hunks) | ||
@@ -69,10 +69,17 @@ | ||
implementation("androidx.constraintlayout:constraintlayout:2.1.4") | ||
implementation("androidx.recyclerview:recyclerview:1.3.2") | ||
implementation("androidx.datastore:datastore-preferences:1.0.0") | ||
- implementation("androidx.activity:activity:1.8.0") | ||
+ implementation("androidx.activity:activity-ktx:1.8.0") | ||
testImplementation("junit:junit:4.13.2") | ||
androidTestImplementation("androidx.test.ext:junit:1.1.5") | ||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") | ||
+ androidTestImplementation("androidx.test:rules:1.4.0") | ||
+ androidTestImplementation("androidx.test:runner:1.4.0") | ||
+ androidTestImplementation("androidx.test.espresso:espresso-contrib:3.5.1") | ||
+ androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1") | ||
+ androidTestImplementation("io.mockk:mockk-android:1.13.3") | ||
+ androidTestImplementation("androidx.arch.core:core-testing:2.1.0") | ||
} | ||
|
||
+ | ||
fun getApiKey(key: String): String = gradleLocalProperties(rootDir, providers).getProperty(key) | ||
\ No newline at end of file |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package campus.tech.kakao.map | ||
|
||
import android.app.Application | ||
import android.net.ConnectivityManager | ||
import android.net.Network | ||
import android.net.NetworkCapabilities | ||
import android.view.View | ||
import com.kakao.vectormap.KakaoMapSdk | ||
import dagger.hilt.android.HiltAndroidApp | ||
|
||
|
||
@HiltAndroidApp | ||
class PlaceApplication: Application() { | ||
|
||
override fun onCreate() { | ||
super.onCreate() | ||
appInstance = this | ||
|
||
initKakaoMapSdk() | ||
} | ||
|
||
private fun initKakaoMapSdk(){ | ||
val key = getString(R.string.kakao_api_key) | ||
KakaoMapSdk.init(this, key) | ||
} | ||
companion object { | ||
@Volatile | ||
private lateinit var appInstance: PlaceApplication | ||
fun isNetworkActive(): Boolean { | ||
val connectivityManager: ConnectivityManager = | ||
appInstance.getSystemService(ConnectivityManager::class.java) | ||
val network: Network = connectivityManager.activeNetwork ?: return false | ||
val actNetwork: NetworkCapabilities = | ||
connectivityManager.getNetworkCapabilities(network) ?: return false | ||
|
||
return actNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || | ||
actNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package campus.tech.kakao.map.data | ||
|
||
import android.content.Context | ||
import campus.tech.kakao.map.domain.model.Place | ||
import javax.inject.Inject | ||
|
||
class LastVisitedPlaceManager @Inject constructor(context: Context) { | ||
|
||
private val sharedPreferences = context.getSharedPreferences("LastVisitedPlace", Context.MODE_PRIVATE) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 생성자로 context를 주입받아서 클래스 내부에서 선언해도 되겠지만, shared preference 자체를 hilt module에 선언하고, 생성자로 전달받도록 할 수 있을것 같습니다 |
||
|
||
fun saveLastVisitedPlace(place: Place) { | ||
val editor = sharedPreferences.edit() | ||
editor.putString("placeName", place.place) | ||
editor.putString("roadAddressName", place.address) | ||
editor.putString("categoryName", place.category) | ||
editor.putString("yPos", place.yPos) | ||
editor.putString("xPos", place.xPos) | ||
editor.apply() | ||
} | ||
|
||
fun getLastVisitedPlace(): Place? { | ||
val placeName = sharedPreferences.getString("placeName", null) | ||
val roadAddressName = sharedPreferences.getString("roadAddressName", null) | ||
val categoryName = sharedPreferences.getString("categoryName", null) | ||
val yPos = sharedPreferences.getString("yPos", null) | ||
val xPos = sharedPreferences.getString("xPos", null) | ||
|
||
return if (placeName != null && roadAddressName != null && categoryName != null && yPos != null && xPos != null) { | ||
Place("", placeName, roadAddressName, categoryName, xPos, yPos) | ||
} else { | ||
null | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package campus.tech.kakao.map.data | ||
|
||
import campus.tech.kakao.map.data.dao.PlaceDao | ||
import campus.tech.kakao.map.data.entity.PlaceEntity | ||
import campus.tech.kakao.map.data.entity.PlaceLogEntity | ||
import campus.tech.kakao.map.domain.model.Place | ||
import campus.tech.kakao.map.domain.repository.PlaceRepository | ||
import javax.inject.Inject | ||
|
||
open class PlaceLocalDataRepository @Inject constructor( | ||
private val placeDao: PlaceDao, | ||
) : PlaceRepository { | ||
|
||
override suspend fun getPlaces(placeName: String): List<Place> { | ||
return placeDao.getPlaces(placeName).map { it.toPlace() } | ||
} | ||
|
||
override suspend fun updatePlaces(places: List<Place>) { | ||
placeDao.deleteAllPlaces() | ||
placeDao.insertPlaces(places.map { | ||
PlaceEntity(it.id, it.place, it.address, it.category, it.xPos, it.yPos) | ||
}) | ||
} | ||
|
||
override suspend fun getPlaceById(id: String): Place? { | ||
return placeDao.getPlaceById(id)?.toPlace() | ||
} | ||
|
||
override suspend fun updateLogs(logs: List<Place>) { | ||
placeDao.deleteAllLogs() | ||
placeDao.insertLogs(logs.map { PlaceLogEntity(it.id, it.place) }) | ||
} | ||
|
||
override suspend fun removeLog(id: String) { | ||
placeDao.removeLog(id) | ||
|
||
} | ||
|
||
override suspend fun getLogs(): List<Place> { | ||
return placeDao.getLogs().map { it.toPlace() } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package campus.tech.kakao.map.data | ||
|
||
import android.content.Context | ||
import campus.tech.kakao.map.BuildConfig | ||
import campus.tech.kakao.map.data.dao.PlaceDao | ||
import campus.tech.kakao.map.data.net.KakaoApi | ||
import campus.tech.kakao.map.domain.model.Place | ||
import dagger.hilt.android.AndroidEntryPoint | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.withContext | ||
import javax.inject.Inject | ||
|
||
class PlaceRemoteDataRepository @Inject constructor( | ||
private val placeDao: PlaceDao, | ||
private val kakaoApi: KakaoApi | ||
) : PlaceLocalDataRepository(placeDao){ | ||
override suspend fun getPlaces(placeName: String): List<Place> { | ||
return withContext(Dispatchers.IO) { | ||
val resultPlaces = mutableListOf<Place>() | ||
for (page in 1..3) { | ||
val response = kakaoApi.getSearchKeyword( | ||
key = BuildConfig.KAKAO_REST_API_KEY, | ||
query = placeName, | ||
size = 15, | ||
page = page | ||
) | ||
if (response.isSuccessful) { | ||
Comment on lines
+20
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아마도 페이지네이션 이나 한번에 3개의 페이지를 불러오고 싶은 상황인것 같습니다. 이럴때는 내부에서 loop를 도는것 보다는 getPlaces() 메소드 자체가 3번 호출되는 방향이 좀더 적절하지 않을까 싶습니다 |
||
response.body()?.documents?.let { resultPlaces.addAll(it) } | ||
} else throw RuntimeException("통신 에러 발생") | ||
} | ||
updatePlaces(resultPlaces) | ||
resultPlaces | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
appInstance
를 활용하고자 하신곳이 있을까요? 이 과제에서는 없는듯 하네요.만약에 application context 를 사용하고 싶으신것이라면, hilt module에
@ApplicationContext
어노테이션을 사용해서 가져올 수 있는 방법이 있습니다. (참고 블로그)