Braze 노티 수신 시, android 에서만 인앱메세지의 ui가 OS 상태바와 중첩되는 이슈가 있어 이슈 추적 및 해소한 과정을 기록해보았다.
Braze.Events.PUSH_NOTIFICATION_EVENT
🔗 (참고문서) Listen for push notifications (optional)
위 문서를 참고한 결과, 문서에 안내된 대로 App.tsx 파일에 아래와같이
브레이즈 노티 알림을 감지하는 코드가 추가되어 있음
const brazePushNotificationSubscription = Braze.addListener(
Braze.Events.PUSH_NOTIFICATION_EVENT,
(data: { push_event_type: any; title: any; deeplink: any }) => {
... // 생략
});
그러나 이곳에서는 인앱메세지 구현체를 찾을 수 없었기 때문에 인앱메세지를 구현하고 스타일링하는 부분을 찾아야 했음.
인앱메세지의 display와 행동을 커스터마이징하는 이벤트 핸들러로 추정되는 리스너 확인
🔗 (참고문서) Custom Listeners
🔗 (참고문서) IInAppMessageManagerListener
🔗 (참고문서) Custom manage in-app message display and behavior
위 문서에 의하면, IInAppMessageManagerListener 를 구현하는 클래스를 생성하면, IInAppMessageManagerListener 내부의 콜백들이 인앱메세지의 라이프사이클 여러 지점에 거쳐 호출될 수 있음을 파악
위 코드가 구현되어있는지 검색해보니 InAppMessageManagerListener.kt 파일 내부에 실제로 구현되어 있음
...
import android.util.Log
import com.braze.models.inappmessage.IInAppMessage
import com.braze.ui.inappmessage.InAppMessageOperation
import com.braze.ui.inappmessage.listeners.IInAppMessageManagerListener
...
class InAppMessageManagerListener(private val reactNativeHost: ReactNativeHost): IInAppMessageManagerListener {
override fun beforeInAppMessageDisplayed(inAppMessage: IInAppMessage): InAppMessageOperation {
val parameters = WritableNativeMap();
parameters.putString("inAppMessageJsonString", inAppMessage.forJsonPut().toString())
reactNativeHost
.reactInstanceManager
.currentReactContext
?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
?.emit("inAppMessageReceived", parameters)
return when (inAppMessage.extras["display"]) {
"discard" -> InAppMessageOperation.DISCARD
else -> InAppMessageOperation.DISPLAY_NOW
}
}
}
beforeInAppMessageDisplayed
메서드가 브레이즈로부터 인앱메세지를 수신했을 때 실행되는 메서드임을 파악beforeInAppMessageDisplayed
메서드 내부에 추가하였으나 안드로이드 빌드 실패// 추가한 코드
inAppMessage.setExtras("position", "top")
inAppMessage.setExtras("offset", 100)
...
import android.util.Log
import com.braze.models.inappmessage.IInAppMessage
import com.braze.ui.inappmessage.InAppMessageOperation
import com.braze.ui.inappmessage.listeners.IInAppMessageManagerListener
...
class InAppMessageManagerListener(private val reactNativeHost: ReactNativeHost): IInAppMessageManagerListener {
override fun beforeInAppMessageDisplayed(inAppMessage: IInAppMessage): InAppMessageOperation {
// 코드 추가된 위치
inAppMessage.setExtras("position", "top")
inAppMessage.setExtras("offset", 100)
val parameters = WritableNativeMap();
parameters.putString("inAppMessageJsonString", inAppMessage.forJsonPut().toString())
reactNativeHost
.reactInstanceManager
.currentReactContext
?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
?.emit("inAppMessageReceived", parameters)
return when (inAppMessage.extras["display"]) {
"discard" -> InAppMessageOperation.DISCARD
else -> InAppMessageOperation.DISPLAY_NOW
}
}
}
styles.xml
파일에서 Braze SDK의 디폴트 스타일을 확인할 수 있다하여 해당 파일 확인해보니 브레이즈 스타일 관련된 코드는 없었으나 styles.xml
파일 자체는 아래와 같이 존재했음
<resources>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="colorPrimary">#...</item>
<item name="colorPrimaryDark">#...</item>
<item name="colorAccent">#...</item>
</style>
<style name="AppTheme.Splash" parent="AppTheme">
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowBackground">@color/splash</item>
<item name="android:statusBarColor">@android:color/transparent</item>
...
</style>
</resources>
그리고 문서에서 보여주는 디폴트 스타일의 예시
<style name="Braze"/>
<style name="Braze.InAppMessage"/>
<style name="Braze.InAppMessage.Header">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">match_parent</item>
<item name="android:padding">0.0dp</item>
<item name="android:background">@android:color/transparent</item>
<item name="android:textColor">@color/com_braze_inappmessage_header_text</item>
<item name="android:textSize">20.0sp</item>
<item name="android:lineSpacingMultiplier">1.3</item>
<item name="android:gravity">center</item>
<item name="android:textStyle">bold</item>
<item name="android:layout_centerHorizontal">true</item>
</style>
위 코드를 참고하여 브레이즈 인앱메세지에 margin을 아래처럼 추가하여 최종적으로 이슈를 해소하였다.
<style name="Braze.InAppMessage">
<item name="android:layout_marginTop">30.0dp</item>
</style>
안드로이드 스타일은 브라우저 CSS와 또 달라서 margin의 경우 marginTop이 아닌 layout_marginTop 으로 지정해야 한다. 처음에 <item name="android:marginTop">
으로 지정했다가 빌드 에러가 발생해서 또 실패했나보다 하고 좌절했는데, 다시 검색해보니 표현방법이 달랐던 것! 간단히 해결할 수 있는 문제였다.
🔗 참고한 블로그 [Android UI - padding, layout_margin 사용하기]
사실 작업하기 전부터 Native 코드를 만져야 할 것만 같아서 불안하긴 했다. 하지만 Braze 자체에서 설정하는 세팅도 아니고, 노티피케이션 쪽 코드는 이런저런 작업을 위해 여러번 보고 손도 댔던 부분이기에 면밀히 살펴보면 찾을 수 있을거란 생각은 들었다.
그리고 결국엔 해결하면서 느낀점 두가지가 있다.