Compare commits

55 Commits

Author SHA1 Message Date
7c44f3d66c docs(i18n): 일본어 리소스 PRD 완료 기준을 갱신한다 2026-08-11 19:01:50 +09:00
43b6496289 chore(app): 앱 버전을 1.56.1로 올린다 2026-08-11 18:53:30 +09:00
30a4a9ffc7 feat(i18n): 일본어 리소스를 반영한다 2026-08-11 17:58:56 +09:00
84cd9bbf41 docs(i18n): 일본어 리소스 반영 계획을 추가한다 2026-08-11 17:57:50 +09:00
75480e7a71 docs(creator): 커뮤니티 후속 작업 기록을 갱신한다 2026-08-11 00:11:11 +09:00
8b8b384b29 feat(creator): 커뮤니티 구매와 메뉴 동작을 정리한다 2026-08-11 00:10:46 +09:00
02cd583f56 chore(app): 데이터스토어 버전을 올린다 2026-08-04 14:13:01 +09:00
3c21a764ae fix(home): 라이브 전체 버튼을 오른쪽에 고정한다 2026-08-04 11:45:29 +09:00
d20d6aa971 docs(agent): 테스트 범위를 로직으로 제한한다 2026-08-04 11:44:58 +09:00
f50f987282 chore(app): 앱 버전 코드를 올린다 2026-08-03 23:04:16 +09:00
f508d847ea feat(creator): 내 채널 팔로워 목록을 연결한다 2026-08-03 21:39:23 +09:00
e86e5a1c3d chore(app): 앱 버전을 1.56.0으로 올린다 2026-08-03 18:21:42 +09:00
26eae18edc fix(content): 콘텐츠 상세 펼침 말줄임을 복원한다 2026-08-03 16:33:35 +09:00
b5cfdc83e3 fix(live): 전체 라이브 제목을 On Air로 통일한다 2026-08-03 15:07:31 +09:00
dd4a5167a9 fix(home): 라이브 전체 항목을 항상 표시한다 2026-07-31 18:34:23 +09:00
d592038edd fix(home): 홈 추천 후속 검증을 반영한다 2026-07-31 16:29:06 +09:00
a106548f87 fix(content): 콘텐츠 고정 한도 안내를 수정한다 2026-07-31 03:48:35 +09:00
49f6fe592c fix(home): 팔로잉 탭 후속 검증을 반영한다 2026-07-31 03:22:50 +09:00
f600d10e8b feat(home): 최근 활동 LIVE 라우팅을 추가한다 2026-07-30 16:42:33 +09:00
a1aa9c2243 docs(docs): 문서 샘플과 리뷰 규칙을 갱신한다 2026-07-30 14:18:07 +09:00
3a62ed8463 chore(git): worktree 디렉터리를 제외한다 2026-07-22 18:11:14 +09:00
6755c8ae99 feat(main): 메인 탭 당겨서 새로고침을 추가한다 2026-07-22 17:48:33 +09:00
b17e250665 docs(main): 당겨서 새로고침 계획을 추가한다 2026-07-22 00:35:34 +09:00
074c945008 chore(app): 앱 버전을 1.55.1로 올린다 2026-07-20 15:41:48 +09:00
16aa946621 feat(main): 홈 크리에이터 FAB를 추가한다 2026-07-17 00:08:33 +09:00
831533b4f6 refactor(creator): 진행 중 라이브 컴포넌트를 공통화한다 2026-07-16 22:59:07 +09:00
de90f771c1 refactor(v2): 도메인 액션 경로를 통합한다 2026-07-16 16:57:39 +09:00
4373452478 docs(v2): 라이브 액션 검증 기록을 갱신한다 2026-07-15 21:27:53 +09:00
0b3042d29e refactor(onair): 라이브 입장 흐름을 공통화한다 2026-07-15 21:27:44 +09:00
cc23992d2e refactor(home): 추천 라이브 진입을 공통화한다 2026-07-15 21:27:37 +09:00
3a49158243 refactor(creator): 라이브 액션을 공통화한다 2026-07-15 21:27:31 +09:00
d1509d4359 refactor(live): 공통 액션 코디네이터를 추가한다 2026-07-15 21:27:23 +09:00
c223b3b8a4 feat(live): 생성 결과 계약을 추가한다 2026-07-15 21:26:53 +09:00
b582545991 feat(live): 입장 정책 모델을 추가한다 2026-07-15 21:26:37 +09:00
fe79431744 refactor(content): 상세 진입을 공통 액션으로 통합한다 2026-07-14 22:11:10 +09:00
1c5fbb38f1 style(content): 주석 오디오 너비 계산 줄바꿈을 정리한다 2026-07-14 20:48:53 +09:00
dadf3a519d test(home): 팔로잉 empty 문구 검증을 정리한다 2026-07-14 20:45:34 +09:00
ddc949f9d9 refactor(live): 온에어 접근 가드를 공통화한다 2026-07-14 20:45:29 +09:00
6973816b8c refactor(creator): 크리에이터 접근 가드를 공통화한다 2026-07-14 20:45:23 +09:00
e5b99be9c4 refactor(main): 기존 로그인 가드를 제거한다 2026-07-14 20:45:16 +09:00
5ff92980a2 refactor(chat): 채팅 접근 가드를 공통화한다 2026-07-14 20:44:58 +09:00
2fda80d602 refactor(content): 콘텐츠 접근 가드를 공통화한다 2026-07-14 20:44:53 +09:00
7e12aa8720 refactor(home): 홈 접근 가드를 공통화한다 2026-07-14 20:44:42 +09:00
d5d3444c3a refactor(main): 메인 접근 가드를 공통화한다 2026-07-14 20:44:36 +09:00
a6601dcea1 feat(access): 공통 접근 실행기를 추가한다 2026-07-14 20:44:20 +09:00
4d51806cee feat(access): 접근 정책 모델을 추가한다 2026-07-14 20:44:13 +09:00
98db0b657e docs(v2): 공통 접근 가드 계획을 보강한다 2026-07-14 20:44:06 +09:00
9f70bcd3eb docs(v2): 공통 접근 가드 계획을 기록한다 2026-07-14 12:22:58 +09:00
cc8d061b51 docs(content): 상세 오디오 광고 제거 검증을 기록한다 2026-07-13 16:59:20 +09:00
1b952054fb fix(content): 상세 오디오 광고를 제거한다 2026-07-13 16:59:10 +09:00
2c609836fd fix(home): 팔로잉 empty 문구를 중앙 표시한다 2026-07-13 16:17:12 +09:00
da36964347 fix(home): 팔로잉 empty 문구 번역을 갱신한다 2026-07-13 16:17:06 +09:00
47b9c3dec2 docs(home): 팔로잉 empty 문구 계획을 기록한다 2026-07-13 16:17:01 +09:00
17a9a2f601 fix(creator): 채널 진입과 채팅 인증을 보강한다 2026-07-13 14:09:06 +09:00
c43b9cdbd8 docs(creator): 채널 로그인 가드 계획을 기록한다 2026-07-13 14:09:01 +09:00
324 changed files with 21235 additions and 3235 deletions

1
.gitignore vendored
View File

@@ -319,5 +319,6 @@ app/release/
.kiro/ .kiro/
.omo/ .omo/
.antigravitycli/ .antigravitycli/
.worktrees/
# End of https://www.toptal.com/developers/gitignore/api/macos,android,androidstudio,visualstudiocode,git,kotlin,java # End of https://www.toptal.com/developers/gitignore/api/macos,android,androidstudio,visualstudiocode,git,kotlin,java

View File

@@ -114,12 +114,22 @@ Strong success criteria let you loop independently. Weak criteria ("make it work
- 예: `docs/20260601_메인_홈_추천_UI와_API_연동/` - 예: `docs/20260601_메인_홈_추천_UI와_API_연동/`
- 기존에 생성된 `docs/prd/`, `docs/plan-task/` 문서는 유지하고, 신규 생성 문서부터 위 구조를 적용한다. - 기존에 생성된 `docs/prd/`, `docs/plan-task/` 문서는 유지하고, 신규 생성 문서부터 위 구조를 적용한다.
- 날짜는 `YYYYMMDD` 8자리 숫자를 사용한다. - 날짜는 `YYYYMMDD` 8자리 숫자를 사용한다.
- PRD 문서는 `sample-prd.md` 파일에서 작업에 필요한 부분만 발췌해 작성한다. `sample-prd.md`가 없거나 위치가 불명확하면 추측하지 말고 사용자에게 확인한다. - 문서 샘플의 기준 위치는 `docs/sample/`다.
- PRD 문서는 `docs/sample/sample-prd.md`, 구현 계획/TASK 문서는 `docs/sample/sample-plan-task.md`를 참조해 작업에 필요한 항목을 구체화한다.
- 샘플의 `<...>` placeholder와 예시 값은 실제 작업 값으로 교체한다. 필요한 샘플이 없거나 위치가 불명확하면 추측하지 말고 사용자에게 확인한다.
- 연속된 하나의 작업이라면 별도 새 문서를 만들지 말고 기존 PRD와 계획/TASK 문서에 추가 작업으로 이어서 기록한다. - 연속된 하나의 작업이라면 별도 새 문서를 만들지 말고 기존 PRD와 계획/TASK 문서에 추가 작업으로 이어서 기록한다.
- 작업 도중 범위가 변경되면 계획/TASK 문서 체크리스트를 먼저 업데이트한 뒤 구현한다. - 작업 도중 범위가 변경되면 계획/TASK 문서 체크리스트를 먼저 업데이트한 뒤 구현한다.
- 특정 Phase 또는 Task에 직접 대응되는 검증 기록은 해당 Phase 또는 Task 아래에 한국어로 남긴다. - 특정 Phase 또는 Task에 직접 대응되는 검증 기록은 해당 Phase 또는 Task 아래에 한국어로 남긴다.
- 여러 Phase에 걸치거나 문서 전체에 해당하는 통합 검증, 회귀 검증, 최종 수동 확인 기록은 문서 최하단 `Verification Log`에 한국어로 남긴다. - 여러 Phase에 걸치거나 문서 전체에 해당하는 통합 검증, 회귀 검증, 최종 수동 확인 기록은 문서 최하단 `Verification Log`에 한국어로 남긴다.
- 후속 수정이 발생해도 기존 검증 기록은 삭제하거나 덮어쓰지 않고 위치별로 누적한다. - 자동 테스트는 기본적으로 `app/src/test`의 로직/local unit test만 작성하고 실행한다.
- 사용자가 명시적으로 요청하지 않으면 `app/src/androidTest` UI/계측 테스트를 작성하거나 `connectedDebugAndroidTest`, 기기·에뮬레이터 UI 조작, 스크린샷·시각 QA를 실행하지 않는다.
- UI 변경의 테스트 코드는 adapter 분기, mapper, formatter, presentation model, 라우팅처럼 화면 표현을 결정하는 로직만 검증하며 View 크기·간격·constraint·visibility 등 레이아웃 속성을 직접 검증하지 않는다.
- 코드 리뷰, 코드 품질 점검, 완료 Phase 재검토를 수행하면 `docs/sample/sample-review.md`를 참조해 `docs/[날짜]_구현할내용한글/reviews/[리뷰범위]-review.md`에 결과를 기록한다.
- 리뷰 보고서명의 `[리뷰범위]``phase<번호>-<구현 내용을 나타내는 영문 kebab-case>` 형식으로 작성한다. 예: `phase2-main-home-recommendation-review.md`
- 일반 빌드, 테스트, 린트 실행 결과는 리뷰 보고서를 만들지 않고 계획/TASK 문서의 Task별 검증 기록 또는 `Verification Log`에 남긴다.
- 리뷰에서 수정 항목이 확정되면 코드를 수정하기 전에 review ID, 대상 파일, 수정 범위, 회귀 테스트, 완료 증거를 포함한 신규 회귀 수정 Task를 `plan-task.md`의 해당 Phase에 추가하고 그 Task에 따라 바로 수정한다.
- 리뷰 후속 수정 시 기존 완료 Task를 다시 열지 않으며, 기존 완료 체크박스와 검증·리뷰 기록은 삭제하거나 덮어쓰지 않고 위치별로 누적한다.
- 샘플의 위치, 문서 구조, 작성·운용 규칙이 변경되면 같은 작업에서 `AGENTS.md`와 관련 가이드 문서를 함께 갱신한다.
## 상세 참조 문서 ## 상세 참조 문서
- 빌드/린트/테스트는 `docs/agent-guides/build-test-style.md`를 참고한다. - 빌드/린트/테스트는 `docs/agent-guides/build-test-style.md`를 참고한다.

View File

@@ -86,8 +86,8 @@ android {
applicationId "kr.co.vividnext.sodalive" applicationId "kr.co.vividnext.sodalive"
minSdk 23 minSdk 23
targetSdk 35 targetSdk 35
versionCode 242 versionCode 246
versionName "1.55.0" versionName "1.56.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -116,8 +116,6 @@ android {
// release용 ad unit id는 배포 전 실제 값으로 교체한다. // release용 ad unit id는 배포 전 실제 값으로 교체한다.
buildConfigField 'String', 'YANDEX_INLINE_BANNER_LIVE_TAB_AD_UNIT_ID', '"R-M-19140295-3"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_LIVE_TAB_AD_UNIT_ID', '"R-M-19140295-3"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_LIVE_ROOM_DETAIL_AD_UNIT_ID', '"R-M-19140295-4"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_LIVE_ROOM_DETAIL_AD_UNIT_ID', '"R-M-19140295-4"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_AUDIO_CONTENT_DETAIL_AD_UNIT_ID', '"R-M-19140295-5"'
buildConfigField 'String', 'YANDEX_INTERSTITIAL_AUDIO_CONTENT_PLAY_AD_UNIT_ID', '"R-M-19140295-6"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_CREATOR_COMMUNITY_ALL_AD_UNIT_ID', '"R-M-19140295-7"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_CREATOR_COMMUNITY_ALL_AD_UNIT_ID', '"R-M-19140295-7"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_SERIES_MAIN_HOME_AD_UNIT_ID', '"R-M-19140295-8"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_SERIES_MAIN_HOME_AD_UNIT_ID', '"R-M-19140295-8"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_SERIES_MAIN_DAY_OF_WEEK_AD_UNIT_ID', '"R-M-19140295-9"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_SERIES_MAIN_DAY_OF_WEEK_AD_UNIT_ID', '"R-M-19140295-9"'
@@ -160,8 +158,6 @@ android {
buildConfigField 'String', 'YANDEX_INLINE_BANNER_LIVE_TAB_AD_UNIT_ID', '"R-M-19140297-3"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_LIVE_TAB_AD_UNIT_ID', '"R-M-19140297-3"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_LIVE_ROOM_DETAIL_AD_UNIT_ID', '"R-M-19140297-4"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_LIVE_ROOM_DETAIL_AD_UNIT_ID', '"R-M-19140297-4"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_AUDIO_CONTENT_DETAIL_AD_UNIT_ID', '"R-M-19140297-5"'
buildConfigField 'String', 'YANDEX_INTERSTITIAL_AUDIO_CONTENT_PLAY_AD_UNIT_ID', '"R-M-19140297-6"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_CREATOR_COMMUNITY_ALL_AD_UNIT_ID', '"R-M-19140297-7"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_CREATOR_COMMUNITY_ALL_AD_UNIT_ID', '"R-M-19140297-7"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_SERIES_MAIN_HOME_AD_UNIT_ID', '"R-M-19140297-8"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_SERIES_MAIN_HOME_AD_UNIT_ID', '"R-M-19140297-8"'
buildConfigField 'String', 'YANDEX_INLINE_BANNER_SERIES_MAIN_DAY_OF_WEEK_AD_UNIT_ID', '"R-M-19140297-9"' buildConfigField 'String', 'YANDEX_INLINE_BANNER_SERIES_MAIN_DAY_OF_WEEK_AD_UNIT_ID', '"R-M-19140297-9"'
@@ -271,7 +267,7 @@ dependencies {
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel' exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel'
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx' exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
} }
implementation "androidx.datastore:datastore-preferences:1.2.0" implementation "androidx.datastore:datastore-preferences:1.2.1"
// Gson // Gson
implementation "com.google.code.gson:gson:2.13.2" implementation "com.google.code.gson:gson:2.13.2"

View File

@@ -361,7 +361,9 @@ class Agora(
fun deInitRtmClient(rtmEventListener: RtmEventListener) { fun deInitRtmClient(rtmEventListener: RtmEventListener) {
rtmClient?.removeEventListener(rtmEventListener) rtmClient?.removeEventListener(rtmEventListener)
rtmClient?.unsubscribe(roomChannelName, object : ResultCallback<Void> { rtmClient?.unsubscribe(
roomChannelName,
object : ResultCallback<Void> {
override fun onSuccess(responseInfo: Void?) { override fun onSuccess(responseInfo: Void?) {
Logger.e("RTM unsubscribe - $roomChannelName") Logger.e("RTM unsubscribe - $roomChannelName")
roomChannelName = null roomChannelName = null
@@ -371,7 +373,8 @@ class Agora(
Logger.e("RTM unsubscribe fail - ${errorInfo.errorCode}") Logger.e("RTM unsubscribe fail - ${errorInfo.errorCode}")
Logger.e("RTM unsubscribe fail - ${errorInfo.errorReason}") Logger.e("RTM unsubscribe fail - ${errorInfo.errorReason}")
} }
}) }
)
rtmClient?.unsubscribe( rtmClient?.unsubscribe(
"inbox_${SharedPreferenceManager.userId}", "inbox_${SharedPreferenceManager.userId}",
object : ResultCallback<Void> { object : ResultCallback<Void> {
@@ -383,7 +386,8 @@ class Agora(
Logger.e("RTM unsubscribe fail - ${errorInfo.errorCode}") Logger.e("RTM unsubscribe fail - ${errorInfo.errorCode}")
Logger.e("RTM unsubscribe fail - ${errorInfo.errorReason}") Logger.e("RTM unsubscribe fail - ${errorInfo.errorReason}")
} }
}) }
)
rtmClient?.logout(object : ResultCallback<Void> { rtmClient?.logout(object : ResultCallback<Void> {
override fun onSuccess(responseInfo: Void?) { override fun onSuccess(responseInfo: Void?) {
Logger.e("RTM logout") Logger.e("RTM logout")
@@ -398,7 +402,6 @@ class Agora(
// 상태 리셋 // 상태 리셋
rtmLoggedIn = false rtmLoggedIn = false
rtmLoginInProgress = false rtmLoginInProgress = false
} }
// endregion // endregion
} }

View File

@@ -136,7 +136,7 @@ class SodaLiveApp : Application(), DefaultLifecycleObserver {
applicationContext, applicationContext,
BuildConfig.NOTIFLY_PROJECT_ID, BuildConfig.NOTIFLY_PROJECT_ID,
BuildConfig.NOTIFLY_USERNAME, BuildConfig.NOTIFLY_USERNAME,
BuildConfig.NOTIFLY_PASSWORD, BuildConfig.NOTIFLY_PASSWORD
) )
} }

View File

@@ -14,5 +14,5 @@ data class AddAllPlaybackTrackingRequest(
data class PlaybackTrackingData( data class PlaybackTrackingData(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("playDateTime") val playDateTime: String, @SerializedName("playDateTime") val playDateTime: String,
@SerializedName("isPreview") val isPreview: Boolean, @SerializedName("isPreview") val isPreview: Boolean
) )

View File

@@ -127,7 +127,7 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
compositeDisposable.add( compositeDisposable.add(
repository.getCategoryList( repository.getCategoryList(
creatorId = userId, creatorId = userId,
token = "Bearer ${SharedPreferenceManager.token}", token = "Bearer ${SharedPreferenceManager.token}"
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View File

@@ -10,5 +10,5 @@ enum class PurchaseOption {
BUY_ONLY, BUY_ONLY,
@SerializedName("RENT_ONLY") @SerializedName("RENT_ONLY")
RENT_ONLY, RENT_ONLY
} }

View File

@@ -21,7 +21,7 @@ import kr.co.vividnext.sodalive.extensions.moneyFormat
class AudioContentNewAllAdapter( class AudioContentNewAllAdapter(
private val itemWidth: Int, private val itemWidth: Int,
private val onClickItem: (Long) -> Unit, private val onClickItem: (Long) -> Unit,
private val onClickCreator: (Long) -> Unit, private val onClickCreator: (Long) -> Unit
) : RecyclerView.Adapter<AudioContentNewAllAdapter.ViewHolder>() { ) : RecyclerView.Adapter<AudioContentNewAllAdapter.ViewHolder>() {
inner class ViewHolder( inner class ViewHolder(

View File

@@ -35,7 +35,7 @@ class AudioContentCommentReplyAdapter(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,
false false
), )
) )
} else { } else {
AudioContentCommentReplyItemViewHolder( AudioContentCommentReplyItemViewHolder(
@@ -172,9 +172,7 @@ class AudioContentCommentReplyItemViewHolder(
private val context: Context, private val context: Context,
private val creatorId: Long, private val creatorId: Long,
private val binding: ItemAudioContentCommentReplyBinding, private val binding: ItemAudioContentCommentReplyBinding,
private val showOptionMenu: ( private val showOptionMenu: (Context, View, Long, Long, Long, onClickModify: () -> Unit) -> Unit,
Context, View, Long, Long, Long, onClickModify: () -> Unit
) -> Unit,
private val modifyComment: (Long, String) -> Unit private val modifyComment: (Long, String) -> Unit
) : AudioContentCommentReplyViewHolder(binding) { ) : AudioContentCommentReplyViewHolder(binding) {

View File

@@ -1,3 +1,5 @@
@file:Suppress("ktlint:package-name", "ktlint:standard:package-name")
package kr.co.vividnext.sodalive.audio_content.detail package kr.co.vividnext.sodalive.audio_content.detail
import android.annotation.SuppressLint import android.annotation.SuppressLint
@@ -10,6 +12,7 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
@@ -28,18 +31,6 @@ import coil.transform.RoundedCornersTransformation
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.google.gson.Gson import com.google.gson.Gson
import com.orhanobut.logger.Logger
import com.yandex.mobile.ads.banner.BannerAdSize
import com.yandex.mobile.ads.common.AdError
import com.yandex.mobile.ads.common.AdRequest
import com.yandex.mobile.ads.common.AdRequestConfiguration
import com.yandex.mobile.ads.common.AdRequestError
import com.yandex.mobile.ads.common.ImpressionData
import com.yandex.mobile.ads.interstitial.InterstitialAd
import com.yandex.mobile.ads.interstitial.InterstitialAdEventListener
import com.yandex.mobile.ads.interstitial.InterstitialAdLoadListener
import com.yandex.mobile.ads.interstitial.InterstitialAdLoader
import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.audio_content.PurchaseOption import kr.co.vividnext.sodalive.audio_content.PurchaseOption
@@ -72,7 +63,6 @@ import kr.co.vividnext.sodalive.report.ReportType
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.roundToInt
@UnstableApi @UnstableApi
class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBinding>( class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBinding>(
@@ -101,42 +91,9 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
private lateinit var orderType: OrderType private lateinit var orderType: OrderType
private lateinit var imm: InputMethodManager private lateinit var imm: InputMethodManager
private var audioContentPlayInterstitialAdLoader: InterstitialAdLoader? = null
private var audioContentPlayInterstitialAd: InterstitialAd? = null
private var pendingAudioContentPlayAction: (() -> Unit)? = null
private var hasConsumedAudioContentPlayInterstitialAttempt = false
private var isAudioContentPlaying = false private var isAudioContentPlaying = false
private var isAudioContentInterstitialEligible = false
private var audioContentStartPlaybackAction: (() -> Unit)? = null private var audioContentStartPlaybackAction: (() -> Unit)? = null
private val audioContentPlayInterstitialAdLoadListener = object : InterstitialAdLoadListener {
override fun onAdLoaded(interstitialAd: InterstitialAd) {
clearAudioContentPlayInterstitialAd()
audioContentPlayInterstitialAd = interstitialAd
}
override fun onAdFailedToLoad(error: AdRequestError) {
Logger.e("Audio content interstitial failed to load: ${error.description}")
}
}
private val audioContentPlayInterstitialAdEventListener = object : InterstitialAdEventListener {
override fun onAdShown() = Unit
override fun onAdFailedToShow(adError: AdError) {
Logger.e("Audio content interstitial failed to show: ${adError.description}")
continuePendingAudioContentPlayAction()
}
override fun onAdDismissed() {
continuePendingAudioContentPlayAction()
}
override fun onAdClicked() = Unit
override fun onAdImpression(impressionData: ImpressionData?) = Unit
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
@@ -154,9 +111,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.rlPreviewAlert.visibility = View.GONE binding.rlPreviewAlert.visibility = View.GONE
audioContentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0) audioContentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
hasConsumedAudioContentPlayInterstitialAttempt = false
releaseAudioContentPlayInterstitial()
setupAudioContentPlayInterstitial()
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() } viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
} }
@@ -367,91 +321,9 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
dialog.show(screenWidth - 26.7f.dpToPx().toInt()) dialog.show(screenWidth - 26.7f.dpToPx().toInt())
} }
setupAudioContentDetailInlineBanner()
setupAudioContentPlayInterstitial()
setupBuyerList() setupBuyerList()
} }
private fun setupAudioContentDetailInlineBanner() {
binding.yandexInlineBannerView.post {
val density = resources.displayMetrics.density
val adWidthPixels = binding.yandexInlineBannerView.width.takeIf { it > 0 } ?: screenWidth
val adWidthDp = (adWidthPixels / density).roundToInt()
val maxAdHeightDp = 90
binding.yandexInlineBannerView.apply {
setAdUnitId(BuildConfig.YANDEX_INLINE_BANNER_AUDIO_CONTENT_DETAIL_AD_UNIT_ID)
setAdSize(
BannerAdSize.inlineSize(
this@AudioContentDetailActivity,
adWidthDp,
maxAdHeightDp
)
)
loadAd(AdRequest.Builder().build())
}
}
}
private fun setupAudioContentPlayInterstitial() {
val adUnitId = BuildConfig.YANDEX_INTERSTITIAL_AUDIO_CONTENT_PLAY_AD_UNIT_ID
if (adUnitId.isBlank()) {
Logger.e("Audio content interstitial blocked: ad unit id is blank.")
return
}
audioContentPlayInterstitialAdLoader = InterstitialAdLoader(this).apply {
setAdLoadListener(audioContentPlayInterstitialAdLoadListener)
}
audioContentPlayInterstitialAdLoader?.loadAd(
AdRequestConfiguration.Builder(adUnitId).build()
)
}
private fun playAudioContentWithInterstitialIfAvailable(playAction: () -> Unit) {
if (hasConsumedAudioContentPlayInterstitialAttempt || isFinishing || isDestroyed) {
playAction()
return
}
val interstitialAd = audioContentPlayInterstitialAd ?: run {
playAction()
return
}
hasConsumedAudioContentPlayInterstitialAttempt = true
pendingAudioContentPlayAction = playAction
interstitialAd.setAdEventListener(audioContentPlayInterstitialAdEventListener)
runCatching {
interstitialAd.show(this)
}.onFailure {
Logger.e("Audio content interstitial failed to show: ${it.message}")
continuePendingAudioContentPlayAction()
}
}
private fun continuePendingAudioContentPlayAction() {
val playAction = pendingAudioContentPlayAction
pendingAudioContentPlayAction = null
clearAudioContentPlayInterstitialAd()
if (isFinishing || isDestroyed) {
return
}
playAction?.invoke()
}
private fun clearAudioContentPlayInterstitialAd() {
audioContentPlayInterstitialAd?.setAdEventListener(null)
audioContentPlayInterstitialAd = null
}
private fun releaseAudioContentPlayInterstitial() {
audioContentPlayInterstitialAdLoader?.setAdLoadListener(null)
audioContentPlayInterstitialAdLoader = null
pendingAudioContentPlayAction = null
clearAudioContentPlayInterstitialAd()
}
private fun pauseAudioContentPlayback() { private fun pauseAudioContentPlayback() {
startService( startService(
Intent(this, AudioContentPlayService::class.java).apply { Intent(this, AudioContentPlayService::class.java).apply {
@@ -474,13 +346,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.ivPlayOrPause.setImageResource(R.drawable.btn_audio_content_play) binding.ivPlayOrPause.setImageResource(R.drawable.btn_audio_content_play)
val startPlaybackClickListener = View.OnClickListener { val startPlaybackClickListener = View.OnClickListener { startPlaybackAction() }
if (isAudioContentInterstitialEligible) {
playAudioContentWithInterstitialIfAvailable(startPlaybackAction)
} else {
startPlaybackAction()
}
}
binding.ivPlayOrPause.setOnClickListener(startPlaybackClickListener) binding.ivPlayOrPause.setOnClickListener(startPlaybackClickListener)
binding.llPreview.setOnClickListener(startPlaybackClickListener) binding.llPreview.setOnClickListener(startPlaybackClickListener)
@@ -600,6 +466,11 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
viewModel.isExpandDetail.observe(this) { viewModel.isExpandDetail.observe(this) {
binding.tvDetail.ellipsize = if (it) {
null
} else {
TextUtils.TruncateAt.END
}
binding.tvDetail.maxLines = if (it) { binding.tvDetail.maxLines = if (it) {
Int.MAX_VALUE Int.MAX_VALUE
} else { } else {
@@ -944,12 +815,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
response.duration response.duration
) )
audioContentStartPlaybackAction = null audioContentStartPlaybackAction = null
isAudioContentInterstitialEligible = false
isAlertPreview = response.creator.creatorId != SharedPreferenceManager.userId && isAlertPreview = response.creator.creatorId != SharedPreferenceManager.userId &&
!response.existOrdered && !response.existOrdered &&
response.price > 0 response.price > 0
isAudioContentInterstitialEligible = response.price <= 0 || isAlertPreview
if ( if (
response.creator.creatorId != SharedPreferenceManager.userId && !response.existOrdered && response.creator.creatorId != SharedPreferenceManager.userId && !response.existOrdered &&
@@ -1046,7 +915,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
} else if (response.releaseDate == null) { } else if (response.releaseDate == null) {
audioContentStartPlaybackAction = null audioContentStartPlaybackAction = null
isAudioContentInterstitialEligible = false
binding.llPreviewNo.visibility = View.VISIBLE binding.llPreviewNo.visibility = View.VISIBLE
} }
@@ -1366,8 +1234,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
override fun onDestroy() { override fun onDestroy() {
binding.yandexInlineBannerView.destroy()
releaseAudioContentPlayInterstitial()
super.onDestroy() super.onDestroy()
} }

View File

@@ -51,7 +51,7 @@ data class GetAudioContentDetailResponse(
data class OtherContentResponse( data class OtherContentResponse(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("coverUrl") val coverUrl: String, @SerializedName("coverUrl") val coverUrl: String
) )
@Keep @Keep

View File

@@ -25,10 +25,15 @@ class AudioContentMainNewContentThemeAdapter(
fun bind(theme: String) { fun bind(theme: String) {
if ( if (
theme == selectedTheme || theme == selectedTheme ||
(selectedTheme == "" && theme == SodaLiveApplicationHolder.get() (
selectedTheme == "" && theme == SodaLiveApplicationHolder.get()
.getString(R.string.audio_content_label_all)) || .getString(R.string.audio_content_label_all)) ||
(selectedTheme == "" && theme == SodaLiveApplicationHolder.get() (
.getString(R.string.screen_home_sort_revenue)) selectedTheme == "" && theme == SodaLiveApplicationHolder.get()
.getString(
R.string.screen_home_sort_revenue
)
)
) { ) {
binding.tvTheme.setBackgroundResource( binding.tvTheme.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_3bb9f1

View File

@@ -76,7 +76,8 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
excludeGif = true, excludeGif = true,
isEnabledFreeStyleCrop = true, isEnabledFreeStyleCrop = true,
config = ImagePickerCropper.Config( config = ImagePickerCropper.Config(
aspectX = 1f, aspectY = 1f, aspectX = 1f,
aspectY = 1f,
compressFormat = Bitmap.CompressFormat.JPEG, compressFormat = Bitmap.CompressFormat.JPEG,
compressQuality = 90 compressQuality = 90
), ),

View File

@@ -28,7 +28,7 @@ class AudioContentOrderConfirmDialog(
orderType: OrderType, orderType: OrderType,
price: Int, price: Int,
isAvailableUsePoint: Boolean, isAvailableUsePoint: Boolean,
confirmButtonClick: () -> Unit, confirmButtonClick: () -> Unit
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog

View File

@@ -1,20 +1,9 @@
package kr.co.vividnext.sodalive.audio_content.order package kr.co.vividnext.sodalive.audio_content.order
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentOrderListBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentOrderListBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class AudioContentOrderListActivity : BaseActivity<ActivityAudioContentOrderListBinding>( class AudioContentOrderListActivity : BaseActivity<ActivityAudioContentOrderListBinding>(
ActivityAudioContentOrderListBinding::inflate ActivityAudioContentOrderListBinding::inflate

View File

@@ -20,5 +20,5 @@ data class GetAudioContentOrderListItem(
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("orderType") val orderType: OrderType, @SerializedName("orderType") val orderType: OrderType,
@SerializedName("likeCount") val likeCount: Int, @SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentCount") val commentCount: Int, @SerializedName("commentCount") val commentCount: Int
) )

View File

@@ -429,7 +429,6 @@ class AudioContentPlayerFragment(
mediaController?.seekTo(progress.toLong()) mediaController?.seekTo(progress.toLong())
} }
} }
}) })
updateMediaMetadata(mediaController?.currentMediaItem?.mediaMetadata) updateMediaMetadata(mediaController?.currentMediaItem?.mediaMetadata)

View File

@@ -14,8 +14,11 @@ import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
class SeriesListAllViewModel(private val repository: SeriesRepository) : BaseViewModel() { class SeriesListAllViewModel(private val repository: SeriesRepository) : BaseViewModel() {
enum class SeriesSortType { enum class SeriesSortType {
@SerializedName("NEWEST") NEWEST, @SerializedName("NEWEST")
@SerializedName("OLDEST") OLDEST NEWEST,
@SerializedName("OLDEST")
OLDEST
} }
private val _toastLiveData = MutableLiveData<String?>() private val _toastLiveData = MutableLiveData<String?>()

View File

@@ -1,6 +1,5 @@
package kr.co.vividnext.sodalive.audio_content.series.detail package kr.co.vividnext.sodalive.audio_content.series.detail
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle

View File

@@ -240,7 +240,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
title = getString(R.string.audio_content_upload_dialog_title), title = getString(R.string.audio_content_upload_dialog_title),
desc = getString(R.string.audio_content_upload_dialog_desc), desc = getString(R.string.audio_content_upload_dialog_desc),
confirmButtonTitle = getString(R.string.confirm), confirmButtonTitle = getString(R.string.confirm),
confirmButtonClick = { finish() }, confirmButtonClick = { finish() }
).show(screenWidth) ).show(screenWidth)
} }
} }

View File

@@ -20,5 +20,5 @@ data class CreateAudioContentRequest(
@SerializedName("isPointAvailable") val isPointAvailable: Boolean, @SerializedName("isPointAvailable") val isPointAvailable: Boolean,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean, @SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@SerializedName("previewStartTime") val previewStartTime: String? = null, @SerializedName("previewStartTime") val previewStartTime: String? = null,
@SerializedName("previewEndTime") val previewEndTime: String? = null, @SerializedName("previewEndTime") val previewEndTime: String? = null
) )

View File

@@ -80,13 +80,7 @@ class AuditionApplicantListAdapter(
binding.sbProgress.max = currentTotalDuration binding.sbProgress.max = currentTotalDuration
binding.sbProgress.progress = currentTime binding.sbProgress.progress = currentTime
binding.tvTotalDuration.text = binding.tvTotalDuration.text = "/${Utils.convertDurationToString(currentTotalDuration, showHours = false)}"
"/${
Utils.convertDurationToString(
currentTotalDuration,
showHours = false
)
}"
binding.tvCurrentTime.text = Utils.convertDurationToString( binding.tvCurrentTime.text = Utils.convertDurationToString(
currentTime, currentTime,
showHours = false showHours = false

View File

@@ -17,7 +17,6 @@ import kr.co.vividnext.sodalive.audition.role.AuditionRoleDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.ToastMessage
import kr.co.vividnext.sodalive.databinding.ActivityAuditionDetailBinding import kr.co.vividnext.sodalive.databinding.ActivityAuditionDetailBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject

View File

@@ -43,7 +43,8 @@ class CharacterAdapter(
translationY = fm.descent translationY = fm.descent
return true return true
} }
}) }
)
} }
} else { } else {
binding.tvRanking.visibility = View.GONE binding.tvRanking.visibility = View.GONE

View File

@@ -17,7 +17,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseFragment import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.UiText
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentCharacterCommentListBinding import kr.co.vividnext.sodalive.databinding.FragmentCharacterCommentListBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
@@ -151,8 +150,7 @@ class CharacterCommentListFragment : BaseFragment<FragmentCharacterCommentListBi
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
val lastVisible = (recyclerView.layoutManager as LinearLayoutManager) val lastVisible = (recyclerView.layoutManager as LinearLayoutManager).findLastCompletelyVisibleItemPosition()
.findLastCompletelyVisibleItemPosition()
val total = recyclerView.adapter?.itemCount ?: 0 val total = recyclerView.adapter?.itemCount ?: 0
if (!recyclerView.canScrollVertically(1) && lastVisible == total - 1) { if (!recyclerView.canScrollVertically(1) && lastVisible == total - 1) {
viewModel.getCommentList(characterId) viewModel.getCommentList(characterId)
@@ -195,7 +193,6 @@ class CharacterCommentListFragment : BaseFragment<FragmentCharacterCommentListBi
imm.hideSoftInputFromWindow(view?.windowToken, 0) imm.hideSoftInputFromWindow(view?.windowToken, 0)
} }
companion object { companion object {
private const val EXTRA_CHARACTER_ID = "extra_character_id" private const val EXTRA_CHARACTER_ID = "extra_character_id"
fun newInstance(characterId: Long): CharacterCommentListFragment { fun newInstance(characterId: Long): CharacterCommentListFragment {

View File

@@ -15,7 +15,6 @@ import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseFragment import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.UiText
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentCharacterCommentReplyBinding import kr.co.vividnext.sodalive.databinding.FragmentCharacterCommentReplyBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
@@ -48,7 +47,10 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
characterId = arguments?.getLong(EXTRA_CHARACTER_ID) ?: 0 characterId = arguments?.getLong(EXTRA_CHARACTER_ID) ?: 0
original = arguments?.let { original = arguments?.let {
val cid = it.getLong(EXTRA_ORIGINAL_COMMENT_ID, -1) val cid = it.getLong(EXTRA_ORIGINAL_COMMENT_ID, -1)
if (cid == -1L) null else CharacterCommentResponse( if (cid == -1L) {
null
} else {
CharacterCommentResponse(
commentId = cid, commentId = cid,
memberId = it.getLong(EXTRA_ORIGINAL_MEMBER_ID), memberId = it.getLong(EXTRA_ORIGINAL_MEMBER_ID),
memberProfileImage = it.getString(EXTRA_ORIGINAL_MEMBER_PROFILE_IMAGE) ?: "", memberProfileImage = it.getString(EXTRA_ORIGINAL_MEMBER_PROFILE_IMAGE) ?: "",
@@ -58,6 +60,7 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
comment = it.getString(EXTRA_ORIGINAL_COMMENT_TEXT) ?: "" comment = it.getString(EXTRA_ORIGINAL_COMMENT_TEXT) ?: ""
) )
} }
}
return super.onCreateView(inflater, container, savedInstanceState) return super.onCreateView(inflater, container, savedInstanceState)
} }
@@ -140,7 +143,7 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
outRect.right = 24f.dpToPx().toInt() outRect.right = 24f.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) { when (parent.getChildAdapterPosition(view)) {
0 -> { 0 -> {
outRect.top = 24f.dpToPx().toInt(); outRect.top = 24f.dpToPx().toInt()
outRect.bottom = 12f.dpToPx().toInt() outRect.bottom = 12f.dpToPx().toInt()
} }
@@ -196,7 +199,6 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
} }
} }
companion object { companion object {
private const val EXTRA_CHARACTER_ID = "extra_character_id" private const val EXTRA_CHARACTER_ID = "extra_character_id"
private const val EXTRA_ORIGINAL_COMMENT_ID = "extra_original_comment_id" private const val EXTRA_ORIGINAL_COMMENT_ID = "extra_original_comment_id"

View File

@@ -1,6 +1,5 @@
package kr.co.vividnext.sodalive.chat.character.comment package kr.co.vividnext.sodalive.chat.character.comment
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup

View File

@@ -434,5 +434,4 @@ class CharacterDetailFragment : BaseFragment<FragmentCharacterDetailBinding>(
binding.tvPersonalityContent.maxLines = 3 binding.tvPersonalityContent.maxLines = 3
binding.tvPersonalityContent.ellipsize = TextUtils.TruncateAt.END binding.tvPersonalityContent.ellipsize = TextUtils.TruncateAt.END
} }
} }

View File

@@ -29,6 +29,7 @@ data class CharacterDetailResponse(
enum class CharacterType { enum class CharacterType {
@SerializedName("Clone") @SerializedName("Clone")
CLONE, CLONE,
@SerializedName("Character") @SerializedName("Character")
CHARACTER CHARACTER
} }

View File

@@ -49,7 +49,6 @@ class OtherCharacterAdapter(
transformations(RoundedCornersTransformation(16f.dpToPx())) transformations(RoundedCornersTransformation(16f.dpToPx()))
} }
binding.root.setOnClickListener { binding.root.setOnClickListener {
onItemClick?.invoke(item) onItemClick?.invoke(item)
} }

View File

@@ -118,9 +118,7 @@ class CharacterGalleryViewModel(
val token = "Bearer ${SharedPreferenceManager.token}" val token = "Bearer ${SharedPreferenceManager.token}"
isPurchasing = true isPurchasing = true
_uiState.value = _uiState.value?.copy( _uiState.value = _uiState.value?.copy(isLoading = isRequesting || isPurchasing)
isLoading = isRequesting || isPurchasing,
)
compositeDisposable.add( compositeDisposable.add(
repository.purchaseCharacterImage(token = token, imageId = imageId) repository.purchaseCharacterImage(token = token, imageId = imageId)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@@ -85,7 +85,6 @@ class OriginalWorkDetailActivity : BaseActivity<ActivityOriginalWorkDetailBindin
override fun onTabReselected(tab: TabLayout.Tab) { override fun onTabReselected(tab: TabLayout.Tab) {
} }
}) })
} }

View File

@@ -99,7 +99,6 @@ class ChatBackgroundPickerDialogFragment : DialogFragment() {
items.addAll(list.map { BgItem(id = it.id, url = it.imageUrl) }) items.addAll(list.map { BgItem(id = it.id, url = it.imageUrl) })
adapter.submit(items, selectedId) adapter.submit(items, selectedId)
loadingDialog.dismiss() loadingDialog.dismiss()
}, { _ -> }, { _ ->
// 실패 시에도 현재까지의 목록 표시(없으면 빈 목록) // 실패 시에도 현재까지의 목록 표시(없으면 빈 목록)
@@ -161,9 +160,7 @@ class ChatBackgroundPickerDialogFragment : DialogFragment() {
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BgVH { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BgVH {
val binding = ItemChatBackgroundImageBinding.inflate( val binding = ItemChatBackgroundImageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
LayoutInflater.from(parent.context), parent, false
)
return BgVH(binding, onClick) return BgVH(binding, onClick)
} }

View File

@@ -12,8 +12,10 @@ import androidx.annotation.Keep
enum class MessageStatus { enum class MessageStatus {
/** 전송 중 */ /** 전송 중 */
SENDING, SENDING,
/** 전송 완료 */ /** 전송 완료 */
SENT, SENT,
/** 전송 실패 */ /** 전송 실패 */
FAILED FAILED
} }

View File

@@ -4,7 +4,11 @@
package kr.co.vividnext.sodalive.chat.talk.room.db package kr.co.vividnext.sodalive.chat.talk.room.db
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.room.* import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
@Keep @Keep
@Dao @Dao

View File

@@ -45,15 +45,21 @@ class ImagePickerCropper(
// 13+ : 시스템 Photo Picker // 13+ : 시스템 Photo Picker
private val pickPhoto: ActivityResultLauncher<PickVisualMediaRequest> = private val pickPhoto: ActivityResultLauncher<PickVisualMediaRequest> =
caller.registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> caller.registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri == null) onError(CancellationException("이미지 선택을 취소했습니다.")) if (uri == null) {
else handlePickedUri(uri) onError(CancellationException("이미지 선택을 취소했습니다."))
} else {
handlePickedUri(uri)
}
} }
// 12- : SAF GetContent // 12- : SAF GetContent
private val pickContent: ActivityResultLauncher<String> = private val pickContent: ActivityResultLauncher<String> =
caller.registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> caller.registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri == null) onError(CancellationException("이미지 선택을 취소했습니다.")) if (uri == null) {
else handlePickedUri(uri) onError(CancellationException("이미지 선택을 취소했습니다."))
} else {
handlePickedUri(uri)
}
} }
// uCrop 결과 수신 // uCrop 결과 수신
@@ -105,7 +111,9 @@ class ImagePickerCropper(
val gifFile = copyUriToCacheAsGif(source) val gifFile = copyUriToCacheAsGif(source)
lastCroppedFile = gifFile lastCroppedFile = gifFile
val fileUri = FileProvider.getUriForFile( val fileUri = FileProvider.getUriForFile(
context, "${BuildConfig.APPLICATION_ID}.fileprovider", gifFile context,
"${BuildConfig.APPLICATION_ID}.fileprovider",
gifFile
) )
// 2) 바로 반환 (크롭 생략) // 2) 바로 반환 (크롭 생략)
@@ -137,13 +145,20 @@ class ImagePickerCropper(
) )
?.use { c -> ?.use { c ->
val idx = c.getColumnIndex(OpenableColumns.DISPLAY_NAME) val idx = c.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (idx >= 0 && c.moveToFirst()) c.getString(idx) else null if (idx >= 0 && c.moveToFirst()) {
c.getString(idx)
} else {
null
}
} }
} }
private fun copyUriToCacheAsGif(uri: Uri): File { private fun copyUriToCacheAsGif(uri: Uri): File {
val base = if (config.useExternalCache) context.externalCacheDir ?: context.cacheDir val base = if (config.useExternalCache) {
else context.cacheDir context.externalCacheDir ?: context.cacheDir
} else {
context.cacheDir
}
// 원본 이름 유지 시도, 실패하면 타임스탬프 // 원본 이름 유지 시도, 실패하면 타임스탬프
val name = getDisplayName(uri)?.takeIf { it.endsWith(".gif", true) } val name = getDisplayName(uri)?.takeIf { it.endsWith(".gif", true) }
@@ -201,8 +216,11 @@ class ImagePickerCropper(
} }
private fun createTempCropFile(): File { private fun createTempCropFile(): File {
val base = if (config.useExternalCache) context.externalCacheDir ?: context.cacheDir val base = if (config.useExternalCache) {
else context.cacheDir context.externalCacheDir ?: context.cacheDir
} else {
context.cacheDir
}
val ext = when (config.compressFormat) { val ext = when (config.compressFormat) {
Bitmap.CompressFormat.PNG -> "png" Bitmap.CompressFormat.PNG -> "png"

View File

@@ -89,14 +89,17 @@ object RealPathUtil {
} // MediaProvider } // MediaProvider
// DownloadsProvider // DownloadsProvider
} else if ("content".equals(uri.scheme!!, ignoreCase = true)) { } else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
// Return the remote address // Return the remote address
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn( return if (isGooglePhotosUri(uri)) {
uri.lastPathSegment
} else {
getDataColumn(
context, context,
uri, uri,
null, null,
null null
) )
}
} else if ("file".equals(uri.scheme!!, ignoreCase = true)) { } else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
return uri.path return uri.path
} // File } // File
@@ -122,7 +125,6 @@ object RealPathUtil {
selection: String?, selection: String?,
selectionArgs: Array<String>? selectionArgs: Array<String>?
): String? { ): String? {
var cursor: Cursor? = null var cursor: Cursor? = null
val column = "_data" val column = "_data"
val projection = arrayOf(column) val projection = arrayOf(column)

View File

@@ -1,10 +1,7 @@
package kr.co.vividnext.sodalive.common package kr.co.vividnext.sodalive.common
import android.content.Context
import android.os.Build
import java.net.URLEncoder import java.net.URLEncoder
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import kr.co.vividnext.sodalive.settings.language.LanguageManager
object Utils { object Utils {
fun convertDurationToString(duration: Int, showHours: Boolean = true): String { fun convertDurationToString(duration: Int, showHours: Boolean = true): String {

View File

@@ -18,7 +18,7 @@ open class LiveDialog(
confirmButtonTitle: String, confirmButtonTitle: String,
confirmButtonClick: () -> Unit, confirmButtonClick: () -> Unit,
cancelButtonTitle: String = "", cancelButtonTitle: String = "",
cancelButtonClick: (() -> Unit)? = null, cancelButtonClick: (() -> Unit)? = null
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog

View File

@@ -11,7 +11,7 @@ import kr.co.vividnext.sodalive.R
class CreatorFollowNotifyFragment( class CreatorFollowNotifyFragment(
private val onClickNotifyAll: () -> Unit, private val onClickNotifyAll: () -> Unit,
private val onClickNotifyNone: () -> Unit, private val onClickNotifyNone: () -> Unit,
private val onClickUnFollow: () -> Unit, private val onClickUnFollow: () -> Unit
) : BottomSheetDialogFragment() { ) : BottomSheetDialogFragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,

View File

@@ -37,12 +37,12 @@ import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllActivity import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllActivity
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
import kr.co.vividnext.sodalive.common.image.BlurTransformation
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.image.BlurTransformation
import kr.co.vividnext.sodalive.databinding.ActivityUserProfileBinding import kr.co.vividnext.sodalive.databinding.ActivityUserProfileBinding
import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityBinding import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityBinding
import kr.co.vividnext.sodalive.dialog.MemberProfileDialog import kr.co.vividnext.sodalive.dialog.MemberProfileDialog
@@ -138,7 +138,7 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
binding.ivMenu.setOnClickListener { binding.ivMenu.setOnClickListener {
showOptionMenu( showOptionMenu(
this, this,
binding.ivMenu, binding.ivMenu
) )
} }
@@ -468,7 +468,7 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
viewModel.modifyCheers( viewModel.modifyCheers(
cheersId = cheersId, cheersId = cheersId,
creatorId = userId, creatorId = userId,
cheersContent = content, cheersContent = content
) )
} }
}, },

View File

@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
data class PutModifyCheersRequest( data class PutModifyCheersRequest(
@SerializedName("cheersId") val cheersId: Long, @SerializedName("cheersId") val cheersId: Long,
@SerializedName("content") var content: String? = null, @SerializedName("content") var content: String? = null,
@SerializedName("isActive") var isActive: Boolean? = null, @SerializedName("isActive") var isActive: Boolean? = null
) )

View File

@@ -149,7 +149,6 @@ class CreatorCommunityAllViewModel(
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {}) .subscribe({}, {})
) )
} }
fun registerComment( fun registerComment(
@@ -282,7 +281,6 @@ class CreatorCommunityAllViewModel(
) )
) )
} }
} }
fun updateCommunityPostFixed(postId: Long, isFixed: Boolean) { fun updateCommunityPostFixed(postId: Long, isFixed: Boolean) {

View File

@@ -39,7 +39,8 @@ class CreatorCommunityReportDialog(
alertDialog.dismiss() alertDialog.dismiss()
confirmButtonClick(reason) confirmButtonClick(reason)
} else { } else {
Toast.makeText(activity, Toast.makeText(
activity,
SodaLiveApplicationHolder.get() SodaLiveApplicationHolder.get()
.getString(R.string.screen_audio_content_detail_report_reason_required), .getString(R.string.screen_audio_content_detail_report_reason_required),
Toast.LENGTH_LONG Toast.LENGTH_LONG

View File

@@ -16,7 +16,7 @@ class PurchaseCommunityPostDialog(
activity: Activity, activity: Activity,
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater,
can: Int, can: Int,
confirmButtonClick: () -> Unit, confirmButtonClick: () -> Unit
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog

View File

@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
data class PurchasePostRequest( data class PurchasePostRequest(
@SerializedName("postId") val postId: Long, @SerializedName("postId") val postId: Long,
@SerializedName("timezone") val timezone: String, @SerializedName("timezone") val timezone: String,
@SerializedName("container") val container: String = "aos", @SerializedName("container") val container: String = "aos"
) )

View File

@@ -127,9 +127,7 @@ class CreatorCommunityCommentReplyItemViewHolder(
private val context: Context, private val context: Context,
private val creatorId: Long, private val creatorId: Long,
private val binding: ItemCommunityPostCommentReplyBinding, private val binding: ItemCommunityPostCommentReplyBinding,
private val showOptionMenu: ( private val showOptionMenu: (Context, View, Long, Long, Long, onClickModify: () -> Unit) -> Unit,
Context, View, Long, Long, Long, onClickModify: () -> Unit
) -> Unit,
private val modifyComment: (Long, String) -> Unit, private val modifyComment: (Long, String) -> Unit,
private val onClickProfile: (Long) -> Unit private val onClickProfile: (Long) -> Unit
) : CreatorCommunityCommentReplyViewHolder(binding) { ) : CreatorCommunityCommentReplyViewHolder(binding) {
@@ -178,5 +176,4 @@ class CreatorCommunityCommentReplyItemViewHolder(
binding.ivMenu.visibility = View.GONE binding.ivMenu.visibility = View.GONE
} }
} }
} }

View File

@@ -1,7 +1,6 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.modify package kr.co.vividnext.sodalive.explorer.profile.creator_community.modify
import android.Manifest import android.Manifest
import android.annotation.SuppressLint
import android.graphics.Bitmap import android.graphics.Bitmap
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -76,7 +75,8 @@ class CreatorCommunityModifyActivity : BaseActivity<ActivityCreatorCommunityModi
context = this, context = this,
isEnabledFreeStyleCrop = true, isEnabledFreeStyleCrop = true,
config = ImagePickerCropper.Config( config = ImagePickerCropper.Config(
aspectX = 1f, aspectY = 1f, aspectX = 1f,
aspectY = 1f,
compressFormat = Bitmap.CompressFormat.JPEG, compressFormat = Bitmap.CompressFormat.JPEG,
compressQuality = 90 compressQuality = 90
), ),

View File

@@ -7,6 +7,7 @@ import com.google.gson.annotations.SerializedName
enum class DonationRankingPeriod { enum class DonationRankingPeriod {
@SerializedName("WEEKLY") @SerializedName("WEEKLY")
WEEKLY, WEEKLY,
@SerializedName("CUMULATIVE") @SerializedName("CUMULATIVE")
CUMULATIVE CUMULATIVE
} }

View File

@@ -19,5 +19,5 @@ data class GetDonationAllResponse(
@SerializedName("donationRankingPeriod") @SerializedName("donationRankingPeriod")
val donationRankingPeriod: DonationRankingPeriod? = null, val donationRankingPeriod: DonationRankingPeriod? = null,
@SerializedName("userDonationRanking") @SerializedName("userDonationRanking")
val userDonationRanking: List<UserDonationRankingResponse>, val userDonationRanking: List<UserDonationRankingResponse>
) )

View File

@@ -103,7 +103,7 @@ class UserProfileFantalkAllViewActivity : BaseActivity<ActivityUserProfileFantal
viewModel.modifyCheers( viewModel.modifyCheers(
cheersId = cheersId, cheersId = cheersId,
creatorId = userId, creatorId = userId,
cheersContent = content, cheersContent = content
) )
} }
}, },

View File

@@ -13,7 +13,6 @@ import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityUserFollowerListBinding import kr.co.vividnext.sodalive.databinding.ActivityUserFollowerListBinding
import kr.co.vividnext.sodalive.dialog.MemberProfileDialog import kr.co.vividnext.sodalive.dialog.MemberProfileDialog
import kr.co.vividnext.sodalive.explorer.profile.CreatorFollowNotifyFragment import kr.co.vividnext.sodalive.explorer.profile.CreatorFollowNotifyFragment
import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewActivity
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject

View File

@@ -1,8 +1,6 @@
package kr.co.vividnext.sodalive.following package kr.co.vividnext.sodalive.following
import kr.co.vividnext.sodalive.live.recommend.LiveRecommendApi import kr.co.vividnext.sodalive.live.recommend.LiveRecommendApi
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
import kr.co.vividnext.sodalive.user.UserApi
class FollowingCreatorRepository(private val api: LiveRecommendApi) { class FollowingCreatorRepository(private val api: LiveRecommendApi) {
fun getFollowedCreatorAllList( fun getFollowedCreatorAllList(

View File

@@ -66,7 +66,6 @@ class CreatorRankingAdapter(
binding.tvFollow.text = context.getString(R.string.screen_home_following) binding.tvFollow.text = context.getString(R.string.screen_home_following)
binding.tvFollow.setBackgroundResource(R.drawable.bg_round_corner_999_455a64) binding.tvFollow.setBackgroundResource(R.drawable.bg_round_corner_999_455a64)
binding.tvFollow.setTextColor(context.getColor(R.color.white)) binding.tvFollow.setTextColor(context.getColor(R.color.white))
} else { } else {
binding.tvFollow.text = context.getString(R.string.screen_home_follow) binding.tvFollow.text = context.getString(R.string.screen_home_follow)
binding.tvFollow.setBackgroundResource(R.drawable.bg_round_corner_999_white) binding.tvFollow.setBackgroundResource(R.drawable.bg_round_corner_999_white)

View File

@@ -10,8 +10,8 @@ import kr.co.vividnext.sodalive.databinding.ItemDayOfWeekBinding
import java.util.Calendar import java.util.Calendar
class DayOfWeekAdapter( class DayOfWeekAdapter(
private val context: Context, context: Context,
private val onClickItem: (SeriesPublishedDaysOfWeek) -> Unit, private val onClickItem: (SeriesPublishedDaysOfWeek) -> Unit
) : RecyclerView.Adapter<DayOfWeekAdapter.ViewHolder>() { ) : RecyclerView.Adapter<DayOfWeekAdapter.ViewHolder>() {
inner class ViewHolder( inner class ViewHolder(
@@ -40,7 +40,7 @@ class DayOfWeekAdapter(
DayOfWeek(dayOfWeekStr = context.getString(R.string.day_fri_short), dayOfWeek = SeriesPublishedDaysOfWeek.FRI), DayOfWeek(dayOfWeekStr = context.getString(R.string.day_fri_short), dayOfWeek = SeriesPublishedDaysOfWeek.FRI),
DayOfWeek(dayOfWeekStr = context.getString(R.string.day_sat_short), dayOfWeek = SeriesPublishedDaysOfWeek.SAT), DayOfWeek(dayOfWeekStr = context.getString(R.string.day_sat_short), dayOfWeek = SeriesPublishedDaysOfWeek.SAT),
DayOfWeek(dayOfWeekStr = context.getString(R.string.day_sun_short), dayOfWeek = SeriesPublishedDaysOfWeek.SUN), DayOfWeek(dayOfWeekStr = context.getString(R.string.day_sun_short), dayOfWeek = SeriesPublishedDaysOfWeek.SUN),
DayOfWeek(dayOfWeekStr = context.getString(R.string.day_random), dayOfWeek = SeriesPublishedDaysOfWeek.RANDOM), DayOfWeek(dayOfWeekStr = context.getString(R.string.day_random), dayOfWeek = SeriesPublishedDaysOfWeek.RANDOM)
) )
// 요일 숫자에 맞춰 배열 // 요일 숫자에 맞춰 배열

View File

@@ -13,7 +13,7 @@ import kr.co.vividnext.sodalive.extensions.dpToPx
class HomeContentAdapter( class HomeContentAdapter(
private val itemSquareSizePx: Int? = null, private val itemSquareSizePx: Int? = null,
private val onClickItem: (Long) -> Unit, private val onClickItem: (Long) -> Unit
) : RecyclerView.Adapter<HomeContentAdapter.ViewHolder>() { ) : RecyclerView.Adapter<HomeContentAdapter.ViewHolder>() {
private val items = mutableListOf<AudioContentMainItem>() private val items = mutableListOf<AudioContentMainItem>()

View File

@@ -149,7 +149,6 @@ class LiveViewModel(
fun getSummary() { fun getSummary() {
if (!_isLoading.value!!) { if (!_isLoading.value!!) {
if (SharedPreferenceManager.token.isNotBlank()) { if (SharedPreferenceManager.token.isNotBlank()) {
getFollowedChannelList() getFollowedChannelList()
getLatestPostListFromCreatorsYouFollow() getLatestPostListFromCreatorsYouFollow()

View File

@@ -1,38 +0,0 @@
package kr.co.vividnext.sodalive.live.event_banner
import android.content.Context
import android.widget.ImageView
import coil.load
import com.zhpan.bannerview.BaseBannerAdapter
import com.zhpan.bannerview.BaseViewHolder
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.settings.event.EventItem
class EventBannerAdapter(
private val context: Context,
private val itemClick: (EventItem) -> Unit
) : BaseBannerAdapter<EventItem>() {
override fun bindData(
holder: BaseViewHolder<EventItem>,
data: EventItem,
position: Int,
pageSize: Int
) {
val ivThumbnail = holder.findViewById<ImageView>(R.id.iv_thumbnail)
ivThumbnail.load(data.thumbnailImageUrl) {
crossfade(true)
val layoutParams = ivThumbnail.layoutParams
val screenWidth = context.resources.displayMetrics.widthPixels
layoutParams.width = screenWidth
layoutParams.height = (screenWidth * 300) / 1000
ivThumbnail.layoutParams = layoutParams
}
ivThumbnail.setOnClickListener { itemClick(data) }
}
override fun getLayoutId(viewType: Int): Int {
return R.layout.item_event_slider
}
}

View File

@@ -14,7 +14,7 @@ import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.GetRoomListResponse import kr.co.vividnext.sodalive.live.GetRoomListResponse
class LiveNowAdapter( class LiveNowAdapter(
private val onClick: (GetRoomListResponse) -> Unit, private val onClick: (GetRoomListResponse) -> Unit
) : RecyclerView.Adapter<LiveNowAdapter.ViewHolder>() { ) : RecyclerView.Adapter<LiveNowAdapter.ViewHolder>() {
var items = mutableListOf<GetRoomListResponse>() var items = mutableListOf<GetRoomListResponse>()

View File

@@ -14,7 +14,7 @@ import kr.co.vividnext.sodalive.extensions.dpToPx
class LiveRecommendChannelAdapter( class LiveRecommendChannelAdapter(
private val onClick: (Long) -> Unit, private val onClick: (Long) -> Unit,
private val onClickMore: () -> Unit, private val onClickMore: () -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val items = mutableListOf<GetRecommendChannelResponse>() private val items = mutableListOf<GetRecommendChannelResponse>()

View File

@@ -15,5 +15,5 @@ data class GetLiveReservationResponse(
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("masterNickname") val masterNickname: String, @SerializedName("masterNickname") val masterNickname: String,
@SerializedName("beginDateTimeUtc") val beginDateTimeUtc: String, @SerializedName("beginDateTimeUtc") val beginDateTimeUtc: String,
@SerializedName("cancelable") val cancelable: Boolean, @SerializedName("cancelable") val cancelable: Boolean
) : Parcelable ) : Parcelable

View File

@@ -126,9 +126,7 @@ class LiveReservationCancelActivity : BaseActivity<ActivityLiveReservationCancel
binding.tvCancel.setOnClickListener { binding.tvCancel.setOnClickListener {
viewModel.cancelReservation { viewModel.cancelReservation {
if (binding.tvPrice.text == SodaLiveApplicationHolder.get() if (binding.tvPrice.text == SodaLiveApplicationHolder.get().getString(R.string.live_reservation_free)) {
.getString(R.string.live_reservation_free)
) {
binding.tvCancelComplete.visibility = View.GONE binding.tvCancelComplete.visibility = View.GONE
} else { } else {
binding.tvCancelComplete.visibility = View.VISIBLE binding.tvCancelComplete.visibility = View.VISIBLE

View File

@@ -6,10 +6,10 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveReservationStatusBinding import kr.co.vividnext.sodalive.databinding.ItemLiveReservationStatusBinding
import kr.co.vividnext.sodalive.extensions.convertDateFormat import kr.co.vividnext.sodalive.extensions.convertDateFormat
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.settings.language.LanguageManager import kr.co.vividnext.sodalive.settings.language.LanguageManager
import kr.co.vividnext.sodalive.settings.language.LocaleHelper import kr.co.vividnext.sodalive.settings.language.LocaleHelper
import java.util.Locale import java.util.Locale
@@ -22,7 +22,7 @@ class LiveReservationStatusAdapter(
val items = mutableListOf<GetLiveReservationResponse>() val items = mutableListOf<GetLiveReservationResponse>()
inner class ViewHolder( inner class ViewHolder(
private val binding: ItemLiveReservationStatusBinding, private val binding: ItemLiveReservationStatusBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetLiveReservationResponse) { fun bind(item: GetLiveReservationResponse) {

View File

@@ -90,7 +90,6 @@ class LiveReservationStatusViewModel(
_isLoading.value = false _isLoading.value = false
}, },
{ {
_isLoading.value = false _isLoading.value = false
it.message?.let { message -> Logger.e(message) } it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue( _toastLiveData.postValue(

View File

@@ -3,7 +3,12 @@ package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
enum class GenderRestriction { enum class GenderRestriction {
@SerializedName("ALL") ALL, @SerializedName("ALL")
@SerializedName("MALE_ONLY") MALE_ONLY, ALL,
@SerializedName("FEMALE_ONLY") FEMALE_ONLY
@SerializedName("MALE_ONLY")
MALE_ONLY,
@SerializedName("FEMALE_ONLY")
FEMALE_ONLY
} }

View File

@@ -481,8 +481,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
context = this, context = this,
excludeGif = true, excludeGif = true,
config = ImagePickerCropper.Config( config = ImagePickerCropper.Config(
aspectX = 2f, aspectY = 3.8f, aspectX = 2f,
maxWidth = 1080, maxHeight = 2052, aspectY = 3.8f,
maxWidth = 1080,
maxHeight = 2052,
compressFormat = Bitmap.CompressFormat.JPEG, compressFormat = Bitmap.CompressFormat.JPEG,
compressQuality = 90 compressQuality = 90
), ),
@@ -640,7 +642,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
userId, userId,
nickname, nickname,
isBlock, isBlock,
view, view
) )
} }
) )
@@ -1208,7 +1210,8 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) )
roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl) roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl)
roomInfoEditDialog.setMenuPreset(it) roomInfoEditDialog.setMenuPreset(it)
roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageFile, isActivateMenu, menuId, menu, isAdult, isEntryMessageEnabled -> roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageFile,
isActivateMenu, menuId, menu, isAdult, isEntryMessageEnabled ->
if (isEntryMessageEnabled != null) { if (isEntryMessageEnabled != null) {
this.isEntryMessageEnabled = isEntryMessageEnabled this.isEntryMessageEnabled = isEntryMessageEnabled
} }
@@ -1337,7 +1340,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
hasInvokedJoinChannel = true hasInvokedJoinChannel = true
joinChannel(response) joinChannel(response)
} else { } else {
Logger.e("joinChannel - skip (rtcConnected=$rtcConnected, rtmLoggedIn=$rtmLoggedIn, hasInvokedJoinChannel=$hasInvokedJoinChannel)") Logger.e(
"joinChannel - skip (rtcConnected=$rtcConnected, " +
"rtmLoggedIn=$rtmLoggedIn, hasInvokedJoinChannel=$hasInvokedJoinChannel)"
)
} }
} }
@@ -3027,8 +3033,10 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
val elapsed = System.currentTimeMillis() - pressStartTime val elapsed = System.currentTimeMillis() - pressStartTime
// 경과 시간 기준으로 물 채우기(progress) 계산 (0..100) // 경과 시간 기준으로 물 채우기(progress) 계산 (0..100)
val progressFraction = (elapsed.coerceAtMost(longPressDurationMs) val progressFraction = (
.toFloat() / longPressDurationMs.toFloat()) elapsed.coerceAtMost(longPressDurationMs)
.toFloat() / longPressDurationMs.toFloat()
)
val progress = (progressFraction * 100f).toInt() val progress = (progressFraction * 100f).toInt()
longPressCenterHeart?.let { heartView -> longPressCenterHeart?.let { heartView ->
try { try {
@@ -3131,8 +3139,11 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
heart.setFrontWaveColor("#ff959a".toColorInt()) heart.setFrontWaveColor("#ff959a".toColorInt())
heart.setBehindWaveColor("#ff959a".toColorInt()) heart.setBehindWaveColor("#ff959a".toColorInt())
val elapsed = System.currentTimeMillis() - pressStartTime val elapsed = System.currentTimeMillis() - pressStartTime
val progress = ((elapsed.coerceAtMost(longPressDurationMs) val progress = (
.toFloat() / longPressDurationMs.toFloat()) * 100f).toInt() (
elapsed.coerceAtMost(longPressDurationMs).toFloat() / longPressDurationMs.toFloat()
) * 100f
).toInt()
try { try {
heart.progress = progress heart.progress = progress
} catch (_: Throwable) { } catch (_: Throwable) {
@@ -3589,14 +3600,20 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
val s = 0.9f val s = 0.9f
moveTo(0f, -0.45f * s) moveTo(0f, -0.45f * s)
cubicTo( cubicTo(
0.62f * s, -1.02f * s, 0.62f * s,
1.22f * s, -0.04f * s, -1.02f * s,
0f, 0.65f * s 1.22f * s,
-0.04f * s,
0f,
0.65f * s
) )
cubicTo( cubicTo(
-1.22f * s, -0.04f * s, -1.22f * s,
-0.62f * s, -1.02f * s, -0.04f * s,
0f, -0.45f * s -0.62f * s,
-1.02f * s,
0f,
-0.45f * s
) )
close() close()
} }
@@ -3784,14 +3801,20 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
val s = 0.9f val s = 0.9f
moveTo(0f, -0.45f * s) moveTo(0f, -0.45f * s)
cubicTo( cubicTo(
0.62f * s, -1.02f * s, 0.62f * s,
1.22f * s, -0.04f * s, -1.02f * s,
0f, 0.65f * s 1.22f * s,
-0.04f * s,
0f,
0.65f * s
) )
cubicTo( cubicTo(
-1.22f * s, -0.04f * s, -1.22f * s,
-0.62f * s, -1.02f * s, -0.04f * s,
0f, -0.45f * s -0.62f * s,
-1.02f * s,
0f,
-0.45f * s
) )
close() close()
} }
@@ -3899,14 +3922,20 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
val s = 0.9f val s = 0.9f
moveTo(0f, -0.45f * s) moveTo(0f, -0.45f * s)
cubicTo( cubicTo(
0.62f * s, -1.02f * s, 0.62f * s,
1.22f * s, -0.04f * s, -1.02f * s,
0f, 0.65f * s 1.22f * s,
-0.04f * s,
0f,
0.65f * s
) )
cubicTo( cubicTo(
-1.22f * s, -0.04f * s, -1.22f * s,
-0.62f * s, -1.02f * s, -0.04f * s,
0f, -0.45f * s -0.62f * s,
-1.02f * s,
0f,
-0.45f * s
) )
close() close()
} }
@@ -3946,7 +3975,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
if (rotateEnabled) 300f * Math.random().toFloat() else 0f // deg/s if (rotateEnabled) 300f * Math.random().toFloat() else 0f // deg/s
val rotation0 = (-180f + 360f * Math.random().toFloat()) val rotation0 = (-180f + 360f * Math.random().toFloat())
val fadeStartRatio = 0.80f + 0.15f * Math.random().toFloat() // 0.80..0.95 val fadeStartRatio = 0.80f + 0.15f * Math.random().toFloat() // 0.80..0.95
val fadeStartY = h * fadeStartRatio val fadeStartY = h * fadeStartRatio
@@ -3991,7 +4019,9 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
val fadeAlpha = if (y >= d.fadeStartY) { val fadeAlpha = if (y >= d.fadeStartY) {
val denom = (h - d.fadeStartY).coerceAtLeast(1f) val denom = (h - d.fadeStartY).coerceAtLeast(1f)
(1f - (y - d.fadeStartY) / denom).coerceIn(0f, 1f) (1f - (y - d.fadeStartY) / denom).coerceIn(0f, 1f)
} else 1f } else {
1f
}
val alpha = fadeAlpha.coerceIn(0f, 1f) val alpha = fadeAlpha.coerceIn(0f, 1f)
paint.alpha = (alpha * 255).toInt().coerceIn(0, 255) paint.alpha = (alpha * 255).toInt().coerceIn(0, 255)
@@ -4139,7 +4169,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
return false return false
} }
}) })
.into(binding.ivSignature) .into(binding.ivSignature)
} }
@@ -4205,7 +4234,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
} }
} }
} else { } else {
binding.flRoulette.visibility = View.GONE binding.flRoulette.visibility = View.GONE
} }
@@ -4221,7 +4249,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
type = LiveRoomChatRawMessageType.ROULETTE_DONATION, type = LiveRoomChatRawMessageType.ROULETTE_DONATION,
message = randomItem, message = randomItem,
can = can, can = can,
donationMessage = "", donationMessage = ""
) )
) )
@@ -4269,7 +4297,8 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
.subscribe( .subscribe(
{ response -> { response ->
Logger.e("성공: $response") Logger.e("성공: $response")
}, { error -> },
{ error ->
Logger.e("실패: ${error.message}") Logger.e("실패: ${error.message}")
} }
) )

View File

@@ -2,7 +2,6 @@ package kr.co.vividnext.sodalive.live.room
import android.app.Activity import android.app.Activity
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -10,12 +9,13 @@ import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.graphics.drawable.toDrawable
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomBinding import kr.co.vividnext.sodalive.databinding.DialogLiveRoomBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
class LiveRoomDialog( class LiveRoomDialog(
activity: Activity, activity: Activity,
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog
@@ -27,7 +27,7 @@ class LiveRoomDialog(
dialogBuilder.setView(dialogView.root) dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create() alertDialog = dialogBuilder.create()
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) alertDialog.window?.setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
} }
fun show(width: Int) { fun show(width: Int) {

View File

@@ -2,10 +2,10 @@ package kr.co.vividnext.sodalive.live.room
import android.app.Activity import android.app.Activity
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.WindowManager import android.view.WindowManager
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.graphics.drawable.toDrawable
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
@@ -17,7 +17,7 @@ class LiveRoomNoChattingDialog(
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater,
nickname: String, nickname: String,
profileUrl: String, profileUrl: String,
confirmButtonClick: () -> Unit, confirmButtonClick: () -> Unit
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog
@@ -30,7 +30,7 @@ class LiveRoomNoChattingDialog(
alertDialog = dialogBuilder.create() alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false) alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) alertDialog.window?.setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
dialogView.tvCancel.setOnClickListener { dialogView.tvCancel.setOnClickListener {
alertDialog.dismiss() alertDialog.dismiss()

View File

@@ -3,10 +3,21 @@ package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
enum class LiveRoomRequestType { enum class LiveRoomRequestType {
@SerializedName("INVITE_SPEAKER") INVITE_SPEAKER, @SerializedName("INVITE_SPEAKER")
@SerializedName("CHANGE_LISTENER") CHANGE_LISTENER, INVITE_SPEAKER,
@SerializedName("KICK_OUT") KICK_OUT,
@SerializedName("SET_MANAGER") SET_MANAGER, @SerializedName("CHANGE_LISTENER")
@SerializedName("RELEASE_MANAGER") RELEASE_MANAGER, CHANGE_LISTENER,
@SerializedName("NO_CHATTING") NO_CHATTING,
@SerializedName("KICK_OUT")
KICK_OUT,
@SerializedName("SET_MANAGER")
SET_MANAGER,
@SerializedName("RELEASE_MANAGER")
RELEASE_MANAGER,
@SerializedName("NO_CHATTING")
NO_CHATTING
} }

View File

@@ -3,6 +3,9 @@ package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
enum class LiveRoomStatus { enum class LiveRoomStatus {
@SerializedName("NOW") NOW, @SerializedName("NOW")
@SerializedName("RESERVATION") RESERVATION NOW,
@SerializedName("RESERVATION")
RESERVATION
} }

View File

@@ -3,6 +3,9 @@ package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
enum class LiveRoomType { enum class LiveRoomType {
@SerializedName("OPEN") OPEN, @SerializedName("OPEN")
@SerializedName("PRIVATE") PRIVATE, OPEN,
@SerializedName("PRIVATE")
PRIVATE
} }

View File

@@ -7,5 +7,5 @@ import java.util.TimeZone
@Keep @Keep
data class StartLiveRequest( data class StartLiveRequest(
@SerializedName("roomId") val roomId: Long, @SerializedName("roomId") val roomId: Long,
@SerializedName("timezone") val timezone: String = TimeZone.getDefault().id, @SerializedName("timezone") val timezone: String = TimeZone.getDefault().id
) )

View File

@@ -61,7 +61,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
"%d-%02d-%02d", "%d-%02d-%02d",
year, year,
monthOfYear + 1, monthOfYear + 1,
dayOfMonth, dayOfMonth
) )
viewModel.setReservationDate( viewModel.setReservationDate(
String.format( String.format(
@@ -136,8 +136,10 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
context = this, context = this,
excludeGif = true, excludeGif = true,
config = ImagePickerCropper.Config( config = ImagePickerCropper.Config(
aspectX = 2f, aspectY = 3.8f, aspectX = 2f,
maxWidth = 1080, maxHeight = 2052, aspectY = 3.8f,
maxWidth = 1080,
maxHeight = 2052,
compressFormat = Bitmap.CompressFormat.JPEG, compressFormat = Bitmap.CompressFormat.JPEG,
compressQuality = 90 compressQuality = 90
), ),

View File

@@ -1,12 +1,12 @@
package kr.co.vividnext.sodalive.live.room.create package kr.co.vividnext.sodalive.live.room.create
import android.net.Uri
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson import com.google.gson.Gson
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.UiText import kr.co.vividnext.sodalive.common.UiText
@@ -16,7 +16,6 @@ import kr.co.vividnext.sodalive.live.LiveRepository
import kr.co.vividnext.sodalive.live.room.GenderRestriction import kr.co.vividnext.sodalive.live.room.GenderRestriction
import kr.co.vividnext.sodalive.live.room.LiveRoomType import kr.co.vividnext.sodalive.live.room.LiveRoomType
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
import kr.co.vividnext.sodalive.R
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.asRequestBody

View File

@@ -20,7 +20,7 @@ class LiveCancelDialog(
confirmButtonTitle: String, confirmButtonTitle: String,
confirmButtonClick: (String) -> Unit, confirmButtonClick: (String) -> Unit,
cancelButtonTitle: String = "", cancelButtonTitle: String = "",
cancelButtonClick: (() -> Unit)? = null, cancelButtonClick: (() -> Unit)? = null
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog
private val dialogView = DialogLiveInputBinding.inflate(layoutInflater) private val dialogView = DialogLiveInputBinding.inflate(layoutInflater)

View File

@@ -21,7 +21,7 @@ class LivePaymentDialog(
confirmButtonTitle: String, confirmButtonTitle: String,
confirmButtonClick: () -> Unit, confirmButtonClick: () -> Unit,
cancelButtonTitle: String = "", cancelButtonTitle: String = "",
cancelButtonClick: (() -> Unit)? = null, cancelButtonClick: (() -> Unit)? = null
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog
private val dialogView = DialogLivePaymentBinding.inflate(layoutInflater) private val dialogView = DialogLivePaymentBinding.inflate(layoutInflater)

View File

@@ -20,7 +20,7 @@ class LiveRoomPasswordDialog(
activity: Activity, activity: Activity,
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater,
can: Int, can: Int,
confirmButtonClick: (String) -> Unit, confirmButtonClick: (String) -> Unit
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog

View File

@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
data class LiveRoomLikeHeartRequest( data class LiveRoomLikeHeartRequest(
@SerializedName("roomId") val roomId: Long, @SerializedName("roomId") val roomId: Long,
@SerializedName("container") val container: String, @SerializedName("container") val container: String,
@SerializedName("heartCount") val heartCount: Int = 1, @SerializedName("heartCount") val heartCount: Int = 1
) )

View File

@@ -15,7 +15,6 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityMenuConfigBinding import kr.co.vividnext.sodalive.databinding.ActivityMenuConfigBinding
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel

View File

@@ -195,19 +195,19 @@ abstract class LiveRoomProfileViewHolder(binding: ViewBinding) :
} }
class LiveRoomProfileSpeakerTitleViewHolder( class LiveRoomProfileSpeakerTitleViewHolder(
private val binding: ItemLiveRoomProfileHeaderBinding, private val binding: ItemLiveRoomProfileHeaderBinding
) : LiveRoomProfileViewHolder(binding) { ) : LiveRoomProfileViewHolder(binding) {
override fun bind(item: LiveRoomProfileItem) = item.bind(binding) override fun bind(item: LiveRoomProfileItem) = item.bind(binding)
} }
class LiveRoomProfileListenerTitleViewHolder( class LiveRoomProfileListenerTitleViewHolder(
private val binding: ItemLiveRoomProfileHeaderBinding, private val binding: ItemLiveRoomProfileHeaderBinding
) : LiveRoomProfileViewHolder(binding) { ) : LiveRoomProfileViewHolder(binding) {
override fun bind(item: LiveRoomProfileItem) = item.bind(binding) override fun bind(item: LiveRoomProfileItem) = item.bind(binding)
} }
class LiveRoomProfileManagerTitleViewHolder( class LiveRoomProfileManagerTitleViewHolder(
private val binding: ItemLiveRoomProfileHeaderBinding, private val binding: ItemLiveRoomProfileHeaderBinding
) : LiveRoomProfileViewHolder(binding) { ) : LiveRoomProfileViewHolder(binding) {
override fun bind(item: LiveRoomProfileItem) = item.bind(binding) override fun bind(item: LiveRoomProfileItem) = item.bind(binding)
} }

View File

@@ -28,7 +28,7 @@ class LiveRoomUserProfileDialog(
private val onClickChangeListener: (Long) -> Unit, private val onClickChangeListener: (Long) -> Unit,
private val onClickKickOut: (Long) -> Unit, private val onClickKickOut: (Long) -> Unit,
private val onClickNoChatting: (Long, String, String) -> Unit, private val onClickNoChatting: (Long, String, String) -> Unit,
private val onClickPopupMenu: (Long, String, Boolean, View) -> Unit, private val onClickPopupMenu: (Long, String, Boolean, View) -> Unit
) { ) {
private val alertDialog: AlertDialog private val alertDialog: AlertDialog
private val dialogView = DialogLiveRoomUserProfileBinding.inflate(layoutInflater) private val dialogView = DialogLiveRoomUserProfileBinding.inflate(layoutInflater)

View File

@@ -24,8 +24,6 @@ class LiveTagFragment(
private val selectedTags: Set<String>, private val selectedTags: Set<String>,
private val onItemClick: (String, Boolean) -> Boolean private val onItemClick: (String, Boolean) -> Boolean
) : BottomSheetDialogFragment() { ) : BottomSheetDialogFragment() {
private val viewModel: LiveTagViewModel by inject() private val viewModel: LiveTagViewModel by inject()
private lateinit var adapter: LiveTagAdapter private lateinit var adapter: LiveTagAdapter

View File

@@ -190,12 +190,12 @@ class LiveRoomEditViewModel(
beginDate = date.convertDateFormat( beginDate = date.convertDateFormat(
from = "yyyy.MM.dd", from = "yyyy.MM.dd",
to = "yyyy-MM-dd", to = "yyyy-MM-dd"
) )
beginTime = time.convertDateFormat( beginTime = time.convertDateFormat(
from = "a hh:mm", from = "a hh:mm",
to = "HH:mm", to = "HH:mm"
) )
beginDateTimeStr = "$beginDate $beginTime" beginDateTimeStr = "$beginDate $beginTime"

View File

@@ -132,9 +132,12 @@ class RouletteView @JvmOverloads constructor(
val targetAngle = 0 - (getAngleForOption(option) + 360 * 10) val targetAngle = 0 - (getAngleForOption(option) + 360 * 10)
val rotateAnimation = RotateAnimation( val rotateAnimation = RotateAnimation(
0f, targetAngle, 0f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f, targetAngle,
RotateAnimation.RELATIVE_TO_SELF, 0.5f RotateAnimation.RELATIVE_TO_SELF,
0.5f,
RotateAnimation.RELATIVE_TO_SELF,
0.5f
) )
rotateAnimation.duration = 2000 rotateAnimation.duration = 2000
rotateAnimation.fillAfter = true rotateAnimation.fillAfter = true

View File

@@ -67,7 +67,8 @@ class RouletteConfigActivity : BaseActivity<ActivityRouletteConfigBinding>(
Service.INPUT_METHOD_SERVICE Service.INPUT_METHOD_SERVICE
) as InputMethodManager ) as InputMethodManager
binding.etSetPrice.filters = arrayOf(InputFilter { source, start, end, _, _, _ -> binding.etSetPrice.filters = arrayOf(
InputFilter { source, start, end, _, _, _ ->
// Only allow numeric input // Only allow numeric input
for (i in start until end) { for (i in start until end) {
if (!Character.isDigit(source[i])) { if (!Character.isDigit(source[i])) {
@@ -75,7 +76,8 @@ class RouletteConfigActivity : BaseActivity<ActivityRouletteConfigBinding>(
} }
} }
null null
}) }
)
binding.ivRouletteIsActive.setOnClickListener { viewModel.toggleIsActive() } binding.ivRouletteIsActive.setOnClickListener { viewModel.toggleIsActive() }
binding.ivAddOption.setOnClickListener { addOption() } binding.ivAddOption.setOnClickListener { addOption() }

View File

@@ -5,19 +5,22 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.app.SodaLiveApp import kr.co.vividnext.sodalive.app.SodaLiveApp
import kr.co.vividnext.sodalive.audition.AuditionActivity import kr.co.vividnext.sodalive.audition.AuditionActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
import kr.co.vividnext.sodalive.message.MessageActivity import kr.co.vividnext.sodalive.message.MessageActivity
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity
import kr.co.vividnext.sodalive.splash.SplashActivity import kr.co.vividnext.sodalive.splash.SplashActivity
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity import kr.co.vividnext.sodalive.v2.chat.action.ChatActionCommand
import kr.co.vividnext.sodalive.v2.chat.action.handleChatAction
import kr.co.vividnext.sodalive.v2.community.action.CommunityActionCommand
import kr.co.vividnext.sodalive.v2.community.action.handleCommunityAction
import kr.co.vividnext.sodalive.v2.content.action.ContentActionCommand
import kr.co.vividnext.sodalive.v2.content.action.handleContentAction
import kr.co.vividnext.sodalive.v2.creator.action.CreatorActionCommand
import kr.co.vividnext.sodalive.v2.creator.action.handleCreatorAction
import kr.co.vividnext.sodalive.v2.main.MainV2Activity import kr.co.vividnext.sodalive.v2.main.MainV2Activity
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
import java.util.Locale import java.util.Locale
class DeepLinkActivity : AppCompatActivity() { class DeepLinkActivity : AppCompatActivity() {
@@ -276,7 +279,7 @@ class DeepLinkActivity : AppCompatActivity() {
?: bundle.getLong(Constants.EXTRA_COMMUNITY_POST_ID).takeIf { it > 0 } ?: bundle.getLong(Constants.EXTRA_COMMUNITY_POST_ID).takeIf { it > 0 }
if (isDmChatDeepLink(bundle) && roomId != null && roomId > 0) { if (isDmChatDeepLink(bundle) && roomId != null && roomId > 0) {
startActivity(DmChatRoomActivity.newIntentByRoomId(applicationContext, roomId)) handleChatAction(ChatActionCommand.DmRoom(roomId))
return true return true
} }
@@ -286,36 +289,29 @@ class DeepLinkActivity : AppCompatActivity() {
return true return true
} }
communityPostId != null && communityPostId > 0 -> {
handleCommunityAction(CommunityActionCommand.PostDetail(communityPostId))
return true
}
channelId != null && channelId > 0 -> { channelId != null && channelId > 0 -> {
startActivity( handleCreatorAction(CreatorActionCommand.Profile(channelId))
CreatorChannelActivity.newIntent(applicationContext, channelId)
)
return true return true
} }
contentId != null && contentId > 0 -> { contentId != null && contentId > 0 -> {
startActivity( handleContentAction(ContentActionCommand.AudioDetail(contentId))
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
}
)
return true return true
} }
messageId != null && messageId > 0 -> { messageId != null && messageId > 0 -> {
// messageId는 DM room ID가 아니므로 과거 알림 수신 호환만 유지한다.
startActivity(Intent(applicationContext, MessageActivity::class.java)) startActivity(Intent(applicationContext, MessageActivity::class.java))
return true return true
} }
communityCreatorId != null && communityCreatorId > 0 -> { communityCreatorId != null && communityCreatorId > 0 -> {
startActivity( handleCreatorAction(CreatorActionCommand.Profile(communityCreatorId))
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, communityCreatorId)
if (communityPostId != null && communityPostId > 0) {
putExtra(Constants.EXTRA_COMMUNITY_POST_ID, communityPostId)
}
}
)
return true return true
} }
@@ -345,11 +341,7 @@ class DeepLinkActivity : AppCompatActivity() {
return false return false
} }
startActivity( handleContentAction(ContentActionCommand.SeriesDetail(deepLinkValueId))
Intent(applicationContext, SeriesDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_SERIES_ID, deepLinkValueId)
}
)
true true
} }
@@ -358,11 +350,7 @@ class DeepLinkActivity : AppCompatActivity() {
return false return false
} }
startActivity( handleContentAction(ContentActionCommand.AudioDetail(deepLinkValueId))
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, deepLinkValueId)
}
)
true true
} }
@@ -371,9 +359,7 @@ class DeepLinkActivity : AppCompatActivity() {
return false return false
} }
startActivity( handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))
CreatorChannelActivity.newIntent(applicationContext, deepLinkValueId)
)
true true
} }
@@ -391,15 +377,13 @@ class DeepLinkActivity : AppCompatActivity() {
return false return false
} }
startActivity( // deepLinkValueId는 legacy creatorId이며 postId는 routeForegroundDeepLink에서 먼저 처리한다.
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply { handleCreatorAction(CreatorActionCommand.Profile(deepLinkValueId))
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, deepLinkValueId)
}
)
true true
} }
"message" -> { "message" -> {
// 현재 DM은 chat/{roomId} 계약을 사용하며 message는 legacy fallback이다.
startActivity(Intent(applicationContext, MessageActivity::class.java)) startActivity(Intent(applicationContext, MessageActivity::class.java))
true true
} }

View File

@@ -5,8 +5,8 @@ import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.ToastMessage import kr.co.vividnext.sodalive.common.ToastMessage
import kr.co.vividnext.sodalive.mypage.auth.AuthRepository import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
@@ -15,7 +15,6 @@ import kr.co.vividnext.sodalive.settings.ContentType
import kr.co.vividnext.sodalive.settings.notice.NoticeItem import kr.co.vividnext.sodalive.settings.notice.NoticeItem
import kr.co.vividnext.sodalive.settings.notice.NoticeRepository import kr.co.vividnext.sodalive.settings.notice.NoticeRepository
import kr.co.vividnext.sodalive.user.UserRepository import kr.co.vividnext.sodalive.user.UserRepository
import kr.co.vividnext.sodalive.R
class MyPageViewModel( class MyPageViewModel(
private val userRepository: UserRepository, private val userRepository: UserRepository,

View File

@@ -11,7 +11,6 @@ import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import com.orhanobut.logger.Logger
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
@@ -211,7 +210,7 @@ class AddAlarmActivity : BaseActivity<ActivityAddAlarmBinding>(
contentTitle = selectedContentTitle, contentTitle = selectedContentTitle,
contentCreatorNickname = selectedContentCreatorNickname, contentCreatorNickname = selectedContentCreatorNickname,
volume = binding.sbVolume.progress, volume = binding.sbVolume.progress,
days = selectedDays.toList(), days = selectedDays.toList()
) )
if (alarmId > 0) { if (alarmId > 0) {

View File

@@ -95,7 +95,7 @@ class AlarmActivity : BaseActivity<ActivityAlarmBinding>(
} }
private fun getContent(contentId: Long) { private fun getContent(contentId: Long) {
contentViewModel.getAudioContentDetail(contentId, {},) contentViewModel.getAudioContentDetail(contentId) {}
} }
private fun bindData() { private fun bindData() {

View File

@@ -5,7 +5,6 @@ import android.annotation.SuppressLint
import android.app.AlarmManager import android.app.AlarmManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
@@ -15,7 +14,7 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.app.NotificationManagerCompat import androidx.core.net.toUri
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.gun0912.tedpermission.PermissionListener import com.gun0912.tedpermission.PermissionListener
import com.gun0912.tedpermission.normal.TedPermission import com.gun0912.tedpermission.normal.TedPermission
@@ -28,7 +27,6 @@ import kr.co.vividnext.sodalive.databinding.ActivityAlarmListBinding
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm import kr.co.vividnext.sodalive.mypage.alarm.db.Alarm
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import androidx.core.net.toUri
class AlarmListActivity : BaseActivity<ActivityAlarmListBinding>( class AlarmListActivity : BaseActivity<ActivityAlarmListBinding>(
ActivityAlarmListBinding::inflate ActivityAlarmListBinding::inflate
@@ -156,7 +154,6 @@ class AlarmListActivity : BaseActivity<ActivityAlarmListBinding>(
binding.tvEmptyAlarms.visibility = binding.tvEmptyAlarms.visibility =
if (it.isEmpty()) View.VISIBLE else View.GONE if (it.isEmpty()) View.VISIBLE else View.GONE
viewModel.getSlotQuantityAndPrice() viewModel.getSlotQuantityAndPrice()
} }
} }
@@ -236,7 +233,7 @@ class AlarmListActivity : BaseActivity<ActivityAlarmListBinding>(
).show() ).show()
adapterRefresh() adapterRefresh()
}, },
cancelButtonTitle = getString(R.string.cancel), cancelButtonTitle = getString(R.string.cancel)
).show(screenWidth) ).show(screenWidth)
} }
@@ -248,7 +245,7 @@ class AlarmListActivity : BaseActivity<ActivityAlarmListBinding>(
desc = getString(R.string.alarm_list_buy_desc), desc = getString(R.string.alarm_list_buy_desc),
confirmButtonTitle = getString(R.string.alarm_list_buy_confirm), confirmButtonTitle = getString(R.string.alarm_list_buy_confirm),
confirmButtonClick = { viewModel.buyExtraSlot() }, confirmButtonClick = { viewModel.buyExtraSlot() },
cancelButtonTitle = getString(R.string.cancel), cancelButtonTitle = getString(R.string.cancel)
).show(screenWidth) ).show(screenWidth)
} }
} }

View File

@@ -1,14 +1,12 @@
package kr.co.vividnext.sodalive.mypage.alarm.select_audio_content package kr.co.vividnext.sodalive.mypage.alarm.select_audio_content
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListItem import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListItem
import kr.co.vividnext.sodalive.audio_content.order.OrderType
import kr.co.vividnext.sodalive.databinding.ItemAudioContentOrderListBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentOrderListBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat

View File

@@ -15,6 +15,6 @@ data class BootpayResponse(
@SerializedName("receipt_id") @SerializedName("receipt_id")
val receiptId: String, val receiptId: String,
@SerializedName("order_id") @SerializedName("order_id")
val orderId: String, val orderId: String
) )
} }

View File

@@ -98,12 +98,10 @@ class BlockMemberActivity : BaseActivity<ActivityBlockMemberBinding>(
} }
viewModel.blockedMemberTotalCountLiveData.observe(this) { viewModel.blockedMemberTotalCountLiveData.observe(this) {
binding.tvTotalCount.text = " ${ binding.tvTotalCount.text = getString(
getString(
R.string.screen_block_member_total_count, R.string.screen_block_member_total_count,
it it
) )
} "
if (it > 0) { if (it > 0) {
binding.tvNone.visibility = View.GONE binding.tvNone.visibility = View.GONE

View File

@@ -7,10 +7,10 @@ import com.android.billingclient.api.Purchase
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.ToastMessage
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.ToastMessage
import kr.co.vividnext.sodalive.mypage.can.CanRepository import kr.co.vividnext.sodalive.mypage.can.CanRepository
class CanChargeIapViewModel(private val repository: CanRepository) : BaseViewModel() { class CanChargeIapViewModel(private val repository: CanRepository) : BaseViewModel() {
@@ -38,8 +38,8 @@ class CanChargeIapViewModel(private val repository: CanRepository) : BaseViewMod
title = title, title = title,
chargeCan = selectedProductDetails.description.toInt(), chargeCan = selectedProductDetails.description.toInt(),
price = ( price = (
selectedProductDetails.oneTimePurchaseOfferDetails?.priceAmountMicros selectedProductDetails.oneTimePurchaseOfferDetails?.priceAmountMicros ?: 0L
?: 0L).toDouble() / 1000000, ).toDouble() / 1000000,
currencyCode = selectedProductDetails.oneTimePurchaseOfferDetails?.priceCurrencyCode currencyCode = selectedProductDetails.oneTimePurchaseOfferDetails?.priceCurrencyCode
?: "KRW", ?: "KRW",
productId = purchase.products[0], productId = purchase.products[0],

View File

@@ -3,13 +3,12 @@ package kr.co.vividnext.sodalive.mypage.can.coupon
import android.os.Bundle import android.os.Bundle
import android.text.InputFilter import android.text.InputFilter
import android.widget.Toast import android.widget.Toast
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityCanCouponBinding import kr.co.vividnext.sodalive.databinding.ActivityCanCouponBinding
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
class CanCouponActivity : BaseActivity<ActivityCanCouponBinding>( class CanCouponActivity : BaseActivity<ActivityCanCouponBinding>(
ActivityCanCouponBinding::inflate ActivityCanCouponBinding::inflate
) { ) {

View File

@@ -5,10 +5,10 @@ import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.ToastMessage
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.ToastMessage
import kr.co.vividnext.sodalive.mypage.can.CanRepository import kr.co.vividnext.sodalive.mypage.can.CanRepository
class CanCouponViewModel(private val repository: CanRepository) : BaseViewModel() { class CanCouponViewModel(private val repository: CanRepository) : BaseViewModel() {
@@ -49,7 +49,6 @@ class CanCouponViewModel(private val repository: CanRepository) : BaseViewModel(
_toastLiveData.value = it.message?.let { message -> _toastLiveData.value = it.message?.let { message ->
ToastMessage(message = message) ToastMessage(message = message)
} ?: ToastMessage(resId = R.string.msg_can_coupon_unknown_error) } ?: ToastMessage(resId = R.string.msg_can_coupon_unknown_error)
} }
) )
) )

View File

@@ -3,6 +3,9 @@ package kr.co.vividnext.sodalive.mypage.can.payment
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
enum class PaymentGateway { enum class PaymentGateway {
@SerializedName("PG") PG, @SerializedName("PG")
@SerializedName("GOOGLE_IAP") GOOGLE_IAP PG,
@SerializedName("GOOGLE_IAP")
GOOGLE_IAP
} }

View File

@@ -220,7 +220,8 @@ class ProfileUpdateActivity : BaseActivity<ActivityProfileUpdateBinding>(
excludeGif = true, excludeGif = true,
isEnabledFreeStyleCrop = false, isEnabledFreeStyleCrop = false,
config = ImagePickerCropper.Config( config = ImagePickerCropper.Config(
aspectX = 1f, aspectY = 1f, aspectX = 1f,
aspectY = 1f,
compressFormat = Bitmap.CompressFormat.JPEG, compressFormat = Bitmap.CompressFormat.JPEG,
compressQuality = 90 compressQuality = 90
), ),

View File

@@ -212,10 +212,12 @@ class ProfileUpdateViewModel(private val repository: UserRepository) : BaseViewM
} }
) )
) )
} else run { } else {
run {
onSuccess() onSuccess()
} }
} }
}
fun updatePassword(onSuccess: () -> Unit) { fun updatePassword(onSuccess: () -> Unit) {
val email = SharedPreferenceManager.email val email = SharedPreferenceManager.email

Some files were not shown because too many files have changed in this diff Show More