Compare commits
198 Commits
926e7eabd3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c44f3d66c | |||
| 43b6496289 | |||
| 30a4a9ffc7 | |||
| 84cd9bbf41 | |||
| 75480e7a71 | |||
| 8b8b384b29 | |||
| 02cd583f56 | |||
| 3c21a764ae | |||
| d20d6aa971 | |||
| f50f987282 | |||
| f508d847ea | |||
| e86e5a1c3d | |||
| 26eae18edc | |||
| b5cfdc83e3 | |||
| dd4a5167a9 | |||
| d592038edd | |||
| a106548f87 | |||
| 49f6fe592c | |||
| f600d10e8b | |||
| a1aa9c2243 | |||
| 3a62ed8463 | |||
| 6755c8ae99 | |||
| b17e250665 | |||
| 074c945008 | |||
| 16aa946621 | |||
| 831533b4f6 | |||
| de90f771c1 | |||
| 4373452478 | |||
| 0b3042d29e | |||
| cc23992d2e | |||
| 3a49158243 | |||
| d1509d4359 | |||
| c223b3b8a4 | |||
| b582545991 | |||
| fe79431744 | |||
| 1c5fbb38f1 | |||
| dadf3a519d | |||
| ddc949f9d9 | |||
| 6973816b8c | |||
| e5b99be9c4 | |||
| 5ff92980a2 | |||
| 2fda80d602 | |||
| 7e12aa8720 | |||
| d5d3444c3a | |||
| a6601dcea1 | |||
| 4d51806cee | |||
| 98db0b657e | |||
| 9f70bcd3eb | |||
| cc8d061b51 | |||
| 1b952054fb | |||
| 2c609836fd | |||
| da36964347 | |||
| 47b9c3dec2 | |||
| 17a9a2f601 | |||
| c43b9cdbd8 | |||
| 7ec4e09fb4 | |||
| 862cec8375 | |||
| 2d4956ae25 | |||
| beadc7d4c4 | |||
| 2633912e93 | |||
| 6cbc751f0c | |||
| 2a499eaf41 | |||
| 9244a00713 | |||
| 79eadbd39d | |||
| c389d1945d | |||
| 2d8f8cab81 | |||
| f799449d54 | |||
| 9da6c7381d | |||
| ced226c3ef | |||
| f69a9a6ee0 | |||
| 94d2773d1e | |||
| bc5fd46671 | |||
| a00745dd38 | |||
| 01908c84d3 | |||
| 39959c3f47 | |||
| 53cc96b84d | |||
| 1aeb336898 | |||
| 1715c591a5 | |||
| eb41506bf4 | |||
| 9fdcd49c98 | |||
| 8538f014aa | |||
| 848cd03068 | |||
| 4debf4654d | |||
| 6ed2be4173 | |||
| 557de11bb3 | |||
| 97d917e35d | |||
| 9b2666e671 | |||
| c883556dbf | |||
| 5bcd0f64d2 | |||
| d53167c48e | |||
| c2b212c1ce | |||
| d1510b8ba3 | |||
| 273feef017 | |||
| 11a42bf8a1 | |||
| d436efdc66 | |||
| 30f7bd7556 | |||
| 62007862a5 | |||
| 2786e45c75 | |||
| 46c3a02655 | |||
| 5ca07ef049 | |||
| c715c890b5 | |||
| 9925a954d1 | |||
| 0c111aaf47 | |||
| 2a2315ecbc | |||
| 17f77351ad | |||
| abd084cff8 | |||
| e77d0a1d5b | |||
| 991d70f22b | |||
| f477b563a2 | |||
| 63a7c28e96 | |||
| 2b2cb66dfc | |||
| bf5cc1d7bb | |||
| bbf94b8a0d | |||
| 951d7cf561 | |||
| 40337c9590 | |||
| 79a6ecdc0b | |||
| 33f42e62b6 | |||
| 1b660fe542 | |||
| d0539b00b0 | |||
| e089ef6f8a | |||
| a8a70df304 | |||
| a207a8f840 | |||
| 6c85f81f98 | |||
| 6a88019851 | |||
| 379263ec87 | |||
| f3d610633c | |||
| 335db22680 | |||
| b71bb43dd6 | |||
| dace1416c5 | |||
| 1baf3ac7e0 | |||
| f3a4578a66 | |||
| b745c3d675 | |||
| f993d343a9 | |||
| 1ba7846f4c | |||
| da26b65a73 | |||
| 04dcb3f09f | |||
| 6916459c9e | |||
| 643fb9fa8e | |||
| f9160ccb02 | |||
| e2b166de11 | |||
| 89f3511864 | |||
| 3271c9e142 | |||
| 1f1069b05d | |||
| a325a1bbe9 | |||
| 255e6dc67d | |||
| b2685959ad | |||
| 536effab40 | |||
| 81537b1e96 | |||
| e691f78e2e | |||
| 6125b9f7d1 | |||
| d10373d265 | |||
| 5c1eb6204a | |||
| c3884ea917 | |||
| bc3fa9546b | |||
| ea66b6da1e | |||
| c9e47214ea | |||
| c04d603ac0 | |||
| 9e524d626a | |||
| e0dc0c2cb7 | |||
| cbd4f04f1b | |||
| eeb777fa01 | |||
| 9272db913c | |||
| ff4c0104c5 | |||
| 31ce0629d2 | |||
| 6ab7878221 | |||
| ddc18386fb | |||
| 809ed5ce23 | |||
| 8e086c4888 | |||
| bc6443e5bd | |||
| 8d205690b3 | |||
| 25af312d91 | |||
| b020b8096b | |||
| 87f6bec138 | |||
| 34266abd90 | |||
| e8e8a63d6d | |||
| d61867af4e | |||
| daeb0f75a2 | |||
| 6c497ed1d2 | |||
| 7b975ec54d | |||
| e3d81792bf | |||
| 5b9b7eafa7 | |||
| 5489c86cdc | |||
| b1f605fda4 | |||
| f9030ffd13 | |||
| f74f5a534d | |||
| 3f3bacebd9 | |||
| 2041036530 | |||
| 81a2b541a0 | |||
| e6cb94179b | |||
| 658b4f0a9d | |||
| 05cbbdfddc | |||
| 3c66ea986f | |||
| d1a0cf27a7 | |||
| 1aef4b1d89 | |||
| c745481ab2 | |||
| 7e281a15a5 | |||
| c5e94bbc92 | |||
| 61fe8a9fee |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
14
AGENTS.md
14
AGENTS.md
@@ -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`를 참고한다.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -114,6 +114,18 @@
|
||||
<activity android:name=".v2.main.MainV2Activity" />
|
||||
<activity android:name=".v2.main.content.overview.ContentOverviewActivity" />
|
||||
<activity android:name=".v2.creator.channel.CreatorChannelActivity" />
|
||||
<activity
|
||||
android:name=".v2.creator.channel.community.detail.CreatorChannelCommunityDetailActivity"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
|
||||
<activity
|
||||
android:name=".v2.creator.channel.community.detail.reply.CreatorChannelCommunityReplyActivity"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
|
||||
<activity
|
||||
android:name=".v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteActivity"
|
||||
android:windowSoftInputMode="stateVisible|adjustResize" />
|
||||
<activity
|
||||
android:name=".v2.creator.channel.fantalk.detail.CreatorChannelFanTalkDetailActivity"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
|
||||
<activity android:name=".v2.live.onair.HomeOnAirLiveActivity" />
|
||||
<activity
|
||||
android:name=".v2.main.chat.dm.DmChatRoomActivity"
|
||||
|
||||
@@ -361,7 +361,9 @@ class Agora(
|
||||
|
||||
fun deInitRtmClient(rtmEventListener: RtmEventListener) {
|
||||
rtmClient?.removeEventListener(rtmEventListener)
|
||||
rtmClient?.unsubscribe(roomChannelName, object : ResultCallback<Void> {
|
||||
rtmClient?.unsubscribe(
|
||||
roomChannelName,
|
||||
object : ResultCallback<Void> {
|
||||
override fun onSuccess(responseInfo: Void?) {
|
||||
Logger.e("RTM unsubscribe - $roomChannelName")
|
||||
roomChannelName = null
|
||||
@@ -371,7 +373,8 @@ class Agora(
|
||||
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
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ class SodaLiveApp : Application(), DefaultLifecycleObserver {
|
||||
applicationContext,
|
||||
BuildConfig.NOTIFLY_PROJECT_ID,
|
||||
BuildConfig.NOTIFLY_USERNAME,
|
||||
BuildConfig.NOTIFLY_PASSWORD,
|
||||
BuildConfig.NOTIFLY_PASSWORD
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@file:Suppress("ktlint:package-name", "ktlint:standard:package-name")
|
||||
|
||||
package kr.co.vividnext.sodalive.audio_content
|
||||
|
||||
import android.app.NotificationChannel
|
||||
@@ -471,9 +473,17 @@ class AudioContentPlayService :
|
||||
}
|
||||
|
||||
private fun updateNotification() {
|
||||
val intent = Intent(this, MainV2Activity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
val intent = Intent(this, MainV2Activity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
putExtra(
|
||||
MainV2Activity.EXTRA_AUDIO_NOTIFICATION_ROUTE,
|
||||
MainV2Activity.ROUTE_AUDIO_DETAIL
|
||||
)
|
||||
contentId?.let {
|
||||
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it)
|
||||
}
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -10,5 +10,5 @@ enum class PurchaseOption {
|
||||
BUY_ONLY,
|
||||
|
||||
@SerializedName("RENT_ONLY")
|
||||
RENT_ONLY,
|
||||
RENT_ONLY
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,10 +25,15 @@ class AudioContentMainNewContentThemeAdapter(
|
||||
fun bind(theme: String) {
|
||||
if (
|
||||
theme == selectedTheme ||
|
||||
(selectedTheme == "" && theme == SodaLiveApplicationHolder.get()
|
||||
(
|
||||
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.screen_home_sort_revenue
|
||||
)
|
||||
)
|
||||
) {
|
||||
binding.tvTheme.setBackgroundResource(
|
||||
R.drawable.bg_round_corner_16_7_transparent_3bb9f1
|
||||
|
||||
@@ -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
|
||||
),
|
||||
|
||||
@@ -28,7 +28,7 @@ class AudioContentOrderConfirmDialog(
|
||||
orderType: OrderType,
|
||||
price: Int,
|
||||
isAvailableUsePoint: Boolean,
|
||||
confirmButtonClick: () -> Unit,
|
||||
confirmButtonClick: () -> Unit
|
||||
) {
|
||||
|
||||
private val alertDialog: AlertDialog
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -429,7 +429,6 @@ class AudioContentPlayerFragment(
|
||||
mediaController?.seekTo(progress.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
updateMediaMetadata(mediaController?.currentMediaItem?.mediaMetadata)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@file:Suppress("ktlint:package-name", "ktlint:standard:package-name")
|
||||
|
||||
package kr.co.vividnext.sodalive.audio_content.player
|
||||
|
||||
import android.app.PendingIntent
|
||||
@@ -155,6 +157,10 @@ class AudioContentPlayerService : MediaSessionService() {
|
||||
private fun initMediaSession() {
|
||||
val contextIntent = Intent(applicationContext, MainV2Activity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
putExtra(
|
||||
MainV2Activity.EXTRA_AUDIO_NOTIFICATION_ROUTE,
|
||||
MainV2Activity.ROUTE_AUDIO_PLAYER
|
||||
)
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
applicationContext,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package kr.co.vividnext.sodalive.audio_content.playlist.modify
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.audio_content.playlist.create.PlaylistContentIdAndOrder
|
||||
|
||||
@Keep
|
||||
data class UpdatePlaylistRequest(
|
||||
@SerializedName("title") val title: String? = null,
|
||||
@SerializedName("desc") val desc: String? = null,
|
||||
|
||||
@@ -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?>()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,7 +5,6 @@ import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
|
||||
import kr.co.vividnext.sodalive.audio_content.series.main.by_genre.GetSeriesGenreListResponse
|
||||
import kr.co.vividnext.sodalive.audio_content.series.main.home.SeriesHomeResponse
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.home.SeriesPublishedDaysOfWeek
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Query
|
||||
@@ -23,7 +22,7 @@ interface SeriesMainApi {
|
||||
|
||||
@GET("/audio-content/series/main/day-of-week")
|
||||
fun getDayOfWeekSeriesList(
|
||||
@Query("dayOfWeek") dayOfWeek: SeriesPublishedDaysOfWeek,
|
||||
@Query("dayOfWeek") dayOfWeek: String,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
|
||||
@@ -19,7 +19,7 @@ class SeriesMainRepository(
|
||||
size: Int,
|
||||
token: String
|
||||
) = api.getDayOfWeekSeriesList(
|
||||
dayOfWeek = dayOfWeek,
|
||||
dayOfWeek = dayOfWeek.queryValue,
|
||||
page = page - 1,
|
||||
size = size,
|
||||
authHeader = token
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -43,7 +43,8 @@ class CharacterAdapter(
|
||||
translationY = fm.descent
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
binding.tvRanking.visibility = View.GONE
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,7 +47,10 @@ 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(
|
||||
if (cid == -1L) {
|
||||
null
|
||||
} else {
|
||||
CharacterCommentResponse(
|
||||
commentId = cid,
|
||||
memberId = it.getLong(EXTRA_ORIGINAL_MEMBER_ID),
|
||||
memberProfileImage = it.getString(EXTRA_ORIGINAL_MEMBER_PROFILE_IMAGE) ?: "",
|
||||
@@ -58,6 +60,7 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
|
||||
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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -434,5 +434,4 @@ class CharacterDetailFragment : BaseFragment<FragmentCharacterDetailBinding>(
|
||||
binding.tvPersonalityContent.maxLines = 3
|
||||
binding.tvPersonalityContent.ellipsize = TextUtils.TruncateAt.END
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ data class CharacterDetailResponse(
|
||||
enum class CharacterType {
|
||||
@SerializedName("Clone")
|
||||
CLONE,
|
||||
|
||||
@SerializedName("Character")
|
||||
CHARACTER
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ class OtherCharacterAdapter(
|
||||
transformations(RoundedCornersTransformation(16f.dpToPx()))
|
||||
}
|
||||
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
onItemClick?.invoke(item)
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package kr.co.vividnext.sodalive.chat.original
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
data class OriginalWorkListResponse(
|
||||
val totalCount: Long,
|
||||
val content: List<OriginalWorkListItemResponse>
|
||||
@SerializedName("totalCount") val totalCount: Long,
|
||||
@SerializedName("content") val content: List<OriginalWorkListItemResponse>
|
||||
)
|
||||
|
||||
@Keep
|
||||
data class OriginalWorkListItemResponse(
|
||||
val id: Long,
|
||||
val imageUrl: String?,
|
||||
val title: String,
|
||||
val contentType: String
|
||||
@SerializedName("id") val id: Long,
|
||||
@SerializedName("imageUrl") val imageUrl: String?,
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("contentType") val contentType: String
|
||||
)
|
||||
|
||||
@@ -85,7 +85,6 @@ class OriginalWorkDetailActivity : BaseActivity<ActivityOriginalWorkDetailBindin
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ import androidx.annotation.Keep
|
||||
enum class MessageStatus {
|
||||
/** 전송 중 */
|
||||
SENDING,
|
||||
|
||||
/** 전송 완료 */
|
||||
SENT,
|
||||
|
||||
/** 전송 실패 */
|
||||
FAILED
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(
|
||||
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)
|
||||
|
||||
@@ -2,7 +2,7 @@ package kr.co.vividnext.sodalive.common
|
||||
|
||||
import android.content.Context
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import java.text.ParseException
|
||||
import java.text.ParsePosition
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
@@ -10,8 +10,9 @@ import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
fun formatUtcRelativeTimeText(context: Context, utcText: String?): String {
|
||||
val pastMillis = parseServerUtcToMillis(utcText)
|
||||
val parsedTime = parseServerUtcToMillis(utcText)
|
||||
?: return context.getString(R.string.character_comment_time_just_now)
|
||||
val pastMillis = parsedTime.millis
|
||||
|
||||
val nowMillis = System.currentTimeMillis()
|
||||
var diff = nowMillis - pastMillis
|
||||
@@ -21,6 +22,10 @@ fun formatUtcRelativeTimeText(context: Context, utcText: String?): String {
|
||||
val hour = 60 * minute
|
||||
val day = 24 * hour
|
||||
|
||||
if (parsedTime.isIsoText && diff < RECENT_ISO_TIME_GRACE_MILLIS) {
|
||||
return context.getString(R.string.character_comment_time_just_now)
|
||||
}
|
||||
|
||||
if (diff < minute) {
|
||||
return context.getString(R.string.character_comment_time_just_now)
|
||||
}
|
||||
@@ -75,19 +80,25 @@ class AndroidUtcRelativeTimeTextFormatter(context: Context) : UtcRelativeTimeTex
|
||||
override fun format(utcText: String?): String = formatUtcRelativeTimeText(applicationContext, utcText)
|
||||
}
|
||||
|
||||
private fun parseServerUtcToMillis(utcText: String?): Long? {
|
||||
private fun parseServerUtcToMillis(utcText: String?): ParsedServerTime? {
|
||||
if (utcText.isNullOrBlank()) return null
|
||||
|
||||
val value = utcText.trim()
|
||||
val value = utcText.trim().normalizeIsoFraction()
|
||||
if (value.all { it.isDigit() }) {
|
||||
return try {
|
||||
value.toLong()
|
||||
val number = value.toLong()
|
||||
ParsedServerTime(
|
||||
millis = if (value.length <= EPOCH_SECONDS_MAX_LENGTH) number * 1000 else number,
|
||||
isIsoText = false
|
||||
)
|
||||
} catch (_: NumberFormatException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val patterns = listOf(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
|
||||
"yyyy-MM-dd'T'HH:mm:ssXXX",
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
|
||||
"yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
"yyyy-MM-dd'T'HH:mm:ss",
|
||||
@@ -99,11 +110,26 @@ private fun parseServerUtcToMillis(utcText: String?): Long? {
|
||||
try {
|
||||
val dateFormat = SimpleDateFormat(pattern, Locale.US)
|
||||
dateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
val parsed: Date? = dateFormat.parse(value)
|
||||
if (parsed != null) return parsed.time
|
||||
} catch (_: ParseException) {
|
||||
val position = ParsePosition(0)
|
||||
val parsed: Date? = dateFormat.parse(value, position)
|
||||
if (parsed != null && position.index == value.length) {
|
||||
return ParsedServerTime(millis = parsed.time, isIsoText = true)
|
||||
}
|
||||
} catch (_: RuntimeException) {
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun String.normalizeIsoFraction(): String {
|
||||
return replace(Regex("""\.(\d{3})\d+"""), ".$1")
|
||||
}
|
||||
|
||||
private data class ParsedServerTime(
|
||||
val millis: Long,
|
||||
val isIsoText: Boolean
|
||||
)
|
||||
|
||||
private const val EPOCH_SECONDS_MAX_LENGTH = 10
|
||||
private const val RECENT_ISO_TIME_GRACE_MILLIS = 10 * 60_000L
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.di
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.orhanobut.logger.Logger
|
||||
import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.agora.v2v.V2vApi
|
||||
import kr.co.vividnext.sodalive.agora.v2v.V2vRepository
|
||||
@@ -181,10 +182,14 @@ import kr.co.vividnext.sodalive.user.signup.SignUpViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.audio.CreatorChannelAudioViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.CreatorChannelCommunityDetailViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.reply.CreatorChannelCommunityReplyViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelApi
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.detail.CreatorChannelFanTalkDetailViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesViewModel
|
||||
import kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveViewModel
|
||||
@@ -345,7 +350,14 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
||||
single { ApiBuilder().build(get(), CharacterCommentApi::class.java) }
|
||||
single { ApiBuilder().build(get(), OriginalWorkApi::class.java) }
|
||||
single { ApiBuilder().build(get<Retrofit>(named("agoraRetrofit")), V2vApi::class.java) }
|
||||
single { DmChatSocketClient(okHttpClient = get(), gson = get(), baseUrl = baseUrl) }
|
||||
single {
|
||||
DmChatSocketClient(
|
||||
okHttpClient = get(),
|
||||
gson = get(),
|
||||
baseUrl = baseUrl,
|
||||
rawJsonLogger = { rawJson -> Logger.d("DM WebSocket raw JSON: $rawJson") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val viewModelModule = module {
|
||||
@@ -452,7 +464,11 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
||||
viewModel { CreatorChannelAudioViewModel(get()) }
|
||||
viewModel { CreatorChannelSeriesViewModel(get()) }
|
||||
viewModel { CreatorChannelCommunityViewModel(get(), get()) }
|
||||
viewModel { CreatorChannelCommunityDetailViewModel(get(), get(), get()) }
|
||||
viewModel { CreatorChannelCommunityReplyViewModel(get(), get(), get()) }
|
||||
viewModel { CreatorChannelFanTalkViewModel(get(), get()) }
|
||||
viewModel { CreatorChannelFanTalkDetailViewModel(get(), get()) }
|
||||
viewModel { CreatorChannelFanTalkWriteViewModel(get()) }
|
||||
viewModel { CreatorChannelDonationViewModel(get(), get()) }
|
||||
viewModel { PushNotificationListViewModel(get()) }
|
||||
viewModel { CharacterTabViewModel(get()) }
|
||||
|
||||
@@ -18,7 +18,7 @@ open class LiveDialog(
|
||||
confirmButtonTitle: String,
|
||||
confirmButtonClick: () -> Unit,
|
||||
cancelButtonTitle: String = "",
|
||||
cancelButtonClick: (() -> Unit)? = null,
|
||||
cancelButtonClick: (() -> Unit)? = null
|
||||
) {
|
||||
|
||||
private val alertDialog: AlertDialog
|
||||
|
||||
@@ -13,6 +13,7 @@ import kr.co.vividnext.sodalive.explorer.profile.channel_donation.PostChannelDon
|
||||
import kr.co.vividnext.sodalive.explorer.profile.donation.GetDonationAllResponse
|
||||
import kr.co.vividnext.sodalive.explorer.profile.follow.GetFollowerListResponse
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
@@ -73,13 +74,13 @@ interface ExplorerApi {
|
||||
fun writeCheers(
|
||||
@Body request: PostWriteCheersRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
): Single<ApiResponse<CreatorChannelFanTalkReplyResponse>>
|
||||
|
||||
@PUT("/explorer/profile/cheers")
|
||||
fun modifyCheers(
|
||||
@Body request: PutModifyCheersRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
): Single<ApiResponse<CreatorChannelFanTalkReplyResponse>>
|
||||
|
||||
@POST("/explorer/profile/channel-donation")
|
||||
fun postChannelDonation(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -149,7 +149,6 @@ class CreatorCommunityAllViewModel(
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({}, {})
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
fun registerComment(
|
||||
@@ -282,7 +281,6 @@ class CreatorCommunityAllViewModel(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun updateCommunityPostFixed(postId: Long, isFixed: Boolean) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,7 +16,7 @@ class PurchaseCommunityPostDialog(
|
||||
activity: Activity,
|
||||
layoutInflater: LayoutInflater,
|
||||
can: Int,
|
||||
confirmButtonClick: () -> Unit,
|
||||
confirmButtonClick: () -> Unit
|
||||
) {
|
||||
|
||||
private val alertDialog: AlertDialog
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
),
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.google.gson.annotations.SerializedName
|
||||
enum class DonationRankingPeriod {
|
||||
@SerializedName("WEEKLY")
|
||||
WEEKLY,
|
||||
|
||||
@SerializedName("CUMULATIVE")
|
||||
CUMULATIVE
|
||||
}
|
||||
|
||||
@@ -19,5 +19,5 @@ data class GetDonationAllResponse(
|
||||
@SerializedName("donationRankingPeriod")
|
||||
val donationRankingPeriod: DonationRankingPeriod? = null,
|
||||
@SerializedName("userDonationRanking")
|
||||
val userDonationRanking: List<UserDonationRankingResponse>,
|
||||
val userDonationRanking: List<UserDonationRankingResponse>
|
||||
)
|
||||
|
||||
@@ -103,7 +103,7 @@ class UserProfileFantalkAllViewActivity : BaseActivity<ActivityUserProfileFantal
|
||||
viewModel.modifyCheers(
|
||||
cheersId = cheersId,
|
||||
creatorId = userId,
|
||||
cheersContent = content,
|
||||
cheersContent = content
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
package kr.co.vividnext.sodalive.home
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
/**
|
||||
* 콘텐츠 랭킹 정렬 기준
|
||||
*/
|
||||
enum class ContentRankingSortType {
|
||||
@Keep
|
||||
enum class ContentRankingSortType(val queryValue: String) {
|
||||
// 매출
|
||||
REVENUE,
|
||||
@SerializedName("REVENUE")
|
||||
REVENUE("REVENUE"),
|
||||
|
||||
// 판매량
|
||||
SALES_COUNT,
|
||||
@SerializedName("SALES_COUNT")
|
||||
SALES_COUNT("SALES_COUNT"),
|
||||
|
||||
// 댓글 수
|
||||
COMMENT_COUNT,
|
||||
@SerializedName("COMMENT_COUNT")
|
||||
COMMENT_COUNT("COMMENT_COUNT"),
|
||||
|
||||
// 좋아요 수
|
||||
LIKE_COUNT
|
||||
@SerializedName("LIKE_COUNT")
|
||||
LIKE_COUNT("LIKE_COUNT")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
// 요일 숫자에 맞춰 배열
|
||||
|
||||
@@ -23,7 +23,7 @@ interface HomeApi {
|
||||
|
||||
@GET("/api/home/day-of-week-series")
|
||||
fun getDayOfWeekSeriesList(
|
||||
@Query("dayOfWeek") dayOfWeek: SeriesPublishedDaysOfWeek,
|
||||
@Query("dayOfWeek") dayOfWeek: String,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<GetSeriesListResponse.SeriesListItem>>>
|
||||
|
||||
@@ -34,7 +34,7 @@ interface HomeApi {
|
||||
|
||||
@GET("/api/home/content-ranking")
|
||||
fun getContentRankingBySort(
|
||||
@Query("sort") sort: ContentRankingSortType,
|
||||
@Query("sort") sort: String,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<GetAudioContentRankingItem>>>
|
||||
}
|
||||
|
||||
@@ -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>()
|
||||
|
||||
|
||||
@@ -14,9 +14,10 @@ class HomeRepository(private val api: HomeApi) {
|
||||
)
|
||||
|
||||
fun getDayOfWeekSeriesList(
|
||||
dayOfWeek: SeriesPublishedDaysOfWeek, token: String
|
||||
dayOfWeek: SeriesPublishedDaysOfWeek,
|
||||
token: String
|
||||
) = api.getDayOfWeekSeriesList(
|
||||
dayOfWeek = dayOfWeek,
|
||||
dayOfWeek = dayOfWeek.queryValue,
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -28,7 +29,7 @@ class HomeRepository(private val api: HomeApi) {
|
||||
sortType: ContentRankingSortType,
|
||||
token: String
|
||||
) = api.getContentRankingBySort(
|
||||
sort = sortType,
|
||||
sort = sortType.queryValue,
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
package kr.co.vividnext.sodalive.home
|
||||
|
||||
enum class SeriesPublishedDaysOfWeek {
|
||||
SUN, MON, TUE, WED, THU, FRI, SAT, RANDOM
|
||||
import androidx.annotation.Keep
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@Keep
|
||||
enum class SeriesPublishedDaysOfWeek(val queryValue: String) {
|
||||
@SerializedName("SUN")
|
||||
SUN("SUN"),
|
||||
|
||||
@SerializedName("MON")
|
||||
MON("MON"),
|
||||
|
||||
@SerializedName("TUE")
|
||||
TUE("TUE"),
|
||||
|
||||
@SerializedName("WED")
|
||||
WED("WED"),
|
||||
|
||||
@SerializedName("THU")
|
||||
THU("THU"),
|
||||
|
||||
@SerializedName("FRI")
|
||||
FRI("FRI"),
|
||||
|
||||
@SerializedName("SAT")
|
||||
SAT("SAT"),
|
||||
|
||||
@SerializedName("RANDOM")
|
||||
RANDOM("RANDOM")
|
||||
}
|
||||
|
||||
@@ -149,7 +149,6 @@ class LiveViewModel(
|
||||
|
||||
fun getSummary() {
|
||||
if (!_isLoading.value!!) {
|
||||
|
||||
if (SharedPreferenceManager.token.isNotBlank()) {
|
||||
getFollowedChannelList()
|
||||
getLatestPostListFromCreatorsYouFollow()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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>()
|
||||
|
||||
@@ -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>()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -90,7 +90,6 @@ class LiveReservationStatusViewModel(
|
||||
_isLoading.value = false
|
||||
},
|
||||
{
|
||||
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}")
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -20,7 +20,7 @@ class LiveRoomPasswordDialog(
|
||||
activity: Activity,
|
||||
layoutInflater: LayoutInflater,
|
||||
can: Int,
|
||||
confirmButtonClick: (String) -> Unit,
|
||||
confirmButtonClick: (String) -> Unit
|
||||
) {
|
||||
|
||||
private val alertDialog: AlertDialog
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user