Skip to content

Commit

Permalink
DEP-420 feat: sentry ๋„์ž… (#215)
Browse files Browse the repository at this point in the history
  • Loading branch information
kimhyeing authored Oct 16, 2023
1 parent 1cc08fc commit 8890152
Show file tree
Hide file tree
Showing 42 changed files with 412 additions and 188 deletions.
8 changes: 6 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,25 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
manifestPlaceholders = [
KAKAO_APP_KEY: properties.getProperty('release.kakao.app-key')
KAKAO_APP_KEY: properties.getProperty('release.kakao.app-key'),
SENTRY_DSN: properties.getProperty('release.sentry.dsn')
]
buildConfigField 'String', 'BASE_URL', "\"${properties.getProperty('release.three-days.base-url')}\""
buildConfigField 'String', 'KAKAO_APP_KEY', "\"${properties.getProperty('release.kakao.app-key')}\""
buildConfigField 'String', 'MIXPANEL_PROJECT_TOKEN', "\"${properties.getProperty('release.mixpanel.project-token')}\""
buildConfigField 'String', 'SENTRY_DSN', "\"${properties.getProperty('release.sentry.dsn')}\""
}
debug {
debuggable true
applicationIdSuffix '.debug'
manifestPlaceholders = [
KAKAO_APP_KEY: properties.getProperty('debug.kakao.app-key')
KAKAO_APP_KEY: properties.getProperty('debug.kakao.app-key'),
SENTRY_DSN: properties.getProperty('debug.sentry.dsn')
]
buildConfigField 'String', 'BASE_URL', "\"${properties.getProperty('debug.three-days.base-url')}\""
buildConfigField 'String', 'KAKAO_APP_KEY', "\"${properties.getProperty('debug.kakao.app-key')}\""
buildConfigField 'String', 'MIXPANEL_PROJECT_TOKEN', "\"${properties.getProperty('debug.mixpanel.project-token')}\""
buildConfigField 'String', 'SENTRY_DSN', "\"${properties.getProperty('debug.sentry.dsn')}\""
}
alpha {
initWith debug
Expand Down
48 changes: 40 additions & 8 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,35 +62,42 @@
android:name="com.depromeet.threedays.mypage.archived_habit.ArchivedHabitActivity"
android:exported="false" />

<activity android:name=".mate.create.step1.ConnectHabitActivity"
<activity
android:name=".mate.create.step1.ConnectHabitActivity"
android:exported="false"
android:taskAffinity="com.depromeet.threedays.mate"
android:theme="@style/WhiteStatusTheme" />

<activity android:name=".mate.create.step2.ChooseMateTypeActivity"
<activity
android:name=".mate.create.step2.ChooseMateTypeActivity"
android:exported="false"
android:taskAffinity="com.depromeet.threedays.mate"
android:theme="@style/WhiteStatusTheme" />

<activity android:name=".mate.create.step3.SetMateNicknameActivity"
<activity
android:name=".mate.create.step3.SetMateNicknameActivity"
android:exported="false"
android:taskAffinity="com.depromeet.threedays.mate"
android:theme="@style/WhiteStatusTheme" />

<activity android:name="com.depromeet.threedays.policy.PolicyActivity"
<activity
android:name="com.depromeet.threedays.policy.PolicyActivity"
android:exported="false" />

<activity android:name=".signup.SignupActivity"
android:exported="true"/>
<activity
android:name=".signup.SignupActivity"
android:exported="true" />

<activity android:name=".signup.complete.SignupCompleteActivity"
android:exported="false"/>
<activity
android:name=".signup.complete.SignupCompleteActivity"
android:exported="false" />

<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

Expand All @@ -109,6 +116,31 @@
</intent-filter>
</service>

<!-- Required: set your sentry.io project identifier (DSN) -->
<meta-data
android:name="io.sentry.dsn"
android:value="${SENTRY_DSN}" />
<!-- enable automatic breadcrumbs for user interactions (clicks, swipes, scrolls) -->
<meta-data
android:name="io.sentry.traces.user-interaction.enable"
android:value="true" />
<!-- enable screenshot for crashes -->
<meta-data
android:name="io.sentry.attach-screenshot"
android:value="true" />
<!-- enable view hierarchy for crashes -->
<meta-data
android:name="io.sentry.attach-view-hierarchy"
android:value="true" />
<!-- enable the performance API by setting a sample-rate, adjust in production env -->
<meta-data
android:name="io.sentry.traces.sample-rate"
android:value="1.0" />
<!-- enable profiling when starting transactions, adjust in production env -->
<meta-data
android:name="io.sentry.traces.profiling.sample-rate"
android:value="1.0" />

</application>

</manifest>
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ plugins {
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
id "org.sonarqube" version "3.4.0.2513"
id 'org.jetbrains.kotlin.jvm' version '1.6.10' apply false
id 'io.sentry.android.gradle' version "3.12.0"
}

task clean(type: Delete) {
Expand Down
3 changes: 3 additions & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ android {
dependencies {
implementation(project(":core-design-system"))
implementation(project(":build-property"))
implementation(project(":domain"))

implementation(jetpackDeps)
implementation(coroutines)
Expand All @@ -55,6 +56,8 @@ dependencies {

implementation deps.mixpanel

implementation deps.sentry

testImplementation(testDeps)
androidTestImplementation(androidTestDeps)
}
15 changes: 8 additions & 7 deletions core/src/main/java/com/depromeet/threedays/core/BaseViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.depromeet.threedays.core

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.depromeet.threedays.domain.exception.ThreeDaysException
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
Expand All @@ -14,8 +15,8 @@ abstract class BaseViewModel : ViewModel() {
val loading: StateFlow<Boolean>
get() = _loading

private val _error = MutableSharedFlow<String>()
val error: SharedFlow<String>
private val _error = MutableSharedFlow<ThreeDaysException>()
val error: SharedFlow<ThreeDaysException>
get() = _error

protected fun startLoading() {
Expand All @@ -26,13 +27,13 @@ abstract class BaseViewModel : ViewModel() {
_loading.value = false
}

protected fun sendErrorMessage(throwable: Throwable) {
sendErrorMessage(throwable.message)
}
protected fun sendError(throwable: ThreeDaysException) {
if (throwable.message.equals(ThreeDaysException.UNKNOWN_EXCEPTION)) {
throwable.message = throwable.defaultMessage
}

protected fun sendErrorMessage(message: String?) {
viewModelScope.launch {
_error.emit(message ?: "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”.")
_error.emit(throwable)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,17 @@ class ThreeDaysToast {

fun error(
context: Context,
title: String?,
title: String,
duration: Int = Toast.LENGTH_SHORT
) {
if(title != null) {
val inflater = LayoutInflater.from(context)
val toast = Toast(context)
val view = inflater.inflate(R.layout.error_toast_three_days, null)
val textView: TextView = view.findViewById(R.id.tv_toast)
textView.text = title
toast.setGravity(Gravity.BOTTOM or Gravity.FILL_HORIZONTAL, 0, 0)
toast.view = view
toast.duration = duration
toast.show()
}
val inflater = LayoutInflater.from(context)
val toast = Toast(context)
val view = inflater.inflate(R.layout.error_toast_three_days, null)
val textView: TextView = view.findViewById(R.id.tv_toast)
textView.text = title
toast.setGravity(Gravity.BOTTOM or Gravity.FILL_HORIZONTAL, 0, 0)
toast.view = view
toast.duration = duration
toast.show()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ResultCall<T>(private val call: Call<T>, private val retrofit: Retrofit) :
if(response.body() == null) {
callback.onResponse(
this@ResultCall,
Response.success(Result.failure(ThreeDaysException("body๊ฐ€ ๋น„์—ˆ์Šต๋‹ˆ๋‹ค.", HttpException(response))))
Response.success(Result.failure(ThreeDaysException(ThreeDaysException.NO_BODY_RESPONSE, HttpException(response))))
)
}
else {
Expand All @@ -32,7 +32,7 @@ class ResultCall<T>(private val call: Call<T>, private val retrofit: Retrofit) :

if(response.errorBody() == null) {
callback.onResponse( this@ResultCall,
Response.success(Result.failure(ThreeDaysException("errorBody๊ฐ€ ๋น„์—ˆ์Šต๋‹ˆ๋‹ค.", HttpException(response))))
Response.success(Result.failure(ThreeDaysException(ThreeDaysException.NO_ERROR_BODY_RESPONSE, HttpException(response))))
)
}
else {
Expand All @@ -41,7 +41,7 @@ class ResultCall<T>(private val call: Call<T>, private val retrofit: Retrofit) :
ErrorResponse::class.java.annotations
).convert(response.errorBody()!!)

val message: String = errorBody?.message ?: "errorBody๊ฐ€ ๋น„์—ˆ์Šต๋‹ˆ๋‹ค"
val message: String = errorBody?.message ?: ThreeDaysException.NO_ERROR_MESSAGE_RESPONSE

callback.onResponse(this@ResultCall,
Response.success(Result.failure(ThreeDaysException(message, HttpException(response))))
Expand All @@ -55,8 +55,8 @@ class ResultCall<T>(private val call: Call<T>, private val retrofit: Retrofit) :

override fun onFailure(call: Call<T>, t: Throwable) {
val message = when (t) {
is IOException -> "์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์ด ๋Š๊ฒผ์Šต๋‹ˆ๋‹ค."
is HttpException -> "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”."
is IOException -> ThreeDaysException.INTERNET_CONNECTION_WAS_LOST
is HttpException -> ThreeDaysException.UNKNOWN_EXCEPTION
else -> t.localizedMessage
}
callback.onResponse(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.depromeet.threedays.data.entity.base

import com.depromeet.threedays.domain.exception.ThreeDaysException

data class ApiResponse<T>(
val data: T?,
val message: String,
Expand All @@ -13,5 +15,5 @@ fun <T>Result<ApiResponse<T>>.getResult(): Result<T>? {
}.onFailure { throwable ->
return Result.failure(throwable)
}
return Result.failure(IllegalStateException("์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."))
return Result.failure(IllegalStateException(ThreeDaysException.UNKNOWN_EXCEPTION))
}
4 changes: 3 additions & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ ext {
"lottie" : "5.2.0",
"datastore" : "1.0.0",
"ossLicenses" : "17.0.0",
"mixpanel" : "7.+",
"mixpanel" : "7.+",
"sentry" : "6.28.0",
]
deps = [
"kotlin" : [
Expand Down Expand Up @@ -113,6 +114,7 @@ ext {
"splash" : "androidx.core:core-splashscreen:1.0.0",
"ossLicenses" : "com.google.android.gms:play-services-oss-licenses:${versions.ossLicenses}",
"mixpanel" : "com.mixpanel.android:mixpanel-android:${versions.mixpanel}",
"sentry" : "io.sentry:sentry-android:${versions.sentry}"
]

coroutines = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package com.depromeet.threedays.domain.exception

class ThreeDaysException(override val message: String, throwable: Throwable) : Exception(message, throwable) {
// override val message: String? = when(code) {
// HABIT_CONSTRAINTS_INSUFFICIENT_DAY_OF_WEEKS -> "์‹ค์ฒœ ์š”์ผ์„ 3๊ฐœ ์ด์ƒ ์„ ํƒํ•ด ์ฃผ์„ธ์š”."
// HABIT_ACHIEVEMENT_CONSTRAINTS_DATE_IS_IN_THE_PAST -> "์ด์ „์˜ ๊ธฐ๋ก์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์–ด์š”."
// HABIT_ACHIEVEMENT_CONSTRAINTS_INVALID_ACHIEVEMENT_DATE -> "์Šต๊ด€ ์ทจ์†Œ๋Š” ์˜ค๋Š˜๋งŒ ํ•  ์ˆ˜ ์žˆ์–ด์š”"
// HABIT_CONSTRAINTS_INSUFFICIENT_NOTIFICATION -> "Push ์•Œ๋ฆผ์„ ๋งˆ์ € ์„ค์ •ํ•ด ์ฃผ์„ธ์š”."
// MEMBER_CONSTRAINTS_INVALID_MEMBER -> "๊ฐ€์ž…๋œ ๊ธฐ๋ก์ด ์—†์–ด์š”."
// MEMBER_NOT_FOUND -> "๊ฐ€์ž…๋œ ๊ธฐ๋ก์ด ์—†์–ด์š”."
// SOCIAL_LOGIN_ERROR -> "๋กœ๊ทธ์ธ์— ์‹คํŒจํ–ˆ์–ด์š”."
// MATE_CONSTRAINTS_ALREADY_EXIST_MATE -> "์ด๋ฏธ ํ•จ๊ป˜ํ•˜๊ณ  ์žˆ๋Š” ์ง๊ฟ์ด ์žˆ์–ด์š”."
// METHOD_ARGUMENT_NOT_VALID_EXCEPTION_BIND_EXCEPTION, BAD_REQUEST, UNKNOWN_ERROR -> "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”."
// NO_INTERNET_CONNECTION -> "์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์ด ๋Š๊ฒผ์Šต๋‹ˆ๋‹ค."
// else -> null
// }
}
class ThreeDaysException(override var message: String?, throwable: Throwable) : Exception(message, throwable) {
var defaultMessage = UNKNOWN_EXCEPTION
companion object {
const val UNKNOWN_EXCEPTION = "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”."
const val INTERNET_CONNECTION_WAS_LOST = "์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์ด ๋Š๊ฒผ์Šต๋‹ˆ๋‹ค."
const val NO_BODY_RESPONSE = "์‘๋‹ต์ด ๋น„์—ˆ์Šต๋‹ˆ๋‹ค."
const val NO_ERROR_BODY_RESPONSE = "์—๋Ÿฌ ์‘๋‹ต์ด ๋น„์—ˆ์Šต๋‹ˆ๋‹ค."
const val NO_ERROR_MESSAGE_RESPONSE = "์—๋Ÿฌ ๋ฉ”์„ธ์ง€๊ฐ€ ๋น„์—ˆ์Šต๋‹ˆ๋‹ค."
const val LOGIN_FAIL = "๋กœ๊ทธ์ธ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
const val LOGOUT_FAIL = "๋กœ๊ทธ์•„์›ƒ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
const val MEMBERSHIP_WITHDRAWAL_FAIL = "ํšŒ์›ํƒˆํ‡ด๋ฅผ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
}
}
2 changes: 2 additions & 0 deletions presentation/create/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ dependencies {

implementation deps.timber

implementation deps.sentry

testImplementation(testDeps)
androidTestImplementation(androidTestDeps)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import com.depromeet.threedays.create.create.HabitCreateViewModel.Action
import com.depromeet.threedays.create.databinding.ActivityHabitCreateBinding
import com.depromeet.threedays.create.emoji.EmojiBottomSheetDialogFragment
import com.depromeet.threedays.domain.entity.Color
import com.depromeet.threedays.domain.exception.ThreeDaysException
import com.depromeet.threedays.domain.key.RESULT_CREATE
import dagger.hilt.android.AndroidEntryPoint
import io.sentry.Sentry
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import java.time.DayOfWeek
Expand Down Expand Up @@ -133,9 +135,9 @@ class HabitCreateActivity :
viewModel.onCreateHabitClick()
}

binding.groupColor.setOnCheckedChangeListener { _ , id ->
binding.groupColor.setOnCheckedChangeListener { _, id ->
viewModel.setColor(
when(id) {
when (id) {
R.id.rb_green -> Color.GREEN
R.id.rb_blue -> Color.BLUE
R.id.rb_pink -> Color.PINK
Expand All @@ -155,26 +157,32 @@ class HabitCreateActivity :
}

private fun observe() {
viewModel.isSaveHabitEnable
.onEach {
binding.tvHabitCreate.isEnabled = it
}.launchIn(lifecycleScope)
viewModel.isSaveHabitEnable
.onEach {
binding.tvHabitCreate.isEnabled = it
}.launchIn(lifecycleScope)

viewModel.action
.onEach { action ->
when(action) {
when (action) {
is Action.SaveClick -> {
setResult(RESULT_CREATE)
finish()
}

is Action.NotificationTimeClick -> {
showTimePicker(action.currentTime)
}
}
}.launchIn(lifecycleScope)

viewModel.error
.onEach { errorMessage -> ThreeDaysToast().error(this, errorMessage) }
.onEach { error ->
ThreeDaysToast().error(this, error.message ?: error.defaultMessage)
if (error.message != ThreeDaysException.INTERNET_CONNECTION_WAS_LOST) {
Sentry.captureException(error)
}
}
.launchIn(lifecycleScope)
}

Expand All @@ -189,7 +197,7 @@ class HabitCreateActivity :
}

fun setBackBtnClickEvent() {
if(viewModel.isInformationEntered.value) {
if (viewModel.isInformationEntered.value) {
ThreeDaysDialogFragment.newInstance(
data = DialogInfo.EMPTY.copy(
onPositiveAction = { finish() },
Expand All @@ -199,8 +207,7 @@ class HabitCreateActivity :
confirmText = getString(com.depromeet.threedays.core.R.string.reply_go_out)
)
).show(supportFragmentManager, ThreeDaysDialogFragment.TAG)
}
else {
} else {
finish()
}
}
Expand Down
Loading

0 comments on commit 8890152

Please sign in to comment.