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] 전화하기 버튼 위치 A/B테스트 구현 #504

Merged
merged 10 commits into from
Dec 15, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ enum class Experiment(
BENEFIT_STORE("Benefit", ExperimentGroup.A, ExperimentGroup.B),
DINING_SHARE("campus_share_v1", ExperimentGroup.SHARE_ORIGINAL, ExperimentGroup.SHARE_NEW),
MAIN_DINING_SEE_MORE("c_main_dining_v1", ExperimentGroup.MAIN_DINING_ORIGINAL, ExperimentGroup.MAIN_DINING_NEW),
MAIN_ARTICLE_KEYWORD_BANNER("c_keyword_ banner_v1", ExperimentGroup.MAIN_BANNER_ORIGINAL, ExperimentGroup.MAIN_BANNER_NEW);
MAIN_ARTICLE_KEYWORD_BANNER("c_keyword_ banner_v1", ExperimentGroup.MAIN_BANNER_ORIGINAL, ExperimentGroup.MAIN_BANNER_NEW),
BUSINESS_CALL("business_call", ExperimentGroup.CALL_NUMBER, ExperimentGroup.CALL_FLOATING);

init {
require(experimentGroups.isNotEmpty()) { "Experiment should have at least one group" }
Expand All @@ -27,4 +28,7 @@ object ExperimentGroup {

const val MAIN_BANNER_ORIGINAL = "banner_original"
const val MAIN_BANNER_NEW = "banner_new"

const val CALL_NUMBER = "call_number"
const val CALL_FLOATING = "call_floating"
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ object AnalyticsConstant {
const val CAMPUS_NOTICE_1 = "CAMPUS_notice_1"
const val POPULAR_NOTICE_BANNER = "popular_notice_banner"
const val TO_MANAGE_KEYWORD = "to_manage_keyword"

const val BUSINESS_CALL_NUMBER = " BUSINESS_call_number"
const val BUSINESS_CALL_FLOATING = "BUSINESS_call_floating"
}

const val PREVIOUS_PAGE = "previous_page"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ import android.content.ClipboardManager
import android.content.Intent
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import `in`.koreatech.koin.R
import `in`.koreatech.koin.core.abtest.ExperimentGroup
import `in`.koreatech.koin.core.analytics.EventAction
import `in`.koreatech.koin.core.analytics.EventExtra
import `in`.koreatech.koin.core.analytics.EventLogger
Expand Down Expand Up @@ -137,7 +141,6 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
)
}
if (currentTab == 2) {// 리뷰탭에서 전화누르기까지 시간

EventLogger.logClickEvent(
EventAction.BUSINESS,
AnalyticsConstant.Label.SHOP_DETAIL_VIEW_REVIEW_BACK,
Expand Down Expand Up @@ -266,6 +269,7 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
viewModel.getShopEvents(storeId)
viewModel.getShopReviews(storeId)

initCallFunction()
}

override fun onTouchEvent(event: MotionEvent?): Boolean {
Expand All @@ -284,6 +288,32 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
super.onBackPressed()
}

private fun initCallFunction(){
binding.storeDetailPhoneImage.setOnClickListener {
callingLogic()
}

binding.storeDetailPhoneTextview.setOnClickListener{
callingLogic()
}

binding.callFloatingButton.setOnClickListener {
callingLogic()
}
}

private fun callingLogic(){
dialogElapsedTime = System.currentTimeMillis() - dialogCurrentTime

showCallDialog()
EventLogger.logClickEvent(
EventAction.BUSINESS,
AnalyticsConstant.Label.SHOP_CALL,
viewModel.store.value?.name ?: "Unknown",
EventExtra(AnalyticsConstant.DURATION_TIME, (dialogElapsedTime / 1000.0 ).toString())
)
}


private fun initViewModel() {
withLoading(this@StoreDetailActivity, viewModel)
Expand Down Expand Up @@ -376,6 +406,23 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
}
}

lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.callABTestExperimentGroup.collect {
when (it) {
ExperimentGroup.CALL_NUMBER-> {
binding.callFloatingButton.visibility = View.GONE
binding.storeDetailPhoneTextview.setTextColor(ContextCompat.getColor(this@StoreDetailActivity, R.color.colorPrimary))
}
ExperimentGroup.CALL_FLOATING -> {
binding.scrollUpButton.visibility = View.GONE
binding.storeDetailPhoneImage.visibility = View.GONE
}
}
}
}
}

}

override fun onRestart() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import `in`.koreatech.koin.core.abtest.Experiment
import `in`.koreatech.koin.core.abtest.ExperimentGroup
import `in`.koreatech.koin.core.analytics.EventAction
import `in`.koreatech.koin.core.analytics.EventLogger
import `in`.koreatech.koin.core.constant.AnalyticsConstant
import `in`.koreatech.koin.core.toast.ToastUtil
import `in`.koreatech.koin.core.viewmodel.BaseViewModel
import `in`.koreatech.koin.core.viewmodel.SingleLiveEvent
Expand All @@ -25,11 +30,16 @@ import `in`.koreatech.koin.domain.usecase.store.GetStoreReviewUseCase
import `in`.koreatech.koin.domain.usecase.store.GetStoreWithMenuUseCase
import `in`.koreatech.koin.domain.usecase.store.ReviewPromptUscCase
import `in`.koreatech.koin.domain.usecase.token.IsTokenSavedInDeviceUseCase
import `in`.koreatech.koin.domain.usecase.user.ABTestUseCase
import `in`.koreatech.koin.domain.usecase.user.GetUserInfoUseCase
import `in`.koreatech.koin.domain.util.onFailure
import `in`.koreatech.koin.domain.util.onSuccess
import `in`.koreatech.koin.ui.splash.state.TokenState
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
Expand All @@ -43,6 +53,7 @@ class StoreDetailViewModel @Inject constructor(
private val getUserInfoUseCase: GetUserInfoUseCase,
private val reviewPromptUscCase: ReviewPromptUscCase,
private val isTokenSavedInDeviceUseCase: IsTokenSavedInDeviceUseCase,
private val abTestUseCase: ABTestUseCase
) : BaseViewModel() {
val store: LiveData<StoreWithMenu> get() = _store
private val _store = MutableLiveData<StoreWithMenu>()
Expand Down Expand Up @@ -70,6 +81,36 @@ class StoreDetailViewModel @Inject constructor(
private val _tokenState = SingleLiveEvent<TokenState>()
val tokenState: LiveData<TokenState> get() = _tokenState

val callABTestExperimentGroup = flow {
abTestUseCase(Experiment.BUSINESS_CALL.experimentTitle).onSuccess {
emit(it)
when (it) {
ExperimentGroup.CALL_NUMBER -> {
EventLogger.logClickEvent(
EventAction.BUSINESS,
AnalyticsConstant.Label.BUSINESS_CALL_NUMBER,
"전화하기버튼 우측위치"
)
hsgo2430 marked this conversation as resolved.
Show resolved Hide resolved
}

ExperimentGroup.CALL_FLOATING -> {
EventLogger.logClickEvent(
EventAction.BUSINESS,
AnalyticsConstant.Label.BUSINESS_CALL_FLOATING,
"전화하기버튼 플로팅"
)
}
}
}.onFailure {
emit(Experiment.BUSINESS_CALL.experimentGroups.first())
hsgo2430 marked this conversation as resolved.
Show resolved Hide resolved
Timber.d("Fail to get A/b Test Data")
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = Experiment.BUSINESS_CALL.experimentGroups.first()
)

fun getStoreWithMenu(storeId: Int) = viewModelScope.launchWithLoading {
getStoreWithMenuUseCase(storeId).also { store ->
_store.value = store
Expand Down
32 changes: 30 additions & 2 deletions koin/src/main/res/layout/store_activity_detail.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,32 @@
android:textAppearance="@style/TextAppearance.Koin.Regular.15"
android:textColor="@color/gray9"
app:layout_constraintStart_toStartOf="@+id/guideline_start"
app:layout_constraintTop_toBottomOf="@+id/store_detail_title_textview" />
app:layout_constraintTop_toBottomOf="@+id/store_detail_title_textview"/>

<TextView
android:id="@+id/store_detail_phone_textview"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textAppearance="@style/TextAppearance.Koin.Regular.15"
android:textColor="@color/black"
app:layout_constraintBaseline_toBaselineOf="@id/store_detail_const_phone_num"
app:layout_constraintEnd_toStartOf="@+id/guideline_end"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@+id/store_detail_const_phone_num"
tools:text="010-5181-1203" />

<ImageView
android:id="@+id/store_detail_phone_image"
android:layout_width="16dp"
android:layout_height="16dp"
nodobi marked this conversation as resolved.
Show resolved Hide resolved
android:layout_marginStart="10dp"
android:src="@drawable/ic_call"
app:layout_constraintBottom_toBottomOf="@id/store_detail_phone_textview"
app:layout_constraintStart_toEndOf="@id/store_detail_phone_textview"
app:layout_constraintTop_toTopOf="@id/store_detail_phone_textview"
app:tint="@color/colorPrimary" />

<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_phone"
android:layout_width="0dp"
Expand Down Expand Up @@ -420,6 +432,22 @@
app:layout_constraintEnd_toEndOf="parent"
/>

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/call_floating_button"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="end|bottom"
android:layout_marginEnd="16dp"
android:layout_marginBottom="15dp"
android:backgroundTint="@color/colorPrimary"
app:borderWidth="0dp"
app:fabSize="mini"
android:src="@drawable/ic_call"
app:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
<!--navigation drawer-->
<include layout="@layout/base_navigation_drawer_right" />
Expand Down