diff --git a/README.md b/README.md
index 4fbed22d1..8d211d8f9 100644
--- a/README.md
+++ b/README.md
@@ -1,136 +1,102 @@
-# Koreatech_IO_Android
-Koreatech Community Project (ver. Android)
-
-## Patch Notes
-- 1.0.0 Beta
- - 자유게시판, 익명게시판, 채용게시판 릴리즈
- - 콜밴쉐어링 서비스
- - 주변상점 서비스 릴리즈
- - 식단 서비스 릴리즈
- - 중고 장터 서비스 릴리즈
-
-
-- 1.1.0 Beta
- - 버스 서비스 릴리즈
- - 홈화면 추가
-
-
-- 2.0.0
- - 정식 release
-
-
-- 2.0.1
- - 서비스 중복 호출 방지
-
-
-- 2.0.2
- - 닉네임 같을시 버그 수정
-
-
-- 2.0.3
- - 회원가입 이메일 409 error 처리
- - 이메일 koreatech portal로 연결
-
-
-- 2.1.0
- - 비로그인 기반 서비스 추가
- - 익명게시판 추가
- - 동아리 서비스 추가
-
-
-- 2.1.1
- - 식단표 스크롤 오류 수정
-
-
-- 2.2.0
- - 전체 서비스 디자인 변경
- - bottom navigation bar 추가
- - 시간표 서비스 추가
-
-
-- 2.2.1
- - 비작성자시 삭제 버튼 노출 수정
-
-
-- 2.2.2
- - 시간표 테두리 변경
- - 위젯 자동 업데이트 변경
- - 카테고리 유지 안됨 수정
-
-- 2.2.3
- - 복덕방 서비스 추가
- - 분실물 서비스 추가
- - 비밀번호 검사 로직 변경
-
-- 2.2.4
- - 버스시간표 수정
-
-- 2.2.5
- - 식단표 위젯 추가
- - 버스 위젯 추가
-
-- 2.2.6
- - 상점 전단지 보기 추가
- - 상점 주소 및 배달비 추가
- - 동아리 상세 페이지 글자 아래로 밀림 수정
- - 전화 버튼 사이즈 조절
- - 버스 타이머 수정
- - 식단표 순서 조절
- - 식단표 2캠패스 출력시 한식 미 출력 수정
- - 채용게시판에서 취업게시판으로 변경
-
-- 2.2.7
- - 검색 기능추가
- - 시간표 학기별 조회 기능 추가
- - 에티터 추가
- - 버스 시간표 업데이트
-
-- 2.2.8
- - 로그인시 중고 장터 접근 안 되는 버그 수정
-
-- 3.0.0
- - 서비스 축소
- - 주변 상점
- - 버스 / 교통
- - 식단
- - 시간표
- - 복덕방
- - 메인 화면 변경
- - Navigation Drawer 변경
-
-- 3.0.1
- - 버그 수정
- - 상점 전화 번호 출력 오류 수정
- - 상점 주소 출력 오류 수정
- - Firebase Event 추가
- - 상점 진입 후 상점에 전화하는 시간 이벤트 추가
-
-- 3.0.2
- - 상점 기능 추가
- - 상점 검색
- - 상점 상세화면에서 다른 상점 추천
- - 카테고리 한번 더 클릭시 전체 상점 출력
- - 상점 전화하기 버튼 디자인 변경
-
-- 3.0.3
- - 원룸 도어락 표시 버그 수정
- - Target SDK 30 업데이트
- - Read & Write Permission 제거
-
-- 3.0.4
- - 복덕방 서비스 디자인 개선
-
-- 3.0.7
- - Target SDK 31 업데이트
- - 버스 서비스 업데이트
- - 식당 서비스 업데이트
- - Kotlin 도입
- - clean architecture 모듈 분리 진행
-
-## Technology stack
- - Java 8
- - Android SDK
- - Firebase
- - Retrofit2
- - RxJava
- - ButterKnife
+
+
+# 코인 - 한기대 커뮤니티
+
+[![Kotlin](https://img.shields.io/badge/Kotlin-1.9.22-blue.svg)](https://kotlinlang.org)
+[![Java](https://img.shields.io/badge/Java-17-orange.svg)](https://kotlinlang.org)
+[![Gradle](https://img.shields.io/badge/gradle-8.6-green.svg)](https://gradle.org/)
+[![Android Gradle](https://img.shields.io/badge/AGP-8.4.1-green.svg)](https://gradle.org/)
+
+[![minSdkVersion](https://img.shields.io/badge/minSdkVersion-26-red)](https://developer.android.com/distribute/best-practices/develop/target-sdk)
+[![compileSdkVersion](https://img.shields.io/badge/compileSdkVersion-34-red)](https://developer.android.com/distribute/best-practices/develop/target-sdk)
+[![targetSdkVersion](https://img.shields.io/badge/targetSdkVersion-34-red)](https://developer.android.com/distribute/best-practices/develop/target-sdk)
+
+코인은 한국기술교육대학교 학생들을 위하여 제공하는 커뮤니티 플랫폼 서비스입니다.
+
+[코인 - 한기대 커뮤니티 : Google Play Store 바로가기](https://play.google.com/store/apps/details?id=in.koreatech.koin&hl=ko)
+
+[✨ BCSD 블로그와 함께 코인 프로젝트 훔쳐보기 ✨](https://blog.bcsdlab.com/introduce)
+
+## 코인 사장님
+`코인 - 한기대 커뮤니티` 앱과는 별도로 사장님들에게 직접 가게를 등록할 수 있도록 코인 사장님 앱을 제공하고 있습니다.
+
+[코인 사장님 : Google Play Store 바로가기](https://play.google.com/store/apps/details?id=in.koreatech.business&hl=ko)
+
+## Tech Stack
+- Java & Kotlin
+- XML & Compose
+- Jetpack AAC
+- Coroutine Flow
+- Multi-Module
+- MVVM & MVI
+- Orbit
+- Retrofit2 & OkHttp3
+- Gson & kotlinx.serialization
+- Hilt
+- Timber
+- Kakao share
+- Naver Map
+- Google Analytics
+- Firebase Crashlytics
+- Firebase Cloud Message
+- Firebase App Distribution
+
+
+
+## Git Branch Strategy
+
+
+```mermaid
+---
+title: KOIN Git Flow
+---
+
+%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'showBranches': true, 'mainBranchName': 'production'}} }%%
+ gitGraph
+ commit tag: "v1.0.0"
+ branch hotfix/A
+ checkout production
+ branch develop
+ checkout develop
+ commit
+ branch feature/A
+ checkout feature/A
+ checkout production
+ checkout hotfix/A
+ commit
+ checkout develop
+ checkout feature/A
+ commit
+ checkout production
+ merge hotfix/A tag: "v1.0.1"
+ checkout feature/A
+ commit
+ checkout develop
+ branch feature/B
+ commit
+ checkout develop
+ merge hotfix/A
+ checkout feature/B
+ commit
+ checkout feature/A
+ commit
+ checkout develop
+ merge feature/A
+ branch release/v1.1.0
+ checkout develop
+ merge feature/B
+ branch release/v1.1.0B
+ checkout release/v1.1.0
+ commit
+ commit
+ checkout release/v1.1.0B
+ commit
+ commit
+ checkout production
+ merge release/v1.1.0 tag: "v1.1.0"
+ merge release/v1.1.0B tag: "v1.1.0B"
+ checkout release/v1.1.0
+ checkout develop
+ merge release/v1.1.0
+ merge release/v1.1.0B
+```
\ No newline at end of file
diff --git a/core/src/main/java/in/koreatech/koin/core/abtest/Experiment.kt b/core/src/main/java/in/koreatech/koin/core/abtest/Experiment.kt
index f906f97d2..965a32171 100644
--- a/core/src/main/java/in/koreatech/koin/core/abtest/Experiment.kt
+++ b/core/src/main/java/in/koreatech/koin/core/abtest/Experiment.kt
@@ -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" }
@@ -27,4 +28,7 @@ object ExperimentGroup {
const val MAIN_BANNER_ORIGINAL = "banner_original"
const val MAIN_BANNER_NEW = "banner_new"
-}
\ No newline at end of file
+
+ const val CALL_NUMBER = "call_number"
+ const val CALL_FLOATING = "call_floating"
+}
diff --git a/core/src/main/java/in/koreatech/koin/core/analytics/EventLogger.kt b/core/src/main/java/in/koreatech/koin/core/analytics/EventLogger.kt
index d6f781c40..454e51d07 100644
--- a/core/src/main/java/in/koreatech/koin/core/analytics/EventLogger.kt
+++ b/core/src/main/java/in/koreatech/koin/core/analytics/EventLogger.kt
@@ -24,6 +24,8 @@ object EventLogger {
logEvent(action, EventCategory.CLICK, label, value, *extras)
}
+
+
/**
* 스크롤 이벤트 로깅
* @param action: 이벤트 발생 도메인(BUSINESS, CAMPUS, USER)
@@ -71,6 +73,8 @@ object EventLogger {
param(EVENT_LABEL, "$label (debug)")
param(VALUE, value)
}
+ println("EventLoggerCustom: ${action}, ${category}, $label, $value, ")
+
} else {
Firebase.analytics.logEvent(action) {
param(EVENT_CATEGORY, category)
@@ -96,6 +100,7 @@ object EventLogger {
param("${it.key}_debug", it.value)
}
}
+ println("EventLogger: ${action.value}, ${category.value}, $label, $value, ${extras.joinToString { ", ${it.key}: ${it.value}" }}")
} else {
Firebase.analytics.logEvent(action.value) {
param(EVENT_CATEGORY, category.value)
@@ -112,14 +117,14 @@ object EventLogger {
enum class EventAction(val value: String) {
BUSINESS("BUSINESS"),
CAMPUS("CAMPUS"),
- USER("USER")
+ USER("USER"),
}
enum class EventCategory(val value: String) {
CLICK("click"),
SCROLL("scroll"),
SWIPE("swipe"), // 하단 뒤로가기(아이폰의 swipe 뒤로가기와 대응)
- NOTIFICATION("notification")
+ NOTIFICATION("notification"),
}
-data class EventExtra(val key: String, val value: String)
\ No newline at end of file
+data class EventExtra(val key: String, val value: String)
diff --git a/core/src/main/java/in/koreatech/koin/core/constant/AnalyticsConstant.kt b/core/src/main/java/in/koreatech/koin/core/constant/AnalyticsConstant.kt
index 2e6df476d..3417b6336 100644
--- a/core/src/main/java/in/koreatech/koin/core/constant/AnalyticsConstant.kt
+++ b/core/src/main/java/in/koreatech/koin/core/constant/AnalyticsConstant.kt
@@ -99,11 +99,15 @@ object AnalyticsConstant {
const val CAMPUS_DINING_1 = "CAMPUS_dining_1"
const val CAMPUS_NOTICE_1 = "CAMPUS_notice_1"
+ const val APP_MAIN_NOTICE_DETAIL = "app_main_notice_detail"
const val POPULAR_NOTICE_BANNER = "popular_notice_banner"
const val TO_MANAGE_KEYWORD = "to_manage_keyword"
+
+ const val BUSINESS_CALL_NUMBER = " BUSINESS_call_1"
+ const val BUSINESS_CALL_FLOATING = "BUSINESS_call_1"
}
const val PREVIOUS_PAGE = "previous_page"
const val CURRENT_PAGE = "current_page"
const val DURATION_TIME = "duration_time"
-}
\ No newline at end of file
+}
diff --git a/core/src/main/res/drawable/ic_benefit_icon.png b/core/src/main/res/drawable/ic_benefit_icon.png
new file mode 100644
index 000000000..4e471e513
Binary files /dev/null and b/core/src/main/res/drawable/ic_benefit_icon.png differ
diff --git a/core/src/main/res/drawable/rounded_corners_black.xml b/core/src/main/res/drawable/rounded_corners_black.xml
index 8897a9943..cf98b013c 100644
--- a/core/src/main/res/drawable/rounded_corners_black.xml
+++ b/core/src/main/res/drawable/rounded_corners_black.xml
@@ -2,5 +2,5 @@
-
+
\ No newline at end of file
diff --git a/data/src/main/java/in/koreatech/koin/data/api/auth/UserAuthApi.kt b/data/src/main/java/in/koreatech/koin/data/api/auth/UserAuthApi.kt
index 81bc3578c..7f99b9ba6 100644
--- a/data/src/main/java/in/koreatech/koin/data/api/auth/UserAuthApi.kt
+++ b/data/src/main/java/in/koreatech/koin/data/api/auth/UserAuthApi.kt
@@ -10,6 +10,7 @@ import `in`.koreatech.koin.data.request.user.UserRequest
import `in`.koreatech.koin.data.response.notification.NotificationPermissionInfoResponse
import `in`.koreatech.koin.data.response.store.StoreReviewResponse
import `in`.koreatech.koin.data.response.user.ABTestResponse
+import `in`.koreatech.koin.data.response.user.ABTestTokenResponse
import `in`.koreatech.koin.data.response.user.UserInfoEditResponse
import `in`.koreatech.koin.data.response.user.UserResponse
import retrofit2.Response
@@ -90,6 +91,9 @@ interface UserAuthApi {
@GET(URLConstant.OWNER.OWNER)
suspend fun getOwnerTokenIsValid()
+ @POST("abtest/assign/token")
+ suspend fun updateABTestToken(): ABTestTokenResponse
+
@POST("abtest/assign")
suspend fun postABTestAssign(@Body abTestRequest: ABTestRequest): ABTestResponse
diff --git a/data/src/main/java/in/koreatech/koin/data/mapper/StoreMapper.kt b/data/src/main/java/in/koreatech/koin/data/mapper/StoreMapper.kt
index 98dd28ee2..5b4c00ca6 100644
--- a/data/src/main/java/in/koreatech/koin/data/mapper/StoreMapper.kt
+++ b/data/src/main/java/in/koreatech/koin/data/mapper/StoreMapper.kt
@@ -63,7 +63,9 @@ fun StoreItemResponse.toStore(): Store = Store(
closeTime = it.closeTime ?: ""
)
}.orEmpty().getOrElse(0) { Store.OpenData(localDayOfWeekName, false, "00:00", "00:00") },
- categoryIds = categoryIds
+ categoryIds = categoryIds,
+ benefitDetails = benefitDetails ?: benefitDetail?.toStringArray() ?: emptyList(),
+
)
fun StoreEventItemReponse.toStoreEvent(): StoreEvent = StoreEvent(
diff --git a/data/src/main/java/in/koreatech/koin/data/repository/UserRepositoryImpl.kt b/data/src/main/java/in/koreatech/koin/data/repository/UserRepositoryImpl.kt
index c6094fce6..6a696135c 100644
--- a/data/src/main/java/in/koreatech/koin/data/repository/UserRepositoryImpl.kt
+++ b/data/src/main/java/in/koreatech/koin/data/repository/UserRepositoryImpl.kt
@@ -120,6 +120,12 @@ class UserRepositoryImpl @Inject constructor(
userRemoteDataSource.verifyPassword(PasswordRequest(hashedPassword))
}
+ override suspend fun updateABTestToken() {
+ userRemoteDataSource.updateABTestToken().accessHistoryId.also {
+ tokenLocalDataSource.saveAccessHistoryId(it)
+ }
+ }
+
override suspend fun postABTestAssign(title: String): ABTest {
userRemoteDataSource.postABTestAssign(ABTestRequest(title)).let {
return ABTest(it.variableName, it.accessHistoryId)
diff --git a/data/src/main/java/in/koreatech/koin/data/response/store/StoreItemResponse.kt b/data/src/main/java/in/koreatech/koin/data/response/store/StoreItemResponse.kt
index 3d260182c..890968a2f 100644
--- a/data/src/main/java/in/koreatech/koin/data/response/store/StoreItemResponse.kt
+++ b/data/src/main/java/in/koreatech/koin/data/response/store/StoreItemResponse.kt
@@ -14,7 +14,9 @@ data class StoreItemResponse(
@SerializedName("is_event") val isEvent: Boolean?,
@SerializedName("is_open") val isOpen: Boolean?,
@SerializedName("average_rate") val averageRate : Double,
- @SerializedName("review_count") val reviewCount : Int
+ @SerializedName("review_count") val reviewCount : Int,
+ @SerializedName("benefit_details") val benefitDetails: List?,
+ @SerializedName("benefit_detail") val benefitDetail: String?,
) {
data class OpenResponseDTO(
@SerializedName("day_of_week")
diff --git a/data/src/main/java/in/koreatech/koin/data/response/user/ABTestTokenResponse.kt b/data/src/main/java/in/koreatech/koin/data/response/user/ABTestTokenResponse.kt
new file mode 100644
index 000000000..b8c4e5be3
--- /dev/null
+++ b/data/src/main/java/in/koreatech/koin/data/response/user/ABTestTokenResponse.kt
@@ -0,0 +1,7 @@
+package `in`.koreatech.koin.data.response.user
+
+import com.google.gson.annotations.SerializedName
+
+data class ABTestTokenResponse(
+ @SerializedName("accessHistoryId") val accessHistoryId: String
+)
diff --git a/data/src/main/java/in/koreatech/koin/data/source/remote/UserRemoteDataSource.kt b/data/src/main/java/in/koreatech/koin/data/source/remote/UserRemoteDataSource.kt
index 8ebd69fea..31e4723cb 100644
--- a/data/src/main/java/in/koreatech/koin/data/source/remote/UserRemoteDataSource.kt
+++ b/data/src/main/java/in/koreatech/koin/data/source/remote/UserRemoteDataSource.kt
@@ -76,6 +76,11 @@ class UserRemoteDataSource(
suspend fun verifyPassword(passwordRequest: PasswordRequest) {
userAuthApi.checkPassword(passwordRequest)
}
+
+ suspend fun updateABTestToken(): ABTestTokenResponse {
+ return userAuthApi.updateABTestToken()
+ }
+
suspend fun postABTestAssign(abTestRequest: ABTestRequest): ABTestResponse {
return userAuthApi.postABTestAssign(abTestRequest)
}
diff --git a/domain/src/main/java/in/koreatech/koin/domain/model/store/Store.kt b/domain/src/main/java/in/koreatech/koin/domain/model/store/Store.kt
index b143d6326..31fc51925 100644
--- a/domain/src/main/java/in/koreatech/koin/domain/model/store/Store.kt
+++ b/domain/src/main/java/in/koreatech/koin/domain/model/store/Store.kt
@@ -18,7 +18,8 @@ data class Store(
val averageRate : Double,
val reviewCount : Int,
val open: OpenData,
- val categoryIds: List
+ val categoryIds: List,
+ val benefitDetails: List,
) {
data class OpenData(
val dayOfWeek: String,
@@ -44,4 +45,4 @@ data class Store(
}
}
}
-}
\ No newline at end of file
+}
diff --git a/domain/src/main/java/in/koreatech/koin/domain/model/store/StoreCategories.kt b/domain/src/main/java/in/koreatech/koin/domain/model/store/StoreCategories.kt
index d81426529..6f9a8d0fd 100644
--- a/domain/src/main/java/in/koreatech/koin/domain/model/store/StoreCategories.kt
+++ b/domain/src/main/java/in/koreatech/koin/domain/model/store/StoreCategories.kt
@@ -2,6 +2,6 @@ package `in`.koreatech.koin.domain.model.store
data class StoreCategories(
val id: Int,
- val imageUrl: String,
+ val imageUrl: Any,
val name: String
)
\ No newline at end of file
diff --git a/domain/src/main/java/in/koreatech/koin/domain/repository/UserRepository.kt b/domain/src/main/java/in/koreatech/koin/domain/repository/UserRepository.kt
index decad4ed3..35a02fba9 100644
--- a/domain/src/main/java/in/koreatech/koin/domain/repository/UserRepository.kt
+++ b/domain/src/main/java/in/koreatech/koin/domain/repository/UserRepository.kt
@@ -27,5 +27,6 @@ interface UserRepository {
suspend fun deleteDeviceToken()
suspend fun verifyPassword(hashedPassword: String)
suspend fun updateUserPassword(user: User, hashedPassword: String)
+ suspend fun updateABTestToken()
suspend fun postABTestAssign(title: String) : ABTest
}
\ No newline at end of file
diff --git a/domain/src/main/java/in/koreatech/koin/domain/usecase/user/ABTestUseCase.kt b/domain/src/main/java/in/koreatech/koin/domain/usecase/user/ABTestUseCase.kt
index 358abfc31..d256f8d88 100644
--- a/domain/src/main/java/in/koreatech/koin/domain/usecase/user/ABTestUseCase.kt
+++ b/domain/src/main/java/in/koreatech/koin/domain/usecase/user/ABTestUseCase.kt
@@ -1,24 +1,25 @@
package `in`.koreatech.koin.domain.usecase.user
-import `in`.koreatech.koin.domain.error.user.UserErrorHandler
-import `in`.koreatech.koin.domain.model.error.ErrorHandler
import `in`.koreatech.koin.domain.repository.TokenRepository
import `in`.koreatech.koin.domain.repository.UserRepository
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
class ABTestUseCase @Inject constructor(
private val userRepository: UserRepository,
- private val tokenRepository: TokenRepository,
- private val userErrorHandler: UserErrorHandler
+ private val tokenRepository: TokenRepository
) {
- suspend operator fun invoke(title: String): Pair {
- return try {
- val accessHistory = userRepository.postABTestAssign(title)
- val accessHistoryId = accessHistory.accessHistoryId
- tokenRepository.saveAccessHistoryId(accessHistoryId)
- accessHistory.variableName to null
- } catch (t: Throwable) {
- null to userErrorHandler.handleVerifyUserPasswordError(t)
+ private val mutex = Mutex()
+
+ suspend operator fun invoke(title: String): Result {
+ return runCatching {
+ mutex.withLock {
+ if (tokenRepository.getAccessHistoryId() == null) {
+ userRepository.updateABTestToken()
+ }
+ }
+ userRepository.postABTestAssign(title).variableName
}
}
}
\ No newline at end of file
diff --git a/koin.png b/koin.png
new file mode 100644
index 000000000..1df212764
Binary files /dev/null and b/koin.png differ
diff --git a/koin/src/main/java/in/koreatech/koin/ui/dining/viewmodel/DiningViewModel.kt b/koin/src/main/java/in/koreatech/koin/ui/dining/viewmodel/DiningViewModel.kt
index 05543bc2c..b38fd6222 100644
--- a/koin/src/main/java/in/koreatech/koin/ui/dining/viewmodel/DiningViewModel.kt
+++ b/koin/src/main/java/in/koreatech/koin/ui/dining/viewmodel/DiningViewModel.kt
@@ -11,8 +11,6 @@ import `in`.koreatech.koin.domain.usecase.user.ABTestUseCase
import `in`.koreatech.koin.domain.usecase.user.GetUserStatusUseCase
import `in`.koreatech.koin.domain.util.DiningUtil
import `in`.koreatech.koin.domain.util.TimeUtil
-import `in`.koreatech.koin.domain.util.onFailure
-import `in`.koreatech.koin.domain.util.onSuccess
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
diff --git a/koin/src/main/java/in/koreatech/koin/ui/main/activity/MainActivity.kt b/koin/src/main/java/in/koreatech/koin/ui/main/activity/MainActivity.kt
index 9d8c3a476..d03c5126f 100644
--- a/koin/src/main/java/in/koreatech/koin/ui/main/activity/MainActivity.kt
+++ b/koin/src/main/java/in/koreatech/koin/ui/main/activity/MainActivity.kt
@@ -3,7 +3,6 @@ package `in`.koreatech.koin.ui.main.activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
-import android.util.Log
import android.view.View
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxWidth
@@ -13,6 +12,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
+import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
@@ -49,6 +49,7 @@ import `in`.koreatech.koin.databinding.ActivityMainBinding
import `in`.koreatech.koin.domain.model.bus.BusType
import `in`.koreatech.koin.domain.model.bus.timer.BusArrivalInfo
import `in`.koreatech.koin.domain.model.dining.DiningPlace
+import `in`.koreatech.koin.domain.model.store.StoreCategories
import `in`.koreatech.koin.ui.article.ArticleActivity
import `in`.koreatech.koin.ui.bus.BusActivity
import `in`.koreatech.koin.ui.dining.DiningActivity
@@ -148,15 +149,28 @@ class MainActivity : KoinNavigationDrawerTimeActivity() {
private val storeCategoriesRecyclerAdapter = StoreCategoriesRecyclerAdapter().apply {
setOnItemClickListener { id, name ->
- EventLogger.logClickEvent(
- EventAction.BUSINESS,
- AnalyticsConstant.Label.MAIN_SHOP_CATEGORIES,
- name,
- EventExtra(AnalyticsConstant.PREVIOUS_PAGE, "메인"),
- EventExtra(AnalyticsConstant.CURRENT_PAGE, name),
- EventExtra(AnalyticsConstant.DURATION_TIME, getElapsedTimeAndReset().toString())
- )
- gotoStoreActivity(id + 1)
+ if(id == 0){
+ startActivity(Intent(this@MainActivity, CallBenefitStoreActivity::class.java))
+ EventLogger.logClickEvent(
+ EventAction.BUSINESS,
+ AnalyticsConstant.Label.MAIN_SHOP_BENEFIT,
+ name,
+ EventExtra(AnalyticsConstant.PREVIOUS_PAGE, "메인"),
+ EventExtra(AnalyticsConstant.CURRENT_PAGE, "benefit"),
+ EventExtra(AnalyticsConstant.DURATION_TIME, getElapsedTimeAndReset().toString())
+ )
+ }
+ else{
+ EventLogger.logClickEvent(
+ EventAction.BUSINESS,
+ AnalyticsConstant.Label.MAIN_SHOP_CATEGORIES,
+ name,
+ EventExtra(AnalyticsConstant.PREVIOUS_PAGE, "메인"),
+ EventExtra(AnalyticsConstant.CURRENT_PAGE, name),
+ EventExtra(AnalyticsConstant.DURATION_TIME, getElapsedTimeAndReset().toString())
+ )
+ gotoStoreActivity(id)
+ }
}
}
@@ -200,6 +214,7 @@ class MainActivity : KoinNavigationDrawerTimeActivity() {
scrollPercentage = 100.0f * offset / (range - extent)
}
viewModel.postABTestAssign(Experiment.BENEFIT_STORE.experimentTitle)
+
storeListButton.setOnClickListener {
gotoStoreActivity(0)
}
@@ -227,6 +242,11 @@ class MainActivity : KoinNavigationDrawerTimeActivity() {
TabLayoutMediator(tabHotArticle, viewPagerHotArticle) { _, _ -> }.attach()
textSeeMoreArticle.setOnClickListener {
+ EventLogger.logClickEvent(
+ EventAction.CAMPUS,
+ AnalyticsConstant.Label.APP_MAIN_NOTICE_DETAIL,
+ getString(R.string.article_more)
+ )
startActivity(Intent(this@MainActivity, ArticleActivity::class.java))
}
@@ -268,8 +288,7 @@ class MainActivity : KoinNavigationDrawerTimeActivity() {
}
recyclerViewStoreCategory.apply {
- layoutManager =
- LinearLayoutManager(this@MainActivity, RecyclerView.HORIZONTAL, false)
+ layoutManager = GridLayoutManager(this@MainActivity, 6)
adapter = storeCategoriesRecyclerAdapter
}
@@ -306,6 +325,44 @@ class MainActivity : KoinNavigationDrawerTimeActivity() {
}
private fun initViewModel() = with(viewModel) {
+ getStoreCategories(StoreCategories(-1, R.drawable.ic_benefit_icon, "혜택"))
+
+ observeLiveData(variableName) {
+ when (viewModel.variableName.value) {
+ ExperimentGroup.A -> {
+ EventLogger.logCustomEvent(
+ action = "AB_TEST",
+ category = "a/b test 로깅(3차 스프린트, 혜택페이지)",
+ label = "BUSINESS_benefit_1",
+ value = "혜택X"
+ )
+ binding.storeButtonLayout.visibility = View.GONE
+ binding.recyclerViewStoreCategory.visibility = View.VISIBLE
+ }
+
+ ExperimentGroup.B -> {
+ EventLogger.logCustomEvent(
+ action = "AB_TEST",
+ category = "a/b test 로깅(3차 스프린트, 혜택페이지)",
+ label = "BUSINESS_benefit_1",
+ value = "혜택O"
+ )
+ binding.storeButtonLayout.visibility = View.VISIBLE
+ binding.recyclerViewStoreCategory.visibility = View.GONE
+ }
+
+ else -> {
+ EventLogger.logCustomEvent(
+ action = "AB_TEST",
+ category = "a/b test 로깅(3차 스프린트, 혜택페이지)",
+ label = "BUSINESS_benefit_1",
+ value = "혜택X"
+ )
+ binding.storeButtonLayout.visibility = View.GONE
+ binding.recyclerViewStoreCategory.visibility = View.VISIBLE
+ }
+ }
+ }
observeLiveData(isLoading) {
binding.mainSwipeRefreshLayout.isRefreshing = it
}
@@ -322,7 +379,7 @@ class MainActivity : KoinNavigationDrawerTimeActivity() {
}
}
observeLiveData(storeCategories) {
- storeCategoriesRecyclerAdapter.submitList(it.drop(1))
+ storeCategoriesRecyclerAdapter.submitList(it)
}
binding.recyclerViewStoreCategory.visibility = View.GONE
binding.storeButtonLayout.visibility = View.VISIBLE
diff --git a/koin/src/main/java/in/koreatech/koin/ui/main/adapter/StoreCategoriesRecyclerAdapter.kt b/koin/src/main/java/in/koreatech/koin/ui/main/adapter/StoreCategoriesRecyclerAdapter.kt
index 4faebb901..691659b30 100644
--- a/koin/src/main/java/in/koreatech/koin/ui/main/adapter/StoreCategoriesRecyclerAdapter.kt
+++ b/koin/src/main/java/in/koreatech/koin/ui/main/adapter/StoreCategoriesRecyclerAdapter.kt
@@ -9,6 +9,7 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import `in`.koreatech.koin.databinding.MainItemStoreBinding
+import `in`.koreatech.koin.databinding.StoreCategoryItemBinding
import `in`.koreatech.koin.domain.model.store.StoreCategories
class StoreCategoriesRecyclerAdapter(): ListAdapter(
@@ -16,7 +17,7 @@ diffCallback
){
var onItemClickListener: OnItemClickListener? = null
- inner class StoreCategoriesViewHolder(val binding: MainItemStoreBinding) : RecyclerView.ViewHolder(binding.root){
+ inner class StoreCategoriesViewHolder(val binding: StoreCategoryItemBinding) : RecyclerView.ViewHolder(binding.root){
val container = binding.container
val storeCategoryImage = binding.imageViewStoreCategory
val storeCategoryName = binding.textViewStoreCategory
@@ -24,7 +25,7 @@ diffCallback
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StoreCategoriesRecyclerAdapter.StoreCategoriesViewHolder {
val inflater = LayoutInflater.from(parent.context)
- val binding = MainItemStoreBinding.inflate(inflater, parent, false)
+ val binding = StoreCategoryItemBinding.inflate(inflater, parent, false)
return StoreCategoriesViewHolder(binding)
}
diff --git a/koin/src/main/java/in/koreatech/koin/ui/main/viewmodel/MainActivityViewModel.kt b/koin/src/main/java/in/koreatech/koin/ui/main/viewmodel/MainActivityViewModel.kt
index aa56b5048..143ef83f2 100644
--- a/koin/src/main/java/in/koreatech/koin/ui/main/viewmodel/MainActivityViewModel.kt
+++ b/koin/src/main/java/in/koreatech/koin/ui/main/viewmodel/MainActivityViewModel.kt
@@ -1,6 +1,5 @@
package `in`.koreatech.koin.ui.main.viewmodel
-import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asFlow
@@ -9,7 +8,6 @@ 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.viewmodel.BaseViewModel
@@ -26,8 +24,6 @@ import `in`.koreatech.koin.domain.usecase.store.GetStoreCategoriesUseCase
import `in`.koreatech.koin.domain.usecase.user.ABTestUseCase
import `in`.koreatech.koin.domain.util.DiningUtil
import `in`.koreatech.koin.domain.util.TimeUtil
-import `in`.koreatech.koin.domain.util.onFailure
-import `in`.koreatech.koin.domain.util.onSuccess
import `in`.koreatech.koin.ui.main.state.ArticleMainState
import `in`.koreatech.koin.ui.main.state.toContent
import `in`.koreatech.koin.ui.main.state.toNoti
@@ -96,16 +92,18 @@ class MainActivityViewModel @Inject constructor(
emit(it)
when (it) {
ExperimentGroup.MAIN_DINING_NEW -> {
- EventLogger.logClickEvent(
- EventAction.CAMPUS,
+ EventLogger.logCustomEvent(
+ "AB_TEST",
+ "a/b test 로깅(식단 메인 진입점)",
AnalyticsConstant.Label.CAMPUS_DINING_1,
"더보기O"
)
}
ExperimentGroup.MAIN_DINING_ORIGINAL -> {
- EventLogger.logClickEvent(
- EventAction.CAMPUS,
+ EventLogger.logCustomEvent(
+ "AB_TEST",
+ "a/b test 로깅(식단 메인 진입점)",
AnalyticsConstant.Label.CAMPUS_DINING_1,
"더보기X"
)
@@ -166,7 +164,6 @@ class MainActivityViewModel @Inject constructor(
init {
updateDining()
- getStoreCategories()
}
fun postABTestAssign(title: String) = viewModelScope.launchWithLoading {
@@ -234,9 +231,11 @@ class MainActivityViewModel @Inject constructor(
}
}
- fun getStoreCategories() {
+ fun getStoreCategories(storeCategory: StoreCategories) {
viewModelScope.launchWithLoading {
- _storeCategories.value = getStoreCategoriesUseCase()
+ val categoryList = getStoreCategoriesUseCase().drop(1).toMutableList()
+ categoryList.add(0, storeCategory)
+ _storeCategories.value = categoryList
}
}
diff --git a/koin/src/main/java/in/koreatech/koin/ui/store/activity/StoreActivity.kt b/koin/src/main/java/in/koreatech/koin/ui/store/activity/StoreActivity.kt
index 0abc1b060..d79e69ab4 100644
--- a/koin/src/main/java/in/koreatech/koin/ui/store/activity/StoreActivity.kt
+++ b/koin/src/main/java/in/koreatech/koin/ui/store/activity/StoreActivity.kt
@@ -1,6 +1,7 @@
package `in`.koreatech.koin.ui.store.activity
import android.annotation.SuppressLint
+import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -35,6 +36,7 @@ import `in`.koreatech.koin.core.viewpager.HorizontalMarginItemDecoration
import `in`.koreatech.koin.databinding.StoreActivityMainBinding
import `in`.koreatech.koin.domain.model.store.StoreEvent
import `in`.koreatech.koin.domain.model.store.StoreSorter
+import `in`.koreatech.koin.ui.businesssignup.BusinessSignUpCheckActivity
import `in`.koreatech.koin.ui.navigation.KoinNavigationDrawerTimeActivity
import `in`.koreatech.koin.ui.navigation.state.MenuState
import `in`.koreatech.koin.ui.store.adapter.StoreCategoriesRecyclerAdapter
@@ -100,18 +102,6 @@ class StoreActivity : KoinNavigationDrawerTimeActivity() {
EventExtra(AnalyticsConstant.DURATION_TIME, getElapsedTimeAndReset().toString())
)
}
-
- EventLogger.logClickEvent(
- EventAction.BUSINESS,
- AnalyticsConstant.Label.SHOP_CLICK,
- it.name,
- EventExtra(
- AnalyticsConstant.PREVIOUS_PAGE,
- viewModel.category.value?.name ?: "Unknown"
- ),
- EventExtra(AnalyticsConstant.CURRENT_PAGE, it.name),
- EventExtra(AnalyticsConstant.DURATION_TIME, getElapsedTimeAndReset().toString())
- )
}
}
@@ -389,7 +379,6 @@ class StoreActivity : KoinNavigationDrawerTimeActivity() {
with(binding) {
storeManyReviewCheckbox.setOnClickListener {
-
if (storeManyReviewCheckbox.isChecked) {
EventLogger.logClickEvent(
EventAction.BUSINESS,
@@ -490,6 +479,10 @@ class StoreActivity : KoinNavigationDrawerTimeActivity() {
viewModel.filterStoreIsDelivery(storeIsDeliveryCheckbox.isChecked)
}
+
+ goToBenefitActivityButton.setOnClickListener {
+ startActivity(Intent(this@StoreActivity, CallBenefitStoreActivity::class.java))
+ }
}
}
diff --git a/koin/src/main/java/in/koreatech/koin/ui/store/activity/StoreDetailActivity.kt b/koin/src/main/java/in/koreatech/koin/ui/store/activity/StoreDetailActivity.kt
index 253df1844..1e9755005 100644
--- a/koin/src/main/java/in/koreatech/koin/ui/store/activity/StoreDetailActivity.kt
+++ b/koin/src/main/java/in/koreatech/koin/ui/store/activity/StoreDetailActivity.kt
@@ -4,21 +4,25 @@ import android.Manifest
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
-import android.content.Intent
+import android.content.SharedPreferences
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.Experiment
+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
@@ -97,6 +101,10 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
setContentView(binding.root)
initViewModel()
+ if (!isABTestAssigned) {
+ viewModel.postABTestAssign(Experiment.BUSINESS_CALL.experimentTitle)
+ isABTestAssigned = true
+ }
binding.koinBaseAppbar.storeDetailClickListener {
when (it.id) {
@@ -107,46 +115,7 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
}
AppBarBase.getRightButtonId() -> {
- dialogElapsedTime = System.currentTimeMillis() - dialogCurrentTime
- reviewElapsedTime = System.currentTimeMillis() - reviewCurrentTime
-
- showCallDialog()
- EventLogger.logClickEvent(
- EventAction.BUSINESS,
- AnalyticsConstant.Label.SHOP_CALL,
- viewModel.store.value?.name ?: "Unknown",
- EventExtra(AnalyticsConstant.DURATION_TIME, (dialogElapsedTime / 1000.0 ).toString())
- )
-
- if(intent.extras?.getBoolean(StoreDetailActivityContract.IS_BENEFIT) == true){
- EventLogger.logClickEvent(
- EventAction.BUSINESS,
- AnalyticsConstant.Label.BENEFIT_SHOP_CALL,
- viewModel.store.value?.name ?: "Unknown",
- EventExtra(AnalyticsConstant.DURATION_TIME, (dialogElapsedTime / 1000.0 ).toString())
- )
- }
- else{
- EventLogger.logClickEvent(
- EventAction.BUSINESS,
- AnalyticsConstant.Label.SHOP_CALL,
- (viewModel.store.value?.name
- ?: "Unknown") ,
- EventExtra(AnalyticsConstant.DURATION_TIME, (dialogElapsedTime / 1000.0 ).toString())
-
- )
- }
- if (currentTab == 2) {// 리뷰탭에서 전화누르기까지 시간
-
- EventLogger.logClickEvent(
- EventAction.BUSINESS,
- AnalyticsConstant.Label.SHOP_DETAIL_VIEW_REVIEW_BACK,
- viewModel.store.value?.name ?: "Unknown",
- EventExtra(AnalyticsConstant.PREVIOUS_PAGE, "리뷰"),
- EventExtra(AnalyticsConstant.CURRENT_PAGE, currentPage),
- EventExtra(AnalyticsConstant.DURATION_TIME, (reviewElapsedTime / 1000.0 ).toString())
- )
- }
+ toggleNavigationDrawer()
}
}
}
@@ -197,7 +166,10 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
viewModel.store.value?.name ?: "Unknown",
EventExtra(AnalyticsConstant.PREVIOUS_PAGE, "리뷰"),
EventExtra(AnalyticsConstant.CURRENT_PAGE, currentPage),
- EventExtra(AnalyticsConstant.DURATION_TIME, (reviewElapsedTime / 1000.0 ).toString())
+ EventExtra(
+ AnalyticsConstant.DURATION_TIME,
+ (reviewElapsedTime / 1000.0).toString()
+ )
)
}
}
@@ -218,7 +190,10 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
viewModel.store.value?.name ?: "Unknown",
EventExtra(AnalyticsConstant.PREVIOUS_PAGE, "리뷰"),
EventExtra(AnalyticsConstant.CURRENT_PAGE, currentPage),
- EventExtra(AnalyticsConstant.DURATION_TIME, (reviewElapsedTime / 1000.0 ).toString())
+ EventExtra(
+ AnalyticsConstant.DURATION_TIME,
+ (reviewElapsedTime / 1000.0).toString()
+ )
)
}
}
@@ -261,11 +236,30 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
.makeShort(getString(R.string.store_detail_wrong_store_id_message))
finish()
}
+
+ when (abtestName) {
+ 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
+ }
+ }
+
viewModel.getStoreWithMenu(storeId!!)
viewModel.getShopMenus(storeId)
viewModel.getShopEvents(storeId)
viewModel.getShopReviews(storeId)
+ initCallFunction()
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
@@ -284,10 +278,77 @@ 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()
+ if (intent.extras?.getBoolean(StoreDetailActivityContract.IS_BENEFIT) == true) {
+ EventLogger.logClickEvent(
+ EventAction.BUSINESS,
+ AnalyticsConstant.Label.BENEFIT_SHOP_CALL,
+ viewModel.store.value?.name ?: "Unknown",
+ EventExtra(AnalyticsConstant.DURATION_TIME, (dialogElapsedTime / 1000.0).toString())
+ )
+ } else {
+ 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)
+ observeLiveData(viewModel.variableName) {
+ abtestName = it
+ when (viewModel.variableName.value) {
+ ExperimentGroup.CALL_NUMBER -> {
+ EventLogger.logCustomEvent(
+ action = "AB_TEST",
+ category = "a/b test 로깅(전화하기)",
+ label = AnalyticsConstant.Label.BUSINESS_CALL_NUMBER,
+ value = "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
+ EventLogger.logCustomEvent(
+ action = "AB_TEST",
+ category = "a/b test 로깅(전화하기)",
+ label = AnalyticsConstant.Label.BUSINESS_CALL_FLOATING,
+ value = "floating"
+ )
+ }
+ }
+ }
+
observeLiveData(viewModel.storeReview) {
binding.storeDetailTabLayout.getTabAt(2)?.text =
getString(R.string.review, it.totalCount.toString())
@@ -363,8 +424,8 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
binding.storeDetailImageview.apply {
adapter = StoreDetailImageViewpagerAdapter(it.imageUrls) {
ImageZoomableDialog(context, it)
- .also {
- zoomableDialog -> zoomableDialog.show()
+ .also { zoomableDialog ->
+ zoomableDialog.show()
}
EventLogger.logClickEvent(
EventAction.BUSINESS,
@@ -376,6 +437,7 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
}
}
+
}
override fun onRestart() {
@@ -390,35 +452,36 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
val category = intent.extras?.getString(StoreDetailActivityContract.CATEGORY)
storeElapsedTime = System.currentTimeMillis() - storeCurrentTime
currentPage = "카테고리($category)"
- if ( isSwipeGesture) {
+ if (isSwipeGesture) {
EventLogger.logSwipeEvent(
EventAction.BUSINESS,
AnalyticsConstant.Label.SHOP_DETAIL_VIEW_BACK,
- viewModel.store.value?.name ?: "Unknown" ,
+ viewModel.store.value?.name ?: "Unknown",
EventExtra(AnalyticsConstant.CURRENT_PAGE, category ?: "Unknown"),
- EventExtra(AnalyticsConstant.DURATION_TIME, (storeElapsedTime / 1000.0 ).toString()),
+ EventExtra(AnalyticsConstant.DURATION_TIME, (storeElapsedTime / 1000.0).toString()),
)
- }
- else{
+ } else {
EventLogger.logSwipeEvent(
EventAction.BUSINESS,
AnalyticsConstant.Label.SHOP_DETAIL_VIEW_BACK,
- viewModel.store.value?.name ?: "Unknown" ,
+ viewModel.store.value?.name ?: "Unknown",
EventExtra(AnalyticsConstant.CURRENT_PAGE, category ?: "Unknown"),
- EventExtra(AnalyticsConstant.DURATION_TIME, (storeElapsedTime / 1000.0 ).toString()),
+ EventExtra(AnalyticsConstant.DURATION_TIME, (storeElapsedTime / 1000.0).toString()),
)
}
- if(currentTab == 2){
+ if (currentTab == 2) {
reviewElapsedTime = System.currentTimeMillis() - reviewCurrentTime
+
+
EventLogger.logClickEvent(
EventAction.BUSINESS,
AnalyticsConstant.Label.SHOP_DETAIL_VIEW_REVIEW_BACK,
viewModel.store.value?.name ?: "Unknown",
EventExtra(AnalyticsConstant.PREVIOUS_PAGE, "리뷰"),
EventExtra(AnalyticsConstant.CURRENT_PAGE, currentPage),
- EventExtra(AnalyticsConstant.DURATION_TIME, (reviewElapsedTime / 1000.0 ).toString())
+ EventExtra(AnalyticsConstant.DURATION_TIME, (reviewElapsedTime / 1000.0).toString())
)
}
@@ -496,5 +559,7 @@ class StoreDetailActivity : KoinNavigationDrawerActivity() {
const val ELAPSED_TIME = "elapsedTime"
const val STORE_NAME = "storeName"
const val BACK_ACTION = "back_action"
+ var isABTestAssigned = false
+ var abtestName = ""
}
-}
\ No newline at end of file
+}
diff --git a/koin/src/main/java/in/koreatech/koin/ui/store/adapter/StoreRecyclerAdapter.kt b/koin/src/main/java/in/koreatech/koin/ui/store/adapter/StoreRecyclerAdapter.kt
index b1e8135fd..17ca5d50a 100644
--- a/koin/src/main/java/in/koreatech/koin/ui/store/adapter/StoreRecyclerAdapter.kt
+++ b/koin/src/main/java/in/koreatech/koin/ui/store/adapter/StoreRecyclerAdapter.kt
@@ -42,25 +42,28 @@ class StoreRecyclerAdapter : ListAdapter
fun bind(store: Store) {
binding.storeNameTextview.text = store.name
binding.storeNameTextview.setStoreNameState(store.isOpen)
- binding.storeRatingScoreTextview.text = String.format("%.1f", store.averageRate)
+ binding.storeRatingScoreTextview.text = String.format("%.1f", store.averageRate)
binding.isRatingImageview.setImageResource(
- if(store.reviewCount > 0)
+ if (store.reviewCount > 0)
R.drawable.ic_rating
else
R.drawable.ic_no_rating
)
binding.storeReviewTextview.text = (
- if(store.reviewCount == 0) itemView.context.getString(R.string.store_no_review)
+ if (store.reviewCount == 0) itemView.context.getString(R.string.store_no_review)
else if (store.reviewCount > 10) itemView.context.getString(R.string.store_many_review)
- else itemView.context.getString(R.string.store_review_count, store.reviewCount.toString())
+ else itemView.context.getString(
+ R.string.store_review_count,
+ store.reviewCount.toString()
+ )
).toString()
- if(!store.isOpen){
+ if (!store.isOpen) {
binding.readyStoreFrameLayout.isVisible = true
- if(store.name.hasJongSungAtLastChar()){
+ if (store.name.hasJongSungAtLastChar()) {
val fullText = itemView.context.getString(R.string.store_eun, store.name)
val spannableString = SpannableString(fullText)
@@ -99,9 +102,37 @@ class StoreRecyclerAdapter : ListAdapter
binding.storeDoesNotOpenTextView.text = spannableString
}
- }
- else{
+ } else {
binding.readyStoreFrameLayout.isInvisible = true
+ if (store.benefitDetails.isEmpty()) {
+ binding.viewFlipper.isInvisible = true
+ } else {
+ binding.viewFlipper.isInvisible = false
+ binding.viewFlipper.removeAllViews()
+ }
+
+ }
+
+ for (text in store.benefitDetails) {
+ val newTextView = TextView(binding.root.context)
+ newTextView.text = text
+ newTextView.setTextColor(
+ ContextCompat.getColor(
+ binding.root.context,
+ R.color.blue_alpha20
+ )
+ )
+ newTextView.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+
+ binding.viewFlipper.addView(newTextView)
+ }
+
+ binding.viewFlipper.post {
+ binding.viewFlipper.startFlipping()
+ binding.viewFlipper.flipInterval = 2500
}
binding.eventImageView.isVisible = store.isEvent
@@ -156,4 +187,4 @@ class StoreRecyclerAdapter : ListAdapter
}
}
}
-}
\ No newline at end of file
+}
diff --git a/koin/src/main/java/in/koreatech/koin/ui/store/viewmodel/StoreDetailViewModel.kt b/koin/src/main/java/in/koreatech/koin/ui/store/viewmodel/StoreDetailViewModel.kt
index d5b4c564a..86555af0f 100644
--- a/koin/src/main/java/in/koreatech/koin/ui/store/viewmodel/StoreDetailViewModel.kt
+++ b/koin/src/main/java/in/koreatech/koin/ui/store/viewmodel/StoreDetailViewModel.kt
@@ -1,6 +1,5 @@
package `in`.koreatech.koin.ui.store.viewmodel
-import android.Manifest
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
@@ -25,6 +24,7 @@ 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
@@ -43,7 +43,10 @@ class StoreDetailViewModel @Inject constructor(
private val getUserInfoUseCase: GetUserInfoUseCase,
private val reviewPromptUscCase: ReviewPromptUscCase,
private val isTokenSavedInDeviceUseCase: IsTokenSavedInDeviceUseCase,
+ private val abTestUseCase: ABTestUseCase,
) : BaseViewModel() {
+
+
val store: LiveData get() = _store
private val _store = MutableLiveData()
val categories: LiveData get() = _categories
@@ -70,6 +73,15 @@ class StoreDetailViewModel @Inject constructor(
private val _tokenState = SingleLiveEvent()
val tokenState: LiveData get() = _tokenState
+ private val _variableName = MutableLiveData()
+ val variableName: LiveData get() = _variableName
+
+ fun postABTestAssign(title: String) = viewModelScope.launchWithLoading {
+ abTestUseCase(title).onSuccess {
+ _variableName.value = it
+ }
+ }
+
fun getStoreWithMenu(storeId: Int) = viewModelScope.launchWithLoading {
getStoreWithMenuUseCase(storeId).also { store ->
_store.value = store
@@ -77,7 +89,7 @@ class StoreDetailViewModel @Inject constructor(
}
}
- fun postReviewPromptNotification(storeId: Int){
+ fun postReviewPromptNotification(storeId: Int) {
viewModelScope.launch {
reviewPromptUscCase(storeId)
.onFailure {
@@ -176,4 +188,4 @@ class StoreDetailViewModel @Inject constructor(
}
}
}
-}
\ No newline at end of file
+}
diff --git a/koin/src/main/res/layout/activity_call_benefit_store_main.xml b/koin/src/main/res/layout/activity_call_benefit_store_main.xml
index ffe37cda3..a64b36414 100644
--- a/koin/src/main/res/layout/activity_call_benefit_store_main.xml
+++ b/koin/src/main/res/layout/activity_call_benefit_store_main.xml
@@ -66,7 +66,8 @@
-
\ No newline at end of file
+
diff --git a/koin/src/main/res/layout/activity_main.xml b/koin/src/main/res/layout/activity_main.xml
index 8fd8281c2..ae0090baa 100644
--- a/koin/src/main/res/layout/activity_main.xml
+++ b/koin/src/main/res/layout/activity_main.xml
@@ -260,14 +260,17 @@
+
+ android:layout_height="match_parent"
+ android:layout_marginVertical="12dp"
+ android:paddingHorizontal="20dp"
+ android:background="@color/white"
+ app:layoutManager="GridLayoutManager"
+ app:spanCount="6"
+ tools:listitem="@layout/store_category_item" />
-
\ No newline at end of file
+
diff --git a/koin/src/main/res/layout/main_card_article.xml b/koin/src/main/res/layout/main_card_article.xml
index 95c0a77be..0440a79d3 100644
--- a/koin/src/main/res/layout/main_card_article.xml
+++ b/koin/src/main/res/layout/main_card_article.xml
@@ -4,14 +4,14 @@
xmlns:tools="http://schemas.android.com/tools">
+ android:layout_height="match_parent"
+ android:layout_marginHorizontal="20dp">
+ android:layout_height="match_parent"
+ android:minHeight="95dp"
+ android:paddingHorizontal="16dp"
+ android:paddingVertical="16dp">
+ android:textStyle="bold" />
+
+
+ app:layout_constraintStart_toEndOf="@id/ic_hot_now"
+ app:layout_constraintTop_toTopOf="@id/ic_hot_now" />
diff --git a/koin/src/main/res/layout/main_card_article_noti.xml b/koin/src/main/res/layout/main_card_article_noti.xml
index 4d69c6d9a..4fb891107 100644
--- a/koin/src/main/res/layout/main_card_article_noti.xml
+++ b/koin/src/main/res/layout/main_card_article_noti.xml
@@ -11,7 +11,7 @@
+ android:paddingVertical="11.5dp">
+ android:textColor="@color/sub_sub600" />
+
+
diff --git a/koin/src/main/res/layout/store_activity_detail.xml b/koin/src/main/res/layout/store_activity_detail.xml
index 9faf455b7..236f80a5a 100644
--- a/koin/src/main/res/layout/store_activity_detail.xml
+++ b/koin/src/main/res/layout/store_activity_detail.xml
@@ -27,7 +27,7 @@
app:leftButtonBackground="@drawable/ic_back_arrow"
app:leftButtonHeight="14dp"
app:leftButtonWidth="14dp"
- app:rightButtonBackground="@drawable/ic_call"
+ app:rightButtonBackground="@drawable/ic_hamburger_button"
app:rightButtonHeight="24dp"
app:rightButtonWidth="24dp"
app:titleText="@string/nearby_store" />
@@ -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"/>
+
+
+
+
-
\ No newline at end of file
+
diff --git a/koin/src/main/res/layout/store_activity_main.xml b/koin/src/main/res/layout/store_activity_main.xml
index a15efd3b0..b12ababc0 100644
--- a/koin/src/main/res/layout/store_activity_main.xml
+++ b/koin/src/main/res/layout/store_activity_main.xml
@@ -190,7 +190,6 @@
app:layoutManager="LinearLayoutManager"
tools:itemCount="4"
tools:listitem="@layout/item_store_search" />
-
+
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@+id/go_to_benefit_activity_button">
-
-
diff --git a/koin/src/main/res/layout/store_list_item.xml b/koin/src/main/res/layout/store_list_item.xml
index 917913e7a..2bb44efea 100644
--- a/koin/src/main/res/layout/store_list_item.xml
+++ b/koin/src/main/res/layout/store_list_item.xml
@@ -1,8 +1,9 @@
-
-
+
+
@@ -16,14 +17,14 @@
android:layout_marginTop="11dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="4dp"
+ android:stateListAnimator="@null"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="8dp"
app:cardElevation="1dp"
- android:stateListAnimator="@null"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="parent">
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ android:layout_height="match_parent">
+ app:layout_constraintTop_toTopOf="parent" />
+
+
+
+
+
+
+
+
+
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/store_name_textview" />
+ app:layout_constraintStart_toEndOf="@id/is_rating_imageview"
+ app:layout_constraintTop_toTopOf="@id/is_rating_imageview" />
+ app:layout_constraintStart_toEndOf="@id/store_rating_score_textview"
+ app:layout_constraintTop_toTopOf="@id/is_rating_imageview" />
+ android:layout_gravity="right"
+ android:visibility="invisible">
+ app:layout_constraintTop_toTopOf="parent" />
diff --git a/koin/src/main/res/values/strings.xml b/koin/src/main/res/values/strings.xml
index 786aef44c..9505d209b 100644
--- a/koin/src/main/res/values/strings.xml
+++ b/koin/src/main/res/values/strings.xml
@@ -557,6 +557,7 @@
공지사항
공지글 검색
키워드 관리
+ 더보기
자유게시판
취업게시판
익명게시판