Call 页面初步

This commit is contained in:
renhaoting 2025-10-30 14:16:07 +08:00
parent f30db51681
commit cac84ec7e8
10 changed files with 455 additions and 27 deletions

View File

@ -2,6 +2,8 @@ package com.remax.visualnovel.ui.chat
import android.graphics.Point
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.activity.viewModels
import androidx.core.view.GravityCompat
import androidx.lifecycle.Observer
@ -14,7 +16,6 @@ import com.hjq.permissions.permission.PermissionLists
import com.hjq.permissions.permission.base.IPermission
import com.remax.visualnovel.app.base.BaseBindingActivity
import com.remax.visualnovel.utils.Routers
import com.remax.visualnovel.utils.StatusBarUtils
import com.pengxr.modular.eventbus.generated.events.EventDefineOfUserEvents
import com.remax.visualnovel.R
import com.remax.visualnovel.databinding.ActivityActorChatBinding
@ -25,6 +26,7 @@ import com.remax.visualnovel.extension.launchAndLoadingCollect
import com.remax.visualnovel.extension.launchWithRequest
import com.remax.visualnovel.extension.toast
import com.remax.visualnovel.manager.nim.NimManager
import com.remax.visualnovel.ui.chat.call.ChatCallView
import com.remax.visualnovel.ui.chat.setting.model.ChatModelDialog
import com.remax.visualnovel.ui.chat.ui.HoldToTalkDialog
import com.remax.visualnovel.utils.RecordHelper
@ -38,7 +40,7 @@ import kotlin.getValue
@AndroidEntryPoint
@Route(path = Routers.CHAT)
class ChatActivity : BaseBindingActivity<ActivityActorChatBinding>() {
private var mMode = MODE_TEXT
private val chatViewModel by viewModels<ChatViewModel>()
private val mRecordAssist = RecordAssist()
@ -59,6 +61,7 @@ class ChatActivity : BaseBindingActivity<ActivityActorChatBinding>() {
with(binding) {
initInputPanelEvents()
initCallViewEvents()
}
}
@ -95,6 +98,16 @@ class ChatActivity : BaseBindingActivity<ActivityActorChatBinding>() {
}
}
private fun initCallViewEvents() {
with(binding.callView) {
setEventListener(object : ChatCallView.IEventListener {
override fun onExitCall() {
switchMode(MODE_TEXT)
}
})
}
}
private fun initInputPanelEvents() {
with(binding.inputPanel) {
holdToTalk(
@ -108,6 +121,49 @@ class ChatActivity : BaseBindingActivity<ActivityActorChatBinding>() {
mRecordAssist.onTouchPointChanged(point)
}
)
setEventListener(object : InputPanel.IEventListener {
override fun onEnterCall() {
switchMode(MODE_CALL)
}
override fun onEnterShortChat() {
// TODO("Not yet implemented")
}
override fun onSendText(content: CharSequence) {
// TODO("Not yet implemented")
}
override fun onMsgRvScroll2Bottom() {
// TODO("Not yet implemented")
}
override fun onCheckVipStatus() {
// TODO("Not yet implemented")
}
})
}
}
private fun switchMode(newMode: Int) {
if (newMode != mMode) {
mMode = newMode
binding.run {
when (mMode) {
MODE_VOICE, MODE_TEXT -> {
callView.visibility = GONE
textVoiceChatContainer.visibility = VISIBLE
}
MODE_CALL -> {
callView.visibility = VISIBLE
textVoiceChatContainer.visibility = GONE
}
}
}
}
}
@ -179,8 +235,12 @@ class ChatActivity : BaseBindingActivity<ActivityActorChatBinding>() {
companion object {
const val ACTOR_ID = "ACTOR_ID"
const val MODE_TEXT = 1
const val MODE_VOICE = 2
const val MODE_CALL = 3
const val ACTOR_ID = "ACTOR_ID"
fun start(actorId: Int) {
ARouter.getInstance()
.build(Routers.CHAT)
@ -275,4 +335,5 @@ class ChatActivity : BaseBindingActivity<ActivityActorChatBinding>() {
}
}

View File

@ -6,10 +6,11 @@ import android.graphics.Point
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.FrameLayout
import android.widget.Toast
import com.dylanc.viewbinding.nonreflection.inflate
import com.remax.visualnovel.R
import com.remax.visualnovel.databinding.ChatInputpanelBinding
import com.remax.visualnovel.ui.chat.ChatActivity.Companion.MODE_TEXT
import com.remax.visualnovel.ui.chat.ChatActivity.Companion.MODE_VOICE
class InputPanel @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
@ -17,10 +18,15 @@ class InputPanel @JvmOverloads constructor(context: Context, attrs: AttributeSet
private var binding: ChatInputpanelBinding
private var mMode = MODE_TEXT
private lateinit var mEventListener: IEventListener
companion object {
private const val MODE_TEXT = 1;
private const val MODE_VOICE = 2;
interface IEventListener {
fun onEnterCall()
fun onEnterShortChat()
fun onSendText(content: CharSequence)
fun onMsgRvScroll2Bottom()
fun onCheckVipStatus()
}
@ -29,10 +35,10 @@ class InputPanel @JvmOverloads constructor(context: Context, attrs: AttributeSet
binding.run {
chatPopMenu.setMenuList(mutableListOf(
PopMenuIconView.MenuItem(R.mipmap.chat_ai_talk) {
Toast.makeText(context, "聊天", Toast.LENGTH_SHORT).show()
mEventListener.onEnterShortChat()
},
PopMenuIconView.MenuItem(R.mipmap.chat_ai_phone) {
Toast.makeText(context, "通话", Toast.LENGTH_SHORT).show()
mEventListener.onEnterCall()
}
))
@ -42,20 +48,24 @@ class InputPanel @JvmOverloads constructor(context: Context, attrs: AttributeSet
ivChatSend.setOnClickListener {
chatEditView.clearText()
mEventListener.onSendText(chatEditView.text as CharSequence)
}
ivChatDownload.setOnClickListener {
// TODO
ivGotoBottom.setOnClickListener {
mEventListener.onMsgRvScroll2Bottom()
}
ivChatVip.setOnClickListener {
// TODO
mEventListener.onCheckVipStatus()
}
}
switchMode(mMode)
}
fun setEventListener(listener: IEventListener) {
this.mEventListener = listener
}
private fun switchMode(newMode: Int) {
if (newMode != mMode) {

View File

@ -0,0 +1,48 @@
package com.remax.visualnovel.ui.chat.call
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import com.remax.visualnovel.databinding.LayoutChatCallViewBinding
class ChatCallView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private var mBinding: LayoutChatCallViewBinding
private lateinit var mEventListener: IEventListener
interface IEventListener {
fun onExitCall()
}
init {
mBinding = LayoutChatCallViewBinding.inflate(LayoutInflater.from(context), this, true)
setupClickListeners()
}
private fun setupClickListeners() {
with (mBinding) {
tvHangUp.setOnClickListener {
mEventListener.onExitCall()
}
}
}
fun setEventListener(listener: IEventListener) {
mEventListener = listener
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<size
android:width="375dp"
android:height="295dp"
/>
<gradient android:type="linear"
android:angle="270"
android:startColor="#00000000"
android:endColor="#cc000000"
/>
</shape>

View File

@ -32,25 +32,46 @@
app:layout_constraintBottom_toBottomOf="parent"
>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_msg"
<com.remax.visualnovel.widget.uitoken.view.UITokenConstraintLayout
android:id="@+id/text_voice_chat_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="-55dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/input_panel"
/>
<com.remax.visualnovel.ui.chat.InputPanel
android:id="@+id/input_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_msg"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="-55dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/input_panel"
/>
<com.remax.visualnovel.ui.chat.InputPanel
android:id="@+id/input_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</com.remax.visualnovel.widget.uitoken.view.UITokenConstraintLayout>
<com.remax.visualnovel.ui.chat.call.ChatCallView
android:id="@+id/call_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"
/>
</com.remax.visualnovel.widget.uitoken.view.UITokenConstraintLayout>
</com.remax.visualnovel.widget.uitoken.view.UITokenConstraintLayout>

View File

@ -108,7 +108,7 @@
>
<com.remax.visualnovel.widget.uitoken.view.UITokenImageView
android:id="@+id/iv_chat_download"
android:id="@+id/iv_goto_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/chat_download"

View File

@ -0,0 +1,133 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/top_bg"
android:layout_width="match_parent"
android:layout_height="250dp"
android:background="@drawable/bg_chat_call"
app:layout_constraintBottom_toBottomOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/bottom_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dp_30"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
>
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
android:id="@+id/tv_hangUp"
android:layout_width="@dimen/dp_65"
android:layout_height="@dimen/dp_65"
android:layout_marginBottom="@dimen/dp_30"
android:gravity="center"
android:text="@string/icon_call_hangup"
android:textSize="45sp"
app:backgroundColorToken="@string/color_important_normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:onlyIconFont="true"
app:radiusToken="@string/radius_pill"
app:textColorToken="@string/color_txt_primary_normal" />
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
android:id="@+id/tv_state_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_30"
android:gravity="center"
android:text="@string/call_connecting"
android:textColor="@color/white"
android:textSize="@dimen/sp_14"
app:layout_constraintBottom_toTopOf="@+id/tv_hangUp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
android:id="@+id/tv_fail_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_25"
android:layout_marginHorizontal="@dimen/dp_20"
android:padding="@dimen/dp_14"
android:gravity="center"
android:text="@string/call_fail_hint"
android:textColor="@color/white"
android:textSize="@dimen/sp_12"
app:advBgColor="@color/gray28"
app:advRadius="@dimen/dp_15"
app:layout_constraintBottom_toTopOf="@+id/tv_hangUp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:visibility="gone"
/>
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottie_speaking"
android:layout_width="156dp"
android:layout_height="24dp"
android:visibility="visible"
android:layout_marginBottom="@dimen/dp_23"
app:layout_constraintBottom_toTopOf="@+id/tv_state_hint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="@raw/call_listening" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/bottom_container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
>
<com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:orientation="vertical"
>
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
android:id="@+id/tv_last_voice_text_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_40"
android:gravity="center"
android:text="The threads of fate intertwine once more... I have been awaiting your arrival, seeker.The threads of fate intertwine once more... I have been awaiting your arrival, seeker.The threads of fate intertwine once more... I have been awaiting your arrival, seeker."
android:textColor="@color/chat_call_voice_text_color"
android:textSize="@dimen/sp_14" />
<com.remax.visualnovel.widget.uitoken.view.UITokenTextView
android:id="@+id/tv_last_voice_text_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_30"
android:layout_marginHorizontal="@dimen/dp_40"
android:gravity="center"
android:text="The threads of fate intertwine once more... I have been awaiting your arrival, seeker.The threads of fate intertwine once more"
android:textColor="@color/white"
android:textSize="@dimen/sp_14" />
</com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,137 @@
{
"v": "5.6.10",
"fr": 24,
"ip": 0,
"op": 16,
"w": 56,
"h": 36,
"nm": "voice",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Bar 1",
"sr": 1,
"ks": {
"o": {"a": 0, "k": 100, "ix": 11},
"r": {"a": 0, "k": 0, "ix": 10},
"p": {"a": 0, "k": [10, 18, 0], "ix": 2},
"a": {"a": 0, "k": [0, 0, 0], "ix": 1},
"s": {"a": 0, "k": [100, 100, 100], "ix": 6}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ty": "rc",
"p": {"a": 0, "k": [0, 0], "ix": 3},
"s": {"a": 0, "k": [6, 24], "ix": 2},
"r": {"a": 0, "k": 3, "ix": 4}
},
{
"ty": "fl",
"c": {"a": 0, "k": [0.227, 0.808, 0.337, 1], "ix": 4}
},
{
"ty": "tr",
"p": {"a": 0, "k": [0, 0], "ix": 2},
"a": {"a": 0, "k": [0, 0], "ix": 1},
"s": {"a": 0, "k": [100, 100], "ix": 3},
"r": {"a": 0, "k": 0, "ix": 6}
}
],
"nm": "Bar Shape"
}
],
"ip": 0, "op": 16, "st": 0, "bm": 0
},
{
"ddd": 0,
"ind": 2,
"ty": 4,
"nm": "Bar 2",
"sr": 1,
"ks": {
"o": {"a": 0, "k": 100, "ix": 11},
"r": {"a": 0, "k": 0, "ix": 10},
"p": {"a": 0, "k": [28, 18, 0], "ix": 2},
"a": {"a": 0, "k": [0, 0, 0], "ix": 1},
"s": {"a": 0, "k": [100, 100, 100], "ix": 6}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ty": "rc",
"p": {"a": 0, "k": [0, 0], "ix": 3},
"s": {"a": 0, "k": [6, 24], "ix": 2},
"r": {"a": 0, "k": 3, "ix": 4}
},
{
"ty": "fl",
"c": {"a": 0, "k": [0.227, 0.808, 0.337, 1], "ix": 4}
},
{
"ty": "tr",
"p": {"a": 0, "k": [0, 0], "ix": 2},
"a": {"a": 0, "k": [0, 0], "ix": 1},
"s": {"a": 0, "k": [100, 100], "ix": 3},
"r": {"a": 0, "k": 0, "ix": 6}
}
],
"nm": "Bar Shape"
}
],
"ip": 0, "op": 16, "st": 0, "bm": 0
},
{
"ddd": 0,
"ind": 3,
"ty": 4,
"nm": "Bar 3",
"sr": 1,
"ks": {
"o": {"a": 0, "k": 100, "ix": 11},
"r": {"a": 0, "k": 0, "ix": 10},
"p": {"a": 0, "k": [46, 18, 0], "ix": 2},
"a": {"a": 0, "k": [0, 0, 0], "ix": 1},
"s": {"a": 0, "k": [100, 100, 100], "ix": 6}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ty": "rc",
"p": {"a": 0, "k": [0, 0], "ix": 3},
"s": {"a": 0, "k": [6, 24], "ix": 2},
"r": {"a": 0, "k": 3, "ix": 4}
},
{
"ty": "fl",
"c": {"a": 0, "k": [0.227, 0.808, 0.337, 1], "ix": 4}
},
{
"ty": "tr",
"p": {"a": 0, "k": [0, 0], "ix": 2},
"a": {"a": 0, "k": [0, 0], "ix": 1},
"s": {"a": 0, "k": [100, 100], "ix": 3},
"r": {"a": 0, "k": 0, "ix": 6}
}
],
"nm": "Bar Shape"
}
],
"ip": 0, "op": 16, "st": 0, "bm": 0
}
],
"markers": []
}

View File

@ -192,6 +192,7 @@
<color name="gray6">#ff666666</color>
<color name="gray9">#ff999999</color>
<color name="grayf6">#fff6f6f6</color>
<color name="gray28">#ff282828</color>
<!-- chat settings -->
<color name="glo_color_chat_setting_item_bg">#F6F6F6</color>
@ -208,6 +209,7 @@
<color name="male_text_color">#ff4876c9</color>
<color name="female_bg">#ffe5c4ff</color>
<color name="male_bg">#ffc7dbff</color>
<color name="chat_call_voice_text_color">#66eaeeff</color>

View File

@ -483,5 +483,7 @@
<string name="setting_chat_bubble_title">Chat buttle</string>
<string name="unlocked">Unlocked</string>
<string name="chat_mode">Chat Mode</string>
<string name="call_connecting">Connecting...</string>
<string name="call_fail_hint">Network connection issue. Please check your connection and try again.</string>
</resources>