feat(chat): 채팅 탭 기본 layout을 추가한다
This commit is contained in:
5
app/src/main/res/drawable/bg_chat_floating_button.xml
Normal file
5
app/src/main/res/drawable/bg_chat_floating_button.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/soda_400" />
|
||||
</shape>
|
||||
@@ -1,5 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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" />
|
||||
android:background="@color/black">
|
||||
|
||||
<include
|
||||
android:id="@+id/view_chat_title_bar"
|
||||
layout="@layout/view_title_bar_default"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<include
|
||||
android:id="@+id/view_chat_filter_tabs"
|
||||
layout="@layout/view_capsule_tab_bar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="52dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/view_chat_title_bar" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_chat_rooms"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="@dimen/spacing_48"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/view_chat_filter_tabs"
|
||||
tools:listitem="@layout/item_v2_chat_room" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_chat_floating"
|
||||
android:layout_width="@dimen/spacing_48"
|
||||
android:layout_height="@dimen/spacing_48"
|
||||
android:layout_marginEnd="@dimen/spacing_20"
|
||||
android:layout_marginBottom="@dimen/spacing_20"
|
||||
android:background="@drawable/bg_chat_floating_button"
|
||||
android:contentDescription="@string/screen_chat_floating_button"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_plus_no_bg"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -24,9 +24,17 @@
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_title_bar_actions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_title_bar_menu"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -157,6 +157,7 @@
|
||||
<string name="screen_chat_filter_all">All</string>
|
||||
<string name="screen_chat_filter_ai">AI Chat</string>
|
||||
<string name="screen_chat_filter_dm">DM</string>
|
||||
<string name="screen_chat_floating_button">New chat</string>
|
||||
<string name="live_now">On Air</string>
|
||||
<string name="screen_live_now_all_title">View all</string>
|
||||
<string name="screen_live_now_all_empty_message">There are currently no live broadcasts available, or access may be restricted due to age limits.\nVerify your identity or follow the channel to get notified when a live broadcast starts.</string>
|
||||
|
||||
@@ -157,6 +157,7 @@
|
||||
<string name="screen_chat_filter_all">すべて</string>
|
||||
<string name="screen_chat_filter_ai">AIチャット</string>
|
||||
<string name="screen_chat_filter_dm">DM</string>
|
||||
<string name="screen_chat_floating_button">新しいチャット</string>
|
||||
<string name="live_now">ただいま配信中</string>
|
||||
<string name="screen_live_now_all_title">配信中のライブをすべて見る</string>
|
||||
<string name="screen_live_now_all_empty_message">参加可能なライブがないか、年齢制限で入室できません。\n本人確認を行うか、チャンネルをフォローして\n配信通知を受け取ってみましょう。</string>
|
||||
|
||||
@@ -156,6 +156,7 @@
|
||||
<string name="screen_chat_filter_all">전체</string>
|
||||
<string name="screen_chat_filter_ai">AI 채팅</string>
|
||||
<string name="screen_chat_filter_dm">DM</string>
|
||||
<string name="screen_chat_floating_button">새 대화</string>
|
||||
<string name="live_now">지금 라이브 중</string>
|
||||
<string name="screen_live_now_all_title">지금 라이브 중 전체보기</string>
|
||||
<string name="screen_live_now_all_empty_message">현재 참여 가능한 라이브 방송이 없거나\n연령제한으로 입장이 불가능합니다.\n본인인증을 해보거나 채널을 팔로잉하고\n라이브 방송 알림을 받아보세요.</string>
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package kr.co.vividnext.sodalive.v2.main.chat
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.v2.widget.CapsuleTabBarView
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Assert.assertTrue
|
||||
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 ChatMainFragmentLayoutTest {
|
||||
|
||||
@Test
|
||||
fun `기본 title bar는 title과 가변 action container를 함께 제공한다`() {
|
||||
val titleBar = inflateView(R.layout.view_title_bar_default) as LinearLayout
|
||||
val title = requireNotNull(titleBar.findViewById<TextView>(R.id.tv_title_bar_title))
|
||||
val actions = requireNotNull(titleBar.findViewById<LinearLayout>(R.id.ll_title_bar_actions))
|
||||
val menu = requireNotNull(titleBar.findViewById<ImageView>(R.id.iv_title_bar_menu))
|
||||
|
||||
assertSame(actions, menu.parent)
|
||||
assertSame(titleBar, title.parent)
|
||||
assertSame(titleBar, actions.parent)
|
||||
assertTrue(titleBar.indexOfChild(title) < titleBar.indexOfChild(actions))
|
||||
assertEquals(LinearLayout.HORIZONTAL, actions.orientation)
|
||||
assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, actions.layoutParams.width)
|
||||
assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, actions.layoutParams.height)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `채팅 fragment layout은 title bar tab list floating button만 포함한다`() {
|
||||
val root = inflateView(R.layout.fragment_v2_main_chat) as ConstraintLayout
|
||||
val titleBar = requireNotNull(root.findViewById<View>(R.id.view_chat_title_bar))
|
||||
val tabBar = requireNotNull(root.findViewById<CapsuleTabBarView>(R.id.view_chat_filter_tabs))
|
||||
val recyclerView = requireNotNull(root.findViewById<RecyclerView>(R.id.rv_chat_rooms))
|
||||
val floatingButton = requireNotNull(root.findViewById<ImageView>(R.id.btn_chat_floating))
|
||||
|
||||
assertEquals(Color.BLACK, (root.background as ColorDrawable).color)
|
||||
assertSame(root, titleBar.parent)
|
||||
assertSame(root, tabBar.parent)
|
||||
assertSame(root, recyclerView.parent)
|
||||
assertSame(root, floatingButton.parent)
|
||||
assertFalse(root.containsClassName("com.google.android.material.bottomnavigation.BottomNavigationView"))
|
||||
assertFalse(root.containsViewIdContaining("unread"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `채팅 fragment list와 floating button은 계획된 constraint를 사용한다`() {
|
||||
val root = inflateView(R.layout.fragment_v2_main_chat)
|
||||
val titleBar = requireNotNull(root.findViewById<View>(R.id.view_chat_title_bar))
|
||||
val tabBar = requireNotNull(root.findViewById<CapsuleTabBarView>(R.id.view_chat_filter_tabs))
|
||||
val recyclerView = requireNotNull(root.findViewById<RecyclerView>(R.id.rv_chat_rooms))
|
||||
val floatingButton = requireNotNull(root.findViewById<ImageView>(R.id.btn_chat_floating))
|
||||
val tabParams = tabBar.layoutParams as ConstraintLayout.LayoutParams
|
||||
val listParams = recyclerView.layoutParams as ConstraintLayout.LayoutParams
|
||||
val buttonParams = floatingButton.layoutParams as ConstraintLayout.LayoutParams
|
||||
|
||||
assertEquals(60.dpToPx(), titleBar.layoutParams.height)
|
||||
assertEquals(52.dpToPx(), tabBar.layoutParams.height)
|
||||
assertEquals(R.id.view_chat_title_bar, tabParams.topToBottom)
|
||||
assertEquals(R.id.view_chat_filter_tabs, listParams.topToBottom)
|
||||
assertEquals(ConstraintLayout.LayoutParams.PARENT_ID, listParams.bottomToBottom)
|
||||
assertEquals(false, recyclerView.clipToPadding)
|
||||
assertTrue(recyclerView.paddingBottom >= 28.dpToPx())
|
||||
assertEquals(ConstraintLayout.LayoutParams.PARENT_ID, buttonParams.endToEnd)
|
||||
assertEquals(ConstraintLayout.LayoutParams.PARENT_ID, buttonParams.bottomToBottom)
|
||||
}
|
||||
|
||||
private fun inflateView(layoutResId: Int): View {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
return LayoutInflater.from(context).inflate(layoutResId, null, false)
|
||||
}
|
||||
|
||||
private fun View.containsViewIdContaining(idNamePart: String): Boolean {
|
||||
if (id != View.NO_ID) {
|
||||
val idName = runCatching { resources.getResourceEntryName(id) }.getOrNull()
|
||||
if (idName != null && idName.contains(idNamePart, ignoreCase = true)) return true
|
||||
}
|
||||
if (this !is ViewGroup) return false
|
||||
return (0 until childCount).any { getChildAt(it).containsViewIdContaining(idNamePart) }
|
||||
}
|
||||
|
||||
private fun View.containsClassName(className: String): Boolean {
|
||||
if (javaClass.name == className) return true
|
||||
if (this !is ViewGroup) return false
|
||||
return (0 until childCount).any { getChildAt(it).containsClassName(className) }
|
||||
}
|
||||
|
||||
private fun Int.dpToPx(): Int {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
return (this * context.resources.displayMetrics.density).toInt()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user