Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
YDL-Component-Medical
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
杨凯
YDL-Component-Medical
Commits
c244aecc
Commit
c244aecc
authored
Oct 13, 2020
by
YKai
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:声网onRtcStat回调,退出房间逻辑去掉,增加日志上传
parent
b9e2abb3
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
141 additions
and
42 deletions
+141
-42
AudioHomeActivity.kt
m-audioim/src/main/java/com/ydl/audioim/AudioHomeActivity.kt
+31
-7
ConsultantAudioHomeActivity.kt
m-audioim/src/main/java/com/ydl/consultantim/ConsultantAudioHomeActivity.kt
+110
-35
No files found.
m-audioim/src/main/java/com/ydl/audioim/AudioHomeActivity.kt
View file @
c244aecc
...
...
@@ -79,87 +79,108 @@ class AudioHomeActivity :
* 专家头像地址
*/
private
var
expertHeadUrl
:
String
?
=
null
/**
* 专家姓名
*/
private
var
expertName
:
String
?
=
null
/**
* 专家文案
*/
private
var
expertTips
:
String
?
=
null
/**
* 声网点对点聊天房间id
*/
private
var
channelId
:
String
?
=
null
/**
* 通话开始时间(接通)
*/
private
var
callStartTime
:
Long
?
=
null
/**
* 倾诉剩余时长(时长单位s,eg:剩余2min15s,返回135)
*/
private
var
remainTime
:
String
?
=
null
/**
*聆听者id(不是聆听者的uid)
*/
private
var
listenId
:
String
?
=
null
/**
* token
*/
private
var
token
:
String
?
=
null
/**
* commentUrl 评价页URL
*/
private
var
commentUrl
:
String
?
=
null
/**
* callId
*/
private
var
callId
:
String
?
=
null
/**
* relation_id
*/
private
var
relationId
:
String
?
=
null
/**
* listenerUid专家uid
*/
private
var
listenerUid
:
String
?
=
null
/**
* 倾述总时长
*/
private
var
totalDuration
:
Int
?
=
0
/**
* 本地记录的当前剩余时间
*/
private
var
localRemainTime
:
Int
?
=
0
/**
* 60s自动挂断倒计时
*/
private
var
waitDisposable
:
Disposable
?
=
null
/**
* 45s倒计时
*/
private
var
disposable
:
Disposable
?
=
null
/**
* 本次倾述倒计时
*/
private
var
totalDisposable
:
Disposable
?
=
null
/**
* 是否连接成功
*/
private
var
isConnectSuccess
:
Boolean
=
false
//电源管理对象
private
var
localPowerManager
:
PowerManager
?
=
null
//电源锁
private
var
localWakeLock
:
PowerManager
.
WakeLock
?
=
null
private
var
sensorManager
:
SensorManager
?
=
null
private
var
sendDoctocrMsg
:
String
?
=
null
private
var
axbPhone
:
String
?
=
null
//是否跳转到拨号页面
private
var
isJumpDail
:
Boolean
=
false
private
var
isShowAXB
:
Boolean
=
true
private
var
mPlayer
:
AudioPlayer
?
=
null
private
var
vibrator
:
Vibrator
?
=
null
private
var
handler
:
Handler
?
=
null
//声网
private
var
voiceManage
:
YDLVoiceManager
?
=
null
...
...
@@ -182,6 +203,7 @@ class AudioHomeActivity :
override
fun
onWarning
(
warn
:
Int
)
{
super
.
onWarning
(
warn
)
uploadException
(
"mRtcEventHandler-onWarning:warnCode--%${warn}"
,
callback
=
null
)
writeAgoraLog
(
"通话挂断:网络异常(${warn})"
)
//103:没有可用的频道资源。可能是因为服务端没法分配频道资源
//104:查找频道超时。在加入频道时 SDK 先要查找指定的频道,出现该警告一般是因为网络太差,连接不到服务器
//105:查找频道请求被服务器拒绝。服务器可能没有办法处理这个请求或请求是非法的
...
...
@@ -286,14 +308,16 @@ class AudioHomeActivity :
override
fun
onRtcStats
(
stats
:
IRtcEngineEventHandler
.
RtcStats
?)
{
super
.
onRtcStats
(
stats
)
uploadLog
()
// 不需要移动端做离开房间逻辑,服务端会判断进行踢人逻辑
//因为用户端直接加入了频道,防止该回调执行时,专家还未加入频道,因此在连接成功之后,才进行频道人数判断
if
(
isConnectSuccess
&&
null
!=
stats
?.
users
&&
stats
.
users
==
1
)
{
writeAgoraLog
(
"通话结束:用户加入了频道,但频道内只有一个人"
)
com
.
yidianling
.
common
.
tools
.
ToastUtil
.
toastShort
(
"专家已挂断"
)
//通话结束或挂断时,上传日志文件
uploadLog
()
leaveChannel
()
}
//
if (isConnectSuccess && null != stats?.users && stats.users == 1) {
//
writeAgoraLog("通话结束:用户加入了频道,但频道内只有一个人")
//
com.yidianling.common.tools.ToastUtil.toastShort("专家已挂断")
//
//通话结束或挂断时,上传日志文件
//
uploadLog()
//
leaveChannel()
//
}
}
override
fun
onConnectionStateChanged
(
state
:
Int
,
reason
:
Int
)
{
...
...
m-audioim/src/main/java/com/ydl/consultantim/ConsultantAudioHomeActivity.kt
View file @
c244aecc
...
...
@@ -68,18 +68,25 @@ import java.util.concurrent.TimeUnit
* @date 2018/10/30
*/
@Route
(
path
=
"/av/ConsultantAudioHomeActivity"
)
class
ConsultantAudioHomeActivity
:
BaseMvpActivity
<
IConsultantAudioHomeActivityContract
.
View
,
IConsultantAudioHomeActivityContract
.
Presenter
>(),
IConsultantAudioHomeActivityContract
.
View
,
SensorEventListener
{
class
ConsultantAudioHomeActivity
:
BaseMvpActivity
<
IConsultantAudioHomeActivityContract
.
View
,
IConsultantAudioHomeActivityContract
.
Presenter
>(),
IConsultantAudioHomeActivityContract
.
View
,
SensorEventListener
{
//语音管理类
private
var
voiceManage
:
YDLVoiceManager
?
=
null
//音视频数据
private
var
mAudioMessageBean
:
AudioMessageBean
?
=
null
//音频播放
private
var
mPlayer
:
AudioPlayer
?
=
null
//当前状态 0.未接听 1.已接听
public
var
status
=
STATUS_NOT_ANSWERED
//电源管理对象
private
var
localPowerManager
:
PowerManager
?
=
null
//电源锁
private
var
localWakeLock
:
PowerManager
.
WakeLock
?
=
null
private
var
sensorManager
:
SensorManager
?
=
null
...
...
@@ -88,6 +95,7 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
* 通话开始时间(接通)
*/
private
var
callStartTime
:
Long
?
=
null
/**
* 本次倾述倒计时
*/
...
...
@@ -107,6 +115,7 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
* 是否连接成功
*/
private
var
isConnectSuccess
:
Boolean
=
false
/**
* 事件回调 (SDK 通过指定的事件通知应用程序 SDK 的运行事件,如: 加入或离开频道,新用户加入频道等)
*/
...
...
@@ -131,7 +140,11 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
runOnUiThread
{
// 加入频道后再通知用户已接受
// YDLRTMClient.instances.acceptCall(mAudioMessageBean?.channelId)
YDLavManager
.
instances
.
acceptCall
(
mAudioMessageBean
!!
.
userId
!!
,
mAudioMessageBean
?.
channelId
,
Gson
().
toJson
(
mAudioMessageBean
))
YDLavManager
.
instances
.
acceptCall
(
mAudioMessageBean
!!
.
userId
!!
,
mAudioMessageBean
?.
channelId
,
Gson
().
toJson
(
mAudioMessageBean
)
)
tv_toast
.
visibility
=
View
.
VISIBLE
tv_toast
.
text
=
"连接中..."
...
...
@@ -189,7 +202,11 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
// 3 网络连接被服务器中止 该情况现在是因为后端踢人逻辑
if
(
reason
==
3
)
{
showToast
(
"对方已挂断"
)
YDLavManager
.
instances
.
callEndStatusUpdate
(
mAudioMessageBean
?.
channelId
!!
,
4
,
"服务端踢人触发的回调"
)
YDLavManager
.
instances
.
callEndStatusUpdate
(
mAudioMessageBean
?.
channelId
!!
,
4
,
"服务端踢人触发的回调"
)
writeAgoraLog
(
"通话挂断:网络连接被服务器中止"
)
//通话结束或挂断时,上传日志文件
...
...
@@ -230,7 +247,11 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
super
.
onUserOffline
(
uid
,
elapsed
)
LogUtil
.
e
(
"[agora]$uid 主播离开频道回调"
)
runOnUiThread
{
YDLavManager
.
instances
.
callEndStatusUpdate
(
mAudioMessageBean
?.
channelId
!!
,
4
,
"对方离开频道"
)
YDLavManager
.
instances
.
callEndStatusUpdate
(
mAudioMessageBean
?.
channelId
!!
,
4
,
"对方离开频道"
)
showToast
(
"对方已挂断"
)
writeAgoraLog
(
"通话接通后挂断:主叫离开频道"
)
//通话结束或挂断时,上传日志文件
...
...
@@ -250,6 +271,7 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
uploadException
(
"mRtcEventHandler-onWarning:warnCode--%${warn}"
)
}
LogUtil
.
e
(
"[agora]发生警告回调=$warn"
)
writeAgoraLog
(
"通话挂断:网络异常($warn)"
)
//103:没有可用的频道资源。可能是因为服务端没法分配频道资源
//104:查找频道超时。在加入频道时 SDK 先要查找指定的频道,出现该警告一般是因为网络太差,连接不到服务器
//105:查找频道请求被服务器拒绝。服务器可能没有办法处理这个请求或请求是非法的
...
...
@@ -311,7 +333,11 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
else
->
{
}
}
YDLavManager
.
instances
.
callEndStatusUpdate
(
mAudioMessageBean
?.
channelId
!!
,
4
,
"频道的错误回调信息${err}"
)
YDLavManager
.
instances
.
callEndStatusUpdate
(
mAudioMessageBean
?.
channelId
!!
,
4
,
"频道的错误回调信息${err}"
)
}
}
...
...
@@ -363,9 +389,11 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
companion
object
{
const
val
PARAM
:
String
=
"param"
//0.未接听 1.已接听
const
val
STATUS_NOT_ANSWERED
=
0
const
val
STATUS_ANSWERED
=
1
//666.未接听,直接挂断 667.已接听,正常挂断 668:未接听,用户端取消了
const
val
RESULT_NOT_ANSWERED_CODE
=
666
const
val
RESULT_ANSWERED_CODE
=
667
...
...
@@ -405,7 +433,8 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
if
(
YDLavManager
.
sdkStatus
!=
Constants
.
CONNECTION_STATE_CONNECTED
)
{
writeAgoraLog
(
"RMT状态:${YDLavManager.sdkStatus},重新登录RMT"
)
val
uid
=
ModularServiceManager
.
provide
(
IUserService
::
class
.
java
).
getUserInfo
()
?.
uid
val
uid
=
ModularServiceManager
.
provide
(
IUserService
::
class
.
java
).
getUserInfo
()
?.
uid
YDLavManager
.
instances
.
login
(
uid
)
{
_isSuccess
,
_msg
->
writeAgoraLog
(
"登录RTM的uid=${uid}"
)
val
result
=
if
(
_isSuccess
)
"RMT登录成功"
else
"RMT登录失败:$_msg"
...
...
@@ -426,7 +455,10 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
sensorManager
=
getSystemService
(
Context
.
SENSOR_SERVICE
)
as
SensorManager
?
localPowerManager
=
getSystemService
(
POWER_SERVICE
)
as
PowerManager
?
localWakeLock
=
localPowerManager
!!
.
newWakeLock
(
PowerManager
.
PROXIMITY_SCREEN_OFF_WAKE_LOCK
,
"yidianling"
)
localWakeLock
=
localPowerManager
!!
.
newWakeLock
(
PowerManager
.
PROXIMITY_SCREEN_OFF_WAKE_LOCK
,
"yidianling"
)
}
/**
...
...
@@ -556,8 +588,10 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
val
account
=
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
()
if
(!
TextUtils
.
isEmpty
(
mAudioMessageBean
?.
channelId
))
{
LogUtil
.
e
(
"[agora] joinChannel:$account"
)
voiceManage
?.
getVoiceApi
()
?.
joinChannel
(
channelToken
?:
""
,
mAudioMessageBean
!!
.
channelId
!!
,
"Extra Optional Data"
,
account
)
voiceManage
?.
getVoiceApi
()
?.
joinChannel
(
channelToken
?:
""
,
mAudioMessageBean
!!
.
channelId
!!
,
"Extra Optional Data"
,
account
)
}
}
...
...
@@ -577,13 +611,15 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
rl_remain_time
.
visibility
=
View
.
VISIBLE
totalDisposable
=
Observable
.
interval
(
0
,
1
,
TimeUnit
.
SECONDS
).
subscribeOn
(
Schedulers
.
computation
()).
observeOn
(
AndroidSchedulers
.
mainThread
()).
subscribe
({
tv_remain_time
.
text
=
DateUtils
.
formatTime
(
it
.
toString
())
},
{
//通话结束或挂断时,上传日志文件
uploadLog
()
close
(
RESULT_NOT_ANSWERED_CODE
,
"对方异常"
)
})
totalDisposable
=
Observable
.
interval
(
0
,
1
,
TimeUnit
.
SECONDS
).
subscribeOn
(
Schedulers
.
computation
())
.
observeOn
(
AndroidSchedulers
.
mainThread
()).
subscribe
({
tv_remain_time
.
text
=
DateUtils
.
formatTime
(
it
.
toString
())
},
{
//通话结束或挂断时,上传日志文件
uploadLog
()
close
(
RESULT_NOT_ANSWERED_CODE
,
"对方异常"
)
})
}
/**
...
...
@@ -622,7 +658,11 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
}
showStopService
()
ActionCountUtils
.
count
(
"shengwang_popup_layer_page|shengwang_popup_layer_refuse_click"
,
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
(),
uid
=
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
())
ActionCountUtils
.
count
(
"shengwang_popup_layer_page|shengwang_popup_layer_refuse_click"
,
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
(),
uid
=
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
()
)
}
/**
...
...
@@ -642,7 +682,11 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
executeCall
(
true
)
ActionCountUtils
.
count
(
"shengwang_popup_layer_page|shengwang_popup_layer_answer_click"
,
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
(),
uid
=
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
())
ActionCountUtils
.
count
(
"shengwang_popup_layer_page|shengwang_popup_layer_answer_click"
,
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
(),
uid
=
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
()
)
}
/**
...
...
@@ -692,9 +736,17 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
override
fun
onResume
()
{
super
.
onResume
()
sensorManager
!!
.
registerListener
(
this
,
sensorManager
!!
.
getDefaultSensor
(
Sensor
.
TYPE_PROXIMITY
),
SensorManager
.
SENSOR_DELAY_NORMAL
)
sensorManager
!!
.
registerListener
(
this
,
sensorManager
!!
.
getDefaultSensor
(
Sensor
.
TYPE_PROXIMITY
),
SensorManager
.
SENSOR_DELAY_NORMAL
)
ActionCountUtils
.
count
(
"shengwang_popup_layer_page|shengwang_popup_layer_page_visit"
,
""
,
uid
=
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
())
ActionCountUtils
.
count
(
"shengwang_popup_layer_page|shengwang_popup_layer_page_visit"
,
""
,
uid
=
YdlCommonRouterManager
.
getYdlCommonRoute
().
getUid
().
toString
()
)
}
override
fun
onAccuracyChanged
(
sensor
:
Sensor
?,
accuracy
:
Int
)
{
...
...
@@ -739,10 +791,20 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
}
else
{
tv_nte_status
.
text
=
msg
if
(
status
==
0
)
{
tv_nte_status
.
setCompoundDrawablesWithIntrinsicBounds
(
ContextCompat
.
getDrawable
(
this
,
R
.
drawable
.
av_audio_wifi_normal
),
null
,
null
,
null
)
tv_nte_status
.
setCompoundDrawablesWithIntrinsicBounds
(
ContextCompat
.
getDrawable
(
this
,
R
.
drawable
.
av_audio_wifi_normal
),
null
,
null
,
null
)
}
if
(
status
==
1
)
{
tv_nte_status
.
setCompoundDrawablesWithIntrinsicBounds
(
ContextCompat
.
getDrawable
(
this
,
R
.
drawable
.
av_audio_wifi_better
),
null
,
null
,
null
)
tv_nte_status
.
setCompoundDrawablesWithIntrinsicBounds
(
ContextCompat
.
getDrawable
(
this
,
R
.
drawable
.
av_audio_wifi_better
),
null
,
null
,
null
)
}
if
(
status
==
-
1
)
{
tv_nte_status
.
setCompoundDrawablesWithIntrinsicBounds
(
null
,
null
,
null
,
null
)
...
...
@@ -769,8 +831,12 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
callStatus
=
2
uploadException
(
"被叫拒绝"
,
zhu
=
""
)
//未接听,直接挂断 发送消息
// YDLRTMClient.instances.refuseCall(mAudioMessageBean?.channelId)
YDLavManager
.
instances
.
refuseCall
(
mAudioMessageBean
!!
.
userId
!!
,
mAudioMessageBean
?.
channelId
,
Gson
().
toJson
(
mAudioMessageBean
))
// YDLRTMClient.instances.refuseCall(mAudioMessageBean?.channelId)
YDLavManager
.
instances
.
refuseCall
(
mAudioMessageBean
!!
.
userId
!!
,
mAudioMessageBean
?.
channelId
,
Gson
().
toJson
(
mAudioMessageBean
)
)
}
RESULT_USER_CANCEL
->
{
callStatus
=
1
...
...
@@ -821,14 +887,20 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
*/
private
fun
connectingStatusWaitingTimeCount
()
{
if
(
connectingStatusDisposable
==
null
)
{
connectingStatusDisposable
=
Observable
.
interval
(
0
,
1
,
TimeUnit
.
SECONDS
).
subscribeOn
(
Schedulers
.
computation
()).
take
(
6
).
observeOn
(
AndroidSchedulers
.
mainThread
()).
subscribe
({},
{},
{
if
(!
isConnectSuccess
)
{
YDLavManager
.
instances
.
callEndStatusUpdate
(
mAudioMessageBean
?.
channelId
!!
,
1
,
"被叫加入频道后主叫未加入超时"
)
writeAgoraLog
(
"通话未接通挂断:连接中的状态超过5s自动挂断"
)
showToast
(
"用户已挂断"
)
close
(
RESULT_NOT_ANSWERED_CODE
,
""
)
}
})
connectingStatusDisposable
=
Observable
.
interval
(
0
,
1
,
TimeUnit
.
SECONDS
).
subscribeOn
(
Schedulers
.
computation
())
.
take
(
6
).
observeOn
(
AndroidSchedulers
.
mainThread
()).
subscribe
({},
{},
{
if
(!
isConnectSuccess
)
{
YDLavManager
.
instances
.
callEndStatusUpdate
(
mAudioMessageBean
?.
channelId
!!
,
1
,
"被叫加入频道后主叫未加入超时"
)
writeAgoraLog
(
"通话未接通挂断:连接中的状态超过5s自动挂断"
)
showToast
(
"用户已挂断"
)
close
(
RESULT_NOT_ANSWERED_CODE
,
""
)
}
})
}
}
...
...
@@ -897,9 +969,11 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
*/
private
fun
uploadException
(
message
:
String
,
zhu
:
String
=
""
,
eventType
:
String
=
"99"
)
{
var
time
:
String
=
(
System
.
currentTimeMillis
()
/
1000
).
toString
()
var
uid
:
String
=
ModularServiceManager
.
provide
(
IUserService
::
class
.
java
).
getUserInfo
()
?.
uid
!!
var
uid
:
String
=
ModularServiceManager
.
provide
(
IUserService
::
class
.
java
).
getUserInfo
()
?.
uid
!!
var
payLoad
=
PayLoad
(
mAudioMessageBean
?.
channelId
?:
"0"
,
time
,
uid
,
"1"
,
"999"
,
message
)
var
connectException
=
ConnectExceptionCommand
(
time
+
zhu
,
"2"
,
eventType
,
payLoad
,
callStatus
)
var
connectException
=
ConnectExceptionCommand
(
time
+
zhu
,
"2"
,
eventType
,
payLoad
,
callStatus
)
YDLavManager
.
instances
.
uploadException
(
connectException
,
null
)
}
...
...
@@ -921,7 +995,8 @@ class ConsultantAudioHomeActivity : BaseMvpActivity<IConsultantAudioHomeActivity
if
(
ActivityManager
.
getActivitySize
()
==
1
)
{
try
{
// startActivity(MainActivity.newIntent(this, false))
ARouter
.
getInstance
().
build
(
"/main/main"
).
addFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
).
navigation
()
ARouter
.
getInstance
().
build
(
"/main/main"
).
addFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
)
.
navigation
()
}
catch
(
e
:
Exception
)
{
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment