Compare commits
5 Commits
0fcd929c6f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4bb91c605a | |||
| 9dfad913bc | |||
| 4815cac49b | |||
| 25d549b06f | |||
| 0c0801561e |
@@ -63,8 +63,8 @@ android {
|
||||
applicationId "kr.co.vividnext.sodalive"
|
||||
minSdk 23
|
||||
targetSdk 35
|
||||
versionCode 231
|
||||
versionName "1.53.0"
|
||||
versionCode 233
|
||||
versionName "1.54.1"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeR
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse
|
||||
import kr.co.vividnext.sodalive.home.AudioContentMainItem
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.http.Body
|
||||
@@ -39,8 +38,6 @@ import retrofit2.http.Query
|
||||
interface AudioContentApi {
|
||||
@GET("/audio-content/all")
|
||||
fun getAllAudioContents(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Query("isFree") isFree: Boolean?,
|
||||
@@ -54,7 +51,6 @@ interface AudioContentApi {
|
||||
fun getAudioContentList(
|
||||
@Query("creator-id") id: Long,
|
||||
@Query("category-id") categoryId: Long,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Query("sort-type") sort: AudioContentViewModel.Sort,
|
||||
@@ -63,8 +59,6 @@ interface AudioContentApi {
|
||||
|
||||
@GET("/audio-content/replay-live")
|
||||
fun getAudioContentReplayLiveList(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Flowable<ApiResponse<List<GetAudioContentMainItem>>>
|
||||
|
||||
@@ -75,8 +69,6 @@ interface AudioContentApi {
|
||||
|
||||
@GET("/audio-content/theme/active")
|
||||
fun getAudioContentActiveThemeList(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("isFree") isFree: Boolean?,
|
||||
@Query("isPointAvailableOnly") isPointAvailableOnly: Boolean?,
|
||||
@Header("Authorization") authHeader: String
|
||||
@@ -85,8 +77,6 @@ interface AudioContentApi {
|
||||
@GET("/audio-content/theme/{id}/content")
|
||||
fun getAudioContentByTheme(
|
||||
@Path("id") id: Long,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Query("sort-type") sort: AudioContentViewModel.Sort,
|
||||
@@ -175,8 +165,6 @@ interface AudioContentApi {
|
||||
@GET("/audio-content/main/new")
|
||||
fun getNewContentOfTheme(
|
||||
@Query("theme") theme: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<GetAudioContentMainItem>>>
|
||||
|
||||
@@ -184,8 +172,6 @@ interface AudioContentApi {
|
||||
fun getNewContentAllOfTheme(
|
||||
@Query("isFree") isFree: Boolean,
|
||||
@Query("theme") theme: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
@@ -205,8 +191,6 @@ interface AudioContentApi {
|
||||
|
||||
@GET("/audio-content/main/theme")
|
||||
fun getNewContentThemeList(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<String>>>
|
||||
|
||||
@@ -225,8 +209,6 @@ interface AudioContentApi {
|
||||
|
||||
@GET("/audio-content/main/curation-list")
|
||||
fun getCurationList(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
|
||||
@@ -6,9 +6,7 @@ import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeRequest
|
||||
import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationRequest
|
||||
import kr.co.vividnext.sodalive.audio_content.order.OrderRequest
|
||||
import kr.co.vividnext.sodalive.audio_content.order.OrderType
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.common.SodaLiveApplicationHolder
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import java.util.TimeZone
|
||||
@@ -27,7 +25,6 @@ class AudioContentRepository(
|
||||
) = api.getAudioContentList(
|
||||
id = id,
|
||||
categoryId = categoryId,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
page = page - 1,
|
||||
size = size,
|
||||
sort = sort,
|
||||
@@ -35,8 +32,6 @@ class AudioContentRepository(
|
||||
)
|
||||
|
||||
fun getAudioContentReplayLiveList(token: String) = api.getAudioContentReplayLiveList(
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -123,16 +118,12 @@ class AudioContentRepository(
|
||||
) = api.getNewContentAllOfTheme(
|
||||
isFree = isFree,
|
||||
theme = theme,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
page = page - 1,
|
||||
size = size,
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun getNewContentThemeList(token: String) = api.getNewContentThemeList(
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -188,8 +179,6 @@ class AudioContentRepository(
|
||||
token: String
|
||||
) = api.getAudioContentByTheme(
|
||||
id = themeId,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
page = page - 1,
|
||||
size = size,
|
||||
sort = sort,
|
||||
@@ -205,8 +194,6 @@ class AudioContentRepository(
|
||||
theme: String? = null,
|
||||
token: String
|
||||
) = api.getAllAudioContents(
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
page = page - 1,
|
||||
size = size,
|
||||
isFree = isFree,
|
||||
@@ -221,8 +208,6 @@ class AudioContentRepository(
|
||||
isPointAvailableOnly: Boolean? = null,
|
||||
token: String
|
||||
) = api.getAudioContentActiveThemeList(
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
isFree = isFree,
|
||||
isPointAvailableOnly = isPointAvailableOnly,
|
||||
authHeader = token
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.audio_content.series.detail.GetSeriesContentListResponse
|
||||
import kr.co.vividnext.sodalive.audio_content.series.detail.GetSeriesDetailResponse
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Path
|
||||
@@ -15,8 +14,6 @@ interface SeriesApi {
|
||||
fun getSeriesList(
|
||||
@Query("creatorId") creatorId: Long?,
|
||||
@Query("sortType") sortType: SeriesListAllViewModel.SeriesSortType,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("isOriginal") isOriginal: Boolean?,
|
||||
@Query("isCompleted") isCompleted: Boolean?,
|
||||
@Query("page") page: Int,
|
||||
@@ -27,14 +24,12 @@ interface SeriesApi {
|
||||
@GET("/audio-content/series/{id}")
|
||||
fun getSeriesDetail(
|
||||
@Path("id") seriesId: Long,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetSeriesDetailResponse>>
|
||||
|
||||
@GET("/audio-content/series/{id}/content")
|
||||
fun getSeriesContentList(
|
||||
@Path("id") seriesId: Long,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Query("sortType") sortType: SeriesListAllViewModel.SeriesSortType,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package kr.co.vividnext.sodalive.audio_content.series
|
||||
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
|
||||
class SeriesRepository(private val api: SeriesApi) {
|
||||
fun getSeriesList(
|
||||
creatorId: Long?,
|
||||
@@ -15,8 +12,6 @@ class SeriesRepository(private val api: SeriesApi) {
|
||||
) = api.getSeriesList(
|
||||
creatorId = creatorId,
|
||||
sortType = sortType,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
isOriginal = isOriginal,
|
||||
isCompleted = isCompleted,
|
||||
page = page - 1,
|
||||
@@ -26,7 +21,6 @@ class SeriesRepository(private val api: SeriesApi) {
|
||||
|
||||
fun getSeriesDetail(seriesId: Long, token: String) = api.getSeriesDetail(
|
||||
seriesId = seriesId,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -38,7 +32,6 @@ class SeriesRepository(private val api: SeriesApi) {
|
||||
token: String
|
||||
) = api.getSeriesContentList(
|
||||
seriesId = seriesId,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
page = page - 1,
|
||||
size = size,
|
||||
sortType = sortType,
|
||||
|
||||
@@ -6,7 +6,6 @@ import kr.co.vividnext.sodalive.audio_content.series.main.by_genre.GetSeriesGenr
|
||||
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 kr.co.vividnext.sodalive.settings.ContentType
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Query
|
||||
@@ -14,23 +13,17 @@ import retrofit2.http.Query
|
||||
interface SeriesMainApi {
|
||||
@GET("/audio-content/series/main")
|
||||
fun fetchHome(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<SeriesHomeResponse>>
|
||||
|
||||
@GET("/audio-content/series/main/recommend")
|
||||
fun getRecommendSeriesList(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<GetSeriesListResponse.SeriesListItem>>>
|
||||
|
||||
@GET("/audio-content/series/main/day-of-week")
|
||||
fun getDayOfWeekSeriesList(
|
||||
@Query("dayOfWeek") dayOfWeek: SeriesPublishedDaysOfWeek,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
@@ -38,16 +31,12 @@ interface SeriesMainApi {
|
||||
|
||||
@GET("/audio-content/series/main/genre-list")
|
||||
fun getGenreList(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<GetSeriesGenreListResponse>>>
|
||||
|
||||
@GET("/audio-content/series/main/list-by-genre")
|
||||
fun getSeriesListByGenre(
|
||||
@Query("genreId") genreId: Long,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
package kr.co.vividnext.sodalive.audio_content.series.main
|
||||
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.home.SeriesPublishedDaysOfWeek
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
|
||||
class SeriesMainRepository(
|
||||
private val api: SeriesMainApi
|
||||
) {
|
||||
fun fetchData(token: String) = api.fetchHome(
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun getRecommendSeriesList(token: String) = api.getRecommendSeriesList(
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -26,16 +20,12 @@ class SeriesMainRepository(
|
||||
token: String
|
||||
) = api.getDayOfWeekSeriesList(
|
||||
dayOfWeek = dayOfWeek,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
page = page - 1,
|
||||
size = size,
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun getGenreList(token: String) = api.getGenreList(
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -46,8 +36,6 @@ class SeriesMainRepository(
|
||||
token: String
|
||||
) = api.getSeriesListByGenre(
|
||||
genreId = genreId,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
page = page - 1,
|
||||
size = size,
|
||||
authHeader = token
|
||||
|
||||
@@ -27,6 +27,7 @@ import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.audio_content.PurchaseOption
|
||||
import kr.co.vividnext.sodalive.audio_content.upload.theme.AudioContentThemeFragment
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.AdultContentVisibilityPolicy
|
||||
import kr.co.vividnext.sodalive.common.ImagePickerCropper
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
||||
@@ -206,7 +207,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
||||
)
|
||||
}
|
||||
|
||||
if (SharedPreferenceManager.isAuth) {
|
||||
if (shouldShowAdultRestrictionSetting()) {
|
||||
binding.llSetAdult.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.llSetAdult.visibility = View.GONE
|
||||
@@ -540,7 +541,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
||||
}
|
||||
}
|
||||
|
||||
if (SharedPreferenceManager.isAuth) {
|
||||
if (shouldShowAdultRestrictionSetting()) {
|
||||
binding.llAgeAll.setOnClickListener {
|
||||
viewModel.setAdult(false)
|
||||
}
|
||||
@@ -955,4 +956,12 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
|
||||
|
||||
return fileName
|
||||
}
|
||||
|
||||
private fun shouldShowAdultRestrictionSetting(): Boolean {
|
||||
return AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = SharedPreferenceManager.countryCode,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
isAuth = SharedPreferenceManager.isAuth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
object AdultContentVisibilityPolicy {
|
||||
private const val COUNTRY_CODE_KR = "KR"
|
||||
|
||||
fun shouldShowAdultRestrictionSetting(
|
||||
countryCode: String,
|
||||
isAdultContentVisible: Boolean,
|
||||
isAuth: Boolean
|
||||
): Boolean {
|
||||
if (!isAdultContentVisible) {
|
||||
return false
|
||||
}
|
||||
|
||||
val isKoreanCountry = countryCode.ifBlank { COUNTRY_CODE_KR } == COUNTRY_CODE_KR
|
||||
return !isKoreanCountry || isAuth
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,6 @@ interface ExplorerApi {
|
||||
fun getCreatorProfile(
|
||||
@Path("id") id: Long,
|
||||
@Query("timezone") timezone: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetCreatorProfileResponse>>
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package kr.co.vividnext.sodalive.explorer
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.explorer.profile.GetCheersResponse
|
||||
import kr.co.vividnext.sodalive.explorer.profile.cheers.PostWriteCheersRequest
|
||||
import kr.co.vividnext.sodalive.explorer.profile.cheers.PutModifyCheersRequest
|
||||
@@ -25,7 +24,6 @@ class ExplorerRepository(
|
||||
fun getCreatorProfile(id: Long, token: String) = api.getCreatorProfile(
|
||||
id = id,
|
||||
timezone = TimeZone.getDefault().id,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem
|
||||
import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Query
|
||||
@@ -13,39 +12,29 @@ interface HomeApi {
|
||||
@GET("/api/home")
|
||||
fun getHomeData(
|
||||
@Query("timezone") timezone: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetHomeResponse>>
|
||||
|
||||
@GET("/api/home/latest-content")
|
||||
fun getLatestContentByTheme(
|
||||
@Query("theme") theme: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<AudioContentMainItem>>>
|
||||
|
||||
@GET("/api/home/day-of-week-series")
|
||||
fun getDayOfWeekSeriesList(
|
||||
@Query("dayOfWeek") dayOfWeek: SeriesPublishedDaysOfWeek,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<GetSeriesListResponse.SeriesListItem>>>
|
||||
|
||||
@GET("/api/home/recommend-contents")
|
||||
fun getRecommendContents(
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<AudioContentMainItem>>>
|
||||
|
||||
@GET("/api/home/content-ranking")
|
||||
fun getContentRankingBySort(
|
||||
@Query("sort") sort: ContentRankingSortType,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<List<GetAudioContentRankingItem>>>
|
||||
}
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
package kr.co.vividnext.sodalive.home
|
||||
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import java.util.TimeZone
|
||||
|
||||
class HomeRepository(private val api: HomeApi) {
|
||||
fun fetchData(token: String) = api.getHomeData(
|
||||
timezone = TimeZone.getDefault().id,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun getLatestContentByTheme(theme: String, token: String) = api.getLatestContentByTheme(
|
||||
theme = theme,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -23,14 +17,10 @@ class HomeRepository(private val api: HomeApi) {
|
||||
dayOfWeek: SeriesPublishedDaysOfWeek, token: String
|
||||
) = api.getDayOfWeekSeriesList(
|
||||
dayOfWeek = dayOfWeek,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
fun getRecommendContents(token: String) = api.getRecommendContents(
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -39,8 +29,6 @@ class HomeRepository(private val api: HomeApi) {
|
||||
token: String
|
||||
) = api.getContentRankingBySort(
|
||||
sort = sortType,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.entries[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartTotalResponse
|
||||
import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest
|
||||
import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
|
||||
import kr.co.vividnext.sodalive.live.room.tag.GetLiveTagResponse
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.http.Body
|
||||
@@ -55,7 +54,6 @@ interface LiveApi {
|
||||
@Query("timezone") timezone: String,
|
||||
@Query("dateString") dateString: String?,
|
||||
@Query("status") status: LiveRoomStatus,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
@@ -250,8 +248,6 @@ interface LiveApi {
|
||||
@GET("/api/live")
|
||||
fun getLiveMain(
|
||||
@Query("timezone") timezone: String,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<LiveMainResponse>>
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package kr.co.vividnext.sodalive.live
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.live.reservation.MakeLiveReservationRequest
|
||||
import kr.co.vividnext.sodalive.live.reservation_status.CancelLiveReservationRequest
|
||||
import kr.co.vividnext.sodalive.live.room.CancelLiveRequest
|
||||
@@ -20,7 +19,6 @@ import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationResponse
|
||||
import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest
|
||||
import kr.co.vividnext.sodalive.live.room.like.LiveRoomLikeHeartRequest
|
||||
import kr.co.vividnext.sodalive.live.room.menu.MenuApi
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
|
||||
import kr.co.vividnext.sodalive.user.UserApi
|
||||
import okhttp3.MultipartBody
|
||||
@@ -43,7 +41,6 @@ class LiveRepository(
|
||||
timezone = TimeZone.getDefault().id,
|
||||
dateString = dateString,
|
||||
status = status,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
page = page - 1,
|
||||
size = size,
|
||||
authHeader = token
|
||||
@@ -287,8 +284,6 @@ class LiveRepository(
|
||||
|
||||
fun getLiveMain(token: String) = api.getLiveMain(
|
||||
timezone = TimeZone.getDefault().id,
|
||||
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ 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.common.AdultContentVisibilityPolicy
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.ImagePickerCropper
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
@@ -286,7 +287,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
||||
)
|
||||
}
|
||||
|
||||
if (SharedPreferenceManager.isAuth) {
|
||||
if (shouldShowAdultRestrictionSetting()) {
|
||||
binding.llSetAdult.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.llSetAdult.visibility = View.GONE
|
||||
@@ -581,10 +582,7 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
||||
}
|
||||
}
|
||||
|
||||
if (SharedPreferenceManager.role == MemberRole.CREATOR.name ||
|
||||
SharedPreferenceManager.isAuth
|
||||
) {
|
||||
|
||||
if (shouldShowAdultRestrictionSetting()) {
|
||||
binding.llAgeAll.setOnClickListener {
|
||||
viewModel.setAdult(false)
|
||||
}
|
||||
@@ -920,4 +918,12 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun shouldShowAdultRestrictionSetting(): Boolean {
|
||||
return AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = SharedPreferenceManager.countryCode,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
isAuth = SharedPreferenceManager.isAuth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingData
|
||||
import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingRepository
|
||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import kr.co.vividnext.sodalive.settings.event.EventItem
|
||||
import kr.co.vividnext.sodalive.settings.event.EventRepository
|
||||
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
|
||||
@@ -103,9 +104,19 @@ class MainViewModel(
|
||||
SharedPreferenceManager.point = data.point
|
||||
SharedPreferenceManager.role = data.role.name
|
||||
SharedPreferenceManager.isAuth = data.isAuth
|
||||
SharedPreferenceManager.countryCode = data.countryCode.ifBlank { "KR" }
|
||||
SharedPreferenceManager.isAdultContentVisible = data.isAdultContentVisible
|
||||
SharedPreferenceManager.contentPreference = data.contentType.ordinal
|
||||
|
||||
val localCountryCode = SharedPreferenceManager.countryCode.ifBlank { "KR" }
|
||||
val resolvedCountryCode = data.countryCode?.ifBlank { "KR" } ?: localCountryCode
|
||||
val resolvedIsAdultContentVisible =
|
||||
data.isAdultContentVisible ?: SharedPreferenceManager.isAdultContentVisible
|
||||
val resolvedContentType =
|
||||
data.contentType
|
||||
?: ContentType.entries.getOrNull(SharedPreferenceManager.contentPreference)
|
||||
?: ContentType.ALL
|
||||
|
||||
SharedPreferenceManager.countryCode = resolvedCountryCode
|
||||
SharedPreferenceManager.isAdultContentVisible = resolvedIsAdultContentVisible
|
||||
SharedPreferenceManager.contentPreference = resolvedContentType.ordinal
|
||||
SharedPreferenceManager.isAuditionNotification =
|
||||
data.auditionNotice ?: false
|
||||
if (
|
||||
|
||||
@@ -400,7 +400,7 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.btnIdentityVerification.root.visibility = View.GONE
|
||||
binding.btnIdentityVerification.root.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
if (it.isAuth) {
|
||||
|
||||
@@ -2,7 +2,6 @@ package kr.co.vividnext.sodalive.search
|
||||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Query
|
||||
@@ -11,16 +10,12 @@ interface SearchApi {
|
||||
@GET("/search")
|
||||
fun searchUnified(
|
||||
@Query("keyword") keyword: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<SearchUnifiedResponse>>
|
||||
|
||||
@GET("/search/creators")
|
||||
fun searchCreatorList(
|
||||
@Query("keyword") keyword: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
@@ -29,8 +24,6 @@ interface SearchApi {
|
||||
@GET("/search/contents")
|
||||
fun searchContentList(
|
||||
@Query("keyword") keyword: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
@@ -39,8 +32,6 @@ interface SearchApi {
|
||||
@GET("/search/series")
|
||||
fun searchSeriesList(
|
||||
@Query("keyword") keyword: String,
|
||||
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
|
||||
@Query("contentType") contentType: ContentType,
|
||||
@Query("page") page: Int,
|
||||
@Query("size") size: Int,
|
||||
@Header("Authorization") authHeader: String
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
package kr.co.vividnext.sodalive.search
|
||||
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
|
||||
class SearchRepository(private val api: SearchApi) {
|
||||
fun searchUnified(
|
||||
keyword: String,
|
||||
token: String
|
||||
) = api.searchUnified(
|
||||
keyword = keyword,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
|
||||
authHeader = token
|
||||
)
|
||||
|
||||
@@ -21,8 +16,6 @@ class SearchRepository(private val api: SearchApi) {
|
||||
token: String
|
||||
) = api.searchCreatorList(
|
||||
keyword = keyword,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
|
||||
page = page - 1,
|
||||
size = size,
|
||||
authHeader = token
|
||||
@@ -35,8 +28,6 @@ class SearchRepository(private val api: SearchApi) {
|
||||
token: String
|
||||
) = api.searchContentList(
|
||||
keyword = keyword,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
|
||||
page = page - 1,
|
||||
size = size,
|
||||
authHeader = token
|
||||
@@ -49,8 +40,6 @@ class SearchRepository(private val api: SearchApi) {
|
||||
token: String
|
||||
) = api.searchSeriesList(
|
||||
keyword = keyword,
|
||||
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
|
||||
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
|
||||
page = page - 1,
|
||||
size = size,
|
||||
authHeader = token
|
||||
|
||||
@@ -21,11 +21,11 @@ data class GetMemberInfoResponse(
|
||||
@SerializedName("auditionNotice")
|
||||
val auditionNotice: Boolean?,
|
||||
@SerializedName("countryCode")
|
||||
val countryCode: String,
|
||||
val countryCode: String? = null,
|
||||
@SerializedName("isAdultContentVisible")
|
||||
val isAdultContentVisible: Boolean,
|
||||
val isAdultContentVisible: Boolean? = null,
|
||||
@SerializedName("contentType")
|
||||
val contentType: ContentType
|
||||
val contentType: ContentType? = null
|
||||
)
|
||||
|
||||
enum class MemberRole {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package kr.co.vividnext.sodalive.common
|
||||
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class AdultContentVisibilityPolicyTest {
|
||||
|
||||
@Test
|
||||
fun `isAdultContentVisible가 false면 항상 미표시`() {
|
||||
assertFalse(
|
||||
AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = "KR",
|
||||
isAdultContentVisible = false,
|
||||
isAuth = true
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(
|
||||
AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = "US",
|
||||
isAdultContentVisible = false,
|
||||
isAuth = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `한국 접속은 isAuth가 true일 때만 표시`() {
|
||||
assertTrue(
|
||||
AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = "KR",
|
||||
isAdultContentVisible = true,
|
||||
isAuth = true
|
||||
)
|
||||
)
|
||||
|
||||
assertFalse(
|
||||
AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = "KR",
|
||||
isAdultContentVisible = true,
|
||||
isAuth = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `비한국 접속은 isAdultContentVisible true면 표시`() {
|
||||
assertTrue(
|
||||
AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = "US",
|
||||
isAdultContentVisible = true,
|
||||
isAuth = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `countryCode가 비어있으면 한국으로 간주한다`() {
|
||||
assertFalse(
|
||||
AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = "",
|
||||
isAdultContentVisible = true,
|
||||
isAuth = false
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(
|
||||
AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(
|
||||
countryCode = "",
|
||||
isAdultContentVisible = true,
|
||||
isAuth = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package kr.co.vividnext.sodalive.settings.notification
|
||||
|
||||
import com.google.gson.Gson
|
||||
import kr.co.vividnext.sodalive.settings.ContentType
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class GetMemberInfoResponseCompatibilityTest {
|
||||
|
||||
private val gson = Gson()
|
||||
|
||||
@Test
|
||||
fun `구서버 응답에서 신규 필드가 없어도 역직렬화된다`() {
|
||||
val json = """
|
||||
{
|
||||
"can": 10,
|
||||
"point": 120,
|
||||
"isAuth": true,
|
||||
"gender": "F",
|
||||
"signupDate": "2024-01-01, 00:00:00",
|
||||
"chargeCount": 3,
|
||||
"role": "USER",
|
||||
"messageNotice": true,
|
||||
"followingChannelLiveNotice": false,
|
||||
"followingChannelUploadContentNotice": true,
|
||||
"auditionNotice": false
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val response = gson.fromJson(json, GetMemberInfoResponse::class.java)
|
||||
|
||||
assertEquals(10, response.can)
|
||||
assertEquals(120, response.point)
|
||||
assertTrue(response.isAuth)
|
||||
assertEquals(MemberRole.USER, response.role)
|
||||
assertNull(response.countryCode)
|
||||
assertNull(response.isAdultContentVisible)
|
||||
assertNull(response.contentType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `신규 필드가 있으면 정상 매핑된다`() {
|
||||
val json = """
|
||||
{
|
||||
"can": 10,
|
||||
"point": 120,
|
||||
"isAuth": true,
|
||||
"gender": "F",
|
||||
"signupDate": "2024-01-01, 00:00:00",
|
||||
"chargeCount": 3,
|
||||
"role": "CREATOR",
|
||||
"messageNotice": true,
|
||||
"followingChannelLiveNotice": false,
|
||||
"followingChannelUploadContentNotice": true,
|
||||
"auditionNotice": false,
|
||||
"countryCode": "US",
|
||||
"isAdultContentVisible": true,
|
||||
"contentType": "FEMALE"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val response = gson.fromJson(json, GetMemberInfoResponse::class.java)
|
||||
|
||||
assertEquals("US", response.countryCode)
|
||||
assertEquals(true, response.isAdultContentVisible)
|
||||
assertEquals(ContentType.FEMALE, response.contentType)
|
||||
}
|
||||
}
|
||||
35
docs/20260327_연령제한표시조건수정.md
Normal file
35
docs/20260327_연령제한표시조건수정.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# 연령제한 설정 UI 표시 조건 수정
|
||||
|
||||
## 작업 목표
|
||||
- 라이브 생성, 콘텐츠 업로드 페이지의 연령제한 설정 UI 표시 조건을 접속국가와 `isAdultContentVisible` 기준으로 조정한다.
|
||||
|
||||
## 체크리스트
|
||||
- [x] AC1: 공통 필수조건으로 `isAdultContentVisible == true`일 때만 연령제한 설정 UI를 표시한다.
|
||||
- QA: `isAdultContentVisible=false`에서 UI 미표시, `isAdultContentVisible=true`에서 국가별 추가 조건 적용 확인
|
||||
- [x] AC2: 접속국가가 한국(`countryCode == "KR"`)인 경우 `isAuth == true`일 때만 연령제한 설정 UI를 표시한다.
|
||||
- QA: KR + `isAuth=false` 미표시, KR + `isAuth=true` 표시
|
||||
- [x] AC3: 접속국가가 한국이 아닌 경우 `isAuth`와 무관하게 AC1만 충족하면 UI를 표시한다.
|
||||
- QA: non-KR + `isAdultContentVisible=true`에서 `isAuth` true/false 모두 표시
|
||||
- [x] AC4: 라이브 생성/콘텐츠 업로드 양쪽 페이지에 동일 규칙이 적용된다.
|
||||
- QA: 두 화면에서 동일 입력 조건 대비 동일 표시 결과 확인
|
||||
- [x] AC5: 변경 파일 진단/테스트/빌드 검증을 통과한다.
|
||||
- QA: Kotlin LSP 미지원 환경 확인, `./gradlew :app:testDebugUnitTest`, `./gradlew :app:assembleDebug`
|
||||
|
||||
## 검증 기록
|
||||
- 무엇/왜/어떻게: 라이브 생성(`LiveRoomCreateActivity`)과 콘텐츠 업로드(`AudioContentUploadActivity`)의 연령제한 UI 노출 조건을 공통 정책(`AdultContentVisibilityPolicy`)으로 통합했다. 요청 조건(필수 `isAdultContentVisible=true`, KR 추가 `isAuth=true`)을 두 화면에서 동일하게 적용하기 위해 기존 `isAuth` 단일 조건을 정책 함수 호출로 교체했다.
|
||||
- 실행 명령: 코드 수정(해당 3개 Kotlin 파일 + 정책 테스트 1개 파일 추가)
|
||||
- 결과: 두 화면 모두 `shouldShowAdultRestrictionSetting()` 기반으로 `llSetAdult` 표시 및 연령선택 핸들러 등록 조건이 동작하도록 반영됨
|
||||
|
||||
- 무엇/왜/어떻게: 정책 로직의 조건 분기 오동작을 방지하기 위해 단위 테스트(`AdultContentVisibilityPolicyTest`)로 KR/non-KR, `isAdultContentVisible`, `isAuth`, 빈 `countryCode` 조합을 검증했다.
|
||||
- 실행 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.common.AdultContentVisibilityPolicyTest"`
|
||||
- 결과: BUILD SUCCESSFUL
|
||||
|
||||
- 무엇/왜/어떻게: 변경 영향 범위의 회귀 확인을 위해 디버그 단위 테스트 전체와 디버그 빌드를 수행했다.
|
||||
- 실행 명령: `./gradlew :app:testDebugUnitTest`
|
||||
- 결과: BUILD SUCCESSFUL
|
||||
- 실행 명령: `./gradlew :app:assembleDebug`
|
||||
- 결과: BUILD SUCCESSFUL
|
||||
|
||||
- 무엇/왜/어떻게: 정적 진단 요구사항 확인을 위해 LSP diagnostics를 시도했으나 현재 실행 환경에는 `.kt`용 LSP 서버가 구성되어 있지 않음을 확인했다. 대신 Gradle 컴파일/테스트/빌드 성공으로 Kotlin 컴파일 오류 유무를 검증했다.
|
||||
- 실행 명령: `lsp_diagnostics` (4개 Kotlin 파일)
|
||||
- 결과: `No LSP server configured for extension: .kt`
|
||||
36
docs/20260327_콘텐츠보기설정파라미터전송정리.md
Normal file
36
docs/20260327_콘텐츠보기설정파라미터전송정리.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 20260327_콘텐츠보기설정파라미터전송정리
|
||||
|
||||
## 작업 목적
|
||||
- `PATCH /member/content-preference`를 제외한 모든 API 요청에서 `isAdultContentVisible`, `contentType` 전송을 제거한다.
|
||||
|
||||
## 구현 체크리스트
|
||||
- [x] 코드베이스에서 `isAdultContentVisible`, `contentType` 전송 위치를 전수 확인한다.
|
||||
- [x] 예외 API(`PATCH /member/content-preference`) 선언 및 호출 경로를 확인한다.
|
||||
- [x] 예외 API 외 DTO/호출부에서 두 파라미터를 제거한다.
|
||||
- [x] 수정 파일 진단, 테스트, 빌드 검증을 수행한다.
|
||||
- [x] 검증 기록을 문서 하단에 누적한다.
|
||||
|
||||
## 수용 기준 (Pass/Fail)
|
||||
- [x] PASS: `PATCH /member/content-preference`에서는 `isAdultContentVisible`, `contentType`가 유지된다.
|
||||
- [x] PASS: 그 외 API 요청에서는 `isAdultContentVisible`, `contentType`가 전송되지 않는다.
|
||||
- [x] PASS: `./gradlew :app:testDebugUnitTest`가 성공한다.
|
||||
- [x] PASS: `./gradlew :app:assembleDebug`가 성공한다.
|
||||
|
||||
## 검증 기록
|
||||
- 2026-03-27
|
||||
- 무엇/왜/어떻게: `/member/content-preference` PATCH를 제외한 API 요청에서 `isAdultContentVisible`, `contentType` 전송을 제거했다. Retrofit API 시그니처와 각 Repository 호출 인자를 함께 정리해 누락 없이 제거했다.
|
||||
- 실행 명령/도구:
|
||||
- `task(explore: 전송 경로 탐색 2건)`
|
||||
- `grep("@Query(\"isAdultContentVisible\")|@Query(\"contentType\")", include="*Api.kt")`
|
||||
- `apply_patch(Live/Home/Search/Explorer/AudioContent/Series API·Repository 수정)`
|
||||
- `lsp_diagnostics(수정된 .kt 파일 14개)`
|
||||
- `grep("@PATCH(\"/member/content-preference\")", include="UserApi.kt")`
|
||||
- `grep("@Query(\"isAdultContentVisible\")|@Query(\"contentType\")", include="*Api.kt")`
|
||||
- `grep("isAdultContentVisible|contentType", include="*Request*.kt")`
|
||||
- `./gradlew :app:testDebugUnitTest :app:assembleDebug`
|
||||
- 결과:
|
||||
- `UserApi.kt`의 `@PATCH("/member/content-preference")`는 유지됨을 확인했다.
|
||||
- 전체 `*Api.kt`에서 `@Query("isAdultContentVisible")`, `@Query("contentType")`가 0건임을 확인했다.
|
||||
- `*Request*.kt`에서 두 필드는 `UpdateContentPreferenceRequest.kt` 1건만 남아 예외 API 요청으로 제한됨을 확인했다.
|
||||
- `lsp_diagnostics`는 Kotlin LSP 미구성으로 진단 불가 메시지를 확인했다.
|
||||
- `./gradlew :app:testDebugUnitTest :app:assembleDebug` 성공(`BUILD SUCCESSFUL`).
|
||||
29
docs/20260328_마이페이지본인인증버튼숨김정렬유지.md
Normal file
29
docs/20260328_마이페이지본인인증버튼숨김정렬유지.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# 마이페이지 본인인증 버튼 숨김 시 정렬 유지 수정
|
||||
|
||||
## 작업 목표
|
||||
- 국가가 한국이 아닌 경우 `btn_identity_verification`을 숨기더라도 Function Buttons Grid의 다른 아이콘 위치가 기존과 동일하게 유지되도록 수정한다.
|
||||
|
||||
## 체크리스트
|
||||
- [x] AC1: `countryCode != "KR"`인 경우 `btn_identity_verification`이 화면에 보이지 않는다.
|
||||
- QA: `btnIdentityVerification.root.visibility`가 `View.INVISIBLE`로 설정되어 슬롯 공간이 유지되는지 코드 확인
|
||||
- [x] AC2: `countryCode != "KR"`인 경우에도 같은 행의 다른 버튼(`btn_notice`, `btn_event`, `btn_customer_service`) 위치가 기존과 동일하게 유지된다.
|
||||
- QA: `View.GONE` 대신 `View.INVISIBLE` 사용 여부 확인
|
||||
- [x] AC3: `countryCode == "KR"`인 경우 기존 본인인증 버튼 노출/동작 로직이 유지된다.
|
||||
- QA: KR 분기에서 기존 `View.VISIBLE` + 인증 상태별 버튼 설정 코드 보존 확인
|
||||
- [x] AC4: 변경 파일 진단/테스트/빌드 검증을 통과한다.
|
||||
- QA: `lsp_diagnostics`, `./gradlew :app:testDebugUnitTest`, `./gradlew :app:assembleDebug`
|
||||
|
||||
## 검증 기록
|
||||
- 2026-03-28
|
||||
- 무엇/왜/어떻게: Function Buttons Grid 두 번째 행이 `LinearLayout`의 `layout_weight` 기반이어서 `btn_identity_verification`을 `GONE` 처리하면 남은 버튼이 재배치된다. 슬롯은 유지하고 아이콘만 숨기기 위해 non-KR 분기에서 `View.GONE`을 `View.INVISIBLE`로 변경했다.
|
||||
- 실행 명령/도구:
|
||||
- `apply_patch(app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt)`
|
||||
- `read(app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt)`
|
||||
- `lsp_diagnostics(app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt)`
|
||||
- `lsp_diagnostics(docs/20260328_마이페이지본인인증버튼숨김정렬유지.md)`
|
||||
- `./gradlew :app:testDebugUnitTest :app:assembleDebug`
|
||||
- 결과:
|
||||
- non-KR 분기에서 `btnIdentityVerification.root.visibility = View.INVISIBLE`로 반영되어 버튼 슬롯 유지 조건을 충족했다.
|
||||
- KR 분기의 `View.VISIBLE` 및 인증 상태별 버튼 구성 로직은 변경 없이 유지됐다.
|
||||
- `.kt` 파일 대상 `lsp_diagnostics`는 현재 환경에 Kotlin LSP가 없어 실행 불가(`No LSP server configured for extension: .kt`)였고, 문서 파일 진단은 이슈 없음.
|
||||
- `:app:testDebugUnitTest`, `:app:assembleDebug`를 포함한 Gradle 실행이 `BUILD SUCCESSFUL`로 완료됐다.
|
||||
49
docs/20260328_멤버정보응답하위호환수정.md
Normal file
49
docs/20260328_멤버정보응답하위호환수정.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 20260328_멤버정보응답하위호환수정.md
|
||||
|
||||
## 개요
|
||||
- 이전 서버의 `/member/info` 응답에 `countryCode`, `isAdultContentVisible`, `contentType`가 없어도 신규 앱이 동일하게 동작하도록 하위 호환을 보장한다.
|
||||
|
||||
## 요구사항 해석(확정)
|
||||
- `GetMemberInfoResponse`의 신규 필드 3개는 구서버 응답에서 누락될 수 있으므로 nullable로 처리한다.
|
||||
- `MainViewModel.getMemberInfo()` 동기화 시 누락된 값은 로컬 저장값(없으면 안전 기본값)으로 대체한다.
|
||||
|
||||
## 완료 기준 (Acceptance Criteria)
|
||||
- [x] AC1: 구서버 응답(JSON에 신규 3개 필드 누락) 역직렬화가 실패하지 않는다.
|
||||
- [x] AC2: 구서버 응답 수신 시 `SharedPreferenceManager.countryCode/isAdultContentVisible/contentPreference`가 null로 오염되지 않고 기존 동작을 유지한다.
|
||||
- [x] AC3: 관련 단위 테스트와 디버그 빌드가 성공한다.
|
||||
|
||||
## 구현 체크리스트
|
||||
- [x] `app/src/main/java/kr/co/vividnext/sodalive/settings/notification/GetMemberInfoResponse.kt`
|
||||
- 신규 필드(`countryCode`, `isAdultContentVisible`, `contentType`)를 nullable + default null로 변경
|
||||
- [x] `app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt`
|
||||
- 멤버 정보 동기화 시 신규 필드 null-safe fallback 적용
|
||||
- [x] `app/src/test/java/kr/co/vividnext/sodalive/settings/notification/GetMemberInfoResponseCompatibilityTest.kt`
|
||||
- 구서버 응답 누락 필드 역직렬화 및 fallback 동작 검증 테스트 추가
|
||||
- [x] 검증 실행
|
||||
- `lsp_diagnostics`(수정 파일)
|
||||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.settings.notification.GetMemberInfoResponseCompatibilityTest"`
|
||||
- `./gradlew :app:testDebugUnitTest`
|
||||
- `./gradlew :app:assembleDebug`
|
||||
|
||||
## 검증 기록
|
||||
- 기록 템플릿(후속 누적):
|
||||
- YYYY-MM-DD
|
||||
- 무엇/왜/어떻게:
|
||||
- 실행 명령/도구:
|
||||
- `명령 또는 사용 도구`
|
||||
- 결과:
|
||||
|
||||
- 2026-03-28
|
||||
- 무엇/왜/어떻게: 구서버(`/member/info`)에서 신규 필드 3종이 누락돼도 신규 앱이 동일 동작하도록 응답 모델 nullable 처리 + 멤버 정보 동기화 fallback 로직을 적용했고, 역직렬화 호환 테스트를 추가했다.
|
||||
- 실행 명령/도구:
|
||||
- `apply_patch(GetMemberInfoResponse.kt, MainViewModel.kt, GetMemberInfoResponseCompatibilityTest.kt, docs/20260328_멤버정보응답하위호환수정.md)`
|
||||
- `lsp_diagnostics(GetMemberInfoResponse.kt, MainViewModel.kt, GetMemberInfoResponseCompatibilityTest.kt)`
|
||||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.settings.notification.GetMemberInfoResponseCompatibilityTest"`
|
||||
- `./gradlew :app:testDebugUnitTest`
|
||||
- `./gradlew :app:assembleDebug`
|
||||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.settings.notification.GetMemberInfoResponseCompatibilityTest" :app:assembleDebug`
|
||||
- `read(app/build/test-results/testDebugUnitTest/TEST-kr.co.vividnext.sodalive.settings.notification.GetMemberInfoResponseCompatibilityTest.xml)`
|
||||
- 결과:
|
||||
- `lsp_diagnostics`는 `.kt` LSP 서버 미구성으로 실행 불가를 확인했다.
|
||||
- 호환성 테스트 2건(구서버 누락 필드 역직렬화/신규 필드 정상 매핑)이 모두 통과했다.
|
||||
- 전체 단위 테스트와 디버그 빌드가 모두 성공했고, 마지막 재검증 명령에서도 성공을 재확인했다.
|
||||
Reference in New Issue
Block a user