feat(creator): 채널 홈 Activity 진입점을 추가한다
This commit is contained in:
@@ -112,6 +112,7 @@
|
||||
</activity>
|
||||
<activity android:name=".main.MainActivity" />
|
||||
<activity android:name=".v2.main.MainV2Activity" />
|
||||
<activity android:name=".v2.creator.channel.CreatorChannelHomeActivity" />
|
||||
<activity
|
||||
android:name=".v2.main.chat.dm.DmChatRoomActivity"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
|
||||
|
||||
@@ -31,6 +31,8 @@ abstract class BaseActivity<T : ViewBinding>(
|
||||
lateinit var binding: T
|
||||
private set
|
||||
|
||||
protected open val shouldApplySystemBarTopInset: Boolean = true
|
||||
|
||||
val screenWidth: Int by lazy {
|
||||
resources.displayMetrics.widthPixels
|
||||
}
|
||||
@@ -81,7 +83,7 @@ abstract class BaseActivity<T : ViewBinding>(
|
||||
|
||||
// 루트는 좌/우/하만 처리(상단은 Toolbar에 위임). IME가 등장하면 하단 패딩을 IME 높이까지 확장
|
||||
val left = max(systemBars.left, ime.left)
|
||||
val top = systemBars.top
|
||||
val top = if (shouldApplySystemBarTopInset) systemBars.top else 0
|
||||
val right = max(systemBars.right, ime.right)
|
||||
val bottom = max(systemBars.bottom, ime.bottom)
|
||||
v.setPadding(left, top, right, bottom)
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
package kr.co.vividnext.sodalive.v2.creator.channel
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.chat.talk.room.ChatRoomActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityCreatorChannelHomeBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
|
||||
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelScheduleResponse
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHeaderUiModel
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHomeUiState
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTab
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTitleBarState
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.CreatorChannelHomeSectionAdapter
|
||||
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class CreatorChannelHomeActivity : BaseActivity<ActivityCreatorChannelHomeBinding>(
|
||||
ActivityCreatorChannelHomeBinding::inflate
|
||||
) {
|
||||
|
||||
private val viewModel: CreatorChannelHomeViewModel by viewModel()
|
||||
private val sectionAdapter = CreatorChannelHomeSectionAdapter(::onScheduleClicked)
|
||||
private var creatorId: Long = 0L
|
||||
private var currentHeader: CreatorChannelHeaderUiModel? = null
|
||||
|
||||
override val shouldApplySystemBarTopInset: Boolean = false
|
||||
|
||||
override fun setupView() {
|
||||
creatorId = intent.getLongExtra(EXTRA_CREATOR_ID, 0L)
|
||||
if (creatorId <= 0L) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
setupRecyclerView()
|
||||
setStatusBarIconAppearance()
|
||||
setTitleBarTopInset()
|
||||
setupClickListeners()
|
||||
observeViewModel()
|
||||
viewModel.loadHome(creatorId)
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
binding.rvHomeSections.layoutManager = LinearLayoutManager(this)
|
||||
binding.rvHomeSections.adapter = sectionAdapter
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
binding.ivMore.setOnClickListener { onMoreClicked() }
|
||||
binding.tvChatButton.setOnClickListener {
|
||||
currentHeader?.characterId?.let { characterId -> viewModel.createChatRoom(characterId) }
|
||||
}
|
||||
binding.tvDmButton.setOnClickListener {
|
||||
startActivity(DmChatRoomActivity.newIntentByCreatorId(this, creatorId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeViewModel() {
|
||||
viewModel.homeStateLiveData.observe(this) { state ->
|
||||
when (state) {
|
||||
is CreatorChannelHomeUiState.Content -> bindContent(state)
|
||||
is CreatorChannelHomeUiState.Error -> Unit
|
||||
CreatorChannelHomeUiState.Empty -> Unit
|
||||
CreatorChannelHomeUiState.Loading -> Unit
|
||||
}
|
||||
}
|
||||
viewModel.chatRoomIdLiveData.observe(this) { event ->
|
||||
event.consume()?.let { chatRoomId ->
|
||||
startActivity(ChatRoomActivity.newIntent(this, chatRoomId))
|
||||
}
|
||||
}
|
||||
viewModel.toastLiveData.observe(this) { event ->
|
||||
event.consume()?.let {
|
||||
val message = it.message ?: it.resId?.let(::getString)
|
||||
message?.let { text -> Toast.makeText(applicationContext, text, Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindContent(content: CreatorChannelHomeUiState.Content) {
|
||||
currentHeader = content.header
|
||||
bindHeader(content.header)
|
||||
bindTitleBar(content.header)
|
||||
bindTabs(content.tabs)
|
||||
sectionAdapter.submitItems(content.sections)
|
||||
}
|
||||
|
||||
private fun bindHeader(header: CreatorChannelHeaderUiModel) {
|
||||
binding.tvNickname.text = header.nickname
|
||||
binding.tvFollowerCount.text = getString(
|
||||
R.string.creator_channel_follower_count,
|
||||
header.followerCount.moneyFormat()
|
||||
)
|
||||
binding.ivHeaderImage.loadUrl(header.profileImageUrl) {
|
||||
placeholder(R.drawable.ic_placeholder_profile)
|
||||
error(R.drawable.ic_placeholder_profile)
|
||||
}
|
||||
updateActionButtonLayout(
|
||||
isChatVisible = header.isAiChatAvailable && header.characterId != null,
|
||||
isDmVisible = header.isDmAvailable
|
||||
)
|
||||
}
|
||||
|
||||
private fun bindTitleBar(header: CreatorChannelHeaderUiModel) {
|
||||
val titleBarState = CreatorChannelTitleBarState.from(
|
||||
isFollow = header.isFollow,
|
||||
isNotify = header.isNotify,
|
||||
isInProgress = false
|
||||
)
|
||||
binding.ivFollow.setImageResource(titleBarState.followIconResId)
|
||||
binding.layoutFollowCapsule.isEnabled = titleBarState.isActionEnabled
|
||||
binding.layoutFollowCapsule.setBackgroundResource(
|
||||
if (header.isFollow) {
|
||||
R.drawable.bg_creator_channel_following_capsule
|
||||
} else {
|
||||
R.drawable.bg_creator_channel_follow_capsule
|
||||
}
|
||||
)
|
||||
binding.layoutFollowCapsule.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
width = if (header.isFollow) 36.dpToPx().toInt() else LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
}
|
||||
binding.tvFollowLabel.isVisible = !header.isFollow
|
||||
titleBarState.bellIconResId?.let {
|
||||
binding.ivBell.setImageResource(it)
|
||||
binding.ivBell.visibility = View.VISIBLE
|
||||
} ?: run {
|
||||
binding.ivBell.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindTabs(tabs: List<CreatorChannelTab>) {
|
||||
binding.tabContainer.removeAllViews()
|
||||
tabs.forEachIndexed { index, tab ->
|
||||
binding.tabContainer.addView(createTabView(tab, isSelected = index == 0))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTabView(tab: CreatorChannelTab, isSelected: Boolean): LinearLayout {
|
||||
val tabText = TextView(this).apply {
|
||||
text = getString(tab.labelResId)
|
||||
gravity = Gravity.CENTER
|
||||
setTextColor(getColor(if (isSelected) R.color.white else R.color.gray_500))
|
||||
setTypeface(null, Typeface.NORMAL)
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
0,
|
||||
1f
|
||||
)
|
||||
}
|
||||
tabText.textSize = 16f
|
||||
val indicator = View(this).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
3.dpToPx().toInt()
|
||||
)
|
||||
}
|
||||
indicator.setBackgroundColor(getColor(R.color.soda_400))
|
||||
indicator.isVisible = isSelected
|
||||
return LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
gravity = Gravity.CENTER
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
).apply {
|
||||
width = 110.dpToPx().toInt()
|
||||
}
|
||||
addView(tabText)
|
||||
addView(indicator)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTitleBarTopInset() {
|
||||
val baseTitleBarHeight = 60.dpToPx().toInt()
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.titleBarContainer) { view, insets ->
|
||||
val topInset = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top
|
||||
view.updatePadding(top = topInset)
|
||||
view.updateLayoutParams {
|
||||
height = baseTitleBarHeight + topInset
|
||||
}
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
private fun setStatusBarIconAppearance() {
|
||||
WindowCompat.getInsetsController(window, binding.root).isAppearanceLightStatusBars = false
|
||||
}
|
||||
|
||||
private fun updateActionButtonLayout(isChatVisible: Boolean, isDmVisible: Boolean) {
|
||||
binding.tvChatButton.isVisible = isChatVisible
|
||||
binding.tvDmButton.isVisible = isDmVisible
|
||||
(binding.tvDmButton.layoutParams as LinearLayout.LayoutParams).apply {
|
||||
marginStart = if (isChatVisible && isDmVisible) 6.dpToPx().toInt() else 0
|
||||
}.also(binding.tvDmButton::setLayoutParams)
|
||||
}
|
||||
|
||||
private fun onMoreClicked() {
|
||||
Toast.makeText(applicationContext, getString(R.string.creator_channel_more_ready), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun onScheduleClicked(schedule: CreatorChannelScheduleResponse) {
|
||||
when (schedule.type) {
|
||||
CreatorActivityType.Audio,
|
||||
CreatorActivityType.LiveReplay -> startActivity(
|
||||
Intent(this, AudioContentDetailActivity::class.java).apply {
|
||||
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, schedule.targetId)
|
||||
}
|
||||
)
|
||||
|
||||
CreatorActivityType.Live -> showLiveRoomDetail(schedule.targetId)
|
||||
|
||||
CreatorActivityType.Community -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLiveRoomDetail(roomId: Long) {
|
||||
val detailFragment = LiveRoomDetailFragment(
|
||||
roomId,
|
||||
onClickParticipant = {},
|
||||
onClickReservation = {},
|
||||
onClickModify = {},
|
||||
onClickStart = {},
|
||||
onClickCancel = {}
|
||||
)
|
||||
if (detailFragment.isAdded) return
|
||||
|
||||
detailFragment.show(supportFragmentManager, detailFragment.tag)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_CREATOR_ID: String = "extra_creator_id"
|
||||
|
||||
fun newIntent(context: Context, creatorId: Long): Intent {
|
||||
return Intent(context, CreatorChannelHomeActivity::class.java).apply {
|
||||
putExtra(EXTRA_CREATOR_ID, creatorId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
225
app/src/main/res/layout/activity_creator_channel_home.xml
Normal file
225
app/src/main/res/layout/activity_creator_channel_home.xml
Normal file
@@ -0,0 +1,225 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/nested_scroll_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:fillViewport="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/header_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="402dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_header_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/color_222222"
|
||||
android:contentDescription="@null"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@drawable/ic_placeholder_profile" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/bg_creator_channel_header_gradient_bottom" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/bg_creator_channel_header_gradient_top" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/header_text_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/spacing_20"
|
||||
android:paddingBottom="@dimen/spacing_14">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_follower_count"
|
||||
style="@style/Typography.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
tools:text="팔로워 82명" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_nickname"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/bold"
|
||||
android:gravity="center"
|
||||
android:maxLines="2"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="32sp"
|
||||
tools:text="크리에이터 이름" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action_button_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_chat_button"
|
||||
style="@style/Typography.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_creator_channel_outline_button"
|
||||
android:drawableStart="@drawable/ic_new_talk"
|
||||
android:drawablePadding="@dimen/spacing_6"
|
||||
android:gravity="center"
|
||||
android:minWidth="108dp"
|
||||
android:paddingHorizontal="@dimen/spacing_12"
|
||||
android:text="@string/creator_channel_chat_button"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_dm_button"
|
||||
style="@style/Typography.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginStart="@dimen/spacing_6"
|
||||
android:background="@drawable/bg_creator_channel_outline_button"
|
||||
android:drawableStart="@drawable/ic_new_dm"
|
||||
android:drawablePadding="@dimen/spacing_6"
|
||||
android:gravity="center"
|
||||
android:minWidth="108dp"
|
||||
android:paddingHorizontal="@dimen/spacing_12"
|
||||
android:text="@string/creator_channel_dm_button"
|
||||
android:textColor="@color/white" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/horizontal_tab_scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:background="@color/black"
|
||||
android:fillViewport="false"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/tab_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="@dimen/spacing_20" />
|
||||
</HorizontalScrollView>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_home_sections"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:paddingBottom="@dimen/spacing_32"
|
||||
tools:itemCount="4"
|
||||
tools:listitem="@layout/item_creator_channel_home_audio" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/title_bar_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:paddingHorizontal="@dimen/spacing_14"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="@string/a11y_back"
|
||||
android:src="@drawable/ic_new_bar_back"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/title_action_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_follow_capsule"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/bg_creator_channel_follow_capsule"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="@dimen/spacing_8"
|
||||
android:paddingVertical="@dimen/spacing_8"
|
||||
tools:background="@drawable/bg_creator_channel_following_capsule">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_follow"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:contentDescription="@null"
|
||||
tools:src="@drawable/ic_new_follow" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_follow_label"
|
||||
style="@style/Typography.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="@dimen/spacing_6"
|
||||
android:gravity="center"
|
||||
android:text="@string/creator_channel_follow_button"
|
||||
android:textColor="@color/white" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bell"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="@dimen/spacing_14"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="gone"
|
||||
tools:src="@drawable/ic_bar_bell"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_more"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="@dimen/spacing_14"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_new_more" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Reference in New Issue
Block a user