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

[Feature] GA4 클릭/스크롤 이벤트 추가 #245

Merged
merged 31 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5b2965d
Add Activity analytics event log function
ThirFir Apr 25, 2024
1398d58
Add main page store click event analytics
ThirFir Apr 25, 2024
d06b6cb
Add StoreActivity category click event analytics
ThirFir Apr 25, 2024
48eb8fd
Add Store click event analytics
ThirFir Apr 25, 2024
bd58a08
Add StoreDetail call click event analytics
ThirFir Apr 25, 2024
6f8efda
Add StoreDetail picture click event analytics
ThirFir Apr 25, 2024
506d28c
Create EventLogger, move activity logging logic to EventLogger
ThirFir Apr 25, 2024
bf0ff79
Add Hamburger click event analytics
ThirFir Apr 25, 2024
9b2b890
Add MainActivity dining click event analytics
ThirFir Apr 25, 2024
a545e9f
Add dining time click/scroll event analytics
ThirFir Apr 25, 2024
047afef
Add MainActivity bus card click event analytics
ThirFir Apr 25, 2024
174b67d
Add MainActivity bus card scroll event analytics
ThirFir Apr 26, 2024
661e3b4
Add BusMainFragment spinner click event analytics
ThirFir Apr 26, 2024
3f61213
Add BusTimetableFragment click event analytics
ThirFir Apr 26, 2024
05d14a7
Add Dining menu image click event analytics
ThirFir Apr 26, 2024
be3a4c8
Add Store search click event analytics, Modify get name logic
ThirFir Apr 26, 2024
5389913
Add Sign up click event analytics
ThirFir Apr 26, 2024
dbfb841
Add Login click event analytics
ThirFir Apr 26, 2024
501bf3c
Change store label
ThirFir Apr 29, 2024
86341b2
Change description store -> shop
ThirFir Apr 29, 2024
ce63904
Use Reflection isInitialized
ThirFir Apr 30, 2024
2ccb7a3
Change navi item analytics click event logic
ThirFir Apr 30, 2024
88a12f9
Consider configuration change, use viewmodel
ThirFir Apr 30, 2024
2fbe342
Set not to log if is debug
ThirFir Apr 30, 2024
1251c4b
Remove unnecessary import
ThirFir Apr 30, 2024
da22668
Remove use of startActivty -> use callDrawerItem
ThirFir Apr 30, 2024
e91519e
Set untranslatable type description
ThirFir May 1, 2024
a04c386
Make parameters constant
ThirFir May 1, 2024
824ae39
Remove unnecassary variable usage
ThirFir May 1, 2024
2cc5f5b
Use StoreCategory.All
ThirFir May 1, 2024
be16867
Apply lint
ThirFir May 1, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package `in`.koreatech.koin.core.analytics

import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase
import `in`.koreatech.koin.core.BuildConfig
import `in`.koreatech.koin.core.constant.AnalyticsConstant

object EventLogger {

/**
* @param action: 이벤트 발생 도메인(BUSINESS, CAMPUS, USER)
* @param label: 이벤트 소분류
* @param value: 이벤트 값
*/
fun logClickEvent(action: String, label: String, value: String) {
logEvent(action, AnalyticsConstant.Category.CLICK, label, value)
}

/**
* @param action: 이벤트 발생 도메인(BUSINESS, CAMPUS, USER)
* @param label: 이벤트 소분류
* @param value: 이벤트 값
*/
fun logScrollEvent(action: String, label: String, value: String) {
logEvent(action, AnalyticsConstant.Category.SCROLL, label, value)
}

/**
* @param action: 이벤트 발생 도메인(BUSINESS, CAMPUS, USER)
* @param category: 이벤트 종류(click, scroll, ...)
* @param label: 이벤트 소분류
* @param value: 이벤트 값
* @sample logEvent("BUSINESS", "click", "main_shop_categories", "전체보기")
*/
private fun logEvent(action: String, category: String, label: String, value: String) {
if(BuildConfig.IS_DEBUG) return
Firebase.analytics.logEvent(action) {
param("event_category", category)
param("event_label", label)
param("value", value)
https-namhoon-kim marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@


import in.koreatech.koin.core.R;
import in.koreatech.koin.core.analytics.EventLogger;
import in.koreatech.koin.core.constant.AnalyticsConstant;


public class AppBarBase extends AppBarLayout {
Expand Down Expand Up @@ -50,7 +52,14 @@ public void setOnClickListener(OnClickListener onClickListener) {
this.onClickListener = onClickListener;
background.setOnClickListener(onClickListener);
leftButton.setOnClickListener(onClickListener);
rightButton.setOnClickListener(onClickListener);
rightButton.setOnClickListener(v -> {
https-namhoon-kim marked this conversation as resolved.
Show resolved Hide resolved
onClickListener.onClick(v);
EventLogger.INSTANCE.logClickEvent(
AnalyticsConstant.Domain.USER,
AnalyticsConstant.Label.HAMBURGER,
getContext().getString(R.string.hamburger)
);
});
title.setOnClickListener(onClickListener);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package `in`.koreatech.koin.core.constant

object AnalyticsConstant {

Choose a reason for hiding this comment

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

object in object 구조를 차용하는 이유가 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이벤트 로깅에 사용되는 상수임을 명확히 하기 위해 관련 상수들을 AnalyticsConstant라는 object 내에 배치하였고, 그 안에서도 각 상수들이 어떤 parameter에 쓰이는지 명확히 하기 위해 object로 다시 한 번 분류하였습니다.


object Domain {
const val BUSINESS = "BUSINESS"
const val CAMPUS = "CAMPUS"
const val USER = "USER"
}

object Category {
const val CLICK = "click"
const val SCROLL = "scroll"
}

object Label {
const val MAIN_SHOP_CATEGORIES = "main_shop_categories"
const val SHOP_CATEGORIES = "shop_categories"
const val SHOP_CATEGORIES_SEARCH = "shop_categories_search"
const val HAMBURGER = "hamburger"
const val HAMBURGER_SHOP = "${HAMBURGER}_shop"
const val HAMBURGER_DINING = "${HAMBURGER}_dining"
const val HAMBURGER_MY_INFO_WITHOUT_LOGIN = "${HAMBURGER}_my_info_without_login"
const val HAMBURGER_MY_INFO_WITH_LOGIN = "${HAMBURGER}_my_info_with_login"
const val HAMBURGER_BUS = "${HAMBURGER}_bus"
const val USER_ONLY_OK = "user_only_ok"
const val MAIN_MENU_MOVEDETAILVIEW = "main_menu_moveDetailView"
const val MAIN_MENU_CORNER = "main_menu_corner"
const val MENU_TIME = "menu_time"
const val MAIN_BUS = "main_bus"
const val MAIN_BUS_CHANGETOFROM = "main_bus_changeToFrom"
const val MAIN_BUS_SCROLL = "main_bus_scroll"
const val BUS_DEPARTURE = "bus_departure"
const val BUS_ARRIVAL = "bus_arrival"
const val BUS_TIMETABLE = "bus_timetable"
const val BUS_TIMETABLE_AREA = "bus_timetable_area"
const val BUS_TIMETABLE_TIME = "bus_timetable_time"
const val BUS_TIMETABLE_EXPRESS = "bus_timetable_express"
const val MENU_IMAGE = "menu_image"
const val LOGIN = "login"
const val START_SIGN_UP = "start_sign_up"
const val COMPLETE_SIGN_UP = "complete_sign_up"
const val SHOP_PICTURE = "shop_picture"
const val SHOP_CALL = "shop_call"
const val SHOP_CLICK = "shop_click"
}

}
1 change: 1 addition & 0 deletions core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,5 @@
<string name="navigation_drawer_right_button_ask">문의하기</string>
<string name="navigation_drawer_right_hello_message">님, 안녕하세요!</string>
<string name="navigation_drawer_right_myinfo">내 정보</string>
<string name="hamburger">햄버거</string>
</resources>
1 change: 1 addition & 0 deletions data/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<string name="bus_name_commuting">통학버스</string>
<string name="bus_name_express">대성고속</string>
<string name="bus_name_shuttle">셔틀버스</string>
<string name="bus_name_school_shuttle">학교셔틀</string>
<string name="city_bus_number_format">%1$d번 버스</string>
<!-- dept -->
<string name="dept_mechanical_engineering">기계공학부</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,14 @@ object DiningUtil {
}
return null
}

fun getKoreanName(type: String): String {
return when (type) {
DiningType.Breakfast.typeEnglish -> DiningType.Breakfast.typeKorean
DiningType.Lunch.typeEnglish -> DiningType.Lunch.typeKorean
DiningType.Dinner.typeEnglish -> DiningType.Dinner.typeKorean
DiningType.NextBreakfast.typeEnglish -> DiningType.NextBreakfast.typeKorean
else -> ""
https-namhoon-kim marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import `in`.koreatech.koin.core.analytics.EventLogger
import `in`.koreatech.koin.core.constant.AnalyticsConstant
import `in`.koreatech.koin.core.util.dataBinding

@AndroidEntryPoint
Expand All @@ -44,11 +46,36 @@ class BusMainFragment : Fragment(R.layout.bus_main_fragment) {
private fun initView() = with(binding) {
busDepartureSpinner.setSelection(BusNode.Koreatech.spinnerSelection)
busArrivalSpinner.setSelection(BusNode.Terminal.spinnerSelection)
busDepartureSpinner.setOnTouchListener { _, _ ->
viewModel.isUserSelection = true
busDepartureSpinner.performClick()
}
busDepartureSpinner.setOnItemSelectedListener { _, _, position, _ ->
viewModel.setDeparture(position.busNodeSelection)
if(viewModel.isUserSelection) {
EventLogger.logClickEvent(
AnalyticsConstant.Domain.CAMPUS,
AnalyticsConstant.Label.BUS_DEPARTURE,
resources.getStringArray(R.array.bus_place)[position]
)
viewModel.isUserSelection = false
}
}

busArrivalSpinner.setOnTouchListener { _, _ ->
viewModel.isUserSelection = true
busArrivalSpinner.performClick()
}
busArrivalSpinner.setOnItemSelectedListener { _, _, position, _ ->
viewModel.setArrival(position.busNodeSelection)
if(viewModel.isUserSelection) {
EventLogger.logClickEvent(
AnalyticsConstant.Domain.CAMPUS,
AnalyticsConstant.Label.BUS_ARRIVAL,
resources.getStringArray(R.array.bus_place)[position]
)
viewModel.isUserSelection = false
}
}
recyclerView.apply {
layoutManager = LinearLayoutManager(requireContext())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import android.view.View
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import `in`.koreatech.koin.core.analytics.EventLogger
import `in`.koreatech.koin.core.constant.AnalyticsConstant

@AndroidEntryPoint
class BusTimetableFragment : DataBindingFragment<BusTimetableFragmentBinding>() {
Expand Down Expand Up @@ -39,12 +41,27 @@ class BusTimetableFragment : DataBindingFragment<BusTimetableFragmentBinding>()

binding.busTimetableBustypeShuttle.setOnClickListener {
busTimetableViewModel.setBusType(BusType.Shuttle)
EventLogger.logClickEvent(
AnalyticsConstant.Domain.CAMPUS,
AnalyticsConstant.Label.BUS_TIMETABLE,
getString(R.string.bus_name_school_shuttle)
)
}
binding.busTimetableBustypeDaesung.setOnClickListener {
busTimetableViewModel.setBusType(BusType.Express)
EventLogger.logClickEvent(
AnalyticsConstant.Domain.CAMPUS,
AnalyticsConstant.Label.BUS_TIMETABLE,
getString(R.string.bus_name_express)
)
}
binding.busTimetableBustypeCity.setOnClickListener {
busTimetableViewModel.setBusType(BusType.City)
EventLogger.logClickEvent(
AnalyticsConstant.Domain.CAMPUS,
AnalyticsConstant.Label.BUS_TIMETABLE,
getString(R.string.bus_name_city)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
package `in`.koreatech.koin.ui.bus.fragment

import android.os.Bundle
import android.view.View
import android.widget.ArrayAdapter
import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import `in`.koreatech.koin.R
import `in`.koreatech.koin.core.analytics.EventLogger
import `in`.koreatech.koin.core.constant.AnalyticsConstant
import `in`.koreatech.koin.core.fragment.DataBindingFragment
import `in`.koreatech.koin.core.progressdialog.IProgressDialog
import `in`.koreatech.koin.databinding.LayoutExpressBusTimetableBinding
import `in`.koreatech.koin.databinding.LayoutShuttleBusTimetableBinding
import `in`.koreatech.koin.ui.bus.adpater.timetable.ExpressBusTimetableAdapter
import `in`.koreatech.koin.ui.bus.adpater.timetable.ShuttleBusTimetableAdapter
import `in`.koreatech.koin.ui.bus.state.toExpressBusTimetableUiItem
import `in`.koreatech.koin.ui.bus.state.toShuttleBusTimetableUiItem
import `in`.koreatech.koin.ui.bus.viewmodel.ExpressBusTimetableViewModel
import `in`.koreatech.koin.ui.bus.viewmodel.ShuttleBusTimetableViewModel
import `in`.koreatech.koin.util.SnackbarUtil
import `in`.koreatech.koin.util.ext.observeLiveData
import `in`.koreatech.koin.util.ext.setOnItemSelectedListener
import `in`.koreatech.koin.util.ext.withLoading
import `in`.koreatech.koin.util.ext.withToastError
import android.os.Bundle
import android.view.View
import android.widget.ArrayAdapter
import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class ExpressBusTimetableFragment : DataBindingFragment<LayoutExpressBusTimetableBinding>() {
override val layoutId: Int = R.layout.layout_express_bus_timetable

private val expressBusTimetableViewModel by viewModels<ExpressBusTimetableViewModel>()
private val expressBusTimetableAdapter = ExpressBusTimetableAdapter()
private var isInitialization = true

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Expand All @@ -47,6 +45,15 @@ class ExpressBusTimetableFragment : DataBindingFragment<LayoutExpressBusTimetabl

busTimetableCoursesSpinner.setOnItemSelectedListener { _, _, position, _ ->
expressBusTimetableViewModel.setCoursePosition(position)
if (isInitialization) {
isInitialization = false
return@setOnItemSelectedListener
}
EventLogger.logClickEvent(
AnalyticsConstant.Domain.CAMPUS,
AnalyticsConstant.Label.BUS_TIMETABLE_EXPRESS,
busTimetableCoursesSpinner.selectedItem.toString()
)
}
}

Expand All @@ -56,7 +63,7 @@ class ExpressBusTimetableFragment : DataBindingFragment<LayoutExpressBusTimetabl
withToastError(this@ExpressBusTimetableFragment, binding.root)

observeLiveData(busCoursesString) { courses ->
if(courses.isNullOrEmpty()) {
if (courses.isNullOrEmpty()) {
binding.busTimetableCoursesSpinner.isVisible = false
} else {
binding.busTimetableCoursesSpinner.isVisible = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import `in`.koreatech.koin.R
import `in`.koreatech.koin.core.analytics.EventLogger
import `in`.koreatech.koin.core.constant.AnalyticsConstant
import `in`.koreatech.koin.core.fragment.DataBindingFragment
import `in`.koreatech.koin.core.progressdialog.IProgressDialog
import `in`.koreatech.koin.databinding.LayoutShuttleBusTimetableBinding
Expand All @@ -25,6 +27,8 @@ class ShuttleBusTimetableFragment : DataBindingFragment<LayoutShuttleBusTimetabl

private val shuttleBusTimetableViewModel by viewModels<ShuttleBusTimetableViewModel>()
private val shuttleBusTimetableAdapter = ShuttleBusTimetableAdapter()
private var isCourseInitialization = true
private var isRouteInitialization = true

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Expand All @@ -42,10 +46,28 @@ class ShuttleBusTimetableFragment : DataBindingFragment<LayoutShuttleBusTimetabl

busTimetableCoursesSpinner.setOnItemSelectedListener { _, _, position, _ ->
shuttleBusTimetableViewModel.setCoursePosition(position)
if (isCourseInitialization) {
isCourseInitialization = false
return@setOnItemSelectedListener
}
EventLogger.logClickEvent(
AnalyticsConstant.Domain.CAMPUS,
AnalyticsConstant.Label.BUS_TIMETABLE_AREA,
busTimetableCoursesSpinner.selectedItem.toString()
)
}

busTimetableRoutesSpinner.setOnItemSelectedListener { _, _, position, _ ->
shuttleBusTimetableViewModel.setRoutePosition(position)
if (isRouteInitialization) {
isRouteInitialization = false
return@setOnItemSelectedListener
}
EventLogger.logClickEvent(
AnalyticsConstant.Domain.CAMPUS,
AnalyticsConstant.Label.BUS_TIMETABLE_TIME,
busTimetableRoutesSpinner.selectedItem.toString()
)
}
}

Expand All @@ -55,7 +77,7 @@ class ShuttleBusTimetableFragment : DataBindingFragment<LayoutShuttleBusTimetabl
withToastError(this@ShuttleBusTimetableFragment, binding.root)

observeLiveData(busCoursesString) { courses ->
if(courses.isNullOrEmpty()) {
if (courses.isNullOrEmpty()) {
binding.busTimetableCoursesSpinner.isVisible = false
} else {
binding.busTimetableCoursesSpinner.isVisible = true
Expand All @@ -81,7 +103,7 @@ class ShuttleBusTimetableFragment : DataBindingFragment<LayoutShuttleBusTimetabl
}

observeLiveData(busRoutes) {
if(it.isNullOrEmpty()) {
if (it.isNullOrEmpty()) {
binding.busTimetableRoutesSpinner.isVisible = false
} else {
binding.busTimetableRoutesSpinner.isVisible = true
Expand All @@ -97,7 +119,8 @@ class ShuttleBusTimetableFragment : DataBindingFragment<LayoutShuttleBusTimetabl
observeLiveData(selectedRoutesPosition) {
shuttleBusTimetableAdapter.submitList(
busTimetables.value?.get(
selectedRoutesPosition.value ?: 0)?.map { it.toShuttleBusTimetableUiItem() }
selectedRoutesPosition.value ?: 0
)?.map { it.toShuttleBusTimetableUiItem() }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class BusMainFragmentViewModel @Inject constructor(

private val _departure = MutableLiveData<BusNode>(BusNode.Koreatech)
private val _arrival = MutableLiveData<BusNode>(BusNode.Terminal)
var isUserSelection = false

val departure: LiveData<BusNode> get() = _departure
val arrival: LiveData<BusNode> get() = _arrival
Expand Down
Loading