Compare commits

65 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
7ec4e09fb4 versionCode 242 2026-07-13 12:00:54 +09:00
862cec8375 fix(dm): 광고 로드 실패 안내를 보강한다 2026-07-12 22:09:00 +09:00
2d4956ae25 fix(dm): 쿼터 안내 광고 버튼을 숨긴다 2026-07-12 22:08:41 +09:00
beadc7d4c4 chore(app): 버전 코드를 갱신한다 2026-07-12 20:52:07 +09:00
2633912e93 test(dm): 채팅 플로팅 버튼 제거 검증을 맞춘다 2026-07-12 20:52:01 +09:00
6cbc751f0c fix(community): 채널 이미지 원본비율 표시를 반영한다 2026-07-12 20:51:54 +09:00
2a499eaf41 fix(home): 커뮤니티 이미지 로드 크기를 전달한다 2026-07-12 20:51:36 +09:00
9244a00713 fix(feed): 커뮤니티 이미지 원본비율 높이를 적용한다 2026-07-12 20:51:30 +09:00
79eadbd39d docs(community): 이미지 원본비율 요구를 기록한다 2026-07-12 20:51:22 +09:00
c389d1945d fix(dm): 채팅 플로팅 버튼을 숨긴다 2026-07-12 17:45:19 +09:00
330 changed files with 21671 additions and 3323 deletions

1
.gitignore vendored
View File

@@ -319,5 +319,6 @@ app/release/
.kiro/
.omo/
.antigravitycli/
.worktrees/
# 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/prd/`, `docs/plan-task/` 문서는 유지하고, 신규 생성 문서부터 위 구조를 적용한다.
- 날짜는 `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 문서에 추가 작업으로 이어서 기록한다.
- 작업 도중 범위가 변경되면 계획/TASK 문서 체크리스트를 먼저 업데이트한 뒤 구현한다.
- 특정 Phase 또는 Task에 직접 대응되는 검증 기록은 해당 Phase 또는 Task 아래에 한국어로 남긴다.
- 여러 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`를 참고한다.

View File

@@ -86,8 +86,8 @@ android {
applicationId "kr.co.vividnext.sodalive"
minSdk 23
targetSdk 35
versionCode 239
versionName "1.55.0"
versionCode 246
versionName "1.56.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -116,8 +116,6 @@ android {
// release용 ad unit id는 배포 전 실제 값으로 교체한다.
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_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_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"'
@@ -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_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_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"'
@@ -271,7 +267,7 @@ dependencies {
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel'
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
implementation "com.google.code.gson:gson:2.13.2"

View File

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

View File

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

View File

@@ -14,5 +14,5 @@ data class AddAllPlaybackTrackingRequest(
data class PlaybackTrackingData(
@SerializedName("contentId") val contentId: Long,
@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(
repository.getCategoryList(
creatorId = userId,
token = "Bearer ${SharedPreferenceManager.token}",
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

View File

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

View File

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

View File

@@ -35,7 +35,7 @@ class AudioContentCommentReplyAdapter(
LayoutInflater.from(parent.context),
parent,
false
),
)
)
} else {
AudioContentCommentReplyItemViewHolder(
@@ -172,9 +172,7 @@ class AudioContentCommentReplyItemViewHolder(
private val context: Context,
private val creatorId: Long,
private val binding: ItemAudioContentCommentReplyBinding,
private val showOptionMenu: (
Context, View, Long, Long, Long, onClickModify: () -> Unit
) -> Unit,
private val showOptionMenu: (Context, View, Long, Long, Long, onClickModify: () -> Unit) -> Unit,
private val modifyComment: (Long, String) -> Unit
) : 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
import android.annotation.SuppressLint
@@ -10,6 +12,7 @@ import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.inputmethod.InputMethodManager
@@ -28,18 +31,6 @@ import coil.transform.RoundedCornersTransformation
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
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.audio_content.AudioContentPlayService
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 org.koin.android.ext.android.inject
import kotlin.math.ceil
import kotlin.math.roundToInt
@UnstableApi
class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBinding>(
@@ -101,42 +91,9 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
private lateinit var orderType: OrderType
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 isAudioContentInterstitialEligible = false
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")
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
@@ -154,9 +111,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.rlPreviewAlert.visibility = View.GONE
audioContentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
hasConsumedAudioContentPlayInterstitialAttempt = false
releaseAudioContentPlayInterstitial()
setupAudioContentPlayInterstitial()
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
}
@@ -367,91 +321,9 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
dialog.show(screenWidth - 26.7f.dpToPx().toInt())
}
setupAudioContentDetailInlineBanner()
setupAudioContentPlayInterstitial()
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() {
startService(
Intent(this, AudioContentPlayService::class.java).apply {
@@ -474,13 +346,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.ivPlayOrPause.setImageResource(R.drawable.btn_audio_content_play)
val startPlaybackClickListener = View.OnClickListener {
if (isAudioContentInterstitialEligible) {
playAudioContentWithInterstitialIfAvailable(startPlaybackAction)
} else {
startPlaybackAction()
}
}
val startPlaybackClickListener = View.OnClickListener { startPlaybackAction() }
binding.ivPlayOrPause.setOnClickListener(startPlaybackClickListener)
binding.llPreview.setOnClickListener(startPlaybackClickListener)
@@ -600,6 +466,11 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
}
viewModel.isExpandDetail.observe(this) {
binding.tvDetail.ellipsize = if (it) {
null
} else {
TextUtils.TruncateAt.END
}
binding.tvDetail.maxLines = if (it) {
Int.MAX_VALUE
} else {
@@ -944,12 +815,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
response.duration
)
audioContentStartPlaybackAction = null
isAudioContentInterstitialEligible = false
isAlertPreview = response.creator.creatorId != SharedPreferenceManager.userId &&
!response.existOrdered &&
response.price > 0
isAudioContentInterstitialEligible = response.price <= 0 || isAlertPreview
if (
response.creator.creatorId != SharedPreferenceManager.userId && !response.existOrdered &&
@@ -1046,7 +915,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
}
} else if (response.releaseDate == null) {
audioContentStartPlaybackAction = null
isAudioContentInterstitialEligible = false
binding.llPreviewNo.visibility = View.VISIBLE
}
@@ -1366,8 +1234,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
}
override fun onDestroy() {
binding.yandexInlineBannerView.destroy()
releaseAudioContentPlayInterstitial()
super.onDestroy()
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,20 +1,9 @@
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.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.audio_content.detail.AudioContentDetailActivity
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.extensions.dpToPx
class AudioContentOrderListActivity : BaseActivity<ActivityAudioContentOrderListBinding>(
ActivityAudioContentOrderListBinding::inflate

View File

@@ -20,5 +20,5 @@ data class GetAudioContentOrderListItem(
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("orderType") val orderType: OrderType,
@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())
}
}
})
updateMediaMetadata(mediaController?.currentMediaItem?.mediaMetadata)

View File

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

View File

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

View File

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

View File

@@ -20,5 +20,5 @@ data class CreateAudioContentRequest(
@SerializedName("isPointAvailable") val isPointAvailable: Boolean,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@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.progress = currentTime
binding.tvTotalDuration.text =
"/${
Utils.convertDurationToString(
currentTotalDuration,
showHours = false
)
}"
binding.tvTotalDuration.text = "/${Utils.convertDurationToString(currentTotalDuration, showHours = false)}"
binding.tvCurrentTime.text = Utils.convertDurationToString(
currentTime,
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.common.Constants
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.extensions.dpToPx
import org.koin.android.ext.android.inject

View File

@@ -43,7 +43,8 @@ class CharacterAdapter(
translationY = fm.descent
return true
}
})
}
)
}
} else {
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.base.BaseFragment
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.databinding.FragmentCharacterCommentListBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
@@ -151,8 +150,7 @@ class CharacterCommentListFragment : BaseFragment<FragmentCharacterCommentListBi
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisible = (recyclerView.layoutManager as LinearLayoutManager)
.findLastCompletelyVisibleItemPosition()
val lastVisible = (recyclerView.layoutManager as LinearLayoutManager).findLastCompletelyVisibleItemPosition()
val total = recyclerView.adapter?.itemCount ?: 0
if (!recyclerView.canScrollVertically(1) && lastVisible == total - 1) {
viewModel.getCommentList(characterId)
@@ -195,7 +193,6 @@ class CharacterCommentListFragment : BaseFragment<FragmentCharacterCommentListBi
imm.hideSoftInputFromWindow(view?.windowToken, 0)
}
companion object {
private const val EXTRA_CHARACTER_ID = "extra_character_id"
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.base.BaseFragment
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.databinding.FragmentCharacterCommentReplyBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
@@ -48,15 +47,19 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
characterId = arguments?.getLong(EXTRA_CHARACTER_ID) ?: 0
original = arguments?.let {
val cid = it.getLong(EXTRA_ORIGINAL_COMMENT_ID, -1)
if (cid == -1L) null else CharacterCommentResponse(
commentId = cid,
memberId = it.getLong(EXTRA_ORIGINAL_MEMBER_ID),
memberProfileImage = it.getString(EXTRA_ORIGINAL_MEMBER_PROFILE_IMAGE) ?: "",
memberNickname = it.getString(EXTRA_ORIGINAL_MEMBER_NICKNAME) ?: "",
createdAt = it.getLong(EXTRA_ORIGINAL_CREATED_AT),
replyCount = it.getInt(EXTRA_ORIGINAL_REPLY_COUNT),
comment = it.getString(EXTRA_ORIGINAL_COMMENT_TEXT) ?: ""
)
if (cid == -1L) {
null
} else {
CharacterCommentResponse(
commentId = cid,
memberId = it.getLong(EXTRA_ORIGINAL_MEMBER_ID),
memberProfileImage = it.getString(EXTRA_ORIGINAL_MEMBER_PROFILE_IMAGE) ?: "",
memberNickname = it.getString(EXTRA_ORIGINAL_MEMBER_NICKNAME) ?: "",
createdAt = it.getLong(EXTRA_ORIGINAL_CREATED_AT),
replyCount = it.getInt(EXTRA_ORIGINAL_REPLY_COUNT),
comment = it.getString(EXTRA_ORIGINAL_COMMENT_TEXT) ?: ""
)
}
}
return super.onCreateView(inflater, container, savedInstanceState)
}
@@ -140,7 +143,7 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
outRect.right = 24f.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 24f.dpToPx().toInt();
outRect.top = 24f.dpToPx().toInt()
outRect.bottom = 12f.dpToPx().toInt()
}
@@ -196,7 +199,6 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
}
}
companion object {
private const val EXTRA_CHARACTER_ID = "extra_character_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
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

View File

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

View File

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

View File

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

View File

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

View File

@@ -85,7 +85,6 @@ class OriginalWorkDetailActivity : BaseActivity<ActivityOriginalWorkDetailBindin
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) })
adapter.submit(items, selectedId)
loadingDialog.dismiss()
}, { _ ->
// 실패 시에도 현재까지의 목록 표시(없으면 빈 목록)
@@ -161,9 +160,7 @@ class ChatBackgroundPickerDialogFragment : DialogFragment() {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BgVH {
val binding = ItemChatBackgroundImageBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
val binding = ItemChatBackgroundImageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return BgVH(binding, onClick)
}

View File

@@ -655,14 +655,11 @@ class ChatMessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
// endregion
/** 쿼터 안내 메시지 뷰홀더: 광고 보기 + 캔 구매 버튼 */
class QuotaNoticeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val btnRewardedAd: View = itemView.findViewById(R.id.ll_rewarded_ad)
private val btnPurchase10Can: View = itemView.findViewById(R.id.ll_purchase_10_can)
private val btnPurchase20Can: View = itemView.findViewById(R.id.ll_purchase_20_can)
fun bind(onAction: (ChatQuotaNoticeAction) -> Unit) {
btnRewardedAd.setOnClickListener { onAction(ChatQuotaNoticeAction.REWARDED_AD) }
btnPurchase10Can.setOnClickListener { onAction(ChatQuotaNoticeAction.PURCHASE_10_CAN) }
btnPurchase20Can.setOnClickListener { onAction(ChatQuotaNoticeAction.PURCHASE_20_CAN) }
}

View File

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

View File

@@ -4,7 +4,11 @@
package kr.co.vividnext.sodalive.chat.talk.room.db
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
@Dao

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ import kr.co.vividnext.sodalive.R
class CreatorFollowNotifyFragment(
private val onClickNotifyAll: () -> Unit,
private val onClickNotifyNone: () -> Unit,
private val onClickUnFollow: () -> Unit,
private val onClickUnFollow: () -> Unit
) : BottomSheetDialogFragment() {
override fun onCreateView(
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.detail.SeriesDetailActivity
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.SodaDialog
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
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.ItemCreatorCommunityBinding
import kr.co.vividnext.sodalive.dialog.MemberProfileDialog
@@ -138,7 +138,7 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
binding.ivMenu.setOnClickListener {
showOptionMenu(
this,
binding.ivMenu,
binding.ivMenu
)
}
@@ -468,7 +468,7 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
viewModel.modifyCheers(
cheersId = cheersId,
creatorId = userId,
cheersContent = content,
cheersContent = content
)
}
},

View File

@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
data class PutModifyCheersRequest(
@SerializedName("cheersId") val cheersId: Long,
@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())
.subscribe({}, {})
)
}
fun registerComment(
@@ -282,49 +281,48 @@ class CreatorCommunityAllViewModel(
)
)
}
}
fun updateCommunityPostFixed(postId: Long, isFixed: Boolean) {
if (_isLoading.value == true) return
_isLoading.value = true
if (_isLoading.value == true) return
_isLoading.value = true
compositeDisposable.add(
repository.updateCommunityPostFixed(
postId = postId,
isFixed = isFixed,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
// 목록을 초기화하고 재조회하여 최신 고정 상태를 반영한다.
page = 1
isLast = false
getCommunityPostList()
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
SodaLiveApplicationHolder.get()
.getString(R.string.common_error_unknown)
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
SodaLiveApplicationHolder.get()
.getString(R.string.common_error_unknown)
)
}
)
compositeDisposable.add(
repository.updateCommunityPostFixed(
postId = postId,
isFixed = isFixed,
token = "Bearer ${SharedPreferenceManager.token}"
)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
// 목록을 초기화하고 재조회하여 최신 고정 상태를 반영한다.
page = 1
isLast = false
getCommunityPostList()
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
SodaLiveApplicationHolder.get()
.getString(R.string.common_error_unknown)
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
SodaLiveApplicationHolder.get()
.getString(R.string.common_error_unknown)
)
}
)
)
}
}

View File

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

View File

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

View File

@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
data class PurchasePostRequest(
@SerializedName("postId") val postId: Long,
@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 creatorId: Long,
private val binding: ItemCommunityPostCommentReplyBinding,
private val showOptionMenu: (
Context, View, Long, Long, Long, onClickModify: () -> Unit
) -> Unit,
private val showOptionMenu: (Context, View, Long, Long, Long, onClickModify: () -> Unit) -> Unit,
private val modifyComment: (Long, String) -> Unit,
private val onClickProfile: (Long) -> Unit
) : CreatorCommunityCommentReplyViewHolder(binding) {
@@ -178,5 +176,4 @@ class CreatorCommunityCommentReplyItemViewHolder(
binding.ivMenu.visibility = View.GONE
}
}
}

View File

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

View File

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

View File

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

View File

@@ -103,7 +103,7 @@ class UserProfileFantalkAllViewActivity : BaseActivity<ActivityUserProfileFantal
viewModel.modifyCheers(
cheersId = cheersId,
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.dialog.MemberProfileDialog
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.moneyFormat
import org.koin.android.ext.android.inject

View File

@@ -1,8 +1,6 @@
package kr.co.vividnext.sodalive.following
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) {
fun getFollowedCreatorAllList(

View File

@@ -66,7 +66,6 @@ class CreatorRankingAdapter(
binding.tvFollow.text = context.getString(R.string.screen_home_following)
binding.tvFollow.setBackgroundResource(R.drawable.bg_round_corner_999_455a64)
binding.tvFollow.setTextColor(context.getColor(R.color.white))
} else {
binding.tvFollow.text = context.getString(R.string.screen_home_follow)
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
class DayOfWeekAdapter(
private val context: Context,
private val onClickItem: (SeriesPublishedDaysOfWeek) -> Unit,
context: Context,
private val onClickItem: (SeriesPublishedDaysOfWeek) -> Unit
) : RecyclerView.Adapter<DayOfWeekAdapter.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_sat_short), dayOfWeek = SeriesPublishedDaysOfWeek.SAT),
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(
private val itemSquareSizePx: Int? = null,
private val onClickItem: (Long) -> Unit,
private val onClickItem: (Long) -> Unit
) : RecyclerView.Adapter<HomeContentAdapter.ViewHolder>() {
private val items = mutableListOf<AudioContentMainItem>()

View File

@@ -149,7 +149,6 @@ class LiveViewModel(
fun getSummary() {
if (!_isLoading.value!!) {
if (SharedPreferenceManager.token.isNotBlank()) {
getFollowedChannelList()
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
class LiveNowAdapter(
private val onClick: (GetRoomListResponse) -> Unit,
private val onClick: (GetRoomListResponse) -> Unit
) : RecyclerView.Adapter<LiveNowAdapter.ViewHolder>() {
var items = mutableListOf<GetRoomListResponse>()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,10 +3,21 @@ package kr.co.vividnext.sodalive.live.room
import com.google.gson.annotations.SerializedName
enum class LiveRoomRequestType {
@SerializedName("INVITE_SPEAKER") INVITE_SPEAKER,
@SerializedName("CHANGE_LISTENER") CHANGE_LISTENER,
@SerializedName("KICK_OUT") KICK_OUT,
@SerializedName("SET_MANAGER") SET_MANAGER,
@SerializedName("RELEASE_MANAGER") RELEASE_MANAGER,
@SerializedName("NO_CHATTING") NO_CHATTING,
@SerializedName("INVITE_SPEAKER")
INVITE_SPEAKER,
@SerializedName("CHANGE_LISTENER")
CHANGE_LISTENER,
@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
enum class LiveRoomStatus {
@SerializedName("NOW") NOW,
@SerializedName("RESERVATION") RESERVATION
@SerializedName("NOW")
NOW,
@SerializedName("RESERVATION")
RESERVATION
}

View File

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

View File

@@ -7,5 +7,5 @@ import java.util.TimeZone
@Keep
data class StartLiveRequest(
@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",
year,
monthOfYear + 1,
dayOfMonth,
dayOfMonth
)
viewModel.setReservationDate(
String.format(
@@ -136,8 +136,10 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
context = this,
excludeGif = true,
config = ImagePickerCropper.Config(
aspectX = 2f, aspectY = 3.8f,
maxWidth = 1080, maxHeight = 2052,
aspectX = 2f,
aspectY = 3.8f,
maxWidth = 1080,
maxHeight = 2052,
compressFormat = Bitmap.CompressFormat.JPEG,
compressQuality = 90
),

View File

@@ -1,12 +1,12 @@
package kr.co.vividnext.sodalive.live.room.create
import android.net.Uri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
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.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.LiveRoomType
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
import kr.co.vividnext.sodalive.R
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody

View File

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

View File

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

View File

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

View File

@@ -7,5 +7,5 @@ import com.google.gson.annotations.SerializedName
data class LiveRoomLikeHeartRequest(
@SerializedName("roomId") val roomId: Long,
@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 kr.co.vividnext.sodalive.R
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.databinding.ActivityMenuConfigBinding
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel

View File

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

View File

@@ -28,7 +28,7 @@ class LiveRoomUserProfileDialog(
private val onClickChangeListener: (Long) -> Unit,
private val onClickKickOut: (Long) -> 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 dialogView = DialogLiveRoomUserProfileBinding.inflate(layoutInflater)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,8 +5,8 @@ import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.R
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.ToastMessage
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.NoticeRepository
import kr.co.vividnext.sodalive.user.UserRepository
import kr.co.vividnext.sodalive.R
class MyPageViewModel(
private val userRepository: UserRepository,

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,12 @@
package kr.co.vividnext.sodalive.mypage.alarm.select_audio_content
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
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.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,10 +5,10 @@ import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
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.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.ToastMessage
import kr.co.vividnext.sodalive.mypage.can.CanRepository
class CanCouponViewModel(private val repository: CanRepository) : BaseViewModel() {
@@ -49,7 +49,6 @@ class CanCouponViewModel(private val repository: CanRepository) : BaseViewModel(
_toastLiveData.value = it.message?.let { message ->
ToastMessage(message = message)
} ?: 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
enum class PaymentGateway {
@SerializedName("PG") PG,
@SerializedName("GOOGLE_IAP") GOOGLE_IAP
@SerializedName("PG")
PG,
@SerializedName("GOOGLE_IAP")
GOOGLE_IAP
}

View File

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

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