Merge branch 'dev/dev_eagle_architecture_upgrade2' into dev_MogoAP_eagle-1030_211020_8.0.14

This commit is contained in:
董宏宇
2021-10-26 10:45:08 +08:00
348 changed files with 16838 additions and 10 deletions

3
.idea/gradle.xml generated
View File

@@ -46,6 +46,9 @@
<option value="$PROJECT_DIR$/modules/mogo-module-adas" />
<option value="$PROJECT_DIR$/modules/mogo-module-apps" />
<option value="$PROJECT_DIR$/modules/mogo-module-back" />
<option value="$PROJECT_DIR$/modules/mogo-module-carchatting" />
<option value="$PROJECT_DIR$/modules/mogo-module-carchattingprovider" />
<option value="$PROJECT_DIR$/modules/mogo-module-chat" />
<option value="$PROJECT_DIR$/modules/mogo-module-common" />
<option value="$PROJECT_DIR$/modules/mogo-module-extensions" />
<option value="$PROJECT_DIR$/modules/mogo-module-main" />

View File

@@ -153,8 +153,6 @@ dependencies {
debugImplementation rootProject.ext.dependencies.debugleakcanary
releaseImplementation rootProject.ext.dependencies.releaseleakcanary
implementation rootProject.ext.dependencies.callchatprovider
implementation rootProject.ext.dependencies.callchat
implementation rootProject.ext.dependencies.mogologlib
compileOnly rootProject.ext.dependencies.adasapi
@@ -186,6 +184,10 @@ dependencies {
implementation rootProject.ext.dependencies.moduleleftpanelnoop
implementation rootProject.ext.dependencies.crashreportupgrade
implementation rootProject.ext.dependencies.crashreportbugly
implementation rootProject.ext.dependencies.callchat
implementation rootProject.ext.dependencies.callchatprovider
} else {
implementation project(':foudations:mogo-aicloud-services-sdk')
implementation project(':foudations:mogo-commons')
@@ -204,6 +206,10 @@ dependencies {
implementation project(':modules:mogo-module-apps')
implementation project(":modules:mogo-module-push-base")
implementation project(":modules:mogo-module-push")
implementation project(':modules:mogo-module-carchatting')
implementation project(':modules:mogo-module-carchattingprovider')
implementation project(':test:crashreport-upgrade')
implementation project(':test:crashreport-bugly')

View File

@@ -83,6 +83,7 @@ ext {
material : 'com.google.android.material:material:1.4.0',
indicator : 'com.github.zhpanvip:viewpagerindicator:1.0.4',
strategy : 'com.zhidaoauto.mic:strategy:1.0.6',
// modules
moduletanlu : "com.mogo.module:module-tanlu:${MOGO_MODULE_TANLU_VERSION}",
@@ -107,6 +108,7 @@ ext {
mogoserviceapi : "com.mogo.service:mogo-service-api:${MOGO_SERVICE_API_VERSION}",
moduleapps : "com.mogo.module:module-apps:${MOGO_MODULE_APPS_VERSION}",
moduleextensions : "com.mogo.module:module-extensions:${MOGO_MODULE_EXTENSIONS_VERSION}",
chat : "com.mogo.module.carchatout:module-chat:${CHAT_VERSION}",
callchat : "com.mogo.module.carchatout:module-carchatting:${CARCHATTING_VERSION}",
callchatprovider : "com.mogo.module.carchatout:module-carchatting-provider:${CARCHATTINGPROVIDER_VERSION}",
@@ -196,6 +198,7 @@ ext {
androidxroomktx : "androidx.room:room-ktx:2.2.3",
// rxjava2 with room
roomRxjava : 'androidx.room:room-rxjava2:2.2.3',
circleimageview : "de.hdodenhof:circleimageview:3.0.1",
//
mogomodulewidgets : "com.mogo.module:module-widgets:${MOGO_MODULE_WIDGETS_VERSION}",

View File

@@ -44,7 +44,6 @@ dependencies {
implementation rootProject.ext.dependencies.androidxrecyclerview
implementation rootProject.ext.dependencies.androidxconstraintlayout
implementation rootProject.ext.dependencies.arouter
implementation rootProject.ext.dependencies.callchatprovider
implementation rootProject.ext.dependencies.coroutinesandroid
implementation rootProject.ext.dependencies.coroutinescore
implementation rootProject.ext.dependencies.rxandroid
@@ -56,12 +55,15 @@ dependencies {
if (Boolean.valueOf(RELEASE)) {
implementation rootProject.ext.dependencies.mogocommons
implementation rootProject.ext.dependencies.mogoserviceapi
implementation rootProject.ext.dependencies.callchatprovider
} else {
implementation project(":foudations:mogo-commons")
implementation project(':services:mogo-service-api')
implementation project(':modules:mogo-module-common')
implementation project(':modules:mogo-module-service')
implementation project(':modules:mogo-module-carchattingprovider')
implementation project(':core:mogo-core-utils')
implementation project(':core:mogo-core-data')

View File

@@ -156,6 +156,7 @@ MOGO_OCH_TAXI_VERSION=2.0.58
# mogoAiCloud sdk services
MOGO_AICLOUD_SERVICES_SDK_VERSION=2.0.58
######## 外部依赖引用
CHAT_VERSION=1.8.0
# 车聊聊
CARCHATTING_VERSION=2.3.5
# 车聊聊接口
@@ -182,7 +183,7 @@ MOGO_TRAFFICLIVE_VERSION=1.1.46
# 定位服务
MOGO_LOCATION_VERSION=1.1.46
# 自研地图
MAP_SDK_VERSION=1.0.1-vr-1.1.6
MAP_SDK_VERSION=V2.0.0
#################架构升级新的版本号
MOGO_CORE_FUNCTION_HMI_VERSION=1.0.0
## 产品库必备配置产品库自动对versionCode和versionName版本进行升级

View File

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

View File

@@ -0,0 +1,92 @@
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
// buildToolsVersion rootProject.ext.android.buildToolsVersion
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())
}
}
}
buildTypes {
release {
minifyEnabled false
zipAlignEnabled false
consumerProguardFiles 'consumer-rules.pro'
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compileOnly rootProject.ext.dependencies.kotlinstdlibjdk7
compileOnly rootProject.ext.dependencies.androidxccorektx
implementation rootProject.ext.dependencies.androidxrecyclerview
compileOnly rootProject.ext.dependencies.coroutinescore
compileOnly rootProject.ext.dependencies.coroutinesandroid
compileOnly rootProject.ext.dependencies.androidxappcompat
compileOnly rootProject.ext.dependencies.androidxconstraintlayout
compileOnly rootProject.ext.dependencies.gson
implementation rootProject.ext.dependencies.arouter
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.circleimageview
implementation rootProject.ext.dependencies.mogowebsocket
if (Boolean.valueOf(RELEASE)) {
compileOnly rootProject.ext.dependencies.callchatprovider
compileOnly rootProject.ext.dependencies.mogomap
compileOnly rootProject.ext.dependencies.mogoutils
compileOnly rootProject.ext.dependencies.mogocommons
compileOnly rootProject.ext.dependencies.mogoserviceapi
compileOnly rootProject.ext.dependencies.modulecommon
implementation rootProject.ext.dependencies.chat
} else {
compileOnly project(':modules:mogo-module-carchattingprovider')
api project(":libraries:mogo-map")
api project(":foudations:mogo-utils")
api project(":foudations:mogo-commons")
api project(':services:mogo-service-api')
implementation project(':modules:mogo-module-common')
implementation project(':modules:mogo-module-chat')
implementation project(':core:mogo-core-data')
}
}
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()

View File

@@ -0,0 +1,33 @@
# 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
-keep class com.mogo.module.carchatting.bean.*{*;}
-keep class com.mogo.module.carchatting.card.CallChatProvider.*{*;}
-keep class com.mogo.module.carchatting.card.CallChatConstant.*{*;}
-keep class com.mogo.module.carchatting.biz.IBizCallChat.*{*;}
-keep class com.mogo.module.carchatting.invoke.CarsChattingProvider.*{*;}
-keep class com.mogo.module.carchatting.net.HttpApi.*{*;}
-keep class com.mogo.module.carchatting.net.HttpConstant.*{*;}
-keep class com.mogo.module.carchatting.util.AnalyticsUtil.*{*;}
-keep class com.mogo.module.carchatting.util.DateUtilKt.*{*;}
-keep class com.mogo.module.carchatting.view.*{*;}
-keep class com.mogo.module.carchatting.voice.*{*;}

View File

@@ -0,0 +1,3 @@
GROUP=com.mogo.module.carchatout
POM_ARTIFACT_ID=module-carchatting
VERSION_CODE=1

View File

@@ -0,0 +1,58 @@
# 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
#kotlin
-dontwarn kotlin.*
-dontwarn kotlin.**
-keep class kotlin.* { *; }
-keepclassmembernames class kotlinx.*{
volatile <fields>;
}
-keepclassmembernames class kotlin.*{
volatile <fields>;
}
-keep class kotlin.Metadata { *; }
-keepclassmembers class **$WhenMappings {
<fields>;
}
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-keepclassmembers class kotlin.Metadata { *; }
-keep @kotlin.Metadata class *
-keepclasseswithmembers @kotlin.Metadata class * { *; }
#-----CallChating-----
#AIDL
-keep class com.zhidao.imdemo.*
-keep class com.zhidao.imdemo.IMCallBack.*{*;}
-keep class com.zhidao.imdemo.IMCallRequest.*{*;}
-keep class com.mogo.module.carchatting.biz.IMCallManager.*{*;}
#Bean
-keep class com.mogo.module.carchatting.bean.*
-keep class com.mogo.module.carchatting.bean.ResponseInfo.*{*;}
-keep class com.mogo.module.carchatting.bean.UserInfo{*;}
-keep class com.mogo.module.carchatting.card.*
-keep class com.mogo.module.carchatting.card.CallChatConstant.MODULE_NAME.*
-keep class com.mogo.module.carchatting.card.CallChatConstant.PROVIDER.*
-keep class com.mogo.module.carchatting.card.CallChatConstant.PATH.*

View File

@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.module.carchatting" />

View File

@@ -0,0 +1,103 @@
package com.mogo.module.carchatting.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.request.RequestOptions;
import com.mogo.chat.model.bean.TeammateInfo;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.module.carchatting.R;
import com.mogo.module.common.glide.SkinAbleBitmapTarget;
import com.mogo.utils.glide.GlideApp;
import java.util.List;
import de.hdodenhof.circleimageview.CircleImageView;
/**
* created by wujifei on 2020/11/16 16:30
* describe:车队人员适配器
*/
public class VehicleTeamAdapter extends RecyclerView.Adapter<VehicleTeamAdapter.VehicleTeamViewHolder> {
private Context mContext;
private List<TeammateInfo> teammates;
public VehicleTeamAdapter(Context mContext, List<TeammateInfo> teammates) {
this.mContext = mContext;
this.teammates = teammates;
}
public void setTeammates(List<TeammateInfo> teammates) {
this.teammates = teammates;
}
@NonNull
@Override
public VehicleTeamViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new VehicleTeamViewHolder(LayoutInflater.from(mContext).inflate(R.layout.module_car_chatting_launcher_teammate_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull VehicleTeamViewHolder holder, int position) {
TeammateInfo teammateInfo = teammates.get(position);
RequestOptions requestOptions = new RequestOptions().circleCrop()
.placeholder(R.mipmap.module_carchatting_default_head_img)
.error(R.mipmap.module_carchatting_default_head_img);
GlideApp.with(AbsMogoApplication.getApp().getApplicationContext()).asBitmap()
.load(teammateInfo.getHeadImgUrl())
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop()
.into(new SkinAbleBitmapTarget(holder.moduleCarchattingCivHead, requestOptions));
holder.moduleCarchattingTvNickname.setText(teammateInfo.getNickName() == null ? "蘑菇车主" : teammateInfo.getNickName());
if (teammateInfo.getVehicleTeamLeader()) {
holder.moduleCarchattingTvIdentity.setText("队长");
holder.moduleCarchattingTvIdentity.setBackgroundResource(R.drawable.module_carchatting_team_teammate_leader_bg);
holder.moduleCarchattingTvIdentity.setTextColor(mContext.getResources().getColor(R.color.module_carchatting_team_leader));
} else {
holder.moduleCarchattingTvIdentity.setText("队员");
holder.moduleCarchattingTvIdentity.setBackgroundResource(R.drawable.module_carchatting_team_teammate_follower_bg);
}
holder.itemView.setOnClickListener(view -> {
if (mClickListener != null) {
mClickListener.onItemClickListener(position, teammateInfo);
}
});
}
@Override
public int getItemCount() {
return teammates == null ? 0 : teammates.size();
}
static class VehicleTeamViewHolder extends RecyclerView.ViewHolder {
private TextView moduleCarchattingTvIdentity;
private CircleImageView moduleCarchattingCivHead;
private TextView moduleCarchattingTvNickname;
public VehicleTeamViewHolder(@NonNull View itemView) {
super(itemView);
moduleCarchattingTvIdentity = (TextView) itemView.findViewById(R.id.module_carchatting_tv_identity);
moduleCarchattingCivHead = (CircleImageView) itemView.findViewById(R.id.module_carchatting_civ_head);
moduleCarchattingTvNickname = (TextView) itemView.findViewById(R.id.module_carchatting_tv_nickname);
}
}
private VehicleTeamItemClickListener mClickListener;
public void setItemClickListener(VehicleTeamItemClickListener clickListener) {
this.mClickListener = clickListener;
}
public interface VehicleTeamItemClickListener {
void onItemClickListener(int position, TeammateInfo teammateInfo);
}
}

View File

@@ -0,0 +1,82 @@
package com.mogo.module.carchatting.anim_dsl
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.view.animation.Interpolator
import android.view.animation.LinearInterpolator
/**
* an Animation just like [ValueAnimator], but it could reverse itself without the limitation of API level
* [ValueAnimator.reverse] is only available to the API level over 26.
* so this class comes to help.
*/
abstract class Anim {
/**
* the real Animator which is about to run
*/
abstract var animator: Animator
var builder: AnimatorSet.Builder? = null
/**
* the duration of Animator
*/
var duration
get() = 300L
set(value) {
animator.duration = value
}
/**
* the interpolator of Animator
*/
var interpolator
get() = LinearInterpolator() as Interpolator
set(value) {
animator.interpolator = value
}
/**
* start delay of Animator
*/
var delay
get() = 0L
set(value) {
animator.startDelay = value
}
/**
* the callbacks describe the status of animation
*/
var onRepeat: ((Animator) -> Unit)? = null
var onEnd: ((Animator) -> Unit)? = null
var onCancel: ((Animator) -> Unit)? = null
var onStart: ((Animator) -> Unit)? = null
/**
* reverse the value of [ValueAnimator]
*/
abstract fun reverse()
/**
* to the beginning of animation
*/
abstract fun toBeginning()
internal fun addListener() {
animator.addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
animation?.let { onRepeat?.invoke(it) }
}
override fun onAnimationEnd(animation: Animator?) {
animation?.let { onEnd?.invoke(it) }
}
override fun onAnimationCancel(animation: Animator?) {
animation?.let { onCancel?.invoke(it) }
}
override fun onAnimationStart(animation: Animator?) {
animation?.let { onStart?.invoke(it) }
}
})
}
}

View File

@@ -0,0 +1,155 @@
package com.mogo.module.carchatting.anim_dsl
import android.animation.Animator
import android.animation.AnimatorSet
/**
* a Container for [Anim] just like [AnimatorSet], but it could reverse itself without API level limitation.
* In addition, it is easy to build mush shorter and readable animation code like the following:
*
* animSet {
* anim {
* values = floatArrayOf(1.0f, 1.4f)
* action = { value -> tv.scaleX = (value as Float) }
* } with anim {
* values = floatArrayOf(0f, -200f)
* action = { value -> tv.translationY = (value as Float) }
* }
* duration = 200L
* }
*
* if you want to run animation with several properties on one Object,
* using [ObjectAnim] is more efficient than [ValueAnim], like the following:
*
* animSet {
* objectAnim {
* target = tvTitle
* translationX = floatArrayOf(0f, 200f)
* alpha = floatArrayOf(1.0f, 0.3f)
* scaleX = floatArrayOf(1.0f, 1.3f)
* }
* duration = 100L
* }
*
*/
class AnimSet : Anim() {
override var animator: Animator = AnimatorSet()
private val animatorSet
get() = animator as AnimatorSet
private val anims by lazy { mutableListOf<Anim>() }
/**
* whether animation is at start point
*/
private var isAtStartPoint: Boolean = true
/**
* whether animation value has reversed
*/
private var hasReverse: Boolean = false
/**
* it creates a single [ValueAnim]
* [with] and [before] is available to combine several [anim] to one complex animation set by chain-invocation style.
*/
fun anim(animCreation: ValueAnim.() -> Unit): Anim = ValueAnim().apply(animCreation).also { it.addListener() }.also { anims.add(it) }
/**
* build an [ObjectAnim] with a much shorter and readable code by DSL
*/
fun objectAnim(action: ObjectAnim.() -> Unit): Anim =
ObjectAnim().apply(action).also { it.setPropertyValueHolder() }.also { it.addListener() }.also { anims.add(it) }
/**
* start the [AnimSet]
*/
fun start() {
if (animatorSet.isRunning) return
anims.takeIf { hasReverse }?.forEach { anim -> anim.reverse() }.also { hasReverse = false }
if (anims.size == 1) animatorSet.play(anims.first().animator)
if (isAtStartPoint) {
animatorSet.start()
isAtStartPoint = false
}
}
/**
* reverse the animation
*/
override fun reverse() {
if (animatorSet.isRunning) return
anims.takeIf { !hasReverse }?.forEach { anim -> anim.reverse() }.also { hasReverse = true }
if (!isAtStartPoint) {
animatorSet.start()
isAtStartPoint = true
}
}
override fun toBeginning() {
anims.forEach { it.toBeginning() }
}
/**
* get the animation in the [AnimSet] by [index]
*/
fun getAnim(index: Int) = anims.takeIf { index in 0 until anims.size }?.let { it[index] }
/**
* cancel the [AnimatorSet]
*/
fun cancel() {
animatorSet.cancel()
}
/**
* if you want to play animations one after another, use [before] to link several [Anim],
* like the following:
*
* animSet {
* anim {
* value = floatArrayOf(1.0f, 1.4f)
* action = { value -> tv.scaleX = (value as Float) }
* } before anim {
* values = floatArrayOf(0f, -200f)
* action = { value -> btn.translationY = (value as Float) }
* }
* duration = 200L
* }
*
*/
infix fun Anim.before(anim: Anim): Anim {
animatorSet.play(animator).before(anim.animator).let { this.builder = it }
return anim
}
/**
* if you want to play animations at the same time, use [with] to link several [Anim],
* like the following:
*
* animSet {
* play {
* value = floatArrayOf(1.0f, 1.4f)
* action = { value -> tv.scaleX = (value as Float) }
* } with anim {
* values = floatArrayOf(0f, -200f)
* action = { value -> btn.translationY = (value as Float) }
* }
* duration = 200L
* }
*
* if there are both [with] and [before] in one invocation chain, [with] has higher priority,
* for example: `a before b with c` means b and c will play at the same time and a plays before them
*
*/
infix fun Anim.with(anim: Anim): Anim {
if (builder == null) builder = animatorSet.play(animator).with(anim.animator)
else builder?.with(anim.animator)
return anim
}
}
/**
* build a set of animation with a much shorter and readable code by DSL
*/
fun animSet(creation: AnimSet.() -> Unit) = AnimSet().apply { creation() }.also { it.addListener() }

View File

@@ -0,0 +1,219 @@
package com.mogo.module.carchatting.anim_dsl
import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
import android.view.View
/**
* An Animator just like [ObjectAnimator], but it could reverse itself without the limitation of API level.
* In addition, combing with [AnimSet], it is easy to build much more readable animation code. See at [AnimSet]
*/
class ObjectAnim : Anim() {
companion object {
private const val TRANSLATION_X = "translationX"
private const val TRANSLATION_Y = "translationY"
private const val SCALE_X = "scaleX"
private const val SCALE_Y = "scaleY"
private const val ALPHA = "alpha"
private const val ROTATION = "rotation"
private const val ROTATION_X = "rotationX"
private const val ROTATION_Y = "rotationY"
}
/**
* the [ObjectAnim] is about to run
*/
override var animator: Animator = ObjectAnimator()
private val objectAnimator
get() = animator as ObjectAnimator
/**
* predefine properties for [android.view.View]
* add a set() for each property inorder to change it dynamically
*/
var translationX: FloatArray? = null
set(value) {
field = value
translationX?.let { PropertyValuesHolder.ofFloat(TRANSLATION_X, *it) }?.let { property ->
valuesHolder[TRANSLATION_X] = property
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}
var translationY: FloatArray? = null
set(value) {
field = value
translationY?.let { PropertyValuesHolder.ofFloat(TRANSLATION_Y, *it) }?.let { property ->
valuesHolder[TRANSLATION_Y] = property
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}
var scaleX: FloatArray? = null
set(value) {
field = value
scaleX?.let { PropertyValuesHolder.ofFloat(SCALE_X, *it) }?.let { property ->
valuesHolder[SCALE_X] = property
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}
var scaleY: FloatArray? = null
set(value) {
field = value
scaleY?.let { PropertyValuesHolder.ofFloat(SCALE_Y, *it) }?.let { property ->
valuesHolder[SCALE_Y] = property
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}
var alpha: FloatArray? = null
set(value) {
field = value
alpha?.let { PropertyValuesHolder.ofFloat(ALPHA, *it) }?.let { property ->
valuesHolder[ALPHA] = property
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}
var rotation: FloatArray? = null
set(value) {
field = value
rotation?.let { PropertyValuesHolder.ofFloat(ROTATION, *it) }?.let { property ->
valuesHolder[ROTATION] = property
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}
var rotationX: FloatArray? = null
set(value) {
field = value
rotationX?.let { PropertyValuesHolder.ofFloat(ROTATION_X, *it) }?.let { property ->
valuesHolder[ROTATION_X] = property
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}
var rotationY: FloatArray? = null
set(value) {
field = value
rotationY?.let { PropertyValuesHolder.ofFloat(ROTATION_Y, *it) }?.let { property ->
valuesHolder[ROTATION_Y] = property
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}
/**
* the object to be animated which is needed for [ObjectAnimator]
*/
var target: Any? = null
set(value) {
field = value
(animator as ObjectAnimator).target = value
}
/**
* the repeat times of [ObjectAnim]
* [ValueAnimator.INFINITE] means repeat forever
*/
var repeatCount
get() = 0
set(value) {
objectAnimator.repeatCount = value
}
/**
* the repeat mode of [ObjectAnim]
* the available value is [ValueAnimator.RESTART] or [ValueAnimator.REVERSE]
*/
var repeatMode
get() = ValueAnimator.RESTART
set(value) {
objectAnimator.repeatMode = value
}
/**
* a map of [PropertyValuesHolder] to describe what kind of animations to run
*/
private val valuesHolder = mutableMapOf<String, PropertyValuesHolder>()
/**
* reverse the value of [ObjectAnimator]
*/
override fun reverse() {
valuesHolder.forEach { valuesHolder ->
when (valuesHolder.key) {
TRANSLATION_X -> translationX?.let {
it.reverse()
this.valuesHolder[TRANSLATION_X]?.setFloatValues(*it)
}
TRANSLATION_Y -> translationY?.let {
it.reverse()
this.valuesHolder[TRANSLATION_Y]?.setFloatValues(*it)
}
SCALE_X -> scaleX?.let {
it.reverse()
this.valuesHolder[SCALE_X]?.setFloatValues(*it)
}
SCALE_Y -> scaleY?.let {
it.reverse()
this.valuesHolder[SCALE_Y]?.setFloatValues(*it)
}
ALPHA -> alpha?.let {
it.reverse()
this.valuesHolder[ALPHA]?.setFloatValues(*it)
}
ROTATION -> rotation?.let {
it.reverse()
this.valuesHolder[ROTATION]?.setFloatValues(*it)
}
ROTATION_X -> rotationX?.let {
it.reverse()
this.valuesHolder[ROTATION_X]?.setFloatValues(*it)
}
ROTATION_Y -> rotationY?.let {
it.reverse()
this.valuesHolder[ROTATION_Y]?.setFloatValues(*it)
}
}
}
}
override fun toBeginning() {
valuesHolder.forEach { valuesHolder ->
when (valuesHolder.key) {
TRANSLATION_X -> translationX?.let {
(target as? View)?.translationX = it.first()
}
TRANSLATION_Y -> translationY?.let {
(target as? View)?.translationY = it.first()
}
SCALE_X -> scaleX?.let {
(target as? View)?.scaleX = it.first()
}
SCALE_Y -> scaleY?.let {
(target as? View)?.scaleY = it.first()
}
ALPHA -> alpha?.let {
(target as? View)?.alpha = it.first()
}
ROTATION -> rotation?.let {
(target as? View)?.rotation = it.first()
}
ROTATION_X -> rotationX?.let {
(target as? View)?.rotationX = it.first()
}
ROTATION_Y -> rotationY?.let {
(target as? View)?.rotationY = it.first()
}
}
}
}
/**
* add available [PropertyValuesHolder] to the list
*/
fun setPropertyValueHolder() {
translationX?.let { PropertyValuesHolder.ofFloat(TRANSLATION_X, *it) }?.let { valuesHolder[TRANSLATION_X] = it }
translationY?.let { PropertyValuesHolder.ofFloat(TRANSLATION_Y, *it) }?.let { valuesHolder[TRANSLATION_Y] = it }
scaleX?.let { PropertyValuesHolder.ofFloat(SCALE_X, *it) }?.let { valuesHolder[SCALE_X] = it }
scaleY?.let { PropertyValuesHolder.ofFloat(SCALE_Y, *it) }?.let { valuesHolder[SCALE_Y] = it }
alpha?.let { PropertyValuesHolder.ofFloat(ALPHA, *it) }?.let { valuesHolder[ALPHA] = it }
rotation?.let { PropertyValuesHolder.ofFloat(ROTATION, *it) }?.let { valuesHolder[ROTATION] = it }
rotationX?.let { PropertyValuesHolder.ofFloat(ROTATION_X, *it) }?.let { valuesHolder[ROTATION_X] = it }
rotationY?.let { PropertyValuesHolder.ofFloat(ROTATION_Y, *it) }?.let { valuesHolder[ROTATION_Y] = it }
objectAnimator.setValues(*valuesHolder.values.toTypedArray())
}
}

View File

@@ -0,0 +1,84 @@
package com.mogo.module.carchatting.anim_dsl
import android.animation.Animator
import android.animation.ValueAnimator
/**
* An Animator just like [ValueAnimator], but it could reverse itself without the limitation of API level.
* In addition, combing with [AnimSet], it is easy to build much more readable animation code. See at [AnimSet]
*/
class ValueAnim : Anim() {
/**
* the [ValueAnimator] is about to run
*/
override var animator: Animator = ValueAnimator()
private val valueAnimator
get() = animator as ValueAnimator
/**
* the animation value
*/
var values: Any? = null
set(value) {
field = value
value?.let {
when (it) {
is FloatArray -> valueAnimator.setFloatValues(*it)
is IntArray -> valueAnimator.setIntValues(*it)
else -> throw IllegalArgumentException("unsupported value type")
}
}
}
/**
* [action] describe what to animate
*/
var action: ((Any) -> Unit)? = null
set(value) {
field = value
valueAnimator.addUpdateListener { valueAnimator ->
valueAnimator.animatedValue.let { value?.invoke(it) }
}
}
/**
* the repeat times of [ValueAnim]
* [ValueAnimator.INFINITE] means repeat forever
*/
var repeatCount
get() = 0
set(value) {
valueAnimator.repeatCount = value
}
/**
* the repeat mode of [ValueAnim]
* the available value is [ValueAnimator.RESTART] or [ValueAnimator.REVERSE]
*/
var repeatMode
get() = ValueAnimator.RESTART
set(value) {
valueAnimator.repeatMode = value
}
/**
* reverse the value of [ValueAnimator]
*/
override fun reverse() {
values?.let {
when (it) {
is FloatArray -> {
it.reverse()
valueAnimator.setFloatValues(*it)
}
is IntArray -> {
it.reverse()
valueAnimator.setIntValues(*it)
}
else -> throw IllegalArgumentException("unsupported type of value")
}
}
}
override fun toBeginning() {
}
}

View File

@@ -0,0 +1,9 @@
package com.mogo.module.carchatting.bean
class EnthusiasmIndex(
val id: Int = 0, val sn: String? = null,
val score: Int = 0, val shareNum: Int = 0,
val likeNum: Int = 0, val notLikeNum: Int = 0,
val enthusiasmIndex: Double = 10.0,
val createTime: String? = null, val updateTime: String? = null
)

View File

@@ -0,0 +1,3 @@
package com.mogo.module.carchatting.bean
data class ErrorInfo(var errorCode: Int, var errorMsg: String)

View File

@@ -0,0 +1,3 @@
package com.mogo.module.carchatting.bean
data class ResponseInfo(var code: Int, var msg: String)

View File

@@ -0,0 +1,3 @@
package com.mogo.module.carchatting.bean
class Result(val enthusiasmIndex: EnthusiasmIndex)

View File

@@ -0,0 +1,52 @@
package com.mogo.module.carchatting.bean
import android.text.TextUtils
import com.mogo.chat.model.bean.Sns
fun toUserInfo(sns: Sns): UserInfo {
return UserInfo(sns.sn,
sns.userId.toLong(),
sns.nickName ?: "",
sns.headImgUrl ?: "",
sns.cardIdSex ?: "",
sns.cardIdAge ?: "",
sns.carInfo ?: "",
sns.cityName ?: "",
sns.lat.toString(),
sns.lon.toString())
}
class UserInfo(
val sn: String,
val userId: Long,
userName: String? = "小蘑菇",
val userHead: String?,
val gender: String?,
val age: String?,
val carTypeName: String?,
val location: String?,
val lat: String,
val lon: String
) {
var userName: String? = "小蘑菇"
set(value) {
field = if (TextUtils.isEmpty(value)) {
"小蘑菇"
} else {
value
}
}
init {
if (TextUtils.isEmpty(userName)) {
this.userName = "小蘑菇"
} else {
this.userName = userName
}
}
override fun toString(): String {
return "UserInfo(sn='$sn', userId=$userId, userHead='$userHead', gender='$gender', age='$age', carTypeName='$carTypeName', location='$location', userName='$userName', lat='$lat', lon='$lon')"
}
}

View File

@@ -0,0 +1,15 @@
package com.mogo.module.carchatting.bean
import com.google.gson.annotations.SerializedName
fun VoiceWake.check():Boolean{
return obj.equals("车聊聊") && operation.equals("打开")
}
class VoiceWake {
@SerializedName("object")
var obj:String? = null
var operation:String? = null
}

View File

@@ -0,0 +1,686 @@
package com.mogo.module.carchatting.biz
import android.content.Context
import androidx.fragment.app.FragmentActivity
import com.alibaba.android.arouter.launcher.ARouter
import com.google.gson.Gson
import com.mogo.chat.constant.CALL_TYPE_VEHICLE_TEAM
import com.mogo.chat.model.bean.TeammateInfo
import com.mogo.chat.service.IMService
import com.mogo.chat.util.UserInfoHelper.currentCallType
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.data.constants.MogoServicePaths.PATH_SERVICE_APIS
import com.mogo.module.carchatting.R
import com.mogo.module.carchatting.bean.UserInfo
import com.mogo.module.carchatting.card.CallChatConstant.Companion.MODULE_NAME
import com.mogo.module.carchatting.util.*
import com.mogo.module.carchatting.view.CallingWindowManager.Companion.callingWindowManager
import com.mogo.module.carchatting.view.MapViewManager.Companion.mapViewManager
import com.mogo.module.carchatting.view.TeamInvitationWindowManager.Companion.teamInvitationWindowManager
import com.mogo.module.carchatting.view.UserWindowManager.Companion.userWindowManager
import com.mogo.module.carchatting.view.VehicleTeamFragment
import com.mogo.module.carchatting.voice.IVoiceCommandListener
import com.mogo.module.carchatting.voice.IVoiceIntentListener
import com.mogo.module.carchatting.voice.VoiceUtil
import com.mogo.service.IMogoServiceApis
import com.mogo.service.statusmanager.IMogoStatusChangedListener
import com.mogo.service.statusmanager.StatusDescriptor
import com.mogo.utils.TipToast
import com.mogo.utils.logger.Logger
import com.zhidao.carchattingprovider.MogoDriverInfo
class CallChatCenter private constructor() : IBizCallChat.IBizCallBack, IVoiceCommandListener,
IVoiceIntentListener {
companion object {
const val TAG: String = "CallChatCenter"
val callChatCenter by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
CallChatCenter()
}
}
private var showCallWindowFromDefault = false
private var currentUserInfo: UserInfo? = null
private var teamMember: List<TeammateInfo>? = null
private var serviceApi: IMogoServiceApis? = null
private var context: Context? = null
private val callChatManager: CallChatManager = CallChatManager(this)
private var callWindowStatusListener: ((Boolean) -> Unit)? = null
private var userWindowStatusListener: ((Boolean) -> Unit)? = null
private var flag: String? = null
private var containerId: Int? = null
private var activity: FragmentActivity? = null
fun init(mContext: Context) {
Logger.i(TAG, "init")
this.context = mContext
initServiceApi()
teamInvitationWindowManager.init(mContext)
callingWindowManager.init(mContext, serviceApi!!, {
match(MATCH_TYPE_MANUAL, 2)
}, {
cancelMatch(false)
}, {
showVehicleTeamFragment()
}, {
hangUp()
}, {
// Logger.i(TAG, "click head to move caller position , currentUserInfo : $currentUserInfo")
// MogoApisHandler.getInstance().apis.mapServiceApi.mapUIController
// .moveToCenter(MogoLatLng(currentUserInfo?.lat?.toDouble()
// ?: 0.0, currentUserInfo?.lon?.toDouble() ?: 0.0)) //todo 需要产品理出需求,兼容他车
})
VoiceUtil.registerAll(context!!, this, this)
syncInitStatus()
}
private fun initServiceApi() {
Logger.i(TAG, "initServiceApi Launch IMService")
IMService.launchService(context!!)
Logger.i(TAG, "initServiceApi IMogoServiceApis")
val mogoService = ARouter.getInstance().build(PATH_SERVICE_APIS).navigation()
if (mogoService is IMogoServiceApis) {
this.serviceApi = mogoService
}
serviceApi?.statusManagerApi?.registerStatusChangedListener(
MODULE_NAME,
StatusDescriptor.MAIN_PAGE_RESUME,
statusChangedListener
)
serviceApi?.statusManagerApi?.registerStatusChangedListener(
MODULE_NAME,
StatusDescriptor.VR_MODE,
statusChangedListener
)
}
private val statusChangedListener = IMogoStatusChangedListener { descriptor, status ->
Logger.i(TAG, "IMogoStatusChangedListener status : $status descriptor : $descriptor")
when (descriptor) {
StatusDescriptor.MAIN_PAGE_RESUME -> {
if (status) {
VoiceUtil.registerAll(context!!, this, this)
syncInitStatus()
Logger.i(TAG, "MAIN_PAGE_RESUME callType: $currentCallType , status : ${callChatManager.isCalling()}")
if (currentCallType == CALL_TYPE_VEHICLE_TEAM && callChatManager.isCalling()) {
callWindowShowCallBack()
callingWindowManager.showVehicleTeamView()
callingWindowManager.updateVehicleTeamHeadView(teamMember)
mapViewManager.updateTeamMarker(context!!, teamMember)
} else if (callChatManager.isInit()) {
callingWindowManager.showInitView()
} else {
syncCallViewStatus()
}
} else {
VoiceUtil.unregisterAll(context!!, this)
onPause()
}
}
StatusDescriptor.VR_MODE -> {
Logger.i(TAG, "VR_MODE callType: $currentCallType , status : ${callChatManager.isCalling()}")
updateCallingWindowInVRMode()
}
else -> {
//do nothing for now
}
}
}
//更新VRMode模式下打电话界面
private fun updateCallingWindowInVRMode() {
callingWindowManager.resetVRModeView()
if (!callingWindowManager.isCallingWindowShow()) {
return
}
if (currentCallType == CALL_TYPE_VEHICLE_TEAM && callChatManager.isCalling()) {
callWindowHideCallBack()
callingWindowManager.hideVehicleTeamView()
callWindowShowCallBack()
callingWindowManager.showVehicleTeamView()
callingWindowManager.updateVehicleTeamHeadView(teamMember)
mapViewManager.updateTeamMarker(context!!, teamMember)
} else {
callingWindowManager.hideCallingView()
callWindowHideCallBack()
syncCallViewStatus()
}
}
fun invokeCall(userData: String) {
Logger.d(TAG, "其他模块来请求打电话给某人: $userData")
if (showCallWindowFromDefault) {
toast(R.string.module_car_chat_can_not_call)
return
}
showCallWindowFromDefault = true
handleCallInvokeData(userData)
}
fun registerCallWindowStatusListener(callWindowStatusListener: ((Boolean) -> Unit)) {
this.callWindowStatusListener = callWindowStatusListener
}
fun unRegisterCallWindowStatusListener() {
this.callWindowStatusListener = null
}
fun registerUserWindowStatusListener(userWindowStatusListener: ((Boolean) -> Unit)) {
this.userWindowStatusListener = userWindowStatusListener
}
fun unRegisterUserWindowStatusListener() {
this.userWindowStatusListener = null
}
fun showUserWindow(mogoDriverInfo: MogoDriverInfo) {
userWindowManager.showUserView(mogoDriverInfo) {
userWindowStatusListener?.invoke(it)
}
}
fun hideUserWindow(onError: ((String) -> Unit)? = null, onFinish: (() -> Unit)? = null) {
if (userWindowManager.isCallingWindowShow()) {
userWindowManager.hideUserView()
} else {
onError?.invoke("user window already hide, please don't repeat call")
}
onFinish?.invoke()
}
fun updateUserCallStatus(status: ((Boolean) -> Unit)) {
val callStatus = callChatManager.canCall() && !showCallWindowFromDefault
status.invoke(callStatus)
}
fun getUserEnthusiasmIndex(sn: String) {
callChatManager.getEnthusiasmIndex(sn, { enthusiasmIndex ->
userWindowManager.updateUserEnthusiasmIndex(enthusiasmIndex)
}, { errorMsg ->
Logger.i(TAG, "获取用户热心指数失败 msg : $errorMsg")
})
}
fun getUserInfo(sn: String) {
callChatManager.getUserInfo(sn, {
val userInfoMap = Gson().fromJson<Map<String, String>>(it, Map::class.java)
userWindowManager.updateUserInfo(convertMapToDriverInfo(userInfoMap))
}, {
Logger.i(TAG, it)
})
}
private fun match(matchType: Int, from: Int = 1) {
LogUtil.i(TAG, "startMatch")
AnalyticsUtil.track(
INVOKE_TRACK_MATCH_SHOW,
mutableMapOf("matchatype" to matchType, "source" to 2, "from" to from)
)
callChatManager.match()
}
private fun cancelMatch(isTTS: Boolean = true) {
LogUtil.i(TAG, "cancelMatch cardStatus : ${callChatManager.getStatusName()}")
if (callChatManager.isMatching()) {
LogUtil.i(TAG, "CARD_MATCHING cancelMatch")
callChatManager.cancelMatch()
cancelMatchToDefault(isTTS)
}
if (callChatManager.isMatchFailed()) {
LogUtil.i(TAG, "CARD_MATCH_FAILED matchFailed")
val type = if (isTTS) 1 else 2
AnalyticsUtil.track(
INVOKE_TRACK_MATCH_FAIL_CLOSE_CLICK,
hashMapOf("type" to type)
)
cancelMatchToDefault(isTTS)
}
}
private fun showVehicleTeamFragment() {
LogUtil.i(TAG, "showVehicleTeamFragment")
activity?.supportFragmentManager
?.beginTransaction()
?.replace(containerId!!, VehicleTeamFragment.newInstance(), VehicleTeamFragment::class.simpleName)
?.commitNowAllowingStateLoss()
}
fun closeVehicleTeamFragment() {
activity?.let {
it.supportFragmentManager.let { fragmentManager ->
fragmentManager.findFragmentByTag(VehicleTeamFragment::class.simpleName)?.let { fragment ->
fragmentManager.beginTransaction()
.remove(fragment)
.commitNowAllowingStateLoss()
}
}
}
}
private fun cancelMatchToDefault(isTTS: Boolean) {
LogUtil.i(TAG, "cancelMatchToDefault ---> cardStatus : ${callChatManager.getStatusName()}")
if (isTTS) {
speakContent(context!!.getString(R.string.module_car_chat_already_cancel))
}
refreshViewByType()
}
override fun onVoiceStartMatch() {
match(MATCH_TYPE_VOICE)
}
override fun onVoiceCancelCall() {
Logger.d(TAG, "语音 收到取消打电话指令====callStatus: ${callChatManager.getStatusName()}")
if (callChatManager.isReadyCalling()) {
refuseCall()
speakContent(context!!.resources.getString(R.string.module_car_chat_already_cancel))
}
}
override fun onVoiceCancelMatch(isTTS: Boolean) {
cancelMatch()
}
private fun speakContent(content: String) {
VoiceUtil.speak(content, context!!, this)
}
private fun handleCallInvokeData(userData: String) {
Logger.d(TAG, "handleCallInvokeData Thread: ${Thread.currentThread()}")
if (callChatManager.isInit()) {
callChatManager.invokeCallData(userData)
} else {
toast(R.string.module_car_chat_can_not_call)
}
}
private fun syncCallViewStatus() {
Logger.d(TAG, "syncCallingStatus callStatus : ${callChatManager.getStatusName()}")
if (callChatManager.isCalling() || callChatManager.isReadyCalling() || callChatManager.isMatching()) {
showLauncherCallingView()
}
}
private fun syncInitStatus() {
if (currentCallType != CALL_TYPE_VEHICLE_TEAM) {
callChatManager.getUserInfoForCall()
}
callChatManager.syncCallTimer { timeStr ->
taskMainLaunch {
Logger.d(TAG, "syncCallTimer ---> $timeStr")
callingWindowManager.updateTimer(parseTime(timeStr))
}
}
}
override fun showUserInfo(userInfo: String?) {
userInfo?.let {
val userInfoMap = Gson().fromJson<Map<String, String>>(userInfo, Map::class.java)
currentUserInfo = convertMapToUserInfo(userInfoMap)
if (currentCallType == CALL_TYPE_VEHICLE_TEAM) {
return
}
syncCallViewStatus()
}
}
override fun toastMsg(msgId: Int) {
toast(msgId)
}
/**
* 展示Launcher浮窗
*/
private fun showReadyToCallView() {
Logger.d(TAG, "showCallingView callStatus : ${callChatManager.getStatusName()}")
if (!callChatManager.isCalling()) {
showLauncherCallingView()
}
}
@Synchronized
private fun showLauncherCallingView() {
Logger.d(TAG, "showLauncherCallingView : onlineCarPanelApi hidePanel")
serviceApi?.onlineCarPanelApi?.hidePanel()
if (!showCallWindowFromDefault) {
Logger.d(TAG, "showLauncherCallingView : 接听电话,隐藏用户信息窗口")
hideUserWindow()
}
callWindowShowCallBack()
if (callChatManager.isMatching()) {
callingWindowManager.showMatchingView()
} else {
callingWindowManager.showCallingView(currentUserInfo) {
if (callChatManager.isCalling()) {
toast(R.string.module_car_chat_call_hangup)
hangUp()
}
if (callChatManager.isReadyCalling() || callChatManager.isInit()) {
refuseCall()
}
}
}
}
@Synchronized
private fun hideLauncherCallingView() {
callWindowHideCallBack()
callingWindowManager.hideCallingView()
}
private fun callWindowShowCallBack() {
if (!callingWindowManager.isCallingWindowShow()) {
Logger.d(TAG, "callWindowStatusListener invoke true")
callWindowStatusListener?.invoke(true)
}
}
private fun callWindowHideCallBack() {
if (callingWindowManager.isCallingWindowShow()) {
Logger.d(TAG, "callWindowStatusListener invoke false")
callWindowStatusListener?.invoke(false)
}
}
override fun callTypeInit() {
refreshViewByType()
}
override fun call(userInfo: UserInfo?) {
Logger.d(TAG, "call===userInfo: $userInfo")
if (userInfo != null) {
currentUserInfo = userInfo
}
Logger.d(TAG, "call===currentUserInfo: $currentUserInfo")
connectSuccess()
}
override fun invokeCallData(userInfo: UserInfo?) {
if (userInfo != null) {
Logger.d(TAG, "invokeCallData===$userInfo")
currentUserInfo = userInfo
currentUserInfo?.let {
if (it.sn == null) {
toast(R.string.module_car_chat_error_call_test)
resetCallingView()
return
}
if (it.sn == MoGoAiCloudClientConfig.getInstance().sn) {
toast(R.string.module_car_chat_self_error)
resetCallingView()
return
}
}
}
Logger.d(TAG, "currentUserInfo: $currentUserInfo")
AnalyticsUtil.track(INVOKE_TRACK_REQUEST_CALL_SHOW)
startCall()
showReadyToCallView()
syncCallViewStatus()
}
private fun startCall() {
AnalyticsUtil.track(INVOKE_TRACK_REQUEST_CALL, mutableMapOf("type" to 1, "source" to 2))
if (currentUserInfo != null) {
callChatManager.callToSomeone(currentUserInfo!!.sn)
} else {
toast(R.string.module_car_chat_user_info_null)
}
}
private fun refreshViewByType() {
Logger.d(TAG, "refreshViewByType===${callChatManager.getStatusName()}")
when {
callChatManager.isReadyCalling() -> {
showReadyToCallView()
}
callChatManager.isCalling() || callChatManager.isMatching() -> {
syncCallViewStatus()
}
callChatManager.isInit() -> {
resetCallingView()
}
}
}
private fun resetCallingView() {
showCallWindowFromDefault = false
hideLauncherCallingView()
mapViewManager.resetMarkerStatus()
}
override fun callInvokeError(msg: String) {
TipToast.shortTip(msg)
resetCallingView()
}
override fun match(userInfo: UserInfo) {
Logger.d(TAG, "match===userInfo: $userInfo")
currentUserInfo = userInfo
connectSuccess()
}
override fun matchInvokeResult(invokeResult: Boolean, userInfo: UserInfo?, errorMsg: String) {
Logger.d(TAG, "matchInvokeResult===$invokeResult, msg: $errorMsg, callStatus: ${callChatManager.getStatusName()}")
if (invokeResult) {
// 开始匹配成功
currentUserInfo = userInfo
} else {
//匹配失败或匹配超时
TipToast.shortTip(errorMsg)
}
refreshViewByType()
}
/**
* 发起车队邀请 接口回调
*/
override fun vehicleTeamInviteResult(invite: Boolean) {
Logger.d(TAG, "vehicleTeamInviteResult===$invite")
if (invite) {
toast(R.string.module_car_chat_team_invited)
} else {
toast(R.string.module_car_chat_in_the_team)
speakContent(context!!.getString(R.string.module_car_chat_in_the_team))
}
}
/**
* 接收到车队邀请
*/
override fun receiverVehicleTeamInvitation(userInfo: UserInfo) {
Logger.d(TAG, "receiverVehicleTeamInvitation$userInfo")
teamInvitationWindowManager.showTeamInvitationView(userInfo)
}
/**
* 加入车队成功,进房,开始聊天
*/
override fun vehicleTeamEnterRoom() {
Logger.d(TAG, "vehicleTeamEnterRoom")
speakContent(context!!.getString(R.string.module_car_chat_joined_team))
callWindowShowCallBack()
callingWindowManager.showVehicleTeamView()
}
/**
* 同意加入车队 接口回调 调用成功,等待消息下发
*/
override fun joinVehicleTeamInvokeSuccess() {
Logger.d(TAG, " joinVehicleTeamInvokeSuccess")
}
/**
* 同意加入车队 接口回调 调用失败,处理 是否重新申请还是toast
*/
override fun joinVehicleTeamInvokeError(msg: String) {
Logger.d(TAG, " joinVehicleTeamInvokeError:$msg")
}
/**
* 车队成员变化
*/
override fun vehicleTeamMemberChange(teamMember: List<TeammateInfo>?) {
try {
Logger.d(TAG, "vehicleTeamMemberChange:" + teamMember?.size + ":" + teamMember.toString())
this.teamMember = teamMember
callingWindowManager.updateVehicleTeamHeadView(teamMember)
mapViewManager.updateTeamMarker(context!!, teamMember)
} catch (e: Exception) {
Logger.d(TAG, "vehicleTeamMemberChangeException:$e")
}
}
/**
* 挂断电话回调
*/
override fun callHangUp(hangUp: Boolean) {
Logger.d(TAG, "hangUp===$hangUp")
if (hangUp) {
resetCallingView()
} else {
toast(R.string.module_car_chat_hangup_fail)
}
}
/**
* 退出车队回调
*/
override fun vehicleTeamHangUp(hangUp: Boolean) {
Logger.d(TAG, "vehicleTeamHangUp===$hangUp")
if (hangUp) {
callWindowHideCallBack()
callingWindowManager.hideVehicleTeamView()
closeVehicleTeamFragment()
resetCallingView()
} else {
//接口调用退出车队 失败
}
}
/**
* 拒接电话回调
*/
override fun callRefuse(refuse: Boolean) {
Logger.d(TAG, "callRefuse===$refuse")
if (refuse) {
resetCallingView()
}
}
/**
* 拒绝加入车队回调
*/
override fun vehicleTeamRefuse(refuse: Boolean) {
Logger.d(TAG, "vehicleTeamRefuse===$refuse")
}
private fun hangUp() {
AnalyticsUtil.track(INVOKE_TRACK_REFUSE)
// resetCallingView()
callChatManager.hangUp(MoGoAiCloudClientConfig.getInstance().sn)
}
private fun refuseCall() {
AnalyticsUtil.track(INVOKE_TRACK_REFUSE)
toast(R.string.module_car_chat_call_hangup)
resetCallingView()
callChatManager.refuseCall(currentUserInfo?.sn ?: MoGoAiCloudClientConfig.getInstance().sn)
}
override fun showError(errInfo: String) {
resetCallingView()
}
private fun connectSuccess() {
teamInvitationWindowManager.hideTeamInvitationView()
AnalyticsUtil.track(INVOKE_TRACK_CHATTING)
mapViewManager.showLocationOnMap(
context!!,
serviceApi,
currentUserInfo?.lat?.toDouble(),
currentUserInfo?.lon?.toDouble(),
currentUserInfo?.userHead
)
refreshViewByType()
}
private fun toast(msgId: Int) {
context?.let {
TipToast.shortTip(it.resources.getString(msgId))
}
}
fun initVehicleTeamContainer(flag: String, containerId: Int, activity: FragmentActivity) {
this.flag = flag
this.containerId = containerId
this.activity = activity
}
/**
* 邀请加入车队
*/
fun inviteJoinVehicleTeam(sn: String) {
Logger.i(TAG, "isCalling():" + callChatManager.isCalling() + ",currentCallType:" + currentCallType)
if (callChatManager.isCalling() && currentCallType != CALL_TYPE_VEHICLE_TEAM) {
toast(R.string.module_car_chat_in_call)
return
}
callChatManager.inviteJoinVehicleTeam(sn)
}
/**
* 加入车队
*/
fun joinVehicleTeam(snSender: String) {
if (callChatManager.isReadyCalling()) {
refuseCall()
}
if (callChatManager.isMatching()) {
cancelMatch(false)
}
callChatManager.joinVehicleTeam(snSender)
}
/**
* 拒绝 来电消息/车队邀请
*/
fun refuseCall(snSender: String) {
callChatManager.refuseCall(snSender)
}
private fun onPause() {
Logger.i(TAG, "onPause currentCallType : $currentCallType , status: ${callChatManager.isCalling()}")
userWindowManager.hideUserView()
callWindowHideCallBack()
if (currentCallType == CALL_TYPE_VEHICLE_TEAM && callChatManager.isCalling()) {
closeVehicleTeamFragment()
callingWindowManager.hideVehicleTeamView()
} else {
callingWindowManager.hideCallingView()
}
callingWindowManager.resetStatus()
}
fun onDestroy() {
serviceApi?.statusManagerApi?.unregisterStatusChangedListener(
MODULE_NAME,
StatusDescriptor.MAIN_PAGE_RESUME,
statusChangedListener
)
serviceApi?.statusManagerApi?.unregisterStatusChangedListener(
MODULE_NAME,
StatusDescriptor.VR_MODE,
statusChangedListener
)
teamInvitationWindowManager.onDestroy()
flag = null
containerId = null
activity = null
context = null
}
}

View File

@@ -0,0 +1,323 @@
package com.mogo.module.carchatting.biz
import com.google.gson.Gson
import com.mogo.chat.base.BaseResponse
import com.mogo.chat.callcenter.*
import com.mogo.chat.callcenter.CallController.Companion.callController
import com.mogo.chat.callcenter.CallTypeManager.Companion.callTypeManager
import com.mogo.chat.constant.CALL_TYPE_MATCHING
import com.mogo.chat.constant.CALL_TYPE_VEHICLE_TEAM
import com.mogo.chat.constant.CALL_TYPE_VOICE
import com.mogo.chat.model.bean.TeammateInfo
import com.mogo.chat.util.CallTimer.Companion.callTimer
import com.mogo.chat.util.UserInfoHelper.currentCallType
import com.mogo.chat.util.UserInfoHelper.tmpSenderInfo
import com.mogo.chat.util.log
import com.mogo.module.carchatting.R
import com.mogo.module.carchatting.bean.EnthusiasmIndex
import com.mogo.module.carchatting.bean.Result
import com.mogo.module.carchatting.bean.UserInfo
import com.mogo.module.carchatting.bean.toUserInfo
import com.mogo.module.carchatting.net.Repository
import com.mogo.module.carchatting.net.request
import com.mogo.utils.logger.Logger
import com.zhidao.carchattingprovider.ICallResponse
class CallChatManager(iBizCallChat: IBizCallChat.IBizCallBack) : ICallResponse {
companion object {
const val TAG: String = "CallChatManager"
const val METHOD_FLAG = "CAR_CALL_TO_DEFAULT"
}
private val repository: Repository = Repository()
private var iBizCallBack: IBizCallChat.IBizCallBack? = iBizCallChat
init {
callController.addCallBack(TAG, object : ICallMessage {
override fun initStatus() {
super.initStatus()
iBizCallBack?.callTypeInit()
}
override fun callSuccess() {
super.callSuccess()
iBizCallBack?.invokeCallData(toUserInfo(tmpSenderInfo))
}
override fun receiverCallingAgree() {
super.receiverCallingAgree()
iBizCallBack?.call(toUserInfo(tmpSenderInfo))
}
override fun matchSuccess() {
super.matchSuccess()
iBizCallBack?.match(toUserInfo(tmpSenderInfo))
}
override fun receiverVehicleTeamInvitation() {
super.receiverVehicleTeamInvitation()
iBizCallBack?.receiverVehicleTeamInvitation(toUserInfo(tmpSenderInfo))
}
override fun vehicleTeamEnterRoom() {
super.vehicleTeamEnterRoom()
iBizCallBack?.vehicleTeamEnterRoom()
}
//接收到被动挂断电话/车队消息
override fun receiverHangUpInfo() {
super.receiverHangUpInfo()
when (currentCallType) {
CALL_TYPE_VOICE, CALL_TYPE_MATCHING -> {
iBizCallBack?.toastMsg(R.string.module_car_chat_call_hangup)
iBizCallBack?.callHangUp(true)
}
CALL_TYPE_VEHICLE_TEAM -> {
iBizCallBack?.toastMsg(R.string.module_car_chat_vehicle_dismiss)
iBizCallBack?.vehicleTeamHangUp(true)
}
}
}
override fun vehicleTeamMemberChange(teamMember: List<TeammateInfo>?) {
super.vehicleTeamMemberChange(teamMember)
iBizCallBack?.vehicleTeamMemberChange(teamMember)
}
override fun receiverCallRefuse() {
super.receiverCallRefuse()
iBizCallBack?.toastMsg(R.string.module_car_chat_call_refuse)
}
override fun receiverVehicleTeamInviteRefuse() {
super.receiverVehicleTeamInviteRefuse()
//车队暂时没有设计 邀请拒绝的文案说明
}
override fun receiverSomeoneExitVehicleTeam() {
super.receiverSomeoneExitVehicleTeam()
//收到某人退出车队通知
}
})
}
/******************************************** Invoke **********************************************/
fun isInit(): Boolean {
return callTypeManager.callStatus.isInit()
}
fun canCall(): Boolean {
return callTypeManager.callStatus.isInit() && SocketClientFactory.socketClient.isOpen
}
fun isReadyCalling(): Boolean {
return callTypeManager.callStatus.isReadyCalling()
}
fun isCalling(): Boolean {
return callTypeManager.callStatus.isCalling()
}
fun isMatching(): Boolean {
return callTypeManager.callStatus.isMatching()
}
fun isMatchFailed(): Boolean {
return callTypeManager.callStatus.isMatchFailed()
}
fun getStatusName(): String {
return callTypeManager.callStatus.name
}
fun syncCallTimer(timer: (Long) -> Unit) {
return callTimer.registerTimerRate({
timer.invoke(it)
}, {
log(TAG, "syncCallTimer timer is stop")
})
}
fun callToSomeone(sn: String) {
Logger.i(TAG, "callToSomeone")
IMCallManager.call(METHOD_FLAG, sn, this)
}
fun match() {
Logger.i(TAG, "match")
IMCallManager.match(METHOD_FLAG, this)
}
fun cancelMatch() {
Logger.i(TAG, "cancelMatch")
IMCallManager.cancelMatch(METHOD_FLAG, this)
}
/**
* 邀请加入车队
*/
fun inviteJoinVehicleTeam(sn: String) {
Logger.i(TAG, "inviteJoinVehicleTeam")
IMCallManager.inviteJoinVehicleTeam(METHOD_FLAG, sn, this)
}
/**
* 加入车队
*/
fun joinVehicleTeam(snSender: String) {
Logger.i(TAG, "joinVehicleTeam")
IMCallManager.joinVehicleTeam(METHOD_FLAG, snSender, this)
}
/**
* 挂断电话/车队聊天
*/
fun hangUp(sn: String?) {
Logger.i(TAG, "hangUp sn : $sn")
IMCallManager.hangUp(METHOD_FLAG, sn ?: "", this)
}
/**
* 拒绝电话/拒绝加入车队/未接通时挂断电话
*/
fun refuseCall(snSender: String) {
Logger.i(TAG, "refuse")
IMCallManager.refuseCall(METHOD_FLAG, snSender, this)
}
fun muteVoice(mute: Boolean) {
Logger.i(TAG, "muteVoice")
IMCallManager.mute(METHOD_FLAG, mute, this)
}
fun getUserInfoForCall() {
Logger.i(TAG, "getUserInfo")
IMCallManager.getUserInfoForCall(METHOD_FLAG, null, this)
}
fun getUserInfo(sn: String, onSuccess: (String) -> Unit, onError: (String) -> Unit) {
IMCallManager.getUserInfo(sn, onSuccess, onError)
}
fun getEnthusiasmIndex(
sn: String,
onSuccess: (EnthusiasmIndex) -> Unit,
onError: (String) -> Unit
) {
request<BaseResponse<Result>> {
loader {
getEnthIndex(sn)
}
onSuccess {
it.result.let { result ->
onSuccess.invoke(result.enthusiasmIndex)
}
}
onError {
it.message?.let { msg ->
onError.invoke(msg)
}
}
}
}
private suspend fun getEnthIndex(sn: String): BaseResponse<Result> {
val tmpMap = mapOf("sn" to sn)
val map = mapOf("data" to Gson().toJson(tmpMap))
Logger.d(TAG, "getEnthIndex : $map")
return repository.apiCall {
repository.getNetWorkApi().queryEnthusiasmIndex(map)
}
}
/******************************************** CallBack **********************************************/
override fun invokeCallData(invokeData: String) {
super.invokeCallData(invokeData)
if (!invokeData.isBlank()) {
val userInfo = Gson().fromJson(invokeData, UserInfo::class.java)
iBizCallBack?.invokeCallData(userInfo)
} else {
iBizCallBack?.invokeCallData(null)
}
}
override fun call(data: String) {
super.call(data)
Logger.i(TAG, "call data: $data")
if (!data.isBlank()) {
val userInfo = Gson().fromJson(data, UserInfo::class.java)
iBizCallBack?.call(userInfo)
} else {
iBizCallBack?.call(null)
}
}
override fun callInvokeError(msg: String) {
super.callInvokeError(msg)
Logger.i(TAG, "callInvokeError : $msg")
iBizCallBack?.callInvokeError(msg)
}
override fun match(data: String) {
super.match(data)
iBizCallBack?.match(Gson().fromJson(data, UserInfo::class.java))
}
override fun matchInvokeResult(invokeResult: Boolean, msg: String) {
super.matchInvokeResult(invokeResult, msg)
if (invokeResult) {
iBizCallBack?.matchInvokeResult(invokeResult, Gson().fromJson(msg, UserInfo::class.java), msg)
} else {
iBizCallBack?.matchInvokeResult(invokeResult, null, msg)
}
}
override fun hangUp(hangUp: Boolean, type: Int) {
super.hangUp(hangUp, type)
when (type) {
CALL_TYPE_VOICE, CALL_TYPE_MATCHING -> iBizCallBack?.callHangUp(hangUp)
CALL_TYPE_VEHICLE_TEAM -> iBizCallBack?.vehicleTeamHangUp(hangUp)
}
}
override fun refuse(refuse: Boolean, type: Int) {
super.refuse(refuse, type)
when (type) {
CALL_TYPE_VOICE, CALL_TYPE_MATCHING -> iBizCallBack?.callRefuse(refuse)
CALL_TYPE_VEHICLE_TEAM -> iBizCallBack?.vehicleTeamRefuse(refuse)
}
}
override fun inviteJoinVehicleTeam(status: Boolean) {
super.inviteJoinVehicleTeam(status)
iBizCallBack?.vehicleTeamInviteResult(status)
}
override fun joinVehicleTeamInvokeSuccess() {
super.joinVehicleTeamInvokeSuccess()
iBizCallBack?.joinVehicleTeamInvokeSuccess()
}
override fun joinVehicleTeamInvokeError(msg: String) {
super.joinVehicleTeamInvokeError(msg)
iBizCallBack?.joinVehicleTeamInvokeError(msg)
}
override fun error(errInfo: String) {
super.error(errInfo)
Logger.i(TAG, "errInfo --- $errInfo")
iBizCallBack?.showError(errInfo)
}
override fun userInfoCallBack(userInfo: String?, errorMsg: String?) {
super.userInfoCallBack(userInfo, errorMsg)
Logger.i(TAG, "userInfoCallBack : $userInfo")
iBizCallBack?.showUserInfo(userInfo)
}
}

View File

@@ -0,0 +1,49 @@
package com.mogo.module.carchatting.biz
import com.mogo.chat.model.bean.TeammateInfo
import com.mogo.module.carchatting.bean.UserInfo
interface IBizCallChat {
interface IBizCallBack {
fun callTypeInit()
fun call(userInfo: UserInfo?)
fun invokeCallData(userInfo: UserInfo?)
fun callInvokeError(msg: String)
fun match(userInfo: UserInfo)
fun matchInvokeResult(invokeResult: Boolean, userInfo: UserInfo?, errorMsg: String)
fun vehicleTeamInviteResult(invite: Boolean)
fun receiverVehicleTeamInvitation(userInfo: UserInfo)
fun vehicleTeamEnterRoom()
fun joinVehicleTeamInvokeSuccess()
fun joinVehicleTeamInvokeError(msg: String)
fun vehicleTeamMemberChange(teamMember: List<TeammateInfo>?)
fun callHangUp(hangUp: Boolean)
fun vehicleTeamHangUp(hangUp: Boolean)
fun callRefuse(refuse: Boolean)
fun vehicleTeamRefuse(refuse: Boolean)
fun showError(errInfo: String)
fun showUserInfo(userInfo: String?)
fun toastMsg(msgId:Int)
}
}

View File

@@ -0,0 +1,226 @@
package com.mogo.module.carchatting.biz
import com.google.gson.Gson
import com.mogo.chat.callcenter.CallTypeManager.Companion.callTypeManager
import com.mogo.chat.callcenter.IMType
import com.mogo.chat.callcenter.SocketClientFactory
import com.mogo.chat.callcenter.isInit
import com.mogo.chat.common.gme.GMEApi
import com.mogo.chat.constant.OFFLINE_STATUS
import com.mogo.chat.constant.ONLINE_STATUS
import com.mogo.chat.service.ChatServiceHandler
import com.mogo.chat.service.InvokeDataProxy
import com.mogo.chat.util.UserInfoHelper
import com.mogo.chat.util.UserInfoHelper.currentCallType
import com.mogo.chat.util.UserInfoHelper.tmpSenderInfo
import com.mogo.chat.util.log
import com.mogo.module.carchatting.card.CallChatConstant.Companion.PARAM_SN
import com.zhidao.carchattingprovider.ICallProviderResponse
import com.zhidao.carchattingprovider.ICallResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class IMCallManager private constructor() {
companion object {
const val TAG = "IMCallManager"
fun canCall(flag: String, response: ICallProviderResponse) {
log(TAG, "是否能拨打电话 flag --->: $flag")
response.canCall(callTypeManager.callStatus.isInit() && SocketClientFactory.socketClient.isOpen)
}
fun call(
flag: String,
snReceiver: String,
response: ICallResponse
) {
log(TAG, "拨打电话 sn --->: $snReceiver , flag: $flag")
if (callTypeManager.callStatus.isInit() && SocketClientFactory.socketClient.isOpen) {
log(TAG, "flag:$flag 拨打电话 sn : $snReceiver")
ChatServiceHandler.call(snReceiver, {
log(TAG, "call somebody success,wait for answer")
}, { exception ->
response.callInvokeError(exception.message!!)
})
} else {
log(TAG, "拨打电话出现异常 能否拨打 :${callTypeManager.callStatus.isInit()} " +
"长连状态:${SocketClientFactory.socketClient.isOpen}")
response.error("call: 正在通话中,请稍后再试")
}
}
fun match(
flag: String,
response: ICallResponse
) {
log(TAG, "开始匹配 ---> flag: $flag ")
ChatServiceHandler.startMatch(flag, {
if (callTypeManager.callStatus != IMType.CALLING_MATCH) {
callTypeManager.callStatus = IMType.MATCHING
}
val userInfo = InvokeDataProxy.convertSnsToInvokeMap(UserInfoHelper.userInfo)
response.matchInvokeResult(true, Gson().toJson(userInfo).toString())
}, { exception ->
callTypeManager.callStatus = IMType.INIT_CALL
log(TAG, "startMatch exception : ${exception.message}")
response.matchInvokeResult(false, "匹配失败")
}, {
callTypeManager.callStatus = IMType.INIT_CALL
response.matchInvokeResult(false, "匹配超时")
})
}
fun cancelMatch(flag: String, response: ICallResponse) {
log(TAG, "取消匹配 ---> flag: $flag ")
ChatServiceHandler.cancelMatch {
response.cancelMatch(true)
}
callTypeManager.callStatus = IMType.INIT_CALL
}
fun inviteJoinVehicleTeam(flag: String, inviteSn: String, response: ICallResponse) {
log(TAG, "邀请加入车队 --->flag: $flag , inviteSn: $inviteSn")
ChatServiceHandler.inviteJoinVehicleTeam(inviteSn, {
response.inviteJoinVehicleTeam(true)
}, { exception ->
log(TAG, "inviteJoinVehicleTeam error : ${exception.message}")
response.inviteJoinVehicleTeam(false)
})
}
fun joinVehicleTeam(flag: String, snSender: String, response: ICallResponse) {
log(TAG, "同意加入车队 flag: $flag")
ChatServiceHandler.answer(snSender, 0, {
response.joinVehicleTeamInvokeSuccess()
}, { exception ->
log(TAG, "inviteJoinVehicleTeam error : ${exception.message}")
response.joinVehicleTeamInvokeError(exception.message ?: "joinVehicleTeam error")
})
}
fun hangUp(
flag: String,
snReceiver: String,
response: ICallResponse
) {
log(TAG, "挂断电话 ---> flag: $flag , snReceiver : $snReceiver")
ChatServiceHandler.hangUp(snReceiver, {
response.hangUp(true, currentCallType)
}, { exception ->
log(TAG, "hangUp error : ${exception.message}")
response.hangUp(false, currentCallType)
})
}
fun refuseCall(flag: String,
snSender: String,
response: ICallResponse) {
log(TAG, "拒绝电话 / 取消打电话 ---> flag: $flag")
ChatServiceHandler.refuseCall(snSender, {
response.refuse(true, currentCallType)
}, { exception ->
log(TAG, "refuseCall error : ${exception.message}")
response.refuse(false, currentCallType)
})
}
fun mute(flag: String, mute: Boolean, response: ICallResponse) {
log(TAG, "静音 ---> flag: $flag , mute $mute")
try {
GlobalScope.launch(Dispatchers.Main) {
if (mute) {
GMEApi.muteMic()
} else {
GMEApi.reMuteMic()
}
}
response.mute(true)
} catch (e: Exception) {
e.printStackTrace()
response.error("mute : ${e.message!!}")
}
}
fun addFriend(flag: String, sn: String, response: ICallResponse) {
log(TAG, "添加好友 ---> flag: $flag")
val params = hashMapOf(PARAM_SN to sn)
ChatServiceHandler.addFriend(params["PARAM_SN"].toString(), {
response.addFriend(true)
}, { exception ->
response.error("addFriend: ${exception.message!!}")
})
}
fun isFriend(flag: String, sn: String, response: ICallResponse) {
log(TAG, "是否是好友 ---> flag: $flag sn: $sn")
val params = hashMapOf(PARAM_SN to sn)
ChatServiceHandler.isFriend(params["PARAM_SN"].toString(), { isFriend: Boolean ->
response.isFriend(isFriend)
}, { exception ->
response.error("isFriend: ${exception.message!!}")
})
}
fun invisibleUser(
flag: String,
visible: Boolean,
response: ICallResponse
) {
log(TAG, "用户隐身 ---> flag: $flag , visible : $visible")
val status: Int = if (visible) {
ONLINE_STATUS
} else {
OFFLINE_STATUS
}
ChatServiceHandler.invisibleUser(status, {
response.invisibleUser(true)
}, { exception ->
response.error("invisibleUser: ${exception.message}")
})
}
fun isOnLine(
flag: String,
sn: String,
response: ICallResponse
) {
log(TAG, "车机是否在线 ---> flag: $flag , sn : $sn")
ChatServiceHandler.isOnLine(sn, { onLine: Boolean ->
response.isOnLine(onLine)
}, { exception ->
response.error("isOnLine: ${exception.message}")
})
}
fun getUserInfoForCall(
flag: String,
sn: String?,
response: ICallResponse
) {
val tmpSn = sn ?: ""
log(TAG, "获取用户信息 ---> flag: $flag , sn : $sn")
if (tmpSn.isBlank()) {
val userInfo = InvokeDataProxy.convertSnsToInvokeMap(tmpSenderInfo)
response.userInfoCallBack(Gson().toJson(userInfo))
return
}
ChatServiceHandler.queryUserInfo(tmpSn, { userInfo ->
response.userInfoCallBack(userInfo)
}, { exception ->
response.error("getUserInfo: ${exception.message}")
})
}
fun getUserInfo(sn: String, onSuccess: (String) -> Unit, onError: (String) -> Unit) {
ChatServiceHandler.queryUserInfo(sn, { userInfo ->
onSuccess.invoke(userInfo)
}, { exception ->
onError.invoke(exception.message ?: "查询用户信息失败")
})
}
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.module.carchatting.card
class CallChatConstant {
companion object {
const val PATH = "/carchatting/ui"
const val PROVIDER = "/callchat/ui"
const val MODULE_NAME = "CARD_TYPE_CARS_CHATTING"
const val ACTION_ERROR = 0
const val ACTION_CALL = 1
const val ACTION_MATCH = 2
const val ACTION_HANG_UP = 3
const val ACTION_MUTE = 4
const val ACTION_CAN_CALL = 5
const val ACTION_ADD_FRIEND = 6
const val ACTION_IS_FRIEND = 7
const val ACTION_CALL_STATUS = 8
const val ACTION_INVISIBLE_USER = 9
const val ACTION_ENTER_PERSONAL_CENTER = 10
const val ACTION_CANCEL_MATCH = 11
const val ACTION_IS_ONLINE = 12
const val ACTION_GET_USER_INFO = 13
const val PARAM_SN = "PARAM_SN"
const val PARAM_MUTE = "PARAM_MUTE"
const val PARAM_VISIBLE = "PARAM_VISIBLE"
const val PARAM_END_TYPE = "PARAM_END_TYPE"
}
}

View File

@@ -0,0 +1,71 @@
package com.mogo.module.carchatting.card
import android.content.Context
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.map.listener.IMogoMapListener
import com.mogo.map.location.IMogoLocationListener
import com.mogo.map.marker.IMogoMarkerClickListener
import com.mogo.map.navi.IMogoNaviListener
import com.mogo.module.carchatting.biz.CallChatCenter.Companion.callChatCenter
import com.mogo.module.common.ModuleType
import com.mogo.service.module.IMogoModuleLifecycle
import com.mogo.service.module.IMogoModuleProvider
@Route(path = CallChatConstant.PROVIDER)
class CallChatProvider : IMogoModuleProvider {
override fun createFragment(context: Context?, data: Bundle?): Fragment? {
return null
}
override fun createView(context: Context?): View? {
return null
}
override fun getNaviListener(): IMogoNaviListener? {
return null
}
override fun getLocationListener(): IMogoLocationListener? {
return null
}
override fun getMarkerClickListener(): IMogoMarkerClickListener? {
return null
}
override fun getModuleName(): String {
return CallChatConstant.MODULE_NAME
}
override fun getType(): Int {
return ModuleType.TYPE_SERVICE
}
override fun init(context: Context) {
callChatCenter.init(context)
}
override fun getCardLifecycle(): IMogoModuleLifecycle? {
return null
}
override fun getMapListener(): IMogoMapListener? {
return null
}
override fun getAppPackage(): String? {
return null
}
override fun getAppName(): String? {
return null
}
override fun onDestroy() {
callChatCenter.onDestroy()
}
}

View File

@@ -0,0 +1,107 @@
package com.mogo.module.carchatting.invoke
import android.content.Context
import androidx.fragment.app.FragmentActivity
import com.alibaba.android.arouter.facade.annotation.Route
import com.zhidao.carchattingprovider.CallChattingProviderConstant.Companion.CAR_CALL_PROVIDER
import com.zhidao.carchattingprovider.ICallProviderResponse
import com.zhidao.carchattingprovider.ICarsChattingProvider
import com.zhidao.carchattingprovider.MogoDriverInfo
@Route(path = CAR_CALL_PROVIDER)
class CarsChattingProvider : ICarsChattingProvider {
override fun addFriend(
flag: String,
context: Context,
friendSn: String,
response: ICallProviderResponse
) {
CarsChattingProviderImpl.addFriend(
flag,
context,
friendSn,
response
)
}
override fun call(params: Map<String, String>) {
CarsChattingProviderImpl.call(params)
}
override fun canCall(flag: String, context: Context, response: ICallProviderResponse) {
CarsChattingProviderImpl.canCall(flag, context, response)
}
override fun getUserInfo(
flag: String,
context: Context,
sn: String,
response: ICallProviderResponse
) {
CarsChattingProviderImpl.getUserInfo(flag, context, sn, response)
}
override fun isOnLine(
flag: String,
context: Context,
sn: String,
response: ICallProviderResponse
) {
CarsChattingProviderImpl.isOnLine(flag, context, sn, response)
}
override fun init(context: Context?) {
}
override fun initVehicleTeamContainer(flag: String, containerId: Int, activity: FragmentActivity) {
CarsChattingProviderImpl.initVehicleTeamContainer(flag, containerId, activity)
}
override fun isFriend(
flag: String,
context: Context,
friendSn: String,
response: ICallProviderResponse
) {
CarsChattingProviderImpl.isFriend(
flag,
context,
friendSn,
response
)
}
override fun registerCallWindowStatusListener(flag: String, context: Context, response: ICallProviderResponse) {
CarsChattingProviderImpl.registerCallWindowStatusListener(flag, context, response)
}
override fun unRegisterCallWindowStatusListener(flag: String, context: Context) {
CarsChattingProviderImpl.unRegisterCallWindowStatusListener(flag, context)
}
override fun registerUserWindowStatusListener(flag: String, context: Context, response: ICallProviderResponse) {
CarsChattingProviderImpl.registerUserWindowStatusListener(flag, context, response)
}
override fun unRegisterUserWindowStatusListener(flag: String, context: Context) {
CarsChattingProviderImpl.unRegisterUserWindowStatusListener(flag, context)
}
override fun showUserWindow(flag: String, mogoDriverInfo: MogoDriverInfo, context: Context) {
CarsChattingProviderImpl.showUserWindow(flag, mogoDriverInfo, context)
}
override fun hideUserWindow(flag: String, context: Context, response: ICallProviderResponse?) {
CarsChattingProviderImpl.hideUserWindow(flag, context, response)
}
override fun invisibleUser(flag: String, context: Context, response: ICallProviderResponse) {
CarsChattingProviderImpl.invisibleUser(
flag,
context,
response
)
}
}

View File

@@ -0,0 +1,131 @@
package com.mogo.module.carchatting.invoke
import android.content.Context
import androidx.fragment.app.FragmentActivity
import com.google.gson.Gson
import com.mogo.module.carchatting.biz.CallChatCenter.Companion.callChatCenter
import com.mogo.module.carchatting.biz.IMCallManager
import com.mogo.utils.logger.Logger
import com.zhidao.carchattingprovider.ICallProviderResponse
import com.zhidao.carchattingprovider.ICarsChattingProvider
import com.zhidao.carchattingprovider.MogoDriverInfo
object CarsChattingProviderImpl : ICarsChattingProvider {
const val TAG = "CarsChattingProviderImpl"
private var callWindowStatusResponse: ICallProviderResponse? = null
private var userWindowStatusResponse: ICallProviderResponse? = null
private var hideUserWindowStatusResponse: ICallProviderResponse? = null
override fun init(context: Context?) {
}
override fun initVehicleTeamContainer(flag: String, containerId: Int, activity: FragmentActivity) {
callChatCenter.initVehicleTeamContainer(flag, containerId, activity)
}
override fun addFriend(
flag: String,
context: Context,
friendSn: String,
response: ICallProviderResponse
) {
IMCallManager.addFriend(
flag,
friendSn,
response
)
}
override fun isFriend(
flag: String,
context: Context,
friendSn: String,
response: ICallProviderResponse
) {
IMCallManager.isFriend(
flag,
friendSn,
response
)
}
override fun canCall(flag: String, context: Context, response: ICallProviderResponse) {
IMCallManager.canCall(flag, response)
}
override fun getUserInfo(
flag: String,
context: Context,
sn: String,
response: ICallProviderResponse
) {
IMCallManager.getUserInfoForCall(flag, sn, response)
}
override fun isOnLine(
flag: String,
context: Context,
sn: String,
response: ICallProviderResponse
) {
IMCallManager.isOnLine(flag, sn, response)
}
override fun call(params: Map<String, String>) {
callChatCenter.invokeCall(Gson().toJson(params))
}
override fun invisibleUser(flag: String, context: Context, response: ICallProviderResponse) {
IMCallManager.invisibleUser(
flag,
true,
response
)
}
override fun registerCallWindowStatusListener(flag: String, context: Context, response: ICallProviderResponse) {
Logger.d(TAG, "registerCallWindowStatusListener flag: $flag")
callWindowStatusResponse = response
callChatCenter.registerCallWindowStatusListener {
callWindowStatusResponse?.callWindowStatus(it)
}
}
override fun unRegisterCallWindowStatusListener(flag: String, context: Context) {
Logger.d(TAG, "unRegisterCallWindowStatusListener flag: $flag")
callWindowStatusResponse = null
callChatCenter.unRegisterCallWindowStatusListener()
}
override fun registerUserWindowStatusListener(flag: String, context: Context, response: ICallProviderResponse) {
Logger.d(TAG, "registerUserWindowStatusListener flag: $flag")
userWindowStatusResponse = response
callChatCenter.registerUserWindowStatusListener {
userWindowStatusResponse?.userWindowStatus(it)
}
}
override fun unRegisterUserWindowStatusListener(flag: String, context: Context) {
Logger.d(TAG, "unRegisterUserWindowStatusListener flag: $flag")
userWindowStatusResponse = null
callChatCenter.unRegisterUserWindowStatusListener()
}
override fun showUserWindow(flag: String, mogoDriverInfo: MogoDriverInfo, context: Context) {
Logger.d(TAG, "showUserWindow flag: $flag , mogoDriverInfo : $mogoDriverInfo")
callChatCenter.showUserWindow(mogoDriverInfo)
}
override fun hideUserWindow(flag: String, context: Context, response: ICallProviderResponse?) {
Logger.d(TAG, "hideUserWindow flag: $flag")
hideUserWindowStatusResponse = response
callChatCenter.hideUserWindow({ errorMsg ->
hideUserWindowStatusResponse?.hideUserWindowError(errorMsg)
}, {
hideUserWindowStatusResponse = null
})
}
}

View File

@@ -0,0 +1,92 @@
package com.mogo.module.carchatting.net
import androidx.lifecycle.LifecycleOwner
import com.mogo.chat.base.BaseResponse
import com.mogo.chat.exception.ApiException
import com.mogo.chat.exception.ApiException.Companion.NULL_REQUEST_DATA_API_EXCEPTION
import com.mogo.chat.exception.CommonException.Companion.NETWORK_EXCEPTION
import com.mogo.chat.exception.CommonException.Companion.NULL_EXCEPTION
import kotlinx.coroutines.*
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.concurrent.TimeoutException
class Request<T> {
private lateinit var loader: suspend () -> T
private var start: (() -> Unit)? = null
private var onSuccess: ((T) -> Unit)? = null
private var onError: ((java.lang.Exception) -> Unit)? = null
private var onComplete: (() -> Unit)? = null
private var addLifecycle: LifecycleOwner? = null
infix fun loader(loader: suspend () -> T) {
this.loader = loader
}
infix fun start(start: (() -> Unit)?) {
this.start = start
}
infix fun onSuccess(onSuccess: ((T) -> Unit)?) {
this.onSuccess = onSuccess
}
infix fun onError(onError: ((java.lang.Exception) -> Unit)?) {
this.onError = onError
}
infix fun onComplete(onComplete: (() -> Unit)?) {
this.onComplete = onComplete
}
infix fun addLifecycle(addLifecycle: LifecycleOwner?) {
this.addLifecycle = addLifecycle
}
fun request() {
GlobalScope.launch(context = Dispatchers.Main) {
start?.invoke()
try {
val deferred = GlobalScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
loader()
}
val result = deferred.await()
if (result != null && result is BaseResponse<*>) {
if (result.code == 0) {
onSuccess?.invoke(result)
} else {
throw ApiException(result.code, result.msg)
}
} else {
throw NULL_REQUEST_DATA_API_EXCEPTION
}
} catch (e: Exception) {
e.printStackTrace()
//数据打点
if (e == null) {
onError?.invoke(NULL_EXCEPTION)
}
when (e) {
is UnknownHostException -> onError?.invoke(NETWORK_EXCEPTION)
is TimeoutException -> onError?.invoke(NETWORK_EXCEPTION)
is SocketTimeoutException -> onError?.invoke(NETWORK_EXCEPTION)
else -> onError?.invoke(java.lang.Exception(e.message))
}
} finally {
onComplete?.invoke()
}
}
}
}
inline fun <T> request(buildRequest: Request<T>.() -> Unit) {
Request<T>().apply(buildRequest).request()
}

View File

@@ -0,0 +1,21 @@
package com.mogo.module.carchatting.net
import com.mogo.chat.base.BaseResponse
import com.mogo.chat.model.bean.TeammateInfo
import com.mogo.module.carchatting.bean.Result
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
interface HttpApi {
//获取热心指数
@FormUrlEncoded
@POST("/deva/poiInfoFabulous/car/poi/no/queryEnthusiasmIndex/v1")
suspend fun queryEnthusiasmIndex(@FieldMap status: Map<String, String>): BaseResponse<Result>
//查询车队成员
@FormUrlEncoded
@POST("/yycp-chat-service/car/chat/no/queryTeamDate/v1")
suspend fun queryTeamDate(@FieldMap date: Map<String, String>): BaseResponse<List<TeammateInfo>>
}

View File

@@ -0,0 +1,23 @@
package com.mogo.module.carchatting.net
import com.mogo.commons.debug.DebugConfig
class HttpConstant {
companion object {
const val HOST_DEV = "http://dzt-test.zhidaohulian.com"
const val HOST_TEST = "http://dzt-test.zhidaohulian.com"
const val HOST_DEMO = "http://dzt-show.zhidaohulian.com"
const val HOST_PRODUCT = "https://dzt.zhidaohulian.com"
fun getNetHost(): String {
return when (DebugConfig.getNetMode()) {
DebugConfig.NET_MODE_DEV -> HOST_DEV
DebugConfig.NET_MODE_QA -> HOST_TEST
DebugConfig.NET_MODE_DEMO -> HOST_DEMO
else -> HOST_PRODUCT
}
}
}
}

View File

@@ -0,0 +1,22 @@
package com.mogo.module.carchatting.net
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.chat.base.BaseResponse
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.service.IMogoServiceApis
open class Repository {
suspend fun <T : Any> apiCall(call: suspend () -> BaseResponse<T>): BaseResponse<T> {
return call.invoke()
}
fun getNetWorkApi(): HttpApi {
var serviceApi: IMogoServiceApis? = null
val mogoService = ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS).navigation()
if (mogoService is IMogoServiceApis) {
serviceApi = mogoService
}
return serviceApi!!.networkApi.createNoCallAdapter(HttpApi::class.java, HttpConstant.getNetHost())
}
}

View File

@@ -0,0 +1,33 @@
package com.mogo.module.carchatting.util
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.service.IMogoServiceApis
import com.mogo.service.analytics.IMogoAnalytics
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
object AnalyticsUtil {
private var trackRouter: IMogoAnalytics? = null
fun track(eventType: String, data: MutableMap<String, Any>? = hashMapOf()) {
if (trackRouter == null) {
val aRouter = ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS).navigation()
if (aRouter is IMogoServiceApis) {
trackRouter = aRouter.analyticsApi
}
}
trackRouter!!.track(eventType, data)
}
}

View File

@@ -0,0 +1,16 @@
package com.mogo.module.carchatting.util
const val SPACE_TIME = 1500
var lastViewClickTime = 0L
var viewId: Int = 0
fun isDoubleClick(view: Int): Boolean {
val time = System.currentTimeMillis()
val timeD = time - lastViewClickTime
if (timeD < SPACE_TIME && viewId == view) {
return true
}
lastViewClickTime = time
viewId = view
return false
}

View File

@@ -0,0 +1,12 @@
package com.mogo.module.carchatting.util
import com.mogo.chat.util.log
class LogUtil {
companion object {
fun i(tag: String, msg: String) {
log(tag, msg)
}
}
}

View File

@@ -0,0 +1,9 @@
package com.mogo.module.carchatting.util
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
fun <T> taskMainLaunch(job: () -> T) = GlobalScope.launch(Dispatchers.Main) {
job()
}

View File

@@ -0,0 +1,57 @@
package com.mogo.module.carchatting.util
import com.mogo.module.carchatting.bean.UserInfo
import com.zhidao.carchattingprovider.MogoDriverInfo
import java.text.SimpleDateFormat
import java.util.*
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)
}
fun convertMapToUserInfo(map: Map<String, String>): UserInfo {
val sn = map["sn"].toString()
val nickName = map["userName"] ?: ""
val headImgUrl = map["userHead"] ?: ""
val carInfo = map["carTypeName"] ?: ""
val cardIdSex = map["gender"] ?: ""
val cityName = map["location"] ?: ""
val cardIdAge = map["age"] ?: ""
val lat = map["lat"].toString()
val lon = map["lon"].toString()
return UserInfo(sn, 0, nickName, headImgUrl, cardIdSex, cardIdAge, carInfo, cityName, lat, lon)
}
fun convertMapToDriverInfo(map: Map<String, String>): MogoDriverInfo {
val mogoDriverInfo = MogoDriverInfo()
val age = map["age"]
if (!age.isNullOrBlank()) {
mogoDriverInfo.age = age.toInt()
}
val sn = map["sn"]
if (!sn.isNullOrBlank() && "null" != sn) {
mogoDriverInfo.sn = sn
}
mogoDriverInfo.carTypeName = map["carTypeName"] ?: ""
mogoDriverInfo.gender = map["gender"] ?: ""
mogoDriverInfo.locationInfo = map["location"] ?: ""
mogoDriverInfo.userHead = map["userHead"] ?: ""
val userName = map["userName"]
if (!userName.isNullOrBlank()) {
mogoDriverInfo.userName = userName
} else {
mogoDriverInfo.userName = "蘑菇用户"
}
val lat = map["lat"]
lat?.let {
mogoDriverInfo.lat = it.toDouble()
}
val lon = map["lon"]
lon?.let {
mogoDriverInfo.lon = it.toDouble()
}
return mogoDriverInfo
}

View File

@@ -0,0 +1,105 @@
package com.mogo.module.carchatting.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.mogo.module.carchatting.R;
import com.mogo.module.common.utils.CarSeries;
import java.util.ArrayList;
public
/*
* @author congtaowang
* @since 2020/12/1
*
* 描述
*/
class CallUserView {
private CallUserView(){
}
private static ArrayList<Bitmap> sBitmaps;
public synchronized static ArrayList<Bitmap> inflateCallingBitmap(Context context) {
if (!CarSeries.isF8xxSeries()) {
return null;
}
if (sBitmaps != null && !sBitmaps.isEmpty()) {
return sBitmaps;
}
sBitmaps = new ArrayList<>();
for (int i = 0; i < calling.length; i++) {
sBitmaps.add(inflateView(context, i));
}
return sBitmaps;
}
private static final int[] calling = {
R.mipmap.module_callchat_green_cycle_00020,
// R.mipmap.module_callchat_green_cycle_00021,
// R.mipmap.module_callchat_green_cycle_00022,
R.mipmap.module_callchat_green_cycle_00023,
// R.mipmap.module_callchat_green_cycle_00024,
// R.mipmap.module_callchat_green_cycle_00025,
R.mipmap.module_callchat_green_cycle_00026,
// R.mipmap.module_callchat_green_cycle_00027,
// R.mipmap.module_callchat_green_cycle_00028,
R.mipmap.module_callchat_green_cycle_00029,
// R.mipmap.module_callchat_green_cycle_00030,
// R.mipmap.module_callchat_green_cycle_00031,
R.mipmap.module_callchat_green_cycle_00032,
// R.mipmap.module_callchat_green_cycle_00033,
// R.mipmap.module_callchat_green_cycle_00034,
R.mipmap.module_callchat_green_cycle_00035,
// R.mipmap.module_callchat_green_cycle_00036,
// R.mipmap.module_callchat_green_cycle_00037,
R.mipmap.module_callchat_green_cycle_00038,
// R.mipmap.module_callchat_green_cycle_00039,
// R.mipmap.module_callchat_green_cycle_00040,
R.mipmap.module_callchat_green_cycle_00041,
// R.mipmap.module_callchat_green_cycle_00042,
// R.mipmap.module_callchat_green_cycle_00043,
R.mipmap.module_callchat_green_cycle_00044,
// R.mipmap.module_callchat_green_cycle_00045,
// R.mipmap.module_callchat_green_cycle_00046,
R.mipmap.module_callchat_green_cycle_00047,
R.mipmap.module_callchat_green_cycle_00048
};
public static Bitmap inflateView(Context context, int index) {
View view = LayoutInflater.from(context).inflate(R.layout.module_car_chatting_calling_user_view, null);
view.setBackgroundResource(calling[index]);
return fromView(view);
}
private static Bitmap fromView(View view) {
view.setDrawingCacheEnabled(true);
processChildView(view);
view.destroyDrawingCache();
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
Bitmap bitmap;
return (bitmap = view.getDrawingCache()) != null ? bitmap.copy(Bitmap.Config.ARGB_8888, false) : null;
}
private static void processChildView(View view) {
if (!(view instanceof ViewGroup)) {
if (view instanceof TextView) {
((TextView) view).setHorizontallyScrolling(false);
}
} else {
for (int var1 = 0; var1 < ((ViewGroup) view).getChildCount(); ++var1) {
processChildView(((ViewGroup) view).getChildAt(var1));
}
}
}
}

View File

@@ -0,0 +1,410 @@
package com.mogo.module.carchatting.view
import android.annotation.SuppressLint
import android.content.Context
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.bumptech.glide.request.RequestOptions
import com.mogo.chat.constant.TAG
import com.mogo.chat.model.bean.TeammateInfo
import com.mogo.commons.debug.DebugConfig
import com.mogo.commons.debug.DebugConfig.CAR_MACHINE_TYPE_BYD
import com.mogo.commons.debug.DebugConfig.CAR_MACHINE_TYPE_SELF_INNOVATE
import com.mogo.module.carchatting.R
import com.mogo.module.carchatting.bean.UserInfo
import com.mogo.module.carchatting.biz.CallChatCenter
import com.mogo.module.carchatting.view.VrModeHelper.Companion.vrModeHelper
import com.mogo.module.common.MogoApisHandler
import com.mogo.module.common.glide.SkinAbleBitmapTarget
import com.mogo.service.IMogoServiceApis
import com.mogo.utils.glide.GlideApp
import com.mogo.utils.glide.GlideRoundedCornersTransform
import com.mogo.utils.logger.Logger
import de.hdodenhof.circleimageview.CircleImageView
class CallingWindowManager private constructor() {
companion object {
val callingWindowManager by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
CallingWindowManager()
}
}
private var serviceApi: IMogoServiceApis? = null
@Volatile
private var isLauncherCallingViewShown = false
private var launcherCallingView: View? = null
private var matchView: ConstraintLayout? = null
private var matchingView: ConstraintLayout? = null
private var callingView: ConstraintLayout? = null
private var cancelMatch: TextView? = null
private var callingHead: ImageView? = null
private var callingNickName: TextView? = null
private var callingTimer: TextView? = null
private var callingHangup: ImageView? = null
private var mContext: Context? = null
private var moduleCarchattingTeamLlHead: LinearLayout? = null
private var moduleCarchattingTeamNum: TextView? = null
private var moduleCarchattingCivHead0: CircleImageView? = null
private var moduleCarchattingCivHead1: CircleImageView? = null
private var moduleCarchattingCivHead2: CircleImageView? = null
private var moduleCarchattingCivHead3: CircleImageView? = null
private var moduleCarchattingCivHead4: CircleImageView? = null
private var moduleCarchattingTeamRlView: ConstraintLayout? = null
private var moduleCarchattingTeamTvInfo: TextView? = null
private var moduleCarchattingTeamIvQuit: ImageView? = null
private var match: (() -> Unit)? = null
private var canceledMatch: (() -> Unit)? = null
private var showVehicleTeamView: (() -> Unit)? = null
private var quitVehicleTeam: (() -> Unit)? = null
private var moveToCenter: (() -> Unit)? = null
@SuppressLint("InflateParams")
fun init(context: Context, serviceApi: IMogoServiceApis,
match: () -> Unit,
cancelMatch: () -> Unit,
showVehicleTeamView: () -> Unit,
quitVehicleTeam: () -> Unit,
moveToCenter: () -> Unit) {
Logger.d(CallChatCenter.TAG, "CallingWindowManager init --->")
this.mContext = context
this.serviceApi = serviceApi
this.match = match
this.canceledMatch = cancelMatch
this.showVehicleTeamView = showVehicleTeamView
this.quitVehicleTeam = quitVehicleTeam
this.moveToCenter = moveToCenter
if (DebugConfig.getCarMachineType() == CAR_MACHINE_TYPE_BYD) {
Logger.d(CallChatCenter.TAG, "CallingWindowManager 展示匹配 addLeftFeatureView--->")
initView()
serviceApi.entranceButtonController?.addLeftFeatureView(launcherCallingView)
}
}
private fun initView() {
Logger.d(CallChatCenter.TAG, "CallingWindowManager initView --->")
launcherCallingView = vrModeHelper.getCallingWindowLayoutView(mContext!!)
launcherCallingView?.setOnClickListener { }
launcherCallingView?.let { view ->
matchView = view.findViewById(R.id.module_carchatting_rl_match_init_view)
matchingView = view.findViewById(R.id.module_carchatting_rl_matching_view)
callingView = view.findViewById(R.id.module_carchatting_rl_call_view)
cancelMatch = view.findViewById(R.id.module_carchatting_tv_cancel_match)
callingHead = view.findViewById(R.id.module_carchatting_call_head)
callingHangup = view.findViewById(R.id.module_carchatting_call_hangUp)
callingNickName = view.findViewById(R.id.module_carchatting_call_nickname)
callingTimer = view.findViewById(R.id.module_carchatting_call_time)
moduleCarchattingTeamLlHead = view.findViewById(R.id.module_carchatting_team_ll_head)
moduleCarchattingTeamNum = view.findViewById(R.id.module_carchatting_team_num);
moduleCarchattingCivHead0 = view.findViewById(R.id.module_carchatting_civ_head0)
moduleCarchattingCivHead1 = view.findViewById(R.id.module_carchatting_civ_head1)
moduleCarchattingCivHead2 = view.findViewById(R.id.module_carchatting_civ_head2)
moduleCarchattingCivHead3 = view.findViewById(R.id.module_carchatting_civ_head3)
moduleCarchattingCivHead4 = view.findViewById(R.id.module_carchatting_civ_head4)
moduleCarchattingTeamRlView = view.findViewById(R.id.module_carchatting_team_rl_view)
moduleCarchattingTeamTvInfo = view.findViewById(R.id.module_carchatting_team_tv_info)
moduleCarchattingTeamIvQuit = view.findViewById(R.id.module_carchatting_iv_team_quit)
if (DebugConfig.getCarMachineType() == CAR_MACHINE_TYPE_BYD) {
matchView?.visibility = View.VISIBLE
}
matchView?.setOnClickListener {
match?.invoke()
matchView?.visibility = View.GONE
matchingView?.visibility = View.VISIBLE
}
cancelMatch?.setOnClickListener {
canceledMatch?.invoke()
matchingView?.visibility = View.GONE
matchView?.visibility = View.VISIBLE
}
moduleCarchattingTeamRlView?.setOnClickListener {
showVehicleTeamView?.invoke()
}
moduleCarchattingTeamIvQuit?.setOnClickListener {
quitVehicleTeam?.invoke()
}
callingHead?.setOnClickListener {
moveToCenter?.invoke()
}
}
}
private fun showCallingWindow() {
if (isLauncherCallingViewShown) {
return
}
if (DebugConfig.getCarMachineType() != CAR_MACHINE_TYPE_BYD) {
serviceApi?.windowManagerApi?.addView(launcherCallingView, vrModeHelper.getCallingWindowLayoutPosition(mContext!!), true)
} else {
serviceApi?.entranceButtonController?.addLeftFeatureView(launcherCallingView)
}
}
fun isCallingWindowShow(): Boolean {
return isLauncherCallingViewShown
}
fun showInitView() {
if (DebugConfig.getCarMachineType() != CAR_MACHINE_TYPE_BYD) {
Logger.d(CallChatCenter.TAG, "showInitView Mogo车机不执行")
return
}
if (launcherCallingView == null) {
Logger.d(CallChatCenter.TAG, "展示Launcher浮窗 showInitView")
initView()
showCallingWindow()
Logger.d(CallChatCenter.TAG, "添加浮窗成功 showInitView")
}
}
fun showMatchingView() {
if (launcherCallingView == null) {
Logger.d(CallChatCenter.TAG, "展示Launcher浮窗 showMatchingView")
initView()
matchView?.visibility = View.GONE
moduleCarchattingTeamRlView?.visibility = View.GONE
callingView?.visibility = View.GONE
matchingView?.visibility = View.VISIBLE
showCallingWindow()
Logger.d(CallChatCenter.TAG, "添加浮窗成功 showMatchingView")
}
}
fun showCallingView(
currentUserInfo: UserInfo?,
hangUpCallBack: () -> Unit
) {
Logger.d(CallChatCenter.TAG, "展示Launcher浮窗 showCallingView===$isLauncherCallingViewShown")
if (!isLauncherCallingViewShown) {
addCallWindowView(hangUpCallBack) {
refreshCallWindowData(currentUserInfo)
}
} else {
Logger.d(CallChatCenter.TAG, "刷新Launcher浮窗数据 showCallingView===currentUserInfo :$currentUserInfo")
refreshCallWindowData(currentUserInfo)
}
}
@SuppressLint("InflateParams")
private inline fun addCallWindowView(
crossinline hangUpCallBack: () -> Unit,
refreshData: (() -> Unit)
) {
if (launcherCallingView == null) {
initView()
}
matchView?.visibility = View.GONE
matchingView?.visibility = View.GONE
moduleCarchattingTeamRlView?.visibility = View.GONE
callingView?.visibility = View.VISIBLE
callingTimer?.text = mContext?.resources?.getString(R.string.module_car_chat_matching_wait)
callingHangup?.setOnClickListener {
hangUpCallBack.invoke()
}
showCallingWindow()
refreshData.invoke()
isLauncherCallingViewShown = true
Logger.d(CallChatCenter.TAG, "添加浮窗成功 showCallingView===$isLauncherCallingViewShown")
}
private fun refreshCallWindowData(currentUserInfo: UserInfo?) {
mContext?.let {
if (MogoApisHandler.getInstance().apis.statusManagerApi.isVrMode) {
GlideApp.with(it).load(currentUserInfo?.userHead)
.apply(RequestOptions.bitmapTransform(
GlideRoundedCornersTransform(20f, GlideRoundedCornersTransform.CornerType.LEFT)))
.placeholder(R.mipmap.module_carchatting_hawk_eye_default_head_img).into(callingHead!!)
} else {
val options: RequestOptions =
RequestOptions()
.placeholder(R.mipmap.module_carchatting_default_head_img)
.error(R.mipmap.module_carchatting_default_head_img)
GlideApp.with(it).asBitmap().load(currentUserInfo?.userHead)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop()
.into(SkinAbleBitmapTarget(callingHead, options))
}
}
// callingNickName?.text = currentUserInfo?.userName ?: "蘑菇车主"
callingNickName?.text = "云平台" //todo 需要产品后面更改逻辑,避免写死造成困扰
}
fun hideCallingView() {
Logger.d(CallChatCenter.TAG, "隐藏Launcher浮窗===$isLauncherCallingViewShown")
if (isLauncherCallingViewShown) {
callingTimer?.text = null
callingNickName?.text = null
isLauncherCallingViewShown = false
}
callingTimer?.text = mContext?.resources?.getString(R.string.module_car_chat_matching_wait)
callingView?.visibility = View.GONE
matchingView?.visibility = View.GONE
if (DebugConfig.getCarMachineType() == CAR_MACHINE_TYPE_BYD) {
matchView?.visibility = View.VISIBLE
}
removeView()
}
private fun removeView() {
if (DebugConfig.getCarMachineType() != CAR_MACHINE_TYPE_BYD) {
serviceApi?.windowManagerApi?.removeView(launcherCallingView)
}
}
fun updateTimer(timeStr: String) {
Logger.d(CallChatCenter.TAG, "CallingWindowManager updateView===$timeStr")
callingTimer?.text = timeStr
}
fun showVehicleTeamView() {
Logger.d(CallChatCenter.TAG, "showVehicleTeamView isLauncherCallingViewShown ===$isLauncherCallingViewShown")
if (!isLauncherCallingViewShown) {
if (launcherCallingView == null) {
initView()
}
callingView?.visibility = View.GONE
matchingView?.visibility = View.GONE
if (DebugConfig.getCarMachineType() == CAR_MACHINE_TYPE_BYD) {
matchView?.visibility = View.GONE
}
moduleCarchattingTeamRlView?.visibility = View.VISIBLE
showCallingWindow()
isLauncherCallingViewShown = true
}
}
fun hideVehicleTeamView() {
Logger.d(CallChatCenter.TAG, "hideVehicleTeamView isLauncherCallingViewShown ===$isLauncherCallingViewShown")
callingView?.visibility = View.GONE
matchingView?.visibility = View.GONE
moduleCarchattingTeamRlView?.visibility = View.GONE
if (DebugConfig.getCarMachineType() == CAR_MACHINE_TYPE_BYD) {
matchView?.visibility = View.VISIBLE
}
if (isLauncherCallingViewShown) {
isLauncherCallingViewShown = false
}
removeView()
}
fun updateVehicleTeamHeadView(teamMember: List<TeammateInfo>?) {
val vrMode = MogoApisHandler.getInstance().apis.statusManagerApi.isVrMode
if (vrMode) {
teamMember?.let {
if (it.size > 1) {
moduleCarchattingTeamLlHead?.visibility = View.VISIBLE
moduleCarchattingTeamNum?.visibility = View.VISIBLE
moduleCarchattingTeamNum?.text = "${teamMember?.size}"
} else {
moduleCarchattingTeamLlHead?.visibility = View.INVISIBLE
}
}
return
}
teamMember?.let {
if (it.size > 1) {
moduleCarchattingTeamLlHead?.visibility = View.VISIBLE
when (it.size) {
2 -> {
moduleCarchattingCivHead0?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[0].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead0!!)
moduleCarchattingCivHead1?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[1].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead1!!)
moduleCarchattingCivHead2?.visibility = View.GONE
moduleCarchattingCivHead3?.visibility = View.GONE
moduleCarchattingCivHead4?.visibility = View.GONE
val layoutParams: ConstraintLayout.LayoutParams = moduleCarchattingTeamLlHead?.layoutParams as ConstraintLayout.LayoutParams
layoutParams.bottomMargin = 0
moduleCarchattingTeamLlHead?.layoutParams = layoutParams
}
3 -> {
moduleCarchattingCivHead0?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[0].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead0!!)
moduleCarchattingCivHead1?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[1].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead1!!)
moduleCarchattingCivHead2?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[2].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead2!!)
moduleCarchattingCivHead3?.visibility = View.GONE
moduleCarchattingCivHead4?.visibility = View.GONE
val layoutParams: ConstraintLayout.LayoutParams = moduleCarchattingTeamLlHead?.layoutParams as ConstraintLayout.LayoutParams
layoutParams.bottomMargin = 0
moduleCarchattingTeamLlHead?.layoutParams = layoutParams
}
4 -> {
moduleCarchattingCivHead0?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[0].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead0!!)
moduleCarchattingCivHead1?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[1].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead1!!)
moduleCarchattingCivHead2?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[2].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead2!!)
moduleCarchattingCivHead3?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[3].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead3!!)
moduleCarchattingCivHead4?.visibility = View.INVISIBLE
val layoutParams: ConstraintLayout.LayoutParams = moduleCarchattingTeamLlHead?.layoutParams as ConstraintLayout.LayoutParams
val margin = mContext!!.resources.getDimension(R.dimen.module_call_chat_team_head_view_top_margin)
layoutParams.bottomMargin = margin.toInt()
moduleCarchattingTeamLlHead?.layoutParams = layoutParams
}
else -> {
moduleCarchattingCivHead0?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[0].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead0!!)
moduleCarchattingCivHead1?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[1].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead1!!)
moduleCarchattingCivHead2?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[2].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead2!!)
moduleCarchattingCivHead3?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[3].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead3!!)
moduleCarchattingCivHead4?.visibility = View.VISIBLE
GlideApp.with(mContext!!).load(teamMember[4].headImgUrl)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop().into(moduleCarchattingCivHead4!!)
val layoutParams: ConstraintLayout.LayoutParams = moduleCarchattingTeamLlHead?.layoutParams as ConstraintLayout.LayoutParams
val margin = mContext!!.resources.getDimension(R.dimen.module_call_chat_team_head_view_top_margin)
layoutParams.bottomMargin = margin.toInt()
moduleCarchattingTeamLlHead?.layoutParams = layoutParams
}
}
} else {
moduleCarchattingTeamLlHead?.visibility = View.INVISIBLE
}
}
}
fun resetStatus() {
isLauncherCallingViewShown = false
serviceApi?.entranceButtonController?.removeLeftFeatureView(launcherCallingView)
launcherCallingView = null
}
fun resetVRModeView() {
launcherCallingView?.let {
removeView()
}
launcherCallingView = null
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.module.carchatting.view
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
class DrawableTextView : androidx.appcompat.widget.AppCompatTextView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
override fun onDraw(canvas: Canvas) {
val drawable = compoundDrawables
val leftDrawable = drawable[0]
if (leftDrawable != null) {
val leftDrawableWidth = leftDrawable.intrinsicWidth
val drawablePadding = compoundDrawablePadding
val textWidth = paint.measureText(text.toString().trim())
val bodyWidth = leftDrawableWidth + drawablePadding + textWidth
canvas.save()
canvas.translate((width - bodyWidth) / 2, 0f)
}
super.onDraw(canvas)
}
}

View File

@@ -0,0 +1,33 @@
package com.mogo.module.carchatting.view
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
/**
* IM通讯能力 Window 正常模式和鹰眼模式 页面切换接口
*/
interface IMogoCallChatWindowModeChange {
/**
* 获取将要更新的聊天窗口 parentView
*/
fun getCallingWindowLayoutView(context: Context): View
/**
* 获取将要更新聊天窗口View的大小位置参数
*/
fun getCallingWindowLayoutPosition(context: Context): FrameLayout.LayoutParams
/**
* 获取将要更新车队邀请的 parentView
*/
fun getTeamInvitationWindowLayoutView(context: Context): View
/**
* 获取将要更新车队邀请View的大小位置参数
*/
fun getTeamInvitationWindowLayoutPosition(context: Context): ViewGroup.MarginLayoutParams
}

View File

@@ -0,0 +1,144 @@
package com.mogo.module.carchatting.view
import android.content.Context
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.widget.ImageView
import com.mogo.chat.model.bean.TeammateInfo
import com.mogo.chat.provider.ServiceApi
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.map.MogoLatLng
import com.mogo.map.marker.MogoMarkerOptions
import com.mogo.module.carchatting.R
import com.mogo.module.common.ModuleNames
import com.mogo.module.common.MogoApisHandler
import com.mogo.module.common.entity.MarkerLocation
import com.mogo.module.common.entity.MarkerShowEntity
import com.mogo.service.IMogoServiceApis
import com.mogo.utils.logger.Logger
import com.zhidao.carchattingprovider.CallChattingProviderConstant
class MapViewManager private constructor() {
companion object {
// 正在组队通话的marker标识
const val CALLING = "CallingUser"
const val TAG = "MapViewManager"
val mapViewManager by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
MapViewManager()
}
}
private var hasMarked = false //是否已经在地图上标记
fun updateTeamMarker(context: Context, teamMember: List<TeammateInfo>?) {
//延时一分钟后获取大而全数据
// MogoApisHandler.getInstance().apis.statusManagerApi?.setUserInteractionStatus(
// CallChattingProviderConstant.CAR_CALL_PROVIDER, true, true)
//// val bitmaps = CallUserView.inflateCallingBitmap(context)
// ServiceApi.mapService()?.getMarkerManager(context)?.removeMarkers(CALLING)
// val imageView = ImageView(context)
// imageView.setImageDrawable(context.resources.getDrawable(R.mipmap.module_carchatting_caller))
// teamMember?.forEach {
// //打点传入type为在线车辆
// val options = MogoMarkerOptions().icon(imageView).latitude(it.lat).longitude(it.lon)
// ServiceApi.mapService()?.getMarkerManager(context)?.addMarker(CALLING, options)?.setClickable(false)
//
// }
}
fun showLocationOnMap(
context: Context,
serviceApi: IMogoServiceApis?,
lat: Double?,
lon: Double?,
userHead: String?
) {
if (!hasMarked) {
hasMarked = true
showLocation(context, serviceApi, lat, lon, userHead)
}
}
private fun showLocation(
context: Context,
serviceApi: IMogoServiceApis?,
lat: Double?,
lon: Double?,
userHead: String?
) {
//延时一分钟后获取大而全数据
serviceApi?.statusManagerApi?.setUserInteractionStatus(
CallChattingProviderConstant.CAR_CALL_PROVIDER,
true,
true
)
//清除地图上所有Marker
serviceApi?.mapServiceApi?.getMarkerManager(context)?.removeMarkers()
//获取需要打点的经纬度
val userLoc =
serviceApi?.mapServiceApi?.getSingletonLocationClient(context)?.lastKnowLocation
val latlonList = mutableListOf<MogoLatLng>()
var currentLatLng: MogoLatLng? = null
userLoc?.let {
currentLatLng = MogoLatLng(it.latitude, it.longitude)
}
latlonList.add(MogoLatLng(lat ?: 0.0, lon ?: 0.0))
//打点传入type为在线车辆 并缩小地图区域
getMarkerEntity(context, lat, lon, userHead) { _: String, options: MogoMarkerOptions ->
val mapMarker =
serviceApi?.mapServiceApi?.getMarkerManager(context)?.addMarker(CALLING, options)
mapMarker?.setClickable(false)
}
val mapUIController = serviceApi?.mapServiceApi?.mapUIController
mapUIController?.showBounds(
CallChattingProviderConstant.CAR_CALL_PROVIDER,
currentLatLng,
latlonList,
Rect(
context.resources.getDimension(R.dimen.dp_736).toInt(),
context.resources.getDimension(R.dimen.dp_198).toInt(),
context.resources.getDimension(R.dimen.dp_59).toInt(),
context.resources.getDimension(R.dimen.dp_59).toInt()
),
true
)
}
@Suppress("DEPRECATION")
private fun getMarkerEntity(
context: Context,
lat: Double?,
lon: Double?,
userHead: String?,
callBack: (markerType: String, options: MogoMarkerOptions) -> Unit
) {
val markerEntity = MarkerShowEntity()
val markerLoc = MarkerLocation()
markerLoc.lat = lat ?: 0.0
markerLoc.lon = lon ?: 0.0
with(markerEntity) {
isHighlighted = true
isChecked = false
markerType = ModuleNames.CARD_TYPE_USER_DATA
iconUrl = userHead
markerLocation = markerLoc
}
val options = MogoMarkerOptions()
.`object`(markerEntity)
.icon((context.resources.getDrawable(R.mipmap.module_carchatting_caller) as BitmapDrawable).bitmap)
.latitude(markerEntity.markerLocation.lat)
.longitude(markerEntity.markerLocation.lon)
callBack.invoke(markerEntity.markerType, options)
}
fun resetMarkerStatus() {
Logger.d(TAG, "resetMarkerStatus")
hasMarked = false
ServiceApi.mapService()?.getMarkerManager(AbsMogoApplication.getApp().applicationContext)?.removeMarkers(CALLING)
}
}

View File

@@ -0,0 +1,139 @@
package com.mogo.module.carchatting.view
import android.annotation.SuppressLint
import android.content.Context
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.mogo.module.carchatting.R
import com.mogo.module.carchatting.bean.UserInfo
import com.mogo.module.carchatting.biz.CallChatCenter
import com.mogo.module.carchatting.biz.CallChatCenter.Companion.callChatCenter
import com.mogo.module.carchatting.view.VrModeHelper.Companion.vrModeHelper
import com.mogo.module.carchatting.voice.IVoiceCommandListener
import com.mogo.module.carchatting.voice.VoiceUtil
import com.mogo.module.common.MogoApisHandler
import com.mogo.service.IMogoServiceApis
import com.mogo.service.windowview.IMogoTopViewStatusListener
import com.mogo.utils.logger.Logger
import kotlinx.coroutines.*
/**
* created by wujifei on 2020/11/17 20:18
* describe:邀请加入车队窗口管理
*/
class TeamInvitationWindowManager private constructor() {
private var mApis: IMogoServiceApis? = null
private var mContext: Context? = null
private var teamInvitationView: View? = null
private var moduleCarchattingTvContent: TextView? = null
private var moduleCarchattingIvJoin: ImageView? = null
private var moduleCarchattingIvRefuse: ImageView? = null
private var userInfo: UserInfo? = null
companion object {
val teamInvitationWindowManager by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
TeamInvitationWindowManager()
}
}
@SuppressLint("InflateParams")
fun init(context: Context) {
mApis = MogoApisHandler.getInstance().apis
this.mContext = context
}
private fun initView() {
teamInvitationView = vrModeHelper.getTeamInvitationWindowLayoutView(mContext!!)
teamInvitationView?.let { view ->
moduleCarchattingTvContent = view.findViewById(R.id.module_carchatting_tv_content)
moduleCarchattingIvJoin = view.findViewById(R.id.module_carchatting_iv_join)
moduleCarchattingIvRefuse = view.findViewById(R.id.module_carchatting_iv_refuse)
moduleCarchattingIvJoin?.setOnClickListener {
userInfo?.let {
callChatCenter.joinVehicleTeam(it.sn)
}
hideTeamInvitationView()
}
moduleCarchattingIvRefuse?.setOnClickListener {
userInfo?.let {
callChatCenter.refuseCall(it.sn)
}
hideTeamInvitationView()
}
}
}
fun showTeamInvitationView(userInfo: UserInfo?) {
this.userInfo = userInfo
// if (teamInvitationView == null) {
initView()
// }
userInfo?.let {
moduleCarchattingTvContent?.text = String.format(mContext!!.getString(R.string.module_car_chat_invitation_window), it.userName)
}
mContext?.let {
mApis?.topViewManager?.addView(teamInvitationView, vrModeHelper.getTeamInvitationWindowLayoutPosition(it), topViewStatusListener)
}
}
fun hideTeamInvitationView() {
mApis?.topViewManager?.removeView(teamInvitationView)
}
private var topViewStatusListener = object : IMogoTopViewStatusListener {
private var job: Job? = null
override fun onViewAdded(view: View?) {
VoiceUtil.speak(mContext!!.getString(R.string.module_car_chat_team_invited_prompt), mContext!!, mVoiceCommandListener)
try {
job = GlobalScope.launch(Dispatchers.Main) {
delay(1000 * 20)
Logger.d(CallChatCenter.TAG, "refuseCall()--->executed")
callChatCenter.refuseCall(userInfo!!.sn)
hideTeamInvitationView()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onViewRemoved(view: View?) {
job?.cancel()
VoiceUtil.unregisterJoinTeam(mContext!!)
}
override fun beforeViewRemoveAnim(view: View?) {
}
override fun beforeViewAddAnim(view: View?) {
}
}
private var mVoiceCommandListener = object : IVoiceCommandListener {
override fun onVoiceJoinTeam() {
super.onVoiceJoinTeam()
callChatCenter.joinVehicleTeam(userInfo!!.sn)
hideTeamInvitationView()
}
override fun onVoiceRefuseJoinTeam() {
super.onVoiceRefuseJoinTeam()
callChatCenter.refuseCall(userInfo!!.sn)
hideTeamInvitationView()
}
override fun onSpeakEnd(speakText: String?) {
super.onSpeakEnd(speakText)
VoiceUtil.registerJoinTeam(mContext!!, this)
}
}
fun onDestroy() {
teamInvitationView = null
userInfo = null
mApis = null
mContext = null
}
}

View File

@@ -0,0 +1,18 @@
package com.mogo.module.carchatting.view
import android.widget.TextView
fun TextView.setLeftDrawable(resId: Int) {
if(resId!=0) {
val drawable = resources.getDrawable(resId)
drawable.setBounds(0, 0, drawable.minimumWidth, drawable.minimumHeight)
setCompoundDrawables(
drawable,
null,
null,
null
)
}else{
setCompoundDrawables(null, null, null, null)
}
}

View File

@@ -0,0 +1,29 @@
package com.mogo.module.carchatting.view;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
public
/**
* @author congtaowang
* @since 2020/12/1
*
* 描述
*/
class UnSupportSkinFrameLayout extends FrameLayout {
public UnSupportSkinFrameLayout( Context context ) {
this( context, null );
}
public UnSupportSkinFrameLayout( Context context, @Nullable AttributeSet attrs ) {
this( context, attrs, 0 );
}
public UnSupportSkinFrameLayout( Context context, @Nullable AttributeSet attrs, int defStyleAttr ) {
super( context, attrs, defStyleAttr );
}
}

View File

@@ -0,0 +1,29 @@
package com.mogo.module.carchatting.view;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
public
/**
* @author congtaowang
* @since 2020/12/1
*
* 描述
*/
class UnSupportSkinImageView extends AppCompatImageView {
public UnSupportSkinImageView( Context context ) {
this( context, null );
}
public UnSupportSkinImageView( Context context, @Nullable AttributeSet attrs ) {
this( context, attrs, 0 );
}
public UnSupportSkinImageView( Context context, @Nullable AttributeSet attrs, int defStyleAttr ) {
super( context, attrs, defStyleAttr );
}
}

View File

@@ -0,0 +1,161 @@
package com.mogo.module.carchatting.view
import android.content.Context
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.request.RequestOptions
import com.google.gson.Gson
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.AbsMogoApplication
import com.mogo.module.carchatting.R
import com.mogo.module.carchatting.bean.EnthusiasmIndex
import com.mogo.module.carchatting.bean.UserInfo
import com.mogo.module.carchatting.biz.CallChatCenter
import com.mogo.module.carchatting.biz.CallChatCenter.Companion.callChatCenter
import com.mogo.module.carchatting.voice.IVoiceCommandListener
import com.mogo.module.carchatting.voice.VoiceUtil
import com.mogo.module.common.dialog.BaseFloatDialog
import com.mogo.module.common.glide.SkinAbleBitmapTarget
import com.mogo.module.common.view.CustomRatingBar
import com.mogo.utils.glide.GlideApp
import com.mogo.utils.glide.GlideRoundedCornersTransform
import com.mogo.utils.logger.Logger
import com.zhidao.carchattingprovider.MogoDriverInfo
fun toUserInfo(driverInfo: MogoDriverInfo): UserInfo {
return UserInfo(
driverInfo.sn,
0,
driverInfo.userName,
driverInfo.userHead,
driverInfo.gender,
driverInfo.age.toString(),
driverInfo.carTypeName,
driverInfo.locationInfo,
driverInfo.lat.toString(),
driverInfo.lon.toString()
)
}
class UserDialog : BaseFloatDialog {
private var moduleCallChatBg: ImageView? = null
private var moduleCallChatUserClose: ImageView? = null
private var moduleCallChatUserHead: ImageView? = null
private var moduleCallChatUserNickName: TextView? = null
private var moduleCallChatRatingBar: CustomRatingBar? = null
private var moduleCallChatUserCaller: TextView? = null
private var moduleCallChatUserJoinTeam: DrawableTextView? = null
private var driverInfo: MogoDriverInfo? = null
constructor(mogoDriverInfo: MogoDriverInfo, context: Context) : super(context) {
driverInfo = mogoDriverInfo
Logger.i(CallChatCenter.TAG, "构造函数赋值 driverInfo$driverInfo")
initView()
}
private fun initView() {
val lp = window!!.attributes
lp.width = context.resources.getDimension(R.dimen.module_call_chat_user_state_x).toInt()
lp.height = context.resources.getDimension(R.dimen.module_call_chat_user_state_y).toInt()
window!!.attributes = lp
setContentView(R.layout.module_car_chatting_launcher_user_view)
moduleCallChatBg = findViewById(R.id.moduleCallChatBg)
moduleCallChatUserClose = findViewById(R.id.moduleCallChatUserClose)
moduleCallChatUserHead = findViewById(R.id.moduleCallChatUserHead)
moduleCallChatUserNickName = findViewById(R.id.moduleCallChatUserNickName)
moduleCallChatUserCaller = findViewById(R.id.moduleCallChatUserCaller)
moduleCallChatUserJoinTeam = findViewById(R.id.moduleCallChatUserJoinTeam)
moduleCallChatRatingBar = findViewById(R.id.moduleCallChatRatingBar)
moduleCallChatUserClose?.setOnClickListener {
dismiss()
}
moduleCallChatUserCaller?.setOnClickListener {
callChatCenter.invokeCall(Gson().toJson(toUserInfo(driverInfo!!)))
dismiss()
}
moduleCallChatUserJoinTeam?.setOnClickListener {
callChatCenter.inviteJoinVehicleTeam(driverInfo!!.sn)
dismiss()
}
if (moduleCallChatRatingBar!!.visibility == View.VISIBLE) {
moduleCallChatRatingBar!!.visibility = View.GONE
}
Logger.i(CallChatCenter.TAG, "initView driverInfo sn === ${driverInfo?.sn}")
val sn = driverInfo?.sn
sn?.let {
if (it != MoGoAiCloudClientConfig.getInstance().sn) {
callChatCenter.updateUserCallStatus { status ->
Logger.i(CallChatCenter.TAG, "刷新用户信息打电话状态=== $status")
if (status) {
moduleCallChatUserCaller?.visibility = View.VISIBLE
}
}
callChatCenter.getUserEnthusiasmIndex(it)
callChatCenter.getUserInfo(it)
}
}
refreshUserWindowData()
}
private fun refreshUserWindowData() {
Logger.i(CallChatCenter.TAG, "refreshUserWindowData")
val options: RequestOptions =
RequestOptions().circleCrop()
.placeholder(R.mipmap.module_carchatting_default_head_img)
.error(R.mipmap.module_carchatting_default_head_img)
GlideApp.with(AbsMogoApplication.getApp().applicationContext).asBitmap().load(driverInfo?.userHead)
.placeholder(R.mipmap.module_carchatting_default_head_img).circleCrop()
.into(SkinAbleBitmapTarget(moduleCallChatUserHead, options))
moduleCallChatUserNickName?.text = driverInfo?.userName ?: "蘑菇车主"
val transform = GlideRoundedCornersTransform(
context.resources.getDimension(R.dimen.dp_45),
GlideRoundedCornersTransform.CornerType.ALL
)
GlideApp.with(AbsMogoApplication.getApp().applicationContext).load(driverInfo?.userHead).apply(
RequestOptions.bitmapTransform(transform)
).into(moduleCallChatBg!!)
}
fun updateUserEnthusiasmIndex(enthusiasmIndex: EnthusiasmIndex) {
moduleCallChatRatingBar?.visibility = View.VISIBLE
val enthIndex = enthusiasmIndex.enthusiasmIndex
moduleCallChatRatingBar?.setRating(enthIndex.toFloat())
}
fun updateUserInfo(mogoDriverInfo: MogoDriverInfo?) {
Logger.i(CallChatCenter.TAG, "updateUserInfo 赋值前 mogoDriverInfo: $mogoDriverInfo")
mogoDriverInfo?.let {
driverInfo?.let { driverInfo ->
if (driverInfo.userHead.isNullOrBlank()) {
driverInfo.userHead = it.userHead
}
if (driverInfo.userName.isNullOrBlank()) {
driverInfo.userName = it.userName
}
}
}
Logger.i(CallChatCenter.TAG, "updateUserInfo 赋值后 driverInfo : $driverInfo 准备更新用户信息")
refreshUserWindowData()
}
override fun show() {
super.show()
VoiceUtil.registerInviteJoinTeam(context, mVoiceCommandListener)
}
override fun dismiss() {
super.dismiss()
VoiceUtil.unregisterInviteJoinTeam(context)
}
private var mVoiceCommandListener = object : IVoiceCommandListener {
override fun onVoiceInviteJoinTeam() {
super.onVoiceInviteJoinTeam()
callChatCenter.inviteJoinVehicleTeam(driverInfo!!.sn)
dismiss()
}
}
}

View File

@@ -0,0 +1,76 @@
package com.mogo.module.carchatting.view
import com.mogo.commons.context.ContextHolderUtil
import com.mogo.module.carchatting.bean.EnthusiasmIndex
import com.mogo.module.carchatting.biz.CallChatCenter
import com.mogo.utils.logger.Logger
import com.zhidao.carchattingprovider.MogoDriverInfo
class UserWindowManager private constructor() {
companion object {
val userWindowManager by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
UserWindowManager()
}
}
private var userDialog: UserDialog? = null
private var userWindowListener: ((Boolean) -> Unit)? = null
fun isCallingWindowShow(): Boolean {
return userDialog != null && userDialog!!.isShowing
}
fun showUserView(
mogoDriverInfo: MogoDriverInfo,
userWindowListener: ((Boolean) -> Unit)
) {
Logger.d(CallChatCenter.TAG, "展示用户信息浮窗===mogoDriverInfo : $mogoDriverInfo")
this.userWindowListener = userWindowListener
val context = ContextHolderUtil.getContext()
context?.let {
if (userDialog == null) {
userDialog = UserDialog(mogoDriverInfo, it)
userDialog!!.show()
userDialog!!.setOnDismissListener {
Logger.d(CallChatCenter.TAG, "主动隐藏用户信息浮窗===")
userDialog = null
this.userWindowListener?.invoke(false)
this.userWindowListener = null
}
this.userWindowListener?.invoke(true)
Logger.d(CallChatCenter.TAG, "添加用户信息浮窗成功===")
} else {
Logger.d(CallChatCenter.TAG, "刷新用户信息浮窗数据===mogoDriverInfo :$mogoDriverInfo")
updateUserInfo(mogoDriverInfo)
}
}
}
fun updateUserEnthusiasmIndex(enthusiasmIndex: EnthusiasmIndex) {
Logger.d(CallChatCenter.TAG, "刷新用户热心指数=== $enthusiasmIndex")
userDialog?.let {
if(it.isShowing){
userDialog?.updateUserEnthusiasmIndex(enthusiasmIndex)
}
}
}
fun updateUserInfo(mogoDriverInfo: MogoDriverInfo) {
Logger.d(CallChatCenter.TAG, "刷新用户信息=== $mogoDriverInfo")
userDialog?.let {
if(it.isShowing){
userDialog?.updateUserInfo(mogoDriverInfo)
}
}
}
fun hideUserView() {
Logger.d(CallChatCenter.TAG, "隐藏用户信息浮窗===")
userDialog?.dismiss()
userWindowListener?.invoke(false)
userWindowListener = null
userDialog = null
}
}

View File

@@ -0,0 +1,87 @@
package com.mogo.module.carchatting.view;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.mogo.chat.model.bean.TeammateInfo;
import com.mogo.commons.mvp.MvpFragment;
import com.mogo.eagle.core.data.map.MogoLatLng;
import com.mogo.module.carchatting.R;
import com.mogo.module.carchatting.adapter.VehicleTeamAdapter;
import com.mogo.module.carchatting.biz.CallChatCenter;
import com.mogo.module.common.MogoApisHandler;
import java.util.List;
/**
* created by wujifei on 2020/11/16 19:43
* describe:车队信息
*/
public class VehicleTeamFragment extends MvpFragment<VehicleTeamView, VehicleTeamPresenter> implements VehicleTeamView {
private TextView tvNum;
private ImageView ivClose;
private RecyclerView rvTeammates;
private VehicleTeamAdapter vehicleTeamAdapter;
public static VehicleTeamFragment newInstance() {
Bundle args = new Bundle();
VehicleTeamFragment fragment = new VehicleTeamFragment();
fragment.setArguments(args);
return fragment;
}
@Override
protected int getLayoutId() {
return R.layout.module_car_chatting_fragment_team_view;
}
@Override
protected void initViews() {
tvNum = findViewById(R.id.tv_num);
ivClose = findViewById(R.id.iv_close);
ivClose.setOnClickListener(view -> CallChatCenter.Companion.getCallChatCenter().closeVehicleTeamFragment());
rvTeammates = findViewById(R.id.rv_teammates);
rvTeammates.setLayoutManager(new LinearLayoutManager(getContext()));
}
@NonNull
@Override
protected VehicleTeamPresenter createPresenter() {
return new VehicleTeamPresenter(this);
}
@Override
public void getTeammates(int roomId) {
mPresenter.getTeammates(roomId);
}
@Override
public void showTeammates(List<TeammateInfo> teammates) {
if (teammates == null) {
return;
}
tvNum.setText("车队人数:" + teammates.size() + "");
if (vehicleTeamAdapter == null) {
vehicleTeamAdapter = new VehicleTeamAdapter(getContext(), teammates);
vehicleTeamAdapter.setItemClickListener((position, teammateInfo) -> {
MogoApisHandler.getInstance().getApis().getMapServiceApi().getMapUIController()
.moveToCenter(new MogoLatLng(teammateInfo.getLat(), teammateInfo.getLon()));
CallChatCenter.Companion.getCallChatCenter().closeVehicleTeamFragment();
});
rvTeammates.setAdapter(vehicleTeamAdapter);
} else {
vehicleTeamAdapter.setTeammates(teammates);
vehicleTeamAdapter.notifyDataSetChanged();
}
}
@Override
public void getTeammatesFailed(String msg) {
}
}

View File

@@ -0,0 +1,47 @@
package com.mogo.module.carchatting.view
import com.google.gson.Gson
import com.mogo.chat.base.BaseResponse
import com.mogo.chat.model.bean.TeammateInfo
import com.mogo.module.carchatting.net.Repository
import com.mogo.module.carchatting.net.request
/**
* created by wujifei on 2020/11/17 11:20
* describe:
*/
class VehicleTeamModel {
private val repository: Repository = Repository()
fun getTeammates(
roomId: Int,
onSuccess: (List<TeammateInfo>) -> Unit,
onError: (String) -> Unit
) {
request<BaseResponse<List<TeammateInfo>>> {
loader {
queryTeamDate(roomId)
}
onSuccess {
it.result.let { result ->
onSuccess.invoke(result)
}
}
onError {
it.message?.let { msg ->
onError.invoke(msg)
}
}
}
}
private suspend fun queryTeamDate(roomId: Int): BaseResponse<List<TeammateInfo>> {
val tmpMap = mapOf("roomId" to roomId)
val map = mapOf("data" to Gson().toJson(tmpMap))
return repository.apiCall {
repository.getNetWorkApi().queryTeamDate(map)
}
}
}

View File

@@ -0,0 +1,33 @@
package com.mogo.module.carchatting.view;
import com.mogo.chat.util.sp.SharedPreferenceUtil;
import com.mogo.commons.mvp.Presenter;
/**
* created by wujifei on 2020/11/16 19:40
* describe:
*/
public class VehicleTeamPresenter extends Presenter<VehicleTeamView> {
private final VehicleTeamModel vehicleTeamModel;
public VehicleTeamPresenter(VehicleTeamView view) {
super(view);
vehicleTeamModel = new VehicleTeamModel();
getTeammates(SharedPreferenceUtil.getRoomId());
}
public void getTeammates(int roomId) {
vehicleTeamModel.getTeammates(roomId, teammateInfos -> {
mView.showTeammates(teammateInfos);
return null;
}, s -> {
mView.getTeammatesFailed(s);
return null;
});
}
}

View File

@@ -0,0 +1,20 @@
package com.mogo.module.carchatting.view;
import com.mogo.chat.model.bean.TeammateInfo;
import com.mogo.commons.mvp.IView;
import java.util.List;
/**
* created by wujifei on 2020/11/16 19:41
* describe:
*/
public interface VehicleTeamView extends IView {
void getTeammates(int roomId);
void showTeammates(List<TeammateInfo> teammateInfos);
void getTeammatesFailed(String msg);
}

View File

@@ -0,0 +1,79 @@
package com.mogo.module.carchatting.view
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.mogo.chat.constant.TAG
import com.mogo.module.carchatting.R
import com.mogo.module.common.MogoApisHandler
import com.mogo.utils.logger.Logger
class VrModeHelper : IMogoCallChatWindowModeChange {
companion object {
val vrModeHelper by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
VrModeHelper()
}
}
@SuppressLint("InflateParams")
override fun getCallingWindowLayoutView(context: Context): View {
val vrMode = MogoApisHandler.getInstance().apis.statusManagerApi.isVrMode
Logger.i(TAG,"vrMode : $vrMode")
return if (vrMode) {
LayoutInflater.from(context)
.inflate(R.layout.module_car_chatting_launcher_calling_hawk_eye_view, null)
} else {
LayoutInflater.from(context)
.inflate(R.layout.module_car_chatting_launcher_calling_view, null)
}
}
override fun getCallingWindowLayoutPosition(context: Context): FrameLayout.LayoutParams {
val params: FrameLayout.LayoutParams
val x: Float
val y: Float
if (MogoApisHandler.getInstance().apis.statusManagerApi.isVrMode) {
x = context.resources.getDimension(R.dimen.module_call_chat_state_location_hawk_eye_x)
y = context.resources.getDimension(R.dimen.module_call_chat_state_location_hawk_eye_y)
params = 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()
)
} else {
x = context.resources.getDimension(R.dimen.module_call_chat_state_location_x)
y = context.resources.getDimension(R.dimen.module_call_chat_state_location_y)
params = FrameLayout.LayoutParams(
context.resources.getDimension(R.dimen.module_call_chat_state_width).toInt(),
context.resources.getDimension(R.dimen.module_call_chat_state_height).toInt()
)
}
params.leftMargin = x.toInt()
params.topMargin = y.toInt()
return params
}
@SuppressLint("InflateParams")
override fun getTeamInvitationWindowLayoutView(context: Context): View {
return if (MogoApisHandler.getInstance().apis.statusManagerApi.isVrMode) {
LayoutInflater.from(context)
.inflate(R.layout.module_car_chatting_launcher_team_invitation_hawk_eye_view, null)
} else {
LayoutInflater.from(context)
.inflate(R.layout.module_car_chatting_launcher_team_invitation_view, null)
}
}
override fun getTeamInvitationWindowLayoutPosition(context: Context): ViewGroup.MarginLayoutParams {
val height: Float = if (MogoApisHandler.getInstance().apis.statusManagerApi.isVrMode) {
context.resources.getDimension(R.dimen.module_call_chat_team_invitation_hawk_eye_height)
} else {
context.resources.getDimension(R.dimen.module_call_chat_team_invitation_layout_height)
}
return ViewGroup.MarginLayoutParams(ViewGroup.MarginLayoutParams.MATCH_PARENT, height.toInt())
}
}

View File

@@ -0,0 +1,22 @@
package com.mogo.module.carchatting.voice
interface IVoiceBusinessListener {
fun onVoiceStartMatch() {}
fun onVoiceCancelCall() {}
fun onVoiceCancelMatch(isTTS: Boolean) {}
fun onVoiceContinueCall(isTTS: Boolean) {
}
fun onVoiceReMatch() {
}
fun onVoiceInviteJoinTeam() {}
fun onVoiceJoinTeam() {}
fun onVoiceRefuseJoinTeam() {}
}

View File

@@ -0,0 +1,35 @@
package com.mogo.module.carchatting.voice
import com.mogo.commons.voice.IMogoVoiceCmdCallBack
import com.mogo.module.carchatting.util.LogUtil
private const val IVoiceCommandTAG = "IVoiceCommandTAG"
interface IVoiceCommandListener : IMogoVoiceCmdCallBack, IVoiceBusinessListener {
override fun onCmdSelected(cmd: String?) {
LogUtil.i(
IVoiceCommandTAG, "onCmdSelected cmd:${cmd ?: "cmd is null"}"
)
cmd?.let {
VoiceManager.handleOnCmdSelected(cmd, this)
}
}
override fun onCmdAction(speakText: String?) {
}
override fun onCmdCancel(speakText: String?) {
}
override fun onSpeakSelectTimeOut(speakText: String?) {
}
override fun onSpeakEnd(speakText: String?) {
LogUtil.i(IVoiceCommandTAG, "onSpeakEnd --- speakText : $speakText")
}
}

View File

@@ -0,0 +1,23 @@
package com.mogo.module.carchatting.voice
import android.content.Intent
import com.mogo.module.carchatting.util.LogUtil
import com.mogo.service.intent.IMogoIntentListener
private const val IVoiceIntentTAG = "IVoiceIntentListener"
interface IVoiceIntentListener : IMogoIntentListener ,IVoiceBusinessListener{
override fun onIntentReceived(cmd: String?, intent: Intent?) {
LogUtil.i(IVoiceIntentTAG, "cmd -> $cmd")
if (intent != null && cmd != null) {
VoiceManager.handleOnIntentCmd(cmd, intent, this)
}
}
//打开车聊聊 切换卡片至车聊聊
fun onSwitchCardToChat(){
}
}

View File

@@ -0,0 +1,18 @@
package com.mogo.module.carchatting.voice
import com.mogo.commons.voice.IMogoVoiceCmdCallBack
interface IVoiceQCommandListener : IMogoVoiceCmdCallBack {
override fun onCmdSelected(cmd: String?) {
}
override fun onSpeakEnd(speakText: String?) {
}
override fun onSpeakSelectTimeOut(speakText: String?) {
}
}

View File

@@ -0,0 +1,98 @@
package com.mogo.module.carchatting.voice
import android.content.Intent
import com.google.gson.Gson
import com.mogo.module.carchatting.bean.VoiceWake
import com.mogo.module.carchatting.bean.check
import com.mogo.module.carchatting.util.LogUtil
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_INTENT_CANCEL_CALL_COMMAND
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_INTENT_CANCEL_MATCH_COMMAND
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_INTENT_CHAT_COMMAND
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_INTENT_CONTINUE_CALL_COMMAND
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_INTENT_REFUSE_CALL
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_INTENT_REMATCH_COMMAND
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_INTENT_START_COMMAND
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_REGISTER_CANCEL_CALL
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_REGISTER_CANCEL_MATCH
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_REGISTER_CONTINUE_CALL
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_REGISTER_INVITE_JOIN_TEAM
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_REGISTER_JOIN_TEAM
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_REGISTER_MATCH
import com.mogo.module.carchatting.voice.VoiceUtil.VOICE_REGISTER_REFUSE_JOIN_TEAM
object VoiceManager {
private const val TAG = "VoiceManager"
fun handleOnCmdSelected(cmd: String, listener: IVoiceCommandListener) {
LogUtil.i(TAG, "handleOnCmdSelected: cmd ---> $cmd")
when (cmd) {
VOICE_REGISTER_MATCH -> {
LogUtil.i(TAG, "语音免唤醒 开始匹配")
listener.onVoiceStartMatch()
}
VOICE_REGISTER_CANCEL_MATCH -> {
LogUtil.i(TAG, "语音免唤醒 取消匹配")
listener.onVoiceCancelMatch(true)
}
VOICE_REGISTER_CANCEL_CALL -> {
LogUtil.i(TAG, "语音免唤醒 取消打电话")
listener.onVoiceCancelCall()
}
VOICE_REGISTER_CONTINUE_CALL -> {
LogUtil.i(TAG, "语音免唤醒 继续呼叫")
listener.onVoiceContinueCall(true)
}
VOICE_REGISTER_INVITE_JOIN_TEAM -> {
LogUtil.i(TAG, "语音免唤醒 邀請加入车队")
listener.onVoiceInviteJoinTeam()
}
VOICE_REGISTER_JOIN_TEAM -> {
LogUtil.i(TAG, "语音免唤醒 加入车队")
listener.onVoiceJoinTeam()
}
VOICE_REGISTER_REFUSE_JOIN_TEAM -> {
LogUtil.i(TAG, "语音免唤醒 拒绝加入车队")
listener.onVoiceRefuseJoinTeam()
}
}
}
fun handleOnIntentCmd(cmd: String, intent: Intent, listener: IVoiceIntentListener) {
LogUtil.i(TAG, "handleOnIntentCmd: cmd -> $cmd")
when (cmd) {
VOICE_INTENT_START_COMMAND -> {
val data = intent.getStringExtra("data")
LogUtil.i(TAG, "intent data -> $data")
data?.let {
val voiceWake = Gson().fromJson(it, VoiceWake::class.java)
if (voiceWake.check()) {
listener.onSwitchCardToChat()
} else {
LogUtil.i(TAG, "voiceWake check failed data:$it")
}
}
}
VOICE_INTENT_CHAT_COMMAND -> {
LogUtil.i(TAG, "语音唤醒 开始匹配")
listener.onVoiceStartMatch()
}
VOICE_INTENT_CANCEL_MATCH_COMMAND -> {
LogUtil.i(TAG, "语音唤醒 取消匹配")
listener.onVoiceCancelMatch(false)
}
VOICE_INTENT_CANCEL_CALL_COMMAND, VOICE_INTENT_REFUSE_CALL -> {
LogUtil.i(TAG, "语音唤醒 取消打电话")
listener.onVoiceCancelCall()
}
VOICE_INTENT_CONTINUE_CALL_COMMAND -> {
LogUtil.i(TAG, "语音唤醒 继续呼叫")
listener.onVoiceContinueCall(false)
}
VOICE_INTENT_REMATCH_COMMAND -> {
LogUtil.i(TAG, "语音唤醒 重新匹配")
listener.onVoiceReMatch()
}
}
}
}

View File

@@ -0,0 +1,209 @@
package com.mogo.module.carchatting.voice
import android.content.Context
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.commons.debug.DebugConfig.CAR_MACHINE_TYPE_SELF_INNOVATE
import com.mogo.commons.debug.DebugConfig.getCarMachineType
import com.mogo.commons.voice.AIAssist
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.module.carchatting.util.LogUtil
import com.mogo.service.IMogoServiceApis
import com.mogo.service.intent.IMogoIntentManager
import com.mogo.utils.logger.Logger
object VoiceUtil {
private const val TAG = "VoiceUtil"
private var intentRegister: IMogoIntentManager? = null
init {
LogUtil.i(TAG, "init")
val register =
ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS).navigation()
if (register is IMogoServiceApis) {
intentRegister = register.intentManagerApi
}
}
//WakeUp Command (Intent)
const val VOICE_INTENT_START_COMMAND = "system.application.operation" //打开车聊聊 卡片切换
const val VOICE_INTENT_CHAT_COMMAND = "com.zhidao.imdemo.chat" //开始匹配
const val VOICE_INTENT_CANCEL_MATCH_COMMAND = "com.zhidao.commin.cancel" //取消匹配
const val VOICE_INTENT_REMATCH_COMMAND = "com.zhidao.imdemo.chat.rematch" //重新匹配
const val VOICE_INTENT_CANCEL_CALL_COMMAND = "com.zhidao.commin.cancel" //取消呼叫,取消通话
const val VOICE_INTENT_REFUSE_CALL = "com.ileja.phone.incoming.reject" //挂断
const val VOICE_INTENT_CONTINUE_CALL_COMMAND = "com.zhidao.imdemo.chat.continue.call" //继续呼叫
//unWakeUp Command
const val VOICE_REGISTER_INVITE_JOIN_TEAM = "CMD_CAR_CHAT_INVITE_JOIN_TEAM"
private val customInviteJoinTeamArray: Array<String> = arrayOf("邀请加入车队")
const val VOICE_REGISTER_JOIN_TEAM = "CMD_CAR_CHAT_JOIN_TEAM"
private val customJoinTeamArray: Array<String> = arrayOf("加入", "加入车队")
const val VOICE_REGISTER_REFUSE_JOIN_TEAM = "CMD_CAR_CHAT_REFUSE_JOIN_TEAM"
private val customRefuseJoinTeamArray: Array<String> = arrayOf("拒绝", "拒绝加入车队")
const val VOICE_REGISTER_MATCH = "CMD_CAR_CHAT_MATCH"
private val customMatchArray: Array<String> = arrayOf("语音匹配", "开始匹配", "匹配好友", "聊一聊", "我要聊天")
const val VOICE_REGISTER_CANCEL_MATCH = "CMD_CAR_CHAT_CANCEL_MATCH"
private val customCancelMatchArray: Array<String> = arrayOf("取消匹配")
const val VOICE_REGISTER_CANCEL_CALL = "CMD_CAR_CHAT_CANCEL_CALL"
private val customCancelCallArray: Array<String> = arrayOf("取消通话", "取消呼叫", "挂断")
const val VOICE_REGISTER_CONTINUE_CALL = "CMD_CAR_CHAT_CONTINUE_CALL"
private val customContinueCallArray: Array<String> = arrayOf("继续呼叫")
//Question and TTS
private const val REQUEST_MATCH_FAIL = "匹配失败,说“重新匹配”继续寻找缘分车友。"
private val requestMatchFailYArray: Array<String> = arrayOf("重新匹配")
private val requestMatchFailNArray: Array<String> = arrayOf("取消", "取消匹配")
fun speak(content: String, context: Context, listener: IVoiceCommandListener) {
AIAssist.getInstance(context).speakTTSVoice(content, listener)
}
private var hasRegister = false
fun registerAll(
context: Context,
voiceCommand: IVoiceCommandListener,
intentCommand: IVoiceIntentListener
) {
if (!hasRegister) {
hasRegister = true
// registerReMatch(intentCommand)
if (getCarMachineType() != CAR_MACHINE_TYPE_SELF_INNOVATE) {
registerMatch(context, voiceCommand, intentCommand)
registerCancelMatch(context, voiceCommand, intentCommand)
}
registerCancelCall(context, voiceCommand, intentCommand)
// registerContinueCall(context, voiceCommand, intentCommand)
// registerIntentVoiceCommand(intentCommand)
}
}
fun registerInviteJoinTeam(context: Context, voiceCommand: IVoiceCommandListener) {
LogUtil.i(TAG, "registerInviteJoinTeam")
AIAssist.getInstance(context)
.registerUnWakeupCommand(VOICE_REGISTER_INVITE_JOIN_TEAM, customInviteJoinTeamArray, voiceCommand)
}
fun registerJoinTeam(context: Context, voiceCommand: IVoiceCommandListener) {
LogUtil.i(TAG, "registerJoinTeam")
AIAssist.getInstance(context)
.registerUnWakeupCommand(VOICE_REGISTER_JOIN_TEAM, customJoinTeamArray, voiceCommand)
AIAssist.getInstance(context)
.registerUnWakeupCommand(VOICE_REGISTER_REFUSE_JOIN_TEAM, customRefuseJoinTeamArray, voiceCommand)
}
private fun registerMatch(
context: Context,
voiceCommand: IVoiceCommandListener,
intentCommand: IVoiceIntentListener
) {
LogUtil.i(TAG, "registerMatch")
AIAssist.getInstance(context)
.registerUnWakeupCommand(
VOICE_REGISTER_MATCH,
customMatchArray, voiceCommand
)
intentRegister?.registerIntentListener(VOICE_INTENT_CHAT_COMMAND, intentCommand)
}
private fun registerReMatch(intentCommand: IVoiceIntentListener) {
LogUtil.i(TAG, "registerReMatch")
intentRegister?.registerIntentListener(VOICE_INTENT_REMATCH_COMMAND, intentCommand)
}
private fun registerCancelMatch(
context: Context,
voiceCommand: IVoiceCommandListener,
intentCommand: IVoiceIntentListener
) {
LogUtil.i(TAG, "registerCancelMatch")
AIAssist.getInstance(context)
.registerUnWakeupCommand(
VOICE_REGISTER_CANCEL_MATCH,
customCancelMatchArray, voiceCommand
)
intentRegister?.registerIntentListener(VOICE_INTENT_CANCEL_MATCH_COMMAND, intentCommand)
}
private fun registerCancelCall(
context: Context,
voiceCommand: IVoiceCommandListener,
intentCommand: IVoiceIntentListener
) {
LogUtil.i(TAG, "registerCancelCall")
AIAssist.getInstance(context).registerUnWakeupCommand(
VOICE_REGISTER_CANCEL_CALL,
customCancelCallArray,
voiceCommand
)
intentRegister?.registerIntentListener(VOICE_INTENT_CANCEL_CALL_COMMAND, intentCommand)
intentRegister?.registerIntentListener(VOICE_INTENT_REFUSE_CALL, intentCommand)
}
private fun registerContinueCall(
context: Context,
voiceCommand: IVoiceCommandListener,
intentCommand: IVoiceIntentListener
) {
LogUtil.i(TAG, "registerContinueCall")
AIAssist.getInstance(context).registerUnWakeupCommand(
VOICE_REGISTER_CONTINUE_CALL,
customContinueCallArray,
voiceCommand
)
intentRegister?.registerIntentListener(
VOICE_INTENT_CONTINUE_CALL_COMMAND,
intentCommand
)
}
fun unregisterAll(context: Context, listener: IVoiceIntentListener) {
LogUtil.i(TAG, "unregister All")
if (getCarMachineType() != CAR_MACHINE_TYPE_SELF_INNOVATE) {
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_MATCH)
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_CANCEL_MATCH)
}
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_CANCEL_CALL)
// AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_CONTINUE_CALL)
hasRegister = false
LogUtil.i(TAG, "unregister IntentVoiceCommand --- intentRegister:$intentRegister")
intentRegister?.let {
// it.unregisterIntentListener(VOICE_INTENT_START_COMMAND, listener)
if (getCarMachineType() != CAR_MACHINE_TYPE_SELF_INNOVATE) {
it.unregisterIntentListener(VOICE_INTENT_CHAT_COMMAND, listener)
it.unregisterIntentListener(VOICE_INTENT_CANCEL_MATCH_COMMAND, listener)
}
it.unregisterIntentListener(VOICE_INTENT_CANCEL_CALL_COMMAND, listener)
it.unregisterIntentListener(VOICE_INTENT_REFUSE_CALL, listener)
// it.unregisterIntentListener(VOICE_INTENT_CONTINUE_CALL_COMMAND, listener)
}
}
fun unregisterInviteJoinTeam(context: Context) {
LogUtil.i(TAG, "unregisterInviteJoinTeam")
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_INVITE_JOIN_TEAM)
}
fun unregisterJoinTeam(context: Context) {
LogUtil.i(TAG, "unregisterJoinTeam")
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_JOIN_TEAM)
AIAssist.getInstance(context).unregisterUnWakeupCommand(VOICE_REGISTER_REFUSE_JOIN_TEAM)
}
private fun registerIntentVoiceCommand(listener: IVoiceIntentListener) {
LogUtil.i(TAG, "registerIntentVoiceCommand --- intentRegister:$intentRegister")
intentRegister?.registerIntentListener(VOICE_INTENT_START_COMMAND, listener)
}
fun weatherReMatch(context: Context, callback: IVoiceQCommandListener) {
Logger.d(TAG, "weatherReMatch")
AIAssist.getInstance(context).speakQAndACmd(
REQUEST_MATCH_FAIL,
requestMatchFailYArray,
requestMatchFailNArray, callback
)
}
}

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: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 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: 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: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 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: 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 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>

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