-
Notifications
You must be signed in to change notification settings - Fork 29
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 박정훈 6주차 과제 Step2 #72
base: pjhn
Are you sure you want to change the base?
Changes from 18 commits
70445a3
ea14330
8b6bc6b
839879d
b365382
8042392
441bdc8
04121dc
536262e
4e6375d
8d2fcf1
4cf6189
7785f05
8a0b716
9cf8362
ba9bf7e
b391e9b
5b3cbee
9e1782f
0c362a9
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,15 +1,13 @@ | ||
# android-map-notification | ||
|
||
## 기능 요구 사항 | ||
- 초기 진입 화면을 추가한다 | ||
- Firebase의 Remote Config를 설정한다 | ||
- 매개변수 serviceState 값이 ON_SERVICE일 때만 초기 진입 화면이 지도 화면으로 넘어간다. | ||
- 매개변수 serviceState 값이 ON_SERVICE이 아닌 경우에는 | ||
serviceMessage 값을 초기 진입 화면 하단에 표시하고 지도 화면으로 진입하지 않는다. | ||
- Firebase Cloud Message를 설정한다. | ||
- 테스트 메시지를 보낸다. | ||
- 앱이 백그라운드 상태일 경우 FCM 기본 값을 사용하여 Notification을 발생한다. | ||
- 앱이 포그라운드 상태일 경우 커스텀 Notification을 발생한다. | ||
- Notification 창을 터치하면 초기 진입 화면이 호출된다. | ||
|
||
## 프로그래밍 요구 사항 | ||
- 서버 상태, UI 로딩 등에 대한 상태 관리를 한다. | ||
- 새로 추가되는 부분에도 MVVM 아키텍처 패턴을 적용한다. | ||
- 코드 컨벤션을 준수하며 프로그래밍한다. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,21 +14,20 @@ class PlaceRemoteDataRepository @Inject constructor( | |
private val placeDao: PlaceDao, | ||
private val kakaoApi: KakaoApi | ||
) : PlaceLocalDataRepository(placeDao){ | ||
override suspend fun getPlaces(placeName: String): List<Place> { | ||
override suspend fun getPlaces(placeName: String, page: Int): 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) { | ||
response.body()?.documents?.let { resultPlaces.addAll(it) } | ||
} else throw RuntimeException("통신 에러 발생") | ||
} | ||
updatePlaces(resultPlaces) | ||
|
||
val response = kakaoApi.getSearchKeyword( | ||
key = BuildConfig.KAKAO_REST_API_KEY, | ||
query = placeName, | ||
size = 15, | ||
page = page | ||
) | ||
Comment on lines
+21
to
+26
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. 한번에 45개 정도로 많은 데이터를 가져오고 싶은 상황이라면, size를 page * 3 정도로 하거나 loadSize를 인자로 넘겨받는 방법도 괜찮을것 같습니다. 현재는 page 1~3 을 모두 호출해야하니 api 통신이 3회 이뤄지게 되는것이니, api 통신횟수를 줄이면서 원하는 양의 데이터를 가져오게 할 수 있을것 같네요 |
||
if (response.isSuccessful) { | ||
response.body()?.documents?.let { resultPlaces.addAll(it) } | ||
} else throw RuntimeException("통신 에러 발생") | ||
|
||
resultPlaces | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package campus.tech.kakao.map.data.service | ||
|
||
import android.app.* | ||
import android.content.Context | ||
import android.content.Intent | ||
import android.graphics.Color | ||
import android.util.Log | ||
import androidx.core.app.NotificationCompat | ||
import com.google.firebase.messaging.FirebaseMessagingService | ||
import com.google.firebase.messaging.RemoteMessage | ||
import campus.tech.kakao.map.R | ||
import campus.tech.kakao.map.presentation.splash.SplashScreenActivity | ||
|
||
class MyFirebaseMessagingService : FirebaseMessagingService() { | ||
companion object { | ||
private const val NOTIFICATION_ID = 222222 | ||
private const val CHANNEL_ID = "main_default_channel" | ||
private const val CHANNEL_NAME = "main channelName" | ||
} | ||
|
||
private lateinit var notificationManager: NotificationManager | ||
|
||
override fun onNewToken(token: String) { | ||
super.onNewToken(token) | ||
Log.d("myToken", "Token: ${token}") | ||
} | ||
|
||
// Foreground 에서 호출 됨 | ||
override fun onMessageReceived(remoteMessage: RemoteMessage) { | ||
|
||
remoteMessage.notification?.let { | ||
sendNotification(it.title, it.body) | ||
} | ||
} | ||
|
||
private fun sendNotification(title: String?, message: String?) { | ||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager | ||
createNotificationChannel() | ||
|
||
val pendingIntent = setPendingIntent() | ||
val builder = NotificationCompat.Builder(this, CHANNEL_ID) | ||
.setSmallIcon(R.drawable.img_location) | ||
.setContentTitle(title) | ||
.setContentText(message) | ||
.setPriority(NotificationCompat.PRIORITY_DEFAULT) | ||
.setContentIntent(pendingIntent) | ||
.setStyle(NotificationCompat.BigTextStyle().bigText(message)) | ||
.setAutoCancel(true) //사용자가 알림을 클릭하면 해당 알림이 자동으로 사라짐 | ||
|
||
notificationManager.notify(NOTIFICATION_ID, builder.build()) | ||
} | ||
|
||
private fun setPendingIntent(): PendingIntent{ | ||
|
||
val intent = Intent(this, SplashScreenActivity::class.java).apply { | ||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK | ||
} | ||
|
||
return PendingIntent.getActivity( | ||
this, | ||
0, | ||
intent, | ||
PendingIntent.FLAG_IMMUTABLE // 생성 후 수정 x | ||
) | ||
} | ||
|
||
private fun createNotificationChannel() { | ||
val channel = NotificationChannel( | ||
CHANNEL_ID, | ||
CHANNEL_NAME, | ||
NotificationManager.IMPORTANCE_DEFAULT | ||
).apply { | ||
description = "기본 채널" | ||
} | ||
notificationManager.createNotificationChannel(channel) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package campus.tech.kakao.map.di | ||
|
||
import android.app.Activity | ||
import campus.tech.kakao.map.domain.model.Place | ||
import campus.tech.kakao.map.presentation.search.SearchActivity | ||
import campus.tech.kakao.map.presentation.search.SearchActivityRecyclerviewListener | ||
import dagger.Module | ||
import dagger.Provides | ||
import dagger.hilt.InstallIn | ||
import dagger.hilt.android.components.ActivityComponent | ||
import dagger.hilt.android.scopes.ActivityScoped | ||
|
||
@Module | ||
@InstallIn(ActivityComponent::class) | ||
object ListenerModule { | ||
|
||
@Provides | ||
@ActivityScoped | ||
fun provideSearchActivityRecyclerviewListener(activity: Activity) | ||
: SearchActivityRecyclerviewListener { | ||
val searchActivity = activity as SearchActivity | ||
|
||
return object : SearchActivityRecyclerviewListener { | ||
override fun onPlaceClick(place: Place) { | ||
searchActivity.handlePlaceClick(place) | ||
} | ||
|
||
override fun onLogDelBtnClick(logId: String) { | ||
searchActivity.handleLogDelBtnClick(logId) | ||
} | ||
} | ||
} | ||
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. 보통 Activity내부에서 사용하는 Listener까지 Hilt를 통해 주입받기 보다는 Activity내부에서 생성하는편입니다. 만약, 지금 SearchActivity 가 아닌 다른곳에서 주입받게 되면 해당 |
||
} |
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.
static function 으로 선언되어있고, hilt를 통해 context가 전달되는 방식이 아니기 때문에
@ApplicationContext
어노테이션은 제거해도 괜찮을것 같습니다