feat(content): 전체보기 화면 등록을 추가한다

This commit is contained in:
2026-06-27 08:30:06 +09:00
parent 0906a21c6d
commit 9ab5101c48
3 changed files with 117 additions and 0 deletions

View File

@@ -112,6 +112,7 @@
</activity>
<activity android:name=".main.MainActivity" />
<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.live.onair.HomeOnAirLiveActivity" />
<activity

View File

@@ -207,6 +207,9 @@ import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingsApi
import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingsRepository
import kr.co.vividnext.sodalive.v2.main.content.data.MainContentAllTabApi
import kr.co.vividnext.sodalive.v2.main.content.data.MainContentAllTabRepository
import kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewViewModel
import kr.co.vividnext.sodalive.v2.main.content.overview.data.ContentOverviewRepository
import kr.co.vividnext.sodalive.v2.main.content.overview.data.ContentOverviewApi
import kr.co.vividnext.sodalive.v2.main.home.HomeCreatorRankingViewModel
import kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModel
import kr.co.vividnext.sodalive.v2.main.home.HomeRecommendationViewModel
@@ -331,6 +334,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
single { ApiBuilder().build(get(), AudioRecommendationsApi::class.java) }
single { ApiBuilder().build(get(), AudioRankingsApi::class.java) }
single { ApiBuilder().build(get(), MainContentAllTabApi::class.java) }
single { ApiBuilder().build(get(), ContentOverviewApi::class.java) }
single { ApiBuilder().build(get(), HomeCreatorRankingApi::class.java) }
single { ApiBuilder().build(get(), HomeFollowingApi::class.java) }
single { ApiBuilder().build(get(), HomeRecommendationApi::class.java) }
@@ -436,6 +440,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { ChatMainViewModel(get()) }
viewModel { DmChatRoomViewModel(get()) }
viewModel { ContentAllTabViewModel(get()) }
viewModel { ContentOverviewViewModel(get()) }
viewModel { ContentMainViewModel(get()) }
viewModel { ContentRankingViewModel(get()) }
viewModel { HomeCreatorRankingViewModel(get()) }
@@ -504,6 +509,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { AudioRecommendationsRepository(get()) }
factory { AudioRankingsRepository(get()) }
factory { MainContentAllTabRepository(get()) }
factory { ContentOverviewRepository(get()) }
factory { HomeCreatorRankingRepository(get()) }
factory { HomeFollowingRepository(get()) }
factory { HomeRecommendationRepository(get()) }

View File

@@ -0,0 +1,110 @@
package kr.co.vividnext.sodalive.v2.main.content.overview
import android.app.Application
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import java.io.File
@RunWith(RobolectricTestRunner::class)
@Config(application = Application::class)
class ContentOverviewActivitySourceTest {
@Test
fun `ContentOverviewActivity source has intent extra and two column grid contract`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivity.kt"
).readText()
assertTrue(source.contains("private const val EXTRA_CONTENT_OVERVIEW_TYPE"))
assertTrue(source.contains("fun newIntent(context: Context, type: ContentOverviewType): Intent"))
assertTrue(source.contains("putExtra(EXTRA_CONTENT_OVERVIEW_TYPE, type.name)"))
assertTrue(source.contains("CONTENT_OVERVIEW_GRID_SPAN_COUNT"))
assertTrue(source.contains("GridLayoutManager(this, CONTENT_OVERVIEW_GRID_SPAN_COUNT)"))
assertTrue(
source.contains(
"binding.rvContentOverviewItems.calculateContentGridItemWidthPx(CONTENT_OVERVIEW_GRID_SPAN_COUNT)"
)
)
assertTrue(source.contains("viewModel.loadMore()"))
}
@Test
fun `ContentOverview layout has Figma title bar and state views`() {
val layout = projectFile("app/src/main/res/layout/activity_content_overview.xml").readText()
assertTrue(layout.contains("@+id/layout_content_overview_root"))
assertTrue(layout.contains("android:background=\"@color/black\""))
assertTrue(layout.contains("@+id/layout_content_overview_title_bar"))
assertTrue(layout.contains("android:layout_height=\"60dp\""))
assertTrue(layout.contains("@+id/iv_content_overview_back"))
assertTrue(layout.contains("@drawable/ic_new_bar_back"))
assertTrue(layout.contains("@+id/tv_content_overview_title"))
assertTrue(layout.contains("@style/Typography.Heading2"))
assertTrue(layout.contains("@+id/rv_content_overview_items"))
assertTrue(layout.contains("@+id/pb_content_overview_initial_loading"))
assertTrue(layout.contains("@+id/pb_content_overview_load_more"))
assertTrue(layout.contains("@+id/tv_content_overview_empty_error"))
}
@Test
fun `ContentOverviewActivity renders first and load more loading states visibly`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivity.kt"
).readText()
assertTrue(source.contains("viewModel.isLoading.observe(this)"))
assertTrue(source.contains("pbContentOverviewInitialLoading.visibility = View.VISIBLE"))
assertTrue(source.contains("pbContentOverviewInitialLoading.visibility = View.GONE"))
assertTrue(source.contains("pbContentOverviewLoadMore.visibility = View.GONE"))
assertTrue(source.contains("pbContentOverviewLoadMore.visibility = if (state.isLoadingMore)"))
}
@Test
fun `ContentOverviewActivity opens audio detail with content id`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivity.kt"
).readText()
assertTrue(source.contains("AudioContentDetailActivity::class.java"))
assertTrue(source.contains("if (contentId <= 0) return"))
assertTrue(source.contains("putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)"))
}
@Test
fun `ContentOverviewAdapter ignores invalid content id clicks`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ui/ContentOverviewAdapter.kt"
).readText()
assertTrue(source.contains("if (item.contentId <= 0) return@setOnClickListener"))
assertTrue(source.contains("onContentClick(item.contentId)"))
}
@Test
fun `manifest registers ContentOverviewActivity and Phase 7 routing is not added`() {
val manifest = projectFile("app/src/main/AndroidManifest.xml").readText()
val homeSource = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt"
).readText()
val contentSource = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt"
).readText()
assertTrue(manifest.contains(".v2.main.content.overview.ContentOverviewActivity"))
assertFalse(homeSource.contains("viewHomeFirstAudioTitle.ivSectionTitleChevron.setOnClickListener"))
assertFalse(contentSource.contains("viewContentNewAndHotTitle.ivSectionTitleChevron.setOnClickListener"))
}
private fun projectFile(relativePath: String): File {
val userDir = requireNotNull(System.getProperty("user.dir"))
val fromRoot = File(userDir, relativePath)
if (fromRoot.exists()) return fromRoot
val projectRoot = requireNotNull(File(userDir).parentFile)
return projectRoot.resolve(relativePath)
}
}