[Chat]车聊聊架构升级

This commit is contained in:
renwj
2022-03-09 11:18:30 +08:00
committed by zhongchao
parent b16e7edbd3
commit 9275ed5ff2
178 changed files with 8116 additions and 301 deletions

View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,82 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
id 'com.alibaba.arouter'
}
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode Integer.valueOf(VERSION_CODE)
versionName getValueFromRootProperties("${project.name.replace("-", "_").toUpperCase()}_VERSION")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
//ARouter apt 参数
kapt {
useBuildCache = false
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation rootProject.ext.dependencies.androidxccorektx
implementation rootProject.ext.dependencies.androidxappcompat
implementation rootProject.ext.dependencies.arouter
implementation rootProject.ext.dependencies.rxandroid
compileOnly rootProject.ext.dependencies.aspectj
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.mogowebsocket
implementation rootProject.ext.dependencies.circleimageview
implementation rootProject.ext.dependencies.androidxconstraintlayout
implementation rootProject.ext.dependencies.androidxrecyclerview
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.modulecommon
implementation rootProject.ext.dependencies.moduleservice
implementation rootProject.ext.dependencies.mogo_core_data
implementation rootProject.ext.dependencies.mogo_core_utils
implementation rootProject.ext.dependencies.mogo_core_function_api
implementation rootProject.ext.dependencies.mogo_core_function_call
implementation rootProject.ext.dependencies.mogo_core_res
} else {
implementation project(':modules:mogo-module-common')
implementation project(':modules:mogo-module-service')
implementation project(':core:mogo-core-data')
implementation project(':core:mogo-core-utils')
implementation project(':core:mogo-core-function-api')
implementation project(':core:mogo-core-function-call')
implementation project(':core:mogo-core-res')
}
}
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()

View File

@@ -0,0 +1,3 @@
GROUP=com.mogo.eagle.core.function.impl
POM_ARTIFACT_ID=v2x
VERSION_CODE=1

View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.eagle.core.function.chat">
</manifest>

View File

@@ -0,0 +1,37 @@
package com.mogo.eagle.core.function.chat
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.eagle.core.function.api.chat.IMoGoChatProvider
import com.mogo.eagle.core.function.api.chat.biz.ChatConsts
import com.mogo.eagle.core.function.api.chat.biz.IMoGoChatFacade
import com.mogo.eagle.core.function.chat.facade.MoGoChatFacade
import com.mogo.eagle.core.function.chat.facade.ui.CallChatWindowManager
import kotlinx.coroutines.ExperimentalCoroutinesApi
@Route(path = ChatConsts.CHAT_PROVIDER_PATH)
class MoGoChatProvider: IMoGoChatProvider {
private val facade by lazy {
MoGoChatFacade
}
private val chatWindowManager by lazy {
CallChatWindowManager()
}
override val functionName = "MoGoChatProvider"
@OptIn(ExperimentalCoroutinesApi::class)
override fun init(context: Context?) {
facade.init(context)
chatWindowManager.init()
}
override fun onDestroy() {
facade.onDestroy()
chatWindowManager.destroy()
}
override fun chat(): IMoGoChatFacade = facade
}

View File

@@ -0,0 +1,17 @@
package com.mogo.eagle.core.function.chat.facade.analytics
object ChatAnalyticsEvents {
const val INVOKE_TRACK_REQUEST_CALL_SHOW = "carchat_requestcall_card_show"
const val INVOKE_TRACK_REQUEST_CALL = "carchat_cardetail_click"
const val INVOKE_TRACK_REFUSE = "carchat_refuse_card_show"
const val INVOKE_TRACK_CHATTING = "carchat_phoneing_card_show"
const val INVOKE_TRACK_MATCH_SHOW = "carchat_carphonecall_match_show"
const val INVOKE_TRACK_MATCH_FAIL_CLOSE_CLICK = "carchat_match_fail_close_click"
const val MATCH_TYPE_MANUAL = 1
const val MATCH_TYPE_VOICE = 2
}

View File

@@ -0,0 +1,11 @@
package com.mogo.eagle.core.function.chat.facade.analytics
import com.mogo.eagle.core.function.api.chat.biz.IMoGoAnalyticsFacade
import com.mogo.eagle.core.function.chat.facade.bridge.BridgeApi
object ChatAnalyticsFacade: IMoGoAnalyticsFacade {
override fun track(eventType: String, data: Map<String, Any>?) {
BridgeApi.analytics()?.track(eventType, data)
}
}

View File

@@ -0,0 +1,73 @@
package com.mogo.eagle.core.function.chat.facade.aop
import android.os.Looper
import android.util.Log
import com.mogo.commons.debug.DebugConfig
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.reflect.CodeSignature
import org.aspectj.lang.reflect.MethodSignature
open class BaseAspectj {
companion object {
@Volatile
private var enable: Boolean = DebugConfig.isDebug()
}
fun enterMethod(joinPoint: ProceedingJoinPoint) {
if (!enable) return
val signature = joinPoint.signature as CodeSignature
val cls = signature.declaringType
val methodName = signature.name
val parameterNames = signature.parameterNames
val parameterValues = joinPoint.args
val builder = StringBuilder("\u21E2 ")
builder.append(methodName).append('(')
parameterValues.forEachIndexed { index, _ ->
if (index > 0) {
builder.append(",")
}
builder.append(parameterNames[index]).append('=')
builder.append(parameterValues[index])
}
builder.append(')')
if (Looper.myLooper() != Looper.getMainLooper()) {
builder.append("[Thread:\"").append(Thread.currentThread().name).append("\"]")
}
Log.i(asTag(cls), builder.toString())
}
fun exitMethod(joinPoint: ProceedingJoinPoint, result: Any?, lengthMill: Long) {
if (!enable) return
val signature = joinPoint.signature
val cls = signature.declaringType
val methodName = signature.name
val hasReturnType = signature is MethodSignature
&& signature.returnType != Void.TYPE
val builder = StringBuilder("\u21E0 ")
.append(methodName)
.append("[")
.append(lengthMill)
.append("ms]")
if (hasReturnType) {
builder.append(" = ")
builder.append(result.let {
result.toString()
})
}
Log.i(asTag(cls), builder.toString())
}
private fun asTag(cls: Class<*>): String {
if (cls.isAnonymousClass) {
return asTag(cls.enclosingClass!!)
}
return cls.simpleName
}
}

View File

@@ -0,0 +1,5 @@
package com.mogo.eagle.core.function.chat.facade.aop
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.CONSTRUCTOR)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class DebugLog

View File

@@ -0,0 +1,42 @@
package com.mogo.eagle.core.function.chat.facade.aop
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import java.util.concurrent.TimeUnit
@Aspect
class LogAspectj : BaseAspectj() {
@Pointcut("within(@com.mogo.eagle.core.function.chat.facade.aop.DebugLog *)")
fun withinAnnotatedClass() {
}
@Pointcut("execution(!synthetic * *(..))&& withinAnnotatedClass()")
fun methodInsideAnnotatedType() {
}
@Pointcut("execution(!synthetic *.new(..))&& withinAnnotatedClass()")
fun constructorInsideAnnotatedType() {
}
@Pointcut("execution(@com.mogo.eagle.core.function.chat.facade.aop.DebugLog * *(..))|| methodInsideAnnotatedType()")
fun method() {
}
@Pointcut("execution(@com.mogo.eagle.core.function.chat.facade.aop.DebugLog *.new(..))|| constructorInsideAnnotatedType()")
fun constructor() {
}
@Around("method() || constructor()")
fun logExecute(joinPoint: ProceedingJoinPoint) {
enterMethod(joinPoint)
val startNanos = System.nanoTime()
val result = joinPoint.proceed()
val stopNanos = System.nanoTime()
val lengthMill = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos)
exitMethod(joinPoint, result, lengthMill)
}
}

View File

@@ -0,0 +1,115 @@
package com.mogo.eagle.core.function.chat.facade.audio
import android.content.Context
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.media.AudioManager.OnAudioFocusChangeListener
import android.os.Build
import android.os.Handler
import android.os.Looper
import com.mogo.eagle.core.function.api.chat.biz.ChatConsts
import com.mogo.eagle.core.function.api.chat.biz.IMoGoAudioFocusFacade
import com.mogo.eagle.core.function.chat.facade.bridge.BridgeApi
import com.mogo.eagle.core.function.chat.facade.utils.log
import kotlinx.coroutines.*
import kotlinx.coroutines.android.asCoroutineDispatcher
import java.util.concurrent.TimeUnit.SECONDS
import java.util.concurrent.atomic.AtomicReference
object AudioFocusFacade: IMoGoAudioFocusFacade {
private val audioListener: MyAudioFocusChangeListener by lazy {
MyAudioFocusChangeListener()
}
private val audioManager: AudioManager by lazy {
BridgeApi.context().getSystemService(Context.AUDIO_SERVICE) as AudioManager
}
private var audioFocusRequest: Any? = null
private val scope by lazy {
CoroutineScope(Handler(Looper.getMainLooper()).asCoroutineDispatcher() + SupervisorJob())
}
private val focusJob by lazy { AtomicReference<Job>() }
@Volatile
private var hasAudioFocus = false
override fun requireAudioFocus(onGetFocus: () -> Unit) {
log(ChatConsts.TAG, "requireDuck===$hasAudioFocus")
scope.launch {
if (!hasAudioFocus) {
while (true) {
try {
val res = audioManager.let {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
it.requestAudioFocus(audioListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
} else {
val audioFocusRequest =
AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
.setAcceptsDelayedFocusGain(true)
.setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setOnAudioFocusChangeListener(audioListener).build()
this@AudioFocusFacade.audioFocusRequest = audioFocusRequest
it.requestAudioFocus(audioFocusRequest)
}
}
if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
hasAudioFocus = true
onGetFocus.invoke()
break
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
delay(SECONDS.toMillis(1))
}
}
} else {
onGetFocus.invoke()
}
}.also { focusJob.set(it) }
}
override fun releaseAudioFocus() {
log(ChatConsts.TAG, "releaseDuck===$hasAudioFocus")
focusJob.get()?.takeIf { it.isActive }?.cancel()
scope.launch {
if (hasAudioFocus) {
hasAudioFocus = false
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
audioManager.abandonAudioFocus(audioListener)
} else {
(audioFocusRequest as? AudioFocusRequest)?.also {
audioManager.abandonAudioFocusRequest(it)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
private class MyAudioFocusChangeListener : OnAudioFocusChangeListener {
override fun onAudioFocusChange(focusChange: Int) {
log(ChatConsts.TAG, "onAudioFocusChange: $focusChange")
when (focusChange) {
AudioManager.AUDIOFOCUS_GAIN -> {
hasAudioFocus = true
}
AudioManager.AUDIOFOCUS_LOSS,
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> hasAudioFocus = false
}
}
}
}

View File

@@ -0,0 +1,35 @@
package com.mogo.eagle.core.function.chat.facade.bridge
import android.content.Context
import com.mogo.eagle.core.utilcode.util.Utils
import com.mogo.module.common.MogoApisHandler
import java.lang.ref.WeakReference
internal object BridgeApi {
private var contextHolder: WeakReference<Context>? = null
private val apis by lazy {
MogoApisHandler.getInstance().apis
}
internal fun init(context: Context?) {
context?.let {
contextHolder = WeakReference(context)
}
}
internal fun context(): Context = contextHolder?.get() ?: Utils.getApp()
internal fun locationClient() = apis?.mapServiceApi?.getSingletonLocationClient(context())
internal fun intentManager() = apis?.intentManagerApi
internal fun statusManager() = apis?.statusManagerApi
internal fun analytics() = apis?.analyticsApi
internal fun topViewManager() = apis?.topViewManager
internal fun floatViewManager() = apis?.windowManagerApi
}

View File

@@ -0,0 +1,79 @@
package com.mogo.eagle.core.function.chat.facade.consts
import com.mogo.commons.debug.DebugConfig
//WebSocket发送数据相关
const val SOCKET_HAND_SHAKE = 0
const val SOCKET_HEART_BEAT = 1
// 与相关接口复用
/**
* 服务端下发消息状态为0存在三种场景
* 1.语音聊天创建房间成功,给拨打方发送创建房间消息。
* 2.有匹配过的车机,表示愿意聊天,如果有人匹配不到,会给有愿意聊天的用户发来打电话邀请
* 3.接收车队邀请消息
*/
const val PUSH_MSG_CREATE_ROOM = 0
/**
* 服务端下发消息状态为1存在三种场景
* 1.语音聊天对方同意,下发同意消息
* 2.匹配成功消息
* 3.车队邀请对方同意,下发同意消息
*/
const val PUSH_MSG_AGREE_ENTER = 1
/**
* 服务端下发消息状态为2存在三种场景
* 1.语音邀请被拒绝
* 2.被动匹配接收到来电邀请,被拒绝
* 3.车队邀请被拒绝
*/
const val PUSH_MSG_DENY_ENTER = 2
/**
* 服务端下发消息状态为3存在两种场景
* 1.语音和匹配接收到消息下发,挂断电话
* 2.车队接收到消息下发,如果消息中有队员,则接收到队员退房消息,若无队员,则挂断车队电话
*/
const val PUSH_MSG_HANG_UP = 3
class ChatHttp {
companion object {
private const val DEV_BASE_URL_OWNER = "http://dzt-show.zhidaohulian.com/"
private const val DEV_CONFIG_URL = "http://dzt-test.zhidaohulian.com/"
private const val RELEASE_BASE_URL_OWNER = "http://dzt.zhidaohulian.com/"
private const val SOCKET_SERVER = "ws://62.234.196.121:4001/ws"
private const val DEV_SOCKET_SERVER = "ws://dzt-test.zhidaohulian.com/ws"
fun getBaseUrl(): String {
return when (DebugConfig.getNetMode()) {
DebugConfig.NET_MODE_DEV, DebugConfig.NET_MODE_QA, DebugConfig.NET_MODE_DEMO -> DEV_BASE_URL_OWNER
DebugConfig.NET_MODE_RELEASE -> RELEASE_BASE_URL_OWNER
else -> RELEASE_BASE_URL_OWNER
}
}
fun getSocketServer(): String {
return when (DebugConfig.getNetMode()) {
DebugConfig.NET_MODE_DEV, DebugConfig.NET_MODE_QA, DebugConfig.NET_MODE_DEMO -> DEV_SOCKET_SERVER
DebugConfig.NET_MODE_RELEASE -> SOCKET_SERVER
else -> SOCKET_SERVER
}
}
fun getConfig(): String {
return when (DebugConfig.getNetMode()) {
DebugConfig.NET_MODE_DEV, DebugConfig.NET_MODE_QA, DebugConfig.NET_MODE_DEMO -> DEV_CONFIG_URL
DebugConfig.NET_MODE_RELEASE -> RELEASE_BASE_URL_OWNER
else -> RELEASE_BASE_URL_OWNER
}
}
}
}

View File

@@ -0,0 +1,236 @@
package com.mogo.eagle.core.function.chat.facade.gme
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.text.TextUtils
import android.util.Log
import com.gme.TMG.ITMGContext
import com.gme.av.sdk.AVError
import com.gme.av.sig.AuthBuffer
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.function.api.chat.biz.ChatConsts
import com.mogo.eagle.core.function.chat.facade.utils.log
import com.mogo.eagle.core.utilcode.util.BuildConfig
import com.mogo.eagle.core.utilcode.util.Utils
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicBoolean
internal object GMEApi {
//GME相关信息
private const val ROOM_TYPE = 1
private const val SDKID = "1400280276"
private const val SDKKEY = "I0USylN9YQq0CAiq"
private var openId: String? = null
private val hasInit by lazy {
AtomicBoolean(false)
}
private val Intent.result: Int
get() = getIntExtra("result" , -1)
private val Intent.errorInfo: String
get() = getStringExtra("error_info") ?: ""
private val Intent.eventId: Int
get() = getIntExtra("event_id", 0)
private val Intent.users: Array<String>
get() = getStringArrayExtra("user_list") ?: emptyArray()
private var cb: ((state: GmeState) -> Unit)? = null
private var ctx: WeakReference<Context>? = null
private val tmgCtx by lazy {
ITMGContext.GetInstance(ctx?.get()?: Utils.getApp())
}
private val delegate = object : ITMGContext.ITMGDelegate() {
override fun OnEvent(type: ITMGContext.ITMG_MAIN_EVENT_TYPE?, data: Intent?) {
super.OnEvent(type, data)
type ?: return
data ?: return
if (type == ITMGContext.ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_ENTER_ROOM) {
val code = data.result
if (code == AVError.AV_OK) {
cb?.invoke(GmeState.EnterRoomSuccess)
} else {
cb?.invoke(GmeState.EnterRoomFail(code, data.errorInfo))
}
}
if (type == ITMGContext.ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVENT_TYPE_EXIT_ROOM) {
cb?.invoke(GmeState.ExitRoomSuccess)
}
if (type == ITMGContext.ITMG_MAIN_EVENT_TYPE.ITMG_MAIN_EVNET_TYPE_USER_UPDATE) {
val eventId = data.eventId
val users = data.users
log(ChatConsts.TAG, "RoomMembersUpdate: ${users.joinToString(",")}")
if (users.isEmpty()) {
log(ChatConsts.TAG, "没人了,准备退房")
cb?.invoke(GmeState.UserChangeInRoom(isEnter = false, emptyArray()))
return
}
val filtered = users.filter {
log(ChatConsts.TAG, "成员进房====$it===ownId:$openId")
it.toInt() > 99999
}.toTypedArray()
if (filtered.isEmpty()) {
log(ChatConsts.TAG, "成员为空,无操作")
return
}
if (eventId == ITMGContext.ITMG_EVENT_ID_USER_ENTER) {
log(ChatConsts.TAG, "成员进房==去掉99999====$filtered")
cb?.invoke(GmeState.UserChangeInRoom(isEnter = true, filtered))
}
if (eventId == ITMGContext.ITMG_EVENT_ID_USER_EXIT) {
cb?.invoke(GmeState.UserChangeInRoom(isEnter = false, filtered))
}
}
}
}
fun enableAudio() {
Log.d(ChatConsts.TAG, "-- enable audio --- 1 ---")
if (!hasInit.get()) {
return
}
Log.d(ChatConsts.TAG, "-- enable audio --- 2 ---")
val audioCtrl = tmgCtx.GetAudioCtrl()
//开启麦克
audioCtrl?.EnableMic(true)
//开启扬声器
audioCtrl?.EnableSpeaker(true)
//设置扬声器音量
audioCtrl?.SetSpeakerVolume(200)
//解除麦克风静音
audioCtrl?.SetMicVolume(100)
}
fun disableAudio() {
Log.d(ChatConsts.TAG, "-- disable audio --- 1 ---")
if (!hasInit.get()) {
return
}
Log.d(ChatConsts.TAG, "-- disable audio --- 2 ---")
val audioCtrl = tmgCtx.GetAudioCtrl()
audioCtrl?.EnableSpeaker(false)
//开启麦克
audioCtrl?.EnableMic(false)
}
fun init(ctx: Context, openId: String, cb: ((s: GmeState) -> Unit)? = null) {
if (hasInit.get()) {
log(ChatConsts.TAG, "Error: GmeApi has initialized.")
return
}
hasInit.set(true)
try {
this.ctx = WeakReference(ctx)
this.cb = cb
tmgCtx.SetTMGDelegate(delegate)
tmgCtx.SetLogLevel(ITMGContext.ITMG_LOG_LEVEL_INFO, ITMGContext.ITMG_LOG_LEVEL_VERBOSE)
this.openId = openId
val ret = tmgCtx.Init(SDKID, openId)
if (ret == AVError.AV_OK) {
GmePoller.start()
cb?.invoke(GmeState.InitSuccess(openId))
} else {
cb?.invoke(GmeState.InitFail(ret))
}
} catch (t: Throwable) {
log(ChatConsts.TAG, "GMEAPI - error: $t")
}
}
fun enterRoom(roomId: String) {
if (tmgCtx.IsRoomEntered()) {
return
}
tmgCtx.EnterRoom(roomId, ROOM_TYPE, createAuthBuffer(roomId))
}
fun exitRoom() {
if (!tmgCtx.IsRoomEntered()) {
return
}
tmgCtx.ExitRoom()
}
fun isRoomEntered() = tmgCtx.IsRoomEntered()
fun unInit() {
if (!hasInit.get()) {
return
}
hasInit.set(false)
cb = null
tmgCtx.SetTMGDelegate(null)
GmePoller.stop()
tmgCtx.Uninit()
}
private fun createAuthBuffer(roomId: String?): ByteArray? {
return if (TextUtils.isEmpty(roomId)) {
AuthBuffer.getInstance().genAuthBuffer(SDKID.toInt(), "0", openId, SDKKEY)
} else {
AuthBuffer.getInstance().genAuthBuffer(SDKID.toInt(), roomId, openId, SDKKEY)
}
}
fun muteVoice(mute: Boolean) {
if (!isRoomEntered()) {
return
}
if (mute) {
tmgCtx.GetAudioCtrl()?.SetMicVolume(0)
} else {
tmgCtx.GetAudioCtrl()?.SetMicVolume(100)
}
}
}
sealed class GmeState {
class InitSuccess(val openId: String): GmeState()
class InitFail(val code: Int): GmeState()
object EnterRoomSuccess: GmeState()
class EnterRoomFail(val code: Int, val msg: String): GmeState()
class UserChangeInRoom(val isEnter: Boolean, val left: Array<String>): GmeState()
object ExitRoomSuccess: GmeState()
}
internal object GmePoller {
private val handler by lazy { Handler() }
private val poll: Runnable = object : Runnable {
override fun run() {
if (ITMGContext.GetInstance(null) != null) {
ITMGContext.GetInstance(null).Poll()
}
handler.postDelayed(this, 30)
}
}
internal fun start() {
handler.postDelayed(poll, 30)
}
internal fun stop() {
handler.removeCallbacks(poll)
}
}

View File

@@ -0,0 +1,61 @@
package com.mogo.eagle.core.function.chat.facade.media
import android.content.Context
import android.media.AudioAttributes
import android.media.MediaPlayer
import android.os.Build
import com.mogo.eagle.core.function.api.chat.biz.ChatConsts
import com.mogo.eagle.core.function.api.chat.biz.IMoGoMediaFacade
import com.mogo.eagle.core.function.chat.facade.utils.log
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicReference
internal object MedialControlFacade : IMoGoMediaFacade {
private val player by lazy {
AtomicReference<WeakReference<MediaPlayer>>()
}
override fun play(context: Context, audioSources: Int, isLoop: Boolean, channel: Int) {
val player = player.get()?.get() ?: MediaPlayer().also { player.set(WeakReference(it)) }
resetStatus(player)
val file = context.resources.openRawResourceFd(audioSources)
player.apply {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
setAudioStreamType(channel)
} else {
setAudioAttributes(AudioAttributes.Builder().setLegacyStreamType(channel).build())
}
setDataSource(file.fileDescriptor, file.startOffset, file.length)
file.close()
isLooping = isLoop
prepareAsync()
setOnPreparedListener { player ->
log(ChatConsts.TAG,"real play 准备播放音频====")
player.start()
}
setOnCompletionListener { player ->
log(ChatConsts.TAG,"播放完成====")
player.reset()
}
}
}
override fun release() {
val player = this.player.get()?.get() ?: return
log(ChatConsts.TAG,"release 释放音频播放====")
player.run {
resetStatus(this)
release()
}
}
private fun resetStatus(player: MediaPlayer) {
player.run {
if (isPlaying) {
stop()
}
reset()
}
}
}

View File

@@ -0,0 +1,156 @@
package com.mogo.eagle.core.function.chat.facade.net
import com.alibaba.android.arouter.launcher.ARouter
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.data.BaseResponse
import com.mogo.eagle.core.data.chat.UserInfo
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.api.chat.biz.ChatConsts
import com.mogo.eagle.core.function.api.chat.biz.IMCallType.CALL_TYPE_VOICE
import com.mogo.eagle.core.function.chat.facade.bridge.BridgeApi
import com.mogo.eagle.core.function.chat.facade.consts.ChatHttp
import com.mogo.eagle.core.function.chat.facade.consts.ChatHttp.Companion.getConfig
import com.mogo.eagle.core.function.chat.facade.net.bean.*
import com.mogo.eagle.core.function.chat.facade.utils.log
import com.mogo.service.IMogoServiceApis
import retrofit2.http.*
import java.lang.IllegalStateException
internal class ChatServiceModel {
suspend fun isOnline(sn: String): Boolean {
val map = hashMapOf<String, String>()
map["sn"] = sn
return apiCall {
getNetWorkApi()?.isOnLine(map) ?: throw IllegalStateException("apis is null.")
}.takeIf { it.code == 0 || it.code == 200 }?.let {
val result = it.result
return@let result.carOnLine == 1 && result.onLine == 1
} ?: false
}
suspend fun queryUserInfo(sn: String): Pair<Error?,UserInfo?>? {
val sns = arrayListOf(sn)
val requestData = SnArrayRequestBody().also { it.sns = sns }
return apiCall {
getNetWorkApi(getConfig())?.queryUserInfoBySnS(requestData) ?: throw IllegalStateException("apis is null.")
}.let { itx ->
if (itx.code != 0 && itx.code != 200) {
return@let Pair(Error(itx.code, itx.msg), null)
}
val result = UserInfo()
val json = itx.result
val array = json.getAsJsonArray("info")
if (array == null || array.size() == 0) {
return Pair(null, null)
}
val first = array[0] as? JsonObject ?: return@let null
val userInfo = first.getAsJsonObject("carAndUserInfoRedisVo")
val location = first.getAsJsonObject("realTimeLocationVo")
if (userInfo == null) {
return@let Pair(null, null)
}
result.sn = userInfo.get("sn")?.takeIf { !it.isJsonNull }?.asString ?: sn
result.name = userInfo.get("userNickName")?.takeIf { !it.isJsonNull }?.asString ?: "小蘑菇"
result.icon = userInfo.get("headImgUrl")?.takeIf { !it.isJsonNull }?.asString
result.sex = userInfo.get("cardIdSex")?.takeIf { !it.isJsonNull }?.asString ?: "未设置"
result.age = userInfo.get("cardIdAge")?.takeIf { !it.isJsonNull }?.asString
result.brand = userInfo.get("lastBrandName")?.takeIf { !it.isJsonNull }?.asString
result.city = userInfo.get("lastActiveCity")?.takeIf { !it.isJsonNull }?.asString
if (location != null) {
result.lat = location.get("lat")?.takeIf { !it.isJsonNull }?.asDouble ?: 0.0
result.lon = location.get("lon")?.takeIf { !it.isJsonNull }?.asDouble ?: 0.0
}
return@let Pair(null, result)
}
}
suspend fun requestConnectStatus(params: ConnectStatusParam): BaseResponse<Any> {
val map = hashMapOf<String, String>()
val sn = MoGoAiCloudClientConfig.getInstance().sn
val location = BridgeApi.locationClient()?.lastKnowLocation
if (location != null) {
params.lon = location.longitude
params.lat = location.latitude
}
log(ChatConsts.TAG, "connectStatusParam:$params")
map["sn"] = MoGoAiCloudClientConfig.getInstance().sn
map["data"] = Gson().toJson(params)
return if (params.type == CALL_TYPE_VOICE.type) {
apiCall {
getNetWorkApi()?.requestConnectStatus(sn, map) ?: throw IllegalStateException("apis is null.")
}
} else {
apiCall {
getNetWorkApi()?.requestVehicleTeamConnectStatus(map) ?: throw IllegalStateException("apis is null.")
}
}
}
suspend fun inviteJoinVehicleTeam(param: CallRequestParam): BaseResponse<Any> {
val map = hashMapOf<String, String>()
log(ChatConsts.TAG, "inviteJoinVehicleTeam paras: $param")
map["sn"] = MoGoAiCloudClientConfig.getInstance().sn
map["data"] = Gson().toJson(param)
return apiCall {
getNetWorkApi()?.inviteJoinVehicleTeam(map) ?: throw IllegalStateException("apis is null.")
}
}
suspend fun requestRoomInfo(param: CallRequestParam): BaseResponse<RoomInfo> {
val map = hashMapOf<String, String>()
val location = BridgeApi.locationClient()?.lastKnowLocation
if (location != null) {
param.lat = location.latitude
param.lon = location.longitude
}
log(ChatConsts.TAG, "roomParam:$param")
map["data"] = Gson().toJson(param)
return apiCall {
getNetWorkApi()?.requestRoomInfo(map) ?: throw IllegalStateException("apis is null.")
}
}
private suspend fun <T : Any> apiCall(call: suspend () -> BaseResponse<T>): BaseResponse<T> {
return call.invoke()
}
private fun getNetWorkApi(baseUrl: String = ChatHttp.getBaseUrl()): HttpApi? {
val serviceApi: IMogoServiceApis? = ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS).navigation() as? IMogoServiceApis
return serviceApi?.networkApi?.createNoCallAdapter(HttpApi::class.java, baseUrl)
}
}
internal interface HttpApi {
@FormUrlEncoded
@POST("/yycp-chat-service/car/voiceRoom/no/operate/v1")
suspend fun requestConnectStatus(@Query("sn") sn: String, @FieldMap connectStatus: Map<String, String>): BaseResponse<Any>
//邀请加入车队
@FormUrlEncoded
@POST("/yycp-chat-service/car/chat/no/inviteJoinTeam/v1")
suspend fun inviteJoinVehicleTeam(@FieldMap inviteVehicleTeam: Map<String, String>): BaseResponse<Any>
//车队状态同步
@FormUrlEncoded
@POST("/yycp-chat-service/car/chat/no/operateTeamRoom/v1")
suspend fun requestVehicleTeamConnectStatus(@FieldMap connectStatus: Map<String, String>): BaseResponse<Any>
//查询用户是否在线
@FormUrlEncoded
@POST("/yycp-chat-service/car/queryOnLineBySn/v1")
suspend fun isOnLine(@FieldMap onLine: Map<String, String>): BaseResponse<OnLineStatus>
@POST("/yycp-realtimeLocations/realTimeLocationServer/queryRsAncCarAndUserInfoBySns")
suspend fun queryUserInfoBySnS(@Body body: SnArrayRequestBody): BaseResponse<JsonObject>
//语音房间信息原路径dataService
@FormUrlEncoded
@POST("/yycp-chat-service/car/sender/no/createRoom/v1")
suspend fun requestRoomInfo(@FieldMap roomInfo: Map<String, String>): BaseResponse<RoomInfo>
}

View File

@@ -0,0 +1,50 @@
package com.mogo.eagle.core.function.chat.facade.net.bean
import androidx.annotation.Keep
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
import com.mogo.eagle.core.function.api.chat.biz.IMCallType
import com.mogo.eagle.core.function.api.chat.biz.IMCallType.CALL_TYPE_VOICE
@Keep class CallRequestParam {
/**
* 发送者sn
*/
var snSender: String = ""
/**
* 接收者sn
*/
var snReceiver: String = ""
/**
* 发送者用户名
*/
var nickName:String? = null
/**
* 发送者头像链接
*/
var headImgUrl:String? = null
/**
* 发送者车辆信息
*/
var carInfo:String? = null
/**
* 发送者经度
*/
var lon:Double = 0.0
/**
* 发送者纬度
*/
var lat: Double = 0.0
/**
* 呼叫类型
*/
var type = CALL_TYPE_VOICE.type
}

View File

@@ -0,0 +1,24 @@
package com.mogo.eagle.core.function.chat.facade.net.bean
class ConnectStatusParam (var snSender: String, var snReceiver: String, var roomId: Int, var status: Int, var type: Int) {
var lat: Double? = null//发送方的纬度
var lon: Double? = null//发送方的经度
var nickName: String? = null//发送方的昵称
var headImgUrl: String? = null//发送方的用户头像
var carInfo: String? = null//发送方的车辆信息
var cardIdAge: String? = null //发送方年龄
var cardIdSex: String? = null//发送发性别
var cityName: String? = null//发送方城市
override fun toString(): String {
return "ConnectStatusParam(snSender='$snSender', snReceiver='$snReceiver', roomId=$roomId, status=$status, type=$type, lat=$lat, lon=$lon, nickName=$nickName, headImgUrl=$headImgUrl, carInfo=$carInfo, cardIdAge=$cardIdAge, cardIdSex=$cardIdSex, cityName=$cityName)"
}
}

View File

@@ -0,0 +1,3 @@
package com.mogo.eagle.core.function.chat.facade.net.bean
data class Error(val code: Int, val msg: String)

View File

@@ -0,0 +1,11 @@
package com.mogo.eagle.core.function.chat.facade.net.bean
/**
* carOnLineACC ON状态 1:在线 2:不在线
* onLine:是否隐身 1:在线 2:隐身
*/
internal class OnLineStatus {
var carOnLine: Int = -1
var onLine: Int = -1
}

View File

@@ -0,0 +1,8 @@
package com.mogo.eagle.core.function.chat.facade.net.bean
import androidx.annotation.Keep
@Keep class RoomInfo {
var roomId:Int = -1
}

View File

@@ -0,0 +1,8 @@
package com.mogo.eagle.core.function.chat.facade.net.bean
import androidx.annotation.Keep
@Keep
internal class SnArrayRequestBody {
var sns: List<String>? = null
}

View File

@@ -0,0 +1,97 @@
package com.mogo.eagle.core.function.chat.facade.socket
import com.google.gson.Gson
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.data.chat.socket.HeartBeat
import com.mogo.eagle.core.function.api.chat.biz.ChatConsts
import com.mogo.eagle.core.function.chat.facade.consts.ChatHttp
import com.mogo.eagle.core.function.chat.facade.consts.SOCKET_HAND_SHAKE
import com.mogo.eagle.core.function.chat.facade.consts.SOCKET_HEART_BEAT
import com.mogo.eagle.core.function.chat.facade.utils.log
import com.mogo.websocket.ISocketMsgCallBack
import com.mogo.websocket.ISocketMsgSetting
import com.mogo.websocket.SocketClient
import java.util.concurrent.atomic.AtomicInteger
/**
* 长链接管理类
*/
internal object SocketConnectManager {
private val client by lazy {
SocketClient()
}
private var msgCallback: ((msg: String) -> Unit)? = null
private val roomId by lazy {
AtomicInteger(0)
}
fun init() {
//开启长连服务
client.initSocketServer(ChatHttp.getSocketServer())
client.getMessageSettings(socketMsgSetting)
client.addISocketMsgCallBack(socketMsgCallBack)
client.startConnect()
}
fun setOnMsgReceiveCb(cb: ((msg: String) -> Unit)) {
msgCallback = cb
}
fun setRoomId(roomId: Int) {
this.roomId.set(roomId)
}
private val socketMsgSetting: ISocketMsgSetting = object : ISocketMsgSetting {
override fun getHandShakeMsg(): String {
log(ChatConsts.TAG, "getHandShakeMsg")
val socketMsg = HeartBeat(SOCKET_HAND_SHAKE, MoGoAiCloudClientConfig.getInstance().sn)
return Gson().toJson(socketMsg)
}
override fun getHeartBeatMsg(): String {
log(ChatConsts.TAG, "getHeartBeatMsg")
val socketMsg = HeartBeat(SOCKET_HEART_BEAT, MoGoAiCloudClientConfig.getInstance().sn, roomId.get())
return Gson().toJson(socketMsg)
}
}
private val socketMsgCallBack: ISocketMsgCallBack = object : ISocketMsgCallBack {
override fun onConnectOpen() {
log(ChatConsts.TAG, "onConnectOpen ---> ")
}
override fun handleError(e: Exception) {
log(ChatConsts.TAG, "handleError ---> msg: ${e.message}")
client.stopHeartBeat()
}
override fun onConnectClose() {
log(ChatConsts.TAG, "onConnectClose ---> stop web socket thread ,and ready to reconnect")
client.stop()
log(ChatConsts.TAG, "onConnectClose ---> stop Heart Beat")
client.stopHeartBeat()
log(ChatConsts.TAG, "ready to reconnect")
client.reConnect()
}
override fun handleMessage(message: String) {
log(ChatConsts.TAG, "handleMessage ---> $message")
msgCallback?.invoke(message)
}
}
fun instance() = client
fun destroy() {
try {
client.stopHeartBeat()
client.disConnect()
client.stop()
} catch (t: Throwable) {
log(ChatConsts.TAG, "IMService -- destroy -- ex: $t")
}
}
}

View File

@@ -0,0 +1,606 @@
package com.mogo.eagle.core.function.chat.facade.ui
import android.content.Context
import android.media.AudioManager
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.ViewCompat
import androidx.lifecycle.Lifecycle.Event.ON_CREATE
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import com.bumptech.glide.request.RequestOptions
import com.mogo.eagle.core.data.chat.UserInfo
import com.mogo.eagle.core.function.api.chat.biz.AnswerState
import com.mogo.eagle.core.function.api.chat.biz.AnswerState.EnterRoomSuccess
import com.mogo.eagle.core.function.api.chat.biz.AnswerState.Error
import com.mogo.eagle.core.function.api.chat.biz.AnswerState.ExitRoomSuccess
import com.mogo.eagle.core.function.api.chat.biz.AnswerState.RoomMemberUpdate
import com.mogo.eagle.core.function.api.chat.biz.ChatConsts
import com.mogo.eagle.core.function.api.chat.biz.HangUpState
import com.mogo.eagle.core.function.api.chat.biz.IMoGoVoiceControlFacade.IMoGoVoiceCallback
import com.mogo.eagle.core.function.api.chat.biz.RefuseState
import com.mogo.eagle.core.function.chat.R
import com.mogo.eagle.core.function.chat.facade.MoGoChatFacade
import com.mogo.eagle.core.function.chat.facade.OnCallingInterrupt
import com.mogo.eagle.core.function.chat.facade.OnInComingCallback
import com.mogo.eagle.core.function.chat.facade.bridge.BridgeApi
import com.mogo.eagle.core.function.chat.facade.utils.log
import com.mogo.eagle.core.function.chat.facade.voice.VoiceControlFacade.REQUEST_CLOUD_VOICE_CALL
import com.mogo.eagle.core.utilcode.kotlin.lifeCycleScope
import com.mogo.eagle.core.utilcode.kotlin.observe
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.kotlin.safeCancel
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform.CornerType.LEFT
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.RENDEZVOUS
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.selects.select
import java.util.concurrent.atomic.AtomicReference
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp
import com.mogo.eagle.core.utilcode.util.ToastUtils
import java.text.SimpleDateFormat
import java.util.*
internal class CallChatWindowManager {
companion object {
const val TAG = "TeamChatWindowManager"
const val DEFAULT_MAX_DIALING_TIME = 30 * 1000L
}
private val facade by lazy {
MoGoChatFacade
}
private val context by lazy {
BridgeApi.context()
}
private val scope by lazy {
BridgeApi.context().lifeCycleScope
}
private var hasAnswered = false
private var hasRefused = false
private var hasHangUpped = false
@OptIn(ExperimentalCoroutinesApi::class)
private var interrupt = Channel<Boolean>(RENDEZVOUS)
@Synchronized
get() = if (field.isClosedForReceive || field.isClosedForSend) {
field = Channel(RENDEZVOUS)
field
} else field
@OptIn(ExperimentalCoroutinesApi::class)
private var exitRoom = Channel<Boolean>(RENDEZVOUS)
@Synchronized
get() = if (field.isClosedForReceive || field.isClosedForSend) {
field = Channel(RENDEZVOUS)
field
} else field
@OptIn(ExperimentalCoroutinesApi::class)
private var callerEnter = Channel<Boolean>(RENDEZVOUS)
@Synchronized
get() = if (field.isClosedForReceive || field.isClosedForSend) {
field = Channel(RENDEZVOUS)
field
} else field
@OptIn(ExperimentalCoroutinesApi::class)
private var refused = Channel<Boolean>(RENDEZVOUS)
@Synchronized
get() = if (field.isClosedForReceive || field.isClosedForSend) {
field = Channel(RENDEZVOUS)
field
} else field
private val onInComingCall = object : OnInComingCallback {
override fun invoke(isTeam: Boolean, user: UserInfo) {
if (isTeam) {
log(TAG, "车队邀请,不做处理....")
} else {
showInComingView(user)
}
}
}
private val onCallInterrupt = object : OnCallingInterrupt {
override fun invoke(isTeam: Boolean, user: UserInfo) {
if (isTeam) {
log(TAG, "车队邀请拒绝,不做处理....")
} else {
onCallInterrupt()
}
}
}
fun init() {
facade.let {
it.onInComingCall(onInComingCall)
it.onCallingInterrupt(onCallInterrupt)
}
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun showInComingView(user: UserInfo) {
val context = BridgeApi.context()
scope.launchWhenResumed {
var incomingView = LayoutInflater.from(context)
.inflate(R.layout.module_car_chatting_launcher_incoming_hawk_eye_view, null)
incomingView.isClickable = true
var answer = incomingView.findViewById<View>(R.id.module_carchatting_incoming_answer)
var refuse = incomingView.findViewById<View>(R.id.module_carchatting_incoming_hangUp)
val dismissJob = AtomicReference<Job>()
var timer: Job? = null
var ring: Job? = null
launch {
speak(REQUEST_CLOUD_VOICE_CALL)
calling()
}.also { ring = it }
answer.onClick {
timer = resetInComingTimer(timer, user, incomingView)
if (hasAnswered) {
ToastUtils.showShort("正在处理, 请稍候...")
return@onClick
}
doAnswer(user, incomingView)
}
refuse.onClick {
timer = resetInComingTimer(timer, user, incomingView)
if (hasRefused) {
ToastUtils.showShort("正在处理, 请稍候...")
return@onClick
}
doRefuse(user)
}
incomingView.observe(arrayOf(ON_CREATE, ON_DESTROY)) { itx ->
if (itx == ON_CREATE) {
timer = inComingTimer(user, incomingView)
launch(Dispatchers.Main) {
val d1 = async {
log(TAG, "监听对方呼出,又挂断状态...")
var interrupted = interrupt.receive()
while (!interrupted) {
interrupted = interrupt.receive()
}
log(TAG, "收到对方呼出又挂断状态...")
}
val d2 = async {
log(TAG, "监听拒绝状态...")
var isRefuse = refused.receive()
while (!isRefuse) {
isRefuse = refused.receive()
}
log(TAG, "收到拒绝状态, 隐藏来电界面")
}
val state = select<Int> {
d1.onAwait { 1 }
d2.onAwait { 2 }
}
when (state) {
1 -> {
log(TAG, "由于对方呼出又挂断,导致来电界面隐藏")
d2.safeCancel()
}
2 -> {
log(TAG, "由于自己主动拒接,导致来电界面隐藏")
d1.safeCancel()
}
}
hide(incomingView)
releaseAudioAndVoice()
}.also { dismissJob.set(it) }
}
if (itx == ON_DESTROY) {
ring?.safeCancel()
timer?.safeCancel()
hasAnswered = false
hasRefused = false
refused.safeCancel()
interrupt.safeCancel()
dismissJob.get()?.safeCancel()
incomingView = null
answer = null
refuse = null
}
}
show(incomingView, getInComingLayoutParams(context))
}
}
private suspend fun speak(text: String) = suspendCancellableCoroutine<Unit> {
val voiceCallback = object : IMoGoVoiceCallback {
override fun onSpeakEnd() {
it.resumeWith(Result.success(Unit))
}
}
val voiceCtl = facade.voice()
it.invokeOnCancellation {
voiceCtl.stopTTS(context, text)
}
voiceCtl.speak(context, text, voiceCallback)
}
private suspend fun calling() = suspendCancellableCoroutine<Unit> {
it.invokeOnCancellation {
releaseAudioAndVoice()
}
playAudioCall {
it.resumeWith(Result.success(Unit))
}
}
private fun getInComingLayoutParams(context: Context): FrameLayout.LayoutParams {
return FrameLayout.LayoutParams(
context.resources.getDimension(R.dimen.module_call_chat_state_incoming_hawk_eye_width).toInt(),
context.resources.getDimension(R.dimen.module_call_chat_state_incoming_hawk_eye_height).toInt())
.also {
val x = context.resources.getDimension(R.dimen.module_call_chat_state_location_hawk_eye_x)
val y = context.resources.getDimension(R.dimen.module_call_chat_state_location_hawk_eye_y)
it.leftMargin = x.toInt()
it.topMargin = y.toInt()
}
}
/**
* 重新开始计时
*/
private fun CoroutineScope.resetInComingTimer(old: Job?, user: UserInfo, incomingView: View): Job {
old?.safeCancel()
return inComingTimer(user, incomingView)
}
private fun CoroutineScope.inComingTimer(user: UserInfo, incomingView: View) = launch {
log(TAG, "延迟30s消失计时开始...")
delay(DEFAULT_MAX_DIALING_TIME)
log(TAG, "延迟30s消失计时结束...")
doRefuse(user, false)
hide(incomingView)
releaseAudioAndVoice()
}
private fun showCallingView(user: UserInfo) {
val context = BridgeApi.context()
scope.launchWhenResumed {
var callingView = LayoutInflater.from(context)
.inflate(R.layout.module_car_chatting_launcher_calling_hawk_eye_view, null)
callingView.isClickable = true
var calling= callingView.findViewById<View>(R.id.module_carchatting_rl_call_view)
var head = callingView.findViewById<ImageView>(R.id.module_carchatting_call_head)
var hangUp = callingView.findViewById<View>(R.id.module_carchatting_call_hangUp)
var name = callingView.findViewById<TextView>(R.id.module_carchatting_call_nickname)
var timer = callingView.findViewById<TextView>(R.id.module_carchatting_call_time)
calling.visibility = View.VISIBLE
name.text = "云平台"
timer.text = context.resources.getString(R.string.module_car_chat_matching_wait)
GlideApp.with(context).load(user.icon)
.apply(
RequestOptions.bitmapTransform(
GlideRoundedCornersTransform(
20f,
LEFT
)
)
)
.placeholder(R.mipmap.module_carchatting_hawk_eye_default_head_img)
.into(head)
hangUp.onClick {
if (hasHangUpped) {
ToastUtils.showShort("正在处理,请稍候...")
return@onClick
}
doHangUp(user, callingView)
}
callingView.observe(arrayOf(ON_CREATE, ON_DESTROY)) {
var job: Job? = null
if (it == ON_CREATE) {
launch(Dispatchers.Main) {
val d1 = async {
log(ChatConsts.TAG, "等着新用户进来...")
var isNewUserEnter = callerEnter.receive()
while (!isNewUserEnter) {
isNewUserEnter = callerEnter.receive()
}
//新用户进来了
log(ChatConsts.TAG, "新用户进来了...")
callingTimer()
.collect {
timer.text = parseTime(it)
}
}
val d2 = async {
log(ChatConsts.TAG, "等着退房通知...")
var exit = exitRoom.receive()
while (!exit) {
exit = exitRoom.receive()
}
}
val state = select<Int> {
d1.onAwait { 1 }
d2.onAwait { 2 }
}
when (state) {
1 -> {
log(TAG, "通话中, 计时任务结束了(不般不会发生)...")
}
2 -> {
log(TAG, "收到退房通知,可以移除通话状态的界面了...")
d1.safeCancel()
}
}
if (hasHangUpped) {
ToastUtils.showShort("已挂断")
}
hide(callingView)
facade.audioFocus().releaseAudioFocus()
}.also { job = it }
}
if (it == ON_DESTROY) {
job?.safeCancel()
callerEnter.safeCancel()
exitRoom.safeCancel()
hasHangUpped = false
callingView = null
calling = null
head = null
hangUp = null
name = null
timer = null
}
}
show(callingView, getCallingLayoutParams(context))
}
}
private fun getCallingLayoutParams(context: Context): FrameLayout.LayoutParams {
return FrameLayout.LayoutParams(
context.resources.getDimension(R.dimen.module_call_chat_state_hawk_eye_width).toInt(),
context.resources.getDimension(R.dimen.module_call_chat_state_hawk_eye_height).toInt()
)
.also {
it.leftMargin = context.resources.getDimension(R.dimen.module_call_chat_state_location_hawk_eye_x).toInt()
it.topMargin = context.resources.getDimension(R.dimen.module_call_chat_state_location_hawk_eye_y).toInt()
}
}
private fun parseTime(time: Long): String {
val date = Date(time)
val formatter = SimpleDateFormat("HH:mm:ss", Locale.CHINA)
formatter.timeZone = TimeZone.getTimeZone("GMT+00:00")
return formatter.format(date)
}
private fun show(view: View, params: FrameLayout.LayoutParams) {
BridgeApi.floatViewManager()?.addView(view, params, true)
}
private fun hide(view: View) {
if (!ViewCompat.isAttachedToWindow(view)) {
return
}
BridgeApi.floatViewManager()?.removeView(view)
}
private fun playAudioCall(onPlay:(() -> Unit)? = null) {
facade.also {
it.audioFocus().requireAudioFocus {
it.media().play(context, R.raw.call, true, AudioManager.STREAM_RING)
onPlay?.invoke()
}
}
}
private fun releaseAudioAndVoice() {
facade.also {
it.media().release()
}
}
private fun onNewUserEnterRoom() {
scope.launch {
callerEnter.send(true)
}
}
private fun onCallInterrupt() {
scope.launch {
interrupt.send(true)
}
}
private fun doAnswer(user: UserInfo, incommingView: View) {
answer(
user,
onEnter = {
releaseAudioAndVoice()
hide(incommingView)
showCallingView(user)
},
onNewEnter = {
onNewUserEnterRoom()
},
onExit = {
exitRoom()
},
onError = { code, msg, extra ->
log(TAG, "-- 应答失败 --: code:: $code; msg:: $msg; extra:: $extra")
when (code) {
AnswerState.CODE_ANSWER_SOCKET_DISCONNECT -> {
ToastUtils.showShort("连接已断开")
}
else -> {
ToastUtils.showShort("应答失败")
}
}
doRefuse(user, false)
releaseAudioAndVoice()
hide(incommingView)
facade.audioFocus().releaseAudioFocus()
}
)
}
/**
* @param user: 来电用户信息
* @param onEnter: 当前用户进房成功通知
* @param onExit: 当前用户退房通知(当前用户主动挂断或对方挂断)
*/
@OptIn(ExperimentalCoroutinesApi::class)
private fun answer(user: UserInfo, onEnter: () -> Unit, onNewEnter: () -> Unit, onExit: () -> Unit, onError: (code: Int, msg: String, extra: Map<String, String>? ) -> Unit) {
facade.also { itx ->
if (hasAnswered) {
return
}
hasAnswered = true
itx.answer(user.sn)
.onEach {
when(it) {
is EnterRoomSuccess -> {
onEnter.invoke()
}
is RoomMemberUpdate -> {
if (!it.isMySelf && it.isEnter) {
onNewEnter.invoke()
}
}
is ExitRoomSuccess -> {
onExit.invoke()
}
is Error -> {
hasAnswered = false
onError.invoke(it.code, it.msg, it.extra)
}
else -> {
log(ChatConsts.TAG, "[Answer][Other]-> $it")
}
}
}
.launchIn(scope)
}
}
private fun doRefuse(user: UserInfo, notify: Boolean = true) {
refuse(user,
onSuccess = {
if (notify) {
ToastUtils.showShort("已拒接")
onRefuseOK()
}
},
onError = { code, msg, extra ->
log(TAG, "-- 拒绝失败 --: code:: $code; msg:: $msg; extra:: $extra")
ToastUtils.showShort("拒绝异常")
})
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun refuse(user: UserInfo, onSuccess: () -> Unit, onError: (code: Int, msg: String, extra: Map<String, String>?) -> Unit){
facade.also {
if (hasRefused) {
return
}
hasRefused = true
it.refuse(user.sn)
.onEach { itx ->
when(itx) {
is RefuseState.Success -> {
onSuccess.invoke()
}
is RefuseState.Error -> {
hasRefused = false
onError.invoke(itx.code, itx.msg, itx.extra)
}
else -> {
log(ChatConsts.TAG, "[Refuse][Other]-> $it")
}
}
}
.launchIn(scope)
}
}
private fun doHangUp(user: UserInfo, callingView: View) {
hangUp(
user,
onSuccess = {
log(TAG, "挂断接口请求成功,等着退房...")
},
onError = { code, msg, extra ->
log(TAG, "挂断异常code: $code; msg: $msg, ; extra: $extra")
ToastUtils.showShort("挂断异常")
})
}
private fun exitRoom() {
scope.launch {
exitRoom.send(true)
}
}
private fun onRefuseOK() {
scope.launch {
refused.send(true)
}
}
@OptIn(InternalCoroutinesApi::class)
private fun callingTimer(): Flow<Long> {
return flow {
var counter = 1
while (true) {
delay(1000)
emit(1000L * (counter ++))
}
}
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun hangUp(user: UserInfo, onSuccess: () -> Unit, onError:(code: Int, msg: String, extra: Map<String, String>?) -> Unit) {
facade.also {
if (hasHangUpped) {
return
}
hasHangUpped = true
it.handUp(user.sn)
.onEach { itx ->
when(itx) {
HangUpState.Success -> {
onSuccess.invoke()
}
is HangUpState.Error -> {
hasHangUpped = false
log(TAG, "-- 挂断失败 --: code:: ${itx.code}; msg:: ${itx.msg}; extra:: ${itx.extra}")
onError.invoke(itx.code, itx.msg, itx.extra)
}
else -> {
log(ChatConsts.TAG, "[HangUp][Other]-> $it")
}
}
}
.launchIn(scope)
}
}
fun destroy() {
hasAnswered = false
hasHangUpped = false
hasRefused = false
interrupt.safeCancel()
refused.safeCancel()
exitRoom.safeCancel()
callerEnter.safeCancel()
}
}

View File

@@ -0,0 +1,9 @@
@file:JvmName("LogUtil")
package com.mogo.eagle.core.function.chat.facade.utils
import com.mogo.eagle.core.function.chat.facade.aop.DebugLog
@DebugLog
fun log(tag: String, msg: String) {}

View File

@@ -0,0 +1,232 @@
package com.mogo.eagle.core.function.chat.facade.voice
import android.content.Context
import android.content.Intent
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.voice.AIAssist
import com.mogo.commons.voice.IMogoVoiceCmdCallBack
import com.mogo.eagle.core.function.api.chat.biz.IMoGoVoiceControlFacade
import com.mogo.eagle.core.function.api.chat.biz.IMoGoVoiceControlFacade.IMoGoVoiceCallback
import com.mogo.eagle.core.function.chat.facade.bridge.BridgeApi
import com.mogo.eagle.core.function.chat.facade.utils.log
import com.mogo.service.intent.IMogoIntentListener
import java.lang.ref.WeakReference
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean
object VoiceControlFacade: IMoGoVoiceControlFacade, IMogoVoiceCmdCallBack, IMogoIntentListener {
private const val TAG = "VoiceControl"
//WakeUp Command (Intent)
private const val VOICE_INTENT_CANCEL_CALL_COMMAND = "com.zhidao.commin.cancel" //取消呼叫,取消通话
private const val VOICE_INTENT_REFUSE_CALL = "com.ileja.phone.incoming.reject" //挂断
//unWakeUp Command
private const val VOICE_REGISTER_INVITE_JOIN_TEAM = "CMD_CAR_CHAT_INVITE_JOIN_TEAM"
private val customInviteJoinTeamArray: Array<String> = arrayOf("邀请加入车队")
private const val VOICE_REGISTER_JOIN_TEAM = "CMD_CAR_CHAT_JOIN_TEAM"
private val customJoinTeamArray: Array<String> = arrayOf("加入", "加入车队")
private const val VOICE_REGISTER_REFUSE_JOIN_TEAM = "CMD_CAR_CHAT_REFUSE_JOIN_TEAM"
private val customRefuseJoinTeamArray: Array<String> = arrayOf("拒绝", "拒绝加入车队")
private const val VOICE_REGISTER_CANCEL_CALL = "CMD_CAR_CHAT_CANCEL_CALL"
private val customCancelCallArray: Array<String> = arrayOf("取消通话", "取消呼叫", "挂断")
private const val VOICE_INTENT_ANSWER_CALL = "com.ileja.phone.incoming.accept" //接听
internal const val REQUEST_CLOUD_VOICE_CALL = "云平台请求跟你进行语音通话"
private val requestCallYArray: Array<String> = arrayOf("接听")
private val requestCallNArray: Array<String> = arrayOf("挂断")
private val hasRegister by lazy { AtomicBoolean(false) }
private val listeners by lazy {
CopyOnWriteArrayList<WeakReference<IMoGoVoiceCallback>>()
}
private var onCmdAgree: WeakReference<onCmdAgree>? = null
private var onSpeechFinish: WeakReference<onSpeedFinish>? = null
private val intentManager by lazy {
BridgeApi.intentManager()
}
override fun speak(context: Context, content: String, listener: IMoGoVoiceCallback) {
listeners += WeakReference(listener)
AIAssist.getInstance(context).speakTTSVoice(content, this)
}
override fun onCmdSelected(cmd: String?) {
super.onCmdSelected(cmd)
cmd?.let { itx ->
listeners
.filter {
it.get() != null
}
.forEach {
when (itx) {
VOICE_REGISTER_CANCEL_CALL -> {
log(TAG, "语音免唤醒 取消打电话")
it.get()?.onVoiceCancelCall()
}
VOICE_REGISTER_INVITE_JOIN_TEAM -> {
log(TAG, "语音免唤醒 邀請加入车队")
it.get()?.onVoiceInviteJoinTeam()
}
VOICE_REGISTER_JOIN_TEAM -> {
log(TAG, "语音免唤醒 加入车队")
it.get()?.onVoiceJoinTeam()
}
VOICE_REGISTER_REFUSE_JOIN_TEAM -> {
log(TAG, "语音免唤醒 拒绝加入车队")
it.get()?.onVoiceRefuseJoinTeam()
}
VOICE_INTENT_ANSWER_CALL -> {
log(TAG, "语音免唤醒 接收电话邀请")
it.get()?.onVoiceAnswerCall()
}
VOICE_INTENT_REFUSE_CALL -> {
log(TAG, "语音免唤醒 来电拒接")
it.get()?.onVoiceRefuseCall()
}
}
}
}
}
override fun onSpeakEnd(speakText: String?) {
super.onSpeakEnd(speakText)
listeners
.filter {
it.get() != null
}
.forEach {
it.get()?.onSpeakEnd()
}
}
override fun onSpeakError(speakText: String?, errorMsg: String?) {
super.onSpeakError(speakText, errorMsg)
listeners
.filter {
it.get() != null
}
.forEach {
it.get()?.onSpeakEnd()
}
}
override fun register() {
if (hasRegister.get()) {
return
}
hasRegister.set(true)
AIAssist.getInstance(BridgeApi.context()).registerUnWakeupCommand(VOICE_REGISTER_CANCEL_CALL, customCancelCallArray, this)
intentManager?.also {
it.registerIntentListener(VOICE_INTENT_CANCEL_CALL_COMMAND, this)
it.registerIntentListener(VOICE_INTENT_REFUSE_CALL, this)
}
}
override fun registerInviteJoinTeam(context: Context, listener: IMoGoVoiceCallback) {
log(TAG, "registerInviteJoinTeam")
listeners += WeakReference(listener)
AIAssist.getInstance(context)
.registerUnWakeupCommand(VOICE_REGISTER_INVITE_JOIN_TEAM, customInviteJoinTeamArray, this)
}
override fun registerJoinTeam(context: Context, listener: IMoGoVoiceCallback) {
log(TAG, "registerJoinTeam")
listeners += WeakReference(listener)
AIAssist.getInstance(context)
.registerUnWakeupCommand(VOICE_REGISTER_JOIN_TEAM, customJoinTeamArray, this)
AIAssist.getInstance(context)
.registerUnWakeupCommand(VOICE_REGISTER_REFUSE_JOIN_TEAM, customRefuseJoinTeamArray, this)
}
override fun unRegisterInviteJoinTeam(context: Context) {
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_INVITE_JOIN_TEAM)
}
override fun unRegisterJoinTeam(context: Context) {
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_JOIN_TEAM)
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_REFUSE_JOIN_TEAM)
}
override fun registerIntentInComingCall(listener: IMoGoVoiceCallback) {
listeners += WeakReference(listener)
intentManager?.registerIntentListener(VOICE_INTENT_ANSWER_CALL, this)
intentManager?.registerIntentListener(VOICE_INTENT_REFUSE_CALL, this)
}
override fun unRegisterIntentInComingCall(context: Context) {
intentManager?.unregisterIntentListener(VOICE_INTENT_ANSWER_CALL, this)
intentManager?.unregisterIntentListener(VOICE_INTENT_REFUSE_CALL, this)
}
override fun speakAndRegisterCall(onCmdAgree: (Boolean) -> Unit, onSpeakFinish: () -> Unit, listener: IMoGoVoiceCallback) {
this.onCmdAgree = WeakReference(onCmdAgree)
this.onSpeechFinish = WeakReference(onSpeakFinish)
listeners += WeakReference(listener)
AIAssist.getInstance(AbsMogoApplication.getApp().applicationContext).speakQAndACmd(REQUEST_CLOUD_VOICE_CALL,
requestCallYArray, requestCallNArray, object : IMogoVoiceCmdCallBack {
override fun onCmdAction(speakText: String?) {
super.onCmdAction(speakText)
log(TAG, "onCmdAction ---> ")
this@VoiceControlFacade.onCmdAgree?.get()?.invoke(true)
}
override fun onCmdCancel(speakText: String?) {
super.onCmdCancel(speakText)
log(TAG, "onCmdCancel ---> ")
this@VoiceControlFacade.onCmdAgree?.get()?.invoke(false)
}
override fun onSpeakEnd(speakText: String?) {
super.onSpeakEnd(speakText)
log(TAG, "onSpeakEnd ---> ")
this@VoiceControlFacade.onSpeechFinish?.get()?.invoke()
}
})
}
override fun stopTTS(context: Context, text: String) {
AIAssist.getInstance(context).stopSpeakTts(text)
}
override fun unRegister() {
if (!hasRegister.get()) {
return
}
hasRegister.set(false)
listeners.clear()
AIAssist.getInstance(BridgeApi.context()).unregisterUnWakeupCommand(VOICE_REGISTER_CANCEL_CALL, this)
intentManager?.also {
it.unregisterIntentListener(VOICE_INTENT_CANCEL_CALL_COMMAND, this)
it.unregisterIntentListener(VOICE_INTENT_REFUSE_CALL, this)
}
}
override fun onIntentReceived(cmd: String?, intent: Intent?) {
val command = cmd ?: return
log(TAG, "handleOnIntentCmd: cmd -> $command")
when (command) {
VOICE_INTENT_CANCEL_CALL_COMMAND, VOICE_INTENT_REFUSE_CALL -> {
log(TAG, "语音唤醒 取消打电话")
listeners
.filter {
it.get() != null
}
.forEach {
it.get()?.onVoiceCancelCall()
}
}
}
}
}
typealias onCmdAgree = ((Boolean) -> Unit)
typealias onSpeedFinish = (() -> Unit)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/module_callchatting_shape_gradient_blue_normal" android:state_pressed="false" />
<item android:drawable="@drawable/module_callchatting_shape_gradient_blue_pressed" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_16" />
<gradient
android:angle="0"
android:endColor="#1F7EFF"
android:startColor="#1E57A4" />
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_16" />
<gradient
android:angle="0"
android:endColor="#1363A4"
android:startColor="#164079" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/module_callchatting_user_pop_call_bg_normal" android:state_pressed="false" />
<item android:drawable="@drawable/module_callchatting_user_pop_call_bg_pressed" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_16" />
<gradient
android:angle="0"
android:endColor="#585E8B"
android:startColor="#4A4C70" />
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_16" />
<gradient
android:angle="0"
android:endColor="#383C5E"
android:startColor="#373856" />
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_30" />
<solid android:color="#F710121E" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/module_carchatting_team_fragment_close_normal" android:state_pressed="false" />
<item android:drawable="@drawable/module_carchatting_team_fragment_close_pressed" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_30" />
<gradient
android:angle="180"
android:endColor="#3F4057"
android:startColor="#5E6079" />
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:topLeftRadius="@dimen/module_call_chat_team_invitation_hawk_eye_bg_corner"
android:bottomLeftRadius="@dimen/module_call_chat_team_invitation_hawk_eye_bg_corner"/>
<gradient
android:angle="135"
android:endColor="#22E2FE"
android:startColor="#005CFF" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/module_carchatting_team_invitation_hawk_eye_join_normal" android:state_pressed="false" />
<item android:drawable="@drawable/module_carchatting_team_invitation_hawk_eye_join_pressed" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/module_carchatting_team_invitation_hawk_eye_refuse_normal" android:state_pressed="false" />
<item android:drawable="@drawable/module_carchatting_team_invitation_hawk_eye_refuse_pressed" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/module_carchatting_team_invitation_join_normal" android:state_pressed="false" />
<item android:drawable="@drawable/module_carchatting_team_invitation_join_pressed" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/module_carchatting_team_invitation_refuse_normal" android:state_pressed="false" />
<item android:drawable="@drawable/module_carchatting_team_invitation_refuse_pressed" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_11" />
<gradient
android:angle="180"
android:endColor="#1DAAA5"
android:startColor="#37DED9" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/module_carchatting_team_quit_normal" android:state_pressed="false" />
<item android:drawable="@drawable/module_carchatting_team_quit_pressed" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_23" />
<solid android:color="#FF1F2131" />
</shape>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_11" />
<gradient
android:angle="180"
android:endColor="#4E5069"
android:startColor="#4E5069" />
</shape>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dp_11" />
<gradient
android:angle="180"
android:endColor="#256BFF"
android:startColor="#5CC1FF" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#3B4577" />
<corners android:radius="@dimen/module_call_chat_calling_bg_radius" />
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<gradient
android:endColor="#595FA2"
android:startColor="#43508E"></gradient>
<size
android:width="@dimen/dp_80"
android:height="@dimen/dp_80" />
</shape>

View File

@@ -0,0 +1,42 @@
<?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:layout_margin="@dimen/module_call_chat_team_fragment_margin"
android:elevation="@dimen/dp_10"
android:background="@drawable/module_carchatting_team_fragment_bg"
android:clickable="true"
android:padding="@dimen/module_call_chat_team_fragment_padding">
<TextView
android:id="@+id/tv_num"
android:layout_width="wrap_content"
android:layout_height="@dimen/module_call_chat_team_fragment_close_view_height"
android:gravity="center_vertical"
android:text="车队人数"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_team_fragment_num_text_size"
app:layout_constraintBaseline_toBaselineOf="@+id/iv_close"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="@dimen/module_call_chat_team_fragment_close_view_width"
android:layout_height="@dimen/module_call_chat_team_fragment_close_view_height"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/module_carchatting_team_fragment_close"
tools:ignore="ContentDescription" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_teammates"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="@dimen/module_call_chat_team_fragment_teammates__margin_top"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_close" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,262 @@
<?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="@dimen/module_call_chat_state_hawk_eye_width"
android:layout_height="@dimen/module_call_chat_state_hawk_eye_height"
android:background="@drawable/module_carchatting_vr_calling_bg"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_rl_match_init_view"
android:layout_width="@dimen/module_call_chat_view_match_width"
android:layout_height="@dimen/module_call_chat_state_hawk_eye_height"
android:background="@drawable/module_callchatting_shape_gradient_blue"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="@dimen/module_call_chat_match_iv_width"
android:layout_height="@dimen/module_call_chat_match_iv_height"
android:layout_marginStart="@dimen/dp_16"
android:src="@mipmap/module_callchatting_match"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/module_call_chat_view_margin_end"
android:text="@string/module_car_chat_match_tip"
android:textColor="@android:color/white"
android:textSize="@dimen/module_call_chat_match_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_rl_matching_view"
android:layout_width="@dimen/module_call_chat_state_hawk_eye_width"
android:layout_height="@dimen/module_call_chat_state_hawk_eye_height"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_30"
android:text="@string/module_car_chat_matching"
android:textColor="@android:color/white"
android:textSize="@dimen/module_call_chat_matching_textsize"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/module_carchatting_tv_cancel_match"
android:layout_width="@dimen/module_call_chat_state_hawk_eye_height"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/module_car_chat_matching_cancel"
android:textColor="@color/module_carchatting_red_color"
android:textSize="@dimen/module_call_chat_matching_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="1px"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/module_call_chat_match_view_line_margin_top_bottom"
android:layout_marginBottom="@dimen/module_call_chat_match_view_line_margin_top_bottom"
android:background="@color/module_carchatting_match_line"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/module_carchatting_tv_cancel_match"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_rl_call_view"
android:layout_width="@dimen/module_call_chat_state_hawk_eye_width"
android:layout_height="@dimen/module_call_chat_state_hawk_eye_height"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/module_carchatting_call_head"
android:layout_width="@dimen/module_call_chat_calling_iv_hawk_eye_width_height"
android:layout_height="@dimen/module_call_chat_calling_iv_hawk_eye_width_height"
android:clickable="true"
android:scaleType="centerInside"
android:src="@mipmap/module_carchatting_hawk_eye_default_head_img"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/module_carchatting_call_hangUp"
android:layout_width="@dimen/module_call_chat_hawk_eye_circle_btn_size"
android:layout_height="@dimen/module_call_chat_hawk_eye_circle_btn_size"
android:layout_marginEnd="@dimen/module_call_chat_calling_iv_hawk_eye_margin_left_right"
android:scaleType="centerInside"
android:src="@mipmap/module_carchatting_launcher_calling_hangup"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/module_carchatting_call_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/module_call_chat_calling_text_hawk_eye_margin_top_bottom"
android:text="@string/module_car_chat_matching_wait"
android:textColor="@color/module_carchatting_hawk_eye_status_color"
android:textSize="@dimen/module_call_chat_calling_text_hawk_eye_time_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/module_carchatting_call_nickname" />
<TextView
android:id="@+id/module_carchatting_call_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_22"
android:layout_marginTop="@dimen/module_call_chat_calling_text_hawk_eye_margin_top_bottom"
android:textColor="@android:color/white"
android:textSize="@dimen/module_call_chat_calling_text_hawk_eye_name_size"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_call_head"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_team_rl_view"
android:layout_width="@dimen/module_call_chat_state_hawk_eye_width"
android:layout_height="@dimen/module_call_chat_state_hawk_eye_height"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/module_carchatting_iv_team_quit"
android:layout_width="@dimen/module_call_chat_hawk_eye_circle_btn_size"
android:layout_height="@dimen/module_call_chat_hawk_eye_circle_btn_size"
android:layout_marginRight="@dimen/module_call_chat_calling_iv_hawk_eye_margin_left_right"
android:src="@drawable/module_carchatting_team_quit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<LinearLayout
android:id="@+id/module_carchatting_team_ll_head"
android:layout_width="@dimen/module_call_chat_team_ll_head_width"
android:layout_height="@dimen/module_call_chat_state_hawk_eye_height"
android:gravity="center"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/module_carchatting_team_num"
android:layout_width="@dimen/module_call_chat_team_num_width"
android:layout_height="@dimen/module_call_chat_team_num_width"
android:background="@drawable/module_carchatting_vr_team_num_bg"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="@dimen/module_call_chat_team_num_text_size"
android:visibility="gone" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head0"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_1_5"
android:visibility="gone"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head1"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:layout_marginLeft="@dimen/module_call_chat_team_head_view_left_margin"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_1"
android:visibility="gone"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head2"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:layout_marginLeft="@dimen/module_call_chat_team_head_view_left_margin"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_0_5"
android:visibility="gone"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head3"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:layout_marginLeft="@dimen/module_call_chat_team_head_view_left_margin4"
android:layout_marginTop="@dimen/module_call_chat_team_head_view_top_margin"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_0_1"
android:visibility="gone"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head4"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:layout_marginLeft="@dimen/module_call_chat_team_head_view_left_margin"
android:layout_marginTop="@dimen/module_call_chat_team_head_view_top_margin"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_0"
android:visibility="gone"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
</LinearLayout>
<TextView
android:id="@+id/module_carchatting_team_tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/module_call_chat_team_info_margin_bottom"
android:text="@string/module_car_chat_team_info"
android:textColor="#7FFFFFFF"
android:textSize="@dimen/module_call_chat_team_info_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_team_ll_head" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/module_call_chat_team_chatting_margin_top"
android:text="@string/module_car_chat_team_calling"
android:textColor="@android:color/white"
android:textSize="@dimen/module_call_chat_team_chatting_text_size"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_team_ll_head"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,257 @@
<?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="@dimen/module_call_chat_state_width"
android:layout_height="@dimen/module_call_chat_state_height"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_rl_match_init_view"
android:layout_width="@dimen/module_call_chat_view_match_width"
android:layout_height="@dimen/module_call_chat_state_height"
android:background="@drawable/module_callchatting_shape_gradient_blue"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="@dimen/module_call_chat_match_iv_width"
android:layout_height="@dimen/module_call_chat_match_iv_height"
android:layout_marginStart="@dimen/module_call_chat_match_iv_left_margin"
android:src="@mipmap/module_callchatting_match"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/module_call_chat_view_margin_end"
android:text="@string/module_car_chat_match_tip"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_match_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_rl_matching_view"
android:layout_width="@dimen/module_call_chat_state_width"
android:layout_height="@dimen/module_call_chat_state_height"
android:background="@drawable/module_carchatting_launcher_calling_bg"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/module_call_chat_match_view_left_margin"
android:text="@string/module_car_chat_matching"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_matching_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/module_carchatting_tv_cancel_match"
android:layout_width="90px"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/module_call_chat_match_view_right_margin"
android:gravity="center"
android:text="@string/module_car_chat_matching_cancel"
android:textColor="@color/module_carchatting_red_color"
android:textSize="@dimen/module_call_chat_matching_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="1px"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/module_call_chat_match_view_line_margin_top_bottom"
android:layout_marginEnd="@dimen/module_call_chat_match_view_right_margin"
android:layout_marginBottom="@dimen/module_call_chat_match_view_line_margin_top_bottom"
android:background="@color/module_carchatting_match_line"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/module_carchatting_tv_cancel_match"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_rl_call_view"
android:layout_width="@dimen/module_call_chat_state_width"
android:layout_height="@dimen/module_call_chat_state_height"
android:background="@drawable/module_carchatting_launcher_calling_bg"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/module_carchatting_call_head"
android:layout_width="@dimen/module_call_chat_calling_iv_width_height"
android:layout_height="@dimen/module_call_chat_calling_iv_width_height"
android:layout_marginStart="@dimen/module_call_chat_calling_iv_margin_left_right"
android:clickable="true"
android:src="@mipmap/module_carchatting_default_head_img"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/module_carchatting_call_hangUp"
android:layout_width="@dimen/module_call_chat_calling_iv_width_height"
android:layout_height="@dimen/module_call_chat_calling_iv_width_height"
android:layout_marginEnd="@dimen/module_call_chat_calling_iv_margin_left_right"
android:src="@mipmap/module_carchatting_launcher_calling_hangup"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/module_carchatting_call_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/module_call_chat_calling_text_margin_top_bottom"
android:text="@string/module_car_chat_matching_wait"
android:textColor="@color/module_carchatting_status_color"
android:textSize="@dimen/module_call_chat_calling_text_time_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/module_carchatting_call_nickname" />
<TextView
android:id="@+id/module_carchatting_call_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_22"
android:layout_marginTop="@dimen/module_call_chat_calling_text_margin_top_bottom"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_calling_text_name_size"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_call_head"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_team_rl_view"
android:layout_width="@dimen/module_call_chat_state_width"
android:layout_height="@dimen/module_call_chat_state_height"
android:background="@drawable/module_carchatting_launcher_calling_bg"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/module_carchatting_team_ll_head"
android:layout_width="@dimen/dp_160"
android:layout_height="@dimen/module_call_chat_state_height"
android:gravity="center"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head0"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_1_5"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head1"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:layout_marginLeft="@dimen/module_call_chat_team_head_view_left_margin"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_1"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head2"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:layout_marginLeft="@dimen/module_call_chat_team_head_view_left_margin"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_0_5"
android:visibility="gone"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head3"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:layout_marginLeft="@dimen/module_call_chat_team_head_view_left_margin4"
android:layout_marginTop="@dimen/module_call_chat_team_head_view_top_margin"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_0_1"
android:visibility="gone"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head4"
android:layout_width="@dimen/module_call_chat_team_head_view_width"
android:layout_height="@dimen/module_call_chat_team_head_view_height"
android:layout_marginLeft="@dimen/module_call_chat_team_head_view_left_margin"
android:layout_marginTop="@dimen/module_call_chat_team_head_view_top_margin"
android:src="@mipmap/module_carchatting_default_head_img"
android:translationZ="@dimen/dp_0"
android:visibility="gone"
app:civ_border_color="#FFFFFF"
app:civ_border_width="2dp" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/module_call_chat_team_calling_left_margin"
android:layout_marginTop="@dimen/module_call_chat_team_calling_top_margin"
android:text="@string/module_car_chat_team_calling"
android:textColor="@color/module_carchatting_nick_color"
android:textSize="@dimen/module_call_chat_team_calling_size"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/module_carchatting_team_tv_info"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_team_ll_head" />
<TextView
android:id="@+id/module_carchatting_team_tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/module_call_chat_team_info_left_margin"
android:layout_marginBottom="@dimen/module_call_chat_team_info_bottom_margin"
android:text="@string/module_car_chat_team_info"
android:textColor="@color/module_carchatting_status_color"
android:textSize="@dimen/module_call_chat_team_info_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_team_ll_head" />
<ImageView
android:id="@+id/module_carchatting_iv_team_quit"
android:layout_width="@dimen/module_call_chat_team_quit_view_width"
android:layout_height="@dimen/module_call_chat_team_quit_view_height"
android:layout_marginRight="@dimen/module_call_chat_team_quit_right_margin"
android:src="@drawable/module_carchatting_team_quit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,80 @@
<?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="@dimen/module_call_chat_state_incoming_hawk_eye_width"
android:layout_height="@dimen/module_call_chat_state_incoming_hawk_eye_height"
android:background="@drawable/module_carchatting_vr_calling_bg"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/module_carchatting_rl_incoming_view"
android:layout_width="@dimen/module_call_chat_state_incoming_hawk_eye_width"
android:layout_height="@dimen/module_call_chat_state_incoming_hawk_eye_height"
android:visibility="visible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/module_carchatting_call_head"
android:layout_width="@dimen/module_call_chat_incoming_aisdk_tag_width"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:src="@drawable/module_carchatting_aicloud_incoming"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/module_carchatting_incoming_answer"
android:layout_width="@dimen/module_call_chat_hawk_eye_incoming_circle_btn_size"
android:layout_height="@dimen/module_call_chat_hawk_eye_incoming_circle_btn_size"
android:layout_marginEnd="@dimen/module_call_chat_calling_iv_hawk_eye_margin_left_right"
android:layout_marginRight="@dimen/module_call_chat_state_incoming_hawk_eye_call_margin_right"
android:clickable="true"
android:scaleType="centerInside"
android:src="@mipmap/module_callchatting_launcher_incoming_answer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/module_carchatting_incoming_hangUp"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/module_carchatting_incoming_hangUp"
android:layout_width="@dimen/module_call_chat_hawk_eye_incoming_circle_btn_size"
android:layout_height="@dimen/module_call_chat_hawk_eye_incoming_circle_btn_size"
android:layout_marginEnd="@dimen/module_call_chat_calling_iv_hawk_eye_margin_left_right"
android:clickable="true"
android:scaleType="centerInside"
android:src="@mipmap/module_callchatting_launcher_incoming_hangup"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/module_carchatting_call_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/module_call_chat_state_incoming_hawk_eye_margin_left"
android:layout_marginBottom="@dimen/module_call_chat_calling_text_hawk_eye_margin_top_bottom"
android:text="请求语音通话..."
android:textColor="@color/module_carchatting_hawk_eye_status_color"
android:textSize="@dimen/module_call_chat_calling_text_hawk_eye_time_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/module_carchatting_call_head" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/module_call_chat_calling_text_hawk_eye_margin_top_bottom"
android:text="云平台"
android:textColor="@android:color/white"
android:textSize="@dimen/module_call_chat_calling_text_hawk_eye_name_size"
app:layout_constraintStart_toStartOf="@+id/module_carchatting_call_time"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,74 @@
<?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="@dimen/module_call_chat_team_invitation_hawk_eye_width"
android:layout_height="@dimen/module_call_chat_team_invitation_hawk_eye_height"
android:background="@drawable/module_carchatting_team_invitation_hawk_eye_bg"
android:elevation="@dimen/dp_8">
<ImageView
android:layout_width="@dimen/dp_121"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/dp_31"
android:layout_marginTop="@dimen/dp_22"
android:layout_marginBottom="@dimen/dp_42"
android:background="@drawable/module_carchatting_team_invitation_hawk_eye_header_bg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/module_carchatting_civ_head"
android:layout_width="@dimen/dp_121"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/dp_31"
android:layout_marginTop="@dimen/dp_22"
android:layout_marginBottom="@dimen/dp_42"
android:src="@drawable/module_carchatting_team_invitation_hawk_eye_head_default"
android:scaleType="centerInside"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/module_carchatting_iv_join"
android:layout_width="@dimen/module_call_chat_hawk_eye_circle_btn_size"
android:layout_height="@dimen/module_call_chat_hawk_eye_circle_btn_size"
android:layout_marginEnd="@dimen/module_call_chat_team_invitation_join_hawk_eye_margin_right"
android:layout_marginBottom="@dimen/dp_21"
android:src="@drawable/module_carchatting_team_invitation_hawk_eye_join"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/module_carchatting_iv_refuse"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/module_carchatting_iv_refuse"
android:layout_width="@dimen/module_call_chat_hawk_eye_circle_btn_size"
android:layout_height="@dimen/module_call_chat_hawk_eye_circle_btn_size"
android:layout_marginBottom="@dimen/dp_21"
android:layout_marginEnd="@dimen/module_call_chat_team_invitation_hawk_eye_padding"
android:src="@drawable/module_carchatting_team_invitation_hawk_eye_refuse"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/module_carchatting_tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/module_call_chat_team_invitation_content_margin_left"
android:text="车友邀请你加入车队!"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_team_invitation_content_text_size"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/dp_21"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_civ_head" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/module_call_chat_team_invitation_height"
android:layout_margin="@dimen/module_common_shadow_width_pos"
android:background="@drawable/module_carchatting_team_invitation_bg"
android:elevation="@dimen/dp_8">
<ImageView
android:id="@+id/module_carchatting_civ_head"
android:layout_width="@dimen/module_call_chat_team_invitation_height"
android:layout_height="@dimen/module_call_chat_team_invitation_height"
android:src="@drawable/module_carchatting_team_invitation_head_default"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/module_carchatting_tv_title"
android:layout_width="@dimen/module_call_chat_team_invitation_title_width"
android:layout_height="@dimen/module_call_chat_team_invitation_title_height"
android:layout_marginLeft="@dimen/module_call_chat_team_invitation_title_margin_left"
android:layout_marginTop="@dimen/module_call_chat_team_invitation_title_margin_top"
android:background="@drawable/module_carchatting_team_invitation_title_bg"
android:gravity="center"
android:text="车队邀请"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_team_invitation_title_text_size"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_civ_head"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/module_carchatting_tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/module_call_chat_team_invitation_content_margin_left"
android:layout_marginBottom="@dimen/module_call_chat_team_invitation_content_margin_bottom"
android:text="车友邀请你加入车队!"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_team_invitation_content_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_civ_head" />
<ImageView
android:id="@+id/module_carchatting_iv_join"
android:layout_width="@dimen/module_call_chat_team_invitation_join_width"
android:layout_height="@dimen/module_call_chat_team_invitation_join_height"
android:layout_marginRight="@dimen/module_call_chat_team_invitation_join_margin_right"
android:src="@drawable/module_carchatting_team_invitation_join"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/module_carchatting_iv_refuse"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/module_carchatting_iv_refuse"
android:layout_width="@dimen/module_call_chat_team_invitation_join_width"
android:layout_height="@dimen/module_call_chat_team_invitation_join_height"
android:layout_marginRight="@dimen/module_call_chat_team_invitation_padding"
android:src="@drawable/module_carchatting_team_invitation_refuse"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@@ -0,0 +1,49 @@
<?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="@dimen/module_call_chat_team_teammate_height"
android:layout_marginTop="@dimen/module_call_chat_team_teammate_margin"
android:layout_marginBottom="@dimen/module_call_chat_team_teammate_margin"
android:background="@drawable/module_carchatting_team_teammate_bg"
android:padding="@dimen/module_call_chat_team_teammate_padding">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/module_carchatting_civ_head"
android:layout_width="@dimen/module_call_chat_team_teammate_head_width"
android:layout_height="@dimen/module_call_chat_team_teammate_head_height"
android:src="@mipmap/module_carchatting_default_head_img"
app:civ_border_color="#4cffffff"
app:civ_border_width="@dimen/module_call_chat_state_head_border"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/module_carchatting_tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/module_call_chat_team_teammate_nickname_margin_left"
android:text="昵称"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_team_teammate_nickname_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/module_carchatting_civ_head"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/module_carchatting_tv_identity"
android:layout_width="@dimen/module_call_chat_team_teammate_identity_width"
android:layout_height="@dimen/module_call_chat_team_teammate_identity_height"
android:background="@drawable/module_carchatting_team_teammate_follower_bg"
android:gravity="center"
android:text="身份"
android:textColor="@color/module_carchatting_white_color"
android:textSize="@dimen/module_call_chat_team_teammate_identity_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,105 @@
<?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="@dimen/module_call_chat_user_state_x"
android:layout_height="@dimen/module_call_chat_user_state_y"
android:orientation="vertical">
<ImageView
android:id="@+id/moduleCallChatBg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY" />
<ImageView
android:id="@+id/moduleCallChatCoverBg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@mipmap/module_carchatting_user_cover_bg" />
<ImageView
android:id="@+id/moduleCallChatUserClose"
android:layout_width="@dimen/module_call_chat_state_close_icon_width"
android:layout_height="@dimen/module_call_chat_state_close_icon_width"
android:layout_marginLeft="@dimen/module_call_chat_state_close_margin"
android:layout_marginTop="@dimen/module_call_chat_state_close_margin"
android:src="@drawable/module_carchatting_team_fragment_close"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/moduleCallChatUserHead"
android:layout_width="@dimen/module_call_chat_state_user_head_width"
android:layout_height="@dimen/module_call_chat_state_user_head_width"
android:layout_marginTop="@dimen/module_call_chat_state_head_margin_top"
android:src="@mipmap/module_carchatting_default_head_img"
app:civ_border_color="#E8EEF9"
app:civ_border_width="@dimen/module_call_chat_state_head_border"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/moduleCallChatUserNickName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/module_call_chat_state_nick_name_margin_top"
android:text="杨超越"
android:textColor="@color/module_carchatting_nick_color"
android:textSize="@dimen/module_call_chat_state_nick_name_size"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/moduleCallChatUserHead" />
<com.mogo.module.common.view.CustomRatingBar
android:id="@+id/moduleCallChatRatingBar"
style="@style/customHeartHeartRatingBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/module_call_chat_state_rating_margin_top"
android:isIndicator="true"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/moduleCallChatUserNickName"
tools:visibility="visible" />
<com.mogo.module.carchatting.view.DrawableTextView
android:id="@+id/moduleCallChatUserJoinTeam"
android:layout_width="@dimen/module_call_chat_user_caller_x"
android:layout_height="@dimen/module_call_chat_state_caller_height"
android:layout_marginLeft="@dimen/module_call_chat_state_caller_margin_left_right"
android:layout_marginBottom="@dimen/module_call_chat_state_caller_margin_bottom"
android:background="@drawable/module_callchatting_shape_gradient_blue"
android:drawableLeft="@mipmap/module_callchatting_user_join_team"
android:drawablePadding="@dimen/module_call_chat_state_caller_drawable_padding"
android:gravity="center_vertical"
android:text="@string/module_car_chat_user_join_team"
android:textColor="#FFFFFF"
android:textSize="@dimen/module_call_chat_state_caller_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/moduleCallChatUserCaller"
android:layout_width="@dimen/module_call_chat_user_caller_x"
android:layout_height="@dimen/module_call_chat_state_caller_height"
android:layout_marginRight="@dimen/module_call_chat_state_caller_margin_left_right"
android:layout_marginBottom="@dimen/module_call_chat_state_caller_margin_bottom"
android:background="@drawable/module_callchatting_user_pop_call_bg"
android:drawableLeft="@mipmap/module_callchatting_user_caller"
android:drawablePadding="@dimen/module_call_chat_state_caller_drawable_padding"
android:gravity="center_vertical"
android:paddingLeft="@dimen/module_call_chat_state_caller_padding_left"
android:text="@string/module_car_chat_user_caller"
android:textColor="@color/module_carchatting_nick_color"
android:textSize="@dimen/module_call_chat_state_caller_text_size"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

Some files were not shown because too many files have changed in this diff Show More