feat(creator): 채널 공유 진입점을 추가한다

This commit is contained in:
2026-09-15 11:45:28 +09:00
parent 5e1a7e9ace
commit 4bc725bdf2
7 changed files with 277 additions and 0 deletions
@@ -30,6 +30,7 @@ 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.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.Utils
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelBinding
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityPostMenuBottomSheetDialog
@@ -230,6 +231,7 @@ class CreatorChannelActivity :
private fun setupClickListeners() {
binding.ivBack.setOnClickListener { finish() }
binding.ivMore.setOnClickListener { onMoreClicked() }
binding.ivShare.setOnClickListener { shareCurrentCreatorChannel() }
binding.tvFollowerCount.setOnClickListener { onFollowerCountClicked() }
binding.layoutFollowCapsule.setOnClickListener { onFollowCapsuleClicked() }
binding.ivBell.setOnClickListener { onBellClicked() }
@@ -291,9 +293,11 @@ class CreatorChannelActivity :
binding.layoutFollowCapsule.visibility = View.GONE
binding.ivBell.visibility = View.GONE
binding.ivMore.visibility = View.GONE
binding.ivShare.visibility = View.VISIBLE
return
}
binding.ivShare.visibility = View.GONE
binding.layoutFollowCapsule.visibility = View.VISIBLE
binding.ivMore.visibility = View.VISIBLE
val titleBarState = CreatorChannelTitleBarState.from(
@@ -435,12 +439,35 @@ class CreatorChannelActivity :
if (header.isOwner) return
CreatorChannelMoreBottomSheet.newInstance().apply {
onClickChannelShare = { shareCurrentCreatorChannel() }
onClickBlock = { showUserBlockDialog() }
onClickUserReport = { showUserReportDialog() }
onClickProfileReport = { showProfileReportDialog() }
}.show(supportFragmentManager, CreatorChannelMoreBottomSheet::class.java.simpleName)
}
private fun shareCurrentCreatorChannel() {
val header = currentHeader ?: return
if (creatorId <= 0L) return
val nickname = header.nickname
val params = mapOf(
"af_dp" to "voiceon://",
"deep_link_value" to "channel",
"deep_link_sub5" to "$creatorId"
)
val shareUrl = Utils.createOneLinkUrl(params = params)
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, "소다라이브 ${nickname}님의 채널입니다.\n$shareUrl")
val shareIntent = Intent.createChooser(
intent,
getString(R.string.screen_user_profile_share_channel)
)
startActivity(shareIntent)
}
private fun showUserBlockDialog() {
val nickname = currentHeader?.nickname.orEmpty()
V2ModalDialog(
@@ -9,6 +9,7 @@ import kr.co.vividnext.sodalive.databinding.DialogCreatorChannelMoreBinding
class CreatorChannelMoreBottomSheet : BottomSheetDialogFragment() {
var onClickChannelShare: (() -> Unit)? = null
var onClickBlock: (() -> Unit)? = null
var onClickUserReport: (() -> Unit)? = null
var onClickProfileReport: (() -> Unit)? = null
@@ -29,6 +30,10 @@ class CreatorChannelMoreBottomSheet : BottomSheetDialogFragment() {
super.onViewCreated(view, savedInstanceState)
val viewBinding = binding ?: return
viewBinding.tvChannelShare.setOnClickListener {
dismiss()
onClickChannelShare?.invoke()
}
viewBinding.tvUserBlock.setOnClickListener {
dismiss()
onClickBlock?.invoke()
@@ -226,6 +226,14 @@
tools:src="@drawable/ic_bar_bell"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_share"
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@string/screen_user_profile_share_channel"
android:src="@drawable/ic_audio_content_share"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_more"
android:layout_width="24dp"
@@ -16,6 +16,16 @@
android:layout_marginBottom="12dp"
android:background="@drawable/bg_bottom_sheet_handle" />
<TextView
android:id="@+id/tv_channel_share"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/medium"
android:paddingVertical="16dp"
android:text="@string/screen_user_profile_share_channel"
android:textColor="@color/white"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_user_block"
android:layout_width="match_parent"
@@ -0,0 +1,55 @@
package kr.co.vividnext.sodalive.common
import android.app.Application
import android.content.Context
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.test.core.app.ApplicationProvider
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [28], application = Application::class)
class SharedPreferenceManagerTest {
private val context: Context = ApplicationProvider.getApplicationContext()
@Before
fun setUp() {
SharedPreferenceManager.resetForTest()
SharedPreferenceManager.init(context)
SharedPreferenceManager.resetCanForTest()
}
@After
fun tearDown() {
SharedPreferenceManager.resetCanForTest()
SharedPreferenceManager.resetForTest()
}
@Test
fun `resetCanForTest는 저장된 can 값을 초기화한다`() {
SharedPreferenceManager.setCanForTest(200)
SharedPreferenceManager.resetCanForTest()
assertEquals(0, SharedPreferenceManager.can)
}
@Test
fun `setCanForTest는 can 값을 동기적으로 저장한다`() {
SharedPreferenceManager.setCanForTest(200)
assertEquals(200, SharedPreferenceManager.can)
runBlocking {
val preferences = AppPreferencesDataStoreProvider.get(context).data.first()
assertEquals(200, preferences[intPreferencesKey(Constants.PREF_CAN)])
}
}
}
@@ -0,0 +1,67 @@
package kr.co.vividnext.sodalive.v2.creator.channel
import org.junit.Assert.assertTrue
import org.junit.Test
import java.io.File
class CreatorChannelShareSourceTest {
@Test
fun `내 채널은 우측 상단 공유 버튼을 표시한다`() {
val activity = projectFile(ACTIVITY_PATH).readText()
val layout = projectFile(ACTIVITY_LAYOUT_PATH).readText()
assertTrue(layout.contains("@+id/iv_share"))
assertTrue(layout.contains("android:src=\"@drawable/ic_audio_content_share\""))
assertTrue(activity.contains("binding.ivShare.visibility = View.VISIBLE"))
assertTrue(activity.contains("binding.ivShare.visibility = View.GONE"))
}
@Test
fun `다른 사람 채널 더보기는 채널 공유 항목을 제공한다`() {
val activity = projectFile(ACTIVITY_PATH).readText()
val bottomSheet = projectFile(BOTTOM_SHEET_PATH).readText()
val layout = projectFile(BOTTOM_SHEET_LAYOUT_PATH).readText()
assertTrue(layout.contains("@+id/tv_channel_share"))
assertTrue(layout.contains("@string/screen_user_profile_share_channel"))
assertTrue(bottomSheet.contains("var onClickChannelShare: (() -> Unit)? = null"))
assertTrue(bottomSheet.contains("viewBinding.tvChannelShare.setOnClickListener"))
assertTrue(activity.contains("onClickChannelShare = { shareCurrentCreatorChannel() }"))
}
@Test
fun `두 진입점은 기존 채널 공유 chooser와 메뉴 동작을 유지한다`() {
val activity = projectFile(ACTIVITY_PATH).readText()
val bottomSheet = projectFile(BOTTOM_SHEET_PATH).readText()
assertTrue(activity.contains("binding.ivShare.setOnClickListener { shareCurrentCreatorChannel() }"))
assertTrue(activity.contains("Intent(Intent.ACTION_SEND)"))
assertTrue(activity.contains("type = \"text/plain\""))
assertTrue(activity.contains("putExtra(Intent.EXTRA_TEXT"))
assertTrue(activity.contains("Intent.createChooser"))
assertTrue(activity.contains("R.string.screen_user_profile_share_channel"))
assertTrue(activity.contains("\"deep_link_value\" to \"channel\""))
assertTrue(activity.contains("\"deep_link_sub5\" to \"${'$'}creatorId\""))
assertTrue(activity.contains("binding.layoutFollowCapsule.visibility = View.VISIBLE"))
assertTrue(activity.contains("binding.ivMore.visibility = View.VISIBLE"))
assertTrue(bottomSheet.contains("onClickBlock?.invoke()"))
assertTrue(bottomSheet.contains("onClickUserReport?.invoke()"))
assertTrue(bottomSheet.contains("onClickProfileReport?.invoke()"))
}
private fun projectFile(relativePath: String): File {
val candidates = listOf(File(relativePath), File("../$relativePath"))
return candidates.firstOrNull { it.exists() }
?: error("Project file not found: $relativePath")
}
private companion object {
const val ACTIVITY_PATH =
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
const val ACTIVITY_LAYOUT_PATH = "app/src/main/res/layout/activity_creator_channel.xml"
const val BOTTOM_SHEET_PATH =
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelMoreBottomSheet.kt"
const val BOTTOM_SHEET_LAYOUT_PATH = "app/src/main/res/layout/dialog_creator_channel_more.xml"
}
}