From 8a33c7995dd4836e5e0115461b529bc0d88cc056 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 27 Jun 2024 18:03:54 +0800 Subject: [PATCH 01/32] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AgoraCloudScene/build.gradle | 19 ++ .../src/main/proto/RttMessage.proto | 41 +++ .../agora_options_icon_rtt_active.png | Bin 0 -> 879 bytes ...ora_options_icon_rtt_conversion_active.png | Bin 0 -> 1316 bytes ...ra_options_icon_rtt_conversion_default.png | Bin 0 -> 1643 bytes .../agora_options_icon_rtt_default.png | Bin 0 -> 838 bytes ...gora_options_icon_rtt_subtitles_active.png | Bin 0 -> 1058 bytes .../agora_options_icon_rtt_subtitles_bg.png | Bin 0 -> 996 bytes ...ora_options_icon_rtt_subtitles_default.png | Bin 0 -> 1381 bytes ...a_options_icon_rtt_subtitles_listening.png | Bin 0 -> 873 bytes ..._edu_rtt_conversion_dialog_icon_search.png | Bin 0 -> 1334 bytes ..._rtt_conversion_dialog_options_arrow_n.png | Bin 0 -> 546 bytes ..._rtt_conversion_dialog_options_arrow_y.png | Bin 0 -> 536 bytes ...du_rtt_conversion_dialog_options_close.png | Bin 0 -> 530 bytes ..._rtt_conversion_dialog_options_setting.png | Bin 0 -> 1355 bytes .../agora_options_icon_rtt_active.png | Bin 0 -> 1052 bytes ...ora_options_icon_rtt_conversion_active.png | Bin 0 -> 1716 bytes ...ra_options_icon_rtt_conversion_default.png | Bin 0 -> 2147 bytes .../agora_options_icon_rtt_default.png | Bin 0 -> 1032 bytes ...gora_options_icon_rtt_subtitles_active.png | Bin 0 -> 1357 bytes .../agora_options_icon_rtt_subtitles_bg.png | Bin 0 -> 1324 bytes ...ora_options_icon_rtt_subtitles_default.png | Bin 0 -> 1835 bytes ...a_options_icon_rtt_subtitles_listening.png | Bin 0 -> 1168 bytes ..._edu_rtt_conversion_dialog_icon_search.png | Bin 0 -> 2181 bytes ..._rtt_conversion_dialog_options_arrow_n.png | Bin 0 -> 630 bytes ..._rtt_conversion_dialog_options_arrow_y.png | Bin 0 -> 608 bytes ...du_rtt_conversion_dialog_options_close.png | Bin 0 -> 663 bytes ..._rtt_conversion_dialog_options_setting.png | Bin 0 -> 1840 bytes .../res/drawable/agora_hollow_radius_8.xml | 7 + ..._option_color_rtt_conversion_change_bg.xml | 21 ++ ...ption_color_rtt_conversion_change_text.xml | 6 + .../res/drawable/agora_option_icon_rtt.xml | 8 + .../agora_option_icon_rtt_conversion.xml | 8 + .../agora_option_icon_rtt_subtitles.xml | 8 + .../agora_options_rtt_time_limit_bg.xml | 8 + ...gora_options_rtt_time_limit_bg_disable.xml | 8 + .../res/drawable/agora_solid_radius_10.xml | 5 + .../drawable/agora_solid_radius_10_top.xml | 5 + .../res/drawable/agora_solid_radius_20.xml | 5 + .../res/drawable/agora_solid_radius_4.xml | 5 + .../res/drawable/agora_solid_radius_6.xml | 5 + .../res/drawable/agora_solid_radius_8.xml | 5 + .../res/drawable/agora_solid_radius_max.xml | 5 + ...du_rtt_conversion_dialog_options_arrow.xml | 6 + .../layout/activity_agora_online_class.xml | 37 +++ .../fcr_online_edu_options_component.xml | 12 + .../layout/fcr_online_edu_rtt_component.xml | 91 +++++++ .../fcr_online_edu_rtt_conversion_dialog.xml | 239 ++++++++++++++++++ .../fcr_online_edu_rtt_options_component.xml | 169 +++++++++++++ .../fcr_online_edu_rtt_setting_dialog.xml | 62 +++++ ...ine_rtt_conversion_dialog_list_content.xml | 74 ++++++ ...ine_rtt_conversion_dialog_list_options.xml | 23 ++ ...online_rtt_setting_dialog_content_list.xml | 12 + ...line_rtt_setting_dialog_content_switch.xml | 33 +++ ...nline_rtt_setting_dialog_content_title.xml | 12 + ..._online_rtt_setting_dialog_list_select.xml | 42 +++ .../fcr_online_toast_layout_default.xml | 7 + .../src/main/res/values-zh/strings.xml | 4 + .../src/main/res/values/colors.xml | 15 ++ .../src/main/res/values/dimens.xml | 9 + .../src/main/res/values/strings.xml | 47 ++++ build.gradle | 1 + config.gradle | 2 +- 63 files changed, 1065 insertions(+), 1 deletion(-) create mode 100644 AgoraCloudScene/src/main/proto/RttMessage.proto create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_active.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_conversion_active.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_conversion_default.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_default.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_active.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_bg.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_default.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_listening.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_icon_search.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_n.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_y.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_close.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_setting.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_active.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_conversion_active.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_conversion_default.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_default.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_active.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_bg.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_default.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_listening.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_icon_search.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_n.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_y.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_options_close.png create mode 100644 AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_options_setting.png create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_hollow_radius_8.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_option_color_rtt_conversion_change_bg.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_option_color_rtt_conversion_change_text.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt_conversion.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt_subtitles.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_options_rtt_time_limit_bg.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_options_rtt_time_limit_bg_disable.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_solid_radius_10.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_solid_radius_10_top.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_solid_radius_20.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_solid_radius_4.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_solid_radius_6.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_solid_radius_8.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/agora_solid_radius_max.xml create mode 100644 AgoraCloudScene/src/main/res/drawable/fcr_online_edu_rtt_conversion_dialog_options_arrow.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_component.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_options_component.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_setting_dialog.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_rtt_conversion_dialog_list_content.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_rtt_conversion_dialog_list_options.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_list.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_switch.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_title.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_list_select.xml create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_toast_layout_default.xml diff --git a/AgoraCloudScene/build.gradle b/AgoraCloudScene/build.gradle index 322b83fc2..334a2dca2 100644 --- a/AgoraCloudScene/build.gradle +++ b/AgoraCloudScene/build.gradle @@ -2,6 +2,7 @@ plugins { id 'com.android.library' id 'kotlin-android' id 'kotlin-parcelize' + id 'com.google.protobuf' } android { @@ -49,6 +50,22 @@ android { } } + +protobuf { + protoc { + artifact = 'com.google.protobuf:protoc:3.21.12' + } + generateProtoTasks { + all().each { task -> + task.builtins { + java { + option 'lite' // 生成精简版类 + } + } + } + } +} + dependencies { //implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:${rootProject.ext.dependencies.core_ktx}" @@ -77,6 +94,8 @@ dependencies { implementation 'com.pierfrancescosoffritti.androidyoutubeplayer:custom-ui:12.0.0' implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1" + + implementation 'com.google.protobuf:protobuf-javalite:3.21.12' } if (readyPublishMaven.toBoolean()) { diff --git a/AgoraCloudScene/src/main/proto/RttMessage.proto b/AgoraCloudScene/src/main/proto/RttMessage.proto new file mode 100644 index 000000000..29fad5ed4 --- /dev/null +++ b/AgoraCloudScene/src/main/proto/RttMessage.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package Agora.SpeechToText; + +option objc_class_prefix = "Rtt"; + +option csharp_namespace = "AgoraRTTSample.Protobuf"; + +option java_package = "io.agora.rtc.speech2text"; +option java_outer_classname = "AgoraSpeech2TextProtobuffer"; + +message Text { + int32 vendor = 1; // Not used + int32 version = 2; // Not used + int32 seqnum = 3; // Not used + int64 uid = 4; // 转写内容对应的Rtc uid + int32 flag = 5; // Not used + int64 time = 6; // 该句转写的开始时间(只在isFinal为true时有值,其他时候为0) + int32 lang = 7; // Not used + int32 starttime = 8; // + int32 offtime = 9; // + repeated Word words = 10; // 转写的结果Array + bool end_of_segment = 11; // + int32 duration_ms = 12; // 转写消耗时间 + string data_type = 13; // transcribe(转写), translate(翻译) + repeated Translation trans = 14; // 翻译结果的Array + string culture = 15; // 该句RTT的转写目标语言 + int64 text_ts = 16; // 转写的时间戳,用于对齐翻译 +} +message Word { + string text = 1; // 转写结果 + int32 start_ms = 2; // + int32 duration_ms = 3; // + bool is_final = 4; // 是否最终结果 + double confidence = 5; // 置信度 +} +message Translation { + bool is_final = 1; // 是否最终结果 + string lang = 2; // 翻译目标语言 + repeated string texts = 3; // 翻译的结果 +} \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_active.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_active.png new file mode 100644 index 0000000000000000000000000000000000000000..25211025d67839c8c35dcdc6b85d9f1691408512 GIT binary patch literal 879 zcmV-#1CacQP)iv5x~NrIb=iDW#NBN~u4|9ioN{WFX{t z?rKOcA#ySU*Q>lc+Vg$SeZVpjwCFFsOdtEyTIM#kF=ZkFo!r0HfpLo{%DJfW=4k2M z%?Fr8fbOpw(4!-GKtc5}CG3Ds=B_lUn_VzDpizDOIUc+15TEF!_M6=!Y8%rC%7V$k z%tA&uw*Lbx0^WHi{my#|@+pL|1Dd96LJlTA=o+Iop#-9)hn#LBgoN`&R4_NYRbD zW|shr-AiV|Wqnc+_u8m@BPbACqWjb~z*N3~m~+NZf^CT_>+%I;MQ%X*=B~&pSiXSF zEwsTsHLOfHr(Km0of<8AnW57y|7bGUGdJbRAQAz^5Ku|nnQ}%tf0#S8q6Me}Ipz|F z#}LF0C@oPW&anPt>Hg;J(f+t?<_3Bc)(pK(>qUJ}SCMqdL#(2h)`bd|KH~H0K%<8D zXQwx@qx#eur48sJeGjTNyd81)Ld$x?+@lJku)i{(AF`kZblr4`=Bqk|GzDS-t#mtK zlGqSAS!;Uj`uy0U{p#j%NFyUeP615s`Fh3sAYc+A0WEiTIsmJz5WT9;pDEg}Zw@*M zv(L@7UHF2Klcsh22LTC^#*4LT3JD{{gVc=Lr`*i=8*B6FG`?N&in0?az(X%IJA54-J#U@o0bC)Ts zP2zTaDIT{itqTnTg*C;Qc5YMlu}A5rIh+VeFO54+Z6pPwSWKs002ovPDHLk FV1j4egPs5Y literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_conversion_active.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_conversion_active.png new file mode 100644 index 0000000000000000000000000000000000000000..cf7550b7fb6e755a0862de952b65e76616e65943 GIT binary patch literal 1316 zcmV+<1>5?GP)K~#7F?V5pg z(=ZT*Pc8$52{04TvH)p2K+6P8U;xSlO;`XXAn61&13=RWS|-43kOdm?Uo2BMm26A4 z(&qKP_ni1CQ6m33-JQ-R5(GgI1VIo4K@bE%5S|l?I;KC+Dc*YK*fiOEsxs=3P6UD| zeWmCO$1XjNFl81v=4wgjKd0(~TGC1&wibvo!O^Fu$;EO>pW9efus|$MlF%hiQ+nED z5NSVDq#%K|NSp!|X%8YTJo`~X0boguXnVwActR%>fVTx1>-j@lh*@fLpFZ9e1nW7a zEd^sgCQm{HdPm!f_t;;MKfBZcTGK-1IZKZ6@M)T|yHo06(i-JnO8NWE5P^bVLGX5t zcTV1J)+37XZC`PEZ0S2s(C?OLW((KoJ5cDW>XTIOB-YzWAAG=Y{b`)A?GN>3k$B>t zG91Gujuqn?;q9#Md%wObuWJqp^PY?`sP`U;+HZb=FgG|FM;%PSlp+v&iLp2%@}p-g zXWuiiODO`;1Y*B&Iq?(6?Cp+1vR?SFPZWWe0u5cCK?ru1yGeQ06MBVL&ap14mLvLd zzk88XD>~tiC<0LgQm`J^MIL_CpCI0d91#W9Gq$x?RSr5YYL$p05N32{Ye%FRK%8&R zetH%Dv-NhixVp`8DHXV)yl}QKeYp>dJD?3EinYKBZX6%gh2g)pMu@|?QR?V3Wvg>p ztZC=0SH@kxz| zA`k}Cx3%K}#x-60wnom9wb`JWWwknItKqGQas0Iwir~@5*sc!;x^dYfia-eSN49oc z81Ko3T0itrhLrOhOTPtM4Nk}9Wx`cLOW5b^U70Af74L0C0g* zWZ18{xlp%gUc(NxsR&86m2)pR%B+mF)QDqX_n+mz&X3d$EDPs+WlMXa>vM5 zB+9FdX`;v(aj+Cms2h59zGxHmAQoxTG`#X|K0m=`mP7ML{ZI_mP1%>he^ma~rV>TY zLk19vw*}{9FS+(i$u%uCr?8gk!BC(6ny9^^Q8i3DjZ+_$+bk8)%HQ%92*18i;aTxY zC_ceJ7o-~&?K`v>U;JQqy(S=l&L?=}JAuj4FCV^go-P zVGOPabmkQpbY25@;cY?Yw#~+Y=!P~e>@Fsqj#w{u%??>Qg7qZ;y93&gp`e(-IR?*=D| zroO{|VUx>^_fKb~F>qeB=V8s7EeFRz!|!+Z5GUW4!XNjWK1k4#5t6`h>Hk?cz9Dd8 zvFKG|$UY0l7vHL4tjB;;ij3<*d9rqh-k|35a@DZJvdr(b1^X*O5ClOG1VIo4K@bGt a`S1_?-w(#D%&gP^0000(y$2dJ@dWlF#Cxy)5J^`jLfZP|rJOSd^6Xw`c%}gOo28wbD zH|VwL6y?@iodXPmC2e$?r46pnpGETJ3N5yDI@`G#ZUYqtR$I8jVJy z(P%y<5Jrso!kV7&VTuYc1!KkoaGhP-g%L6m1I@m$Oeo+AFP8Xk?1t%~34{&=!TR2= z?Z6GWk%3;6t!b}?8@y=3U4*bvycbk=8`-@ovw_4oehUu>U>fc)e1mRbf@ZQBDU*SQ z7^jKEZhi|8hSR%mii?q2r}W5iY~|}JLIzqbtYM#V$l)tl-TUq4^Zq8xzO+mVa0Bt` z`s6_t?;8>degH+3J*X3RPI@+P?rpDC;0`f6U#;`M#cLapW>g=b0gALPz>2ODfqU1C zBX|>J$+bndv1gS*5oH4*;*;wepaypkr(rhsNN8?>AY9l7VG*_XUc%Ndm0-N z5?1&E#v&Al!fN|r+;-oG)ueF&-^c*NHnMgKopzf;{v60aE@__I9n&$Jo5OArgX=2& z>U`LMTVgCiQ9#JIN0gw{IjG|?`oU(F7Hlpa#N|MrF(hb<_)8jx1GEf}ok<$^>}7Si z$SoK)jty;L8I=enQQF)yKj5jC1pqNxPoW&zc3(Lm4Y-ZN_jc% zTtU0$Ga&`J#30cs{A3-7siVZ*`6*UpKgR~QyMyS%#67aT3)*6r>6?l&MbFQ#*26M} zNVx&ly$-?+)3k>6sq+ic$WM>ZOgMro^oZ8x0K{Ih;GNN<;v_5p^kp>XG7ENI8dMUL z`cT?^Y7|XYOFAyMYCcrFNUJJi^Q;v2n32{`djj3U=DEEGl7%3oH>TRG8!VB7-!ezy=loxEUO<8ZXu8l9j zsPL1|H5Q@x`;J}bfAyzxR>cS#TNw8azZn=*b+)h6b|>ak%sI>M38tx9A$vRQ!96ji zw*?EBe6B18k_+vgR2t}M>!eFVh2uGs)cu0nio##%CbJmDvG3r8vAG`L&h$ zi^Cq39H!<;V-3d0K&7sZGb}tP8%XpGr`aAOQH48!Ct)9Y40O7R4&0O!5+qX=^NYb! z;Q~v&Ur=-^=G;Di;oqyh-d+pSK8wkvVnMyRe~DE7``g3YADKU(lI&G5@Bl{^EQizk zoljdfgfA9VIpSjWn=}Vz7K$==Lm`Lb>-L59)8tjlL}UXi?nh}wD1@I390js7!WuC(PX0_4n~*FWJGW`GYdpigBzoR(+M7^e); pXfzs)Mx)VaG#ZUYqtX0-`47rJo|lzDz=8k(002ovPDHLkV1k*a`$qr( literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_default.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_default.png new file mode 100644 index 0000000000000000000000000000000000000000..b4ca3d4c6b3b8bc3c334cc9add2ee6a3d1650ac8 GIT binary patch literal 838 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!oCO|{#S9FJ79h;%I?XTvD9BhG zK{9O>0P^pf2IEW#~_j1V|WZq+}J;5*{T*xA%#zxp{bm)$9h_(%Ye@|6Ex%k?O>dfE2X78PHY@d#caP6Lx${L*xZr!N@6DB+j zoIij0sy5GuJawG6tC#+qQ5Uw}QO9C|%}!3sNolwAW1HqKj>-4F{YtV!#7aRiuzmga zKWkHVhkV}j_v@6TquMzR?@mk3h)qdRSe17}bLljJ1sfP|7D#b~|N7^AU0}fk#+{uv z`Sd4M9+9}SU(V$4kvFN~$JLlb^cJj~_BHe7-@q65RunM0PFxtZXm{S{4u@85jgoT` zo1SkI?wVWrkZIDf02znf{gbD%aR9Y^{W_I-w=$oU)TMb}9G6U39r5VjuP7S>DdX>I zjK$mBBG@?jPF!nzaRe=26^D^q|4ULV=&}L^Bp<7tqTJu1kvYjH9JF zo}A)T`!K)u-QQo|KJ!;@n<<*;`f>z4+&cEEn|MApF;z$<0zs6> z7`nlAM$HJ5W`S#NmNa=7n<*X0K?3n?fhZ$f5gjMf<&r)hGOL^gVsV0iX80P?aT7zN zuB%8+0<}n-4J^_dh_n#)qktU1k{VEZ#9{b_j>rM=46?K57qt+()aH^t;u(b5^NCst z#(qIUTm*Vc?L|EHS0v|*Dj-Q{Vb(Fr8Y8f23B~`vQU@=sRP1Gp+-|xEs=iXQKNoca6S1gTAIXNEHXMVk7VD=2N+V@osDlclK@FEc2=zUf@JWUVc+~)R6 zR_;IEZs!@-RC4Uai(wW#=KNIThJR-@LL82bLTUhCZa&^_*w~$Yov?iJggvK*s3Q*A z&~z!*310&3h{3jnZA?MG)13An)DT_-g3XAs+9A-eplg)X$Wd~V>@M3n@FEc0e5TF7 zEaIH3zvp2?j&m~lEjY5~;FD^`Te4bIz2mK-AZ4^Mkk?BT<6x*K`_dCDAP1TMmNCML zYrExCh%{OCCJ+aU4V;)>zXWb=5d5e8jYen_(l5)-zOd%60AP2uzcx!Yx# zPA4aRYUOoR6gP~q6eg5ia_!iXYj39);wRXq+vjI`%Cb!m@dBgbeXqi0>D@?ouaZBe zny7d|$>BwNGP08-Qh~(Tyjka2RqSkZ$T$k^usgw&h~#$=+aUat#=`yz5RctOpeePK zeYL1O!~)sqxQ!x>U5>ywx%q;8>geFH=9O}ba1tn$JjQh7q_&V^a=NJqU`0_BMNt$*Q4~c{ c6y;^{7r17F;-*|9&;S4c07*qoM6N<$g4T-YD*ylh literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_bg.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..940223b483516f10023ed55ba50895aeb02ada88 GIT binary patch literal 996 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9FJK(?X*8o|0J?9Ffdnox;TbZFut98+xKyRK-+OQr6;;86MR`6 zSf#BRxNfXvSdrk&p2IqOErV6WiiUuLZCsD0Ylv$a8ec0+nUr8H{(JhN|DMM-S$;5> zpMGq5`^OY^m%u9$y>8<3p3i;t&9~qGYsul<-%jUq*YA73-)H&d=T&>}P1gOG;;i#x z^2sMr-*s(sPKvweZ(Eo5{q~87Xa}ti=9}Wfe}t{*IG1C#J39PG6w|TCANASnq(v{T zyP8$rdR;uyM23%fZ8iV8ZIf%Nq*i}cy7}O_?uDY_rD5?Bf3|Yyq(vF(eG*IK+AwKx z=~`tS%h?w$bbmXTG-KnRyfDF0}n}{Nclg_m6(|W?J(w z>aWnWxv|2pTx+-8m@&Pof$jf%gCD6EHdvoLI$54&N1GsrChP9WSGX?w&7VD8A-w7z zx902s=}#MvS#4=9VcXIg(ztiW9#6hUJP)qMTYlE@Fj}Cx#Oqn40C#}!btf&?kIiAkC<{caQh}1*V6x1Gg`7ZInE+ zt)DeQ@{-OE-6cXf-wKQ_#wo z$y>cn_-%-|mbLYG=>E@}{#{V3U$yhh#5*d|*Gq2C|7#cAU?{<($H&L_k0amz`q8X^ zYeOfmaox$pW1f4S&6eG%XruX=%{Si^g`SOHa=|?Dw|m*|Bl{+Jzj*aGa^eH&RwISp z)aG|<%wsq`JLHqTD(&@WGZjzGW#sg8IqjSDcNKr0|2MVD$p_SPbQFBO+B!E}>}Y%w szMtc6T0H-KlNYhPzaB4h^#9Cv@{+TcQS`KaV5Vg7boFyt=akR{0OJ{^D*ylh literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_default.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_default.png new file mode 100644 index 0000000000000000000000000000000000000000..49790468edfa846dd6aea981a54e186b7aabb700 GIT binary patch literal 1381 zcmV-r1)BPaP)Hqk;w0SWI3fD^341qdgIIYHnAn@U|4wNSh#XnKNrPq66+ zR#jsA^^FsPN$eyu@g$KtKM4_!JnS1^Rt#;Dm6<n0pQP$EZ+E!oDJWrHNr z0z$;MuN$BNYlzo&yM%O=Y~$tf79>%zL6zi%)uX>afvWA4;old7Dih#|V03ix19XKj z(w>V#Xj=tnjv7$j<4W5gUfYzs=H)flJA^<=0iLkh3pJkzD21oNi0Z$6cH0x*$$=Q_3U>@H%{o>SUVE&(&?X=(V2iSEvoR%~?{`Me zBYX{?z%6ZpJ$*S+C)sb<_vYcVo(NuxfMTzcF-g{vE7p=mR$jDsSii{V=c7eu%T8sq z2#74O(|jobT|eBqPFBA?+o@1LJpm3Qs(H8h7Q^O2ix=Z)`^U8QFoer#{h}a4>wwyM zR|@AvkANs$3ua9cqt{XT%lohmD?)o5^bEpkYD9~oML?VmObA05Z4vxU>xUV!4EL?U ziqIn*N?zUYZpy{hoLE6G_B;we`Q-lvsH7DbyEiz8kkBngBe7pX=N zk|?fAc|EQed;kq-b@MEwhKi7cEeeQg<2B_O9kuM}YLA12kRqb9c_$l0a4r6GTG0{d z1qwXWUCzOpZ-k)Y{j>Dt(~US8O-9?|^8@dgur}=s1VyQan2Ymm zkpP1RRJ_#YBEgdatf1G8HkhLxVAK68D7kXgoi0& zT!eQ)lHsJ3(vP`?*%KZwaZRZ))>NdI*m#tT>^)*_SVzXj52DFVuz4s+!qpd=!QD+bG7cATPgVIw~2;e)|o nFc=I5gTY`h7z_r3!Jx>0j#4#hUgAt`00000NkvXXu0mjf>i2nD literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_listening.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/agora_options_icon_rtt_subtitles_listening.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb8372dc796789c59ac15b8db6ea1925af5ac72 GIT binary patch literal 873 zcmV-v1D5=WP))*BCge+oszARjgqzLdQ);ya$c=gudq~<0%9ep zk^;NYV~$E9SPI$#l>ET)e1jReiffn^O_fBJ?z53U=K9Wz+d9TRD3{40G_(?wWEPW8 zOq^)D@7ZD*S=)VHV%&i%AsyoL4n?LA5|$Nxh7lNrp4ePZ5CUc;DKGF2EY?*7;Udf$ zGMr!w#B%GUB;Yd^Ye*d9^eXSM%oVg8^uu<{O%+^#&zOg!M3ymIYzD!KBRVX|Vihs? zidor;)Y8Aj?#xmL$F*aAdhaAb^8?Jo9cf=I{W@^chy0~P*A+SWOea*Xhs+)SYcz8y zg7QW$j44OtqwgwHyjSv|+Ml4z9xEOlb6#L#uAvCdj7fTX%mZLPGgq<@W9D!6W; z24Qc;{3AlwKC?74KX{*RhJ9iXVv>>zf4`wb!(73Q+eKZE>t*};Q8uz$Y zQpJoKCdTkU>nAMfd1ZZTw*6&X83X3dz-O>5v7KL5@BJ)iDEcacDe(Y4gZ0d^aEwWq zGBWHaqOeEC>J`{bgIyUSSH@Gvyf0+z348~eMOV}^HYMZI0dszV3Ax6up=nElf0A}v zoQT==ZwLXbSC(QBU6@tfjkt9=ZlM)G9%RtF4Q!^=y^FJHgkae$os<29k6FT!R`sG((D7)&mVfdvt&Usthe7TusoHbBpu@) z7v~lg%sreBXHi1fxdR8w*L<8KmfLG=6p&iR%(i9T3~ZiNcCm-0g5_?Ibtpjh$FOv; z%n`J8(8HF(xQ2F-uzoBi2!bF8f*=TjAhh!ju%*zuJWUEb00000NkvXXu0mjfOu>Ss literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_icon_search.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_icon_search.png new file mode 100644 index 0000000000000000000000000000000000000000..66ed7b5357394a5f500a87b471dbcc9997b96cdb GIT binary patch literal 1334 zcmV-61D81_-L4$n>NJvPRp7{O%prDL%{UN&JE=~O(E%bB=P(5Ta8!#;pT0qpG2rK93&s#7h zrXn4ar4xQF2MYsLrzJlT=|bV_4Gpgk0aO{523@Jws>hRz9822lZU)YV>~s{wb``w5 zl=`1MX`5@q? ztr3jci2A$mw`x6oNDA01lz5So5iecToHDF2v!9fm?$^>vfb&$p*_A=F!wH=en z4r}!bxY0twqglu%(cKt1C(fNmioOiPGix1-bk~N@-6la*1)1+%CNL}FL*Dt3sg%HG z*Jc~fCvDs!?eFcsa2+~CIu3t7t-z8H>>ZSe=#kaIhf|T4AKWN!u-`8n$V-9s-jWFm zuCFc|J0I@KW75vq`{@6!ubO=`>F;!Wali|EI;z%dr$?|N_=N{2E>och?)A(>^&0u6 zb6p%Qv0+#ty6ahf8Z)<^%#fB{b{+(cl|>%9Ng(Tqcd|MNb1}~@Y1c0Eg>jMQCzp-b zos#A1RECP?*ZhT!04R!3hZY5i8Xc`nnoM3d z&<5>QySX$N9ul7qf^A$m0`!-#PEZLx92Y5g1M!BEM(dv6gmr>W@ZmEjJ3VaZ9Q;dQ z4r>6zBXQ>V4!IORZMhYqS6NsCm}EHFrs*Fx4~H*Qo)7B)&fcvymev$RU`11$B|m^Q z06DTK(E^nb1If1QASfo#c7O4DE}@gER$|Nlg(EM6tyoA|9W+Eg@w)FP z+k3k#=<6OgH>{dU)E8 zt%qU{&dYuVNBS2-L}vMlYd+IB!wckmZwjuR|G(mz>0y$@cD_Oxo5#*i;DV3pSVb%~f;m>_&VsDgBh^y)WNc%wl+iYLlW* zw?EkZgL(bkXOTAHIDMMeXrR`|E-7{*vc?Y&yS3OHDE`8TZ5H7F?RE^Qr=u$Y!B;+k zemt`Lz@aTq5=t^Gx#ieOfP@GmDhoWxgBiSUDr4cB3OGyDC~~ z#OJ8k8uD_GCNrBsn2H!ak(g&(`7PMn|A7zW;XqS!Cenk5YvU}fu~Nm8@qvR+h7PVN sDR=ARl5lYRK5cEhM_f`Z-GPVy1GQoyu=jEKP5=M^07*qoM6N<$g19bn?f?J) literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_n.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_n.png new file mode 100644 index 0000000000000000000000000000000000000000..bf0b4984fc441ef948ebc6a7febbe31dc9a40263 GIT binary patch literal 546 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8oCO|{#S9FJ79h;%I?XTvD9BhG zpl*k z;Xv@B(4?j`NJse_w9MyY2PEn5g-c%*y$>i>FE5DV0BI@VL?bu+UZ2 zJl(KW(UT9p{c0?kVp6dBs$OgUjmdQm9ap_}elw9MaZl;kcp%Vo0F%Ic?iBzdM&afsA#}{I*0J1q2lvx|uh9>^}EW z@Tj16K}3LyUL^nTvxVm}WlF5)CI>CGS$f!1dal=&6&ofh-00l*@OAW*Baw&aonZM@ zZ@)TxAo=669m7|lrcId*oYEjcPgg&ebxsLQ06v215C8xG literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_y.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_y.png new file mode 100644 index 0000000000000000000000000000000000000000..e3197aa440f5c621a6a1ef0039a451290f6d9567 GIT binary patch literal 536 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8oCO|{#S9FJ79h;%I?XTvD9BhG zND4m>e07e%#J17M8S0*&@O*@aB26N(*ZlZT~8-WEIoVrZpM#VQ6rJSj162OudH|5PIkRE zb!zlp-|e%`K1{K3ircw(={uc(qx-ZzF|0P$FE@2u;xz4fb?dtg`nH9AS3hY9+^o%0 zTz;oHHFS<;=ar(!o&6Jn{~Zjn`l;>K?83N{y?drjqx$3Z%;7()KBc|X?0|!RTl@`; WcfE1i)@KNeNCr<=KbLh*2~7aGv*#25 literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_close.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_close.png new file mode 100644 index 0000000000000000000000000000000000000000..1a3574f2c7baa03c0931da4e974497cebd9d8b67 GIT binary patch literal 530 zcmV+t0`2{YP)=^%vot%|ZmL%ha-EN1m}XP_zsF&c z9dF;Ob=SHBVB*0~@Z;P8l$ZH<3Ia$RDLo!|zQE(2Q=cu56<&5_Nq~m_H6em=MO_Oj4?a&q{*eh%s z_01XL>q8aQmOnJB)Hv*l8}3TnhMN3u(;;>($rjK<02TSa=JDVjPz@lK@3H{lJim0` zGg|?Q0Yvg$S59pGUIIkRbC>)+0z}Ej?p3q{u;rzjEX@FHd3sou?p3q`@E^b(O!j5L zlD}Rp=v1Qgto{j${tg(hTRu%j?$g03UTZGhtNiEzhi@djn{!@$7=~dOhG7JJ0RhM# Ut&OT6;s5{u07*qoM6N<$f+ca?MgRZ+ literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_setting.png b/AgoraCloudScene/src/main/res/drawable-xxhdpi/fcr_online_edu_rtt_conversion_dialog_options_setting.png new file mode 100644 index 0000000000000000000000000000000000000000..3e8018b348ea73d17f79989ddc9a07d27006fad0 GIT binary patch literal 1355 zcmV-R1+@B!P)$_=h!X@qASf)7m2v>fCkULt;sn46Y))Wt0^tjwb%Gs{qzdH(=m>gA zib{5;)4f7wXEigsGE;epzACA-v-8tE)BSZ%3t){k{@*|di0!>-3zZu2qz!8af+>HW zU4OWI1A(9*AXH5aox%$cn*2GAqNo9Z5KMtR-hJNTW`(YeKmS%e2!vn?qzVtJ_Eidh zgg^*qLWnPnN2L$0uP#wO2)bi-rmawB6hr}=`FyG1-QxH7DvsIqr@7a`%&1=0L3Zk| zM7=_wr-Uk|G@btTF`nf6B#3>3&WPT{aXia6A;CET950^l&1kT+R$7aYbd-%r=*@T> zPr-vy8r|9N-WGS41(_k>gn-_&V&C&LkufQo&-kbKJm0q4jv@jz0&}7%A)K=i-?Ajj zB*5>VZ0Kjj&8V!v_FnfuDR>Jj;z?aUwJq9ecWzkmEm#4(S*O2VU7on!JAFWoQ`H`h zyxL+kf2hMk zo!Y0M8Gj~4C8=I>T7lJsL}RL}TXXVXtld5IrL-D+W(=)JKi@M*$Qki!F~eCGFz~%o$P)W z1*B6-Rqq3-)joaX0 zS71Md`sFV_#}~FSP8yZPn72Dc+N^;Z`Vz6Z;_5~mFn2fTF{|D9_*`HUUgC;Nc&`Ei zp*5xHsL;%bm^<%hL0>yAH4Pu1&j&B1ET?6+x6*8M{4pNzL}0$(cZ$ueoBG{Toc zEKn((MpyK;fc$-fT_qPNRbb9MX13`A7Ea3mr`fOqs!+FA0iiU~Y5=AX)357pA%%i% zryy;ZQXjl9_GsIM=U2 zX`vw0y>w^bWbW<`swrSe=6WHkX;q+sUYH`;lR0ZuLYzt0A)g0HWkT5CL>|OPQl%oL zI56>rM3X+V*{2VmE;J&WQa6;Oe=O#ZyLizGxX+PQR_6lk?n%B3!Vor@?+AkWSIcWCVm4shwL@kN5S#cZ?`9;Pa-UOT{V#9a99%=CYMr_`OQ> z06ZuyEoP}feSW>u8YeZa<7;zA`g{RrlfhSu23y&%SiP32-d7vci{sI!s4gdWm%x?= z#dis6aO&~+(qa`@%7!zp&TZyXj$(<@R(|{(1gTD!RiDeM*N;K4|Hv$~>jtOQ*B?eb z2!Me9k?FWGZx^prwfgF}}M_)$E)e-c?47??MDx;TbZFuuKI>nH6f!S+iY;26nPayk%+(FkRB5$fboxSCeApPfX&Ad0i zYL^^UIJ3@V{qy#j{0d5_K$I=|K;2=%RTbRbv;NFDQJNI@Wc|UFY#+Wa2)pmOS~j!E zwlajb|N82X%gbK!PEP%Q;9rAHJkyM;lLY((W<)VOKDM66#`I6FGpAylTFalw@h6#g z_%{^_%op@vQN8pdxAj@xXxoHc*->8o4s8=kl?-w?%c=?D9T&D%f7 zis>etbTj?=cio+WtuGZsm;=5hoIfRf@quhuxytqMpTAvgkI!Dj_Q9ar>{|Hr)vAg+ z&g_0IN86{+)W_?7*dE9=&fffE$=2G~KMXo1W`FX$&z!Dl z3J+{!*yG<*?o!ojzpwUU?}fQN7o~nZ+xafi@gcLp`T|XTUWe44JcmflX$#ZjZ7VC8 z_iImmr*^JalzBtc;X^U&Ha^KZxqY{hif!2qkA|~z#Ixsh#5#3u{ZVxP{U?QQPwSbx zo!40FXzE!nU&&f9cNy>8xPn($;as@UouIMi6vpJS4UcHjn^u(<6 zLgks?Pk+yimS4Ipx%aNIoY~7BQ`^nSvzGf#cIX79kMgYx@0Dw}3pv@yHf8_Ok>>6B zfA?ZyNN>_wfh*G%Uiz`l!fUBn$(_~jq+-)^7jHI=E#AxJW%9jAB-{Dy8_pRQ*SK!Y zQqNnuhFAB}=Tni=Q3^Z?25bTn%p5(8EXNp_k{KEe8xBY~Bwz?nU1oMAY4cN+eHv^6 zn%CNH&Ab(`IHJ4D^89C4T?Xd8w=SIMNy~Cxe8t41s&RMvlu)s2i`IGTGK6nUFz9+3 z-M@9=w)gKW*Xum}((I{~DRO%G)zhD24o6qc+r50Zc?TNEVExa~@j^B?Oxp^Wml+s5 MUHx3vIVCg!08U%jQ~&?~ literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_conversion_active.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_conversion_active.png new file mode 100644 index 0000000000000000000000000000000000000000..2fc70841fa6822909c748d907e8066669fd989be GIT binary patch literal 1716 zcmV;l221&gP)@~0drDELIAGL9O(c600d`2O+f$vv5yP0uKn0jf0f7pT?+PGPaB>CU0xp;epn(fc1qT(N>wpvhoBzmzpQ2~4 zSK8G_@ciD>v#_PLc4l|yYYma2D2k#eilQirq9}@@D2k#eilQirqA1E+B%%%&rzVN< zLNO)y^BoPF|D%W=pP%VrF^}%4Wm;1Jgg%O6IwM0Vz9pewlN`#({{1Jqrj}?$0U!S| z$HveFP7`_?w5Eud0`te8=5Gl9hTe|wnFM{BbKfX1L1%zGU`B77?CY0kN`VNbfE^M3 zE9X$cB?Tae0tzoDrbpTxo?c$)0A5l~o&>Q0Y@lej80|%LMFH@h0XsPQS6~+Wj#=+ON6!bkMPrMZX_v{nxDCql`Jn>dQ^y@~s-k|Rv$dlj5pM+Wfqw5JBT;TKx z=ihN&UTZ(#y?s~Zb6d2GX7AUu^^Qk!!-15LCm|FN3=z~dUTEEfU9aM3Vjiob7ws%?OzQgSXzLDbjqBWl zfXqvVz0Uw1^*BykM(H}x{TW^^N^hOtT{VkR;nmDS7}h%UssMC0$8qEtQLbjp3t`lM ziRRQ0)By50czV)K$6xm|GV*isfQ_-7p8E)&+1u;hqw7-vQ{RdcN8wn4I1ZVINRCMj zqf>fWf_Uk{I-QXR5dIj;#RaR@3$FoY0GJZzXpPsCHL^C*goa2aft+e2_NVKCm;_)q+$A4f*x!i zN^Kz_H@?|EM39)0gYYGAo7!R#QN+C*xBMP2k>p9|3c!+K_k1Gv>?)L%+dhX-Pi=9k z#37TWK_KqCB~SWrLpP=x3b&GtVt}>^EXGw*9|ui^ABsMfAg>BiAf}t#VDXj?iwtSW z+X7Ew8y-WEQ;uKYc#3ZK0j80!lbe4R`vD}(Rhd&NU) zL?1KY(DosW^d%cd3eXm2)SNvWJ{NG&An6C*Lb=vZbDqC^ko7@mA4<4sG86hR!XsWgxA@#SUf$!n zlv5E9V6|f$-4=qAoe7zxEf#uOeINI6^($NoVyOgf^Ss_|o;0e6!8m@siVnyF$%OK{ zJ;Ji}zD^}gJe@n+HPun?+D|;oQ61N|pL8@v3p(Vp?k+thUaF`V&9!!^MNxaT-@Te` z_gN2M@4;&cD3+?Fr*rJ8Q2x%kb>BL$cT~qc?X@DtMzE>(@Ct7=k<*Lu#GE=@pHj=9 zUt{NMsb7*JsPRi`tqe4bpv#Xayf;&A0}=C~>$QC=fEgLg1PJb`;cPgo{GD0b>Y(^y zYNV+LvoA=4`cjuTSgbfIimTi%w z{6~#EQ)43IE|U;5fXjm3rMb4ia>hn?B@oOee%|r6`+RKM%a*ch8SfbYgE^-GQ`$LK zfahIM%W%QQ^;&NQNcM6;1Esoso%1wpJ};J^MV)F{kT`$~+h!Q0#&wLUsR7@ebT*!A zwjD$PMPlrwy-xX3O_0efaIU<$!fnvF|PY}ZK$C5Z4(n*3%scrJ*Q08eAmg}+^sM5*^dYEFUxGnJ}KCwv`ky``EuFf zT@}lpx_;wWqVFAbilQirq9}@@D2k#eilQirq9}@@D2k#e!{!IBh^>VP^D`v?0000< KMNUMnLSTZGBqKlo literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_conversion_default.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_conversion_default.png new file mode 100644 index 0000000000000000000000000000000000000000..cbdd6dbc0f271785e7e50e3e4ccbdff4d73923b9 GIT binary patch literal 2147 zcmV-p2%PtcP)@~0drDELIAGL9O(c600d`2O+f$vv5yPN|k6tg}Ug1u#383LJ|Z7smT)%o*-jhAkGUAv+Itq?21GpDKrV0 ztjG?tt4alC*QF~ph-U0LJ!fJ|jy;|a-}y+Q`;#nxjjzY|+K@mH@0YLSm`;!We zL)h$fsl5GLwF*gm8zjJ;Icu!x;KzXnAT6T{Yg?P?M-@q6-m}IFh!%Iwg8_#$iX(L6 z``GWig7iihKjmqcpVf`UWEN* z299{%fg&PhjQ+>^5wY(r;w5tFa_2pRpk{mpwWv{uv^8?T=UIU9-nRH$>xFadX+qlK z-&yZnFq6pi6N!R);izG#g~{uvCO+U!p92R>w=+NKsL>8CKXe^vOwE?9AXhiVk_Z~O zC~eWGe_O<mutU-q4Y5*C%EeCg%d@0hU&cm5|kDe0C-!Lqcy?~gj0h*5IBE~?0&Vnx^U#asw z=o4g2Zfk%i-T{n*0+Meu3D#!rSqtKJK#`^k%5B6Y7Xx?;r@Cxi6P@YYIgqGk3DjtC zXgZ%q=AMfS8YJHJLA1ult@B=Tz58Vzsg1T^1~U4iZF!Q-N#nDiw(=lz100EW17%*1 zSVCU-=x^{sJ-_{;YQqU6a|5&vIou2w{&6~gX3iQXCwTNXiQ7s*4tG9tczS_q1%tnj zaE>FUn}{bg6Ar@Vg*VXs#86RNoZ$v7VjLB%Cgon*vU9C{^Kymh`<4lcWWET830SQI zT8F4@QSb=l5t^zZn}l1-BMKUr>`3`XoDB{PNt~Mh!inFB%iIM?r==lkC57lnU>G;~ zL`|7tT@jY&f_RO3XE?N)RMlMOGeBhBf$wovY?@YNIeDlrw6mVCB zIgnDyY9P1fNk?dWN8=USJFJRM}L^Xtm(F>nSY`Da^#{%CU3rUNP!Ji=|F|dd6 z;zel_%5grFix$M$X}O4AtxX`}LU@y8q4#y!M&uo@dSQenzW`K=4#-Bz;L(T2%Sw(G zPj&UQAF|lxt;fj^a#2GiJ0+wipn%L0aaCTmDbmA%$FYm*>yU!7dKz$I@!HO9KVauY zRYssvEG84IMFB@Kr+$ZJz4hYW1vX8b8Sc%zw=Fj$dbD98o;BoFO+1x zCsYK>i!cE%AtHy@>ebQY?ji6V>2XTacP%TaKe8nX(H@TwGwd)w-yZ!dqF0Bj&wvrn zXZy5_OT#LJsbVDW*80AJH*6vw3yMe>3I4u1vZ|RSZCS#G^swXXmL-x0a7HUyOy9!O zrD{H%nXyujzaxXR3~3Rw?ZkFxZ_Qek0k{%!Sf{ijQ+SQ4Z*4q2CfH5gx2h~tEJA{? z?|-thxe`ldO>}#lMW{+#H83-fCc9P9_Wu%O8#1YxS!*89WZb{4CVH)i4}b34SAUpb zqed5_vVv!UdF9)QXZ8C27kOU)WB<#Q?0;ua+W47`ry{ zNu&hM2Zr9PPJxm!YMlG<{>`h-3-!PD8&`gu_?45h^K76A7y@xSt3a8-Q;sAIs&kL+@l*SxR$FT{8jVJy(P%UpjYgx1 literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_default.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_default.png new file mode 100644 index 0000000000000000000000000000000000000000..6850594054d21770655f8b38c43b5cd9ecd5e713 GIT binary patch literal 1032 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9EO-XP4l)OOlRpde#$ zkh>GZx^prwfgF}}M_)$E)e-c?47?|gJx;TbZFuuLz7$oc{!uH_1TELFU%15MA z96BAvs+gX#Oj>GP#B`W*k-CQs<3pBB8zhuZJ>Wbc7_nmm$ISHaOI9>-s0jaBI`icJ zT;r*2|I*{itK!p3qC+(Zrl(tr?PSKw!=#b;3BD|Qxof>rvzmFv$^RA)H*NCVQhdd_^YZem z%v%{+e#mATs^(r&b-J{qq)FX4b%9gkoSQ|a?-i~v1(-fOxoPqV#=XneyonM#as7qG zyf05ICZA_e6A1p7QI_hpMDnZOxkYhvY>)JCi8sVn-1Pmaef#Uf>i5_8rA2DRFa+!D znYko;W~6*ym3`>7El#W#q_4g+(oEpIRVZV9|Ej1OL$unYC)4C(*3D@BfBydI&fV+R za0xq1%YD9PTIEws%difHw0mV~Dn@^N`$Ux(I8)|tF)TOX`%ol*Amg^@x$MuSN$2OE z;ndPRQOodc#>^?5C(_qGetn1C!0K`M)%wur{7IAkuutgt-1>3X0h#d0i|(*bh~cx& z?7gN?5)oP#KH)dRwrys9os+GW>W9k+ubaHdDaYA-_4(aXK1dgLz06y_Xvs$ zR_ok3=ofZYZyKk)@%shxQD(oQZl5dcTCehQ%9I%e{O^99VcQrPeOu6LTjsG%OLhV^ zg*UauT(97IQ@-iLyCtu4B3Ff8pEpxo>GiJ*_iJ|k>wM;-%TUcevymkiFX zYgzuLW5>7hk~hB$aw9L@E9;ByIJQQ6vGeXFrRj%(L2K^xb?*6I#Z1PT3U5w-pRJ#N z=JMB+*6oK4=DdEEsK~rS$o2U8_9IV)rt-;quU0xU^Of+0tgWZ_=xj6J?4-Br$&6h^ zg%>kS?7qk!uFzY-I>GN)kkI7c-;NYZ#MZ+OoNvpkSw`oP0j(Z*oo%rH-p;k-aYI>!NH z3}F-I8QInC?H}g^@HBj_a`xo=)Bn6JuKbI_+mj{r;kU$&9!$9Yy<*)8)(aU|ej9N( vC7JJC_|j#9%&DhuWw}1f@grv$5c}O{{^#K(dHjBt_Jg>du6{1-oD!M<`QXfA literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_active.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_active.png new file mode 100644 index 0000000000000000000000000000000000000000..d2f5c1d33591ec3bedc6628000c2223cd919d45a GIT binary patch literal 1357 zcmV-T1+w~yP)@~0drDELIAGL9O(c600d`2O+f$vv5yP)siXi$6_~7mQUNik3gi?Ji>@GY1wuMt3J}kK>61q0 zhyaK^;6P&E42F<^SrPYd?{@bN0t~}248t%C!!QiPFbu;m48t%C!!QiPFb4g3JLjL7`s^juOle0JR77u=hVgFZh{Wu!I7& z2e9!^(Np;BBPdY|!0_x1dJ3O?2qkI(lKstc)$rM0Ldj3ilc53FyOw;w1y7ee|DWei z$J&?t-Emd%#TGwqd4K+ltryahYj&gzN7rg&n!EK*t z>$1$2z&vKmkKmbZ8R{sAU)&)8v~tN~ylYf(a^~(fdoS%Qc%1DTr}J5)gtAjm!GuiJ zhS5DhdOeS~ReI@jh{y0_`s7#VH?w+Ks(4G=QC(C4*zhtYpC~iRM~_*7z5Zh|M}1T= zeI0^VLwCXV{2U5oGWMtE%ErghQTq{HOaUg^6(`BUIRr@_G7nK4^J^Hb5@iUIp@-x2 z7z)_u{@IBHM1bU2ICMC886zQ7Bu*_BrS{J})w_Dk4{30mk zZP_I5ZAI(hWyF|-zZPBeYYQ2w@ulb@6RQzA-1tK40&vJEj(n2p)m5qO6kVr>6qPAD zK@1Prn(VB@N|_V`*jkfxyY~y75}=R75Fp!jK_*nUn?S|h;?VfkLM^Qekfy~GEQg>= zRK&SrTfE-bmcmfP@PJ>7E^?B-kX!))Y|*3UQkq7Gi6KB?xmVT$q(WLn1V%x}GI1B( zBL%-MdqR{28TRs%D)oOh?np`z{0p07y=1o!5zd5#xN!wrvg|m*Guz`IvgA5w7i8kB zh;efKJWF0dfv<$#@EB0gUo`u3eD*Ds(47}!v;xE_N3-`*xIdv_9WlvJh4sX)+NCD8 z*pP2y%oj1&1E%bGszhmB2l_s~pK1jaC}iabEEG{!#Q4D1Ws_(b8~`3-{ps+`sY(y` z_wDBmQ3W6}_Wt{tb$`_7ae_`0^-B~PS|+vt!oblJ*l@F&-td0o=W$(IK1ynp&g={F zSAMb9%Wt>e-~xt*U!3gd|!Cu4JQt_Ko;$wDm8` zWZ~sF-IIS=h86M^)^BR&Fbu;m48t%C!!QiPFbu;m48t%C!!QiPFlz7(!RF-c`B8F9 P00000NkvXXu0mjfoMUUp literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_bg.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..1128b44e41859d27b27f34acfa53b7683a1cb824 GIT binary patch literal 1324 zcmdUv>o?nX0LH&TToT!FTr-!lo>pCoSd|b`DH%q{LD8Zb?V5uct+YBqv&OBXhb|lK zm`k(j$Vt*>(ypqL-y)5&RtRCJa1`b#YRGxp+np7VKLKj*nZO^m^yoKXM( zOk8YqvN2g7WNB`6DUmj646EYUqa^_BkN+UVt%&9xV~Hq9jyVhj&s?O(m5Fo}rz^%JOqBMh}?XJIRJRuXMSYk= znrb&&9j3OBQ(?n3Emf_WckzdXW%h$bJ_e!|Eoz)?X37+hu(rcbFqF-y0EFw<>wJG41)W3e1B28$0_%gqI;H zMa%o?TH(BYxdSY|R{vnQoKAjo^Xkn9-W~<|%6B1@UN6WX$=jHRhO#UqPRyn0pq}2x zN`KrMIHnuFzGkxZks{ck1Ilu`ejYH_Rg;FJduE*QT6Ua;hOSGH(IYry|OX&Th`}6mKWfE^KBO5u*&6>L~k497P4OTMxzIKuF0r81;uK#V$<<`$s;! zG~kT50OX#1X_Y7IQtM436A>P_K~oAC_69HQ!8sHNMY7udAN2~Nj*=nU#Tu$QVrx#a zX}!~rj^U2~Xmv|_k-6-)__!(=TT_u^8twVrM3CN12NmN7WQrh~?R36a`Gg3*)kv#Q z-G*T~u3Y=P3gy3D;Lcv%hr_^-0!PE;?Ns9CP>=nfbURdre)F_kY06;5D+L~Gv+_fO z-2tZ&_|VX}No4O>L6&fOd3Ei{-g58n5e$znE;R>$q#n(r9g&ZaVUhe1i! zg&dz7eE46KyCoE^9S?Fyy&_bHk#oGPBUPS8lFO8;Lt@P}Twi_sKaQ({Mak00pyrtW zcG;bm?+bC*EE-S8egcuS!n3bRI|%eV;ObaiVf|shOC#$2gr3P1NJlCPr+8XG1xLDd zg10A^7CvVZ1OF_|X%XuOzF%-+xz9_Bzw9T^#~kOBWNp!T%#@ykl9UW=@Lk;7r#HKw z5bK!g)8qVw5>*+!X=nF~BO}2Lxtx|yAH625*}vdVA=U?6JS^!m^o>k%1aZAM)#CaB8QMl>0ZQ7P%Y-AB2DwjT?tMY9 zifk`6KjVELNA9z!o8G|I)p%o*WE=R~?=P;XXCW+~aKGi9Qw{4Iaz{AsXXCknIC5gN J;P8pd{{c|HO1c05 literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_default.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_default.png new file mode 100644 index 0000000000000000000000000000000000000000..63d6605cf2dcb4364de3892741b18b79297efcc1 GIT binary patch literal 1835 zcmV+`2h{k9P)@~0drDELIAGL9O(c600d`2O+f$vv5yPZuJFBiK#f?{zJP+7FG$arr~>;rXw;CF-t4<790N=zs>Hsl5AXZ>jK`2D+gvdhBh{kULQ1kpXt>6L-yLvq;AHJ^D zAc}8;2zXGm^KBPD4m<&A8$D`gzbh=Rh3y^uk)**K2}oYKn9(e3?4tqF!?Ap%?% zD?y$lf@TM3&Dp&ghVSB>Pm~g;&y|dY@323M%khMJ&aZdD5aTOg(Cqh(@6lan-+Y!N ziPSLJ4E_cIVX1veHyo#C6izKLhV;5yz6SSrH8jVrD8=z&QA-smq3<$rd#C3~hd~yZ>HV-K_Gwwh6|^ldA=w0Wp%O z17!AgTzqE~OOdX3-!89gJ|~vHVNxi50Wnbrv|T?$%ht!DGhMp|lGSX187)pt*OI8*6A3|s z#JfI-8hqX>-b-#?{+MTK>q0OCnf;?}d6L6LYPVG;S`klD=uK=2L`2>hpiu59reF&>9U}M2EogvM+61IMhgbh@#JDToQGFh;E&Ex=o0a$6mre zyKfJ7t0n2gh6&*-stGZZcw35OMMpbN);yy1Nv;N#u0QM3XWhRt=~(Q5I>bzB`hwo> z)#|bjAjV@;jIuVgJd4(Yq-z=LXs`P1#aIrE0>X9@{j5_-Luo?6?ZTLK`4LS>v>f#O z4COjrZYShW;r<1UValmEBC4@E2)Sv@R$~W9CEB|u%P~C~g!vUP zHz0GcaBEamGtFG|Fa>H&(i8@U7&}1HNC=LvBdeNeg=hZ4px#w~!+$>|cS&+ZyB>5S z+htYLI`f{!@$ic%klR$VXg#KocE?T!CNB6c6WJ{X_f~D&0;~=>O|F(SJ3%H;9)>Ba zTr(W*R4Whxk>2ycYGQ?RP}4rmbQ7WRg-D+j@!|Jhotxj?VW(mNW|4@EFGT*d6mdfS z)}0f1<^tCPGtq+L>q;zZ9PQPfLBveqwzL1Z$N4i?kXv|FAJ`;SzC|zEpP*)XNBqF= zfEbXhg7aPZ$+K*c@<*1ziLVdMvx3pM`gi&(ilQirq9}@@D2k#eilQirq9}@@D2k#a Z!@m>I+|KT3yXF7@002ovPDHLkV1i)BWi0>z literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_listening.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/agora_options_icon_rtt_subtitles_listening.png new file mode 100644 index 0000000000000000000000000000000000000000..9647c68a75a9144fbb5370d05eaa4bb1fbeebf95 GIT binary patch literal 1168 zcmV;B1aJF^P)@~0drDELIAGL9O(c600d`2O+f$vv5yPF<` zGmnTEU%S-fgRIZpqVzZ>>!Y*C`aA8LuW5=HkUBgIzEm_MAEdyuZJrF=AYSZ@m@PoH)0#aMoyEZ?^X>;uNS5(wX;R)+X2eSHPyjJ|_X$3}tJNah`bZj9D|TkPYS7bQ6)S zTlCq4Zr09g`Zui8XyOYUo^RfK#Rtjy!ktI zNFNIJEm1KtoK>As7~_XcRI$|KTDy%WPRNCD%Ddc2fvGo`IJgMNEtoiWuX;rwu2wWA zz@)1Ixll2_apr|`MQuT21AHY-1!Na(DLHV%I<4UK0&G>xCsd1wovr0m15Cj^7{FK4 zek9}zW=*Z_jdH)mZyGC%vaS;Hp{iO=6+jOK{$61$GI_E9-tsF|!e9&YyBw_0-dCdG z$GJzT1+MPkZ|gj@4%W~5G4#RQLthH^vnNH>cx|vQXRbZbf#^%Y>%=@nle&TLYuBzo zG;Uxsx;OW?QcV_26G=k8vEQB~C?_6o33;w{N|8_o4f+~Tj#kE1*l82gNU@##G*aWX zfK4_wRedR@k{Y)O>_f{u=q@YB7M^m)S2^no@XamLbq{X)Um7K#oyO}@YK*b>1IO^T z^IlsS68ruO_@{kN4FMl*lH{{K!At)y!$=2A>)0R?(=pKgB7xCiFRl{w~9qr%K z)M?92KvLr{*t5MKQ#%%_3;6c_O2_jEAB-vzEGX9+fNCA~Tl*9ZK!G7UGrHM$#e z1ODd^3Jk=02lbOOu<5LMFsXpZlI-!)wuvv4{(jfTLMsx=Al}VWg$okmG*ohdgufO% zU_-AWp;+UCjqe_0wQTS|>t%q=>P(hqz5-?xX^!trBe!WFS!sXU>DK@RK@bE%5ClOG i1VIo4K@bE%=$XGA)rBdSb1zf?0000@~0drDELIAGL9O(c600d`2O+f$vv5yP zTU8UDId#{;3v9m%f;;hOhri&(|>Bi2z=YBkQ=FH5Q88h_= zx!flMVzwtlc!-2XhZcz`w)I}&sOWV`9 z>_o720G1N|7G#Hu%gc8v)FpIVH69ik(`Jtuk)OploD zY~h!Dkq(JN5x#Wk$`Hq7{>X?nK<-MF=MSUmF=`W;+?8pL(M^y|(XoL?@eG$z>EXXN z9+t~(o!RQZz##3Y`d*`Up_W*v%z;JT5;9xCZaaX#0}@zn-A1Tw$gSei-Pu-0w?x>H z^R9PbYAPq3eWPncaYE^$ECrp>{ID%_Tvl~c~zNNzAY@> zi8KmFh4u9{N+!n;&@g;Hy%#=;&!$tame$w**xcQ-Pr_pCH~w2lzOZ<=CzklIP)U=q zx7Aa4h9D#`Gxa<62o@h5=eoHF z?N%lOT)Oi%op4|kr(85a5T>Qjg4nP|U{1mu=ZyDC z_GGH^K_KoHjpKdCg?C&K6{xh>tZ$$(ra*lF=&f$IJ#(3b$jxI8*16RO;g^v-LbU7D zCqOu?YCY#!S~|5hBG$XcWr_8LboanWC9wExkuE|e4E&fkFNHbLahC8Xfn?^Ak7`d%{c_ibtH*mxLt&LU= z;cUn^!BG*Qz64B`@7cjsB^)|e5tW>yK7<5QH`EZT^@f#}aG%9ITq}R{5fZ`d<#57c znqjVbGZ|ub{OjZs2r=8b`D@nJt{S$dybA=e zbg^I5I*~}=d65OM>|NVyvy2HA2vYKl(3AT@tuJ1DpO9$8G~~-+$Jx-zlN1k|mC&mQ z>b14h$Vmsst!_SbhSLdBxdCC+bA1GU`S{?~Z3GqS_W`soPQ)Y>Oy zwbnBybuR1NJ)cVBn@sr@LaNTYY399H8dCOVCn4o&d<@bFL@;;Fxd`bBRjc#6|LhSi z6c$PmQr3o+`An|$wu_^{`1m_0{y~?c3xJjShPHjHr6?)msQTn-lr8A^k#J2gFP?cx z&87YT*cWb#5~V`Ghu3%Q)*HwecIGV&F^ zKcjVAJl}TRFmY+-#r)0sa~syf@MSaJ!jv92F;wel^tdE_=FAzgFu9htzW}2fu+7G8 zE|8XI>V|3oaQ@)=4J{9l+*T?%_P4+0N~iIfK0pfi_hfpsvax=2wej)saZ0A&1V7jE z_`MRXKMvAQ)xQ0M^yMrpb3;HC+T#Jz_tP(rYJ~L$h2u1A{gh6TRC;tZd47y~tKtc# z#{W+|uWO-cFuMt|iLjA1(!UYgufl}k?X^s&F5DNAck%TF^1{98v*7(8^~&($jfdTr zwp!u~<8P=i2Zii+RBLm^ga7#VRQMV?tofXu?GB8Juj)zvaEICyz3O$d(@FHex71u( zBdziFD76g9gK$xopRW9&buQMbrR*h8gPUWg=4Dh=SiFO9R@)Ttf98NhLxF2NjcY!Q z7xSlxNXUT4RCRv3?7Dxb4P+;;%;J*Qh}t_3qst(?8B{a0eb~7@jrN}n+J})pLrST| z2G)9L7ULG&&jY-zIr2SMjWq64Zo4Bslewymo*K6vZ<0?y!tl4n#f95-Es3hWUeemA z)^ literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_n.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_options_arrow_n.png new file mode 100644 index 0000000000000000000000000000000000000000..4b3a7c1af9301e7bb3f397fb19c785d072ed2ed0 GIT binary patch literal 630 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)oCO|{#S9EO-XP4l)OOlRpde#$ zkh>GZx^prwfgF}}M_)$E)e-c?47?`*{T^vIy7~kIV_GSu{IQH@T8H>v|QhD0m zndbzCbc;=D7RZhIdNK9~XV)et)kUprUX`Z>qg+DPpW7*UK3{c(fz6zU`)4RJ0ZoR3 zAI%EhqFk*%ee||Zsd;BQd;L_erM_aD@(!d}l;{1NBGa|6&bZ=DdDQi7ye}uYPWm|K z=LVG)j{Wu8MTfp$HxFC1+->niRqqJn2N^c`CiiwqSlygor|eOYl&5HQ;{BCXS6?;g zJeZ(*C*kFn?H}zr9qn_zAK{EV)Fm8Yv)@MH;hT^D6^o*G{|>3%QLT8mhd)9m?#ap0 z{*0oF&u;lGkK3O=(b0{)=dp!~eYIP#P@LMWdloh8uIAl4A&|_hc64ciq2D2g%f;<8 z->o`nBrQ21I7aVss##ECUg1ukX_3D<=j_=LlXhxN=>d_WPcOx#vb=fa&+rbV7?*wuA<>83N6 z*ssLI=%uDkvs%#ksA#3u<||yYwe+TU8F^bbKI=ZMy8TQL+m;nOVlJJU^Gi(5{qXlS zl_w6XGX1ycTkqth`L)4M;8~^^Z@b*j_qV4^GVM1_5B(*4BilZxKH=GZx^prwfgF}}M_)$E)e-c@Na$h}N978G?-`+CxV{#NZ_HjG2NZ2K5(R_zw zmA0gd-4VA=^7hP~Tg9}4(Y+v-r?a(!=|YCTDyzZcb+aUdd-HG3tla$Ux9JRymOVcA z=BFqy0j+?8AI>g~Sz8~qtqYxX^M2Mo8Q%7jEleVJ7&afAnRUh6KI+d~`L-y&W&50_5B}93#r*1*!%6F=dS`k%cZ;3@8#t9 z&y!c@SMexefA{XV@6t>iJf8+>QFR%>NB9K8~{gzaYaM1hIIGZycN zNlF#7Y_#k;ts0j0UhrgWjNU?{YS{;?jy|2Fv3a{}#=;#jai`y;vPWs@O}}nrZQsb( zb^7VjGw1vlG(Re;)ZBcz!$Mncdb^Re^n%xqigxO4o-F%fc~YW08(J7TV#&e2tR1Qdu#(`M#bx+BKybW8<%-JQp)45#)_H4?wq_)@iE z`l*%cLP}g%S&P?3hPAS<*!oi1$T6nq`NZ018Hsu;6}0a&+t;7Jq$<2N*fV4CdX@ao f@YsMc9?Y+1Ry=a`Fpp(j9Z1a6)z4*}Q$iB}J3#o% literal 0 HcmV?d00001 diff --git a/AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_options_close.png b/AgoraCloudScene/src/main/res/drawable-xxxhdpi/fcr_online_edu_rtt_conversion_dialog_options_close.png new file mode 100644 index 0000000000000000000000000000000000000000..7d7d2ed57607dc058d35fbc143b8140b78d94367 GIT binary patch literal 663 zcmV;I0%-k-P)j{00009a7bBm001mY z001mY0i`{bsQ>@~0drDELIAGL9O(c600d`2O+f$vv5yPZ(gtRaB{!rCuO6C^^n&N=5k8U&_JCu_$h?iRaRi-=pN$A<@? z=*O==U5w!$KmvGo-y04GIV>pwrsv>U@#_Z0xZnAm{RJhoZ#u@)@nRO6ja^`1hcAmE z>1z}~`=otO91Fr4F!0Yj#+|8c!UfC$0@E{J^09!o6>5DBj}k82me4o}~m6>nzh`MK1PGOvL3^xOH()x`*w#9!gX_`keK zdO91P4EnQHf*9g9FtEcHgV^oF-%4ZY%mNT|U|?VNtuO7PqQZ^hjX)ys71BI(3#_Lp x-RIwnM^GcY3%&6;zXj)u&C<@~0drDELIAGL9O(c600d`2O+f$vv5yPg`XQJN>*wXFp-M)K&e($|Cz!+nUF~GJ$B&=@6e0lLyei2TbhEPS928=?2q8!)0Q}~CHqQ_Ux1azN zA^;HLJMacp30^|Wg4_kLuu!c4!Zc*S+XJ}*IimzQBa&i8djVnd$VY)U1c;+=yr(($ zAxGry1m~_UwUZhk!kxzB+Tb_yi%V+=aCdnA>5@7dhT%Tsgj`L)r2axio5|5C)UA=X z8F&kT9TSH&m$63me-FP7f7NV+A?af$G1k$6@-t;|nqDR_w|M;)0()=xasJ+Kxl{O>mhQ&Mde@Hr8O^qrD zcXoc?c5ZXU_^+nnrSw}j_JDIX?kOyoac;-^K|k7u{s~a=IPaJ*ft+O zkWw2kI?8Xl^0v#sz-7?n^vo7`QrSU}-`JNRe%xu)zfT*x_jji`d4B3IdNmao<^>+0 z7gm`(H}%urUdO5hsTx3z>8@TSG30-|IqL#Mc+Qk?rrFqD14lHUhMPN2>NB*7b^FqM z@7{B0E|;+sJb)BfeL(E^Cdeu2V)=kA$JyQly)l#(0eFo<*EAq@==b$Fyp4c(?Le#v zDX#IF^wmqHL@Q7#?BBmaQQ_lAydXL`Jv=`GReNrE2CD#Ouhce$5I2)oaD&v@$;OI; zC4#he8rzpl^Nc3IlGkpY-)t9i<>y(@(=@FqRA#@qe$zD1C;&APRPLD1a7`2#Lx>DJztu+=CMW-aOM~{Z--~ z2Ik4I@_;xdU*Kp&^m7;jD(9wV%CBFwe}N2O4f=HQ;vw7K+62x|=*JKG zt4I#8zHFQCWYFel&`VTy8{6yPh!54_2Z>yP_=1SkdKyz4#AQmirIdR0CTD$m0xzY} zVP}svw^*y25}dSH+lP%;#Gr(Sgiv!4f^{v9R1rF@<6IB;w}i`7s_p2m1t6UA1ST}r zL??Ez>Fh0Di=&igEoO(Vn`bx+fSsZ^z?zo&Iy5_sydt@nQ#BYP)Q&;rzIldMMTi$B zn`uD?n0ZAKZzH${88KLtaX~SCffEcb0mQXHPINNBNUSsv2k;Uy;%mwYe8xKqfbG)g zbp`&DHop~zBP~}y3JQllHav^~_)fJS|#2u`)0EWFHWZ(QF+Psx#7X?#4AqM#r zDv__6<{Fcsm}*L4ux)Hnoo~6*>AZszfW24k@cf18v-Cda6Bj9I`)ulB>-DQY|Ip3# zFNpLiLd$tJEVF=1bb8-Ho5TzS5qS{l8I=&_Q>WlWZT=8KPsycJ#*UUz06lMvx+aBl z)1gQhn>UDUTS3I?1i9S&QCGBura_S`iw6~fs!AWf>XpR>nM$-e3aRO@9iJ}aE-|fX zYQ~ycYH5e9T*9Eq!}85LgPmC|G88%(aR<07x!PkqVAXHN#woD&?b{;&!>J%LceU2m zH61*#PY-+FwQ@9j_4*BJs@xm3Nt)J(WjHZGfw&5zp-hrqo4c}EH(eD{1sp1_nao19E(_t2SMRIwwPMB9nGsvPEWywuHxa=Er)Zj`&B~+K0~? z;QFzb#NtFosl#k;pfkIn6s)qn?KbLXnI6ZTT^g&+)+}d!OlOL?OUxDC{@Q$UOi&?~ z)%=4{+VLZ?m{|2KVNbKRKR7fwRc<|xzPTQ$)1hx#z6>=}Nz6T@xgUM=J5rYfZ#)Y< z&$TA#bM(#UNZoNF_oK1`;bGDYu$l;oB0l{!JUbkfNv0up0rbp3CPN#A#3P?EIn!1B zM?vmoaibZyAZPVw6w()9GXQVoul|f;8Y9HYQcsf&p$PSl?*j$F=Z~g@P!C5vT{gxT eV~jC|1O5YOxn}qkW&Pj)0000 + + + + + diff --git a/AgoraCloudScene/src/main/res/drawable/agora_option_color_rtt_conversion_change_bg.xml b/AgoraCloudScene/src/main/res/drawable/agora_option_color_rtt_conversion_change_bg.xml new file mode 100644 index 000000000..e58f7aaaf --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_option_color_rtt_conversion_change_bg.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable/agora_option_color_rtt_conversion_change_text.xml b/AgoraCloudScene/src/main/res/drawable/agora_option_color_rtt_conversion_change_text.xml new file mode 100644 index 000000000..dc6f56f77 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_option_color_rtt_conversion_change_text.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt.xml b/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt.xml new file mode 100644 index 000000000..3033e71ea --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt_conversion.xml b/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt_conversion.xml new file mode 100644 index 000000000..422956e59 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt_conversion.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt_subtitles.xml b/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt_subtitles.xml new file mode 100644 index 000000000..41f636010 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_option_icon_rtt_subtitles.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable/agora_options_rtt_time_limit_bg.xml b/AgoraCloudScene/src/main/res/drawable/agora_options_rtt_time_limit_bg.xml new file mode 100644 index 000000000..d3f9bb247 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_options_rtt_time_limit_bg.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable/agora_options_rtt_time_limit_bg_disable.xml b/AgoraCloudScene/src/main/res/drawable/agora_options_rtt_time_limit_bg_disable.xml new file mode 100644 index 000000000..ea39d1638 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_options_rtt_time_limit_bg_disable.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_10.xml b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_10.xml new file mode 100644 index 000000000..91d327658 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_10.xml @@ -0,0 +1,5 @@ + + + + diff --git a/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_10_top.xml b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_10_top.xml new file mode 100644 index 000000000..a7d822dcd --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_10_top.xml @@ -0,0 +1,5 @@ + + + + diff --git a/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_20.xml b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_20.xml new file mode 100644 index 000000000..b0b04530f --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_20.xml @@ -0,0 +1,5 @@ + + + + diff --git a/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_4.xml b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_4.xml new file mode 100644 index 000000000..d13be47ec --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_4.xml @@ -0,0 +1,5 @@ + + + + diff --git a/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_6.xml b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_6.xml new file mode 100644 index 000000000..2380025e5 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_6.xml @@ -0,0 +1,5 @@ + + + + diff --git a/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_8.xml b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_8.xml new file mode 100644 index 000000000..dcb6b52cd --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_8.xml @@ -0,0 +1,5 @@ + + + + diff --git a/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_max.xml b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_max.xml new file mode 100644 index 000000000..0668eab73 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/agora_solid_radius_max.xml @@ -0,0 +1,5 @@ + + + + diff --git a/AgoraCloudScene/src/main/res/drawable/fcr_online_edu_rtt_conversion_dialog_options_arrow.xml b/AgoraCloudScene/src/main/res/drawable/fcr_online_edu_rtt_conversion_dialog_options_arrow.xml new file mode 100644 index 000000000..d8dc10ec0 --- /dev/null +++ b/AgoraCloudScene/src/main/res/drawable/fcr_online_edu_rtt_conversion_dialog_options_arrow.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml b/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml index 7eaa4a3e8..5a8272968 100644 --- a/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml +++ b/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml @@ -89,6 +89,35 @@ + + + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_options_component.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_options_component.xml index 7541cf4fe..9aeb2ce85 100644 --- a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_options_component.xml +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_options_component.xml @@ -31,6 +31,18 @@ android:visibility="gone" tools:visibility="visible" /> + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml new file mode 100644 index 000000000..84ecad400 --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_options_component.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_options_component.xml new file mode 100644 index 000000000..914e234f3 --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_options_component.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_setting_dialog.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_setting_dialog.xml new file mode 100644 index 000000000..f1ff3f6b7 --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_setting_dialog.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_conversion_dialog_list_content.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_conversion_dialog_list_content.xml new file mode 100644 index 000000000..81ca785ff --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_conversion_dialog_list_content.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_conversion_dialog_list_options.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_conversion_dialog_list_options.xml new file mode 100644 index 000000000..d854b31ce --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_conversion_dialog_list_options.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_list.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_list.xml new file mode 100644 index 000000000..4104b2dce --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_list.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_switch.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_switch.xml new file mode 100644 index 000000000..78c3a7489 --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_switch.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_title.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_title.xml new file mode 100644 index 000000000..03607da59 --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_content_title.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_list_select.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_list_select.xml new file mode 100644 index 000000000..3d4c6b6b1 --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_rtt_setting_dialog_list_select.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_toast_layout_default.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_toast_layout_default.xml new file mode 100644 index 000000000..37e7e87d3 --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_toast_layout_default.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/values-zh/strings.xml b/AgoraCloudScene/src/main/res/values-zh/strings.xml index f9f7df299..8b05cf05a 100644 --- a/AgoraCloudScene/src/main/res/values-zh/strings.xml +++ b/AgoraCloudScene/src/main/res/values-zh/strings.xml @@ -583,4 +583,8 @@ 老师放下你的手 所有学生都放下手 老师已结束投票 + + + 字幕 + 实时转写 diff --git a/AgoraCloudScene/src/main/res/values/colors.xml b/AgoraCloudScene/src/main/res/values/colors.xml index 926e34d0a..48836f4e5 100644 --- a/AgoraCloudScene/src/main/res/values/colors.xml +++ b/AgoraCloudScene/src/main/res/values/colors.xml @@ -22,6 +22,7 @@ #191919 #D7D7E6 #7D8798 + #CC787676 #357BF6 #C0D6FF @@ -30,6 +31,11 @@ #FE313F #FFC700 + #D9DFF7 + #373C42 + #BBBBBB + #CC787676 + #66000000 #0AFFFFFF #4D6277 @@ -42,7 +48,12 @@ #ffff0000 #357BF6 #4262FF + #1FDACF + #EEF2FA #BDBDBD + #F1F3F8 + #616161 + #959595 #357BF6 #639AFA @@ -98,6 +109,7 @@ #FF018786 #666667 #F2F2F2 + #CCF2F2F2 #CEE3F6 #F5ECCE #fff2f2f2 @@ -129,4 +141,7 @@ #F9F9FC #E3E7EF #F5655C + #1AF5655C + + #809BFFA5 diff --git a/AgoraCloudScene/src/main/res/values/dimens.xml b/AgoraCloudScene/src/main/res/values/dimens.xml index 86b68ad89..7a5d9168b 100644 --- a/AgoraCloudScene/src/main/res/values/dimens.xml +++ b/AgoraCloudScene/src/main/res/values/dimens.xml @@ -10,6 +10,8 @@ 253dp 280dp 214dp + 180dp + 90dp 6dp 8dp @@ -45,6 +47,7 @@ 40.0000dp 44.0000dp 48.0000dp + 50.0000dp 70.0000dp 72.0000dp 88.0000dp @@ -56,9 +59,11 @@ 420.0000dp 450.0000dp 10.0000sp + 11.0000sp 12.0000sp 13.0000sp 14.0000sp + 15.0000sp 16.0000sp 17.0000sp 20.0000sp @@ -295,4 +300,8 @@ 14dp 6dp + + 490dp + 375dp + diff --git a/AgoraCloudScene/src/main/res/values/strings.xml b/AgoraCloudScene/src/main/res/values/strings.xml index 89d62d7af..995b1db6a 100644 --- a/AgoraCloudScene/src/main/res/values/strings.xml +++ b/AgoraCloudScene/src/main/res/values/strings.xml @@ -578,4 +578,51 @@ Delete your account will result in ….\n Teacher lowers your hand All student\'s hands lowered Teacher has ended the poll + + + 字幕 + 实时转写 + 转写中... + 字幕和转写设置 + 声源语言(全会统一) + 翻译为 (自己想看的语言,且只对自己可见) + 同时显示双语 + 确定修改 + 修改声源语言 + 你修改本场会议的声源语言为%s,将对会议所有参会者的字幕和转写生效。 + 限时体验 + 限时体验{0}分钟 + 限时体验结束 + 实时转写 + 剩余体验时间 {0}:{1} 分钟 + 每个账号限时{0}分钟体验字幕和转写功能,剩余 {1}:{2} 分钟。 + 每个账号限时{0}分钟体验字幕和转写功能,体验时间已用完。 + 无法使用 + 正在开启字幕 + 点击字幕位置可以更改字幕设置 + 开始转写 + 停止转写 + 当前没有人在说话 + 正在聆听 + 不翻译 + zh-HK + zh-CN + zh-TW + en-IN + en-US + fr-FR + de-DE + th-TH + hi-IN + id-ID + it-IT + ja-JP + ko-KR + ms-MY* + fa-IR* + pt-PT + ru-RU + es-ES + tr-TR + vi-VN diff --git a/build.gradle b/build.gradle index 3559663bc..f1d925881 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.19' } } diff --git a/config.gradle b/config.gradle index 512d0192a..7be2ac76c 100644 --- a/config.gradle +++ b/config.gradle @@ -2,7 +2,7 @@ ext { //def fcrApaasVersion = "2.8.0" sdkVersion = [// TODO versionName 如果以'-SNAPSHOT'结尾,aar将会被发送到SNAPSHOT仓库 - 'appVersion':"2.8.102" + 'appVersion':"2.8.111-SNAPSHOT" ] maven = [ From 2327a421025fbd8785fa9236ec4033632bcbccad Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 27 Jun 2024 18:04:17 +0800 Subject: [PATCH 02/32] =?UTF-8?q?=E5=BC=B9=E7=AA=97=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E5=90=90=E5=8F=B8=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../online/component/dialog/AgoraUIDialog.kt | 8 + .../component/dialog/AgoraUIDialogBuilder.kt | 41 ++- .../dialog/AgoraUIRttConversionDialog.kt | 216 +++++++++++ .../dialog/AgoraUIRttSettingDialog.kt | 338 ++++++++++++++++++ .../online/component/toast/AgoraUIToast.kt | 94 ++++- 5 files changed, 690 insertions(+), 7 deletions(-) create mode 100644 AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt create mode 100644 AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIDialog.kt index f8e800410..c3068780f 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIDialog.kt @@ -4,6 +4,7 @@ import android.app.Dialog import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable +import android.text.SpannableString import android.view.View import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView @@ -60,6 +61,13 @@ class AgoraUIDialog(context: Context) : Dialog(context, R.style.agora_full_scree this.message.visibility = View.VISIBLE this.message.text = message } + fun setMessage(message: SpannableString) { + this.message.visibility = View.VISIBLE + this.message.text = message + } + fun setMessagePaddingHorizontal(messagePaddingHorizontal: Int) { + this.message.setPadding(messagePaddingHorizontal,this.message.paddingTop,messagePaddingHorizontal,this.message.paddingBottom) + } fun setIconResource(iconResource: Int) { this.icon.visibility = View.VISIBLE diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIDialogBuilder.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIDialogBuilder.kt index 26b7243b0..08b32a83e 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIDialogBuilder.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIDialogBuilder.kt @@ -1,14 +1,17 @@ package io.agora.online.component.dialog import android.content.Context +import android.text.SpannableString import android.view.View class AgoraUIDialogBuilder(private val context: Context) { private var title: String? = null private var message: String? = null + private var messageSpan: SpannableString? = null private var positiveText: String? = null private var negativeText: String? = null private var iconResource: Int? = null + private var messagePaddingHorizontal: Int? = null private var positiveListener: View.OnClickListener? = null private var negativeListener: View.OnClickListener? = null private var mCancelable: Boolean? = null @@ -28,6 +31,16 @@ class AgoraUIDialogBuilder(private val context: Context) { return this } + fun message(message: SpannableString): AgoraUIDialogBuilder { + this.messageSpan = message + return this + } + + fun messagePaddingHorizontal(messagePaddingHorizontal: Int): AgoraUIDialogBuilder { + this.messagePaddingHorizontal = messagePaddingHorizontal + return this + } + fun positiveText(text: String): AgoraUIDialogBuilder { this.positiveText = text return this @@ -57,6 +70,8 @@ class AgoraUIDialogBuilder(private val context: Context) { val dialog = AgoraUIDialog(context) title?.let { dialog.setTitle(it) } message?.let { dialog.setMessage(it) } + messageSpan?.let { dialog.setMessage(it) } + messagePaddingHorizontal?.let { dialog.setMessagePaddingHorizontal(it) } positiveText?.let { dialog.setPositiveButtonText(it) } positiveListener?.let { dialog.setPositiveClick(it) } negativeText?.let { dialog.setNegativeButtonText(it) } @@ -138,4 +153,28 @@ class AgoraUICustomDialogBuilder(private val context: Context) { } return dialog } -} \ No newline at end of file +} + +class AgoraUIRttSettingBuilder(private val context: Context) { + private var listener: AgoraUIRttSettingDialogListener? = null + fun build(): AgoraUIRttSettingDialog { + val dialog = AgoraUIRttSettingDialog(context) + listener?.let { dialog.setListener(it) } + return dialog + } + + /** + * 设置监听 + */ + fun setListener(listener: AgoraUIRttSettingDialogListener): AgoraUIRttSettingBuilder { + this.listener = listener + return this + } +} + +class AgoraUIRttConversionDialogBuilder(private val context: Context) { + fun build(): AgoraUIRttConversionDialog { + val dialog = AgoraUIRttConversionDialog(context) + return dialog + } +} diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt new file mode 100644 index 000000000..5a0f0db08 --- /dev/null +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -0,0 +1,216 @@ +package io.agora.online.component.dialog + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatTextView +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import io.agora.online.R +import io.agora.online.databinding.FcrOnlineEduRttConversionDialogBinding +import io.agora.online.helper.RttRecordItem +import java.text.MessageFormat +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +/** + * 功能作用:Rtt实时转写显示弹窗 + * 创建人:王亮(Loren) + * 思路: + * 方法: + * 注意: + * 修改人: + * 修改时间: + * 备注: + * + * @author 王亮(Loren) + */ +class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.agora_full_screen_dialog) { + + private val binding: FcrOnlineEduRttConversionDialogBinding by lazy { + FcrOnlineEduRttConversionDialogBinding.inflate(layoutInflater, null, false) + } + + /** + * 操作回调 + */ + var optionsCallback: ConversionOptionsInterface? = null + + /** + * 适配器 + */ + private val mAdapter = RecordAdapter(context, arrayListOf()) + + init { + setContentView(binding.root) + val window = this.window; + window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + window?.decorView?.setBackgroundResource(android.R.color.transparent) + val layout = findViewById(R.id.agora_dialog_layout) + layout.elevation = 10f + + initView() + } + + private fun initView() { + binding.fcrOnlineEduRttConversionDialogChangeStatus.setOnClickListener { + if (it.isActivated) { + closeConversion() + } else { + openConversion() + } + } + binding.fcrOnlineEduRttConversionDialogOptionsSetting.setOnClickListener { + optionsCallback?.openSetting() + } + binding.fcrOnlineEduRttConversionDialogOptionsClose.setOnClickListener { dismiss() } + binding.fcrOnlineEduRttConversionDialogList.adapter = mAdapter + binding.fcrOnlineEduRttConversionDialogList.layoutManager = LinearLayoutManager(context) + } + + /** + * 设置限时体验信息 + */ + fun setExperienceInfo(allowUseConfig: Boolean, rttExperienceReduceTime: Int) { + binding.fcrOnlineEduRttConversionDialogChangeStatus.isEnabled = allowUseConfig || rttExperienceReduceTime > 0 + if (allowUseConfig) { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.visibility = View.GONE + binding.fcrOnlineEduRttConversionDialogTimeLimitReduce.visibility = View.GONE + } else { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.visibility = View.VISIBLE + if (rttExperienceReduceTime <= 0) { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setText(R.string.fcr_dialog_rtt_time_limit_end) + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setBackgroundResource(R.drawable.agora_options_rtt_time_limit_bg_disable) + binding.fcrOnlineEduRttConversionDialogTimeLimitReduce.visibility = View.GONE + closeConversion() + } else { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setText(R.string.fcr_dialog_rtt_time_limit) + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setBackgroundResource(R.drawable.agora_options_rtt_time_limit_bg) + binding.fcrOnlineEduRttConversionDialogTimeLimitReduce.visibility = View.VISIBLE + binding.fcrOnlineEduRttConversionDialogTimeLimitReduce.text = MessageFormat.format( + context.resources.getString(R.string.fcr_dialog_rtt_dialog_time_limit_reduce), + if (rttExperienceReduceTime / 60000 > 9) rttExperienceReduceTime / 60000 else "0${rttExperienceReduceTime / 60000}", + if (rttExperienceReduceTime % 60000 / 1000 > 9) rttExperienceReduceTime % 60000 / 1000 else "0${rttExperienceReduceTime % 60000 / 1000}", + ) + } + } + } + + /** + * 刷新记录信息 + */ + fun updateShowList(list: List) { + if (mAdapter.dataList.size != list.size) { + mAdapter.dataList.clear() + mAdapter.dataList.addAll(list) + mAdapter.notifyItemRangeChanged(0, list.size) + binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) + } else { + mAdapter.dataList[mAdapter.dataList.size - 1] = list[list.size - 1] + mAdapter.notifyItemChanged(mAdapter.dataList.size - 1) + binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) + } + } + + /** + * 开始转写 + */ + private fun openConversion() { + if (binding.fcrOnlineEduRttConversionDialogChangeStatus.isEnabled) { + optionsCallback?.openConversion() + binding.fcrOnlineEduRttConversionDialogChangeStatus.isActivated = true + binding.fcrOnlineEduRttConversionDialogChangeStatus.setText(R.string.fcr_dialog_rtt_dialog_conversion_close) + binding.fcrOnlineEduRttConversionDialogChangeStatus.setTextColor(ContextCompat.getColor(context, R.color.fcr_text_red)) + } + } + + /** + * 关闭转写 + */ + private fun closeConversion() { + optionsCallback?.closeConversion() + binding.fcrOnlineEduRttConversionDialogChangeStatus.isActivated = false + binding.fcrOnlineEduRttConversionDialogChangeStatus.setText(R.string.fcr_dialog_rtt_dialog_conversion_open) + binding.fcrOnlineEduRttConversionDialogChangeStatus.setTextColor(ContextCompat.getColor(context, R.color.fcr_white)) + } + + /** + * 显示弹窗 + */ + fun show(list: List) { + mAdapter.dataList.clear() + mAdapter.dataList.addAll(list) + openConversion() + super.show() + } +} + +/** + * 转写操作接口类 + */ +interface ConversionOptionsInterface { + /** + * 开启转写 + */ + fun openConversion() + + /** + * 关闭转写 + */ + fun closeConversion() + + /** + * 打开设置页面 + */ + fun openSetting() +} + +/** + * 数据记录适配器 + */ +private class RecordAdapter(private val context: Context, val dataList: ArrayList) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + if (dataList[viewType].statusText.isNullOrEmpty()) { + return object : RecyclerView.ViewHolder( + LayoutInflater.from(context).inflate(R.layout.fcr_online_rtt_conversion_dialog_list_content, parent, false)) {} + } + return object : + RecyclerView.ViewHolder(LayoutInflater.from(context).inflate(R.layout.fcr_online_rtt_conversion_dialog_list_options, parent, false)) {} + } + + override fun getItemCount() = dataList.size + + override fun getItemViewType(position: Int): Int { + return position + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val bean = dataList[position] + if (bean.statusText.isNullOrEmpty()) { + holder.itemView.let { + Glide.with(context).load(bean.userHeader).skipMemoryCache(true).placeholder(R.drawable.agora_video_ic_audio_on) + .apply(RequestOptions.circleCropTransform()).into(it.findViewById(R.id.agora_fcr_rtt_text_dialog_user_header)) + it.findViewById(R.id.agora_fcr_rtt_text_dialog_user_name).text = bean.userName + it.findViewById(R.id.agora_fcr_rtt_text_dialog_time).text = + (if (bean.time == null || bean.time == 0L) null else bean.time)?.let { time -> Date(time) } + ?.let { date -> SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA).format(date) } + it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_origin).text = bean.sourceText + + it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_result).apply { + text = if (bean.currentTargetLan.isNullOrEmpty()) "" else bean.targetText + visibility = if (bean.currentTargetLan.isNullOrEmpty()) View.GONE else View.VISIBLE + } + } + } else { + holder.itemView.findViewById(R.id.fcr_online_rtt_conversion_dialog_list_options_text).text = bean.statusText + } + } +} \ No newline at end of file diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt new file mode 100644 index 000000000..d1bc7891e --- /dev/null +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt @@ -0,0 +1,338 @@ +package io.agora.online.component.dialog + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.drawable.ColorDrawable +import android.text.SpannableString +import android.text.Spanned +import android.text.style.ForegroundColorSpan +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatImageView +import androidx.appcompat.widget.AppCompatTextView +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import io.agora.online.R +import io.agora.online.databinding.FcrOnlineEduRttSettingDialogBinding +import io.agora.online.helper.RttSettingInfo + + +/** + * 功能作用:字幕、转写设置弹窗 + * 创建人:王亮(Loren) + * 思路: + * 方法: + * 注意: + * 修改人: + * 修改时间: + * 备注: + * + * @author 王亮(Loren) + */ +class AgoraUIRttSettingDialog(context: Context) : Dialog(context, R.style.agora_full_screen_dialog) { + + private val binding: FcrOnlineEduRttSettingDialogBinding by lazy { FcrOnlineEduRttSettingDialogBinding.inflate(layoutInflater, null, false) } + + /** + * 内容适配器 + */ + private val adapter = ContentAdapter(context) + + init { + setContentView(binding.root) + val window = this.window + window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + window?.decorView?.setBackgroundResource(android.R.color.transparent) + val layout = findViewById(R.id.agora_dialog_layout) + layout.elevation = 10f + initView() + } + + private fun initView() { + binding.agoraFcrRttSettingDialogList.adapter = adapter + binding.agoraFcrRttSettingDialogList.layoutManager = LinearLayoutManager(context) + binding.agoraFcrRttSettingDialogClose.setOnClickListener { dismiss() } + } + + /** + * 显示设置弹窗 + */ + fun show(currentSettingInfo: RttSettingInfo) { + adapter.setConfigInfo(currentSettingInfo) + super.show() + } + + /** + * 设置监听 + */ + fun setListener(listener: AgoraUIRttSettingDialogListener) { + adapter.setListener(listener) + } + +} + +/** + * 设置弹窗监听 + */ +interface AgoraUIRttSettingDialogListener { + /** + * 修改双语显示 + */ + fun changeDoubleShow(showDouble: Boolean) + + /** + * 设置目标语言 + */ + fun setTargetLan(code: String) + + /** + * 设置声源语言 + */ + fun setSourceLan(code: String) +} + +/** + * 语言选择列表适配器 + */ +private class SelectListAdapter(var context: Context, var dataList: MutableList) : RecyclerView.Adapter() { + private var currentSelect: SelectItem? = null + private var lastSelect: SelectItem? = null + var onSelectChangedListener: OnSelectChangedListener? = null + + init { + currentSelect = dataList.find { it.select } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(context).inflate(R.layout.fcr_online_rtt_setting_dialog_list_select, parent, false)) + } + + @SuppressLint("NotifyDataSetChanged") + override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { + val item = dataList[position] + viewHolder.iconCheck.visibility = if (item.select) View.VISIBLE else View.GONE + viewHolder.iconDefault.visibility = if (!item.select) View.VISIBLE else View.GONE + viewHolder.itemView.setOnClickListener { + if (viewHolder.iconDefault.visibility == View.VISIBLE && item.allowSelect) { + lastSelect = if (lastSelect == null) { + dataList.find { it.select } + } else { + currentSelect + } + lastSelect?.select = false + item.select = true + currentSelect = item + onSelectChangedListener?.onChanged(currentSelect!!) + notifyItemRangeChanged(0, itemCount) + } + } + viewHolder.title.text = dataList[position].text + } + + /** + * 重置到上一次 + */ + fun resetUseLast() { + if (lastSelect != null) { + currentSelect?.select = false + currentSelect = lastSelect + dataList.find { it.code == lastSelect!!.code }?.select = true + lastSelect = null + notifyItemRangeChanged(0, itemCount) + } + } + + override fun getItemCount() = dataList.size +} + +private class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var iconCheck: AppCompatImageView = view.findViewById(R.id.dialog_rtt_select_item_icon_checked) + var iconDefault: AppCompatImageView = view.findViewById(R.id.dialog_rtt_select_item_icon_default) + var title: AppCompatTextView = view.findViewById(R.id.dialog_rtt_select_item_title) +} + +/** + * 语音列表选择改变监听 + */ +private interface OnSelectChangedListener { + fun onChanged(select: SelectItem) +} + +/** + * 内容显示适配器 + */ +private class ContentAdapter(var context: Context) : RecyclerView.Adapter() { + + private val mFromAdapter by lazy { SelectListAdapter(context, mutableListOf()) } + private val mToAdapter by lazy { SelectListAdapter(context, mutableListOf()) } + + /** + * 配置信息 + */ + private var currentSettingInfo: RttSettingInfo? = null + + /** + * 监听 + */ + private var listener: AgoraUIRttSettingDialogListener? = null + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return object : RecyclerView.ViewHolder(LayoutInflater.from(context).inflate(getLayout(viewType), parent, false)) {} + } + + override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { + when (getLayout(position)) { + R.layout.fcr_online_rtt_setting_dialog_content_title -> { + if (position == 0) { + (viewHolder.itemView as AppCompatTextView).setText(R.string.fcr_dialog_rtt_setting_dialog_title_origin) + } else if (position == 2) { + (viewHolder.itemView as AppCompatTextView).setText(R.string.fcr_dialog_rtt_setting_dialog_title_result) + } + } + + R.layout.fcr_online_rtt_setting_dialog_content_list -> { + if (position == 1) { + (viewHolder.itemView as RecyclerView).apply { + adapter = mFromAdapter + mFromAdapter.onSelectChangedListener = object : OnSelectChangedListener { + override fun onChanged(select: SelectItem) { + val useText = "“${select.text}”"//使用的变色文本 + val content = SpannableString( + String.format(resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_content), useText)) + content.setSpan(ForegroundColorSpan(ContextCompat.getColor(context, R.color.fcr_blue_357BF6)), + content.indexOf(useText), content.indexOf(useText) + useText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + + AgoraUIDialogBuilder(context).title(resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_title)) + .message(content).messagePaddingHorizontal(resources.getDimensionPixelOffset(R.dimen.dp_10)) + .negativeText(context.resources.getString(R.string.fcr_user_kick_out_cancel)).negativeClick { + mFromAdapter.resetUseLast() + }.positiveText(context.resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_confirm)).positiveClick { + listener?.setSourceLan(select.code) + + }.build().show() + } + } + layoutManager = LinearLayoutManager(context) + for (i in 0 until itemDecorationCount) { + removeItemDecorationAt(0) + } + addItemDecoration(ListSelectItemDecoration(this)) + } + } else if (position == 3) { + (viewHolder.itemView as RecyclerView).apply { + adapter = mToAdapter + mToAdapter.onSelectChangedListener = object : OnSelectChangedListener { + override fun onChanged(select: SelectItem) { + listener?.setTargetLan(select.code) + } + } + layoutManager = LinearLayoutManager(context) + for (i in 0 until itemDecorationCount) { + removeItemDecorationAt(0) + } + addItemDecoration(ListSelectItemDecoration(this)) + } + } + } + + R.layout.fcr_online_rtt_setting_dialog_content_switch -> { + viewHolder.itemView.apply { + findViewById(R.id.fcr_online_rtt_setting_dialog_content_switch_title).setText( + R.string.fcr_dialog_rtt_setting_dialog_title_switch) + findViewById(R.id.fcr_online_rtt_setting_dialog_content_switch_icon).isActivated = + this@ContentAdapter.currentSettingInfo?.showDoubleLan ?: false + setOnClickListener { + if (false == this@ContentAdapter.currentSettingInfo?.showDoubleLan) { + findViewById(R.id.fcr_online_rtt_setting_dialog_content_switch_icon).isActivated = true + currentSettingInfo?.showDoubleLan = true + listener?.changeDoubleShow(true) + } else { + findViewById(R.id.fcr_online_rtt_setting_dialog_content_switch_icon).isActivated = false + currentSettingInfo?.showDoubleLan = false + listener?.changeDoubleShow(false) + } + } + } + } + } + } + + /** + * 获取布局类型 + */ + private fun getLayout(position: Int): Int { + return when (position) { + 0 -> R.layout.fcr_online_rtt_setting_dialog_content_title + 1 -> R.layout.fcr_online_rtt_setting_dialog_content_list + 2 -> R.layout.fcr_online_rtt_setting_dialog_content_title + 3 -> R.layout.fcr_online_rtt_setting_dialog_content_list + 4 -> R.layout.fcr_online_rtt_setting_dialog_content_switch + else -> R.layout.fcr_online_rtt_setting_dialog_content_title + } + } + + override fun getItemCount() = 5 + + override fun getItemViewType(position: Int) = position + + /** + * 设置配置信息 + */ + fun setConfigInfo(currentSettingInfo: RttSettingInfo) { + this.currentSettingInfo = currentSettingInfo + mFromAdapter.dataList.clear() + for (item in currentSettingInfo.sourceListLan) { + mFromAdapter.dataList.add( + SelectItem(context.getString(item.textRes), item.value, true, item.value === currentSettingInfo.sourceLan.value)) + } + mToAdapter.dataList.clear() + for (item in currentSettingInfo.targetListLan) { + mToAdapter.dataList.add( + SelectItem(context.getString(item.textRes), item.value, true, currentSettingInfo.targetLan.find { it.value === item.value } != null)) + } +// mFromAdapter.notifyItemRangeChanged(0, mFromAdapter.itemCount) +// mToAdapter.notifyItemRangeChanged(0, mToAdapter.itemCount) + notifyItemRangeChanged(0, itemCount) + } + + /** + * 设置监听 + */ + fun setListener(listener: AgoraUIRttSettingDialogListener) { + this.listener = listener + } +} + +/** + * 选项列表分割线 + */ +private class ListSelectItemDecoration(private val recyclerView: RecyclerView) : RecyclerView.ItemDecoration() { + private val paint = Paint().apply { + this.isAntiAlias = true + this.color = ContextCompat.getColor(recyclerView.context, R.color.fcr_im_input_bg_line) + } + + override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + super.onDrawOver(c, parent, state) + val childCount = parent.childCount + for (i in 0 until childCount - 1) { // childCount - 1 to avoid drawing a divider after the last item + val child = parent.getChildAt(i) + val params = child.layoutParams as RecyclerView.LayoutParams + + val top = child.bottom + params.bottomMargin + c.drawRect(recyclerView.paddingLeft.toFloat(), top.toFloat(), recyclerView.right.toFloat(), + (top + recyclerView.resources.getDimensionPixelSize(R.dimen.dp_1)).toFloat(), paint) + } + } +} + +/** + * 选项 + */ +private class SelectItem(val text: String, val code: String, val allowSelect: Boolean, var select: Boolean) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt index 3dd4415d7..e84c832db 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt @@ -2,18 +2,26 @@ package io.agora.online.component.toast import android.annotation.SuppressLint import android.content.Context +import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.os.Handler +import android.os.Looper +import android.os.Message +import android.text.SpannableString +import android.util.TypedValue import android.view.Gravity import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.widget.RelativeLayout import android.widget.Toast import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView +import androidx.appcompat.widget.LinearLayoutCompat import androidx.core.content.ContextCompat +import androidx.core.view.children import io.agora.online.R @SuppressLint("StaticFieldLeak") @@ -118,6 +126,77 @@ object AgoraUIToast { showToast(context.applicationContext, LEVEL_ERROR, anchor, text, duration) } + /** + * 显示默认弹窗 + */ + fun showDefaultToast(context: Context, message: String, duration: Int = LENGTH_SHORT) { + showDefaultToast(context, SpannableString(message), duration) + } + + /** + * 上一次显示的默认吐司 + */ + private var lastShowToast: Toast? = null + + /** + * 默认吐司控制器,用来移除到达时间的view视图 + */ + private val defaultToastHandler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + if (lastShowToast?.view != null) { + (lastShowToast!!.view as ViewGroup).children.forEach { + if (it.id == msg.what) { + (lastShowToast!!.view as ViewGroup).removeView(it) + } + } + } + } + } + + /** + * 显示默认弹窗 + */ + fun showDefaultToast(context: Context, message: SpannableString, duration: Int = LENGTH_SHORT) { + ContextCompat.getMainExecutor(context).execute { + computeValues() + if (lastShowToast == null) { + lastShowToast = Toast(context.applicationContext) + lastShowToast!!.view = LayoutInflater.from(context).inflate(R.layout.fcr_online_toast_layout_default, null, false) + } + val view = getDefaultTextView(context, message) + view.id = (Math.random() * 1000000000000).toInt() + //一定时间之后移除视图 + defaultToastHandler.sendEmptyMessageDelayed(view.id, + if (LENGTH_SHORT == duration) 1500 else if (LENGTH_LONG == duration) 3000 else duration.toLong()) + (lastShowToast!!.view as ViewGroup).addView(view) + lastShowToast!!.duration = Toast.LENGTH_LONG + lastShowToast!!.setGravity(Gravity.CENTER, 0, -context.resources.getDimensionPixelOffset(R.dimen.dp_40)) + lastShowToast!!.show() + } + } + + private fun getDefaultTextView(context: Context, message: SpannableString): View { + val showView = AppCompatTextView(context) + showView.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, context.resources.getDimensionPixelOffset(R.dimen.dp_40)) + showView.setPadding(context.resources.getDimensionPixelOffset(R.dimen.dp_20), context.resources.getDimensionPixelOffset(R.dimen.dp_10), + context.resources.getDimensionPixelOffset(R.dimen.dp_20), context.resources.getDimensionPixelOffset(R.dimen.dp_10)) + showView.setTextColor(ContextCompat.getColor(context, R.color.fcr_white)) + showView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.resources.getDimension(R.dimen.sp_13)) + showView.setBackgroundResource(R.drawable.agora_solid_radius_max) + showView.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(context, R.color.fcr_black_66000000)) + showView.text = message + showView.gravity = Gravity.CENTER + return LinearLayoutCompat(context).apply{ + orientation = LinearLayoutCompat.VERTICAL + layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + addView(showView) + addView(View(context).also {view-> + view.layoutParams = ViewGroup.LayoutParams(10, context.resources.getDimensionPixelOffset(R.dimen.dp_10)) + }) + } + } + /** * Display the toast below the target anchor view * @param anchor tells how to position this toast on the screen, @@ -127,8 +206,7 @@ object AgoraUIToast { private fun showToast(context: Context, level: Int, anchor: View?, text: String, duration: Int) { ContextCompat.getMainExecutor(context).execute { computeValues() - val toastLayout = LayoutInflater.from(context).inflate( - R.layout.fcr_online_toast_layout, null, false) + val toastLayout = LayoutInflater.from(context).inflate(R.layout.fcr_online_toast_layout, null, false) toastLayout.findViewById(R.id.agora_toast_icon)?.let { icon -> getToastIconRes(level)?.let { iconRes -> @@ -157,26 +235,30 @@ object AgoraUIToast { } } + private fun computeValues() { } - private fun buildToastBgDrawable(context: Context,level: Int): Drawable? { + private fun buildToastBgDrawable(context: Context, level: Int): Drawable? { val bgColor: Int val borderColor: Int when (level) { LEVEL_INFO -> { - bgColor = ContextCompat.getColor(context,R.color.fcr_system_safe_color) + bgColor = ContextCompat.getColor(context, R.color.fcr_system_safe_color) borderColor = COLOR_INFO_BORDER } + LEVEL_WARN -> { - bgColor = ContextCompat.getColor(context,R.color.fcr_system_warning_color) + bgColor = ContextCompat.getColor(context, R.color.fcr_system_warning_color) borderColor = COLOR_WARN_BORDER } + LEVEL_ERROR -> { - bgColor = ContextCompat.getColor(context,R.color.fcr_system_error_color) + bgColor = ContextCompat.getColor(context, R.color.fcr_system_error_color) borderColor = COLOR_ERROR_BORDER } + else -> { return null } From 64609bdb5b4aa5de51f68afe081b33b22f12c239 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 27 Jun 2024 18:05:29 +0800 Subject: [PATCH 03/32] =?UTF-8?q?=E6=96=B0=E5=A2=9Ertt=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../online/component/AgoraEduRttComponent.kt | 114 +++++++++++ .../options/AgoraEduOptionsComponent.kt | 118 +++++++---- .../options/AgoraEduRttOptionsComponent.kt | 184 ++++++++++++++++++ 3 files changed, 373 insertions(+), 43 deletions(-) create mode 100644 AgoraCloudScene/src/main/java/io/agora/online/component/AgoraEduRttComponent.kt create mode 100644 AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/AgoraEduRttComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/AgoraEduRttComponent.kt new file mode 100644 index 000000000..2c2a3fb56 --- /dev/null +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/AgoraEduRttComponent.kt @@ -0,0 +1,114 @@ +package io.agora.online.component + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import io.agora.online.R +import io.agora.online.component.common.AbsAgoraEduComponent +import io.agora.online.component.common.IAgoraUIProvider +import io.agora.online.databinding.FcrOnlineEduRttComponentBinding +import io.agora.online.helper.RttOptionsManager +import io.agora.online.util.AppUtil +import java.text.MessageFormat + +class AgoraEduRttComponent : AbsAgoraEduComponent { + constructor(context: Context) : super(context) + constructor(context: Context, attr: AttributeSet) : super(context, attr) + constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr) + + + private var binding: FcrOnlineEduRttComponentBinding = FcrOnlineEduRttComponentBinding.inflate(LayoutInflater.from(context), this, true) + private lateinit var rttOptionsManager: RttOptionsManager + + //点击间隔时间 + private val clickInterval = 500L + + fun initView(rttOptionsManager: RttOptionsManager, agoraUIProvider: IAgoraUIProvider) { + this.rttOptionsManager = rttOptionsManager + super.initView(agoraUIProvider) + } + + init { + binding.agoraRttDialogLayout.clipToOutline = true + setButtonClickListeners() + } + + private fun setButtonClickListeners() { + resetStatus(null) + setFastClickAvoidanceListener(binding.agoraRttDialogSubtitles) { + if (this.rttOptionsManager.isOpenSubtitles()) { + binding.agoraRttDialogSubtitlesIcon.isActivated = false + this.rttOptionsManager.closeSubtitles() + } else { + binding.agoraRttDialogSubtitlesIcon.isActivated = true + this.rttOptionsManager.openSubtitles() + } + } + setFastClickAvoidanceListener(binding.agoraRttDialogConversion) { + if (this.rttOptionsManager.isOpenConversion()) { + binding.agoraRttDialogConversionIcon.isActivated = false + this.rttOptionsManager.closeConversion() + } else { + binding.agoraRttDialogConversionIcon.isActivated = true + this.rttOptionsManager.openConversion() + } + } + } + + + private fun setFastClickAvoidanceListener(view: View, worker: ((Boolean) -> Unit)?) { + view.setOnClickListener { + if (!AppUtil.isFastClick(clickInterval)) { + worker?.invoke(view.isActivated) + } + } + } + + + fun dismiss() { +// this.parent?.let { +// var contains = false +// it.forEach check@{ child -> +// if (child == this) { +// contains = true +// return@check +// } +// } +// +// if (contains) { +// it.removeView(this) +// } +// } + } + + override fun release() { + super.release() + } + + /** + * 重置显示状态 + */ + fun resetStatus(experienceReduceTime: Int?) { + binding.agoraRttDialogSubtitlesIcon.isActivated = this::rttOptionsManager.isInitialized && rttOptionsManager.isOpenSubtitles() + binding.agoraRttDialogConversionIcon.isActivated = this::rttOptionsManager.isInitialized && rttOptionsManager.isOpenConversion() + if (experienceReduceTime != null) { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { + MessageFormat.format(resources.getString(R.string.fcr_dialog_rtt_time_limit_time), experienceReduceTime / 60000) + } else { + resources.getString(R.string.fcr_dialog_rtt_time_limit_end) + } + } + } + + /** + * 设置是否可以使用RTT功能 + */ + fun setAllowUse(allowUse: Boolean, reduceTime: Int) { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.visibility = if (reduceTime > 0) View.VISIBLE else View.GONE + binding.agoraRttDialogSubtitles.isEnabled = allowUse + binding.agoraRttDialogConversion.isEnabled = allowUse + binding.agoraRttDialogSubtitles.alpha = if (allowUse) 1F else 0.8F + binding.agoraRttDialogConversion.alpha = if (allowUse) 1F else 0.8F + } +} diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt index f28dacde3..49e43d2ab 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt @@ -8,12 +8,6 @@ import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.ImageView import androidx.core.content.ContextCompat -import io.agora.online.component.AgoraEduChatComponent -import io.agora.online.component.AgoraEduSettingComponent -import io.agora.online.component.common.AbsAgoraEduConfigComponent -import io.agora.online.component.common.IAgoraUIProvider -import io.agora.online.component.common.UIUtils -import io.agora.online.component.whiteboard.data.AgoraEduApplianceData import com.google.gson.Gson import io.agora.agoraeducore.core.AgoraEduCoreManager import io.agora.agoraeducore.core.context.AgoraEduContextUserInfo @@ -38,9 +32,16 @@ import io.agora.agoraeducore.core.internal.transport.OnAgoraTransportListener import io.agora.agoraeducore.extensions.widgets.bean.AgoraWidgetDefaultId import io.agora.agoraeducore.extensions.widgets.bean.AgoraWidgetMessageObserver import io.agora.online.R +import io.agora.online.component.AgoraEduChatComponent +import io.agora.online.component.AgoraEduSettingComponent +import io.agora.online.component.common.AbsAgoraEduConfigComponent +import io.agora.online.component.common.IAgoraUIProvider +import io.agora.online.component.common.UIUtils import io.agora.online.component.toast.AgoraUIToast +import io.agora.online.component.whiteboard.data.AgoraEduApplianceData import io.agora.online.config.FcrUIConfig import io.agora.online.databinding.FcrOnlineEduOptionsComponentBinding +import io.agora.online.helper.RttOptionsManager import io.agora.online.impl.chat.ChatPopupWidgetListener import io.agora.online.impl.whiteboard.bean.AgoraBoardGrantData import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionPacket @@ -48,8 +49,7 @@ import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionSignal import io.agora.online.provider.AgoraUIUserDetailInfo import io.agora.online.provider.UIDataProviderListenerImpl -class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhiteBoardIconClickListener, - OnAgoraTransportListener { +class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhiteBoardIconClickListener, OnAgoraTransportListener { constructor(context: Context) : super(context) constructor(context: Context, attr: AttributeSet) : super(context, attr) constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr) @@ -58,9 +58,9 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite lateinit var uuid: String lateinit var rootContainer: ViewGroup // 给IM用的,view root lateinit var itemContainer: ViewGroup // 显示侧边栏 + lateinit var rttOptionsManager: RttOptionsManager - private var binding: FcrOnlineEduOptionsComponentBinding = - FcrOnlineEduOptionsComponentBinding.inflate(LayoutInflater.from(context), this, true) + private var binding: FcrOnlineEduOptionsComponentBinding = FcrOnlineEduOptionsComponentBinding.inflate(LayoutInflater.from(context), this, true) private var agroSettingWidget: AgoraEduSettingComponent? = null private var popupViewRoster: AgoraEduRosterComponent? = null @@ -75,10 +75,15 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite AgoraTransportManager.addListener(AgoraTransportEventId.EVENT_ID_OPTIONS_PANEL, this) } - fun initView(uuid: String, rootContainer: ViewGroup, itemContainer: ViewGroup, agoraUIProvider: IAgoraUIProvider) { + fun initView( + rttOptionsManager: RttOptionsManager, uuid: String, rootContainer: ViewGroup, itemContainer: ViewGroup, + agoraUIProvider: IAgoraUIProvider, + ) { this.uuid = uuid this.itemContainer = itemContainer this.rootContainer = rootContainer + this.rttOptionsManager = rttOptionsManager + this.rttOptionsManager.setEduOptionsComponent(this) initView(agoraUIProvider) } @@ -134,37 +139,40 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite isRequestHelp = true } } else { - AgoraUIToast.warn( - context.applicationContext, - text = resources.getString(R.string.fcr_group_teacher_exist_hint) - ) + AgoraUIToast.warn(context.applicationContext, text = resources.getString(R.string.fcr_group_teacher_exist_hint)) } } binding.optionItemSetting.setOnClickListener { //LogX.e(TAG,">>>${eduContext?.userContext()?.getCoHostList()}") - if (!binding.optionItemSetting.isActivated) { + if (!it.isActivated) { showItem(agroSettingWidget) setIconActivated(binding.optionItemSetting) } else { hiddenItem() - binding.optionItemSetting.isActivated = false + it.isActivated = false + } + } + binding.optionItemRtt.setOnClickListener { + //LogX.e(TAG,">>>${eduContext?.userContext()?.getCoHostList()}") + if (!it.isActivated) { + rttOptionsManager.resetEduRttToolBoxStatus() + showItem(rttOptionsManager.rttToolBoxWidget, R.dimen.agora_edu_options_rtt_dialog_w, R.dimen.agora_userlist_dialog_large_h) + setIconActivated(binding.optionItemRtt) + } else { + hiddenItem() + it.isActivated = false } } binding.optionItemRoster.setOnClickListener { if (!binding.optionItemRoster.isActivated) { popupViewRoster?.isShow = true - if (eduContext?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.SMALL_CLASS.value - || eduContext?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.GROUPING_CLASS.value - ) { + if (eduContext?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.SMALL_CLASS.value || eduContext?.roomContext() + ?.getRoomInfo()?.roomType?.value == RoomType.GROUPING_CLASS.value) { showItem(popupViewRoster, R.dimen.agora_userlist_dialog_w, R.dimen.agora_userlist_dialog_h) popupViewRoster?.showUserList() } else { popupViewRoster?.updateStuListData()//默认加载第一页数据 - showItem( - popupViewRoster, - R.dimen.agora_userlist_dialog_large_w, - R.dimen.agora_userlist_dialog_large_h - ) + showItem(popupViewRoster, R.dimen.agora_userlist_dialog_large_w, R.dimen.agora_userlist_dialog_large_h) } setIconActivated(binding.optionItemRoster) } else { @@ -188,11 +196,10 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite eduContext?.widgetContext()?.setWidgetActive(AgoraWidgetDefaultId.WhiteBoard.id, info) } - eduCore?.eduContextPool()?.widgetContext() - ?.addWidgetMessageObserver(whiteBoardWidgetMsgObserver, AgoraWidgetDefaultId.WhiteBoard.id) + eduCore?.eduContextPool()?.widgetContext()?.addWidgetMessageObserver(whiteBoardWidgetMsgObserver, AgoraWidgetDefaultId.WhiteBoard.id) } - fun cancelHandsUp(){ + fun cancelHandsUp() { ContextCompat.getMainExecutor(context).execute { binding.optionShowHandup.visibility = View.GONE binding.optionItemHandup.cancelHandsUp() @@ -233,9 +240,8 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite val localUserUuid = eduContext?.userContext()?.getLocalUserInfo()?.userUuid - if (eduContext?.userContext()?.getLocalUserInfo()?.role == AgoraEduContextUserRole.Student - && eduContext?.roomContext()?.getRoomInfo()?.roomType == RoomType.LARGE_CLASS - ) {//大班课,才会进这里的逻辑 + if (eduContext?.userContext()?.getLocalUserInfo()?.role == AgoraEduContextUserRole.Student && eduContext?.roomContext() + ?.getRoomInfo()?.roomType == RoomType.LARGE_CLASS) {//大班课,才会进这里的逻辑 isLocalUserOnStage = eduContext?.userContext()?.getCoHostList()?.find { it.userUuid == localUserUuid } != null if (!isLocalUserOnStage) {//本地不在台上了//todo //收回白板权限 @@ -304,7 +310,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite override fun onRemoteUserLeft( user: AgoraEduContextUserInfo, operator: AgoraEduContextUserInfo?, - reason: EduContextUserLeftReason + reason: EduContextUserLeftReason, ) { super.onRemoteUserLeft(user, operator, reason) // 老师离开小组 @@ -319,10 +325,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite super.onTeacherLaterJoin() if (isRequestHelp) { ContextCompat.getMainExecutor(context).execute { - AgoraUIToast.info( - context.applicationContext, - text = resources.getString(R.string.fcr_group_help_teacher_busy_msg) - ) + AgoraUIToast.info(context.applicationContext, text = resources.getString(R.string.fcr_group_help_teacher_busy_msg)) } isRequestHelp = false } @@ -339,7 +342,6 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite agroSettingWidget?.onExitListener = { onExitListener?.invoke() } - if (getUIConfig().roster.isVisible && eduCore?.config?.roleType != AgoraEduRoleType.AgoraEduRoleTypeObserver.value) { popupViewRoster = AgoraEduRosterComponent(context) popupViewRoster?.initView(agoraUIProvider) @@ -370,7 +372,8 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite } } LogX.i(TAG, "isOpenBoard=" + AgoraEduApplianceData.isOpenBoardWidget(eduCore) + "granted=" + localUserGranted) - if (AgoraEduApplianceData.isOpenBoardWidget(eduCore) && (localUserGranted || localUser.role == AgoraEduContextUserRole.Teacher)) { // 可以显示白板按钮,如果是老师则默认显示 + if (AgoraEduApplianceData.isOpenBoardWidget( + eduCore) && (localUserGranted || localUser.role == AgoraEduContextUserRole.Teacher)) { // 可以显示白板按钮,如果是老师则默认显示 setWhiteboardViewTool(true) } else { setWhiteboardViewTool(false) @@ -401,11 +404,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite */ private fun showItem(item: View?, widthDimenId: Int, heightDimenId: Int) { itemContainer.removeAllViews() - itemContainer.addView( - item, - context.resources.getDimensionPixelOffset(widthDimenId), - context.resources.getDimensionPixelOffset(heightDimenId) - ) + itemContainer.addView(item, context.resources.getDimensionPixelOffset(widthDimenId), context.resources.getDimensionPixelOffset(heightDimenId)) if (popupViewChat == item) { hiddenChatNews() } @@ -415,6 +414,14 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemChatNews.visibility = View.GONE } + /** + * 隐藏rtt相关的 + */ + fun hiddenRtt(){ + hiddenItem() + binding.optionItemRtt.isActivated = false + } + fun setHandsupTimeout(seconds: Int) { //binding.optionItemHandup.setHandsupTimeout(seconds) } @@ -435,14 +442,24 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemSetting -> { binding.optionItemRoster.isActivated = false binding.optionItemChat.isActivated = false + binding.optionItemRtt.isActivated = false + } + + binding.optionItemRtt -> { + binding.optionItemRoster.isActivated = false + binding.optionItemChat.isActivated = false } + binding.optionItemRoster -> { binding.optionItemSetting.isActivated = false binding.optionItemChat.isActivated = false + binding.optionItemRtt.isActivated = false } + binding.optionItemChat -> { binding.optionItemRoster.isActivated = false binding.optionItemSetting.isActivated = false + binding.optionItemRtt.isActivated = false } } } @@ -492,6 +509,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite when (roomType) { RoomType.ONE_ON_ONE -> { binding.optionItemSetting.visibility = View.VISIBLE + binding.optionItemRtt.visibility = View.GONE binding.optionItemToolbox.visibility = View.GONE binding.optionItemChat.visibility = View.VISIBLE binding.optionItemRoster.visibility = View.GONE @@ -499,8 +517,10 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemWhiteboardTool.visibility = GONE initChat() } + RoomType.SMALL_CLASS -> { binding.optionItemSetting.visibility = View.VISIBLE + binding.optionItemRtt.visibility = View.VISIBLE binding.optionItemToolbox.visibility = View.GONE binding.optionItemRoster.visibility = View.VISIBLE binding.optionItemHandup.visibility = View.VISIBLE @@ -509,17 +529,21 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemChat.visibility = View.VISIBLE initChat() } + RoomType.LARGE_CLASS -> { binding.optionItemSetting.visibility = View.VISIBLE + binding.optionItemRtt.visibility = View.GONE binding.optionItemToolbox.visibility = View.GONE binding.optionItemChat.visibility = View.GONE binding.optionItemRoster.visibility = View.GONE binding.optionItemHandup.visibility = View.VISIBLE binding.optionItemWhiteboardTool.visibility = GONE } + RoomType.GROUPING_CLASS -> { binding.optionItemAsking.visibility = View.VISIBLE binding.optionItemSetting.visibility = View.VISIBLE + binding.optionItemRtt.visibility = View.GONE binding.optionItemToolbox.visibility = View.GONE binding.optionItemRoster.visibility = View.VISIBLE binding.optionItemHandup.visibility = View.VISIBLE @@ -538,6 +562,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemRoster.visibility = View.GONE binding.optionItemHandup.visibility = View.GONE } + RoomType.SMALL_CLASS -> { binding.optionItemRoster.visibility = View.VISIBLE binding.optionItemHandup.visibility = View.VISIBLE @@ -545,6 +570,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemChat.visibility = View.VISIBLE initChat() } + RoomType.LARGE_CLASS -> { binding.optionItemChat.visibility = View.GONE binding.optionItemRoster.visibility = View.VISIBLE @@ -555,6 +581,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite } binding.optionItemToolbox.visibility = View.GONE + binding.optionItemRtt.visibility = View.GONE binding.optionItemSetting.visibility = View.VISIBLE //setWhiteboardViewTool(AgoraEduApplianceData.isGrantBoard(eduCore)) } else { @@ -562,6 +589,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite when (roomType) { RoomType.ONE_ON_ONE -> { binding.optionItemSetting.visibility = View.VISIBLE + binding.optionItemRtt.visibility = View.GONE binding.optionItemToolbox.visibility = View.GONE binding.optionItemChat.visibility = View.VISIBLE initChat() @@ -569,8 +597,10 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemHandup.visibility = View.GONE binding.optionItemWhiteboardTool.visibility = GONE } + RoomType.SMALL_CLASS -> { binding.optionItemSetting.visibility = View.VISIBLE + binding.optionItemRtt.visibility = View.GONE binding.optionItemToolbox.visibility = View.GONE binding.optionItemRoster.visibility = View.GONE binding.optionItemHandup.visibility = View.GONE @@ -582,6 +612,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite RoomType.LARGE_CLASS -> { binding.optionItemSetting.visibility = View.VISIBLE + binding.optionItemRtt.visibility = View.GONE binding.optionItemToolbox.visibility = View.GONE binding.optionItemChat.visibility = View.GONE binding.optionItemRoster.visibility = View.GONE @@ -633,6 +664,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite super.release() popupViewChat?.release() agroSettingWidget?.release() + rttOptionsManager.release() popupViewRoster?.release() eduContext?.roomContext()?.removeHandler(roomHandler) eduContext?.userContext()?.removeHandler(userHandler) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt new file mode 100644 index 000000000..c1bb7d5da --- /dev/null +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt @@ -0,0 +1,184 @@ +package io.agora.online.options + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewTreeObserver.OnGlobalLayoutListener +import androidx.constraintlayout.widget.ConstraintLayout +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import io.agora.online.R +import io.agora.online.component.common.AbsAgoraEduComponent +import io.agora.online.databinding.FcrOnlineEduRttOptionsComponentBinding +import io.agora.online.helper.RttOptionsManager +import java.text.MessageFormat + + +class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { + constructor(context: Context) : super(context) + constructor(context: Context, attr: AttributeSet) : super(context, attr) + constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr) + + private var binding: FcrOnlineEduRttOptionsComponentBinding = + FcrOnlineEduRttOptionsComponentBinding.inflate(LayoutInflater.from(context), this, true) + + private var rttOptionsManager: RttOptionsManager? = null + + init { + binding.root.setOnClickListener { + rttOptionsManager?.openSetting() + } + } + + fun initView(rttOptionsManager: RttOptionsManager) { + this.rttOptionsManager = rttOptionsManager + binding.agoraFcrRttTextDialogClose.setOnClickListener { + rttOptionsManager.closeSubtitles() + } + } + + private var touchX: Float = 0F + private var touchY: Float = 0F + private var move = false + override fun dispatchTouchEvent(event: MotionEvent?): Boolean { + when (event?.action) { + MotionEvent.ACTION_DOWN -> { + touchX = event.x + touchY = event.y + move = false + } + + MotionEvent.ACTION_MOVE -> { + move = true + val params = layoutParams as MarginLayoutParams + params.leftMargin = Math.max(0, Math.min(params.leftMargin + (event.x - touchX).toInt(), (parent as View).width - width)) + params.bottomMargin = Math.max(0, Math.min(params.bottomMargin - (event.y - touchY).toInt(), (parent as View).height - height)) + setLayoutParams(params) + return true + } + + MotionEvent.ACTION_UP -> { + if (move) { + return true + } + } + } + return super.dispatchTouchEvent(event) + } + + /** + * 重置显示位置 + */ + fun resetShowPosition() { + if (width == 0) { + viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { + override fun onGlobalLayout() { + resetShowPosition() + viewTreeObserver.removeOnGlobalLayoutListener(this) + } + }) + } else { + val params = layoutParams as MarginLayoutParams + params.leftMargin = ((parent as View).width - width) / 2 + params.bottomMargin = 100 + if (params is ConstraintLayout.LayoutParams) { + params.topToTop = -1 + params.bottomToBottom = (parent as View).id + } + setLayoutParams(params) + } + } + + /** + * 显示的时候需要再树布局测绘完成后再显示 + */ + override fun setVisibility(visibility: Int) { + resetShowPosition() + super.setVisibility(visibility) + } + + /** + * 设置限时体验信息 + */ + fun setExperienceInfo(allowUseConfig: Boolean, rttExperienceDefaultTime: Int, rttExperienceReduceTime: Int) { + runOnUIThread { + if (allowUseConfig) { + binding.agoraFcrRttTextDialogHintLayout.visibility = View.GONE + } else { + binding.agoraFcrRttTextDialogHintLayout.visibility = View.VISIBLE + if (rttExperienceReduceTime <= 0) { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setText(R.string.fcr_dialog_rtt_time_limit_end) + binding.fcrOnlineEduRttConversionDialogTimeLimitReduce.text = + MessageFormat.format(resources.getString(R.string.fcr_dialog_rtt_subtitles_dialog_time_limit_end), + rttExperienceDefaultTime / 60000) + } else { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setText(R.string.fcr_dialog_rtt_time_limit) + binding.fcrOnlineEduRttConversionDialogTimeLimitReduce.text = MessageFormat.format( + resources.getString(R.string.fcr_dialog_rtt_subtitles_dialog_time_limit_reduce), + rttExperienceDefaultTime / 60000, + if (rttExperienceReduceTime / 60000 > 9) rttExperienceReduceTime / 60000 else "0${rttExperienceReduceTime / 60000}", + if (rttExperienceReduceTime % 60000 / 1000 > 9) rttExperienceReduceTime % 60000 / 1000 else "0${rttExperienceReduceTime % 60000 / 1000}", + ) + } + } + } + } + + /** + * 设置显示状态信息 + * @param showIcon 是否显示图标 + * @param showProgress 是否显示进度圈 + */ + fun setShowStatusInfo(showProgress: Boolean, showIcon: Boolean, text: String) { + runOnUIThread { + binding.agoraFcrRttTextDialogLayoutStatus.visibility = View.VISIBLE + binding.agoraFcrRttTextDialogLayoutText.visibility = View.GONE + binding.agoraFcrRttTextDialogStatusText.text = text + binding.agoraFcrRttTextDialogProgress.visibility = if (showProgress) VISIBLE else GONE + binding.agoraFcrRttTextDialogIcon.visibility = if (showIcon) VISIBLE else GONE + } + } + + /** + * 设置显示翻译信息 + * @param headImage 头像 + * @param name 用户名称 + * @param originText 翻译原文 + * @param resultText 翻译结果 + */ + fun setShowTranslatorsInfo(headImage: String, name: String, originText: String, resultText: String? = null) { + runOnUIThread { + binding.agoraFcrRttTextDialogLayoutStatus.visibility = View.GONE + binding.agoraFcrRttTextDialogLayoutText.visibility = View.VISIBLE + Glide.with(this).load(headImage).skipMemoryCache(true).placeholder(R.drawable.agora_video_ic_audio_on) + .apply(RequestOptions.circleCropTransform()).into(binding.agoraFcrRttTextDialogUserHeader) + binding.agoraFcrRttTextDialogUserName.text = name + binding.agoraFcrRttTextDialogTextOrigin.text = originText + binding.agoraFcrRttTextDialogTextResult.text = resultText + binding.agoraFcrRttTextDialogTextResult.visibility = if (resultText.isNullOrEmpty()) View.GONE else View.VISIBLE + } + } + +} + + + + + + + + + + + + + + + + + + + + From 6df296f94ba64cdc9fabed1f3d6208d76c0fe7e7 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 27 Jun 2024 18:08:05 +0800 Subject: [PATCH 04/32] =?UTF-8?q?=E6=B7=BB=E5=8A=A0rtt=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=B1=BB=E4=BB=A5=E5=8F=8A=E5=85=A5=E5=8F=A3=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agora/online/helper/RttOptionsManager.kt | 1391 +++++++++++++++++ .../online/sdk/AgoraOnlineClassActivity.kt | 35 +- .../sdk/common/AgoraEduClassActivity.kt | 143 +- .../io/agora/education/EduApplication.java | 2 +- 4 files changed, 1478 insertions(+), 93 deletions(-) create mode 100644 AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt new file mode 100644 index 000000000..fda82a757 --- /dev/null +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt @@ -0,0 +1,1391 @@ +package io.agora.online.helper + +import android.content.Context +import android.os.Handler +import android.os.Message +import android.view.View +import android.view.ViewGroup +import androidx.annotation.StringRes +import io.agora.agoraeducore.core.AgoraEduCore +import io.agora.agoraeducore.core.context.AgoraEduContextUserInfo +import io.agora.agoraeducore.core.context.EduContextUserLeftReason +import io.agora.agoraeducore.core.context.StreamContext +import io.agora.agoraeducore.core.internal.base.http.AppRetrofitManager +import io.agora.agoraeducore.core.internal.education.impl.network.HttpBaseRes +import io.agora.agoraeducore.core.internal.education.impl.network.HttpCallback +import io.agora.agoraeducore.core.internal.framework.impl.handler.RoomHandler +import io.agora.agoraeducore.core.internal.framework.impl.handler.StreamHandler +import io.agora.agoraeducore.core.internal.framework.impl.handler.UserHandler +import io.agora.agoraeducore.core.internal.framework.utils.GsonUtil +import io.agora.agoraeducore.core.internal.log.LogX +import io.agora.online.R +import io.agora.online.component.AgoraEduRttComponent +import io.agora.online.component.common.IAgoraUIProvider +import io.agora.online.component.dialog.AgoraUIRttConversionDialogBuilder +import io.agora.online.component.dialog.AgoraUIRttSettingBuilder +import io.agora.online.component.dialog.AgoraUIRttSettingDialogListener +import io.agora.online.component.dialog.ConversionOptionsInterface +import io.agora.online.component.toast.AgoraUIToast +import io.agora.online.easeim.utils.TAG +import io.agora.online.options.AgoraEduOptionsComponent +import io.agora.online.options.AgoraEduRttOptionsComponent +import io.agora.online.util.MultiLanguageUtil +import io.agora.rtc.audio2text.Audio2TextProtobuffer +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.Headers +import retrofit2.http.PUT +import retrofit2.http.Path +import java.util.Locale +import java.util.UUID + +/** + * 功能作用:Rtt相关操作管理 + * 创建人:王亮(Loren) + * 思路: + * 方法: + * 注意: + * 修改人: + * 修改时间: + * 备注: + * + * 需求: + * 如有人正在说话,字母区域提示:listening(正在聆听) ... + * 如没有人说话,提示:no one is currently speaking(当前没有人在说话) + * 房间内有音频输出时,字幕区域显示对应转写文字,无音频输出时,字幕区域在音频停止3S后自动消失 + * + * 场景: + * 音频流结束&翻译未结束 + * 音频流未结束&翻译未结束 + * 音频流未结束&上一次翻译结束 + * 音频流结束&当前翻译结束 + * + * + * 实现逻辑: + * + * + * + * @author 王亮(Loren) + */ +class RttOptionsManager(internal val rttOptions: IRttOptions) { + + private val TAG = "RttOptionsManager:" + + /** + * 字幕工具箱组件 + */ + var rttToolBoxWidget: AgoraEduRttComponent? = null + private set + + /** + * 教室相关信息 + */ + private var eduCore: AgoraEduCore? = null + + /** + * 转写管理 + */ + private val conversionManager by lazy { + RttConversionManager(this, object : ConversionOptionsInterface { + /** + * 开启转写 + */ + override fun openConversion() { + if (!isOpenSubtitles()) { + useManager.startExperience() + } + } + + /** + * 关闭转写 + */ + override fun closeConversion() { + if (!isOpenSubtitles()) { + useManager.stopExperience() + } + } + + /** + * 打开设置页面 + */ + override fun openSetting() { + if (!isOpenSubtitles()) { + openSetting() + } + } + }) + } + + /** + * 字幕管理 + */ + private val subtitlesManager by lazy { + RttSubtitlesManager(this, object : SubtitlesOptionsInterface { + override fun open() { + if (!isOpenConversion()) { + useManager.startExperience() + } + } + + override fun close() { + if (!isOpenConversion()) { + useManager.stopExperience() + } + } + }) + } + + /** + * 设置管理 + */ + private val settingsManager by lazy { + RttSettingManager(this, object : SettingOptionsInterface {}) + } + + /** + * 使用管理 + */ + private val useManager by lazy { + RttUseManager(this, false, object : ExperienceOptionsInterface { + /** + * 当前倒计时信息 + * @param configAllowUse 配置是否允许使用rtt + * @param defTime 默认体验时间 + * @param reduceTime 体验剩余时间 + */ + override fun countDownCurrent(configAllowUse: Boolean, defTime: Int, reduceTime: Int) { + setExperienceInfo(configAllowUse, defTime, reduceTime) + } + + /** + * 结束体验 + */ + override fun stop() { + setShowStatusInfo(showProgress = false, showIcon = false, + text = rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) + } + }) + } + + /** + * 父布局工具视图 + */ + private var agoraEduOptionsComponent: AgoraEduOptionsComponent? = null + + /** + * 流回调监听 + */ + private val steamHandler = object : StreamHandler() { + override fun onStreamMessage(channelId: String, streamId: Int, data: ByteArray?) { + super.onStreamMessage(channelId, streamId, data) + val parseFrom = Audio2TextProtobuffer.Text.parseFrom(data) + val recordItem = useManager.disposeData(parseFrom, settingsManager.currentSettingInfo, eduCore?.eduContextPool()?.streamContext()) + subtitlesManager.setShowCurrentData(recordItem, settingsManager.currentSettingInfo) + conversionManager.updateShowList(useManager.getRecordList()) + LogX.i(TAG, "onStreamMessage channelId=$channelId, streamId=$streamId, data=${GsonUtil.toJson(parseFrom)}") + + } + + override fun onStreamMessageError( + channelId: String, + streamId: Int, + error: Int, + missed: Int, + cached: Int, + ) { + super.onStreamMessageError(channelId, streamId, error, missed, cached) + LogX.i(TAG, "onStreamMessageError channelId=$channelId, streamId=$streamId, error=$error, missed=$missed, cached=$cached") + } + } + + /** + * 房间回调监听 + */ + private val roomHandler = object : RoomHandler() { + override fun onRoomPropertiesUpdated(properties: Map, cause: Map?, operator: AgoraEduContextUserInfo?) { + super.onRoomPropertiesUpdated(properties, cause, operator) + LogX.i(TAG, "onRoomPropertiesUpdated properties=${GsonUtil.toJson(properties)}, cause=${ + cause?.let { GsonUtil.toJson(it) } + }, operator=${operator?.let { GsonUtil.toJson(it) }}") + } + } + + /** + * 用户监听 + */ + private val userHandler = object : UserHandler() { + override fun onRemoteUserLeft(user: AgoraEduContextUserInfo, operator: AgoraEduContextUserInfo?, reason: EduContextUserLeftReason) { + super.onRemoteUserLeft(user, operator, reason) + useManager.formatAllUserInfo(eduCore?.eduContextPool()?.streamContext()) + } + + override fun onRemoteUserJoined(user: AgoraEduContextUserInfo) { + super.onRemoteUserJoined(user) + useManager.formatAllUserInfo(eduCore?.eduContextPool()?.streamContext()) + } + } + + init { + AgoraUIToast.init(rttOptions.getApplicationContext()) + } + + /** + * 初始化视图 + * @param rttTipLeftTopStatus 左上角撰写中提示控件(布局内部组件) + * @param rttBottomCenterSubtitlesView 底部中间的字幕显示控件(布局内部组件) + * @param agoraUIProvider UI配置信息 + */ + fun initView(rttTipLeftTopStatus: ViewGroup, rttBottomCenterSubtitlesView: AgoraEduRttOptionsComponent, agoraUIProvider: IAgoraUIProvider) { + conversionManager.rttTipLeftTopConversionStatusView = rttTipLeftTopStatus + subtitlesManager.rttBottomCenterSubtitlesView = rttBottomCenterSubtitlesView + subtitlesManager.rttBottomCenterSubtitlesView!!.initView(this) + //生成动态的工具箱显示组件 + this.rttToolBoxWidget = AgoraEduRttComponent(rttOptions.getActivityContext()) + this.rttToolBoxWidget!!.initView(this, agoraUIProvider) + this.eduCore = agoraUIProvider.getAgoraEduCore() + //重置视图 + this.setRttFunctionStatusConfig() + //添加监听 + this.eduCore?.eduContextPool()?.streamContext()?.addHandler(steamHandler) + this.eduCore?.eduContextPool()?.roomContext()?.addHandler(roomHandler) + this.eduCore?.eduContextPool()?.userContext()?.addHandler(userHandler) + } + + /** + * 是否开启了字幕 + */ + fun isOpenSubtitles(): Boolean { + return subtitlesManager.isOpenSubtitles() + } + + /** + * 是否开启了转写 + */ + fun isOpenConversion(): Boolean { + return conversionManager.isOpenConversion() + } + + /** + * 是否允许使用Rtt功能 + */ + fun isAllowUseRtt(): Boolean { + return useManager.isAllowUse() + } + + /** + * 设置工具view的组件 + */ + fun setEduOptionsComponent(agoraEduOptionsComponent: AgoraEduOptionsComponent) { + this.agoraEduOptionsComponent = agoraEduOptionsComponent + } + + /** + * 设置RTT功能状态配置 + * @param allowUse 是否允许使用rtt功能,不可使用的话则提供体验时间 + */ + fun setRttFunctionStatusConfig(allowUse: Boolean = false) { + useManager.configAllowUse = allowUse + this.rttToolBoxWidget?.setAllowUse(useManager.isAllowUse(), useManager.rttExperienceReduceTime) + if (!allowUse) { + this.resetShow() + } + } + + /** + * 释放相关 + */ + fun release() { + rttToolBoxWidget?.release() + conversionManager.resetShow() + subtitlesManager.resetShow() + this.eduCore?.eduContextPool()?.streamContext()?.removeHandler(steamHandler) + this.eduCore?.eduContextPool()?.roomContext()?.removeHandler(roomHandler) + this.eduCore?.eduContextPool()?.userContext()?.removeHandler(userHandler) + } + + /** + * 开启字幕 + */ + fun openSubtitles() { + this.agoraEduOptionsComponent?.hiddenRtt() + subtitlesManager.openSubtitles(useManager.configAllowUse, useManager.rttExperienceDefaultTime, useManager.rttExperienceReduceTime) + } + + /** + * 关闭字幕 + */ + fun closeSubtitles() { + this.agoraEduOptionsComponent?.hiddenRtt() + subtitlesManager.closeSubtitles() + } + + /** + * 开启转写 + */ + fun openConversion() { + this.agoraEduOptionsComponent?.hiddenRtt() + conversionManager.openConversion(useManager.getRecordList()) + } + + /** + * 关闭转写 + */ + fun closeConversion() { + this.agoraEduOptionsComponent?.hiddenRtt() + conversionManager.closeConversion() + } + + /** + * 开启设置 + */ + fun openSetting() { + this.settingsManager.openSetting() + } + + /** + * 关闭设置 + */ + fun closeSetting() { + this.agoraEduOptionsComponent?.hiddenRtt() + this.settingsManager.closeSetting() + } + + /** + * 设置转换源语言 + */ + fun setSourceLanguage(lan: RttLanguageEnum) { + settingsManager.currentSettingInfo.sourceLan = lan + sendRequest(isOpenConversion() || isOpenSubtitles(), isOpenSubtitles(), object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + useManager.setLastFinal() + } + }) + } + + /** + * 设置转换目标语言 + */ + fun setTargetLanguage(lan: Array) { + settingsManager.currentSettingInfo.targetLan = lan + sendRequest(isOpenConversion() || isOpenSubtitles(), isOpenSubtitles(), object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + useManager.setLastFinal() + } + }) + } + + /** + * 硒鼓双语显示 + */ + fun changeDoubleShow(showDouble: Boolean) { + settingsManager.currentSettingInfo.showDoubleLan = showDouble + } + + /** + * 重置edu字幕工具箱 + */ + fun resetEduRttToolBoxStatus() { + rttToolBoxWidget?.resetStatus(useManager.rttExperienceReduceTime) + } + + /** + * 设置语言列表 + */ + fun setSourceListLanguages(list: ArrayList) { + this.settingsManager.currentSettingInfo.sourceListLan.clear() + this.settingsManager.currentSettingInfo.sourceListLan.addAll(list) + this.settingsManager.currentSettingInfo.sourceListLan.distinct() + } + + /** + * 新增语言列表 + */ + fun setTargetListLanguages(list: ArrayList) { + this.settingsManager.currentSettingInfo.targetListLan.clear() + this.settingsManager.currentSettingInfo.targetListLan.addAll(list) + this.settingsManager.currentSettingInfo.sourceListLan.distinct() + } + + /** + * 发起请求 + * @param openRtt 是否打开rtt功能 + * @param openRttSubtitles 是否开启字幕 + */ + internal fun sendRequest(openRtt: Boolean, openRttSubtitles: Boolean, callback: HttpCallback>? = null) { + val body = RttChangeOptionsBody(openRttSubtitles, settingsManager.currentSettingInfo.sourceLan.value, + settingsManager.currentSettingInfo.targetLan.map { it.value }.toTypedArray()) + val call = AppRetrofitManager.instance().getService(IRttOptionsService::class.java) + .buildTokens(eduCore?.config?.appId, eduCore?.config?.roomUuid, if (openRtt) 1 else 0, body) + AppRetrofitManager.exc(call, object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + useManager.setExperienceReduceTime(useManager.rttExperienceDefaultTime - ((result?.data?.duration ?: 0) * 1000)) + callback?.onSuccess(result) + } + + override fun onError(httpCode: Int, code: Int, message: String?) { + callback?.onError(httpCode, code, message) + } + }) + } + + /** + * 重置显示 + */ + private fun resetShow() { + conversionManager.resetShow() + subtitlesManager.resetShow() + this.agoraEduOptionsComponent?.hiddenRtt() + } + + /** + * 设置状态信息 + */ + private fun setShowStatusInfo(showProgress: Boolean, showIcon: Boolean, text: String) { + subtitlesManager.setShowStatusInfo(showProgress, showIcon, text) + } + + /** + * 设置体验信息 + */ + private fun setExperienceInfo(configAllowUse: Boolean, defTime: Int, reduceTime: Int) { + subtitlesManager.setExperienceInfo(configAllowUse, defTime, reduceTime) + conversionManager.setExperienceInfo(configAllowUse, reduceTime) + } + +} + +/** + * rtt操作接口 + */ +interface IRttOptions { + /** + * 获取应用实例 + */ + fun getApplicationContext(): Context + + /** + * 当前页面实例 + */ + fun getActivityContext(): Context + + /** + * 判断并切换主线程 + */ + fun runOnUiThread(runnable: Runnable) +} + +/** + * 接口中请求的语言配置信息 + */ +internal class RttChangOptionsLanguage(val source: String, targetList: Array) { + val target: Array = targetList.filter { it.isNotEmpty() }.toTypedArray() +} + +/** + * 接口请求的语言配置信息响应 + */ +internal data class RttChangeOptionsRes( + /** + * 已用时间 + */ + val duration: Int? = null, + /** + * 语言配置 + */ + val languages: RttChangOptionsLanguage? = null, + /** + * 开始时间 + */ + val startTime: Long? = null, + /** + * 1-开启rtt,0-关闭rtt + */ + val state: Int? = null, + /** + * 流Id + */ + val streamUuid: String? = null, + /** + * 1-开启了字幕,0-关闭了字幕 + */ + val subtitle: Int? = null, + /** + * 任务id + */ + val taskId: String? = null, + /** + * token信息 + */ + val token: String? = null, + /** + * 1-开启了转写,0-关闭了转写 + */ + val transcribe: Int? = null, +) + +/** + * RTT转写记录 + */ +class RttRecordItemTran { + /** + * 目标语言 + */ + var language: RttLanguageEnum? = null + + /** + * 文案 + */ + var text: String? = null +} + +/** + * RTT记录 + */ +class RttRecordItem { + /** + * 转写和字幕翻译之外的状态文本 + */ + var statusText: String? = null + + /** + * 唯一id + */ + var uuid: String? = null + + /** + * 语言信息 + */ + var sourceLan: RttLanguageEnum? = null + + /** + * 当前翻译的目标语言 + */ + var currentTargetLan: Array? = null + + /** + * 置信度 + */ + var sourceConfidence: Double? = null + + /** + * 转写内容对应的Rtc uid + */ + var uid: Int? = null + + /** + * 说话的用户昵称 + */ + var userName: String? = null + + /** + * 说话的用户头像 + */ + var userHeader: String? = null + + /** + * 源文案 + */ + var sourceText: String? = null + + /** + * 目标文案 + */ + var targetText: String? = null + + /** + * 目标信息 + */ + var targetInfo: ArrayList? = null + + /** + * 时间 + */ + var time: Long? = null + + /** + * 是否是最终结果 + */ + var isFinal: Boolean = false + +} + +/** + * 当前语言配置信息 + * @param sourceListLan 可用的源语言列表 + * @param targetListLan 可用的目标语言列表 + * @param sourceLan 转换的源语言 + * @param targetLan 转换的目标语言 + * @param showDoubleLan 是否显示双语 + * @param rttTranslatorsRecordList 转换数据记录 + */ +class RttSettingInfo( + rttOptionsManager: RttOptionsManager, + val sourceListLan: ArrayList = arrayListOf(RttLanguageEnum.ZH_CN, RttLanguageEnum.EN_US, RttLanguageEnum.JA_JP), + val targetListLan: ArrayList = arrayListOf(RttLanguageEnum.NONE, RttLanguageEnum.ZH_CN, RttLanguageEnum.EN_US, + RttLanguageEnum.JA_JP), + var sourceLan: RttLanguageEnum = RttLanguageEnum.ZH_CN, + var targetLan: Array = arrayOf(RttLanguageEnum.NONE), + var showDoubleLan: Boolean = false, +) { + init { + val locale: Locale = MultiLanguageUtil.getAppLocale(rttOptionsManager.rttOptions.getActivityContext()) + // zh_CN || zh_TW + val sysLanguage = locale.language // zh + val sysCountry = locale.country // CN + //默认源语言设置 + sourceLan = + if (sysLanguage.equals(Locale.SIMPLIFIED_CHINESE.language, ignoreCase = true) && sysCountry.equals(Locale.SIMPLIFIED_CHINESE.country, + ignoreCase = true)) { + RttLanguageEnum.ZH_CN + } else if (sysLanguage.equals(Locale.US.language, ignoreCase = true) && sysCountry.equals(Locale.US.country, ignoreCase = true)) { + RttLanguageEnum.EN_US + } else if (Locale.getDefault().language.equals(Locale.SIMPLIFIED_CHINESE.language, + ignoreCase = true) && Locale.getDefault().country.equals(Locale.SIMPLIFIED_CHINESE.country, ignoreCase = true)) { + RttLanguageEnum.ZH_CN + } else if (Locale.getDefault().language.equals(Locale.US.language, ignoreCase = true) && Locale.getDefault().country.equals( + Locale.US.country, ignoreCase = true)) { + RttLanguageEnum.EN_US + } else { + RttLanguageEnum.EN_US + } + } +} + +/** + * RTT可用语言枚举 + */ +enum class RttLanguageEnum(@StringRes val textRes: Int, val value: String) { + /** + * 不翻译 + */ + NONE(R.string.fcr_dialog_rtt_language_none, ""), + + /** + * Chinese (Cantonese, Traditional) + */ + ZH_HK(R.string.fcr_dialog_rtt_language_zh_hk, "zh-HK"), + + /** + * Chinese (Mandarin, Simplified) + */ + ZH_CN(R.string.fcr_dialog_rtt_language_zh_cn, "zh-CN"), + + /** + * Chinese (Taiwanese Putonghua) + */ + ZH_TW(R.string.fcr_dialog_rtt_language_zh_tw, "zh-TW"), + + /** + * English (India) + */ + EN_IN(R.string.fcr_dialog_rtt_language_en_in, "en-IN"), + + /** + * English (US) + */ + EN_US(R.string.fcr_dialog_rtt_language_en_us, "en-US"), + + /** + * French (French) + */ + FR_FR(R.string.fcr_dialog_rtt_language_fr_fr, "fr-FR"), + + /** + * German (Germany) + */ + DE_DE(R.string.fcr_dialog_rtt_language_de_de, "de-DE"), + + /** + * Hai (Thailand) + */ + TH_TH(R.string.fcr_dialog_rtt_language_th_th, "th-TH"), + + /** + * Hindi (India) + */ + HI_IN(R.string.fcr_dialog_rtt_language_hi_in, "hi-IN"), + + /** + * Indonesian (Indonesia) + */ + ID_ID(R.string.fcr_dialog_rtt_language_id_id, "id-ID"), + + /** + * Italian (Italy) + */ + IT_IT(R.string.fcr_dialog_rtt_language_it_it, "it-IT"), + + /** + * Japanese (Japan) + */ + JA_JP(R.string.fcr_dialog_rtt_language_ja_jp, "ja-JP"), + + /** + * Korean (South Korea) + */ + KO_KR(R.string.fcr_dialog_rtt_language_ko_kr, "ko-KR"), + + /** + * Malay (Malaysia) + */ + MS_MY(R.string.fcr_dialog_rtt_language_ms_my, "ms-MY*"), + + /** + * Persian (Iran) + */ + FA_IR(R.string.fcr_dialog_rtt_language_fa_ir, "fa-IR*"), + + /** + * Portuguese (Portugal) + */ + PT_PT(R.string.fcr_dialog_rtt_language_pt_pt, "pt-PT"), + + /** + * Russian (Russia) + */ + RU_RU(R.string.fcr_dialog_rtt_language_ru_ru, "ru-RU"), + + /** + * Spanish (Spain) + */ + ES_ES(R.string.fcr_dialog_rtt_language_es_es, "es-ES"), + + /** + * Turkish (Turkey) + */ + TR_TR(R.string.fcr_dialog_rtt_language_tr_tr, "tr-TR"), + + /** + * Vietnamese (Vietnam) + */ + VI_VN(R.string.fcr_dialog_rtt_language_vi_vn, "vi-VN"), +} + +private interface IRttOptionsService { + /** + * 修改RTT配置信息 + */ + @Headers("Content-Type: application/json") + @PUT("edu/apps/{appId}/v2/rooms/{roomId}/widgets/rtt/states/{state}") + fun buildTokens( + @Path("appId") appId: String?, @Path("roomId") roomId: String?, @Path("state") state: Int?, + @Body body: RttChangeOptionsBody, + ): Call> +} + +/** + * 修改RTT配置操作请求实体 + */ +private class RttChangeOptionsBody( + openSubtitle: Boolean, sourceLan: String, + targetLan: Array, +) { + /** + * 语言配置 + */ + val languages = RttChangOptionsLanguage(sourceLan, targetLan) + + /** + * 是否开启翻译 + */ + val transcribe: Int = if (targetLan.isNotEmpty()) 1 else 0 + + /** + * 是否开启转写 + */ + val subtitle: Int = if (openSubtitle) 1 else 0 +} + +/** + * Rtt-字幕逻辑管理 + */ +private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManager, private val optionsCallback: SubtitlesOptionsInterface) { + + /** + * 字幕组件 + */ + var rttBottomCenterSubtitlesView: AgoraEduRttOptionsComponent? = null + + /** + * 是否开启成功 + */ + private var openSuccess = false + + /** + * 字幕开启成功 + */ + private val messageWhatOpenSuccess = 0 + + /** + * 无人讲话的消息类型 + */ + private val messageWhatNoSpeaking = 1 + + /** + * 所有的定时相关的处理 + */ + private val handler: Handler = object : Handler(rttOptionsManager.rttOptions.getActivityContext().mainLooper) { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + when (msg.what) { + messageWhatOpenSuccess -> { + setShowStatusInfo(showProgress = false, showIcon = false, + text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) + openSuccess = true + optionsCallback.open() + } + + messageWhatNoSpeaking -> { + changeEduOptionsComponent(View.GONE) + } + } + } + } + + /** + * 修改字幕显示状态 + */ + private fun changeEduOptionsComponent(toView: Int) { + rttOptionsManager.rttOptions.runOnUiThread { + this.rttBottomCenterSubtitlesView?.visibility = toView + } + } + + /** + * 开启字幕 + * @param configAllowUse 配置是否允许rtt + * @param experienceDefaultTime 体验默认时间 + * @param experienceReduceTime 体验剩余时间 + */ + fun openSubtitles(configAllowUse: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) { + changeEduOptionsComponent(View.VISIBLE) + if (rttOptionsManager.isAllowUseRtt()) { + //可以使用的话先隐藏体验,后续再根据条件判断是否显示 + setExperienceInfo(true, 0, 0) + //显示文案 + setShowStatusInfo(showProgress = true, showIcon = false, + text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening)) + //发起开启rtt请求 + rttOptionsManager.sendRequest(openRtt = true, openRttSubtitles = true, + callback = object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + subtitlesOpenSuccess() + } + + override fun onError(httpCode: Int, code: Int, message: String?) { + openSuccess = false + optionsCallback.close() + } + }) + } else { + setExperienceInfo(configAllowUse, experienceDefaultTime, experienceReduceTime) + setShowStatusInfo(showProgress = false, showIcon = false, + text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) + openSuccess = false + optionsCallback.close() + } + } + + /** + * 关闭字幕 + */ + fun closeSubtitles() { + openSuccess = false + this.resetShow() + //发起开启rtt请求 + rttOptionsManager.sendRequest(rttOptionsManager.isOpenConversion(), false, object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + openSuccess = false + optionsCallback.close() + } + + override fun onError(httpCode: Int, code: Int, message: String?) { + } + }) + } + + /** + * 重置显示 + */ + fun resetShow() { + this.changeEduOptionsComponent(View.GONE) + } + + /** + * 是否开启了字幕 + */ + fun isOpenSubtitles(): Boolean { + return openSuccess + } + + /** + * 设置体验信息 + */ + fun setExperienceInfo(allowUseConfig: Boolean, rttExperienceDefaultTime: Int, rttExperienceReduceTime: Int) { + rttBottomCenterSubtitlesView?.setExperienceInfo(allowUseConfig, rttExperienceDefaultTime, rttExperienceReduceTime) + } + + /** + * 设置状态细心 + */ + fun setShowStatusInfo(showProgress: Boolean, showIcon: Boolean, text: String) { + rttBottomCenterSubtitlesView?.setShowStatusInfo(showProgress = showProgress, showIcon = showIcon, text = text) + } + + /** + * 字幕开启成功 + */ + private fun subtitlesOpenSuccess() { + //延迟两秒开启文案:点击字幕位置可以更改字幕设置 + setShowStatusInfo(showProgress = false, showIcon = false, text = rttOptionsManager.rttOptions.getApplicationContext() + .getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening_success_hint)) + handler.sendEmptyMessageDelayed(messageWhatOpenSuccess, 2000) + } + + /** + * 设置当前翻译数据 + */ + fun setShowCurrentData(recordItem: RttRecordItem?, currentSettingInfo: RttSettingInfo) { + if (isOpenSubtitles()) { + changeEduOptionsComponent(View.VISIBLE) + handler.removeMessages(messageWhatOpenSuccess) + handler.removeMessages(messageWhatNoSpeaking) + + //是否开启双语显示 + val showTranslateOnly = currentSettingInfo.showDoubleLan + val sourceText = recordItem?.sourceText + val targetText = recordItem?.targetText + val translating = targetText.isNullOrEmpty() && showTranslateOnly + + if (sourceText.isNullOrEmpty() && !translating) { + setShowStatusInfo(showProgress = false, showIcon = false, + text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) + } else if (translating) { + setShowStatusInfo(showProgress = false, showIcon = true, + text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_subtitles_text_listening)) + } else { + rttBottomCenterSubtitlesView?.setShowTranslatorsInfo(recordItem?.userHeader ?: "", recordItem?.userName ?: "", sourceText ?: "", + targetText) + } + //房间内有音频输出时,字幕区域显示对应转写文字,无音频输出时,字幕区域在音频停止3S后自动消失 + handler.sendEmptyMessageDelayed(messageWhatNoSpeaking, 3000) + } + } +} + +/** + * Rtt-转写逻辑管理 + */ +private class RttConversionManager(private val rttOptionsManager: RttOptionsManager, private val optionsCallback: ConversionOptionsInterface) { + + /** + * 是否开启成功 + */ + private var openSuccess = false + + /** + * Rtt转写弹窗 + */ + private val rttConversionDialog by lazy { + AgoraUIRttConversionDialogBuilder(rttOptionsManager.rttOptions.getActivityContext()).build().apply { + setOnDismissListener { + this.optionsCallback!!.closeConversion() + } + optionsCallback = object : ConversionOptionsInterface { + /** + * 开启转写 + */ + override fun openConversion() { + if (rttOptionsManager.isOpenSubtitles()) { + openSuccess = true + this@RttConversionManager.optionsCallback.openConversion() + } else { + rttOptionsManager.sendRequest(openRtt = true, rttOptionsManager.isOpenSubtitles(), + callback = object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + openSuccess = true + this@RttConversionManager.optionsCallback.openConversion() + } + + override fun onError(httpCode: Int, code: Int, message: String?) { + openSuccess = false + this@RttConversionManager.optionsCallback.closeConversion() + } + }) + } + + } + + /** + * 关闭转写 + */ + override fun closeConversion() { + if (!rttOptionsManager.isOpenSubtitles()) { + rttOptionsManager.sendRequest(false, rttOptionsManager.isOpenSubtitles(), + callback = object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + openSuccess = false + this@RttConversionManager.optionsCallback.closeConversion() + } + + override fun onError(httpCode: Int, code: Int, message: String?) { + } + }) + } else { + openSuccess = false + this@RttConversionManager.optionsCallback.closeConversion() + } + } + + /** + * 打开设置页面 + */ + override fun openSetting() { + rttOptionsManager.openSetting() + } + + } + } + } + + /** + * 左上角撰写中view + */ + var rttTipLeftTopConversionStatusView: ViewGroup? = null + + /** + * 开启转写 + */ + fun openConversion(list: List) { + this.resetShow() + if (rttOptionsManager.isAllowUseRtt()) { + rttConversionDialog.show(list) + rttConversionDialog.optionsCallback!!.openConversion() + } + } + + /** + * 关闭转写 + */ + fun closeConversion() { + this.resetShow() + if (rttOptionsManager.isAllowUseRtt()) { + rttConversionDialog.dismiss() + } + } + + /** + * 重置显示 + */ + fun resetShow() { + this.rttTipLeftTopConversionStatusView?.visibility = View.GONE + } + + /** + * 是否开启了转写 + */ + fun isOpenConversion(): Boolean { + return openSuccess + } + + /** + * 设置体验信息 + */ + fun setExperienceInfo(allowUseConfig: Boolean, rttExperienceReduceTime: Int) { + rttConversionDialog.setExperienceInfo(allowUseConfig, rttExperienceReduceTime) + } + + /** + * 新增转写数据 + */ + fun updateShowList(list: List) { + if (isOpenConversion()) { + rttOptionsManager.rttOptions.runOnUiThread { + rttConversionDialog.updateShowList(list) + } + } + } +} + +/** + * Rtt-设置弹窗管理 + */ +private class RttSettingManager(private val rttOptionsManager: RttOptionsManager, private val optionsCallback: SettingOptionsInterface) { + /** + * RTT/转写设置弹窗 + */ + private val rttSettingDialog by lazy { + AgoraUIRttSettingBuilder(rttOptionsManager.rttOptions.getActivityContext()).setListener(object : AgoraUIRttSettingDialogListener { + /** + * 修改双语显示 + */ + override fun changeDoubleShow(showDouble: Boolean) { + currentSettingInfo.showDoubleLan = showDouble + rttOptionsManager.changeDoubleShow(showDouble) + } + + /** + * 设置目标语言 + */ + override fun setTargetLan(code: String) { + RttLanguageEnum.values().find { it.value == code }?.let { + rttOptionsManager.setTargetLanguage(arrayOf(it)) + } + } + + /** + * 设置声源语言 + */ + override fun setSourceLan(code: String) { + RttLanguageEnum.values().find { it.value == code }?.let { + rttOptionsManager.setSourceLanguage(it) + } + } + }).build() + } + + /** + * 当前语言的管理信息 + */ + val currentSettingInfo by lazy { RttSettingInfo(rttOptionsManager) } + + /** + * 开启设置 + */ + fun openSetting() { + if (rttOptionsManager.isAllowUseRtt()) { + rttSettingDialog.show(currentSettingInfo) + } + } + + /** + * 关闭设置 + */ + fun closeSetting() { + rttSettingDialog.dismiss() + } +} + +/** + * Rtt-使用管理 + * @param configAllowUse RTT功能是否允许使用,默认不可以使用,不可使用的话则提供体验时间 + */ +private class RttUseManager( + private val rttOptionsManager: RttOptionsManager, var configAllowUse: Boolean = false, + private val optionsCallback: ExperienceOptionsInterface, +) { + /** + * rtt功能默认体验时间,默认十分钟 + */ + val rttExperienceDefaultTime: Int = 600000 + + /** + * rtt功能剩余体验时间,默认十分钟 + */ + var rttExperienceReduceTime: Int = rttExperienceDefaultTime + private set + + /** + * 所有的定时相关的处理 + */ + private val handler: Handler = object : Handler(rttOptionsManager.rttOptions.getActivityContext().mainLooper) { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + rttExperienceReduceTime -= 1000 + if (rttExperienceReduceTime <= 0) { + optionsCallback.countDownCurrent(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) + stopExperience() + } else { + optionsCallback.countDownCurrent(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) + sendEmptyMessageDelayed(msg.what, 1000) + } + } + } + + /** + * 记录数据列表 + */ + private val recordList = arrayListOf() + + /** + * 是否允许体验 + */ + fun isAllowUse(): Boolean { + return this.configAllowUse || this.rttExperienceReduceTime > 0 + } + + /** + * 设置体验剩余时间 + */ + fun setExperienceReduceTime(time: Int) { + this.rttExperienceReduceTime = time.coerceAtMost(this.rttExperienceReduceTime).coerceAtLeast(0) + } + + /** + * 开始体验 + */ + fun startExperience() { + if (isAllowUse() && rttExperienceReduceTime > 0) { + optionsCallback.countDownCurrent(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) + handler.removeMessages(0) + handler.sendEmptyMessageDelayed(0, 1000) + } + } + + /** + * 结束体验 + */ + fun stopExperience() { + optionsCallback.stop() + handler.removeMessages(0) + } + + /** + * 处理数据 + */ + fun disposeData(rttMsgData: Audio2TextProtobuffer.Text, settingInfo: RttSettingInfo, streamContext: StreamContext?): RttRecordItem? { + val lastItem = (if (recordList.isEmpty()) null else recordList[recordList.size - 1]) + val lastItemByUid = lastItem?.uid + var paramsData: RttRecordItem? = null + when (rttMsgData.dataType) { + //转写 + "transcribe" -> { + val sourceTextStr = StringBuffer() + var final = false + var confidence = 0.0 + rttMsgData.wordsList.forEach { word -> + sourceTextStr.append(word.text) + final = word.isFinal + confidence = word.confidence + } + LogX.i(TAG, "transcribe: $lastItemByUid$$$$$$$$sourceTextStr") + if (lastItemByUid == null || rttMsgData.uid != lastItemByUid || lastItem.isFinal) { + paramsData = RttRecordItem().apply { + uuid = UUID.randomUUID().toString() + currentTargetLan = settingInfo.targetLan + sourceLan = RttLanguageEnum.values().find { it.value == rttMsgData.culture } + sourceText = sourceTextStr.toString() + uid = rttMsgData.uid + time = rttMsgData.time + isFinal = final + sourceConfidence = confidence + } + recordList.add(paramsData) + } else { + paramsData = recordList.findLast { it.uid == rttMsgData.uid }?.apply { + uuid = UUID.randomUUID().toString() + currentTargetLan = settingInfo.targetLan + sourceText = sourceTextStr.toString() + time = rttMsgData.time + isFinal = final + sourceConfidence = confidence + } + } + sourceTextStr.setLength(0) + } + //翻译 + "translate" -> { + LogX.i(TAG, "Translation:" + GsonUtil.toJson(rttMsgData)) + val tranList = arrayListOf() + val transTextStr = StringBuffer() + val lanMapText = hashMapOf() + rttMsgData.transList.forEach { transItem -> + transItem.textsList.forEach { text -> + LogX.i(TAG, "Translation:$lastItemByUid$$$$$$$$$text") + transTextStr.append(text) + } + tranList.add(RttRecordItemTran().apply { + language = RttLanguageEnum.values().find { it.value == transItem.lang } + text = transTextStr.toString() + }) + lanMapText[transItem.lang] = + if (lanMapText.contains(transItem.lang)) lanMapText[transItem.lang] + transTextStr.toString() else transTextStr.toString() + transTextStr.setLength(0) + } + + paramsData = recordList.findLast { it.uid == lastItemByUid }?.apply { + //处理拼接数据,当然,当前只有一个目标语言,为了扩展使用以下方式 + transTextStr.setLength(0) + currentTargetLan?.forEach { item -> + if (lanMapText.contains(item.value)) { + if (transTextStr.isNotEmpty()) { + transTextStr.append("\n") + } + transTextStr.append(lanMapText[item.value]) + } + } + currentTargetLan = settingInfo.targetLan + uuid = UUID.randomUUID().toString() + targetInfo = tranList + targetText = transTextStr.toString() + transTextStr.setLength(0) + } + } + } + //格式化所有的用户信息 + formatAllUserInfo(streamContext) + return if (true == paramsData?.isFinal) paramsData else null + } + + /** + * 格式化所有的用户信息 + */ + fun formatAllUserInfo(streamContext: StreamContext?) { + recordList.forEach { item -> + streamContext?.getAllStreamList()?.find { it.streamUuid == item.uid?.toString() }?.owner?.let { info -> + item.userName = info.userName + item.userHeader = info.userName + } + } + } + + /** + * 获取记录列表 + */ + fun getRecordList(): List { + return recordList.toList() + } + + /** + * 设置最后一条信息为最后 + */ + fun setLastFinal() { + if (recordList.isNotEmpty()) { + recordList.get(recordList.size - 1).isFinal = true + } + } +} + +/** + * 字幕接口逻辑处理 + */ +private interface SubtitlesOptionsInterface { + fun open() + fun close() +} + +/** + * 体验相关接口回调 + */ +private interface ExperienceOptionsInterface { + /** + * 当前倒计时信息 + * @param configAllowUse 配置是否允许使用rtt + * @param defTime 默认体验时间 + * @param reduceTime 体验剩余时间 + */ + fun countDownCurrent(configAllowUse: Boolean, defTime: Int, reduceTime: Int) + + /** + * 结束体验 + */ + fun stop() +} + +/** + * 设置相关接口回调 + */ +private interface SettingOptionsInterface { + +} + diff --git a/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassActivity.kt b/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassActivity.kt index 259fea505..280af2e9a 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassActivity.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassActivity.kt @@ -1,40 +1,50 @@ package io.agora.online.sdk +import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View import androidx.core.content.ContextCompat -import io.agora.online.component.common.UIUtils -import io.agora.online.helper.AgoraUIDeviceSetting -import io.agora.online.helper.FcrHandsUpManager -import io.agora.online.helper.RoomPropertiesHelper -import io.agora.agoraeducore.core.context.* +import io.agora.agoraeducore.core.context.AgoraEduContextClassState +import io.agora.agoraeducore.core.context.AgoraEduContextSystemDevice +import io.agora.agoraeducore.core.context.AgoraEduContextUserInfo +import io.agora.agoraeducore.core.context.EduContextCallback +import io.agora.agoraeducore.core.context.EduContextError +import io.agora.agoraeducore.core.context.EduContextRoomInfo +import io.agora.agoraeducore.core.context.FcrCustomMessage import io.agora.agoraeducore.core.internal.base.ToastManager import io.agora.agoraeducore.core.internal.framework.impl.handler.RoomHandler import io.agora.agoraeducore.core.internal.log.LogX import io.agora.agoraeducore.extensions.widgets.bean.AgoraWidgetDefaultId import io.agora.online.R +import io.agora.online.component.common.UIUtils import io.agora.online.component.dialog.AgoraUIDialog import io.agora.online.component.dialog.AgoraUIDialogBuilder import io.agora.online.component.teachaids.presenter.FCRSmallClassVideoPresenter import io.agora.online.component.toast.AgoraUIToast +import io.agora.online.databinding.ActivityAgoraOnlineClassBinding +import io.agora.online.helper.AgoraUIDeviceSetting +import io.agora.online.helper.FcrHandsUpManager +import io.agora.online.helper.IRttOptions +import io.agora.online.helper.RoomPropertiesHelper +import io.agora.online.helper.RttOptionsManager import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionPacket import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionSignal import io.agora.online.sdk.common.AgoraEduClassActivity import io.agora.online.sdk.presenter.AgoraClassVideoPresenter -import io.agora.online.databinding.ActivityAgoraOnlineClassBinding /** * author : hefeng * date : 2022/1/24 * description : 小班课(200) */ -open class AgoraOnlineClassActivity : AgoraEduClassActivity() { +open class AgoraOnlineClassActivity : AgoraEduClassActivity(), IRttOptions { override var TAG = "AgoraOnlineClassActivity" var agoraClassVideoPresenter: AgoraClassVideoPresenter? = null private lateinit var binding: ActivityAgoraOnlineClassBinding var cameraDialog: AgoraUIDialog? = null var micDialog: AgoraUIDialog? = null + private val rttOptionsManager:RttOptionsManager by lazy { RttOptionsManager(this) } protected val roomHandler = object : RoomHandler() { override fun onJoinRoomSuccess(roomInfo: EduContextRoomInfo) { @@ -225,7 +235,7 @@ open class AgoraOnlineClassActivity : AgoraEduClassActivity() { binding.agoraEduWhiteboard.initView(uuid, this) // tool bar - binding.agoraEduOptions.initView(uuid, binding.root, binding.agoraEduOptionsItemContainer, this) + binding.agoraEduOptions.initView(rttOptionsManager,uuid, binding.root, binding.agoraEduOptionsItemContainer, this) launchConfig?.shareUrl?.let { binding.agoraEduOptions.setShareRoomLink(it) } @@ -246,6 +256,8 @@ open class AgoraOnlineClassActivity : AgoraEduClassActivity() { UIUtils.setViewVisible(binding.agoraClassUserListVideo, getUIConfig().isStageVisible) UIUtils.setViewVisible(binding.agoraEduWhiteboard, getUIConfig().isEngagementVisible) UIUtils.setViewVisible(binding.agoraEduOptions, getUIConfig().isEngagementVisible) + //初始化管理器 + rttOptionsManager.initView(binding.agoraAreaBoardConversionStatus,binding.agoraRttOptions,this) } join() } @@ -303,4 +315,11 @@ open class AgoraOnlineClassActivity : AgoraEduClassActivity() { super.cancelHandsUp() binding.agoraEduOptions.cancelHandsUp() } + + /** + * 当前页面实例 + */ + override fun getActivityContext(): Context { + return this + } } \ No newline at end of file diff --git a/AgoraCloudScene/src/main/java/io/agora/online/sdk/common/AgoraEduClassActivity.kt b/AgoraCloudScene/src/main/java/io/agora/online/sdk/common/AgoraEduClassActivity.kt index f2b38e9ce..4d511fc1a 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/sdk/common/AgoraEduClassActivity.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/sdk/common/AgoraEduClassActivity.kt @@ -70,8 +70,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide private val streamHandler = object : StreamHandler() { override fun onStreamJoined(streamInfo: AgoraEduContextStreamInfo, operator: AgoraEduContextUserInfo?) { super.onStreamJoined(streamInfo, operator) - if (localStreamInfo == null && streamInfo.owner.userUuid == eduCore()?.eduContextPool()?.userContext() - ?.getLocalUserInfo()?.userUuid) { + if (localStreamInfo == null && streamInfo.owner.userUuid == eduCore()?.eduContextPool()?.userContext()?.getLocalUserInfo()?.userUuid) { localStreamInfo = streamInfo } } @@ -83,13 +82,28 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } } + override fun onStreamMessage(channelId: String, streamId: Int, data: ByteArray?) { + super.onStreamMessage(channelId, streamId, data) + LogX.i(TAG, "onStreamMessage channelId=$channelId, streamId=$streamId, data=${data?.contentToString()}") + + } + + override fun onStreamMessageError( + channelId: String, + streamId: Int, + error: Int, + missed: Int, + cached: Int, + ) { + super.onStreamMessageError(channelId, streamId, error, missed, cached) + LogX.i(TAG, "onStreamMessageError channelId=$channelId, streamId=$streamId, error=$error, missed=$missed, cached=$cached") + } + override fun onStreamUpdated(streamInfo: AgoraEduContextStreamInfo, operator: AgoraEduContextUserInfo?) { super.onStreamUpdated(streamInfo, operator) updateDevice(streamInfo) eduCore()?.eduContextPool()?.userContext()?.getLocalUserInfo()?.let { localUser -> - if (localUser.role == AgoraEduContextUserRole.Student - && localUser.userUuid == streamInfo.owner.userUuid - && operator?.role != AgoraEduContextUserRole.Student) { + if (localUser.role == AgoraEduContextUserRole.Student && localUser.userUuid == streamInfo.owner.userUuid && operator?.role != AgoraEduContextUserRole.Student) { if (localStreamInfo?.videoState?.value != streamInfo.videoState.value) {//如果上一次的视频状态和本次的视频状态不一样 when (streamInfo.streamType) { @@ -99,11 +113,9 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide // text = String.format(context.getString(R.string.fcr_stream_start_video)) // ) } + AgoraEduContextMediaStreamType.Audio, AgoraEduContextMediaStreamType.None -> { - AgoraUIToast.error( - applicationContext, - text = String.format(context.getString(R.string.fcr_switch_tips_banned_video)) - ) + AgoraUIToast.error(applicationContext, text = String.format(context.getString(R.string.fcr_switch_tips_banned_video))) } } } @@ -116,11 +128,9 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide // text = String.format(context.getString(R.string.fcr_stream_start_audio)) // ) } + AgoraEduContextMediaStreamType.Video, AgoraEduContextMediaStreamType.None -> { - AgoraUIToast.error( - applicationContext, - text = String.format(context.getString(R.string.fcr_switch_tips_muted)) - ) + AgoraUIToast.error(applicationContext, text = String.format(context.getString(R.string.fcr_switch_tips_muted))) } } } @@ -140,16 +150,16 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide AgoraEduContextSystemDevice.CameraBack } - FcrHandsUpManager.getDeviceState(eduCore(), device){ - if(it){ + FcrHandsUpManager.getDeviceState(eduCore(), device) { + if (it) { getEduContext()?.mediaContext()?.closeSystemDevice(device) } } } if (streamInfo.audioState == AgoraEduMediaState.Off) { - FcrHandsUpManager.getDeviceState(eduCore(), AgoraEduContextSystemDevice.Microphone){ - if(it){ + FcrHandsUpManager.getDeviceState(eduCore(), AgoraEduContextSystemDevice.Microphone) { + if (it) { getEduContext()?.mediaContext()?.closeSystemDevice(AgoraEduContextSystemDevice.Microphone) } } @@ -232,10 +242,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } if (connectionState == EduContextConnectionState.Aborted) { - AgoraUIToast.error( - applicationContext, - text = resources.getString(R.string.fcr_monitor_login_remote_device) - ) + AgoraUIToast.error(applicationContext, text = resources.getString(R.string.fcr_monitor_login_remote_device)) val roomUuid = eduCore()?.eduContextPool()?.roomContext()?.getRoomInfo()?.roomUuid FCRHandlerManager.roomHandlerMap.forEach { if (roomUuid == it.key) { @@ -272,13 +279,11 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(AgoraEduContextSystemDevice.Speaker) eduCore()?.eduContextPool()?.userContext()?.getLocalUserInfo()?.let { if (it.role == AgoraEduContextUserRole.Teacher) { - eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice( - if (AgoraUIDeviceSetting.isFrontCamera()) { - AgoraEduContextSystemDevice.CameraFront - } else { - AgoraEduContextSystemDevice.CameraBack - } - ) + eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(if (AgoraUIDeviceSetting.isFrontCamera()) { + AgoraEduContextSystemDevice.CameraFront + } else { + AgoraEduContextSystemDevice.CameraBack + }) eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(AgoraEduContextSystemDevice.Microphone) } else { eduCore()?.eduContextPool()?.mediaContext()?.closeSystemDevice(AgoraEduContextSystemDevice.CameraFront) @@ -290,13 +295,11 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide protected fun openSystemDevices() { // 打开语音,摄像头,麦克风 eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(AgoraEduContextSystemDevice.Speaker) - eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice( - if (AgoraUIDeviceSetting.isFrontCamera()) { - AgoraEduContextSystemDevice.CameraFront - } else { - AgoraEduContextSystemDevice.CameraBack - } - ) + eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(if (AgoraUIDeviceSetting.isFrontCamera()) { + AgoraEduContextSystemDevice.CameraFront + } else { + AgoraEduContextSystemDevice.CameraBack + }) eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(AgoraEduContextSystemDevice.Microphone) } @@ -328,14 +331,10 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide * 设置小流分辨率 */ fun setLowStream() { - val lowStream = String.format( - Locale.US, - "{\"che.video.lowBitRateStreamParameter\": {\"width\":%d,\"height\":%d,\"frameRate\":%d,\"bitRate\":%d}}", - FcrStreamParameters.LowStream.width, - FcrStreamParameters.LowStream.height, - FcrStreamParameters.LowStream.frameRate, - FcrStreamParameters.LowStream.bitRate - ) + val lowStream = + String.format(Locale.US, "{\"che.video.lowBitRateStreamParameter\": {\"width\":%d,\"height\":%d,\"frameRate\":%d,\"bitRate\":%d}}", + FcrStreamParameters.LowStream.width, FcrStreamParameters.LowStream.height, FcrStreamParameters.LowStream.frameRate, + FcrStreamParameters.LowStream.bitRate) eduCore()?.eduContextPool()?.streamContext()?.setRtcParameters(lowStream) LogX.i(TAG, "joinRoom lowStream = $lowStream") } @@ -355,6 +354,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide super.finish() removeHandler() } + override fun onRelease() { super.onRelease() removeHandler() @@ -408,11 +408,12 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide override fun onRemoteUserLeft( user: AgoraEduContextUserInfo, operator: AgoraEduContextUserInfo?, - reason: EduContextUserLeftReason + reason: EduContextUserLeftReason, ) { super.onRemoteUserLeft(user, operator, reason) //FcrHandsUpManager.remove(user.userUuid) } + override fun onLocalUserKickedOut() { super.onLocalUserKickedOut() classManager?.showKickOut() @@ -428,7 +429,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide list: List, batch: FcrEventBatch, cause: Map, - operator: AgoraEduContextUserInfo? + operator: AgoraEduContextUserInfo?, ) { super.onUserRewardedList(list, batch, cause, operator) ContextCompat.getMainExecutor(context).execute { @@ -456,11 +457,8 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } } - val tips = if (list.size > 3) String.format( - context.resources.getString(R.string.fcr_room_tips_reward_congratulation_multiplayer), - name, - "" + list.size - ) else String.format(context.resources.getString(R.string.fcr_room_tips_reward_congratulation_single), name) + val tips = if (list.size > 3) String.format(context.resources.getString(R.string.fcr_room_tips_reward_congratulation_multiplayer), name, + "" + list.size) else String.format(context.resources.getString(R.string.fcr_room_tips_reward_congratulation_single), name) return tips } @@ -469,7 +467,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide override fun onUserRewarded( user: AgoraEduContextUserInfo, rewardCount: Int, - operator: AgoraEduContextUserInfo? + operator: AgoraEduContextUserInfo?, ) { super.onUserRewarded(user, rewardCount, operator) ContextCompat.getMainExecutor(context).execute { @@ -533,13 +531,8 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide releaseData() finish() // 欢迎加入{xxxx小组名}与大家互动讨论 - AgoraUIToast.info( - applicationContext, - text = String.format( - context.resources.getString(R.string.fcr_group_enter_welcome), - current.payload.groupName - ) - ) + AgoraUIToast.info(applicationContext, + text = String.format(context.resources.getString(R.string.fcr_group_enter_welcome), current.payload.groupName)) } else { // 加入失败,重新加入 isSowGroupDialog = true @@ -564,16 +557,13 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } if (!groupInfo.state && getRoomType() == RoomType.GROUPING_CLASS) { // 关闭分组,直接返回大房间 - if(!groupInfo.state){ + if (!groupInfo.state) { FcrGroupUserManager.clearGroupList() } ContextCompat.getMainExecutor(context).execute { if (!isJoinMainRoom.get()) { isJoinMainRoom.set(true) - AgoraUIToast.info( - applicationContext, - text = resources.getString(R.string.fcr_group_back_main_room) - ) + AgoraUIToast.info(applicationContext, text = resources.getString(R.string.fcr_group_back_main_room)) fullLoading.setContent(getString(R.string.fcr_group_back_main_room)) launchMainRoom() } @@ -590,10 +580,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide if (info.subRoomUuid == roomUuid) { LogX.i(TAG, "Group 删除分组,返回到大房间") classManager?.dismissJoinDialog() - AgoraUIToast.info( - applicationContext, - text = resources.getString(R.string.fcr_group_back_main_room) - ) + AgoraUIToast.info(applicationContext, text = resources.getString(R.string.fcr_group_back_main_room)) fullLoading.setContent(getString(R.string.fcr_group_back_main_room)) launchMainRoom() } @@ -642,21 +629,15 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide // 切换到新的分组 LogX.i(TAG, "Group 当前分组:$roomUuid , 切换到新的分组:${groupInfo.groupUuid}") - AgoraUIToast.info( - applicationContext, - text = String.format( - resources.getString(R.string.fcr_group_invitation), - groupInfo.groupName - ) - ) + AgoraUIToast.info(applicationContext, + text = String.format(resources.getString(R.string.fcr_group_invitation), groupInfo.groupName)) isJoining.set(true) showFullDialog() classManager?.launchSubRoom(groupInfo, true) { code, state, groupUuid -> if (state == AgoraEduEvent.AgoraEduEventReady) { // 关闭当前分组的channel - eduCore()?.eduContextPool()?.roomContext() - ?.leaveRoom(object : EduContextCallback { + eduCore()?.eduContextPool()?.roomContext()?.leaveRoom(object : EduContextCallback { override fun onSuccess(target: Unit?) { releaseData() finish() @@ -745,13 +726,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide // 切换到新的分组 LogX.i(TAG, "Group 当前分组:$roomUuid , 切换到新的分组:${toSubRoomUuid}") - AgoraUIToast.info( - applicationContext, - text = String.format( - resources.getString(R.string.fcr_group_invitation), - groupInfo.groupName - ) - ) + AgoraUIToast.info(applicationContext, text = String.format(resources.getString(R.string.fcr_group_invitation), groupInfo.groupName)) isJoining.set(true) showFullDialog() @@ -826,7 +801,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } } - open fun cancelHandsUp(){} + open fun cancelHandsUp() {} override fun getUIConfig(): FcrUIConfig { eduCore()?.config?.roomType?.let { diff --git a/app/src/main/java/io/agora/education/EduApplication.java b/app/src/main/java/io/agora/education/EduApplication.java index d3641c721..c198c88a6 100644 --- a/app/src/main/java/io/agora/education/EduApplication.java +++ b/app/src/main/java/io/agora/education/EduApplication.java @@ -54,7 +54,7 @@ public void onCreate() { // } // TODO test XXX - // setTestDev(); + setTestDev(); setDevHost(); setDarkMode(); From 2853c1226e8b66366064c19db65956b76c253be2 Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 1 Jul 2024 13:24:15 +0800 Subject: [PATCH 05/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9rtt=E7=9A=84proto?= =?UTF-8?q?=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/agora/online/helper/RttOptionsManager.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt index fda82a757..de686ccf1 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt @@ -30,7 +30,7 @@ import io.agora.online.easeim.utils.TAG import io.agora.online.options.AgoraEduOptionsComponent import io.agora.online.options.AgoraEduRttOptionsComponent import io.agora.online.util.MultiLanguageUtil -import io.agora.rtc.audio2text.Audio2TextProtobuffer +import io.agora.rtc.speech2text.AgoraSpeech2TextProtobuffer import retrofit2.Call import retrofit2.http.Body import retrofit2.http.Headers @@ -178,7 +178,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { private val steamHandler = object : StreamHandler() { override fun onStreamMessage(channelId: String, streamId: Int, data: ByteArray?) { super.onStreamMessage(channelId, streamId, data) - val parseFrom = Audio2TextProtobuffer.Text.parseFrom(data) + val parseFrom = AgoraSpeech2TextProtobuffer.Text.parseFrom(data) val recordItem = useManager.disposeData(parseFrom, settingsManager.currentSettingInfo, eduCore?.eduContextPool()?.streamContext()) subtitlesManager.setShowCurrentData(recordItem, settingsManager.currentSettingInfo) conversionManager.updateShowList(useManager.getRecordList()) @@ -570,7 +570,7 @@ class RttRecordItem { /** * 转写内容对应的Rtc uid */ - var uid: Int? = null + var uid: Long? = null /** * 说话的用户昵称 @@ -1243,7 +1243,7 @@ private class RttUseManager( /** * 处理数据 */ - fun disposeData(rttMsgData: Audio2TextProtobuffer.Text, settingInfo: RttSettingInfo, streamContext: StreamContext?): RttRecordItem? { + fun disposeData(rttMsgData: AgoraSpeech2TextProtobuffer.Text, settingInfo: RttSettingInfo, streamContext: StreamContext?): RttRecordItem? { val lastItem = (if (recordList.isEmpty()) null else recordList[recordList.size - 1]) val lastItemByUid = lastItem?.uid var paramsData: RttRecordItem? = null From 29caf84e2ba70ce6d0d698f4a26ce9155a8096f2 Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 1 Jul 2024 18:44:20 +0800 Subject: [PATCH 06/32] =?UTF-8?q?widget=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../online/component/AgoraEduRttComponent.kt | 114 ------------ .../component/FcrRttToolBoxComponent.kt | 79 +++++++++ .../agora/online/helper/RttOptionsManager.kt | 106 +++++++++-- .../options/AgoraEduOptionsComponent.kt | 29 ++- .../online/sdk/AgoraOnlineClassActivity.kt | 17 +- .../online/sdk/AgoraOnlineClassroomSDK.kt | 16 +- .../agora/online/widget/FcrWidgetManager.kt | 1 + .../online/widget/rtt/FcrRttToolBoxWidget.kt | 165 ++++++++++++++++++ .../layout/activity_agora_online_class.xml | 4 +- .../layout/fcr_online_edu_rtt_component.xml | 92 +--------- .../fcr_online_tool_box_widget_content.xml | 91 ++++++++++ 11 files changed, 469 insertions(+), 245 deletions(-) delete mode 100644 AgoraCloudScene/src/main/java/io/agora/online/component/AgoraEduRttComponent.kt create mode 100644 AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt create mode 100644 AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt create mode 100644 AgoraCloudScene/src/main/res/layout/fcr_online_tool_box_widget_content.xml diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/AgoraEduRttComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/AgoraEduRttComponent.kt deleted file mode 100644 index 2c2a3fb56..000000000 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/AgoraEduRttComponent.kt +++ /dev/null @@ -1,114 +0,0 @@ -package io.agora.online.component - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import io.agora.online.R -import io.agora.online.component.common.AbsAgoraEduComponent -import io.agora.online.component.common.IAgoraUIProvider -import io.agora.online.databinding.FcrOnlineEduRttComponentBinding -import io.agora.online.helper.RttOptionsManager -import io.agora.online.util.AppUtil -import java.text.MessageFormat - -class AgoraEduRttComponent : AbsAgoraEduComponent { - constructor(context: Context) : super(context) - constructor(context: Context, attr: AttributeSet) : super(context, attr) - constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr) - - - private var binding: FcrOnlineEduRttComponentBinding = FcrOnlineEduRttComponentBinding.inflate(LayoutInflater.from(context), this, true) - private lateinit var rttOptionsManager: RttOptionsManager - - //点击间隔时间 - private val clickInterval = 500L - - fun initView(rttOptionsManager: RttOptionsManager, agoraUIProvider: IAgoraUIProvider) { - this.rttOptionsManager = rttOptionsManager - super.initView(agoraUIProvider) - } - - init { - binding.agoraRttDialogLayout.clipToOutline = true - setButtonClickListeners() - } - - private fun setButtonClickListeners() { - resetStatus(null) - setFastClickAvoidanceListener(binding.agoraRttDialogSubtitles) { - if (this.rttOptionsManager.isOpenSubtitles()) { - binding.agoraRttDialogSubtitlesIcon.isActivated = false - this.rttOptionsManager.closeSubtitles() - } else { - binding.agoraRttDialogSubtitlesIcon.isActivated = true - this.rttOptionsManager.openSubtitles() - } - } - setFastClickAvoidanceListener(binding.agoraRttDialogConversion) { - if (this.rttOptionsManager.isOpenConversion()) { - binding.agoraRttDialogConversionIcon.isActivated = false - this.rttOptionsManager.closeConversion() - } else { - binding.agoraRttDialogConversionIcon.isActivated = true - this.rttOptionsManager.openConversion() - } - } - } - - - private fun setFastClickAvoidanceListener(view: View, worker: ((Boolean) -> Unit)?) { - view.setOnClickListener { - if (!AppUtil.isFastClick(clickInterval)) { - worker?.invoke(view.isActivated) - } - } - } - - - fun dismiss() { -// this.parent?.let { -// var contains = false -// it.forEach check@{ child -> -// if (child == this) { -// contains = true -// return@check -// } -// } -// -// if (contains) { -// it.removeView(this) -// } -// } - } - - override fun release() { - super.release() - } - - /** - * 重置显示状态 - */ - fun resetStatus(experienceReduceTime: Int?) { - binding.agoraRttDialogSubtitlesIcon.isActivated = this::rttOptionsManager.isInitialized && rttOptionsManager.isOpenSubtitles() - binding.agoraRttDialogConversionIcon.isActivated = this::rttOptionsManager.isInitialized && rttOptionsManager.isOpenConversion() - if (experienceReduceTime != null) { - binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { - MessageFormat.format(resources.getString(R.string.fcr_dialog_rtt_time_limit_time), experienceReduceTime / 60000) - } else { - resources.getString(R.string.fcr_dialog_rtt_time_limit_end) - } - } - } - - /** - * 设置是否可以使用RTT功能 - */ - fun setAllowUse(allowUse: Boolean, reduceTime: Int) { - binding.fcrOnlineEduRttConversionDialogTimeLimitHint.visibility = if (reduceTime > 0) View.VISIBLE else View.GONE - binding.agoraRttDialogSubtitles.isEnabled = allowUse - binding.agoraRttDialogConversion.isEnabled = allowUse - binding.agoraRttDialogSubtitles.alpha = if (allowUse) 1F else 0.8F - binding.agoraRttDialogConversion.alpha = if (allowUse) 1F else 0.8F - } -} diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt new file mode 100644 index 000000000..f4a64ff80 --- /dev/null +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt @@ -0,0 +1,79 @@ +package io.agora.online.component + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import io.agora.agoraeducore.core.internal.framework.impl.managers.AgoraWidgetActiveObserver +import io.agora.agoraeducore.core.internal.framework.impl.managers.AgoraWidgetRoomPropsUpdateReq +import io.agora.online.component.common.AbsAgoraEduComponent +import io.agora.online.component.common.IAgoraUIProvider +import io.agora.online.databinding.FcrOnlineEduRttComponentBinding +import io.agora.online.options.AgoraEduOptionsComponent +import io.agora.online.options.AgoraEduRttOptionsComponent +import io.agora.online.widget.FcrWidgetManager.WIDGETS_RTT_ID +import io.agora.online.widget.rtt.FcrRttToolBoxWidget + +class FcrRttToolBoxComponent : AbsAgoraEduComponent { + constructor(context: Context) : super(context) + constructor(context: Context, attr: AttributeSet) : super(context, attr) + constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr) + + + private var binding: FcrOnlineEduRttComponentBinding = FcrOnlineEduRttComponentBinding.inflate(LayoutInflater.from(context), this, true) + + /** + * widget插件 + */ + private var widget: FcrRttToolBoxWidget? = null + + /** + * 界面上设置好的布局 + */ + private var conversionStatusView: ViewGroup? = null + private var subtitleView: AgoraEduRttOptionsComponent? = null + private var agoraEduOptionsComponent: AgoraEduOptionsComponent? = null + + /** + * 插件注册监听 + */ + private val widgetActiveObserver = object : AgoraWidgetActiveObserver { + override fun onWidgetActive(widgetId: String) { + if (widget == null) { + val widgetConfig = eduContext?.widgetContext()?.getWidgetConfig(widgetId) + widgetConfig?.let { config -> + val widget = eduContext?.widgetContext()?.create(config) as FcrRttToolBoxWidget? + widget?.init(binding.root, agoraUIProvider, agoraEduOptionsComponent!!, conversionStatusView!!, subtitleView!!) + } + } + } + + override fun onWidgetInActive(widgetId: String) { + if (widget != null) { + ContextCompat.getMainExecutor(binding.root.context).execute { + widget!!.release() + } + } + } + } + + /** + * 重置工具准提 + */ + fun resetEduRttToolBoxStatus() { + widget?.resetEduRttToolBoxStatus() + } + + fun initView( + agoraUIProvider: IAgoraUIProvider, agoraEduOptionsComponent: AgoraEduOptionsComponent, conversionStatusView: ViewGroup, + subtitleView: AgoraEduRttOptionsComponent, + ) { + super.initView(agoraUIProvider) + this.agoraEduOptionsComponent = agoraEduOptionsComponent + this.conversionStatusView = conversionStatusView + this.subtitleView = subtitleView + eduContext?.widgetContext()?.addWidgetActiveObserver(widgetActiveObserver, WIDGETS_RTT_ID) + eduContext?.widgetContext()?.setWidgetActive(WIDGETS_RTT_ID, AgoraWidgetRoomPropsUpdateReq(state = 1)) + } +} diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt index de686ccf1..7bfc59a56 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt @@ -19,7 +19,6 @@ import io.agora.agoraeducore.core.internal.framework.impl.handler.UserHandler import io.agora.agoraeducore.core.internal.framework.utils.GsonUtil import io.agora.agoraeducore.core.internal.log.LogX import io.agora.online.R -import io.agora.online.component.AgoraEduRttComponent import io.agora.online.component.common.IAgoraUIProvider import io.agora.online.component.dialog.AgoraUIRttConversionDialogBuilder import io.agora.online.component.dialog.AgoraUIRttSettingBuilder @@ -71,12 +70,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { private val TAG = "RttOptionsManager:" - /** - * 字幕工具箱组件 - */ - var rttToolBoxWidget: AgoraEduRttComponent? = null - private set - /** * 教室相关信息 */ @@ -239,9 +232,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { conversionManager.rttTipLeftTopConversionStatusView = rttTipLeftTopStatus subtitlesManager.rttBottomCenterSubtitlesView = rttBottomCenterSubtitlesView subtitlesManager.rttBottomCenterSubtitlesView!!.initView(this) - //生成动态的工具箱显示组件 - this.rttToolBoxWidget = AgoraEduRttComponent(rttOptions.getActivityContext()) - this.rttToolBoxWidget!!.initView(this, agoraUIProvider) this.eduCore = agoraUIProvider.getAgoraEduCore() //重置视图 this.setRttFunctionStatusConfig() @@ -285,7 +275,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ fun setRttFunctionStatusConfig(allowUse: Boolean = false) { useManager.configAllowUse = allowUse - this.rttToolBoxWidget?.setAllowUse(useManager.isAllowUse(), useManager.rttExperienceReduceTime) +// this.rttToolBoxWidget?.setAllowUse(useManager.isAllowUse(), useManager.rttExperienceReduceTime) if (!allowUse) { this.resetShow() } @@ -295,7 +285,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 释放相关 */ fun release() { - rttToolBoxWidget?.release() conversionManager.resetShow() subtitlesManager.resetShow() this.eduCore?.eduContextPool()?.streamContext()?.removeHandler(steamHandler) @@ -382,10 +371,10 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } /** - * 重置edu字幕工具箱 + * 获取体验剩余时间 */ - fun resetEduRttToolBoxStatus() { - rttToolBoxWidget?.resetStatus(useManager.rttExperienceReduceTime) + fun getExperienceReduceTime(): Int { + return useManager.rttExperienceReduceTime; } /** @@ -1389,3 +1378,90 @@ private interface SettingOptionsInterface { } +/** + * Rtt操作状态监听 + */ +abstract class FcrRttOptionsStatusListener { + /** + * rtt功能状态变更 + * @param open 开启-true,关闭-false + */ + open fun rttStateChange(open: Boolean) {} + + /** + * 体验信息变更 + * @param configAllowUseRtt 配置是否可以使用rtt功能 + * @param experienceReduceTime 剩余体验时间 + * @param experienceDefaultTime 默认体验时间 + */ + open fun experienceInfoChange(configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) {} + + /** + * 字幕状态变更 + * @param toOpen 开启-true,关闭-false + * @param configAllowUseRtt 配置是否可以使用rtt功能 + * @param experienceReduceTime 剩余体验时间 + * @param experienceDefaultTime 默认体验时间 + */ + open fun subtitlesStateChange(toOpen: Boolean, configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) {} + + /** + * 字幕状态变更-网络请求结果 + * @param open 开启-true,关闭-false + */ + open fun subtitlesStateChangeNetResult(open: Boolean) {} + + /** + * 转写状态变更 + * @param toOpen 开启-true,关闭-false + * @param configAllowUseRtt 配置是否可以使用rtt功能 + * @param experienceReduceTime 剩余体验时间 + * @param experienceDefaultTime 默认体验时间 + */ + open fun conversionStateChange(toOpen: Boolean, configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) {} + + /** + * 转写状态变更-网络请求结果 + * @param open 开启-true,关闭-false + */ + open fun conversionStateChangeNetResult(open: Boolean) {} + + /** + * 声源语言修改 + */ + open fun sourceLanguageChange(language: RttLanguageEnum) {} + + /** + * 声源语言修改-网络请求结果 + */ + open fun sourceLanguageChangeNetResult(language: RttLanguageEnum) {} + + /** + * 目标语言修改-网络请求结果 + */ + open fun targetLanguageChange(languages: List) {} + + /** + * 目标语言修改 + */ + open fun targetLanguageChangeNetResult(languages: List) {} + + /** + * 双语状态变更 + * @param open 开启-true,关闭-false + */ + open fun showDoubleLanguage(open: Boolean) {} + + /** + * 双语状态变更-网络请求结果 + * @param open 开启-true,关闭-false + */ + open fun showDoubleLanguageNetResult(open: Boolean) {} + + /** + * 消息改变 + * @param recordList 消息记录数据 + * @param currentData 当前要显示的数据 + */ + open fun onMessageChange(recordList: List, currentData: RttRecordItem?) {} +} diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt index 49e43d2ab..589b466e4 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt @@ -34,6 +34,7 @@ import io.agora.agoraeducore.extensions.widgets.bean.AgoraWidgetMessageObserver import io.agora.online.R import io.agora.online.component.AgoraEduChatComponent import io.agora.online.component.AgoraEduSettingComponent +import io.agora.online.component.FcrRttToolBoxComponent import io.agora.online.component.common.AbsAgoraEduConfigComponent import io.agora.online.component.common.IAgoraUIProvider import io.agora.online.component.common.UIUtils @@ -41,7 +42,6 @@ import io.agora.online.component.toast.AgoraUIToast import io.agora.online.component.whiteboard.data.AgoraEduApplianceData import io.agora.online.config.FcrUIConfig import io.agora.online.databinding.FcrOnlineEduOptionsComponentBinding -import io.agora.online.helper.RttOptionsManager import io.agora.online.impl.chat.ChatPopupWidgetListener import io.agora.online.impl.whiteboard.bean.AgoraBoardGrantData import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionPacket @@ -58,13 +58,13 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite lateinit var uuid: String lateinit var rootContainer: ViewGroup // 给IM用的,view root lateinit var itemContainer: ViewGroup // 显示侧边栏 - lateinit var rttOptionsManager: RttOptionsManager private var binding: FcrOnlineEduOptionsComponentBinding = FcrOnlineEduOptionsComponentBinding.inflate(LayoutInflater.from(context), this, true) private var agroSettingWidget: AgoraEduSettingComponent? = null private var popupViewRoster: AgoraEduRosterComponent? = null private var popupViewChat: AgoraEduChatComponent? = null + private var rttToolBoxWidget: FcrRttToolBoxComponent? = null private lateinit var optionPresenter: AgoraEduOptionPresenter var onExitListener: (() -> Unit)? = null // 退出 private var isRequestHelp = false // 分组是否请求了帮助 @@ -76,14 +76,14 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite } fun initView( - rttOptionsManager: RttOptionsManager, uuid: String, rootContainer: ViewGroup, itemContainer: ViewGroup, + uuid: String, rootContainer: ViewGroup, itemContainer: ViewGroup, agoraUIProvider: IAgoraUIProvider, ) { this.uuid = uuid this.itemContainer = itemContainer this.rootContainer = rootContainer - this.rttOptionsManager = rttOptionsManager - this.rttOptionsManager.setEduOptionsComponent(this) +// this.rttOptionsManager = rttOptionsManager +// this.rttOptionsManager.setEduOptionsComponent(this) initView(agoraUIProvider) } @@ -155,8 +155,8 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemRtt.setOnClickListener { //LogX.e(TAG,">>>${eduContext?.userContext()?.getCoHostList()}") if (!it.isActivated) { - rttOptionsManager.resetEduRttToolBoxStatus() - showItem(rttOptionsManager.rttToolBoxWidget, R.dimen.agora_edu_options_rtt_dialog_w, R.dimen.agora_userlist_dialog_large_h) + rttToolBoxWidget!!.resetEduRttToolBoxStatus() + showItem(rttToolBoxWidget, R.dimen.agora_edu_options_rtt_dialog_w, R.dimen.agora_userlist_dialog_large_h) setIconActivated(binding.optionItemRtt) } else { hiddenItem() @@ -414,10 +414,21 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemChatNews.visibility = View.GONE } + /** + * 初始化Rtt + */ + fun initRtt(conversionStatusView: ViewGroup, subtitleView: AgoraEduRttOptionsComponent) { + //初始化聊天组件 + if (rttToolBoxWidget == null) { + rttToolBoxWidget = FcrRttToolBoxComponent(context) + rttToolBoxWidget?.initView(agoraUIProvider,this, conversionStatusView, subtitleView) + } + } + /** * 隐藏rtt相关的 */ - fun hiddenRtt(){ + fun hiddenRtt() { hiddenItem() binding.optionItemRtt.isActivated = false } @@ -664,7 +675,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite super.release() popupViewChat?.release() agroSettingWidget?.release() - rttOptionsManager.release() + rttToolBoxWidget?.release() popupViewRoster?.release() eduContext?.roomContext()?.removeHandler(roomHandler) eduContext?.userContext()?.removeHandler(userHandler) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassActivity.kt b/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassActivity.kt index 280af2e9a..e3ce6163d 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassActivity.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassActivity.kt @@ -1,6 +1,5 @@ package io.agora.online.sdk -import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View @@ -25,9 +24,7 @@ import io.agora.online.component.toast.AgoraUIToast import io.agora.online.databinding.ActivityAgoraOnlineClassBinding import io.agora.online.helper.AgoraUIDeviceSetting import io.agora.online.helper.FcrHandsUpManager -import io.agora.online.helper.IRttOptions import io.agora.online.helper.RoomPropertiesHelper -import io.agora.online.helper.RttOptionsManager import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionPacket import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionSignal import io.agora.online.sdk.common.AgoraEduClassActivity @@ -38,13 +35,12 @@ import io.agora.online.sdk.presenter.AgoraClassVideoPresenter * date : 2022/1/24 * description : 小班课(200) */ -open class AgoraOnlineClassActivity : AgoraEduClassActivity(), IRttOptions { +open class AgoraOnlineClassActivity : AgoraEduClassActivity(){ override var TAG = "AgoraOnlineClassActivity" var agoraClassVideoPresenter: AgoraClassVideoPresenter? = null private lateinit var binding: ActivityAgoraOnlineClassBinding var cameraDialog: AgoraUIDialog? = null var micDialog: AgoraUIDialog? = null - private val rttOptionsManager:RttOptionsManager by lazy { RttOptionsManager(this) } protected val roomHandler = object : RoomHandler() { override fun onJoinRoomSuccess(roomInfo: EduContextRoomInfo) { @@ -235,7 +231,8 @@ open class AgoraOnlineClassActivity : AgoraEduClassActivity(), IRttOptions { binding.agoraEduWhiteboard.initView(uuid, this) // tool bar - binding.agoraEduOptions.initView(rttOptionsManager,uuid, binding.root, binding.agoraEduOptionsItemContainer, this) + binding.agoraEduOptions.initView(uuid, binding.root, binding.agoraEduOptionsItemContainer, this) + binding.agoraEduOptions.initRtt(binding.agoraAreaBoardConversionStatus,binding.agoraRttOptions) launchConfig?.shareUrl?.let { binding.agoraEduOptions.setShareRoomLink(it) } @@ -257,7 +254,7 @@ open class AgoraOnlineClassActivity : AgoraEduClassActivity(), IRttOptions { UIUtils.setViewVisible(binding.agoraEduWhiteboard, getUIConfig().isEngagementVisible) UIUtils.setViewVisible(binding.agoraEduOptions, getUIConfig().isEngagementVisible) //初始化管理器 - rttOptionsManager.initView(binding.agoraAreaBoardConversionStatus,binding.agoraRttOptions,this) +// rttOptionsManager.initView(binding.agoraAreaBoardConversionStatus,binding.agoraRttOptions,this) } join() } @@ -316,10 +313,4 @@ open class AgoraOnlineClassActivity : AgoraEduClassActivity(), IRttOptions { binding.agoraEduOptions.cancelHandsUp() } - /** - * 当前页面实例 - */ - override fun getActivityContext(): Context { - return this - } } \ No newline at end of file diff --git a/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassroomSDK.kt b/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassroomSDK.kt index 457ce867c..17e3baa73 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassroomSDK.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/sdk/AgoraOnlineClassroomSDK.kt @@ -1,17 +1,20 @@ package io.agora.online.sdk import android.content.Context -import io.agora.online.component.chat.AgoraChatRTMWidget -import io.agora.online.component.chat.AgoraEduEaseChatWidget import io.agora.agoraeducore.core.AgoraEduCore import io.agora.agoraeducore.core.ClassInfoCache import io.agora.agoraeducore.core.internal.framework.impl.managers.AgoraWidgetManager.Companion.registerDefault -import io.agora.agoraeducore.core.internal.framework.impl.managers.UserOnlineManager import io.agora.agoraeducore.core.internal.framework.proxy.RoomType -import io.agora.agoraeducore.core.internal.launch.* +import io.agora.agoraeducore.core.internal.launch.AgoraEduLaunchCallback +import io.agora.agoraeducore.core.internal.launch.AgoraEduLaunchConfig +import io.agora.agoraeducore.core.internal.launch.AgoraEduRegion +import io.agora.agoraeducore.core.internal.launch.AgoraEduSDK +import io.agora.agoraeducore.core.internal.launch.AgoraServiceType import io.agora.agoraeducore.core.internal.log.LogX import io.agora.agoraeducore.extensions.widgets.bean.AgoraWidgetConfig import io.agora.agoraeducore.extensions.widgets.bean.AgoraWidgetDefaultId +import io.agora.online.component.chat.AgoraChatRTMWidget +import io.agora.online.component.chat.AgoraEduEaseChatWidget import io.agora.online.component.teachaids.AgoraTeachAidCountDownWidget import io.agora.online.component.teachaids.AgoraTeachAidIClickerWidget import io.agora.online.component.teachaids.networkdisk.FCRCloudDiskWidget @@ -19,9 +22,11 @@ import io.agora.online.component.teachaids.vote.AgoraTeachAidVoteWidget import io.agora.online.component.teachaids.webviewwidget.FcrWebViewWidget import io.agora.online.impl.video.AgoraUILargeVideoWidget import io.agora.online.impl.whiteboard.AgoraWhiteBoardWidget -import io.agora.online.util.SpUtil import io.agora.online.sdk.common.AgoraBaseClassActivity import io.agora.online.sdk.helper.FCRLauncherManager +import io.agora.online.util.SpUtil +import io.agora.online.widget.FcrWidgetManager.WIDGETS_RTT_ID +import io.agora.online.widget.rtt.FcrRttToolBoxWidget /** * 一键拉起教室 @@ -157,6 +162,7 @@ object AgoraOnlineClassroomSDK { ) ) widgetConfigs.add(AgoraWidgetConfig(FCRCloudDiskWidget::class.java, AgoraWidgetDefaultId.AgoraCloudDisk.id)) + widgetConfigs.add(AgoraWidgetConfig(FcrRttToolBoxWidget::class.java, WIDGETS_RTT_ID, extraInfo = mutableMapOf())) return widgetConfigs } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/FcrWidgetManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/FcrWidgetManager.kt index d223a9108..7cf1c6aef 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/FcrWidgetManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/FcrWidgetManager.kt @@ -12,6 +12,7 @@ object FcrWidgetManager { val WIDGET_WEBVIEW_RUL = "webViewUrl" val WIDGET_WEBVIEW_TITLE = "webviewTitle" val WIDGET_WEBVIEW_MEDIAPLAYER = "mediaPlayer" + val WIDGETS_RTT_ID = "rtt" fun isWebViewWidget(widgetId: String): Boolean { return widgetId.contains(WIDGET_WEBVIEW) || widgetId.contains(WIDGET_WEBVIEW_MEDIAPLAYER) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt new file mode 100644 index 000000000..65d657f81 --- /dev/null +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -0,0 +1,165 @@ +package io.agora.online.widget.rtt + +import android.app.Activity +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import io.agora.agoraeducore.core.internal.framework.data.EduBaseUserInfo +import io.agora.agoraeducore.extensions.widgets.AgoraBaseWidget +import io.agora.online.R +import io.agora.online.component.common.IAgoraUIProvider +import io.agora.online.databinding.FcrOnlineToolBoxWidgetContentBinding +import io.agora.online.helper.IRttOptions +import io.agora.online.helper.RttOptionsManager +import io.agora.online.options.AgoraEduOptionsComponent +import io.agora.online.options.AgoraEduRttOptionsComponent +import java.text.MessageFormat + +/** + * 功能作用:Rtt工具箱widget + * 初始注释时间: 2024/7/1 17:25 + * 创建人:王亮(Loren) + * 思路: + * 方法: + * 注意: + * 修改人: + * 修改时间: + * 备注: + * + * @author 王亮(Loren) + */ +class FcrRttToolBoxWidget : AgoraBaseWidget() { + override val TAG = "FcrRttToolsBoxWidget" + + private var contentView: AgoraRttToolBoxWidgetContent? = null + + + fun init( + container: ViewGroup, + agoraUIProvider: IAgoraUIProvider, agoraEduOptionsComponent: AgoraEduOptionsComponent, conversionStatusView: ViewGroup, + subtitleView: AgoraEduRttOptionsComponent, + ) { + super.init(container) + contentView = AgoraRttToolBoxWidgetContent(container, agoraUIProvider, agoraEduOptionsComponent, conversionStatusView, subtitleView) + } + + override fun release() { + contentView?.dispose() + super.release() + } + + override fun onWidgetRoomPropertiesUpdated( + properties: MutableMap, cause: MutableMap?, keys: MutableList, + operator: EduBaseUserInfo?, + ) { + super.onWidgetRoomPropertiesUpdated(properties, cause, keys, operator) + } + + /** + * 重置工具准给他 + */ + fun resetEduRttToolBoxStatus() { + contentView?.resetStatus() + } + + + internal inner class AgoraRttToolBoxWidgetContent( + val container: ViewGroup, agoraUIProvider: IAgoraUIProvider, + agoraEduOptionsComponent: AgoraEduOptionsComponent, conversionStatusView: ViewGroup, subtitleView: AgoraEduRttOptionsComponent, + ) : IRttOptions { + private val TAG = "AgoraRttToolBoxWidgetContent" + + /** + * Rtt功能的管理 + */ + private val rttOptionsManager: RttOptionsManager by lazy { + RttOptionsManager(this).also { + it.initView(conversionStatusView, subtitleView, agoraUIProvider) + it.setEduOptionsComponent(agoraEduOptionsComponent) + } + } + + /** + * 内容视图 + */ + private var binding: FcrOnlineToolBoxWidgetContentBinding = + FcrOnlineToolBoxWidgetContentBinding.inflate(LayoutInflater.from(container.context), container, true) + + init { + binding.agoraRttDialogLayout.clipToOutline = true + binding.agoraRttDialogSubtitles.setOnClickListener { + if (this.rttOptionsManager.isOpenSubtitles()) { + binding.agoraRttDialogSubtitlesIcon.isActivated = false + this.rttOptionsManager.closeSubtitles() + } else { + binding.agoraRttDialogSubtitlesIcon.isActivated = true + this.rttOptionsManager.openSubtitles() + } + } + binding.agoraRttDialogConversion.setOnClickListener { + if (this.rttOptionsManager.isOpenConversion()) { + binding.agoraRttDialogConversionIcon.isActivated = false + this.rttOptionsManager.closeConversion() + } else { + binding.agoraRttDialogConversionIcon.isActivated = true + this.rttOptionsManager.openConversion() + } + } + resetStatus() + } + + /** + * 重置显示状态 + */ + fun resetStatus() { + val experienceReduceTime = rttOptionsManager.getExperienceReduceTime() + binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() + binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { + MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), experienceReduceTime / 60000) + } else { + container.context.getString(R.string.fcr_dialog_rtt_time_limit_end) + } + } + + /** + * 设置是否可以使用RTT功能 + */ + fun setAllowUse(allowUse: Boolean, reduceTime: Int) { + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.visibility = if (reduceTime > 0) View.VISIBLE else View.GONE + binding.agoraRttDialogSubtitles.isEnabled = allowUse + binding.agoraRttDialogConversion.isEnabled = allowUse + binding.agoraRttDialogSubtitles.alpha = if (allowUse) 1F else 0.8F + binding.agoraRttDialogConversion.alpha = if (allowUse) 1F else 0.8F + } + + fun dispose() { + container.removeView(binding.root) + } + + /** + * 获取应用实例 + */ + override fun getApplicationContext(): Context { + if (container.context is Activity) { + return container.context.applicationContext + } + return container.context + } + + /** + * 当前页面实例 + */ + override fun getActivityContext(): Context { + return container.context + } + + /** + * 判断并切换主线程 + */ + override fun runOnUiThread(runnable: Runnable) { + container.post(runnable) + } + } +} \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml b/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml index 5a8272968..a32e2ac30 100644 --- a/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml +++ b/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml @@ -101,7 +101,9 @@ android:layout_margin="@dimen/dp_6" android:paddingVertical="@dimen/dp_3" android:paddingHorizontal="@dimen/dp_4" - android:gravity="center_vertical"> + android:gravity="center_vertical" + android:visibility="gone" + tools:visibility="visible"> - + android:layout_width="wrap_content" + android:layout_height="wrap_content"> - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_tool_box_widget_content.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_tool_box_widget_content.xml new file mode 100644 index 000000000..0f807a2d9 --- /dev/null +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_tool_box_widget_content.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From b6e3552bd47fa412506ebe6ee1988fc06a959c18 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 4 Jul 2024 18:43:13 +0800 Subject: [PATCH 07/32] =?UTF-8?q?rtt=E9=80=BB=E8=BE=91=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dialog/AgoraUIRttConversionDialog.kt | 217 +++++- .../online/component/toast/AgoraUIToast.kt | 41 +- ...ionsManager.kt => FcrRttOptionsManager.kt} | 684 +++++++++++------- .../options/AgoraEduOptionsComponent.kt | 6 +- .../options/AgoraEduRttOptionsComponent.kt | 36 +- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 94 ++- ...du_rtt_conversion_dialog_options_arrow.xml | 1 + .../fcr_online_edu_rtt_conversion_dialog.xml | 1 + .../src/main/res/values/strings.xml | 9 + 9 files changed, 753 insertions(+), 336 deletions(-) rename AgoraCloudScene/src/main/java/io/agora/online/helper/{RttOptionsManager.kt => FcrRttOptionsManager.kt} (67%) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt index 5a0f0db08..c8e4c56cb 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -1,12 +1,19 @@ package io.agora.online.component.dialog +import android.annotation.SuppressLint import android.app.Dialog import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable +import android.text.Spannable +import android.text.SpannableString +import android.text.style.BackgroundColorSpan +import android.text.style.ForegroundColorSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager import androidx.appcompat.widget.AppCompatTextView import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager @@ -51,7 +58,7 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago init { setContentView(binding.root) - val window = this.window; + val window = this.window window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) window?.decorView?.setBackgroundResource(android.R.color.transparent) val layout = findViewById(R.id.agora_dialog_layout) @@ -60,6 +67,7 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago initView() } + @SuppressLint("SetTextI18n") private fun initView() { binding.fcrOnlineEduRttConversionDialogChangeStatus.setOnClickListener { if (it.isActivated) { @@ -74,6 +82,32 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago binding.fcrOnlineEduRttConversionDialogOptionsClose.setOnClickListener { dismiss() } binding.fcrOnlineEduRttConversionDialogList.adapter = mAdapter binding.fcrOnlineEduRttConversionDialogList.layoutManager = LinearLayoutManager(context) + binding.fcrOnlineEduRttConversionDialogInput.setOnEditorActionListener { v, actionId, _ -> + if (EditorInfo.IME_ACTION_SEARCH == actionId) { + searchData(if (v.text != null) v.text.toString() else "") + val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(v.windowToken, 0) + return@setOnEditorActionListener true + } + return@setOnEditorActionListener false + } + binding.fcrOnlineEduRttConversionDialogOptionsGoPre.setOnClickListener { + if (mAdapter.currentSearchResultIndex > 0) { + mAdapter.currentSearchResultIndex-- + changeShowSearchResult() + } + } + binding.fcrOnlineEduRttConversionDialogOptionsGoNext.setOnClickListener { + if (mAdapter.currentSearchResultIndex + 1 < mAdapter.getSumResultCount()) { + mAdapter.currentSearchResultIndex++ + changeShowSearchResult() + } + } + binding.fcrOnlineEduRttConversionDialogSearchClear.setOnClickListener { + binding.fcrOnlineEduRttConversionDialogInput.text = null + binding.fcrOnlineEduRttConversionDialogInput.clearFocus() + searchData(null) + } } /** @@ -108,15 +142,22 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago * 刷新记录信息 */ fun updateShowList(list: List) { - if (mAdapter.dataList.size != list.size) { - mAdapter.dataList.clear() - mAdapter.dataList.addAll(list) - mAdapter.notifyItemRangeChanged(0, list.size) - binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) - } else { - mAdapter.dataList[mAdapter.dataList.size - 1] = list[list.size - 1] - mAdapter.notifyItemChanged(mAdapter.dataList.size - 1) - binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) + binding.root.post { + if (mAdapter.dataList.size != list.size) { + mAdapter.dataList.clear() + mAdapter.dataList.addAll(list) + mAdapter.notifyItemRangeChanged(0, list.size) + if (mAdapter.searchText.isNullOrEmpty()) { + binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) + } + } else { + mAdapter.dataList[mAdapter.dataList.size - 1] = list[list.size - 1] + mAdapter.notifyItemChanged(mAdapter.dataList.size - 1) + if (mAdapter.searchText.isNullOrEmpty()) { + binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) + } + } + this.searchData(mAdapter.searchText) } } @@ -142,6 +183,40 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago binding.fcrOnlineEduRttConversionDialogChangeStatus.setTextColor(ContextCompat.getColor(context, R.color.fcr_white)) } + /** + * 搜索数据 + */ + @SuppressLint("SetTextI18n") + private fun searchData(text: String?) { + mAdapter.searchText = text + if (!text.isNullOrEmpty()) { + mAdapter.searchResultListIndexMap.clear() + mAdapter.dataList.forEachIndexed { index, rttRecordItem -> + findSubstringsIndexes(rttRecordItem.sourceText, text).apply { + addAll(findSubstringsIndexes(if (rttRecordItem.currentTargetLan.isNullOrEmpty()) "" else rttRecordItem.targetText, + text).map { return@map it + (rttRecordItem.sourceText?.length ?: 0) }) + }.let { + if (it.isNotEmpty()) { + mAdapter.searchResultListIndexMap[index] = it + } + } + } + mAdapter.currentSearchResultIndex = mAdapter.currentSearchResultIndex.coerceAtMost(mAdapter.itemCount) + binding.fcrOnlineEduRttConversionDialogSearchClear.visibility = View.VISIBLE + binding.fcrOnlineEduRttConversionDialogSearchCount.visibility = View.VISIBLE + binding.fcrOnlineEduRttConversionDialogOptionsChangeLoc.visibility = View.VISIBLE + changeShowSearchResult() + } else { + mAdapter.searchResultListIndexMap.clear() + mAdapter.currentSearchResultIndex = 0 + mAdapter.notifyItemRangeChanged(0, mAdapter.itemCount) + binding.fcrOnlineEduRttConversionDialogSearchClear.visibility = View.GONE + binding.fcrOnlineEduRttConversionDialogSearchCount.visibility = View.GONE + binding.fcrOnlineEduRttConversionDialogOptionsChangeLoc.visibility = View.GONE + } + } + + /** * 显示弹窗 */ @@ -151,6 +226,44 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago openConversion() super.show() } + + /** + * 从字符串中查找子字符串位置列表 + */ + private fun findSubstringsIndexes(input: String?, searchString: String): ArrayList { + if (input.isNullOrEmpty()) { + return arrayListOf() + } + val indexes = arrayListOf() + var index = input.indexOf(searchString, 0) + while (index >= 0) { + indexes.add(index) + index = input.indexOf(searchString, index + 1) + } + return indexes + } + + /** + * 修改显示的搜索结果 + */ + @SuppressLint("SetTextI18n") + private fun changeShowSearchResult() { + binding.fcrOnlineEduRttConversionDialogSearchCount.text = "${mAdapter.currentSearchResultIndex + 1}/${mAdapter.getSumResultCount()}" + binding.fcrOnlineEduRttConversionDialogOptionsGoPre.isEnabled = mAdapter.currentSearchResultIndex > 0 + binding.fcrOnlineEduRttConversionDialogOptionsGoNext.isEnabled = mAdapter.currentSearchResultIndex < (mAdapter.getSumResultCount() - 1) + mAdapter.notifyItemRangeChanged(0, mAdapter.itemCount) + //滑动到指定位置 + var count = 0 + for (entry in mAdapter.searchResultListIndexMap) { + if (count <= mAdapter.currentSearchResultIndex && mAdapter.currentSearchResultIndex < count + entry.value.size) { + binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(entry.key) + break + } + count += entry.value.size + } + + + } } /** @@ -177,6 +290,48 @@ interface ConversionOptionsInterface { * 数据记录适配器 */ private class RecordAdapter(private val context: Context, val dataList: ArrayList) : RecyclerView.Adapter() { + /** + * 搜索结果下标 + * key值是列表中位置 + * value是数据元素位置 + */ + var searchResultListIndexMap = HashMap>() + + /** + * 搜索的文本 + */ + var searchText: String? = null + + /** + * 当前显示的查询结果位置 + */ + var currentSearchResultIndex = 0 + + /** + * 获取总的查询结果数量 + */ + fun getSumResultCount(): Int { + var count = 0 + for (entry in searchResultListIndexMap) { + count += entry.value.size + } + return count + } + + /** + * 获取当前条目之前的结果数量 + */ + fun getPreItemResultCount(position: Int): Int { + var count = 0 + for (entry in searchResultListIndexMap) { + if (entry.key < position) { + count += entry.value.size + } + } + return count + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { if (dataList[viewType].statusText.isNullOrEmpty()) { return object : RecyclerView.ViewHolder( @@ -202,10 +357,48 @@ private class RecordAdapter(private val context: Context, val dataList: ArrayLis it.findViewById(R.id.agora_fcr_rtt_text_dialog_time).text = (if (bean.time == null || bean.time == 0L) null else bean.time)?.let { time -> Date(time) } ?.let { date -> SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA).format(date) } - it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_origin).text = bean.sourceText + + if (searchText.isNullOrEmpty() || !searchResultListIndexMap.containsKey(position)) { + it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_origin).text = bean.sourceText + it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_result).text = + if (bean.currentTargetLan.isNullOrEmpty()) "" else bean.targetText + } else { + val resultCount = getPreItemResultCount(position) + val sourceSpan = SpannableString(bean.sourceText) + val targetTextSpan = + if (bean.currentTargetLan.isNullOrEmpty() || bean.targetText.isNullOrEmpty()) null else SpannableString(bean.targetText) + searchResultListIndexMap[position]?.forEachIndexed { index, position -> + if (resultCount + index == currentSearchResultIndex) { + //当前是定位到的位置 + if (position < sourceSpan.length) { + sourceSpan.setSpan(ForegroundColorSpan(Color.WHITE), position, position + searchText!!.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + sourceSpan.setSpan(BackgroundColorSpan(Color.parseColor("#4262FF")), position, position + searchText!!.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } else { + targetTextSpan?.setSpan(ForegroundColorSpan(Color.WHITE), position, position + searchText!!.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + targetTextSpan?.setSpan(BackgroundColorSpan(Color.parseColor("#4262FF")), position, position + searchText!!.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } else { + //非定位到的位置 + if (position < sourceSpan.length) { + sourceSpan.setSpan(BackgroundColorSpan(Color.parseColor("#334262FF")), position, position + searchText!!.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } else { + targetTextSpan?.setSpan(BackgroundColorSpan(Color.parseColor("#334262FF")), position, position + searchText!!.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } + + + } + it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_origin).text = sourceSpan + it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_result).text = targetTextSpan + } it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_result).apply { - text = if (bean.currentTargetLan.isNullOrEmpty()) "" else bean.targetText visibility = if (bean.currentTargetLan.isNullOrEmpty()) View.GONE else View.VISIBLE } } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt index e84c832db..1ebc9932a 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt @@ -7,8 +7,6 @@ import android.graphics.Color import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.os.Handler -import android.os.Looper -import android.os.Message import android.text.SpannableString import android.util.TypedValue import android.view.Gravity @@ -21,7 +19,6 @@ import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.LinearLayoutCompat import androidx.core.content.ContextCompat -import androidx.core.view.children import io.agora.online.R @SuppressLint("StaticFieldLeak") @@ -138,21 +135,21 @@ object AgoraUIToast { */ private var lastShowToast: Toast? = null - /** - * 默认吐司控制器,用来移除到达时间的view视图 - */ - private val defaultToastHandler = object : Handler(Looper.getMainLooper()) { - override fun handleMessage(msg: Message) { - super.handleMessage(msg) - if (lastShowToast?.view != null) { - (lastShowToast!!.view as ViewGroup).children.forEach { - if (it.id == msg.what) { - (lastShowToast!!.view as ViewGroup).removeView(it) - } - } - } - } - } +// /** +// * 默认吐司控制器,用来移除到达时间的view视图 +// */ +// private val defaultToastHandler = object : Handler(Looper.getMainLooper()) { +// override fun handleMessage(msg: Message) { +// super.handleMessage(msg) +// if (lastShowToast?.view != null) { +// (lastShowToast!!.view as ViewGroup).children.forEach { +// if (it.id == msg.what) { +// (lastShowToast!!.view as ViewGroup).removeView(it) +// } +// } +// } +// } +// } /** * 显示默认弹窗 @@ -164,11 +161,13 @@ object AgoraUIToast { lastShowToast = Toast(context.applicationContext) lastShowToast!!.view = LayoutInflater.from(context).inflate(R.layout.fcr_online_toast_layout_default, null, false) } + lastShowToast!!.cancel() + (lastShowToast!!.view as ViewGroup).removeAllViews() val view = getDefaultTextView(context, message) view.id = (Math.random() * 1000000000000).toInt() - //一定时间之后移除视图 - defaultToastHandler.sendEmptyMessageDelayed(view.id, - if (LENGTH_SHORT == duration) 1500 else if (LENGTH_LONG == duration) 3000 else duration.toLong()) +// //一定时间之后移除视图 +// defaultToastHandler.sendEmptyMessageDelayed(view.id, +// if (LENGTH_SHORT == duration) 1500 else if (LENGTH_LONG == duration) 3000 else duration.toLong()) (lastShowToast!!.view as ViewGroup).addView(view) lastShowToast!!.duration = Toast.LENGTH_LONG lastShowToast!!.setGravity(Gravity.CENTER, 0, -context.resources.getDimensionPixelOffset(R.dimen.dp_40)) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt similarity index 67% rename from AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt rename to AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 7bfc59a56..a0f7dd201 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/RttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -3,9 +3,11 @@ package io.agora.online.helper import android.content.Context import android.os.Handler import android.os.Message -import android.view.View -import android.view.ViewGroup +import android.text.SpannableString +import android.text.Spanned +import android.text.style.ForegroundColorSpan import androidx.annotation.StringRes +import androidx.core.content.ContextCompat import io.agora.agoraeducore.core.AgoraEduCore import io.agora.agoraeducore.core.context.AgoraEduContextUserInfo import io.agora.agoraeducore.core.context.EduContextUserLeftReason @@ -13,6 +15,8 @@ import io.agora.agoraeducore.core.context.StreamContext import io.agora.agoraeducore.core.internal.base.http.AppRetrofitManager import io.agora.agoraeducore.core.internal.education.impl.network.HttpBaseRes import io.agora.agoraeducore.core.internal.education.impl.network.HttpCallback +import io.agora.agoraeducore.core.internal.framework.data.EduBaseUserInfo +import io.agora.agoraeducore.core.internal.framework.data.EduUserRole import io.agora.agoraeducore.core.internal.framework.impl.handler.RoomHandler import io.agora.agoraeducore.core.internal.framework.impl.handler.StreamHandler import io.agora.agoraeducore.core.internal.framework.impl.handler.UserHandler @@ -26,8 +30,6 @@ import io.agora.online.component.dialog.AgoraUIRttSettingDialogListener import io.agora.online.component.dialog.ConversionOptionsInterface import io.agora.online.component.toast.AgoraUIToast import io.agora.online.easeim.utils.TAG -import io.agora.online.options.AgoraEduOptionsComponent -import io.agora.online.options.AgoraEduRttOptionsComponent import io.agora.online.util.MultiLanguageUtil import io.agora.rtc.speech2text.AgoraSpeech2TextProtobuffer import retrofit2.Call @@ -67,7 +69,6 @@ import java.util.UUID * @author 王亮(Loren) */ class RttOptionsManager(internal val rttOptions: IRttOptions) { - private val TAG = "RttOptionsManager:" /** @@ -75,95 +76,29 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ private var eduCore: AgoraEduCore? = null + private val listenerList = arrayListOf() + /** * 转写管理 */ private val conversionManager by lazy { - RttConversionManager(this, object : ConversionOptionsInterface { - /** - * 开启转写 - */ - override fun openConversion() { - if (!isOpenSubtitles()) { - useManager.startExperience() - } - } - - /** - * 关闭转写 - */ - override fun closeConversion() { - if (!isOpenSubtitles()) { - useManager.stopExperience() - } - } - - /** - * 打开设置页面 - */ - override fun openSetting() { - if (!isOpenSubtitles()) { - openSetting() - } - } - }) + RttConversionManager(this, this.getManagerListener()) } /** * 字幕管理 */ - private val subtitlesManager by lazy { - RttSubtitlesManager(this, object : SubtitlesOptionsInterface { - override fun open() { - if (!isOpenConversion()) { - useManager.startExperience() - } - } - - override fun close() { - if (!isOpenConversion()) { - useManager.stopExperience() - } - } - }) - } + private val subtitlesManager by lazy { RttSubtitlesManager(this, getManagerListener()) } /** * 设置管理 */ - private val settingsManager by lazy { - RttSettingManager(this, object : SettingOptionsInterface {}) - } + private val settingsManager by lazy { RttSettingManager(this) } /** * 使用管理 */ - private val useManager by lazy { - RttUseManager(this, false, object : ExperienceOptionsInterface { - /** - * 当前倒计时信息 - * @param configAllowUse 配置是否允许使用rtt - * @param defTime 默认体验时间 - * @param reduceTime 体验剩余时间 - */ - override fun countDownCurrent(configAllowUse: Boolean, defTime: Int, reduceTime: Int) { - setExperienceInfo(configAllowUse, defTime, reduceTime) - } - - /** - * 结束体验 - */ - override fun stop() { - setShowStatusInfo(showProgress = false, showIcon = false, - text = rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) - } - }) - } - - /** - * 父布局工具视图 - */ - private var agoraEduOptionsComponent: AgoraEduOptionsComponent? = null + private val useManager by lazy { RttUseManager(this, false, getManagerListener()) } /** * 流回调监听 @@ -173,7 +108,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { super.onStreamMessage(channelId, streamId, data) val parseFrom = AgoraSpeech2TextProtobuffer.Text.parseFrom(data) val recordItem = useManager.disposeData(parseFrom, settingsManager.currentSettingInfo, eduCore?.eduContextPool()?.streamContext()) - subtitlesManager.setShowCurrentData(recordItem, settingsManager.currentSettingInfo) + subtitlesManager.setShowCurrentData(useManager.getRecordList(), recordItem, settingsManager.currentSettingInfo) conversionManager.updateShowList(useManager.getRecordList()) LogX.i(TAG, "onStreamMessage channelId=$channelId, streamId=$streamId, data=${GsonUtil.toJson(parseFrom)}") @@ -222,16 +157,166 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { AgoraUIToast.init(rttOptions.getApplicationContext()) } + /** + * 获取管理器监听 + */ + private fun getManagerListener(): FcrRttOptionsStatusListener { + return object : FcrRttOptionsStatusListener() { + /** + * rtt功能状态变更 + * @param open 开启-true,关闭-false + */ + override fun rttStateChange(open: Boolean) { + listenerList.forEach { it.rttStateChange(open) } + } + + /** + * 音频状态-无人讲话 + */ + override fun audioStateNoSpeaking() { + listenerList.forEach { it.audioStateNoSpeaking() } + } + + /** + * 音频状态-有人讲话 + */ + override fun audioStateSpeaking() { + listenerList.forEach { it.audioStateSpeaking() } + } + + /** + * 音频状态-超过一定时间无人讲话 + */ + override fun audioStateNoSpeakingMoreTime() { + listenerList.forEach { it.audioStateNoSpeakingMoreTime() } + } + + /** + * 音频状态-开启中 + */ + override fun audioStateOpening() { + listenerList.forEach { it.audioStateOpening() } + } + + /** + * 音频状态-无法使用 + */ + override fun audioStateNotAllowUse() { + listenerList.forEach { it.audioStateNotAllowUse() } + } + + /** + * 音频状态-显示设置提示 + */ + override fun audioStateShowSettingHint() { + listenerList.forEach { it.audioStateShowSettingHint() } + } + + /** + * 体验信息变更 + * @param configAllowUseRtt 配置是否可以使用rtt功能 + * @param experienceReduceTime 剩余体验时间 + * @param experienceDefaultTime 默认体验时间 + */ + override fun experienceInfoChange(configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) { + conversionManager.setExperienceInfo(configAllowUseRtt, experienceReduceTime) + listenerList.forEach { it.experienceInfoChange(configAllowUseRtt, experienceDefaultTime, experienceReduceTime) } + } + + /** + * 字幕状态变更 + * @param toOpen 开启-true,关闭-false + */ + override fun subtitlesStateChange(toOpen: Boolean) { + listenerList.forEach { it.subtitlesStateChange(toOpen) } + } + + /** + * 字幕视图重置 + */ + override fun subtitlesViewReset(openSuccess: Boolean) { + listenerList.forEach { it.subtitlesViewReset(openSuccess) } + } + + /** + * 转写状态变更 + * @param toOpen 开启-true,关闭-false + */ + override fun conversionStateChange(toOpen: Boolean) { + listenerList.forEach { it.conversionStateChange(toOpen) } + } + + /** + * 转写视图重置 + */ + override fun conversionViewReset() { + listenerList.forEach { it.conversionViewReset() } + } + + /** + * 声源语言修改 + */ + override fun sourceLanguageChange(language: RttLanguageEnum) { + listenerList.forEach { it.sourceLanguageChange(language) } + } + + /** + * 目标语言修改-网络请求结果 + */ + override fun targetLanguageChange(languages: List) { + listenerList.forEach { it.targetLanguageChange(languages) } + } + + /** + * 双语状态变更 + * @param open 开启-true,关闭-false + */ + override fun showDoubleLanguage(open: Boolean) { + listenerList.forEach { it.showDoubleLanguage(open) } + } + + /** + * 双语状态变更-网络请求结果 + * @param open 开启-true,关闭-false + */ + override fun showDoubleLanguageNetResult(open: Boolean) { + listenerList.forEach { it.showDoubleLanguageNetResult(open) } + } + + /** + * 消息改变 + * @param recordList 消息记录数据 + * @param currentData 当前要显示的数据 + */ + override fun onMessageChange(recordList: List, currentData: RttRecordItem?) { + listenerList.forEach { it.onMessageChange(recordList, currentData) } + } + } + } + + /** + * 新增监听 + */ + fun addListener(listener: FcrRttOptionsStatusListener) { + if (!listenerList.contains(listener)) { + listenerList.add(listener) + } + } + + /** + * 移除监听 + */ + fun removeListener(listener: FcrRttOptionsStatusListener) { + if (listenerList.contains(listener)) { + listenerList.remove(listener) + } + } + /** * 初始化视图 - * @param rttTipLeftTopStatus 左上角撰写中提示控件(布局内部组件) - * @param rttBottomCenterSubtitlesView 底部中间的字幕显示控件(布局内部组件) * @param agoraUIProvider UI配置信息 */ - fun initView(rttTipLeftTopStatus: ViewGroup, rttBottomCenterSubtitlesView: AgoraEduRttOptionsComponent, agoraUIProvider: IAgoraUIProvider) { - conversionManager.rttTipLeftTopConversionStatusView = rttTipLeftTopStatus - subtitlesManager.rttBottomCenterSubtitlesView = rttBottomCenterSubtitlesView - subtitlesManager.rttBottomCenterSubtitlesView!!.initView(this) + fun initView(agoraUIProvider: IAgoraUIProvider) { this.eduCore = agoraUIProvider.getAgoraEduCore() //重置视图 this.setRttFunctionStatusConfig() @@ -262,20 +347,12 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { return useManager.isAllowUse() } - /** - * 设置工具view的组件 - */ - fun setEduOptionsComponent(agoraEduOptionsComponent: AgoraEduOptionsComponent) { - this.agoraEduOptionsComponent = agoraEduOptionsComponent - } - /** * 设置RTT功能状态配置 * @param allowUse 是否允许使用rtt功能,不可使用的话则提供体验时间 */ - fun setRttFunctionStatusConfig(allowUse: Boolean = false) { + private fun setRttFunctionStatusConfig(allowUse: Boolean = false) { useManager.configAllowUse = allowUse -// this.rttToolBoxWidget?.setAllowUse(useManager.isAllowUse(), useManager.rttExperienceReduceTime) if (!allowUse) { this.resetShow() } @@ -285,18 +362,17 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 释放相关 */ fun release() { - conversionManager.resetShow() - subtitlesManager.resetShow() + listenerList.forEach { it.conversionViewReset();it.subtitlesViewReset(false) } this.eduCore?.eduContextPool()?.streamContext()?.removeHandler(steamHandler) this.eduCore?.eduContextPool()?.roomContext()?.removeHandler(roomHandler) this.eduCore?.eduContextPool()?.userContext()?.removeHandler(userHandler) + listenerList.clear() } /** * 开启字幕 */ fun openSubtitles() { - this.agoraEduOptionsComponent?.hiddenRtt() subtitlesManager.openSubtitles(useManager.configAllowUse, useManager.rttExperienceDefaultTime, useManager.rttExperienceReduceTime) } @@ -304,7 +380,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 关闭字幕 */ fun closeSubtitles() { - this.agoraEduOptionsComponent?.hiddenRtt() subtitlesManager.closeSubtitles() } @@ -312,7 +387,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 开启转写 */ fun openConversion() { - this.agoraEduOptionsComponent?.hiddenRtt() conversionManager.openConversion(useManager.getRecordList()) } @@ -320,7 +394,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 关闭转写 */ fun closeConversion() { - this.agoraEduOptionsComponent?.hiddenRtt() conversionManager.closeConversion() } @@ -335,10 +408,23 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 关闭设置 */ fun closeSetting() { - this.agoraEduOptionsComponent?.hiddenRtt() this.settingsManager.closeSetting() } + /** + * 开启体验 + */ + fun startExperience() { + this.useManager.startExperience() + } + + /** + * 关闭体验 + */ + fun stopExperience() { + this.useManager.stopExperience() + } + /** * 设置转换源语言 */ @@ -348,6 +434,10 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { override fun onSuccess(result: HttpBaseRes?) { useManager.setLastFinal() } + + override fun onError(httpCode: Int, code: Int, message: String?) { + super.onError(httpCode, code, message) + } }) } @@ -374,7 +464,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 获取体验剩余时间 */ fun getExperienceReduceTime(): Int { - return useManager.rttExperienceReduceTime; + return useManager.rttExperienceReduceTime } /** @@ -401,7 +491,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * @param openRttSubtitles 是否开启字幕 */ internal fun sendRequest(openRtt: Boolean, openRttSubtitles: Boolean, callback: HttpCallback>? = null) { - val body = RttChangeOptionsBody(openRttSubtitles, settingsManager.currentSettingInfo.sourceLan.value, + val body = FcrRttChangeOptionsData.formatUseData(openRtt, openRttSubtitles, settingsManager.currentSettingInfo.sourceLan.value, settingsManager.currentSettingInfo.targetLan.map { it.value }.toTypedArray()) val call = AppRetrofitManager.instance().getService(IRttOptionsService::class.java) .buildTokens(eduCore?.config?.appId, eduCore?.config?.roomUuid, if (openRtt) 1 else 0, body) @@ -412,6 +502,11 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } override fun onError(httpCode: Int, code: Int, message: String?) { + if (httpCode == 409) { + //已经开始了转写翻译任务 + callback?.onSuccess(null) + return + } callback?.onError(httpCode, code, message) } }) @@ -421,26 +516,108 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 重置显示 */ private fun resetShow() { - conversionManager.resetShow() - subtitlesManager.resetShow() - this.agoraEduOptionsComponent?.hiddenRtt() + listenerList.forEach { it.conversionViewReset();it.subtitlesViewReset(false) } } /** - * 设置状态信息 - */ - private fun setShowStatusInfo(showProgress: Boolean, showIcon: Boolean, text: String) { - subtitlesManager.setShowStatusInfo(showProgress, showIcon, text) + * 房间widget属性更新 + */ + fun onWidgetRoomPropertiesUpdated(properties: MutableMap, operator: EduBaseUserInfo?) { + if ("server" != operator?.userUuid) { + GsonUtil.jsonToObject(GsonUtil.toJson(properties))?.let { + operator?.let { userInfo -> + val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() + //判断是否改变了转写状态 + if (checkUpdateConversion(it)) { + val toOpen = 1 == it.transcribe + val textContent = "${formatRoleName(userInfo, localUserInfo)}${ + rttOptions.getApplicationContext().getString( + if (toOpen) R.string.fcr_dialog_rtt_text_conversion_state_open else R.string.fcr_dialog_rtt_text_conversion_state_close) + }" + val toastContent = "${formatRoleName(userInfo, localUserInfo)}${ + rttOptions.getApplicationContext().getString(if (userInfo.userUuid == localUserInfo?.userUuid) { + if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open_me_show else R.string.fcr_dialog_rtt_toast_conversion_state_close_me_show + } else { + if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open else R.string.fcr_dialog_rtt_toast_conversion_state_close + }) + }" + AgoraUIToast.showDefaultToast(rttOptions.getApplicationContext(), toastContent) + useManager.disposeDataChangeSourceLanguage(userInfo, textContent) + conversionManager.updateShowList(useManager.getRecordList()) + LogX.i(TAG, "changeConversionState result=$textContent}") + } + //判断是否开启了翻译 + if (!it.languages?.target.isNullOrEmpty() && settingsManager.lastRecordWidgetInfo?.languages?.target.isNullOrEmpty()) { + val showContent = rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_open_target_language) + useManager.disposeDataChangeSourceLanguage(userInfo, showContent) + conversionManager.updateShowList(useManager.getRecordList()) + LogX.i(TAG, "changeToOpenTargetLanguage result=$showContent}") + } + //判断是否修改了声源语言 + if (!it.languages?.source.isNullOrEmpty() && it.languages?.source != settingsManager.lastRecordWidgetInfo?.languages?.source) { + val sourceEnum = RttLanguageEnum.values().find { item -> item.value == it.languages?.source } + if (sourceEnum != null) { + //前置名称 + val useText = "${ + formatRoleName(userInfo, eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo()) + }${rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_change_source_language)}" + val languageText = rttOptions.getApplicationContext().getString(sourceEnum.textRes) + val showContent = SpannableString("$useText${languageText}") + showContent.setSpan( + ForegroundColorSpan(ContextCompat.getColor(rttOptions.getApplicationContext(), R.color.fcr_blue_4262)), + showContent.lastIndexOf(languageText), showContent.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) + AgoraUIToast.showDefaultToast(rttOptions.getApplicationContext(), showContent) + //通知处理 + useManager.disposeDataChangeSourceLanguage(userInfo, showContent.toString()) + conversionManager.updateShowList(useManager.getRecordList()) + LogX.i(TAG, "changeSourceLanguage source=${settingsManager.currentSettingInfo.sourceLan}, result=$sourceEnum}") + } + } + } + + settingsManager.lastRecordWidgetInfo = it + } + } } /** - * 设置体验信息 + * 检测是否更新了转写状态 */ - private fun setExperienceInfo(configAllowUse: Boolean, defTime: Int, reduceTime: Int) { - subtitlesManager.setExperienceInfo(configAllowUse, defTime, reduceTime) - conversionManager.setExperienceInfo(configAllowUse, reduceTime) + private fun checkUpdateConversion(it: FcrRttChangeOptionsData): Boolean { + if (1 == it.transcribe) { + if (settingsManager.lastRecordWidgetInfo == null || 0 == settingsManager.lastRecordWidgetInfo?.transcribe) { + return true + } + } else if (0 == it.transcribe) { + if (settingsManager.lastRecordWidgetInfo == null || 1 == settingsManager.lastRecordWidgetInfo?.transcribe) { + return true + } + } + return false } + /** + * 格式化名称角色显示 + * @param optionsUser 发起设置修改的用户信息 + * @param localUser 当前本地的用户信息 + */ + private fun formatRoleName(optionsUser: EduBaseUserInfo, localUser: AgoraEduContextUserInfo?): String { + return "${ + if (EduUserRole.STUDENT == optionsUser.role) { + rttOptions.getApplicationContext().getString(R.string.fcr_role_student) + } else if (EduUserRole.TEACHER == optionsUser.role) { + rttOptions.getApplicationContext().getString(R.string.fcr_role_teacher) + } else { + "" + } + }(${ + if (optionsUser.userUuid == localUser?.userUuid) { + rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_role_me) + } else { + optionsUser.userName + } + }) " + } } /** @@ -605,7 +782,6 @@ class RttRecordItem { * @param sourceLan 转换的源语言 * @param targetLan 转换的目标语言 * @param showDoubleLan 是否显示双语 - * @param rttTranslatorsRecordList 转换数据记录 */ class RttSettingInfo( rttOptionsManager: RttOptionsManager, @@ -758,42 +934,41 @@ private interface IRttOptionsService { @PUT("edu/apps/{appId}/v2/rooms/{roomId}/widgets/rtt/states/{state}") fun buildTokens( @Path("appId") appId: String?, @Path("roomId") roomId: String?, @Path("state") state: Int?, - @Body body: RttChangeOptionsBody, + @Body body: FcrRttChangeOptionsData, ): Call> } /** * 修改RTT配置操作请求实体 */ -private class RttChangeOptionsBody( - openSubtitle: Boolean, sourceLan: String, - targetLan: Array, -) { +private data class FcrRttChangeOptionsData( /** * 语言配置 */ - val languages = RttChangOptionsLanguage(sourceLan, targetLan) + var languages: RttChangOptionsLanguage? = null, /** - * 是否开启翻译 + * 是否开启转写 */ - val transcribe: Int = if (targetLan.isNotEmpty()) 1 else 0 + var transcribe: Int = 0, /** - * 是否开启转写 + * 是否开启字幕 */ - val subtitle: Int = if (openSubtitle) 1 else 0 + var subtitle: Int = 0, +) { + companion object { + fun formatUseData(openRtt: Boolean, openSubtitle: Boolean, sourceLan: String, targetLan: Array): FcrRttChangeOptionsData { + return FcrRttChangeOptionsData(RttChangOptionsLanguage(sourceLan, targetLan), if (targetLan.isNotEmpty() && openRtt) 1 else 0, + if (openSubtitle && openRtt) 1 else 0) + } + } } /** * Rtt-字幕逻辑管理 */ -private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManager, private val optionsCallback: SubtitlesOptionsInterface) { - - /** - * 字幕组件 - */ - var rttBottomCenterSubtitlesView: AgoraEduRttOptionsComponent? = null +private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManager, private val listener: FcrRttOptionsStatusListener) { /** * 是否开启成功 @@ -818,28 +993,18 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag super.handleMessage(msg) when (msg.what) { messageWhatOpenSuccess -> { - setShowStatusInfo(showProgress = false, showIcon = false, - text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) + listener.audioStateNoSpeaking() openSuccess = true - optionsCallback.open() + listener.subtitlesStateChange(true) } messageWhatNoSpeaking -> { - changeEduOptionsComponent(View.GONE) + listener.audioStateNoSpeakingMoreTime() } } } } - /** - * 修改字幕显示状态 - */ - private fun changeEduOptionsComponent(toView: Int) { - rttOptionsManager.rttOptions.runOnUiThread { - this.rttBottomCenterSubtitlesView?.visibility = toView - } - } - /** * 开启字幕 * @param configAllowUse 配置是否允许rtt @@ -847,31 +1012,32 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag * @param experienceReduceTime 体验剩余时间 */ fun openSubtitles(configAllowUse: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) { - changeEduOptionsComponent(View.VISIBLE) + listener.subtitlesViewReset(true) if (rttOptionsManager.isAllowUseRtt()) { //可以使用的话先隐藏体验,后续再根据条件判断是否显示 - setExperienceInfo(true, 0, 0) + listener.experienceInfoChange(configAllowUse, experienceDefaultTime, experienceReduceTime) //显示文案 - setShowStatusInfo(showProgress = true, showIcon = false, - text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening)) + listener.audioStateOpening() //发起开启rtt请求 rttOptionsManager.sendRequest(openRtt = true, openRttSubtitles = true, callback = object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { - subtitlesOpenSuccess() + //延迟两秒开启文案:点击字幕位置可以更改字幕设置 + listener.audioStateShowSettingHint() + rttOptionsManager.startExperience() + handler.sendEmptyMessageDelayed(messageWhatOpenSuccess, 2000) } override fun onError(httpCode: Int, code: Int, message: String?) { openSuccess = false - optionsCallback.close() + listener.subtitlesStateChange(false) } }) } else { - setExperienceInfo(configAllowUse, experienceDefaultTime, experienceReduceTime) - setShowStatusInfo(showProgress = false, showIcon = false, - text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) + listener.experienceInfoChange(configAllowUse, experienceDefaultTime, experienceReduceTime) + listener.audioStateNotAllowUse() openSuccess = false - optionsCallback.close() + listener.subtitlesStateChange(false) } } @@ -880,12 +1046,15 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag */ fun closeSubtitles() { openSuccess = false - this.resetShow() + listener.subtitlesViewReset(false) //发起开启rtt请求 rttOptionsManager.sendRequest(rttOptionsManager.isOpenConversion(), false, object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { openSuccess = false - optionsCallback.close() + listener.subtitlesStateChange(false) + if (!rttOptionsManager.isOpenConversion()) { + rttOptionsManager.stopExperience() + } } override fun onError(httpCode: Int, code: Int, message: String?) { @@ -893,13 +1062,6 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag }) } - /** - * 重置显示 - */ - fun resetShow() { - this.changeEduOptionsComponent(View.GONE) - } - /** * 是否开启了字幕 */ @@ -907,36 +1069,12 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag return openSuccess } - /** - * 设置体验信息 - */ - fun setExperienceInfo(allowUseConfig: Boolean, rttExperienceDefaultTime: Int, rttExperienceReduceTime: Int) { - rttBottomCenterSubtitlesView?.setExperienceInfo(allowUseConfig, rttExperienceDefaultTime, rttExperienceReduceTime) - } - - /** - * 设置状态细心 - */ - fun setShowStatusInfo(showProgress: Boolean, showIcon: Boolean, text: String) { - rttBottomCenterSubtitlesView?.setShowStatusInfo(showProgress = showProgress, showIcon = showIcon, text = text) - } - - /** - * 字幕开启成功 - */ - private fun subtitlesOpenSuccess() { - //延迟两秒开启文案:点击字幕位置可以更改字幕设置 - setShowStatusInfo(showProgress = false, showIcon = false, text = rttOptionsManager.rttOptions.getApplicationContext() - .getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening_success_hint)) - handler.sendEmptyMessageDelayed(messageWhatOpenSuccess, 2000) - } - /** * 设置当前翻译数据 */ - fun setShowCurrentData(recordItem: RttRecordItem?, currentSettingInfo: RttSettingInfo) { + fun setShowCurrentData(recordList: List, recordItem: RttRecordItem?, currentSettingInfo: RttSettingInfo) { if (isOpenSubtitles()) { - changeEduOptionsComponent(View.VISIBLE) + listener.subtitlesViewReset(true) handler.removeMessages(messageWhatOpenSuccess) handler.removeMessages(messageWhatNoSpeaking) @@ -947,14 +1085,13 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag val translating = targetText.isNullOrEmpty() && showTranslateOnly if (sourceText.isNullOrEmpty() && !translating) { - setShowStatusInfo(showProgress = false, showIcon = false, - text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) + listener.audioStateNoSpeaking() } else if (translating) { - setShowStatusInfo(showProgress = false, showIcon = true, - text = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_subtitles_text_listening)) + listener.audioStateSpeaking() } else { - rttBottomCenterSubtitlesView?.setShowTranslatorsInfo(recordItem?.userHeader ?: "", recordItem?.userName ?: "", sourceText ?: "", - targetText) + listener.onMessageChange(recordList, recordItem) +// rttBottomCenterSubtitlesView?.setShowTranslatorsInfo(recordItem?.userHeader ?: "", recordItem?.userName ?: "", sourceText ?: "", +// targetText) } //房间内有音频输出时,字幕区域显示对应转写文字,无音频输出时,字幕区域在音频停止3S后自动消失 handler.sendEmptyMessageDelayed(messageWhatNoSpeaking, 3000) @@ -965,7 +1102,7 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag /** * Rtt-转写逻辑管理 */ -private class RttConversionManager(private val rttOptionsManager: RttOptionsManager, private val optionsCallback: ConversionOptionsInterface) { +private class RttConversionManager(private val rttOptionsManager: RttOptionsManager, private val listener: FcrRttOptionsStatusListener) { /** * 是否开启成功 @@ -977,9 +1114,9 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana */ private val rttConversionDialog by lazy { AgoraUIRttConversionDialogBuilder(rttOptionsManager.rttOptions.getActivityContext()).build().apply { - setOnDismissListener { - this.optionsCallback!!.closeConversion() - } +// setOnDismissListener { +// this.optionsCallback!!.closeConversion() +// } optionsCallback = object : ConversionOptionsInterface { /** * 开启转写 @@ -987,18 +1124,19 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana override fun openConversion() { if (rttOptionsManager.isOpenSubtitles()) { openSuccess = true - this@RttConversionManager.optionsCallback.openConversion() + listener.conversionStateChange(true) } else { rttOptionsManager.sendRequest(openRtt = true, rttOptionsManager.isOpenSubtitles(), callback = object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { openSuccess = true - this@RttConversionManager.optionsCallback.openConversion() + listener.conversionStateChange(true) + rttOptionsManager.startExperience() } override fun onError(httpCode: Int, code: Int, message: String?) { openSuccess = false - this@RttConversionManager.optionsCallback.closeConversion() + listener.conversionStateChange(false) } }) } @@ -1014,7 +1152,8 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana callback = object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { openSuccess = false - this@RttConversionManager.optionsCallback.closeConversion() + listener.conversionStateChange(false) + rttOptionsManager.stopExperience() } override fun onError(httpCode: Int, code: Int, message: String?) { @@ -1022,7 +1161,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana }) } else { openSuccess = false - this@RttConversionManager.optionsCallback.closeConversion() + listener.conversionStateChange(false) } } @@ -1037,16 +1176,11 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana } } - /** - * 左上角撰写中view - */ - var rttTipLeftTopConversionStatusView: ViewGroup? = null - /** * 开启转写 */ fun openConversion(list: List) { - this.resetShow() + listener.conversionViewReset() if (rttOptionsManager.isAllowUseRtt()) { rttConversionDialog.show(list) rttConversionDialog.optionsCallback!!.openConversion() @@ -1057,18 +1191,13 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana * 关闭转写 */ fun closeConversion() { - this.resetShow() + listener.conversionViewReset() if (rttOptionsManager.isAllowUseRtt()) { rttConversionDialog.dismiss() + rttConversionDialog.optionsCallback!!.closeConversion() } } - /** - * 重置显示 - */ - fun resetShow() { - this.rttTipLeftTopConversionStatusView?.visibility = View.GONE - } /** * 是否开启了转写 @@ -1089,9 +1218,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana */ fun updateShowList(list: List) { if (isOpenConversion()) { - rttOptionsManager.rttOptions.runOnUiThread { - rttConversionDialog.updateShowList(list) - } + rttConversionDialog.updateShowList(list) } } } @@ -1099,7 +1226,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana /** * Rtt-设置弹窗管理 */ -private class RttSettingManager(private val rttOptionsManager: RttOptionsManager, private val optionsCallback: SettingOptionsInterface) { +private class RttSettingManager(private val rttOptionsManager: RttOptionsManager) { /** * RTT/转写设置弹窗 */ @@ -1138,6 +1265,11 @@ private class RttSettingManager(private val rttOptionsManager: RttOptionsManager */ val currentSettingInfo by lazy { RttSettingInfo(rttOptionsManager) } + /** + * 记录的上一次房间更新信息 + */ + var lastRecordWidgetInfo: FcrRttChangeOptionsData? = null + /** * 开启设置 */ @@ -1161,7 +1293,7 @@ private class RttSettingManager(private val rttOptionsManager: RttOptionsManager */ private class RttUseManager( private val rttOptionsManager: RttOptionsManager, var configAllowUse: Boolean = false, - private val optionsCallback: ExperienceOptionsInterface, + private val listener: FcrRttOptionsStatusListener, ) { /** * rtt功能默认体验时间,默认十分钟 @@ -1182,10 +1314,10 @@ private class RttUseManager( super.handleMessage(msg) rttExperienceReduceTime -= 1000 if (rttExperienceReduceTime <= 0) { - optionsCallback.countDownCurrent(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) + listener.experienceInfoChange(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) stopExperience() } else { - optionsCallback.countDownCurrent(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) + listener.experienceInfoChange(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) sendEmptyMessageDelayed(msg.what, 1000) } } @@ -1215,7 +1347,7 @@ private class RttUseManager( */ fun startExperience() { if (isAllowUse() && rttExperienceReduceTime > 0) { - optionsCallback.countDownCurrent(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) + listener.experienceInfoChange(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) handler.removeMessages(0) handler.sendEmptyMessageDelayed(0, 1000) } @@ -1225,10 +1357,22 @@ private class RttUseManager( * 结束体验 */ fun stopExperience() { - optionsCallback.stop() + listener.audioStateNotAllowUse() handler.removeMessages(0) } + /** + * 声源语言调整数据处理 + */ + fun disposeDataChangeSourceLanguage(userInfo: EduBaseUserInfo, text: String): RttRecordItem { + return RttRecordItem().apply { + uuid = UUID.randomUUID().toString() + userName = userInfo.userName + statusText = text + recordList.add(this) + } + } + /** * 处理数据 */ @@ -1313,7 +1457,7 @@ private class RttUseManager( } //格式化所有的用户信息 formatAllUserInfo(streamContext) - return if (true == paramsData?.isFinal) paramsData else null + return paramsData } /** @@ -1340,53 +1484,51 @@ private class RttUseManager( */ fun setLastFinal() { if (recordList.isNotEmpty()) { - recordList.get(recordList.size - 1).isFinal = true + recordList[recordList.size - 1].isFinal = true } } -} -/** - * 字幕接口逻辑处理 - */ -private interface SubtitlesOptionsInterface { - fun open() - fun close() } /** - * 体验相关接口回调 + * Rtt操作状态监听 */ -private interface ExperienceOptionsInterface { +abstract class FcrRttOptionsStatusListener { /** - * 当前倒计时信息 - * @param configAllowUse 配置是否允许使用rtt - * @param defTime 默认体验时间 - * @param reduceTime 体验剩余时间 + * rtt功能状态变更 + * @param open 开启-true,关闭-false */ - fun countDownCurrent(configAllowUse: Boolean, defTime: Int, reduceTime: Int) + open fun rttStateChange(open: Boolean) {} /** - * 结束体验 + * 音频状态-无人讲话 */ - fun stop() -} + open fun audioStateNoSpeaking() {} -/** - * 设置相关接口回调 - */ -private interface SettingOptionsInterface { + /** + * 音频状态-有人讲话 + */ + open fun audioStateSpeaking() {} -} + /** + * 音频状态-超过一定时间无人讲话 + */ + open fun audioStateNoSpeakingMoreTime() {} -/** - * Rtt操作状态监听 - */ -abstract class FcrRttOptionsStatusListener { /** - * rtt功能状态变更 - * @param open 开启-true,关闭-false + * 音频状态-开启中 */ - open fun rttStateChange(open: Boolean) {} + open fun audioStateOpening() {} + + /** + * 音频状态-无法使用 + */ + open fun audioStateNotAllowUse() {} + + /** + * 音频状态-显示设置提示 + */ + open fun audioStateShowSettingHint() {} /** * 体验信息变更 @@ -1399,53 +1541,35 @@ abstract class FcrRttOptionsStatusListener { /** * 字幕状态变更 * @param toOpen 开启-true,关闭-false - * @param configAllowUseRtt 配置是否可以使用rtt功能 - * @param experienceReduceTime 剩余体验时间 - * @param experienceDefaultTime 默认体验时间 */ - open fun subtitlesStateChange(toOpen: Boolean, configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) {} + open fun subtitlesStateChange(toOpen: Boolean) {} /** - * 字幕状态变更-网络请求结果 - * @param open 开启-true,关闭-false + * 字幕视图重置 */ - open fun subtitlesStateChangeNetResult(open: Boolean) {} + open fun subtitlesViewReset(openSuccess: Boolean) {} /** * 转写状态变更 * @param toOpen 开启-true,关闭-false - * @param configAllowUseRtt 配置是否可以使用rtt功能 - * @param experienceReduceTime 剩余体验时间 - * @param experienceDefaultTime 默认体验时间 */ - open fun conversionStateChange(toOpen: Boolean, configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) {} + open fun conversionStateChange(toOpen: Boolean) {} /** - * 转写状态变更-网络请求结果 - * @param open 开启-true,关闭-false + * 转写视图重置 */ - open fun conversionStateChangeNetResult(open: Boolean) {} + open fun conversionViewReset() {} /** * 声源语言修改 */ open fun sourceLanguageChange(language: RttLanguageEnum) {} - /** - * 声源语言修改-网络请求结果 - */ - open fun sourceLanguageChangeNetResult(language: RttLanguageEnum) {} - /** * 目标语言修改-网络请求结果 */ open fun targetLanguageChange(languages: List) {} - /** - * 目标语言修改 - */ - open fun targetLanguageChangeNetResult(languages: List) {} - /** * 双语状态变更 * @param open 开启-true,关闭-false diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt index 589b466e4..a98b23ba4 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt @@ -429,8 +429,10 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite * 隐藏rtt相关的 */ fun hiddenRtt() { - hiddenItem() - binding.optionItemRtt.isActivated = false + post { + hiddenItem() + binding.optionItemRtt.isActivated = false + } } fun setHandsupTimeout(seconds: Int) { diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt index c1bb7d5da..d68b67973 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt @@ -72,22 +72,24 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { * 重置显示位置 */ fun resetShowPosition() { - if (width == 0) { - viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { - override fun onGlobalLayout() { - resetShowPosition() - viewTreeObserver.removeOnGlobalLayoutListener(this) + runOnUIThread { + if (width == 0) { + viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { + override fun onGlobalLayout() { + resetShowPosition() + viewTreeObserver.removeOnGlobalLayoutListener(this) + } + }) + } else { + val params = layoutParams as MarginLayoutParams + params.leftMargin = ((parent as View).width - width) / 2 + params.bottomMargin = 100 + if (params is ConstraintLayout.LayoutParams) { + params.topToTop = -1 + params.bottomToBottom = (parent as View).id } - }) - } else { - val params = layoutParams as MarginLayoutParams - params.leftMargin = ((parent as View).width - width) / 2 - params.bottomMargin = 100 - if (params is ConstraintLayout.LayoutParams) { - params.topToTop = -1 - params.bottomToBottom = (parent as View).id + setLayoutParams(params) } - setLayoutParams(params) } } @@ -95,8 +97,10 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { * 显示的时候需要再树布局测绘完成后再显示 */ override fun setVisibility(visibility: Int) { - resetShowPosition() - super.setVisibility(visibility) + runOnUIThread{ + resetShowPosition() + super.setVisibility(visibility) + } } /** diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index 65d657f81..1f1926e08 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -10,8 +10,10 @@ import io.agora.agoraeducore.extensions.widgets.AgoraBaseWidget import io.agora.online.R import io.agora.online.component.common.IAgoraUIProvider import io.agora.online.databinding.FcrOnlineToolBoxWidgetContentBinding +import io.agora.online.helper.FcrRttOptionsStatusListener import io.agora.online.helper.IRttOptions import io.agora.online.helper.RttOptionsManager +import io.agora.online.helper.RttRecordItem import io.agora.online.options.AgoraEduOptionsComponent import io.agora.online.options.AgoraEduRttOptionsComponent import java.text.MessageFormat @@ -54,6 +56,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { operator: EduBaseUserInfo?, ) { super.onWidgetRoomPropertiesUpdated(properties, cause, keys, operator) + contentView?.onWidgetRoomPropertiesUpdated(properties, cause, keys, operator) } /** @@ -66,17 +69,88 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { internal inner class AgoraRttToolBoxWidgetContent( val container: ViewGroup, agoraUIProvider: IAgoraUIProvider, - agoraEduOptionsComponent: AgoraEduOptionsComponent, conversionStatusView: ViewGroup, subtitleView: AgoraEduRttOptionsComponent, + agoraEduOptionsComponent: AgoraEduOptionsComponent?, conversionStatusView: ViewGroup?, subtitleView: AgoraEduRttOptionsComponent?, ) : IRttOptions { - private val TAG = "AgoraRttToolBoxWidgetContent" - /** * Rtt功能的管理 */ private val rttOptionsManager: RttOptionsManager by lazy { RttOptionsManager(this).also { - it.initView(conversionStatusView, subtitleView, agoraUIProvider) - it.setEduOptionsComponent(agoraEduOptionsComponent) + subtitleView?.initView(agoraUIProvider) + it.initView(agoraUIProvider) + it.addListener(object : FcrRttOptionsStatusListener() { + override fun conversionViewReset() { + super.conversionViewReset() + agoraEduOptionsComponent?.hiddenRtt() + conversionStatusView?.visibility = View.GONE + } + + override fun subtitlesViewReset(openSuccess: Boolean) { + super.subtitlesViewReset(openSuccess) + agoraEduOptionsComponent?.hiddenRtt() + if (openSuccess) { + subtitleView?.visibility = View.VISIBLE + } else { + subtitleView?.visibility = View.GONE + } + } + + override fun subtitlesStateChange(toOpen: Boolean) { + super.subtitlesStateChange(toOpen) + agoraEduOptionsComponent?.hiddenRtt() + } + + override fun conversionStateChange(toOpen: Boolean) { + super.conversionStateChange(toOpen) + agoraEduOptionsComponent?.hiddenRtt() + } + + override fun experienceInfoChange(configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) { + super.experienceInfoChange(configAllowUseRtt, experienceDefaultTime, experienceReduceTime) + subtitleView?.setExperienceInfo(configAllowUseRtt, experienceDefaultTime, experienceReduceTime) + } + + override fun audioStateNotAllowUse() { + super.audioStateNotAllowUse() + subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) + } + + override fun audioStateNoSpeaking() { + super.audioStateNoSpeaking() + subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) + } + + override fun audioStateNoSpeakingMoreTime() { + super.audioStateNoSpeakingMoreTime() + subtitleView?.visibility = View.GONE + } + + override fun audioStateOpening() { + super.audioStateOpening() + subtitleView?.setShowStatusInfo(showProgress = true, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening)) + } + + override fun audioStateShowSettingHint() { + super.audioStateShowSettingHint() + subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening_success_hint)) + } + + override fun audioStateSpeaking() { + super.audioStateSpeaking() + subtitleView?.setShowStatusInfo(showProgress = false, showIcon = true, + text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_listening)) + } + + override fun onMessageChange(recordList: List, currentData: RttRecordItem?) { + super.onMessageChange(recordList, currentData) + subtitleView?.setShowTranslatorsInfo(currentData?.userHeader ?: "", currentData?.userName ?: "", + currentData?.sourceText ?: "", currentData?.targetText) + } + }) } } @@ -161,5 +235,15 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun runOnUiThread(runnable: Runnable) { container.post(runnable) } + + /** + * widget属性信息变更 + */ + fun onWidgetRoomPropertiesUpdated( + properties: MutableMap, cause: MutableMap?, keys: MutableList, + operator: EduBaseUserInfo?, + ) { + rttOptionsManager.onWidgetRoomPropertiesUpdated(properties, operator) + } } } \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/drawable/fcr_online_edu_rtt_conversion_dialog_options_arrow.xml b/AgoraCloudScene/src/main/res/drawable/fcr_online_edu_rtt_conversion_dialog_options_arrow.xml index d8dc10ec0..808b3d2b8 100644 --- a/AgoraCloudScene/src/main/res/drawable/fcr_online_edu_rtt_conversion_dialog_options_arrow.xml +++ b/AgoraCloudScene/src/main/res/drawable/fcr_online_edu_rtt_conversion_dialog_options_arrow.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml index 84ecad400..5c3d78c78 100644 --- a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml @@ -104,6 +104,7 @@ android:textColor="@color/fcr_v2_black" android:textColorHint="@color/fcr_black_BBBBBB" android:hint="@string/fcr_cloud_search" + android:imeOptions="actionSearch" android:imeActionLabel="@string/fcr_cloud_search"/> 停止转写 当前没有人在说话 正在聆听 + + 已将声源语言设置为 + 已开启实时转写 + 已停止实时转写 + 开启翻译识别内容 + 开启了实时转写服务,全体用户可见。 + 停止了实时转写服务,全体用户可见。 + 开启了实时转写服务。 + 停止了实时转写服务,全体用户可见 不翻译 zh-HK zh-CN From 5fa522c479475649f3a6cab5ce76c1368d916201 Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 22 Jul 2024 00:30:12 +0800 Subject: [PATCH 08/32] =?UTF-8?q?ui=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dialog/AgoraUIRttConversionDialog.kt | 7 +- .../online/component/toast/AgoraUIToast.kt | 42 +--- .../online/helper/FcrRttOptionsManager.kt | 185 +++++++++++++----- .../options/AgoraEduRttOptionsComponent.kt | 3 +- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 152 +++++++------- .../fcr_online_edu_rtt_options_component.xml | 16 ++ ...ine_rtt_conversion_dialog_list_content.xml | 15 +- .../src/main/res/values/colors.xml | 1 + 8 files changed, 261 insertions(+), 160 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt index c8e4c56cb..3d07c9375 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -22,6 +22,7 @@ import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions import io.agora.online.R import io.agora.online.databinding.FcrOnlineEduRttConversionDialogBinding +import io.agora.online.helper.RttLanguageEnum import io.agora.online.helper.RttRecordItem import java.text.MessageFormat import java.text.SimpleDateFormat @@ -331,7 +332,6 @@ private class RecordAdapter(private val context: Context, val dataList: ArrayLis return count } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { if (dataList[viewType].statusText.isNullOrEmpty()) { return object : RecyclerView.ViewHolder( @@ -353,6 +353,8 @@ private class RecordAdapter(private val context: Context, val dataList: ArrayLis holder.itemView.let { Glide.with(context).load(bean.userHeader).skipMemoryCache(true).placeholder(R.drawable.agora_video_ic_audio_on) .apply(RequestOptions.circleCropTransform()).into(it.findViewById(R.id.agora_fcr_rtt_text_dialog_user_header)) + it.findViewById(R.id.agora_fcr_rtt_text_dialog_user_header_text).text = + if (bean.userName.isNullOrEmpty()) "" else bean.userName!!.substring(0, 1) it.findViewById(R.id.agora_fcr_rtt_text_dialog_user_name).text = bean.userName it.findViewById(R.id.agora_fcr_rtt_text_dialog_time).text = (if (bean.time == null || bean.time == 0L) null else bean.time)?.let { time -> Date(time) } @@ -399,7 +401,8 @@ private class RecordAdapter(private val context: Context, val dataList: ArrayLis } it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_result).apply { - visibility = if (bean.currentTargetLan.isNullOrEmpty()) View.GONE else View.VISIBLE + visibility = if (bean.currentTargetLan.isNullOrEmpty() || bean.currentTargetLan!!.contains( + RttLanguageEnum.NONE)) View.GONE else View.VISIBLE } } } else { diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt index 1ebc9932a..5426e5ec0 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt @@ -130,48 +130,22 @@ object AgoraUIToast { showDefaultToast(context, SpannableString(message), duration) } - /** - * 上一次显示的默认吐司 - */ - private var lastShowToast: Toast? = null - -// /** -// * 默认吐司控制器,用来移除到达时间的view视图 -// */ -// private val defaultToastHandler = object : Handler(Looper.getMainLooper()) { -// override fun handleMessage(msg: Message) { -// super.handleMessage(msg) -// if (lastShowToast?.view != null) { -// (lastShowToast!!.view as ViewGroup).children.forEach { -// if (it.id == msg.what) { -// (lastShowToast!!.view as ViewGroup).removeView(it) -// } -// } -// } -// } -// } - /** * 显示默认弹窗 */ fun showDefaultToast(context: Context, message: SpannableString, duration: Int = LENGTH_SHORT) { ContextCompat.getMainExecutor(context).execute { computeValues() - if (lastShowToast == null) { - lastShowToast = Toast(context.applicationContext) - lastShowToast!!.view = LayoutInflater.from(context).inflate(R.layout.fcr_online_toast_layout_default, null, false) - } - lastShowToast!!.cancel() - (lastShowToast!!.view as ViewGroup).removeAllViews() + val showToast = Toast(context.applicationContext) + showToast.view = LayoutInflater.from(context).inflate(R.layout.fcr_online_toast_layout_default, null, false) + showToast.cancel() + (showToast.view as ViewGroup).removeAllViews() val view = getDefaultTextView(context, message) view.id = (Math.random() * 1000000000000).toInt() -// //一定时间之后移除视图 -// defaultToastHandler.sendEmptyMessageDelayed(view.id, -// if (LENGTH_SHORT == duration) 1500 else if (LENGTH_LONG == duration) 3000 else duration.toLong()) - (lastShowToast!!.view as ViewGroup).addView(view) - lastShowToast!!.duration = Toast.LENGTH_LONG - lastShowToast!!.setGravity(Gravity.CENTER, 0, -context.resources.getDimensionPixelOffset(R.dimen.dp_40)) - lastShowToast!!.show() + (showToast.view as ViewGroup).addView(view) + showToast.duration = Toast.LENGTH_LONG + showToast.setGravity(Gravity.CENTER, 0, -context.resources.getDimensionPixelOffset(R.dimen.dp_40)) + showToast.show() } } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index a0f7dd201..ae8d628cf 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -106,6 +106,10 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { private val steamHandler = object : StreamHandler() { override fun onStreamMessage(channelId: String, streamId: Int, data: ByteArray?) { super.onStreamMessage(channelId, streamId, data) + //不允许使用或者已到体验时间就不在回调 + if (!isAllowUseRtt()) { + return + } val parseFrom = AgoraSpeech2TextProtobuffer.Text.parseFrom(data) val recordItem = useManager.disposeData(parseFrom, settingsManager.currentSettingInfo, eduCore?.eduContextPool()?.streamContext()) subtitlesManager.setShowCurrentData(useManager.getRecordList(), recordItem, settingsManager.currentSettingInfo) @@ -244,6 +248,11 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ override fun conversionStateChange(toOpen: Boolean) { listenerList.forEach { it.conversionStateChange(toOpen) } + val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() + if (localUserInfo != null) { + conversionManager.addStateChangeTextMessage(if (toOpen) 1 else 0, EduBaseUserInfo(localUserInfo.userUuid, localUserInfo.userName, + EduUserRole.values().find { it.value == localUserInfo.role.value }!!), localUserInfo, useManager) + } } /** @@ -258,6 +267,11 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ override fun sourceLanguageChange(language: RttLanguageEnum) { listenerList.forEach { it.sourceLanguageChange(language) } + val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() + if (localUserInfo != null) { + conversionManager.addSourceLanguageChangeMessage(EduBaseUserInfo(localUserInfo.userUuid, localUserInfo.userName, + EduUserRole.values().find { it.value == localUserInfo.role.value }!!), localUserInfo, useManager, settingsManager, language) + } } /** @@ -265,6 +279,12 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ override fun targetLanguageChange(languages: List) { listenerList.forEach { it.targetLanguageChange(languages) } + val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() + if (localUserInfo != null) { + conversionManager.addTargetLanguageChangeMessage(!languages.contains(RttLanguageEnum.NONE), + EduBaseUserInfo(localUserInfo.userUuid, localUserInfo.userName, + EduUserRole.values().find { it.value == localUserInfo.role.value }!!), useManager) + } } /** @@ -433,10 +453,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { sendRequest(isOpenConversion() || isOpenSubtitles(), isOpenSubtitles(), object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { useManager.setLastFinal() - } - - override fun onError(httpCode: Int, code: Int, message: String?) { - super.onError(httpCode, code, message) + this@RttOptionsManager.getManagerListener().sourceLanguageChange(lan) } }) } @@ -449,6 +466,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { sendRequest(isOpenConversion() || isOpenSubtitles(), isOpenSubtitles(), object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { useManager.setLastFinal() + this@RttOptionsManager.getManagerListener().targetLanguageChange(lan.toList()) } }) } @@ -493,6 +511,9 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { internal fun sendRequest(openRtt: Boolean, openRttSubtitles: Boolean, callback: HttpCallback>? = null) { val body = FcrRttChangeOptionsData.formatUseData(openRtt, openRttSubtitles, settingsManager.currentSettingInfo.sourceLan.value, settingsManager.currentSettingInfo.targetLan.map { it.value }.toTypedArray()) + if (settingsManager.lastRecordWidgetInfo == null) { + settingsManager.lastRecordWidgetInfo = body + } val call = AppRetrofitManager.instance().getService(IRttOptionsService::class.java) .buildTokens(eduCore?.config?.appId, eduCore?.config?.roomUuid, if (openRtt) 1 else 0, body) AppRetrofitManager.exc(call, object : HttpCallback>() { @@ -529,52 +550,45 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() //判断是否改变了转写状态 if (checkUpdateConversion(it)) { - val toOpen = 1 == it.transcribe - val textContent = "${formatRoleName(userInfo, localUserInfo)}${ - rttOptions.getApplicationContext().getString( - if (toOpen) R.string.fcr_dialog_rtt_text_conversion_state_open else R.string.fcr_dialog_rtt_text_conversion_state_close) - }" - val toastContent = "${formatRoleName(userInfo, localUserInfo)}${ - rttOptions.getApplicationContext().getString(if (userInfo.userUuid == localUserInfo?.userUuid) { - if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open_me_show else R.string.fcr_dialog_rtt_toast_conversion_state_close_me_show - } else { - if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open else R.string.fcr_dialog_rtt_toast_conversion_state_close - }) - }" - AgoraUIToast.showDefaultToast(rttOptions.getApplicationContext(), toastContent) - useManager.disposeDataChangeSourceLanguage(userInfo, textContent) - conversionManager.updateShowList(useManager.getRecordList()) - LogX.i(TAG, "changeConversionState result=$textContent}") + conversionManager.addStateChangeTextMessage(it.transcribe, userInfo, localUserInfo, useManager) } //判断是否开启了翻译 if (!it.languages?.target.isNullOrEmpty() && settingsManager.lastRecordWidgetInfo?.languages?.target.isNullOrEmpty()) { - val showContent = rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_open_target_language) - useManager.disposeDataChangeSourceLanguage(userInfo, showContent) - conversionManager.updateShowList(useManager.getRecordList()) - LogX.i(TAG, "changeToOpenTargetLanguage result=$showContent}") + settingsManager.currentSettingInfo.targetLan = + it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() + ?: arrayOf() + conversionManager.addTargetLanguageChangeMessage(true, userInfo, useManager) } //判断是否修改了声源语言 if (!it.languages?.source.isNullOrEmpty() && it.languages?.source != settingsManager.lastRecordWidgetInfo?.languages?.source) { val sourceEnum = RttLanguageEnum.values().find { item -> item.value == it.languages?.source } if (sourceEnum != null) { - //前置名称 - val useText = "${ - formatRoleName(userInfo, eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo()) - }${rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_change_source_language)}" - val languageText = rttOptions.getApplicationContext().getString(sourceEnum.textRes) - val showContent = SpannableString("$useText${languageText}") - showContent.setSpan( - ForegroundColorSpan(ContextCompat.getColor(rttOptions.getApplicationContext(), R.color.fcr_blue_4262)), - showContent.lastIndexOf(languageText), showContent.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) - AgoraUIToast.showDefaultToast(rttOptions.getApplicationContext(), showContent) - //通知处理 - useManager.disposeDataChangeSourceLanguage(userInfo, showContent.toString()) - conversionManager.updateShowList(useManager.getRecordList()) - LogX.i(TAG, "changeSourceLanguage source=${settingsManager.currentSettingInfo.sourceLan}, result=$sourceEnum}") + settingsManager.currentSettingInfo.sourceLan = sourceEnum + conversionManager.addSourceLanguageChangeMessage(userInfo, localUserInfo, useManager, settingsManager, sourceEnum) } } } + settingsManager.lastRecordWidgetInfo = it + } + } + } + /** + * 初始化widget属性 + */ + fun onWidgetRoomPropertiesInit(properties: MutableMap?) { + if (properties != null) { + GsonUtil.jsonToObject(GsonUtil.toJson(properties))?.let { + //转写状态 + conversionManager.initOpenConversion(it.transcribe == 1) + //翻译 + settingsManager.currentSettingInfo.targetLan = + it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() ?: arrayOf() + //声源语言 + val sourceEnum = RttLanguageEnum.values().find { item -> item.value == it.languages?.source } + if (sourceEnum != null) { + settingsManager.currentSettingInfo.sourceLan = sourceEnum + } settingsManager.lastRecordWidgetInfo = it } } @@ -601,7 +615,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * @param optionsUser 发起设置修改的用户信息 * @param localUser 当前本地的用户信息 */ - private fun formatRoleName(optionsUser: EduBaseUserInfo, localUser: AgoraEduContextUserInfo?): String { + fun formatRoleName(optionsUser: EduBaseUserInfo, localUser: AgoraEduContextUserInfo?): String { return "${ if (EduUserRole.STUDENT == optionsUser.role) { rttOptions.getApplicationContext().getString(R.string.fcr_role_student) @@ -618,6 +632,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } }) " } + } /** @@ -985,6 +1000,11 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag */ private val messageWhatNoSpeaking = 1 + /** + * 正在聆听的消息类型 + */ + private val messageWhatListening = 2 + /** * 所有的定时相关的处理 */ @@ -993,9 +1013,15 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag super.handleMessage(msg) when (msg.what) { messageWhatOpenSuccess -> { - listener.audioStateNoSpeaking() openSuccess = true listener.subtitlesStateChange(true) + listener.audioStateSpeaking() + //显示两秒正在聆听 + sendEmptyMessageDelayed(messageWhatListening, 2000) + } + + messageWhatListening -> { + listener.audioStateNoSpeaking() } messageWhatNoSpeaking -> { @@ -1076,6 +1102,7 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag if (isOpenSubtitles()) { listener.subtitlesViewReset(true) handler.removeMessages(messageWhatOpenSuccess) + handler.removeMessages(messageWhatListening) handler.removeMessages(messageWhatNoSpeaking) //是否开启双语显示 @@ -1198,7 +1225,6 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana } } - /** * 是否开启了转写 */ @@ -1206,6 +1232,13 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana return openSuccess } + /** + * 初始化转写状态 + */ + fun initOpenConversion(state: Boolean) { + openSuccess = true + } + /** * 设置体验信息 */ @@ -1217,9 +1250,64 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana * 新增转写数据 */ fun updateShowList(list: List) { - if (isOpenConversion()) { - rttConversionDialog.updateShowList(list) + rttConversionDialog.updateShowList(list) + } + + /** + * 新增状态改变文本消息 + */ + fun addStateChangeTextMessage(transcribe: Int, userInfo: EduBaseUserInfo, localUserInfo: AgoraEduContextUserInfo?, useManager: RttUseManager) { + val toOpen = 1 == transcribe + val textContent = "${rttOptionsManager.formatRoleName(userInfo, localUserInfo)}${ + rttOptionsManager.rttOptions.getApplicationContext() + .getString(if (toOpen) R.string.fcr_dialog_rtt_text_conversion_state_open else R.string.fcr_dialog_rtt_text_conversion_state_close) + }" + val toastContent = "${rttOptionsManager.formatRoleName(userInfo, localUserInfo)}${ + rttOptionsManager.rttOptions.getApplicationContext().getString(if (userInfo.userUuid == localUserInfo?.userUuid) { + if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open_me_show else R.string.fcr_dialog_rtt_toast_conversion_state_close_me_show + } else { + if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open else R.string.fcr_dialog_rtt_toast_conversion_state_close + }) + }" + if(rttOptionsManager.isOpenConversion() || rttOptionsManager.isOpenSubtitles()) { + AgoraUIToast.showDefaultToast(rttOptionsManager.rttOptions.getApplicationContext(), toastContent) } + useManager.disposeDataChangeSourceLanguage(userInfo, textContent) + rttOptionsManager.rttOptions.runOnUiThread { updateShowList(useManager.getRecordList()) } + LogX.i(TAG, "changeConversionState result=$textContent}") + } + + /** + * 新增目标文本改变消息 + */ + fun addTargetLanguageChangeMessage(open: Boolean, userInfo: EduBaseUserInfo, useManager: RttUseManager) { + val showContent = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_open_target_language) + useManager.disposeDataChangeSourceLanguage(userInfo, showContent) + rttOptionsManager.rttOptions.runOnUiThread { updateShowList(useManager.getRecordList()) } + LogX.i(TAG, "changeToOpenTargetLanguage result=$showContent}") + } + + /** + * 新增声源语言改变消息 + */ + fun addSourceLanguageChangeMessage( + userInfo: EduBaseUserInfo, localUserInfo: AgoraEduContextUserInfo?, useManager: RttUseManager, + settingsManager: RttSettingManager, sourceEnum: RttLanguageEnum, + ) { + //前置名称 + val useText = "${ + rttOptionsManager.formatRoleName(userInfo, localUserInfo) + }${rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_change_source_language)}" + val languageText = rttOptionsManager.rttOptions.getApplicationContext().getString(sourceEnum.textRes) + val showContent = SpannableString("$useText${languageText}") + showContent.setSpan(ForegroundColorSpan(ContextCompat.getColor(rttOptionsManager.rttOptions.getApplicationContext(), R.color.fcr_blue_4262)), + showContent.lastIndexOf(languageText), showContent.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) + AgoraUIToast.showDefaultToast(rttOptionsManager.rttOptions.getApplicationContext(), showContent) + //通知处理 + useManager.disposeDataChangeSourceLanguage(userInfo, showContent.toString()) + updateShowList(useManager.getRecordList()) + LogX.i(TAG, "changeSourceLanguage source=${settingsManager.currentSettingInfo.sourceLan}, result=$sourceEnum}") + } } @@ -1313,11 +1401,10 @@ private class RttUseManager( override fun handleMessage(msg: Message) { super.handleMessage(msg) rttExperienceReduceTime -= 1000 + listener.experienceInfoChange(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) if (rttExperienceReduceTime <= 0) { - listener.experienceInfoChange(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) stopExperience() } else { - listener.experienceInfoChange(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) sendEmptyMessageDelayed(msg.what, 1000) } } @@ -1369,7 +1456,9 @@ private class RttUseManager( uuid = UUID.randomUUID().toString() userName = userInfo.userName statusText = text - recordList.add(this) + if (recordList.isEmpty() || recordList.isNotEmpty() && text != recordList[recordList.size - 1].statusText) { + recordList.add(this) + } } } @@ -1399,7 +1488,7 @@ private class RttUseManager( sourceLan = RttLanguageEnum.values().find { it.value == rttMsgData.culture } sourceText = sourceTextStr.toString() uid = rttMsgData.uid - time = rttMsgData.time + time = if (final) if (rttMsgData.time == 0L) rttMsgData.time else System.currentTimeMillis() else rttMsgData.time isFinal = final sourceConfidence = confidence } @@ -1409,7 +1498,7 @@ private class RttUseManager( uuid = UUID.randomUUID().toString() currentTargetLan = settingInfo.targetLan sourceText = sourceTextStr.toString() - time = rttMsgData.time + time = if (final) if (rttMsgData.time == 0L) rttMsgData.time else System.currentTimeMillis() else rttMsgData.time isFinal = final sourceConfidence = confidence } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt index d68b67973..9d670b40d 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt @@ -97,7 +97,7 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { * 显示的时候需要再树布局测绘完成后再显示 */ override fun setVisibility(visibility: Int) { - runOnUIThread{ + runOnUIThread { resetShowPosition() super.setVisibility(visibility) } @@ -158,6 +158,7 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { binding.agoraFcrRttTextDialogLayoutText.visibility = View.VISIBLE Glide.with(this).load(headImage).skipMemoryCache(true).placeholder(R.drawable.agora_video_ic_audio_on) .apply(RequestOptions.circleCropTransform()).into(binding.agoraFcrRttTextDialogUserHeader) + binding.agoraFcrRttTextDialogUserHeaderText.text = if (headImage.isEmpty()) "" else headImage.substring(0, 1) binding.agoraFcrRttTextDialogUserName.text = name binding.agoraFcrRttTextDialogTextOrigin.text = originText binding.agoraFcrRttTextDialogTextResult.text = resultText diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index 1f1926e08..3872baf7d 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -71,6 +71,80 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { val container: ViewGroup, agoraUIProvider: IAgoraUIProvider, agoraEduOptionsComponent: AgoraEduOptionsComponent?, conversionStatusView: ViewGroup?, subtitleView: AgoraEduRttOptionsComponent?, ) : IRttOptions { + private val listener = object : FcrRttOptionsStatusListener() { + override fun conversionViewReset() { + super.conversionViewReset() + agoraEduOptionsComponent?.hiddenRtt() + conversionStatusView?.visibility = View.GONE + } + + override fun subtitlesViewReset(openSuccess: Boolean) { + super.subtitlesViewReset(openSuccess) + agoraEduOptionsComponent?.hiddenRtt() + if (openSuccess) { + subtitleView?.visibility = View.VISIBLE + } else { + subtitleView?.visibility = View.GONE + } + } + + override fun subtitlesStateChange(toOpen: Boolean) { + super.subtitlesStateChange(toOpen) + agoraEduOptionsComponent?.hiddenRtt() + } + + override fun conversionStateChange(toOpen: Boolean) { + super.conversionStateChange(toOpen) + agoraEduOptionsComponent?.hiddenRtt() + } + + override fun experienceInfoChange(configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) { + super.experienceInfoChange(configAllowUseRtt, experienceDefaultTime, experienceReduceTime) + subtitleView?.setExperienceInfo(configAllowUseRtt, experienceDefaultTime, experienceReduceTime) + } + + override fun audioStateNotAllowUse() { + super.audioStateNotAllowUse() + subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) + } + + override fun audioStateNoSpeaking() { + super.audioStateNoSpeaking() + subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) + } + + override fun audioStateNoSpeakingMoreTime() { + super.audioStateNoSpeakingMoreTime() + subtitleView?.visibility = View.GONE + } + + override fun audioStateOpening() { + super.audioStateOpening() + subtitleView?.setShowStatusInfo(showProgress = true, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening)) + } + + override fun audioStateShowSettingHint() { + super.audioStateShowSettingHint() + subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening_success_hint)) + } + + override fun audioStateSpeaking() { + super.audioStateSpeaking() + subtitleView?.setShowStatusInfo(showProgress = false, showIcon = true, + text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_listening)) + } + + override fun onMessageChange(recordList: List, currentData: RttRecordItem?) { + super.onMessageChange(recordList, currentData) + subtitleView?.setShowTranslatorsInfo(currentData?.userHeader ?: "", currentData?.userName ?: "", + currentData?.sourceText ?: "", currentData?.targetText) + } + } + /** * Rtt功能的管理 */ @@ -78,79 +152,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { RttOptionsManager(this).also { subtitleView?.initView(agoraUIProvider) it.initView(agoraUIProvider) - it.addListener(object : FcrRttOptionsStatusListener() { - override fun conversionViewReset() { - super.conversionViewReset() - agoraEduOptionsComponent?.hiddenRtt() - conversionStatusView?.visibility = View.GONE - } - - override fun subtitlesViewReset(openSuccess: Boolean) { - super.subtitlesViewReset(openSuccess) - agoraEduOptionsComponent?.hiddenRtt() - if (openSuccess) { - subtitleView?.visibility = View.VISIBLE - } else { - subtitleView?.visibility = View.GONE - } - } - - override fun subtitlesStateChange(toOpen: Boolean) { - super.subtitlesStateChange(toOpen) - agoraEduOptionsComponent?.hiddenRtt() - } - - override fun conversionStateChange(toOpen: Boolean) { - super.conversionStateChange(toOpen) - agoraEduOptionsComponent?.hiddenRtt() - } - - override fun experienceInfoChange(configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) { - super.experienceInfoChange(configAllowUseRtt, experienceDefaultTime, experienceReduceTime) - subtitleView?.setExperienceInfo(configAllowUseRtt, experienceDefaultTime, experienceReduceTime) - } - - override fun audioStateNotAllowUse() { - super.audioStateNotAllowUse() - subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, - text = container.context.getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) - } - - override fun audioStateNoSpeaking() { - super.audioStateNoSpeaking() - subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, - text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) - } - - override fun audioStateNoSpeakingMoreTime() { - super.audioStateNoSpeakingMoreTime() - subtitleView?.visibility = View.GONE - } - - override fun audioStateOpening() { - super.audioStateOpening() - subtitleView?.setShowStatusInfo(showProgress = true, showIcon = false, - text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening)) - } - - override fun audioStateShowSettingHint() { - super.audioStateShowSettingHint() - subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, - text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening_success_hint)) - } - - override fun audioStateSpeaking() { - super.audioStateSpeaking() - subtitleView?.setShowStatusInfo(showProgress = false, showIcon = true, - text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_listening)) - } - - override fun onMessageChange(recordList: List, currentData: RttRecordItem?) { - super.onMessageChange(recordList, currentData) - subtitleView?.setShowTranslatorsInfo(currentData?.userHeader ?: "", currentData?.userName ?: "", - currentData?.sourceText ?: "", currentData?.targetText) - } - }) + it.addListener(listener) } } @@ -187,6 +189,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { * 重置显示状态 */ fun resetStatus() { + rttOptionsManager.onWidgetRoomPropertiesInit(widgetInfo?.roomProperties) val experienceReduceTime = rttOptionsManager.getExperienceReduceTime() binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() @@ -210,6 +213,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { fun dispose() { container.removeView(binding.root) + rttOptionsManager.removeListener(listener) } /** @@ -233,7 +237,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { * 判断并切换主线程 */ override fun runOnUiThread(runnable: Runnable) { - container.post(runnable) + (getActivityContext() as Activity).runOnUiThread(runnable) } /** diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_options_component.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_options_component.xml index 914e234f3..477f9d2a2 100644 --- a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_options_component.xml +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_options_component.xml @@ -112,6 +112,22 @@ android:background="@drawable/agora_solid_radius_max" android:backgroundTint="@color/fcr_white"/> + + + + #E2E2EE #E1EBFC #F9F9FC + #FC4C14 #191919 From a09136c991b73b77f9ca5ab6512e5d93d7a5e59b Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 22 Jul 2024 11:49:35 +0800 Subject: [PATCH 09/32] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../online/helper/FcrRttOptionsManager.kt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index ae8d628cf..df9129c90 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -450,7 +450,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ fun setSourceLanguage(lan: RttLanguageEnum) { settingsManager.currentSettingInfo.sourceLan = lan - sendRequest(isOpenConversion() || isOpenSubtitles(), isOpenSubtitles(), object : HttpCallback>() { + sendRequest(isOpenConversion(), isOpenSubtitles(), object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { useManager.setLastFinal() this@RttOptionsManager.getManagerListener().sourceLanguageChange(lan) @@ -463,7 +463,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ fun setTargetLanguage(lan: Array) { settingsManager.currentSettingInfo.targetLan = lan - sendRequest(isOpenConversion() || isOpenSubtitles(), isOpenSubtitles(), object : HttpCallback>() { + sendRequest(isOpenConversion() , isOpenSubtitles(), object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { useManager.setLastFinal() this@RttOptionsManager.getManagerListener().targetLanguageChange(lan.toList()) @@ -508,14 +508,14 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * @param openRtt 是否打开rtt功能 * @param openRttSubtitles 是否开启字幕 */ - internal fun sendRequest(openRtt: Boolean, openRttSubtitles: Boolean, callback: HttpCallback>? = null) { - val body = FcrRttChangeOptionsData.formatUseData(openRtt, openRttSubtitles, settingsManager.currentSettingInfo.sourceLan.value, + internal fun sendRequest(openRttConversion: Boolean, openRttSubtitles: Boolean, callback: HttpCallback>? = null) { + val body = FcrRttChangeOptionsData.formatUseData(openRttConversion, openRttSubtitles, settingsManager.currentSettingInfo.sourceLan.value, settingsManager.currentSettingInfo.targetLan.map { it.value }.toTypedArray()) if (settingsManager.lastRecordWidgetInfo == null) { settingsManager.lastRecordWidgetInfo = body } val call = AppRetrofitManager.instance().getService(IRttOptionsService::class.java) - .buildTokens(eduCore?.config?.appId, eduCore?.config?.roomUuid, if (openRtt) 1 else 0, body) + .buildTokens(eduCore?.config?.appId, eduCore?.config?.roomUuid, if (openRttConversion || openRttSubtitles) 1 else 0, body) AppRetrofitManager.exc(call, object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { useManager.setExperienceReduceTime(useManager.rttExperienceDefaultTime - ((result?.data?.duration ?: 0) * 1000)) @@ -973,9 +973,9 @@ private data class FcrRttChangeOptionsData( var subtitle: Int = 0, ) { companion object { - fun formatUseData(openRtt: Boolean, openSubtitle: Boolean, sourceLan: String, targetLan: Array): FcrRttChangeOptionsData { - return FcrRttChangeOptionsData(RttChangOptionsLanguage(sourceLan, targetLan), if (targetLan.isNotEmpty() && openRtt) 1 else 0, - if (openSubtitle && openRtt) 1 else 0) + fun formatUseData(openRttConversion : Boolean, openSubtitle: Boolean, sourceLan: String, targetLan: Array): FcrRttChangeOptionsData { + return FcrRttChangeOptionsData(RttChangOptionsLanguage(sourceLan, targetLan), if(openRttConversion) 1 else 0, + if (openSubtitle) 1 else 0) } } } @@ -1045,7 +1045,7 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag //显示文案 listener.audioStateOpening() //发起开启rtt请求 - rttOptionsManager.sendRequest(openRtt = true, openRttSubtitles = true, + rttOptionsManager.sendRequest(rttOptionsManager.isOpenConversion(), openRttSubtitles = true, callback = object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { //延迟两秒开启文案:点击字幕位置可以更改字幕设置 @@ -1153,7 +1153,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana openSuccess = true listener.conversionStateChange(true) } else { - rttOptionsManager.sendRequest(openRtt = true, rttOptionsManager.isOpenSubtitles(), + rttOptionsManager.sendRequest( true, rttOptionsManager.isOpenSubtitles(), callback = object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { openSuccess = true From 2fbbbd3b4fb124b1ed612e7421024a1a74bf1736 Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 22 Jul 2024 19:26:52 +0800 Subject: [PATCH 10/32] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../online/component/toast/AgoraUIToast.kt | 55 ++++++------------- .../online/helper/FcrRttOptionsManager.kt | 6 +- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 7 ++- .../fcr_online_toast_layout_default.xml | 17 +++++- 4 files changed, 42 insertions(+), 43 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt index 5426e5ec0..68920b44f 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/toast/AgoraUIToast.kt @@ -2,22 +2,18 @@ package io.agora.online.component.toast import android.annotation.SuppressLint import android.content.Context -import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.os.Handler import android.text.SpannableString -import android.util.TypedValue import android.view.Gravity import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.widget.RelativeLayout import android.widget.Toast import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView -import androidx.appcompat.widget.LinearLayoutCompat import androidx.core.content.ContextCompat import io.agora.online.R @@ -29,6 +25,7 @@ object AgoraUIToast { private const val LEVEL_INFO = 1 private const val LEVEL_WARN = 2 private const val LEVEL_ERROR = 3 + private const val LEVEL_NONE = 4 // private var COLOR_INFO = Color.parseColor("#FAFFFF") // private var COLOR_WARN = Color.parseColor("#FFFBF4") @@ -136,37 +133,15 @@ object AgoraUIToast { fun showDefaultToast(context: Context, message: SpannableString, duration: Int = LENGTH_SHORT) { ContextCompat.getMainExecutor(context).execute { computeValues() - val showToast = Toast(context.applicationContext) - showToast.view = LayoutInflater.from(context).inflate(R.layout.fcr_online_toast_layout_default, null, false) - showToast.cancel() - (showToast.view as ViewGroup).removeAllViews() - val view = getDefaultTextView(context, message) - view.id = (Math.random() * 1000000000000).toInt() - (showToast.view as ViewGroup).addView(view) - showToast.duration = Toast.LENGTH_LONG - showToast.setGravity(Gravity.CENTER, 0, -context.resources.getDimensionPixelOffset(R.dimen.dp_40)) - showToast.show() - } - } - - private fun getDefaultTextView(context: Context, message: SpannableString): View { - val showView = AppCompatTextView(context) - showView.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, context.resources.getDimensionPixelOffset(R.dimen.dp_40)) - showView.setPadding(context.resources.getDimensionPixelOffset(R.dimen.dp_20), context.resources.getDimensionPixelOffset(R.dimen.dp_10), - context.resources.getDimensionPixelOffset(R.dimen.dp_20), context.resources.getDimensionPixelOffset(R.dimen.dp_10)) - showView.setTextColor(ContextCompat.getColor(context, R.color.fcr_white)) - showView.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.resources.getDimension(R.dimen.sp_13)) - showView.setBackgroundResource(R.drawable.agora_solid_radius_max) - showView.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(context, R.color.fcr_black_66000000)) - showView.text = message - showView.gravity = Gravity.CENTER - return LinearLayoutCompat(context).apply{ - orientation = LinearLayoutCompat.VERTICAL - layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) - addView(showView) - addView(View(context).also {view-> - view.layoutParams = ViewGroup.LayoutParams(10, context.resources.getDimensionPixelOffset(R.dimen.dp_10)) - }) + val toastLayout = LayoutInflater.from(context).inflate(R.layout.fcr_online_toast_layout_default, null, false) + toastLayout.findViewById(R.id.tvText)?.let { msgView -> + msgView.text = message + } + val toast = Toast(context.applicationContext) + toast.view = toastLayout + toast.duration = duration + toast.setGravity(Gravity.TOP, 0, 200) + toast.show() } } @@ -176,14 +151,18 @@ object AgoraUIToast { * if null, display the toast at the system default position */ @SuppressLint("InflateParams") - private fun showToast(context: Context, level: Int, anchor: View?, text: String, duration: Int) { + private fun showToast(context: Context, level: Int, anchor: View?, text: CharSequence, duration: Int) { ContextCompat.getMainExecutor(context).execute { computeValues() val toastLayout = LayoutInflater.from(context).inflate(R.layout.fcr_online_toast_layout, null, false) toastLayout.findViewById(R.id.agora_toast_icon)?.let { icon -> - getToastIconRes(level)?.let { iconRes -> - icon.setImageResource(iconRes) + val res = getToastIconRes(level) + if(res !=null){ + icon.visibility = View.VISIBLE + icon.setImageResource(res) + }else{ + icon.visibility = View.GONE } } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index df9129c90..1d62f5a35 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -547,6 +547,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { if ("server" != operator?.userUuid) { GsonUtil.jsonToObject(GsonUtil.toJson(properties))?.let { operator?.let { userInfo -> + LogX.i(TAG, "onWidgetRoomPropertiesUpdated result=${GsonUtil.toJson(properties)}__user=${GsonUtil.toJson(operator)}") val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() //判断是否改变了转写状态 if (checkUpdateConversion(it)) { @@ -579,6 +580,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { fun onWidgetRoomPropertiesInit(properties: MutableMap?) { if (properties != null) { GsonUtil.jsonToObject(GsonUtil.toJson(properties))?.let { + LogX.i(TAG, "onWidgetRoomPropertiesInit result=${GsonUtil.toJson(properties)}}") //转写状态 conversionManager.initOpenConversion(it.transcribe == 1) //翻译 @@ -1208,8 +1210,8 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana */ fun openConversion(list: List) { listener.conversionViewReset() + rttConversionDialog.show(list) if (rttOptionsManager.isAllowUseRtt()) { - rttConversionDialog.show(list) rttConversionDialog.optionsCallback!!.openConversion() } } @@ -1236,7 +1238,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana * 初始化转写状态 */ fun initOpenConversion(state: Boolean) { - openSuccess = true + openSuccess = state } /** diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index 3872baf7d..4a4715ac3 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -76,6 +76,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { super.conversionViewReset() agoraEduOptionsComponent?.hiddenRtt() conversionStatusView?.visibility = View.GONE + resetStatus() } override fun subtitlesViewReset(openSuccess: Boolean) { @@ -86,16 +87,19 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { } else { subtitleView?.visibility = View.GONE } + resetStatus() } override fun subtitlesStateChange(toOpen: Boolean) { super.subtitlesStateChange(toOpen) agoraEduOptionsComponent?.hiddenRtt() + resetStatus() } override fun conversionStateChange(toOpen: Boolean) { super.conversionStateChange(toOpen) agoraEduOptionsComponent?.hiddenRtt() + resetStatus() } override fun experienceInfoChange(configAllowUseRtt: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) { @@ -176,11 +180,10 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { binding.agoraRttDialogConversion.setOnClickListener { if (this.rttOptionsManager.isOpenConversion()) { binding.agoraRttDialogConversionIcon.isActivated = false - this.rttOptionsManager.closeConversion() } else { binding.agoraRttDialogConversionIcon.isActivated = true - this.rttOptionsManager.openConversion() } + this.rttOptionsManager.openConversion() } resetStatus() } diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_toast_layout_default.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_toast_layout_default.xml index 37e7e87d3..d5d01764d 100644 --- a/AgoraCloudScene/src/main/res/layout/fcr_online_toast_layout_default.xml +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_toast_layout_default.xml @@ -3,5 +3,20 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:gravity="center" android:orientation="vertical" - tools:ignore="ResourceName" /> \ No newline at end of file + tools:ignore="ResourceName"> + + + + \ No newline at end of file From 20f3a05cd6eecad97bdf9d4ed84a382a6edf008d Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 22 Jul 2024 19:37:59 +0800 Subject: [PATCH 11/32] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index 4a4715ac3..82e8f92ec 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -185,6 +185,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { } this.rttOptionsManager.openConversion() } + rttOptionsManager.onWidgetRoomPropertiesInit(widgetInfo?.roomProperties) resetStatus() } @@ -192,7 +193,6 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { * 重置显示状态 */ fun resetStatus() { - rttOptionsManager.onWidgetRoomPropertiesInit(widgetInfo?.roomProperties) val experienceReduceTime = rttOptionsManager.getExperienceReduceTime() binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() From 2024a1bc4f1689ad4f3fedd1e4ec8e84cbd1e82f Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 5 Aug 2024 14:18:46 +0800 Subject: [PATCH 12/32] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/FcrRttToolBoxComponent.kt | 8 +- .../dialog/AgoraUIRttConversionDialog.kt | 1 - .../dialog/AgoraUIRttSettingDialog.kt | 146 +++++++++++------- .../options/AgoraEduOptionsComponent.kt | 46 ++++-- .../sdk/common/AgoraEduClassActivity.kt | 143 ++++++++++------- 5 files changed, 207 insertions(+), 137 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt index f4a64ff80..23094d76e 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt @@ -43,18 +43,14 @@ class FcrRttToolBoxComponent : AbsAgoraEduComponent { if (widget == null) { val widgetConfig = eduContext?.widgetContext()?.getWidgetConfig(widgetId) widgetConfig?.let { config -> - val widget = eduContext?.widgetContext()?.create(config) as FcrRttToolBoxWidget? + widget = eduContext?.widgetContext()?.create(config) as FcrRttToolBoxWidget? widget?.init(binding.root, agoraUIProvider, agoraEduOptionsComponent!!, conversionStatusView!!, subtitleView!!) } } } override fun onWidgetInActive(widgetId: String) { - if (widget != null) { - ContextCompat.getMainExecutor(binding.root.context).execute { - widget!!.release() - } - } + ContextCompat.getMainExecutor(binding.root.context).execute { widget?.release() } } } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt index 3d07c9375..4983dacee 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -217,7 +217,6 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago } } - /** * 显示弹窗 */ diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt index d1bc7891e..03342f52c 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt @@ -13,6 +13,7 @@ import android.text.style.ForegroundColorSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.LayoutRes import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView import androidx.core.content.ContextCompat @@ -182,66 +183,68 @@ private class ContentAdapter(var context: Context) : RecyclerView.Adapter { - if (position == 0) { - (viewHolder.itemView as AppCompatTextView).setText(R.string.fcr_dialog_rtt_setting_dialog_title_origin) - } else if (position == 2) { - (viewHolder.itemView as AppCompatTextView).setText(R.string.fcr_dialog_rtt_setting_dialog_title_result) - } + val itemEnum = contentConfigList[position] + when(itemEnum){ + FcrRttSettingContentItemEnum.SOURCE_LAN_TITLE -> { + (viewHolder.itemView as AppCompatTextView).setText(R.string.fcr_dialog_rtt_setting_dialog_title_origin) } - - R.layout.fcr_online_rtt_setting_dialog_content_list -> { - if (position == 1) { - (viewHolder.itemView as RecyclerView).apply { - adapter = mFromAdapter - mFromAdapter.onSelectChangedListener = object : OnSelectChangedListener { - override fun onChanged(select: SelectItem) { - val useText = "“${select.text}”"//使用的变色文本 - val content = SpannableString( - String.format(resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_content), useText)) - content.setSpan(ForegroundColorSpan(ContextCompat.getColor(context, R.color.fcr_blue_357BF6)), - content.indexOf(useText), content.indexOf(useText) + useText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) - - AgoraUIDialogBuilder(context).title(resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_title)) - .message(content).messagePaddingHorizontal(resources.getDimensionPixelOffset(R.dimen.dp_10)) - .negativeText(context.resources.getString(R.string.fcr_user_kick_out_cancel)).negativeClick { - mFromAdapter.resetUseLast() - }.positiveText(context.resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_confirm)).positiveClick { - listener?.setSourceLan(select.code) - - }.build().show() - } + FcrRttSettingContentItemEnum.SOURCE_LAN_LIST -> { + (viewHolder.itemView as RecyclerView).apply { + adapter = mFromAdapter + mFromAdapter.onSelectChangedListener = object : OnSelectChangedListener { + override fun onChanged(select: SelectItem) { + val useText = "“${select.text}”"//使用的变色文本 + val content = SpannableString( + String.format(resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_content), useText)) + content.setSpan(ForegroundColorSpan(ContextCompat.getColor(context, R.color.fcr_blue_357BF6)), + content.indexOf(useText), content.indexOf(useText) + useText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + + AgoraUIDialogBuilder(context).title(resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_title)) + .message(content).messagePaddingHorizontal(resources.getDimensionPixelOffset(R.dimen.dp_10)) + .negativeText(context.resources.getString(R.string.fcr_user_kick_out_cancel)).negativeClick { + mFromAdapter.resetUseLast() + }.positiveText(context.resources.getString(R.string.fcr_dialog_rtt_setting_dialog_change_confirm)).positiveClick { + listener?.setSourceLan(select.code) + + }.build().show() } - layoutManager = LinearLayoutManager(context) - for (i in 0 until itemDecorationCount) { - removeItemDecorationAt(0) - } - addItemDecoration(ListSelectItemDecoration(this)) } - } else if (position == 3) { - (viewHolder.itemView as RecyclerView).apply { - adapter = mToAdapter - mToAdapter.onSelectChangedListener = object : OnSelectChangedListener { - override fun onChanged(select: SelectItem) { - listener?.setTargetLan(select.code) - } - } - layoutManager = LinearLayoutManager(context) - for (i in 0 until itemDecorationCount) { - removeItemDecorationAt(0) + layoutManager = LinearLayoutManager(context) + for (i in 0 until itemDecorationCount) { + removeItemDecorationAt(0) + } + addItemDecoration(ListSelectItemDecoration(this)) + } + } + FcrRttSettingContentItemEnum.TARGET_LAN_TITLE -> { + (viewHolder.itemView as AppCompatTextView).setText(R.string.fcr_dialog_rtt_setting_dialog_title_result) + } + FcrRttSettingContentItemEnum.TARGET_LAN_LIST -> { + (viewHolder.itemView as RecyclerView).apply { + adapter = mToAdapter + mToAdapter.onSelectChangedListener = object : OnSelectChangedListener { + override fun onChanged(select: SelectItem) { + listener?.setTargetLan(select.code) } - addItemDecoration(ListSelectItemDecoration(this)) } + layoutManager = LinearLayoutManager(context) + for (i in 0 until itemDecorationCount) { + removeItemDecorationAt(0) + } + addItemDecoration(ListSelectItemDecoration(this)) } } - - R.layout.fcr_online_rtt_setting_dialog_content_switch -> { + FcrRttSettingContentItemEnum.DOUBLE_LAN_SHOW_SWITCH -> { viewHolder.itemView.apply { findViewById(R.id.fcr_online_rtt_setting_dialog_content_switch_title).setText( R.string.fcr_dialog_rtt_setting_dialog_title_switch) @@ -267,17 +270,10 @@ private class ContentAdapter(var context: Context) : RecyclerView.Adapter R.layout.fcr_online_rtt_setting_dialog_content_title - 1 -> R.layout.fcr_online_rtt_setting_dialog_content_list - 2 -> R.layout.fcr_online_rtt_setting_dialog_content_title - 3 -> R.layout.fcr_online_rtt_setting_dialog_content_list - 4 -> R.layout.fcr_online_rtt_setting_dialog_content_switch - else -> R.layout.fcr_online_rtt_setting_dialog_content_title - } + return contentConfigList[position].layoutResId } - override fun getItemCount() = 5 + override fun getItemCount() = contentConfigList.size override fun getItemViewType(position: Int) = position @@ -336,3 +332,39 @@ private class ListSelectItemDecoration(private val recyclerView: RecyclerView) : * 选项 */ private class SelectItem(val text: String, val code: String, val allowSelect: Boolean, var select: Boolean) + +/** + * Rtt设置内容弹窗的item枚举 + */ +private enum class FcrRttSettingContentItemEnum(@LayoutRes val layoutResId: Int) { + /** + * 声源语言标题 + */ + SOURCE_LAN_TITLE(R.layout.fcr_online_rtt_setting_dialog_content_title), + + /** + * 声源语言列表 + */ + SOURCE_LAN_LIST(R.layout.fcr_online_rtt_setting_dialog_content_list), + + /** + * 翻译语言标题 + */ + TARGET_LAN_TITLE(R.layout.fcr_online_rtt_setting_dialog_content_title), + + /** + * 翻译语言列表 + */ + TARGET_LAN_LIST(R.layout.fcr_online_rtt_setting_dialog_content_list), + + /** + * 是否显示双语 + */ + DOUBLE_LAN_SHOW_SWITCH(R.layout.fcr_online_rtt_setting_dialog_content_switch); + + companion object { + fun getShowConfigList(): List { + return listOf(SOURCE_LAN_TITLE, SOURCE_LAN_LIST, TARGET_LAN_TITLE, TARGET_LAN_LIST, DOUBLE_LAN_SHOW_SWITCH) + } + } +} diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt index a98b23ba4..290f047f6 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt @@ -49,7 +49,8 @@ import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionSignal import io.agora.online.provider.AgoraUIUserDetailInfo import io.agora.online.provider.UIDataProviderListenerImpl -class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhiteBoardIconClickListener, OnAgoraTransportListener { +class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhiteBoardIconClickListener, + OnAgoraTransportListener { constructor(context: Context) : super(context) constructor(context: Context, attr: AttributeSet) : super(context, attr) constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr) @@ -139,7 +140,10 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite isRequestHelp = true } } else { - AgoraUIToast.warn(context.applicationContext, text = resources.getString(R.string.fcr_group_teacher_exist_hint)) + AgoraUIToast.warn( + context.applicationContext, + text = resources.getString(R.string.fcr_group_teacher_exist_hint) + ) } } binding.optionItemSetting.setOnClickListener { @@ -166,13 +170,18 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite binding.optionItemRoster.setOnClickListener { if (!binding.optionItemRoster.isActivated) { popupViewRoster?.isShow = true - if (eduContext?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.SMALL_CLASS.value || eduContext?.roomContext() - ?.getRoomInfo()?.roomType?.value == RoomType.GROUPING_CLASS.value) { + if (eduContext?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.SMALL_CLASS.value + || eduContext?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.GROUPING_CLASS.value + ) { showItem(popupViewRoster, R.dimen.agora_userlist_dialog_w, R.dimen.agora_userlist_dialog_h) popupViewRoster?.showUserList() } else { popupViewRoster?.updateStuListData()//默认加载第一页数据 - showItem(popupViewRoster, R.dimen.agora_userlist_dialog_large_w, R.dimen.agora_userlist_dialog_large_h) + showItem( + popupViewRoster, + R.dimen.agora_userlist_dialog_large_w, + R.dimen.agora_userlist_dialog_large_h + ) } setIconActivated(binding.optionItemRoster) } else { @@ -196,10 +205,11 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite eduContext?.widgetContext()?.setWidgetActive(AgoraWidgetDefaultId.WhiteBoard.id, info) } - eduCore?.eduContextPool()?.widgetContext()?.addWidgetMessageObserver(whiteBoardWidgetMsgObserver, AgoraWidgetDefaultId.WhiteBoard.id) + eduCore?.eduContextPool()?.widgetContext() + ?.addWidgetMessageObserver(whiteBoardWidgetMsgObserver, AgoraWidgetDefaultId.WhiteBoard.id) } - fun cancelHandsUp() { + fun cancelHandsUp(){ ContextCompat.getMainExecutor(context).execute { binding.optionShowHandup.visibility = View.GONE binding.optionItemHandup.cancelHandsUp() @@ -240,8 +250,9 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite val localUserUuid = eduContext?.userContext()?.getLocalUserInfo()?.userUuid - if (eduContext?.userContext()?.getLocalUserInfo()?.role == AgoraEduContextUserRole.Student && eduContext?.roomContext() - ?.getRoomInfo()?.roomType == RoomType.LARGE_CLASS) {//大班课,才会进这里的逻辑 + if (eduContext?.userContext()?.getLocalUserInfo()?.role == AgoraEduContextUserRole.Student + && eduContext?.roomContext()?.getRoomInfo()?.roomType == RoomType.LARGE_CLASS + ) {//大班课,才会进这里的逻辑 isLocalUserOnStage = eduContext?.userContext()?.getCoHostList()?.find { it.userUuid == localUserUuid } != null if (!isLocalUserOnStage) {//本地不在台上了//todo //收回白板权限 @@ -310,7 +321,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite override fun onRemoteUserLeft( user: AgoraEduContextUserInfo, operator: AgoraEduContextUserInfo?, - reason: EduContextUserLeftReason, + reason: EduContextUserLeftReason ) { super.onRemoteUserLeft(user, operator, reason) // 老师离开小组 @@ -325,7 +336,10 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite super.onTeacherLaterJoin() if (isRequestHelp) { ContextCompat.getMainExecutor(context).execute { - AgoraUIToast.info(context.applicationContext, text = resources.getString(R.string.fcr_group_help_teacher_busy_msg)) + AgoraUIToast.info( + context.applicationContext, + text = resources.getString(R.string.fcr_group_help_teacher_busy_msg) + ) } isRequestHelp = false } @@ -342,6 +356,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite agroSettingWidget?.onExitListener = { onExitListener?.invoke() } + if (getUIConfig().roster.isVisible && eduCore?.config?.roleType != AgoraEduRoleType.AgoraEduRoleTypeObserver.value) { popupViewRoster = AgoraEduRosterComponent(context) popupViewRoster?.initView(agoraUIProvider) @@ -372,8 +387,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite } } LogX.i(TAG, "isOpenBoard=" + AgoraEduApplianceData.isOpenBoardWidget(eduCore) + "granted=" + localUserGranted) - if (AgoraEduApplianceData.isOpenBoardWidget( - eduCore) && (localUserGranted || localUser.role == AgoraEduContextUserRole.Teacher)) { // 可以显示白板按钮,如果是老师则默认显示 + if (AgoraEduApplianceData.isOpenBoardWidget(eduCore) && (localUserGranted || localUser.role == AgoraEduContextUserRole.Teacher)) { // 可以显示白板按钮,如果是老师则默认显示 setWhiteboardViewTool(true) } else { setWhiteboardViewTool(false) @@ -404,7 +418,11 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite */ private fun showItem(item: View?, widthDimenId: Int, heightDimenId: Int) { itemContainer.removeAllViews() - itemContainer.addView(item, context.resources.getDimensionPixelOffset(widthDimenId), context.resources.getDimensionPixelOffset(heightDimenId)) + itemContainer.addView( + item, + context.resources.getDimensionPixelOffset(widthDimenId), + context.resources.getDimensionPixelOffset(heightDimenId) + ) if (popupViewChat == item) { hiddenChatNews() } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/sdk/common/AgoraEduClassActivity.kt b/AgoraCloudScene/src/main/java/io/agora/online/sdk/common/AgoraEduClassActivity.kt index 4d511fc1a..f2b38e9ce 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/sdk/common/AgoraEduClassActivity.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/sdk/common/AgoraEduClassActivity.kt @@ -70,7 +70,8 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide private val streamHandler = object : StreamHandler() { override fun onStreamJoined(streamInfo: AgoraEduContextStreamInfo, operator: AgoraEduContextUserInfo?) { super.onStreamJoined(streamInfo, operator) - if (localStreamInfo == null && streamInfo.owner.userUuid == eduCore()?.eduContextPool()?.userContext()?.getLocalUserInfo()?.userUuid) { + if (localStreamInfo == null && streamInfo.owner.userUuid == eduCore()?.eduContextPool()?.userContext() + ?.getLocalUserInfo()?.userUuid) { localStreamInfo = streamInfo } } @@ -82,28 +83,13 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } } - override fun onStreamMessage(channelId: String, streamId: Int, data: ByteArray?) { - super.onStreamMessage(channelId, streamId, data) - LogX.i(TAG, "onStreamMessage channelId=$channelId, streamId=$streamId, data=${data?.contentToString()}") - - } - - override fun onStreamMessageError( - channelId: String, - streamId: Int, - error: Int, - missed: Int, - cached: Int, - ) { - super.onStreamMessageError(channelId, streamId, error, missed, cached) - LogX.i(TAG, "onStreamMessageError channelId=$channelId, streamId=$streamId, error=$error, missed=$missed, cached=$cached") - } - override fun onStreamUpdated(streamInfo: AgoraEduContextStreamInfo, operator: AgoraEduContextUserInfo?) { super.onStreamUpdated(streamInfo, operator) updateDevice(streamInfo) eduCore()?.eduContextPool()?.userContext()?.getLocalUserInfo()?.let { localUser -> - if (localUser.role == AgoraEduContextUserRole.Student && localUser.userUuid == streamInfo.owner.userUuid && operator?.role != AgoraEduContextUserRole.Student) { + if (localUser.role == AgoraEduContextUserRole.Student + && localUser.userUuid == streamInfo.owner.userUuid + && operator?.role != AgoraEduContextUserRole.Student) { if (localStreamInfo?.videoState?.value != streamInfo.videoState.value) {//如果上一次的视频状态和本次的视频状态不一样 when (streamInfo.streamType) { @@ -113,9 +99,11 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide // text = String.format(context.getString(R.string.fcr_stream_start_video)) // ) } - AgoraEduContextMediaStreamType.Audio, AgoraEduContextMediaStreamType.None -> { - AgoraUIToast.error(applicationContext, text = String.format(context.getString(R.string.fcr_switch_tips_banned_video))) + AgoraUIToast.error( + applicationContext, + text = String.format(context.getString(R.string.fcr_switch_tips_banned_video)) + ) } } } @@ -128,9 +116,11 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide // text = String.format(context.getString(R.string.fcr_stream_start_audio)) // ) } - AgoraEduContextMediaStreamType.Video, AgoraEduContextMediaStreamType.None -> { - AgoraUIToast.error(applicationContext, text = String.format(context.getString(R.string.fcr_switch_tips_muted))) + AgoraUIToast.error( + applicationContext, + text = String.format(context.getString(R.string.fcr_switch_tips_muted)) + ) } } } @@ -150,16 +140,16 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide AgoraEduContextSystemDevice.CameraBack } - FcrHandsUpManager.getDeviceState(eduCore(), device) { - if (it) { + FcrHandsUpManager.getDeviceState(eduCore(), device){ + if(it){ getEduContext()?.mediaContext()?.closeSystemDevice(device) } } } if (streamInfo.audioState == AgoraEduMediaState.Off) { - FcrHandsUpManager.getDeviceState(eduCore(), AgoraEduContextSystemDevice.Microphone) { - if (it) { + FcrHandsUpManager.getDeviceState(eduCore(), AgoraEduContextSystemDevice.Microphone){ + if(it){ getEduContext()?.mediaContext()?.closeSystemDevice(AgoraEduContextSystemDevice.Microphone) } } @@ -242,7 +232,10 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } if (connectionState == EduContextConnectionState.Aborted) { - AgoraUIToast.error(applicationContext, text = resources.getString(R.string.fcr_monitor_login_remote_device)) + AgoraUIToast.error( + applicationContext, + text = resources.getString(R.string.fcr_monitor_login_remote_device) + ) val roomUuid = eduCore()?.eduContextPool()?.roomContext()?.getRoomInfo()?.roomUuid FCRHandlerManager.roomHandlerMap.forEach { if (roomUuid == it.key) { @@ -279,11 +272,13 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(AgoraEduContextSystemDevice.Speaker) eduCore()?.eduContextPool()?.userContext()?.getLocalUserInfo()?.let { if (it.role == AgoraEduContextUserRole.Teacher) { - eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(if (AgoraUIDeviceSetting.isFrontCamera()) { - AgoraEduContextSystemDevice.CameraFront - } else { - AgoraEduContextSystemDevice.CameraBack - }) + eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice( + if (AgoraUIDeviceSetting.isFrontCamera()) { + AgoraEduContextSystemDevice.CameraFront + } else { + AgoraEduContextSystemDevice.CameraBack + } + ) eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(AgoraEduContextSystemDevice.Microphone) } else { eduCore()?.eduContextPool()?.mediaContext()?.closeSystemDevice(AgoraEduContextSystemDevice.CameraFront) @@ -295,11 +290,13 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide protected fun openSystemDevices() { // 打开语音,摄像头,麦克风 eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(AgoraEduContextSystemDevice.Speaker) - eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(if (AgoraUIDeviceSetting.isFrontCamera()) { - AgoraEduContextSystemDevice.CameraFront - } else { - AgoraEduContextSystemDevice.CameraBack - }) + eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice( + if (AgoraUIDeviceSetting.isFrontCamera()) { + AgoraEduContextSystemDevice.CameraFront + } else { + AgoraEduContextSystemDevice.CameraBack + } + ) eduCore()?.eduContextPool()?.mediaContext()?.openSystemDevice(AgoraEduContextSystemDevice.Microphone) } @@ -331,10 +328,14 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide * 设置小流分辨率 */ fun setLowStream() { - val lowStream = - String.format(Locale.US, "{\"che.video.lowBitRateStreamParameter\": {\"width\":%d,\"height\":%d,\"frameRate\":%d,\"bitRate\":%d}}", - FcrStreamParameters.LowStream.width, FcrStreamParameters.LowStream.height, FcrStreamParameters.LowStream.frameRate, - FcrStreamParameters.LowStream.bitRate) + val lowStream = String.format( + Locale.US, + "{\"che.video.lowBitRateStreamParameter\": {\"width\":%d,\"height\":%d,\"frameRate\":%d,\"bitRate\":%d}}", + FcrStreamParameters.LowStream.width, + FcrStreamParameters.LowStream.height, + FcrStreamParameters.LowStream.frameRate, + FcrStreamParameters.LowStream.bitRate + ) eduCore()?.eduContextPool()?.streamContext()?.setRtcParameters(lowStream) LogX.i(TAG, "joinRoom lowStream = $lowStream") } @@ -354,7 +355,6 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide super.finish() removeHandler() } - override fun onRelease() { super.onRelease() removeHandler() @@ -408,12 +408,11 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide override fun onRemoteUserLeft( user: AgoraEduContextUserInfo, operator: AgoraEduContextUserInfo?, - reason: EduContextUserLeftReason, + reason: EduContextUserLeftReason ) { super.onRemoteUserLeft(user, operator, reason) //FcrHandsUpManager.remove(user.userUuid) } - override fun onLocalUserKickedOut() { super.onLocalUserKickedOut() classManager?.showKickOut() @@ -429,7 +428,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide list: List, batch: FcrEventBatch, cause: Map, - operator: AgoraEduContextUserInfo?, + operator: AgoraEduContextUserInfo? ) { super.onUserRewardedList(list, batch, cause, operator) ContextCompat.getMainExecutor(context).execute { @@ -457,8 +456,11 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } } - val tips = if (list.size > 3) String.format(context.resources.getString(R.string.fcr_room_tips_reward_congratulation_multiplayer), name, - "" + list.size) else String.format(context.resources.getString(R.string.fcr_room_tips_reward_congratulation_single), name) + val tips = if (list.size > 3) String.format( + context.resources.getString(R.string.fcr_room_tips_reward_congratulation_multiplayer), + name, + "" + list.size + ) else String.format(context.resources.getString(R.string.fcr_room_tips_reward_congratulation_single), name) return tips } @@ -467,7 +469,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide override fun onUserRewarded( user: AgoraEduContextUserInfo, rewardCount: Int, - operator: AgoraEduContextUserInfo?, + operator: AgoraEduContextUserInfo? ) { super.onUserRewarded(user, rewardCount, operator) ContextCompat.getMainExecutor(context).execute { @@ -531,8 +533,13 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide releaseData() finish() // 欢迎加入{xxxx小组名}与大家互动讨论 - AgoraUIToast.info(applicationContext, - text = String.format(context.resources.getString(R.string.fcr_group_enter_welcome), current.payload.groupName)) + AgoraUIToast.info( + applicationContext, + text = String.format( + context.resources.getString(R.string.fcr_group_enter_welcome), + current.payload.groupName + ) + ) } else { // 加入失败,重新加入 isSowGroupDialog = true @@ -557,13 +564,16 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } if (!groupInfo.state && getRoomType() == RoomType.GROUPING_CLASS) { // 关闭分组,直接返回大房间 - if (!groupInfo.state) { + if(!groupInfo.state){ FcrGroupUserManager.clearGroupList() } ContextCompat.getMainExecutor(context).execute { if (!isJoinMainRoom.get()) { isJoinMainRoom.set(true) - AgoraUIToast.info(applicationContext, text = resources.getString(R.string.fcr_group_back_main_room)) + AgoraUIToast.info( + applicationContext, + text = resources.getString(R.string.fcr_group_back_main_room) + ) fullLoading.setContent(getString(R.string.fcr_group_back_main_room)) launchMainRoom() } @@ -580,7 +590,10 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide if (info.subRoomUuid == roomUuid) { LogX.i(TAG, "Group 删除分组,返回到大房间") classManager?.dismissJoinDialog() - AgoraUIToast.info(applicationContext, text = resources.getString(R.string.fcr_group_back_main_room)) + AgoraUIToast.info( + applicationContext, + text = resources.getString(R.string.fcr_group_back_main_room) + ) fullLoading.setContent(getString(R.string.fcr_group_back_main_room)) launchMainRoom() } @@ -629,15 +642,21 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide // 切换到新的分组 LogX.i(TAG, "Group 当前分组:$roomUuid , 切换到新的分组:${groupInfo.groupUuid}") - AgoraUIToast.info(applicationContext, - text = String.format(resources.getString(R.string.fcr_group_invitation), groupInfo.groupName)) + AgoraUIToast.info( + applicationContext, + text = String.format( + resources.getString(R.string.fcr_group_invitation), + groupInfo.groupName + ) + ) isJoining.set(true) showFullDialog() classManager?.launchSubRoom(groupInfo, true) { code, state, groupUuid -> if (state == AgoraEduEvent.AgoraEduEventReady) { // 关闭当前分组的channel - eduCore()?.eduContextPool()?.roomContext()?.leaveRoom(object : EduContextCallback { + eduCore()?.eduContextPool()?.roomContext() + ?.leaveRoom(object : EduContextCallback { override fun onSuccess(target: Unit?) { releaseData() finish() @@ -726,7 +745,13 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide // 切换到新的分组 LogX.i(TAG, "Group 当前分组:$roomUuid , 切换到新的分组:${toSubRoomUuid}") - AgoraUIToast.info(applicationContext, text = String.format(resources.getString(R.string.fcr_group_invitation), groupInfo.groupName)) + AgoraUIToast.info( + applicationContext, + text = String.format( + resources.getString(R.string.fcr_group_invitation), + groupInfo.groupName + ) + ) isJoining.set(true) showFullDialog() @@ -801,7 +826,7 @@ abstract class AgoraEduClassActivity : AgoraBaseClassActivity(), IAgoraUIProvide } } - open fun cancelHandsUp() {} + open fun cancelHandsUp(){} override fun getUIConfig(): FcrUIConfig { eduCore()?.config?.roomType?.let { From b89929086b09177e064286da6e8db7cb008dc6d1 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 8 Aug 2024 16:38:14 +0800 Subject: [PATCH 13/32] =?UTF-8?q?bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/FcrRttToolBoxComponent.kt | 4 +- .../dialog/AgoraUIRttSettingDialog.kt | 12 +- .../online/helper/FcrRttOptionsManager.kt | 141 +++++++++++++----- .../options/AgoraEduRttOptionsComponent.kt | 19 ++- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 8 +- .../src/main/res/values/strings.xml | 40 ++--- 6 files changed, 152 insertions(+), 72 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt index 23094d76e..422503e30 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt @@ -44,7 +44,9 @@ class FcrRttToolBoxComponent : AbsAgoraEduComponent { val widgetConfig = eduContext?.widgetContext()?.getWidgetConfig(widgetId) widgetConfig?.let { config -> widget = eduContext?.widgetContext()?.create(config) as FcrRttToolBoxWidget? - widget?.init(binding.root, agoraUIProvider, agoraEduOptionsComponent!!, conversionStatusView!!, subtitleView!!) + runOnUIThread{ + widget?.init(binding.root, agoraUIProvider, agoraEduOptionsComponent!!, conversionStatusView!!, subtitleView!!) + } } } } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt index 03342f52c..318333ba8 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt @@ -249,15 +249,15 @@ private class ContentAdapter(var context: Context) : RecyclerView.Adapter(R.id.fcr_online_rtt_setting_dialog_content_switch_title).setText( R.string.fcr_dialog_rtt_setting_dialog_title_switch) findViewById(R.id.fcr_online_rtt_setting_dialog_content_switch_icon).isActivated = - this@ContentAdapter.currentSettingInfo?.showDoubleLan ?: false + this@ContentAdapter.currentSettingInfo?.isShowDoubleLan() ?: false setOnClickListener { - if (false == this@ContentAdapter.currentSettingInfo?.showDoubleLan) { + if (false == this@ContentAdapter.currentSettingInfo?.isShowDoubleLan()) { findViewById(R.id.fcr_online_rtt_setting_dialog_content_switch_icon).isActivated = true - currentSettingInfo?.showDoubleLan = true + currentSettingInfo?.setShowDoubleLan(true) listener?.changeDoubleShow(true) } else { findViewById(R.id.fcr_online_rtt_setting_dialog_content_switch_icon).isActivated = false - currentSettingInfo?.showDoubleLan = false + currentSettingInfo?.setShowDoubleLan(false) listener?.changeDoubleShow(false) } } @@ -285,12 +285,12 @@ private class ContentAdapter(var context: Context) : RecyclerView.Adapter() @@ -107,7 +108,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { override fun onStreamMessage(channelId: String, streamId: Int, data: ByteArray?) { super.onStreamMessage(channelId, streamId, data) //不允许使用或者已到体验时间就不在回调 - if (!isAllowUseRtt()) { + if (!isAllowUseRtt() || (!conversionManager.isOpenConversion() && !subtitlesManager.isOpenSubtitles())) { return } val parseFrom = AgoraSpeech2TextProtobuffer.Text.parseFrom(data) @@ -407,6 +408,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 开启转写 */ fun openConversion() { + this.localIsChangeTranscribeState = true; conversionManager.openConversion(useManager.getRecordList()) } @@ -449,7 +451,8 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 设置转换源语言 */ fun setSourceLanguage(lan: RttLanguageEnum) { - settingsManager.currentSettingInfo.sourceLan = lan + settingsManager.currentSettingInfo.setSourceLan(lan) + this.localIsChangeSourceLan = true sendRequest(isOpenConversion(), isOpenSubtitles(), object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { useManager.setLastFinal() @@ -462,8 +465,9 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 设置转换目标语言 */ fun setTargetLanguage(lan: Array) { - settingsManager.currentSettingInfo.targetLan = lan - sendRequest(isOpenConversion() , isOpenSubtitles(), object : HttpCallback>() { + settingsManager.currentSettingInfo.setTargetLan(lan) + this.localIsChangeTarget = true + sendRequest(isOpenConversion(), isOpenSubtitles(), object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { useManager.setLastFinal() this@RttOptionsManager.getManagerListener().targetLanguageChange(lan.toList()) @@ -475,7 +479,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 硒鼓双语显示 */ fun changeDoubleShow(showDouble: Boolean) { - settingsManager.currentSettingInfo.showDoubleLan = showDouble + settingsManager.currentSettingInfo.setShowDoubleLan(showDouble) } /** @@ -484,6 +488,12 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { fun getExperienceReduceTime(): Int { return useManager.rttExperienceReduceTime } + /** + * 获取体验默认时间 + */ + fun getExperienceDefaultTime(): Int { + return useManager.rttExperienceDefaultTime + } /** * 设置语言列表 @@ -508,9 +518,12 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * @param openRtt 是否打开rtt功能 * @param openRttSubtitles 是否开启字幕 */ - internal fun sendRequest(openRttConversion: Boolean, openRttSubtitles: Boolean, callback: HttpCallback>? = null) { - val body = FcrRttChangeOptionsData.formatUseData(openRttConversion, openRttSubtitles, settingsManager.currentSettingInfo.sourceLan.value, - settingsManager.currentSettingInfo.targetLan.map { it.value }.toTypedArray()) + internal fun sendRequest( + openRttConversion: Boolean, openRttSubtitles: Boolean, + callback: HttpCallback>? = null, + ) { + val body = FcrRttChangeOptionsData.formatUseData(openRttConversion, openRttSubtitles, settingsManager.currentSettingInfo.getSourceLan().value, + settingsManager.currentSettingInfo.getTargetLan().map { it.value }.toTypedArray()) if (settingsManager.lastRecordWidgetInfo == null) { settingsManager.lastRecordWidgetInfo = body } @@ -540,6 +553,21 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { listenerList.forEach { it.conversionViewReset();it.subtitlesViewReset(false) } } + /** + * 本地是否修改了声源语言,因为未返回实际的修改字段,所以需要做首次获取的更新处理,因为首次开启关闭会触发这个更新,但是实际上声源不一定会有修改 + */ + private var localIsChangeSourceLan = false + + /** + * 本地是否修改了转写状态,原因同上 + */ + private var localIsChangeTranscribeState = false + + /** + * 本地是否修改了翻译状态,原因同上 + */ + private var localIsChangeTarget = false + /** * 房间widget属性更新 */ @@ -548,23 +576,26 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { GsonUtil.jsonToObject(GsonUtil.toJson(properties))?.let { operator?.let { userInfo -> LogX.i(TAG, "onWidgetRoomPropertiesUpdated result=${GsonUtil.toJson(properties)}__user=${GsonUtil.toJson(operator)}") + //剩余体验时间 + useManager.setExperienceReduceTime(useManager.rttExperienceDefaultTime - (it.duration * 1000)) + //用户信息 val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() //判断是否改变了转写状态 - if (checkUpdateConversion(it)) { + if (checkUpdateConversion(it) || localIsChangeTranscribeState && operator.userUuid === localUserInfo?.userUuid) { conversionManager.addStateChangeTextMessage(it.transcribe, userInfo, localUserInfo, useManager) } //判断是否开启了翻译 - if (!it.languages?.target.isNullOrEmpty() && settingsManager.lastRecordWidgetInfo?.languages?.target.isNullOrEmpty()) { - settingsManager.currentSettingInfo.targetLan = - it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() - ?: arrayOf() + if (!it.languages?.target.isNullOrEmpty() && settingsManager.lastRecordWidgetInfo?.languages?.target.isNullOrEmpty() || localIsChangeTarget && operator.userUuid === localUserInfo?.userUuid) { + settingsManager.currentSettingInfo.setTargetLan(it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() + ?: arrayOf()) + conversionManager.addTargetLanguageChangeMessage(true, userInfo, useManager) } //判断是否修改了声源语言 - if (!it.languages?.source.isNullOrEmpty() && it.languages?.source != settingsManager.lastRecordWidgetInfo?.languages?.source) { + if (!it.languages?.source.isNullOrEmpty() && it.languages?.source != settingsManager.lastRecordWidgetInfo?.languages?.source || localIsChangeSourceLan && operator.userUuid === localUserInfo?.userUuid) { val sourceEnum = RttLanguageEnum.values().find { item -> item.value == it.languages?.source } if (sourceEnum != null) { - settingsManager.currentSettingInfo.sourceLan = sourceEnum + settingsManager.currentSettingInfo.setSourceLan(sourceEnum) conversionManager.addSourceLanguageChangeMessage(userInfo, localUserInfo, useManager, settingsManager, sourceEnum) } } @@ -581,17 +612,20 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { if (properties != null) { GsonUtil.jsonToObject(GsonUtil.toJson(properties))?.let { LogX.i(TAG, "onWidgetRoomPropertiesInit result=${GsonUtil.toJson(properties)}}") + //剩余体验时间 + useManager.setExperienceReduceTime(useManager.rttExperienceDefaultTime - (it.duration * 1000)) //转写状态 conversionManager.initOpenConversion(it.transcribe == 1) //翻译 - settingsManager.currentSettingInfo.targetLan = - it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() ?: arrayOf() + settingsManager.currentSettingInfo.setTargetLan(it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() ?: arrayOf()) //声源语言 val sourceEnum = RttLanguageEnum.values().find { item -> item.value == it.languages?.source } if (sourceEnum != null) { - settingsManager.currentSettingInfo.sourceLan = sourceEnum + settingsManager.currentSettingInfo.setSourceLan(sourceEnum) } settingsManager.lastRecordWidgetInfo = it + //字幕状态 + subtitlesManager.initOpenSubtitle(it.subtitle == 1) } } } @@ -801,13 +835,13 @@ class RttRecordItem { * @param showDoubleLan 是否显示双语 */ class RttSettingInfo( - rttOptionsManager: RttOptionsManager, + var rttOptionsManager: RttOptionsManager, val sourceListLan: ArrayList = arrayListOf(RttLanguageEnum.ZH_CN, RttLanguageEnum.EN_US, RttLanguageEnum.JA_JP), val targetListLan: ArrayList = arrayListOf(RttLanguageEnum.NONE, RttLanguageEnum.ZH_CN, RttLanguageEnum.EN_US, RttLanguageEnum.JA_JP), - var sourceLan: RttLanguageEnum = RttLanguageEnum.ZH_CN, - var targetLan: Array = arrayOf(RttLanguageEnum.NONE), - var showDoubleLan: Boolean = false, + private var sourceLan: RttLanguageEnum = RttLanguageEnum.ZH_CN, + private var targetLan: Array = arrayOf(RttLanguageEnum.NONE), + private var showDoubleLan: Boolean = SpUtil.getBoolean(rttOptionsManager.rttOptions.getApplicationContext(),"${rttOptionsManager.eduCore?.config?.roomUuid ?:""}_showDoubleLan",false), ) { init { val locale: Locale = MultiLanguageUtil.getAppLocale(rttOptionsManager.rttOptions.getActivityContext()) @@ -831,6 +865,22 @@ class RttSettingInfo( RttLanguageEnum.EN_US } } + + fun getSourceLan() = sourceLan + fun getTargetLan() = targetLan + fun isShowDoubleLan() = showDoubleLan + fun setSourceLan(sourceLan: RttLanguageEnum) { + this.sourceLan = sourceLan + } + + fun setTargetLan(targetLan: Array) { + this.targetLan = if(targetLan.isNotEmpty()) targetLan else arrayOf(RttLanguageEnum.NONE) + } + + fun setShowDoubleLan(show: Boolean) { + this.showDoubleLan = show + SpUtil.saveBoolean(rttOptionsManager.rttOptions.getApplicationContext(),"${rttOptionsManager.eduCore?.config?.roomUuid ?:""}_showDoubleLan",show) + } } /** @@ -974,10 +1024,14 @@ private data class FcrRttChangeOptionsData( */ var subtitle: Int = 0, ) { + /** + * 已用的体验时间,接收使用,请求的时候不需要 + */ + var duration: Int = 0 + companion object { - fun formatUseData(openRttConversion : Boolean, openSubtitle: Boolean, sourceLan: String, targetLan: Array): FcrRttChangeOptionsData { - return FcrRttChangeOptionsData(RttChangOptionsLanguage(sourceLan, targetLan), if(openRttConversion) 1 else 0, - if (openSubtitle) 1 else 0) + fun formatUseData(openRttConversion: Boolean, openSubtitle: Boolean, sourceLan: String, targetLan: Array): FcrRttChangeOptionsData { + return FcrRttChangeOptionsData(RttChangOptionsLanguage(sourceLan, targetLan), if (openRttConversion) 1 else 0, if (openSubtitle) 1 else 0) } } } @@ -1097,6 +1151,20 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag return openSuccess } + /** + * 初始化字幕状态 + */ + fun initOpenSubtitle(state: Boolean) { + openSuccess = state + if (openSuccess) { + listener.subtitlesViewReset(true) + rttOptionsManager.startExperience() + //显示文案 + listener.audioStateOpening() + handler.sendEmptyMessage(messageWhatOpenSuccess) + } + } + /** * 设置当前翻译数据 */ @@ -1108,7 +1176,8 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag handler.removeMessages(messageWhatNoSpeaking) //是否开启双语显示 - val showTranslateOnly = currentSettingInfo.showDoubleLan + val showTranslateOnly = + currentSettingInfo.isShowDoubleLan() && currentSettingInfo.getTargetLan().isNotEmpty() && RttLanguageEnum.NONE !== currentSettingInfo.getTargetLan()[0] && currentSettingInfo.getSourceLan() !== currentSettingInfo.getTargetLan()[0] val sourceText = recordItem?.sourceText val targetText = recordItem?.targetText val translating = targetText.isNullOrEmpty() && showTranslateOnly @@ -1155,7 +1224,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana openSuccess = true listener.conversionStateChange(true) } else { - rttOptionsManager.sendRequest( true, rttOptionsManager.isOpenSubtitles(), + rttOptionsManager.sendRequest(true, rttOptionsManager.isOpenSubtitles(), callback = object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { openSuccess = true @@ -1239,6 +1308,10 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana */ fun initOpenConversion(state: Boolean) { openSuccess = state + if (openSuccess) { + listener.conversionViewReset() + rttConversionDialog.show(arrayListOf()) + } } /** @@ -1271,7 +1344,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open else R.string.fcr_dialog_rtt_toast_conversion_state_close }) }" - if(rttOptionsManager.isOpenConversion() || rttOptionsManager.isOpenSubtitles()) { + if (rttOptionsManager.isOpenConversion() || rttOptionsManager.isOpenSubtitles()) { AgoraUIToast.showDefaultToast(rttOptionsManager.rttOptions.getApplicationContext(), toastContent) } useManager.disposeDataChangeSourceLanguage(userInfo, textContent) @@ -1308,7 +1381,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana //通知处理 useManager.disposeDataChangeSourceLanguage(userInfo, showContent.toString()) updateShowList(useManager.getRecordList()) - LogX.i(TAG, "changeSourceLanguage source=${settingsManager.currentSettingInfo.sourceLan}, result=$sourceEnum}") + LogX.i(TAG, "changeSourceLanguage source=${settingsManager.currentSettingInfo.getSourceLan()}, result=$sourceEnum}") } } @@ -1326,7 +1399,7 @@ private class RttSettingManager(private val rttOptionsManager: RttOptionsManager * 修改双语显示 */ override fun changeDoubleShow(showDouble: Boolean) { - currentSettingInfo.showDoubleLan = showDouble + currentSettingInfo.setShowDoubleLan(showDouble) rttOptionsManager.changeDoubleShow(showDouble) } @@ -1486,7 +1559,7 @@ private class RttUseManager( if (lastItemByUid == null || rttMsgData.uid != lastItemByUid || lastItem.isFinal) { paramsData = RttRecordItem().apply { uuid = UUID.randomUUID().toString() - currentTargetLan = settingInfo.targetLan + currentTargetLan = settingInfo.getTargetLan() sourceLan = RttLanguageEnum.values().find { it.value == rttMsgData.culture } sourceText = sourceTextStr.toString() uid = rttMsgData.uid @@ -1498,7 +1571,7 @@ private class RttUseManager( } else { paramsData = recordList.findLast { it.uid == rttMsgData.uid }?.apply { uuid = UUID.randomUUID().toString() - currentTargetLan = settingInfo.targetLan + currentTargetLan = settingInfo.getTargetLan() sourceText = sourceTextStr.toString() time = if (final) if (rttMsgData.time == 0L) rttMsgData.time else System.currentTimeMillis() else rttMsgData.time isFinal = final @@ -1538,7 +1611,7 @@ private class RttUseManager( transTextStr.append(lanMapText[item.value]) } } - currentTargetLan = settingInfo.targetLan + currentTargetLan = settingInfo.getTargetLan() uuid = UUID.randomUUID().toString() targetInfo = tranList targetText = transTextStr.toString() diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt index 9d670b40d..8c0881be5 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt @@ -11,6 +11,7 @@ import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions import io.agora.online.R import io.agora.online.component.common.AbsAgoraEduComponent +import io.agora.online.component.common.IAgoraUIProvider import io.agora.online.databinding.FcrOnlineEduRttOptionsComponentBinding import io.agora.online.helper.RttOptionsManager import java.text.MessageFormat @@ -32,7 +33,8 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { } } - fun initView(rttOptionsManager: RttOptionsManager) { + fun initView(rttOptionsManager: RttOptionsManager,agoraUIProvider: IAgoraUIProvider) { + super.initView(agoraUIProvider) this.rttOptionsManager = rttOptionsManager binding.agoraFcrRttTextDialogClose.setOnClickListener { rttOptionsManager.closeSubtitles() @@ -51,12 +53,14 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { } MotionEvent.ACTION_MOVE -> { - move = true - val params = layoutParams as MarginLayoutParams - params.leftMargin = Math.max(0, Math.min(params.leftMargin + (event.x - touchX).toInt(), (parent as View).width - width)) - params.bottomMargin = Math.max(0, Math.min(params.bottomMargin - (event.y - touchY).toInt(), (parent as View).height - height)) - setLayoutParams(params) - return true + if(Math.abs(event.x - touchX) > 30 || Math.abs(event.y - touchY) > 30) { + move = true + val params = layoutParams as MarginLayoutParams + params.leftMargin = Math.max(0, Math.min(params.leftMargin + (event.x - touchX).toInt(), (parent as View).width - width)) + params.bottomMargin = Math.max(0, Math.min(params.bottomMargin - (event.y - touchY).toInt(), (parent as View).height - height)) + setLayoutParams(params) + return true + } } MotionEvent.ACTION_UP -> { @@ -98,7 +102,6 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { */ override fun setVisibility(visibility: Int) { runOnUIThread { - resetShowPosition() super.setVisibility(visibility) } } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index 82e8f92ec..8c9efc15f 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -69,7 +69,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { internal inner class AgoraRttToolBoxWidgetContent( val container: ViewGroup, agoraUIProvider: IAgoraUIProvider, - agoraEduOptionsComponent: AgoraEduOptionsComponent?, conversionStatusView: ViewGroup?, subtitleView: AgoraEduRttOptionsComponent?, + agoraEduOptionsComponent: AgoraEduOptionsComponent?, conversionStatusView: ViewGroup?,val subtitleView: AgoraEduRttOptionsComponent?, ) : IRttOptions { private val listener = object : FcrRttOptionsStatusListener() { override fun conversionViewReset() { @@ -109,6 +109,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun audioStateNotAllowUse() { super.audioStateNotAllowUse() + subtitleView?.resetShowPosition() subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, text = container.context.getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) } @@ -126,6 +127,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun audioStateOpening() { super.audioStateOpening() + subtitleView?.resetShowPosition() subtitleView?.setShowStatusInfo(showProgress = true, showIcon = false, text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening)) } @@ -154,7 +156,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { */ private val rttOptionsManager: RttOptionsManager by lazy { RttOptionsManager(this).also { - subtitleView?.initView(agoraUIProvider) + subtitleView?.initView(it,agoraUIProvider) it.initView(agoraUIProvider) it.addListener(listener) } @@ -197,7 +199,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { - MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), experienceReduceTime / 60000) + MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), rttOptionsManager.getExperienceDefaultTime() / 60000) } else { container.context.getString(R.string.fcr_dialog_rtt_time_limit_end) } diff --git a/AgoraCloudScene/src/main/res/values/strings.xml b/AgoraCloudScene/src/main/res/values/strings.xml index 3128ce350..81808df5d 100644 --- a/AgoraCloudScene/src/main/res/values/strings.xml +++ b/AgoraCloudScene/src/main/res/values/strings.xml @@ -614,24 +614,24 @@ Delete your account will result in ….\n 开启了实时转写服务。 停止了实时转写服务,全体用户可见 不翻译 - zh-HK - zh-CN - zh-TW - en-IN - en-US - fr-FR - de-DE - th-TH - hi-IN - id-ID - it-IT - ja-JP - ko-KR - ms-MY* - fa-IR* - pt-PT - ru-RU - es-ES - tr-TR - vi-VN + 中文(繁体) + 简体中文 + 中文(繁体) + 英语(印第安语) + 英语 + 法语 + 德语 + 泰语 + 印度语 + 印尼语 + 意大利语 + 日语 + 韩语 + 马来西亚语 + 波斯语 + 葡萄牙语 + 俄语 + 西班牙语 + 土耳其语 + 越南语 From ffb575b5cb95eda391341d45b8c7a3817d8a4f7e Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 8 Aug 2024 16:51:02 +0800 Subject: [PATCH 14/32] =?UTF-8?q?=E5=B4=A9=E6=BA=83=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agora/online/helper/FcrRttOptionsManager.kt | 2 +- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index c447a1a91..20ccf20f0 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -408,7 +408,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * 开启转写 */ fun openConversion() { - this.localIsChangeTranscribeState = true; + this.localIsChangeTranscribeState = true conversionManager.openConversion(useManager.getRecordList()) } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index 8c9efc15f..c9e37ebeb 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -81,6 +81,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun subtitlesViewReset(openSuccess: Boolean) { super.subtitlesViewReset(openSuccess) + subtitleView?.resetShowPosition() agoraEduOptionsComponent?.hiddenRtt() if (openSuccess) { subtitleView?.visibility = View.VISIBLE @@ -195,13 +196,15 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { * 重置显示状态 */ fun resetStatus() { - val experienceReduceTime = rttOptionsManager.getExperienceReduceTime() - binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() - binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() - binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { - MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), rttOptionsManager.getExperienceDefaultTime() / 60000) - } else { - container.context.getString(R.string.fcr_dialog_rtt_time_limit_end) + runOnUiThread{ + val experienceReduceTime = rttOptionsManager.getExperienceReduceTime() + binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() + binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() + binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { + MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), rttOptionsManager.getExperienceDefaultTime() / 60000) + } else { + container.context.getString(R.string.fcr_dialog_rtt_time_limit_end) + } } } From 20929e35c2a5df2778e715e564079ba4a394d365 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 8 Aug 2024 16:55:45 +0800 Subject: [PATCH 15/32] =?UTF-8?q?=E6=98=BE=E7=A4=BAUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/agora/online/options/AgoraEduRttOptionsComponent.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt index 8c0881be5..62b274329 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt @@ -33,7 +33,7 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { } } - fun initView(rttOptionsManager: RttOptionsManager,agoraUIProvider: IAgoraUIProvider) { + fun initView(rttOptionsManager: RttOptionsManager, agoraUIProvider: IAgoraUIProvider) { super.initView(agoraUIProvider) this.rttOptionsManager = rttOptionsManager binding.agoraFcrRttTextDialogClose.setOnClickListener { @@ -53,7 +53,7 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { } MotionEvent.ACTION_MOVE -> { - if(Math.abs(event.x - touchX) > 30 || Math.abs(event.y - touchY) > 30) { + if (Math.abs(event.x - touchX) > 30 || Math.abs(event.y - touchY) > 30) { move = true val params = layoutParams as MarginLayoutParams params.leftMargin = Math.max(0, Math.min(params.leftMargin + (event.x - touchX).toInt(), (parent as View).width - width)) @@ -77,6 +77,7 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { */ fun resetShowPosition() { runOnUIThread { + visibility = View.INVISIBLE if (width == 0) { viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { override fun onGlobalLayout() { @@ -93,6 +94,7 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { params.bottomToBottom = (parent as View).id } setLayoutParams(params) + visibility = View.VISIBLE } } } From 8b98c0077e0378bd550bd0d57c35349da82d7d41 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 8 Aug 2024 19:27:56 +0800 Subject: [PATCH 16/32] =?UTF-8?q?=E6=98=BE=E7=A4=BAUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../online/helper/FcrRttOptionsManager.kt | 33 ++++++++++++++++--- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 5 +++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 20ccf20f0..1aea5b9c6 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -1061,6 +1061,11 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag */ private val messageWhatListening = 2 + /** + * 隐藏rtt的消息类型 + */ + private val messageWhatHidde = 3 + /** * 所有的定时相关的处理 */ @@ -1083,6 +1088,11 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag messageWhatNoSpeaking -> { listener.audioStateNoSpeakingMoreTime() } + + messageWhatHidde -> { + listener.subtitlesViewReset(false) + listener.subtitlesStateChange(false) + } } } } @@ -1094,6 +1104,8 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag * @param experienceReduceTime 体验剩余时间 */ fun openSubtitles(configAllowUse: Boolean, experienceDefaultTime: Int, experienceReduceTime: Int) { + //清除消息 + this.clearHandlerMsg() listener.subtitlesViewReset(true) if (rttOptionsManager.isAllowUseRtt()) { //可以使用的话先隐藏体验,后续再根据条件判断是否显示 @@ -1119,7 +1131,7 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag listener.experienceInfoChange(configAllowUse, experienceDefaultTime, experienceReduceTime) listener.audioStateNotAllowUse() openSuccess = false - listener.subtitlesStateChange(false) + handler.sendEmptyMessageDelayed(messageWhatHidde, 2000) } } @@ -1127,6 +1139,8 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag * 关闭字幕 */ fun closeSubtitles() { + //清除消息 + this.clearHandlerMsg() openSuccess = false listener.subtitlesViewReset(false) //发起开启rtt请求 @@ -1171,9 +1185,8 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag fun setShowCurrentData(recordList: List, recordItem: RttRecordItem?, currentSettingInfo: RttSettingInfo) { if (isOpenSubtitles()) { listener.subtitlesViewReset(true) - handler.removeMessages(messageWhatOpenSuccess) - handler.removeMessages(messageWhatListening) - handler.removeMessages(messageWhatNoSpeaking) + //清除消息 + this.clearHandlerMsg() //是否开启双语显示 val showTranslateOnly = @@ -1195,6 +1208,16 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag handler.sendEmptyMessageDelayed(messageWhatNoSpeaking, 3000) } } + + /** + * 清除定时handler消息 + */ + private fun clearHandlerMsg(){ + handler.removeMessages(messageWhatOpenSuccess) + handler.removeMessages(messageWhatListening) + handler.removeMessages(messageWhatNoSpeaking) + handler.removeMessages(messageWhatHidde) + } } /** @@ -1478,6 +1501,7 @@ private class RttUseManager( rttExperienceReduceTime -= 1000 listener.experienceInfoChange(configAllowUse, rttExperienceDefaultTime, rttExperienceReduceTime) if (rttExperienceReduceTime <= 0) { + listener.audioStateNotAllowUse() stopExperience() } else { sendEmptyMessageDelayed(msg.what, 1000) @@ -1519,7 +1543,6 @@ private class RttUseManager( * 结束体验 */ fun stopExperience() { - listener.audioStateNotAllowUse() handler.removeMessages(0) } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index c9e37ebeb..5b103dba2 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -111,12 +111,14 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun audioStateNotAllowUse() { super.audioStateNotAllowUse() subtitleView?.resetShowPosition() + subtitleView?.visibility = View.VISIBLE subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, text = container.context.getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) } override fun audioStateNoSpeaking() { super.audioStateNoSpeaking() + subtitleView?.visibility = View.VISIBLE subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) } @@ -129,6 +131,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun audioStateOpening() { super.audioStateOpening() subtitleView?.resetShowPosition() + subtitleView?.visibility = View.VISIBLE subtitleView?.setShowStatusInfo(showProgress = true, showIcon = false, text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening)) } @@ -141,12 +144,14 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun audioStateSpeaking() { super.audioStateSpeaking() + subtitleView?.visibility = View.VISIBLE subtitleView?.setShowStatusInfo(showProgress = false, showIcon = true, text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_listening)) } override fun onMessageChange(recordList: List, currentData: RttRecordItem?) { super.onMessageChange(recordList, currentData) + subtitleView?.visibility = View.VISIBLE subtitleView?.setShowTranslatorsInfo(currentData?.userHeader ?: "", currentData?.userName ?: "", currentData?.sourceText ?: "", currentData?.targetText) } From dbdc9e5830193cd6cb67d441d19503692697266d Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 8 Aug 2024 19:34:14 +0800 Subject: [PATCH 17/32] =?UTF-8?q?=E5=8F=8C=E8=AF=AD=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/agora/online/helper/FcrRttOptionsManager.kt | 7 +++++++ .../agora/online/options/AgoraEduRttOptionsComponent.kt | 9 +++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 1aea5b9c6..26d95f5fb 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -354,6 +354,13 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { return subtitlesManager.isOpenSubtitles() } + /** + * 是否显示双语 + */ + fun isShowDoubleLan():Boolean{ + return settingsManager.currentSettingInfo.isShowDoubleLan() + } + /** * 是否开启了转写 */ diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt index 62b274329..b2d9eff1f 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduRttOptionsComponent.kt @@ -165,9 +165,14 @@ class AgoraEduRttOptionsComponent : AbsAgoraEduComponent { .apply(RequestOptions.circleCropTransform()).into(binding.agoraFcrRttTextDialogUserHeader) binding.agoraFcrRttTextDialogUserHeaderText.text = if (headImage.isEmpty()) "" else headImage.substring(0, 1) binding.agoraFcrRttTextDialogUserName.text = name - binding.agoraFcrRttTextDialogTextOrigin.text = originText + //语言显示 + val showDouble = rttOptionsManager?.isShowDoubleLan() ?: false + val leve2Text = if(showDouble) resultText else null + val leve1Text = if(!showDouble) resultText else originText; + + binding.agoraFcrRttTextDialogTextOrigin.text = leve1Text binding.agoraFcrRttTextDialogTextResult.text = resultText - binding.agoraFcrRttTextDialogTextResult.visibility = if (resultText.isNullOrEmpty()) View.GONE else View.VISIBLE + binding.agoraFcrRttTextDialogTextResult.visibility = if (leve2Text.isNullOrEmpty()) View.GONE else View.VISIBLE } } From 30d8293934165ac857a13b6154fadba3ba031c12 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 8 Aug 2024 19:52:21 +0800 Subject: [PATCH 18/32] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dialog/AgoraUIRttSettingDialog.kt | 6 ++-- .../options/AgoraEduRttOptionsComponent.kt | 4 +-- .../fcr_online_edu_rtt_options_component.xml | 28 +++++++++++-------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt index 318333ba8..49c0c3027 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt @@ -126,7 +126,7 @@ private class SelectListAdapter(var context: Context, var dataList: MutableList< } else { currentSelect } - lastSelect?.select = false + dataList.find { it.code == lastSelect!!.code }?.select = false item.select = true currentSelect = item onSelectChangedListener?.onChanged(currentSelect!!) @@ -292,8 +292,8 @@ private class ContentAdapter(var context: Context) : RecyclerView.Adapter - + android:padding="4dp"> + + + From 6d8258c4a7f8154e664b524d00ac43f46615a177 Mon Sep 17 00:00:00 2001 From: wangliang Date: Fri, 9 Aug 2024 19:01:37 +0800 Subject: [PATCH 19/32] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../online/helper/FcrRttOptionsManager.kt | 294 ++++++++++-------- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 17 +- 2 files changed, 179 insertions(+), 132 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 26d95f5fb..93d7072fb 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -113,8 +113,8 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } val parseFrom = AgoraSpeech2TextProtobuffer.Text.parseFrom(data) val recordItem = useManager.disposeData(parseFrom, settingsManager.currentSettingInfo, eduCore?.eduContextPool()?.streamContext()) - subtitlesManager.setShowCurrentData(useManager.getRecordList(), recordItem, settingsManager.currentSettingInfo) - conversionManager.updateShowList(useManager.getRecordList()) + subtitlesManager.setShowCurrentData(recordItem, settingsManager.currentSettingInfo) + conversionManager.updateShowList(useManager.getShowRecordList()) LogX.i(TAG, "onStreamMessage channelId=$channelId, streamId=$streamId, data=${GsonUtil.toJson(parseFrom)}") } @@ -248,12 +248,10 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { * @param toOpen 开启-true,关闭-false */ override fun conversionStateChange(toOpen: Boolean) { - listenerList.forEach { it.conversionStateChange(toOpen) } - val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() - if (localUserInfo != null) { - conversionManager.addStateChangeTextMessage(if (toOpen) 1 else 0, EduBaseUserInfo(localUserInfo.userUuid, localUserInfo.userName, - EduUserRole.values().find { it.value == localUserInfo.role.value }!!), localUserInfo, useManager) + if (conversionManager.isOpenConversion() == toOpen) { + return } + listenerList.forEach { it.conversionStateChange(toOpen) } } /** @@ -268,11 +266,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ override fun sourceLanguageChange(language: RttLanguageEnum) { listenerList.forEach { it.sourceLanguageChange(language) } - val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() - if (localUserInfo != null) { - conversionManager.addSourceLanguageChangeMessage(EduBaseUserInfo(localUserInfo.userUuid, localUserInfo.userName, - EduUserRole.values().find { it.value == localUserInfo.role.value }!!), localUserInfo, useManager, settingsManager, language) - } } /** @@ -280,12 +273,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ override fun targetLanguageChange(languages: List) { listenerList.forEach { it.targetLanguageChange(languages) } - val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() - if (localUserInfo != null) { - conversionManager.addTargetLanguageChangeMessage(!languages.contains(RttLanguageEnum.NONE), - EduBaseUserInfo(localUserInfo.userUuid, localUserInfo.userName, - EduUserRole.values().find { it.value == localUserInfo.role.value }!!), useManager) - } } /** @@ -306,11 +293,10 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { /** * 消息改变 - * @param recordList 消息记录数据 * @param currentData 当前要显示的数据 */ - override fun onMessageChange(recordList: List, currentData: RttRecordItem?) { - listenerList.forEach { it.onMessageChange(recordList, currentData) } + override fun onMessageChange(currentData: RttRecordItem?) { + listenerList.forEach { it.onMessageChange(currentData) } } } } @@ -357,7 +343,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { /** * 是否显示双语 */ - fun isShowDoubleLan():Boolean{ + fun isShowDoubleLan(): Boolean { return settingsManager.currentSettingInfo.isShowDoubleLan() } @@ -416,7 +402,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ fun openConversion() { this.localIsChangeTranscribeState = true - conversionManager.openConversion(useManager.getRecordList()) + conversionManager.openConversion(useManager.getShowRecordList()) } /** @@ -495,6 +481,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { fun getExperienceReduceTime(): Int { return useManager.rttExperienceReduceTime } + /** * 获取体验默认时间 */ @@ -520,6 +507,9 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { this.settingsManager.currentSettingInfo.sourceListLan.distinct() } + //是否正在发起请求 + private var isSendRequesting = false + /** * 发起请求 * @param openRtt 是否打开rtt功能 @@ -529,11 +519,12 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { openRttConversion: Boolean, openRttSubtitles: Boolean, callback: HttpCallback>? = null, ) { + if (isSendRequesting) { + return + } + isSendRequesting = true val body = FcrRttChangeOptionsData.formatUseData(openRttConversion, openRttSubtitles, settingsManager.currentSettingInfo.getSourceLan().value, settingsManager.currentSettingInfo.getTargetLan().map { it.value }.toTypedArray()) - if (settingsManager.lastRecordWidgetInfo == null) { - settingsManager.lastRecordWidgetInfo = body - } val call = AppRetrofitManager.instance().getService(IRttOptionsService::class.java) .buildTokens(eduCore?.config?.appId, eduCore?.config?.roomUuid, if (openRttConversion || openRttSubtitles) 1 else 0, body) AppRetrofitManager.exc(call, object : HttpCallback>() { @@ -550,6 +541,11 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } callback?.onError(httpCode, code, message) } + + override fun onComplete() { + super.onComplete() + isSendRequesting = false + } }) } @@ -589,17 +585,20 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() //判断是否改变了转写状态 if (checkUpdateConversion(it) || localIsChangeTranscribeState && operator.userUuid === localUserInfo?.userUuid) { + this.localIsChangeTranscribeState = false conversionManager.addStateChangeTextMessage(it.transcribe, userInfo, localUserInfo, useManager) } - //判断是否开启了翻译 - if (!it.languages?.target.isNullOrEmpty() && settingsManager.lastRecordWidgetInfo?.languages?.target.isNullOrEmpty() || localIsChangeTarget && operator.userUuid === localUserInfo?.userUuid) { - settingsManager.currentSettingInfo.setTargetLan(it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() - ?: arrayOf()) - + //判断是否开启了翻译(仅当自己生效) + if (localIsChangeTarget && operator.userUuid === localUserInfo?.userUuid && !it.languages?.target.isNullOrEmpty() && this.localIsChangeTarget) { + this.localIsChangeTarget = false + settingsManager.currentSettingInfo.setTargetLan( + it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() + ?: arrayOf()) conversionManager.addTargetLanguageChangeMessage(true, userInfo, useManager) } //判断是否修改了声源语言 - if (!it.languages?.source.isNullOrEmpty() && it.languages?.source != settingsManager.lastRecordWidgetInfo?.languages?.source || localIsChangeSourceLan && operator.userUuid === localUserInfo?.userUuid) { + if (!it.languages?.source.isNullOrEmpty() && it.languages?.source != settingsManager.currentSettingInfo.getSourceLan().value || localIsChangeSourceLan && operator.userUuid === localUserInfo?.userUuid) { + this.localIsChangeSourceLan = false val sourceEnum = RttLanguageEnum.values().find { item -> item.value == it.languages?.source } if (sourceEnum != null) { settingsManager.currentSettingInfo.setSourceLan(sourceEnum) @@ -607,7 +606,6 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } } } - settingsManager.lastRecordWidgetInfo = it } } } @@ -621,18 +619,11 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { LogX.i(TAG, "onWidgetRoomPropertiesInit result=${GsonUtil.toJson(properties)}}") //剩余体验时间 useManager.setExperienceReduceTime(useManager.rttExperienceDefaultTime - (it.duration * 1000)) - //转写状态 - conversionManager.initOpenConversion(it.transcribe == 1) - //翻译 - settingsManager.currentSettingInfo.setTargetLan(it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() ?: arrayOf()) //声源语言 val sourceEnum = RttLanguageEnum.values().find { item -> item.value == it.languages?.source } if (sourceEnum != null) { settingsManager.currentSettingInfo.setSourceLan(sourceEnum) } - settingsManager.lastRecordWidgetInfo = it - //字幕状态 - subtitlesManager.initOpenSubtitle(it.subtitle == 1) } } } @@ -642,11 +633,11 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { */ private fun checkUpdateConversion(it: FcrRttChangeOptionsData): Boolean { if (1 == it.transcribe) { - if (settingsManager.lastRecordWidgetInfo == null || 0 == settingsManager.lastRecordWidgetInfo?.transcribe) { + if (!conversionManager.isOpenConversion()) { return true } } else if (0 == it.transcribe) { - if (settingsManager.lastRecordWidgetInfo == null || 1 == settingsManager.lastRecordWidgetInfo?.transcribe) { + if (conversionManager.isOpenConversion()) { return true } } @@ -776,6 +767,11 @@ class RttRecordItem { */ var uuid: String? = null + /** + * 是否显示双语 + */ + var showDoubleLan: Boolean = false + /** * 语言信息 */ @@ -848,7 +844,8 @@ class RttSettingInfo( RttLanguageEnum.JA_JP), private var sourceLan: RttLanguageEnum = RttLanguageEnum.ZH_CN, private var targetLan: Array = arrayOf(RttLanguageEnum.NONE), - private var showDoubleLan: Boolean = SpUtil.getBoolean(rttOptionsManager.rttOptions.getApplicationContext(),"${rttOptionsManager.eduCore?.config?.roomUuid ?:""}_showDoubleLan",false), + private var showDoubleLan: Boolean = SpUtil.getBoolean(rttOptionsManager.rttOptions.getApplicationContext(), + "${rttOptionsManager.eduCore?.config?.roomUuid ?: ""}_showDoubleLan", false), ) { init { val locale: Locale = MultiLanguageUtil.getAppLocale(rttOptionsManager.rttOptions.getActivityContext()) @@ -881,12 +878,13 @@ class RttSettingInfo( } fun setTargetLan(targetLan: Array) { - this.targetLan = if(targetLan.isNotEmpty()) targetLan else arrayOf(RttLanguageEnum.NONE) + this.targetLan = if (targetLan.isNotEmpty()) targetLan else arrayOf(RttLanguageEnum.NONE) } fun setShowDoubleLan(show: Boolean) { this.showDoubleLan = show - SpUtil.saveBoolean(rttOptionsManager.rttOptions.getApplicationContext(),"${rttOptionsManager.eduCore?.config?.roomUuid ?:""}_showDoubleLan",show) + SpUtil.saveBoolean(rttOptionsManager.rttOptions.getApplicationContext(), "${rttOptionsManager.eduCore?.config?.roomUuid ?: ""}_showDoubleLan", + show) } } @@ -1119,21 +1117,25 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag listener.experienceInfoChange(configAllowUse, experienceDefaultTime, experienceReduceTime) //显示文案 listener.audioStateOpening() - //发起开启rtt请求 - rttOptionsManager.sendRequest(rttOptionsManager.isOpenConversion(), openRttSubtitles = true, - callback = object : HttpCallback>() { - override fun onSuccess(result: HttpBaseRes?) { - //延迟两秒开启文案:点击字幕位置可以更改字幕设置 - listener.audioStateShowSettingHint() - rttOptionsManager.startExperience() - handler.sendEmptyMessageDelayed(messageWhatOpenSuccess, 2000) - } + if (openSuccess) { + listener.audioStateNoSpeakingMoreTime() + } else { + //发起开启rtt请求 + rttOptionsManager.sendRequest(rttOptionsManager.isOpenConversion(), openRttSubtitles = true, + callback = object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + //延迟两秒开启文案:点击字幕位置可以更改字幕设置 + listener.audioStateShowSettingHint() + rttOptionsManager.startExperience() + handler.sendEmptyMessageDelayed(messageWhatOpenSuccess, 2000) + } - override fun onError(httpCode: Int, code: Int, message: String?) { - openSuccess = false - listener.subtitlesStateChange(false) - } - }) + override fun onError(httpCode: Int, code: Int, message: String?) { + openSuccess = false + listener.subtitlesStateChange(false) + } + }) + } } else { listener.experienceInfoChange(configAllowUse, experienceDefaultTime, experienceReduceTime) listener.audioStateNotAllowUse() @@ -1148,21 +1150,23 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag fun closeSubtitles() { //清除消息 this.clearHandlerMsg() - openSuccess = false - listener.subtitlesViewReset(false) //发起开启rtt请求 - rttOptionsManager.sendRequest(rttOptionsManager.isOpenConversion(), false, object : HttpCallback>() { - override fun onSuccess(result: HttpBaseRes?) { - openSuccess = false - listener.subtitlesStateChange(false) - if (!rttOptionsManager.isOpenConversion()) { - rttOptionsManager.stopExperience() + if (openSuccess) { + rttOptionsManager.sendRequest(rttOptionsManager.isOpenConversion(), false, object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + openSuccess = false + listener.subtitlesStateChange(false) + if (!rttOptionsManager.isOpenConversion()) { + rttOptionsManager.stopExperience() + } } - } - override fun onError(httpCode: Int, code: Int, message: String?) { - } - }) + override fun onError(httpCode: Int, code: Int, message: String?) { + } + }) + } + openSuccess = false + listener.subtitlesViewReset(false) } /** @@ -1189,15 +1193,15 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag /** * 设置当前翻译数据 */ - fun setShowCurrentData(recordList: List, recordItem: RttRecordItem?, currentSettingInfo: RttSettingInfo) { + fun setShowCurrentData(recordItem: RttRecordItem?, currentSettingInfo: RttSettingInfo) { if (isOpenSubtitles()) { listener.subtitlesViewReset(true) //清除消息 this.clearHandlerMsg() //是否开启双语显示 - val showTranslateOnly = - currentSettingInfo.isShowDoubleLan() && currentSettingInfo.getTargetLan().isNotEmpty() && RttLanguageEnum.NONE !== currentSettingInfo.getTargetLan()[0] && currentSettingInfo.getSourceLan() !== currentSettingInfo.getTargetLan()[0] + val showTranslateOnly = currentSettingInfo.isShowDoubleLan() && currentSettingInfo.getTargetLan() + .isNotEmpty() && RttLanguageEnum.NONE !== currentSettingInfo.getTargetLan()[0] && currentSettingInfo.getSourceLan() !== currentSettingInfo.getTargetLan()[0] val sourceText = recordItem?.sourceText val targetText = recordItem?.targetText val translating = targetText.isNullOrEmpty() && showTranslateOnly @@ -1207,7 +1211,7 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag } else if (translating) { listener.audioStateSpeaking() } else { - listener.onMessageChange(recordList, recordItem) + listener.onMessageChange(recordItem) // rttBottomCenterSubtitlesView?.setShowTranslatorsInfo(recordItem?.userHeader ?: "", recordItem?.userName ?: "", sourceText ?: "", // targetText) } @@ -1218,8 +1222,8 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag /** * 清除定时handler消息 - */ - private fun clearHandlerMsg(){ + */ + private fun clearHandlerMsg() { handler.removeMessages(messageWhatOpenSuccess) handler.removeMessages(messageWhatListening) handler.removeMessages(messageWhatNoSpeaking) @@ -1254,19 +1258,23 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana openSuccess = true listener.conversionStateChange(true) } else { - rttOptionsManager.sendRequest(true, rttOptionsManager.isOpenSubtitles(), - callback = object : HttpCallback>() { - override fun onSuccess(result: HttpBaseRes?) { - openSuccess = true - listener.conversionStateChange(true) - rttOptionsManager.startExperience() - } - - override fun onError(httpCode: Int, code: Int, message: String?) { - openSuccess = false - listener.conversionStateChange(false) - } - }) + if (!openSuccess) { + rttOptionsManager.sendRequest(true, rttOptionsManager.isOpenSubtitles(), + callback = object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + openSuccess = true + listener.conversionStateChange(true) + rttOptionsManager.startExperience() + } + + override fun onError(httpCode: Int, code: Int, message: String?) { + openSuccess = false + listener.conversionStateChange(false) + } + }) + } else { + listener.conversionStateChange(true) + } } } @@ -1276,17 +1284,21 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana */ override fun closeConversion() { if (!rttOptionsManager.isOpenSubtitles()) { - rttOptionsManager.sendRequest(false, rttOptionsManager.isOpenSubtitles(), - callback = object : HttpCallback>() { - override fun onSuccess(result: HttpBaseRes?) { - openSuccess = false - listener.conversionStateChange(false) - rttOptionsManager.stopExperience() - } - - override fun onError(httpCode: Int, code: Int, message: String?) { - } - }) + if (openSuccess) { + rttOptionsManager.sendRequest(false, rttOptionsManager.isOpenSubtitles(), + callback = object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + openSuccess = false + listener.conversionStateChange(false) + rttOptionsManager.stopExperience() + } + + override fun onError(httpCode: Int, code: Int, message: String?) { + } + }) + } else { + listener.conversionStateChange(false) + } } else { openSuccess = false listener.conversionStateChange(false) @@ -1378,7 +1390,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana AgoraUIToast.showDefaultToast(rttOptionsManager.rttOptions.getApplicationContext(), toastContent) } useManager.disposeDataChangeSourceLanguage(userInfo, textContent) - rttOptionsManager.rttOptions.runOnUiThread { updateShowList(useManager.getRecordList()) } + rttOptionsManager.rttOptions.runOnUiThread { updateShowList(useManager.getShowRecordList()) } LogX.i(TAG, "changeConversionState result=$textContent}") } @@ -1388,7 +1400,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana fun addTargetLanguageChangeMessage(open: Boolean, userInfo: EduBaseUserInfo, useManager: RttUseManager) { val showContent = rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_open_target_language) useManager.disposeDataChangeSourceLanguage(userInfo, showContent) - rttOptionsManager.rttOptions.runOnUiThread { updateShowList(useManager.getRecordList()) } + rttOptionsManager.rttOptions.runOnUiThread { updateShowList(useManager.getShowRecordList()) } LogX.i(TAG, "changeToOpenTargetLanguage result=$showContent}") } @@ -1410,7 +1422,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana AgoraUIToast.showDefaultToast(rttOptionsManager.rttOptions.getApplicationContext(), showContent) //通知处理 useManager.disposeDataChangeSourceLanguage(userInfo, showContent.toString()) - updateShowList(useManager.getRecordList()) + updateShowList(useManager.getShowRecordList()) LogX.i(TAG, "changeSourceLanguage source=${settingsManager.currentSettingInfo.getSourceLan()}, result=$sourceEnum}") } @@ -1458,11 +1470,6 @@ private class RttSettingManager(private val rttOptionsManager: RttOptionsManager */ val currentSettingInfo by lazy { RttSettingInfo(rttOptionsManager) } - /** - * 记录的上一次房间更新信息 - */ - var lastRecordWidgetInfo: FcrRttChangeOptionsData? = null - /** * 开启设置 */ @@ -1517,9 +1524,19 @@ private class RttUseManager( } /** - * 记录数据列表 + * 总记录数据列表 + */ + private val allRecordList = arrayListOf() + + /** + * 转写显示的记录列表 */ - private val recordList = arrayListOf() + private val showRecordList = arrayListOf() + + /** + * 最后一条信息 + */ + private var lastRecord: RttRecordItem? = null /** * 是否允许体验 @@ -1561,8 +1578,9 @@ private class RttUseManager( uuid = UUID.randomUUID().toString() userName = userInfo.userName statusText = text - if (recordList.isEmpty() || recordList.isNotEmpty() && text != recordList[recordList.size - 1].statusText) { - recordList.add(this) + if (allRecordList.isEmpty() || allRecordList.isNotEmpty() && text != allRecordList[allRecordList.size - 1].statusText) { + allRecordList.add(this) + showRecordList.add(this) } } } @@ -1571,7 +1589,7 @@ private class RttUseManager( * 处理数据 */ fun disposeData(rttMsgData: AgoraSpeech2TextProtobuffer.Text, settingInfo: RttSettingInfo, streamContext: StreamContext?): RttRecordItem? { - val lastItem = (if (recordList.isEmpty()) null else recordList[recordList.size - 1]) + val lastItem = (if (allRecordList.isEmpty()) null else allRecordList[allRecordList.size - 1]) val lastItemByUid = lastItem?.uid var paramsData: RttRecordItem? = null when (rttMsgData.dataType) { @@ -1590,6 +1608,7 @@ private class RttUseManager( paramsData = RttRecordItem().apply { uuid = UUID.randomUUID().toString() currentTargetLan = settingInfo.getTargetLan() + showDoubleLan = settingInfo.isShowDoubleLan() sourceLan = RttLanguageEnum.values().find { it.value == rttMsgData.culture } sourceText = sourceTextStr.toString() uid = rttMsgData.uid @@ -1597,11 +1616,12 @@ private class RttUseManager( isFinal = final sourceConfidence = confidence } - recordList.add(paramsData) + allRecordList.add(paramsData) } else { - paramsData = recordList.findLast { it.uid == rttMsgData.uid }?.apply { + paramsData = allRecordList.findLast { it.uid == rttMsgData.uid }?.apply { uuid = UUID.randomUUID().toString() currentTargetLan = settingInfo.getTargetLan() + showDoubleLan = settingInfo.isShowDoubleLan() sourceText = sourceTextStr.toString() time = if (final) if (rttMsgData.time == 0L) rttMsgData.time else System.currentTimeMillis() else rttMsgData.time isFinal = final @@ -1630,7 +1650,7 @@ private class RttUseManager( transTextStr.setLength(0) } - paramsData = recordList.findLast { it.uid == lastItemByUid }?.apply { + paramsData = allRecordList.findLast { it.uid == lastItemByUid }?.apply { //处理拼接数据,当然,当前只有一个目标语言,为了扩展使用以下方式 transTextStr.setLength(0) currentTargetLan?.forEach { item -> @@ -1643,22 +1663,45 @@ private class RttUseManager( } currentTargetLan = settingInfo.getTargetLan() uuid = UUID.randomUUID().toString() + showDoubleLan = settingInfo.isShowDoubleLan() targetInfo = tranList targetText = transTextStr.toString() transTextStr.setLength(0) } } } + lastRecord = paramsData + if (lastRecord != null && rttOptionsManager.isOpenConversion() && allRecordList.isNotEmpty()) { + val showItem = if(showRecordList.isNotEmpty()) showRecordList[showRecordList.size - 1] else null + if (showItem?.uuid != null && showItem.uuid == lastRecord!!.uuid) { + showRecordList[showRecordList.size - 1] = lastRecord!! + } else { + showRecordList.add(lastRecord!!) + } + } //格式化所有的用户信息 formatAllUserInfo(streamContext) return paramsData } + /** * 格式化所有的用户信息 */ fun formatAllUserInfo(streamContext: StreamContext?) { - recordList.forEach { item -> + allRecordList.forEach { item -> + streamContext?.getAllStreamList()?.find { it.streamUuid == item.uid?.toString() }?.owner?.let { info -> + item.userName = info.userName + item.userHeader = info.userName + } + } + showRecordList.forEach { item -> + streamContext?.getAllStreamList()?.find { it.streamUuid == item.uid?.toString() }?.owner?.let { info -> + item.userName = info.userName + item.userHeader = info.userName + } + } + lastRecord?.let { item -> streamContext?.getAllStreamList()?.find { it.streamUuid == item.uid?.toString() }?.owner?.let { info -> item.userName = info.userName item.userHeader = info.userName @@ -1669,17 +1712,21 @@ private class RttUseManager( /** * 获取记录列表 */ - fun getRecordList(): List { - return recordList.toList() + fun getShowRecordList(): List { + return showRecordList.toList() } /** * 设置最后一条信息为最后 */ fun setLastFinal() { - if (recordList.isNotEmpty()) { - recordList[recordList.size - 1].isFinal = true + if (allRecordList.isNotEmpty()) { + allRecordList[allRecordList.size - 1].isFinal = true + } + if (showRecordList.isNotEmpty()) { + showRecordList[showRecordList.size - 1].isFinal = true } + lastRecord?.isFinal = true } } @@ -1778,8 +1825,7 @@ abstract class FcrRttOptionsStatusListener { /** * 消息改变 - * @param recordList 消息记录数据 * @param currentData 当前要显示的数据 */ - open fun onMessageChange(recordList: List, currentData: RttRecordItem?) {} + open fun onMessageChange(currentData: RttRecordItem?) {} } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index 5b103dba2..5992a6262 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -69,7 +69,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { internal inner class AgoraRttToolBoxWidgetContent( val container: ViewGroup, agoraUIProvider: IAgoraUIProvider, - agoraEduOptionsComponent: AgoraEduOptionsComponent?, conversionStatusView: ViewGroup?,val subtitleView: AgoraEduRttOptionsComponent?, + agoraEduOptionsComponent: AgoraEduOptionsComponent?, conversionStatusView: ViewGroup?, val subtitleView: AgoraEduRttOptionsComponent?, ) : IRttOptions { private val listener = object : FcrRttOptionsStatusListener() { override fun conversionViewReset() { @@ -149,11 +149,11 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_listening)) } - override fun onMessageChange(recordList: List, currentData: RttRecordItem?) { - super.onMessageChange(recordList, currentData) + override fun onMessageChange(currentData: RttRecordItem?) { + super.onMessageChange(currentData) subtitleView?.visibility = View.VISIBLE - subtitleView?.setShowTranslatorsInfo(currentData?.userHeader ?: "", currentData?.userName ?: "", - currentData?.sourceText ?: "", currentData?.targetText) + subtitleView?.setShowTranslatorsInfo(currentData?.userHeader ?: "", currentData?.userName ?: "", currentData?.sourceText ?: "", + currentData?.targetText) } } @@ -162,7 +162,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { */ private val rttOptionsManager: RttOptionsManager by lazy { RttOptionsManager(this).also { - subtitleView?.initView(it,agoraUIProvider) + subtitleView?.initView(it, agoraUIProvider) it.initView(agoraUIProvider) it.addListener(listener) } @@ -201,12 +201,13 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { * 重置显示状态 */ fun resetStatus() { - runOnUiThread{ + runOnUiThread { val experienceReduceTime = rttOptionsManager.getExperienceReduceTime() binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { - MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), rttOptionsManager.getExperienceDefaultTime() / 60000) + MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), + rttOptionsManager.getExperienceDefaultTime() / 60000) } else { container.context.getString(R.string.fcr_dialog_rtt_time_limit_end) } From dd5167b15c84dede3f57eedd505c346f2bfe2eb7 Mon Sep 17 00:00:00 2001 From: wangliang Date: Fri, 9 Aug 2024 19:28:44 +0800 Subject: [PATCH 20/32] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dialog/AgoraUIRttConversionDialog.kt | 23 +++++++++++-------- .../online/helper/FcrRttOptionsManager.kt | 19 +++++++++++++-- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 4 ++-- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt index 4983dacee..f0416613b 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -193,9 +193,11 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago if (!text.isNullOrEmpty()) { mAdapter.searchResultListIndexMap.clear() mAdapter.dataList.forEachIndexed { index, rttRecordItem -> - findSubstringsIndexes(rttRecordItem.sourceText, text).apply { - addAll(findSubstringsIndexes(if (rttRecordItem.currentTargetLan.isNullOrEmpty()) "" else rttRecordItem.targetText, - text).map { return@map it + (rttRecordItem.sourceText?.length ?: 0) }) + val showText = rttRecordItem.getShowText() + val level1Text = showText[0] + val level2Text = showText[1] + findSubstringsIndexes(level1Text, text).apply { + addAll(findSubstringsIndexes(if (level2Text.isNullOrEmpty()) "" else level2Text, text).map { return@map it + (level1Text?.length ?: 0) }) }.let { if (it.isNotEmpty()) { mAdapter.searchResultListIndexMap[index] = it @@ -359,15 +361,16 @@ private class RecordAdapter(private val context: Context, val dataList: ArrayLis (if (bean.time == null || bean.time == 0L) null else bean.time)?.let { time -> Date(time) } ?.let { date -> SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA).format(date) } - if (searchText.isNullOrEmpty() || !searchResultListIndexMap.containsKey(position)) { - it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_origin).text = bean.sourceText - it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_result).text = - if (bean.currentTargetLan.isNullOrEmpty()) "" else bean.targetText + val showText = bean.getShowText() + val level1Text = showText[0] + val level2Text = showText[1] + if (!searchResultListIndexMap.containsKey(position)) { + it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_origin).text = level1Text + it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_result).text = level2Text } else { val resultCount = getPreItemResultCount(position) - val sourceSpan = SpannableString(bean.sourceText) - val targetTextSpan = - if (bean.currentTargetLan.isNullOrEmpty() || bean.targetText.isNullOrEmpty()) null else SpannableString(bean.targetText) + val sourceSpan = SpannableString(level1Text) + val targetTextSpan = if (level2Text.isNullOrEmpty()) null else SpannableString(level2Text) searchResultListIndexMap[position]?.forEachIndexed { index, position -> if (resultCount + index == currentSearchResultIndex) { //当前是定位到的位置 diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 93d7072fb..b87cf5a01 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -827,6 +827,21 @@ class RttRecordItem { */ var isFinal: Boolean = false + + /** + * 获取显示的源文本信息 + * @returns + */ + fun getShowText(): Array { + //是否设置了翻译语言 + val enableTargetLan = !currentTargetLan.isNullOrEmpty() && "" !== currentTargetLan!![0].value + //语言显示 + val leve2Text = + if (showDoubleLan && enableTargetLan && !currentTargetLan.isNullOrEmpty() && sourceLan != null && sourceLan!!.value !== currentTargetLan!![0].value) targetText else null + val leve1Text = if (!showDoubleLan && enableTargetLan) targetText else sourceText + return arrayOf(leve1Text, leve2Text) + } + } /** @@ -1671,8 +1686,8 @@ private class RttUseManager( } } lastRecord = paramsData - if (lastRecord != null && rttOptionsManager.isOpenConversion() && allRecordList.isNotEmpty()) { - val showItem = if(showRecordList.isNotEmpty()) showRecordList[showRecordList.size - 1] else null + if (lastRecord != null && rttOptionsManager.isOpenConversion()) { + val showItem = if (showRecordList.isNotEmpty()) showRecordList[showRecordList.size - 1] else null if (showItem?.uuid != null && showItem.uuid == lastRecord!!.uuid) { showRecordList[showRecordList.size - 1] = lastRecord!! } else { diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index 5992a6262..d23eb7f12 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -152,8 +152,8 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun onMessageChange(currentData: RttRecordItem?) { super.onMessageChange(currentData) subtitleView?.visibility = View.VISIBLE - subtitleView?.setShowTranslatorsInfo(currentData?.userHeader ?: "", currentData?.userName ?: "", currentData?.sourceText ?: "", - currentData?.targetText) + val showText = currentData?.getShowText() + subtitleView?.setShowTranslatorsInfo(currentData?.userHeader ?: "", currentData?.userName ?: "", showText!![0] ?: "", showText[1]) } } From 49fdec178a7df4b375d3f49de386f4fbdfc7366f Mon Sep 17 00:00:00 2001 From: wangliang Date: Fri, 9 Aug 2024 20:11:48 +0800 Subject: [PATCH 21/32] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/dialog/AgoraUIRttConversionDialog.kt | 10 ++++++---- .../io/agora/online/helper/FcrRttOptionsManager.kt | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt index f0416613b..1c24a1dd6 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -152,10 +152,12 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) } } else { - mAdapter.dataList[mAdapter.dataList.size - 1] = list[list.size - 1] - mAdapter.notifyItemChanged(mAdapter.dataList.size - 1) - if (mAdapter.searchText.isNullOrEmpty()) { - binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) + if(list.isNotEmpty() && mAdapter.dataList.isNotEmpty()) { + mAdapter.dataList[mAdapter.dataList.size - 1] = list[list.size - 1] + mAdapter.notifyItemChanged(mAdapter.dataList.size - 1) + if (mAdapter.searchText.isNullOrEmpty()) { + binding.fcrOnlineEduRttConversionDialogList.scrollToPosition(mAdapter.dataList.size - 1) + } } } this.searchData(mAdapter.searchText) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index b87cf5a01..bc791feef 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -838,7 +838,7 @@ class RttRecordItem { //语言显示 val leve2Text = if (showDoubleLan && enableTargetLan && !currentTargetLan.isNullOrEmpty() && sourceLan != null && sourceLan!!.value !== currentTargetLan!![0].value) targetText else null - val leve1Text = if (!showDoubleLan && enableTargetLan) targetText else sourceText + val leve1Text = if (!showDoubleLan && enableTargetLan && !targetText.isNullOrEmpty()) targetText else sourceText return arrayOf(leve1Text, leve2Text) } From 86ecdd4dfa063504f6ba059a2b70509c3109633f Mon Sep 17 00:00:00 2001 From: wangliang Date: Sun, 11 Aug 2024 19:33:58 +0800 Subject: [PATCH 22/32] =?UTF-8?q?=E8=BD=AC=E5=86=99=E5=BC=80=E5=85=B3?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/agora/online/helper/FcrRttOptionsManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index bc791feef..92ee9cfd0 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -1269,7 +1269,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana * 开启转写 */ override fun openConversion() { - if (rttOptionsManager.isOpenSubtitles()) { + if (rttOptionsManager.isOpenConversion()) { openSuccess = true listener.conversionStateChange(true) } else { @@ -1298,7 +1298,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana * 关闭转写 */ override fun closeConversion() { - if (!rttOptionsManager.isOpenSubtitles()) { + if (rttOptionsManager.isOpenConversion()) { if (openSuccess) { rttOptionsManager.sendRequest(false, rttOptionsManager.isOpenSubtitles(), callback = object : HttpCallback>() { From 907d52a9e47b2c282e8b2a1ee37a19c63145930a Mon Sep 17 00:00:00 2001 From: wangliang Date: Fri, 16 Aug 2024 19:56:18 +0800 Subject: [PATCH 23/32] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dialog/AgoraUIRttConversionDialog.kt | 3 +- .../dialog/AgoraUIRttSettingDialog.kt | 2 +- .../online/helper/FcrRttOptionsManager.kt | 87 ++++++++++--------- .../online/widget/rtt/FcrRttToolBoxWidget.kt | 7 ++ .../fcr_online_tool_box_widget_content.xml | 2 + .../src/main/res/values/strings.xml | 2 + 6 files changed, 61 insertions(+), 42 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt index 1c24a1dd6..65d79f5f8 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -405,8 +405,7 @@ private class RecordAdapter(private val context: Context, val dataList: ArrayLis } it.findViewById(R.id.agora_fcr_rtt_text_dialog_text_result).apply { - visibility = if (bean.currentTargetLan.isNullOrEmpty() || bean.currentTargetLan!!.contains( - RttLanguageEnum.NONE)) View.GONE else View.VISIBLE + visibility = if (RttLanguageEnum.NONE == bean.currentTargetLan) View.GONE else View.VISIBLE } } } else { diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt index 49c0c3027..bb3cf00a8 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt @@ -290,7 +290,7 @@ private class ContentAdapter(var context: Context) : RecyclerView.Adapter) { + fun setTargetLanguage(lan: RttLanguageEnum) { settingsManager.currentSettingInfo.setTargetLan(lan) this.localIsChangeTarget = true sendRequest(isOpenConversion(), isOpenSubtitles(), object : HttpCallback>() { override fun onSuccess(result: HttpBaseRes?) { useManager.setLastFinal() - this@RttOptionsManager.getManagerListener().targetLanguageChange(lan.toList()) + this@RttOptionsManager.getManagerListener().targetLanguageChange(settingsManager.currentSettingInfo.getTargetLanList()) } }) } @@ -524,7 +524,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } isSendRequesting = true val body = FcrRttChangeOptionsData.formatUseData(openRttConversion, openRttSubtitles, settingsManager.currentSettingInfo.getSourceLan().value, - settingsManager.currentSettingInfo.getTargetLan().map { it.value }.toTypedArray()) + if (openRttConversion || openRttSubtitles) settingsManager.currentSettingInfo.getTargetLanList().filter { "" != it.value }.map { it.value }.distinct().toTypedArray() else arrayOf()) val call = AppRetrofitManager.instance().getService(IRttOptionsService::class.java) .buildTokens(eduCore?.config?.appId, eduCore?.config?.roomUuid, if (openRttConversion || openRttSubtitles) 1 else 0, body) AppRetrofitManager.exc(call, object : HttpCallback>() { @@ -586,12 +586,13 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { //判断是否改变了转写状态 if (checkUpdateConversion(it) || localIsChangeTranscribeState && operator.userUuid === localUserInfo?.userUuid) { this.localIsChangeTranscribeState = false + conversionManager.initOpenConversion(1 == it.transcribe) conversionManager.addStateChangeTextMessage(it.transcribe, userInfo, localUserInfo, useManager) } //判断是否开启了翻译(仅当自己生效) if (localIsChangeTarget && operator.userUuid === localUserInfo?.userUuid && !it.languages?.target.isNullOrEmpty() && this.localIsChangeTarget) { this.localIsChangeTarget = false - settingsManager.currentSettingInfo.setTargetLan( + settingsManager.currentSettingInfo.setTargetLanList( it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() ?: arrayOf()) conversionManager.addTargetLanguageChangeMessage(true, userInfo, useManager) @@ -605,6 +606,19 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { conversionManager.addSourceLanguageChangeMessage(userInfo, localUserInfo, useManager, settingsManager, sourceEnum) } } + + //是否开启字幕 + if (0 == it.subtitle && this.isOpenSubtitles()) { + //重新发起字幕开启 + sendRequest(this.isOpenConversion(), openRttSubtitles = this.isOpenSubtitles(), + callback = object : HttpCallback>() { + override fun onSuccess(result: HttpBaseRes?) { + } + + override fun onError(httpCode: Int, code: Int, message: String?) { + } + }) + } } } } @@ -780,7 +794,7 @@ class RttRecordItem { /** * 当前翻译的目标语言 */ - var currentTargetLan: Array? = null + var currentTargetLan: RttLanguageEnum? = null /** * 置信度 @@ -834,10 +848,10 @@ class RttRecordItem { */ fun getShowText(): Array { //是否设置了翻译语言 - val enableTargetLan = !currentTargetLan.isNullOrEmpty() && "" !== currentTargetLan!![0].value + val enableTargetLan = "" !== currentTargetLan?.value //语言显示 val leve2Text = - if (showDoubleLan && enableTargetLan && !currentTargetLan.isNullOrEmpty() && sourceLan != null && sourceLan!!.value !== currentTargetLan!![0].value) targetText else null + if (showDoubleLan && enableTargetLan && currentTargetLan != null && sourceLan != null && sourceLan!!.value !== currentTargetLan?.value) targetText else null val leve1Text = if (!showDoubleLan && enableTargetLan && !targetText.isNullOrEmpty()) targetText else sourceText return arrayOf(leve1Text, leve2Text) } @@ -858,7 +872,8 @@ class RttSettingInfo( val targetListLan: ArrayList = arrayListOf(RttLanguageEnum.NONE, RttLanguageEnum.ZH_CN, RttLanguageEnum.EN_US, RttLanguageEnum.JA_JP), private var sourceLan: RttLanguageEnum = RttLanguageEnum.ZH_CN, - private var targetLan: Array = arrayOf(RttLanguageEnum.NONE), + private var targetLanList: ArrayList = arrayListOf(RttLanguageEnum.NONE), + private var targetLan:RttLanguageEnum =RttLanguageEnum.NONE, private var showDoubleLan: Boolean = SpUtil.getBoolean(rttOptionsManager.rttOptions.getApplicationContext(), "${rttOptionsManager.eduCore?.config?.roomUuid ?: ""}_showDoubleLan", false), ) { @@ -886,14 +901,20 @@ class RttSettingInfo( } fun getSourceLan() = sourceLan + fun getTargetLanList() = targetLanList fun getTargetLan() = targetLan fun isShowDoubleLan() = showDoubleLan fun setSourceLan(sourceLan: RttLanguageEnum) { this.sourceLan = sourceLan } - fun setTargetLan(targetLan: Array) { - this.targetLan = if (targetLan.isNotEmpty()) targetLan else arrayOf(RttLanguageEnum.NONE) + fun setTargetLan(targetLan: RttLanguageEnum) { + this.targetLan = targetLan + this.targetLanList.add(targetLan) + } + + fun setTargetLanList(targetLan: Array) { + this.targetLanList = targetLan.toCollection(arrayListOf()) } fun setShowDoubleLan(show: Boolean) { @@ -1214,14 +1235,9 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag //清除消息 this.clearHandlerMsg() - //是否开启双语显示 - val showTranslateOnly = currentSettingInfo.isShowDoubleLan() && currentSettingInfo.getTargetLan() - .isNotEmpty() && RttLanguageEnum.NONE !== currentSettingInfo.getTargetLan()[0] && currentSettingInfo.getSourceLan() !== currentSettingInfo.getTargetLan()[0] - val sourceText = recordItem?.sourceText - val targetText = recordItem?.targetText - val translating = targetText.isNullOrEmpty() && showTranslateOnly - - if (sourceText.isNullOrEmpty() && !translating) { + val showText = recordItem?.getShowText() + val translating = 2 == (showText?.size ?: 0) && showText?.get(0).isNullOrEmpty() && showText?.get(1).isNullOrEmpty() + if (2 != (showText?.size ?: 0) || showText?.get(0).isNullOrEmpty() && !translating) { listener.audioStateNoSpeaking() } else if (translating) { listener.audioStateSpeaking() @@ -1465,7 +1481,7 @@ private class RttSettingManager(private val rttOptionsManager: RttOptionsManager */ override fun setTargetLan(code: String) { RttLanguageEnum.values().find { it.value == code }?.let { - rttOptionsManager.setTargetLanguage(arrayOf(it)) + rttOptionsManager.setTargetLanguage(it) } } @@ -1648,40 +1664,33 @@ private class RttUseManager( //翻译 "translate" -> { LogX.i(TAG, "Translation:" + GsonUtil.toJson(rttMsgData)) - val tranList = arrayListOf() + val last = allRecordList.findLast { it.uid == lastItemByUid } + val tranList = last?.targetInfo ?: arrayListOf() val transTextStr = StringBuffer() - val lanMapText = hashMapOf() rttMsgData.transList.forEach { transItem -> transItem.textsList.forEach { text -> LogX.i(TAG, "Translation:$lastItemByUid$$$$$$$$$text") transTextStr.append(text) } - tranList.add(RttRecordItemTran().apply { - language = RttLanguageEnum.values().find { it.value == transItem.lang } - text = transTextStr.toString() - }) - lanMapText[transItem.lang] = - if (lanMapText.contains(transItem.lang)) lanMapText[transItem.lang] + transTextStr.toString() else transTextStr.toString() + tranList.find { transItem.lang == it.language?.value }.let {find-> + if(find != null){ + find.text = transTextStr.toString() + }else{ + tranList.add(RttRecordItemTran().apply { + language = RttLanguageEnum.values().find { it.value == transItem.lang } + text = transTextStr.toString() + }) + } + } transTextStr.setLength(0) } paramsData = allRecordList.findLast { it.uid == lastItemByUid }?.apply { - //处理拼接数据,当然,当前只有一个目标语言,为了扩展使用以下方式 - transTextStr.setLength(0) - currentTargetLan?.forEach { item -> - if (lanMapText.contains(item.value)) { - if (transTextStr.isNotEmpty()) { - transTextStr.append("\n") - } - transTextStr.append(lanMapText[item.value]) - } - } currentTargetLan = settingInfo.getTargetLan() uuid = UUID.randomUUID().toString() showDoubleLan = settingInfo.isShowDoubleLan() targetInfo = tranList - targetText = transTextStr.toString() - transTextStr.setLength(0) + targetText = tranList.find { item-> (item.language?.value ?:"") == currentTargetLan?.value }?.text } } } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index d23eb7f12..c7f830723 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -9,6 +9,7 @@ import io.agora.agoraeducore.core.internal.framework.data.EduBaseUserInfo import io.agora.agoraeducore.extensions.widgets.AgoraBaseWidget import io.agora.online.R import io.agora.online.component.common.IAgoraUIProvider +import io.agora.online.component.toast.AgoraUIToast import io.agora.online.databinding.FcrOnlineToolBoxWidgetContentBinding import io.agora.online.helper.FcrRttOptionsStatusListener import io.agora.online.helper.IRttOptions @@ -95,6 +96,9 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { super.subtitlesStateChange(toOpen) agoraEduOptionsComponent?.hiddenRtt() resetStatus() + if(!toOpen){ + AgoraUIToast.showDefaultToast(container.context,container.context.getString(R.string.fcr_dialog_rtt_subtitles_myself_close_finish)) + } } override fun conversionStateChange(toOpen: Boolean) { @@ -179,9 +183,11 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { binding.agoraRttDialogSubtitles.setOnClickListener { if (this.rttOptionsManager.isOpenSubtitles()) { binding.agoraRttDialogSubtitlesIcon.isActivated = false + binding.agoraRttDialogSubtitlesText.setText(R.string.fcr_dialog_rtt_subtitles_close) this.rttOptionsManager.closeSubtitles() } else { binding.agoraRttDialogSubtitlesIcon.isActivated = true + binding.agoraRttDialogSubtitlesText.setText(R.string.fcr_dialog_rtt_subtitles) this.rttOptionsManager.openSubtitles() } } @@ -204,6 +210,7 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { runOnUiThread { val experienceReduceTime = rttOptionsManager.getExperienceReduceTime() binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() + binding.agoraRttDialogSubtitlesText.setText(if(rttOptionsManager.isOpenSubtitles())R.string.fcr_dialog_rtt_subtitles_close else R.string.fcr_dialog_rtt_subtitles) binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_tool_box_widget_content.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_tool_box_widget_content.xml index 0f807a2d9..93f80280b 100644 --- a/AgoraCloudScene/src/main/res/layout/fcr_online_tool_box_widget_content.xml +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_tool_box_widget_content.xml @@ -47,6 +47,7 @@ android:src="@drawable/agora_option_icon_rtt_subtitles" /> 字幕 + 关闭字幕 + 你已关闭字幕 实时转写 转写中... 字幕和转写设置 From 53bb2214f69614b198381526621087024aabd366 Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 19 Aug 2024 15:15:36 +0800 Subject: [PATCH 24/32] =?UTF-8?q?=E5=88=86=E7=BB=84=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=A4=84=E7=90=86rtt=E6=98=BE=E7=A4=BA=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/agora/online/helper/FcrRttOptionsManager.kt | 4 ++-- .../io/agora/online/options/AgoraEduOptionsComponent.kt | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index b6258c535..3dd2bba77 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -107,8 +107,8 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { private val steamHandler = object : StreamHandler() { override fun onStreamMessage(channelId: String, streamId: Int, data: ByteArray?) { super.onStreamMessage(channelId, streamId, data) - //不允许使用或者已到体验时间就不在回调 - if (!isAllowUseRtt() || (!conversionManager.isOpenConversion() && !subtitlesManager.isOpenSubtitles())) { + //不允许使用或者已到体验时间就不在回调,在分组中也不对消息数据进行处理 + if (!isAllowUseRtt() || (!conversionManager.isOpenConversion() && !subtitlesManager.isOpenSubtitles()) || eduCore?.eduContextPool()?.groupContext()?.groupInfo != null) { return } val parseFrom = AgoraSpeech2TextProtobuffer.Text.parseFrom(data) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt index 290f047f6..56cdac2b5 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt @@ -361,6 +361,12 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite popupViewRoster = AgoraEduRosterComponent(context) popupViewRoster?.initView(agoraUIProvider) } + //Rtt功能只在非分组状态下使用 + if(eduContext?.groupContext()?.groupInfo == null){ + binding.optionItemRtt.visibility = VISIBLE + }else{ + binding.optionItemRtt.visibility = GONE + } } /** From f5888785a21fee8658c4caa7e7dece169fdf6ce8 Mon Sep 17 00:00:00 2001 From: wangliang Date: Mon, 19 Aug 2024 15:26:01 +0800 Subject: [PATCH 25/32] =?UTF-8?q?=E5=88=86=E7=BB=84=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=A4=84=E7=90=86rtt=E6=98=BE=E7=A4=BA=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/agora/online/helper/FcrRttOptionsManager.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 3dd2bba77..3cf3161c4 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -1124,6 +1124,8 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag messageWhatListening -> { listener.audioStateNoSpeaking() + //显示两秒正在聆听 + sendEmptyMessageDelayed(messageWhatNoSpeaking, 2000) } messageWhatNoSpeaking -> { From 310427b80e158a2ef10b27497821b7313774e8f4 Mon Sep 17 00:00:00 2001 From: wangliang Date: Tue, 20 Aug 2024 17:43:58 +0800 Subject: [PATCH 26/32] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=88=86=E7=BB=84?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/agora/online/helper/FcrRttOptionsManager.kt | 10 +++++++++- .../agora/online/options/AgoraEduOptionsComponent.kt | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 3cf3161c4..14e0f078f 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -30,6 +30,7 @@ import io.agora.online.component.dialog.AgoraUIRttSettingDialogListener import io.agora.online.component.dialog.ConversionOptionsInterface import io.agora.online.component.toast.AgoraUIToast import io.agora.online.easeim.utils.TAG +import io.agora.online.easeim.view.ui.widget.RoomType import io.agora.online.util.MultiLanguageUtil import io.agora.online.util.SpUtil import io.agora.rtc.speech2text.AgoraSpeech2TextProtobuffer @@ -108,7 +109,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { override fun onStreamMessage(channelId: String, streamId: Int, data: ByteArray?) { super.onStreamMessage(channelId, streamId, data) //不允许使用或者已到体验时间就不在回调,在分组中也不对消息数据进行处理 - if (!isAllowUseRtt() || (!conversionManager.isOpenConversion() && !subtitlesManager.isOpenSubtitles()) || eduCore?.eduContextPool()?.groupContext()?.groupInfo != null) { + if (!isAllowUseRtt() || (!conversionManager.isOpenConversion() && !subtitlesManager.isOpenSubtitles()) || checkUserInSubRoom()) { return } val parseFrom = AgoraSpeech2TextProtobuffer.Text.parseFrom(data) @@ -301,6 +302,13 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } } + /** + * 检测用户是否在分组中 + */ + fun checkUserInSubRoom():Boolean{ + return eduCore?.eduContextPool()?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.GROUPING_CLASS.value + } + /** * 新增监听 */ diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt index 56cdac2b5..a78754640 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt @@ -362,7 +362,7 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite popupViewRoster?.initView(agoraUIProvider) } //Rtt功能只在非分组状态下使用 - if(eduContext?.groupContext()?.groupInfo == null){ + if(eduContext?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.GROUPING_CLASS.value){ binding.optionItemRtt.visibility = VISIBLE }else{ binding.optionItemRtt.visibility = GONE From 83f1760e49bc6edf98d020b3d2e6984d35107c9d Mon Sep 17 00:00:00 2001 From: wangliang Date: Fri, 23 Aug 2024 14:53:08 +0800 Subject: [PATCH 27/32] =?UTF-8?q?=E6=96=87=E6=A1=88=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/values-zh-rTW/strings.xml | 58 ++++++++++++++ .../src/main/res/values-zh/strings.xml | 54 +++++++++++++ .../src/main/res/values/strings.xml | 76 +++++++++---------- 3 files changed, 150 insertions(+), 38 deletions(-) diff --git a/AgoraCloudScene/src/main/res/values-zh-rTW/strings.xml b/AgoraCloudScene/src/main/res/values-zh-rTW/strings.xml index 050163315..f791b3265 100644 --- a/AgoraCloudScene/src/main/res/values-zh-rTW/strings.xml +++ b/AgoraCloudScene/src/main/res/values-zh-rTW/strings.xml @@ -499,4 +499,62 @@ 請先關閉分組討論 請先關閉擴展屏 + + + 字幕 + 關閉字幕 + 你已關閉字幕 + 實時轉寫 + 轉寫中... + 字幕和轉寫設置 + 聲源語言(全會統一) + 翻譯為(自己想看的語言,且只對自己可見) + 同時顯示雙語 + 確定修改 + 修改聲源語言 + 你修改教室的聲源語言為%s,將對會議所有參會者的字幕和轉寫生效。 + 限時體驗 + 限時體驗{0}分鐘 + 限時體驗結束 + 實時轉寫 + 剩余體驗時間 {0}:{1} 分鐘 + 每個房間限時{0}分鐘體驗字幕和轉寫功能,剩余 {1}:{2} 分鐘。 + 每個房間限時{0}分鐘體驗字幕和轉寫功能,體驗時間已用完。 + 無法使用 + 正在開啟字幕 + 點擊字幕位置可以更改字幕設置 + 開始轉寫 + 停止轉寫 + 當前沒有人在說話 + 正在聆聽 + + 已將聲源語言設置為 + 已開啟實時轉寫 + 已停止實時轉寫 + 開啟翻譯識別內容 + 開啟了實時轉寫服務,全體用戶可見。 + 停止了實時轉寫服務,全體用戶不可見。 + 開啟了實時轉寫服務。 + 停止了實時轉寫服務。 + 不翻譯 + 中文(繁体) + 简体中文 + 中文(繁体) + 英语(印第安语) + English + 法语 + 德语 + 泰语 + 印度语 + 印尼语 + 意大利语 + 日本語 + 韩语 + 马来西亚语 + 波斯语 + 葡萄牙语 + 俄语 + 西班牙语 + 土耳其语 + 越南语 diff --git a/AgoraCloudScene/src/main/res/values-zh/strings.xml b/AgoraCloudScene/src/main/res/values-zh/strings.xml index 8b05cf05a..bc8e11a8c 100644 --- a/AgoraCloudScene/src/main/res/values-zh/strings.xml +++ b/AgoraCloudScene/src/main/res/values-zh/strings.xml @@ -586,5 +586,59 @@ 字幕 + 关闭字幕 + 你已关闭字幕 实时转写 + 转写中... + 字幕和转写设置 + 声源语言(全会统一) + 翻译为(自己想看的语言,且只对自己可见) + 同时显示双语 + 确定修改 + 修改声源语言 + 你修改本场会议的声源语言为%s,将对会议所有参会者的字幕和转写生效。 + 限时体验 + 限时体验{0}分钟 + 限时体验结束 + 实时转写 + 剩余体验时间 {0}:{1} 分钟 + 每个房间限时{0}分钟体验字幕和转写功能,剩余 {1}:{2} 分钟。 + 每个房间限时{0}分钟体验字幕和转写功能,体验时间已用完。 + 无法使用 + 正在开启字幕 + 点击字幕位置可以更改字幕设置 + 开始转写 + 停止转写 + 当前没有人在说话 + 正在聆听 + + 已将声源语言设置为 + 已开启实时转写 + 已停止实时转写 + 开启翻译识别内容 + 开启了实时转写服务,全体用户可见。 + 停止了实时转写服务,全体用户可见。 + 开启了实时转写服务。 + 停止了实时转写服务,全体用户可见 + 不翻译 + 中文(繁体) + 简体中文 + 中文(繁体) + 英语(印第安语) + English + 法语 + 德语 + 泰语 + 印度语 + 印尼语 + 意大利语 + 日本語 + 韩语 + 马来西亚语 + 波斯语 + 葡萄牙语 + 俄语 + 西班牙语 + 土耳其语 + 越南语 diff --git a/AgoraCloudScene/src/main/res/values/strings.xml b/AgoraCloudScene/src/main/res/values/strings.xml index 284a415df..424f61c36 100644 --- a/AgoraCloudScene/src/main/res/values/strings.xml +++ b/AgoraCloudScene/src/main/res/values/strings.xml @@ -580,54 +580,54 @@ Delete your account will result in ….\n Teacher has ended the poll - 字幕 - 关闭字幕 - 你已关闭字幕 - 实时转写 - 转写中... - 字幕和转写设置 - 声源语言(全会统一) - 翻译为 (自己想看的语言,且只对自己可见) - 同时显示双语 - 确定修改 - 修改声源语言 - 你修改本场会议的声源语言为%s,将对会议所有参会者的字幕和转写生效。 - 限时体验 - 限时体验{0}分钟 - 限时体验结束 - 实时转写 - 剩余体验时间 {0}:{1} 分钟 - 每个账号限时{0}分钟体验字幕和转写功能,剩余 {1}:{2} 分钟。 - 每个账号限时{0}分钟体验字幕和转写功能,体验时间已用完。 - 无法使用 - 正在开启字幕 - 点击字幕位置可以更改字幕设置 - 开始转写 - 停止转写 - 当前没有人在说话 - 正在聆听 - - 已将声源语言设置为 - 已开启实时转写 - 已停止实时转写 - 开启翻译识别内容 - 开启了实时转写服务,全体用户可见。 - 停止了实时转写服务,全体用户可见。 - 开启了实时转写服务。 - 停止了实时转写服务,全体用户可见 - 不翻译 + Subtitles + Disable subtitles + You have turned off the subtitles. + Live transcription + Transcribing + Subtitles and transliteration Settings + Original Audio + Translation Display + Bilingual Mode + Confirm + Change source language + You are changing the source language of this Room to %s.This will affect captions and transcripts for all attendees + Limited time experience + {0} Limited time experience + Limited time experience ended + Live transcription + remaining experience time {0}:{1} + Each room has a {0} minute limit to experience subtitles and transcription features, with {1}:{2} minutes remaining. + Each room has a {0} minute limit to experience subtitles and transcription features, and the experience time has run out. + Unavailable + Turning on subtitles + Click on the subtitle position to change subtitle settings. + Transcribe + End Transcript + No one is currently speaking + Listening + Me + changed the source language for captions and transcription to + Turned on live transcription + Turned off live transcription + Enable translation of recognized content + The live transcription service has been activated and is visible to all users. + The live transcription service has been stopped and is invisible to all users. + Turned on live transcription + Turned off live transcription + Do Not Translate 中文(繁体) 简体中文 中文(繁体) 英语(印第安语) - 英语 + English 法语 德语 泰语 印度语 印尼语 意大利语 - 日语 + 日本語 韩语 马来西亚语 波斯语 From 7e2da9f6c45430774125f4b95b78c6f3243de208 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 12 Sep 2024 17:19:08 +0800 Subject: [PATCH 28/32] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../res/layout/fcr_online_edu_rtt_conversion_dialog.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml index 5c3d78c78..6143dbc45 100644 --- a/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml +++ b/AgoraCloudScene/src/main/res/layout/fcr_online_edu_rtt_conversion_dialog.xml @@ -54,7 +54,7 @@ Date: Sat, 14 Sep 2024 16:05:01 +0800 Subject: [PATCH 29/32] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dialog/AgoraUIRttConversionDialog.kt | 5 ++-- .../online/helper/FcrRttOptionsManager.kt | 24 ++++++++++++++----- .../src/main/res/values-zh-rTW/strings.xml | 2 ++ .../src/main/res/values-zh/strings.xml | 2 ++ .../src/main/res/values/strings.xml | 2 ++ 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt index 65d79f5f8..e2e841c1c 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -206,7 +206,8 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago } } } - mAdapter.currentSearchResultIndex = mAdapter.currentSearchResultIndex.coerceAtMost(mAdapter.itemCount) + mAdapter.currentSearchResultIndex = + mAdapter.currentSearchResultIndex.coerceAtMost(mAdapter.itemCount - 1).coerceAtMost(mAdapter.getSumResultCount() - 1).coerceAtLeast(0) binding.fcrOnlineEduRttConversionDialogSearchClear.visibility = View.VISIBLE binding.fcrOnlineEduRttConversionDialogSearchCount.visibility = View.VISIBLE binding.fcrOnlineEduRttConversionDialogOptionsChangeLoc.visibility = View.VISIBLE @@ -252,7 +253,7 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago */ @SuppressLint("SetTextI18n") private fun changeShowSearchResult() { - binding.fcrOnlineEduRttConversionDialogSearchCount.text = "${mAdapter.currentSearchResultIndex + 1}/${mAdapter.getSumResultCount()}" + binding.fcrOnlineEduRttConversionDialogSearchCount.text = "${if(mAdapter.getSumResultCount() == 0) "0" else (mAdapter.currentSearchResultIndex + 1)}/${mAdapter.getSumResultCount()}" binding.fcrOnlineEduRttConversionDialogOptionsGoPre.isEnabled = mAdapter.currentSearchResultIndex > 0 binding.fcrOnlineEduRttConversionDialogOptionsGoNext.isEnabled = mAdapter.currentSearchResultIndex < (mAdapter.getSumResultCount() - 1) mAdapter.notifyItemRangeChanged(0, mAdapter.itemCount) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 14e0f078f..7692951bc 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -24,6 +24,7 @@ import io.agora.agoraeducore.core.internal.framework.utils.GsonUtil import io.agora.agoraeducore.core.internal.log.LogX import io.agora.online.R import io.agora.online.component.common.IAgoraUIProvider +import io.agora.online.component.dialog.AgoraUIDialogBuilder import io.agora.online.component.dialog.AgoraUIRttConversionDialogBuilder import io.agora.online.component.dialog.AgoraUIRttSettingBuilder import io.agora.online.component.dialog.AgoraUIRttSettingDialogListener @@ -592,10 +593,10 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { //用户信息 val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() //判断是否改变了转写状态 - if (checkUpdateConversion(it) || localIsChangeTranscribeState && operator.userUuid === localUserInfo?.userUuid) { + if (checkUpdateConversion(it) || localIsChangeTranscribeState && operator.userUuid !== localUserInfo?.userUuid) { this.localIsChangeTranscribeState = false - conversionManager.initOpenConversion(1 == it.transcribe) conversionManager.addStateChangeTextMessage(it.transcribe, userInfo, localUserInfo, useManager) + conversionManager.initOpenConversion(1 == it.transcribe) } //判断是否开启了翻译(仅当自己生效) if (localIsChangeTarget && operator.userUuid === localUserInfo?.userUuid && !it.languages?.target.isNullOrEmpty() && this.localIsChangeTarget) { @@ -1298,6 +1299,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana if (rttOptionsManager.isOpenConversion()) { openSuccess = true listener.conversionStateChange(true) + rttOptionsManager.startExperience() } else { if (!openSuccess) { rttOptionsManager.sendRequest(true, rttOptionsManager.isOpenSubtitles(), @@ -1393,7 +1395,6 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana openSuccess = state if (openSuccess) { listener.conversionViewReset() - rttConversionDialog.show(arrayListOf()) } } @@ -1408,7 +1409,9 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana * 新增转写数据 */ fun updateShowList(list: List) { - rttConversionDialog.updateShowList(list) + try { + rttConversionDialog.updateShowList(list) + }catch (ignore:Exception){} } /** @@ -1416,6 +1419,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana */ fun addStateChangeTextMessage(transcribe: Int, userInfo: EduBaseUserInfo, localUserInfo: AgoraEduContextUserInfo?, useManager: RttUseManager) { val toOpen = 1 == transcribe + val appContext = rttOptionsManager.rttOptions.getApplicationContext() val textContent = "${rttOptionsManager.formatRoleName(userInfo, localUserInfo)}${ rttOptionsManager.rttOptions.getApplicationContext() .getString(if (toOpen) R.string.fcr_dialog_rtt_text_conversion_state_open else R.string.fcr_dialog_rtt_text_conversion_state_close) @@ -1427,9 +1431,17 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open else R.string.fcr_dialog_rtt_toast_conversion_state_close }) }" - if (rttOptionsManager.isOpenConversion() || rttOptionsManager.isOpenSubtitles()) { - AgoraUIToast.showDefaultToast(rttOptionsManager.rttOptions.getApplicationContext(), toastContent) + if (!rttOptionsManager.isOpenConversion() && toOpen) { + rttOptionsManager.rttOptions.runOnUiThread{ + AgoraUIDialogBuilder(rttOptionsManager.rttOptions.getActivityContext()).title(appContext.resources.getString(R.string.fcr_dialog_rtt_conversion)) + .message(textContent).messagePaddingHorizontal(appContext.resources.getDimensionPixelOffset(R.dimen.dp_10)) + .negativeText(appContext.resources.getString(R.string.fcr_dialog_rtt_oher_change_source_hint_cancel)) + .positiveText(appContext.resources.getString(R.string.fcr_dialog_rtt_oher_change_source_hint_confirm)).positiveClick { + openConversion(useManager.getShowRecordList()) + }.build().show() + } } + AgoraUIToast.showDefaultToast(appContext, toastContent) useManager.disposeDataChangeSourceLanguage(userInfo, textContent) rttOptionsManager.rttOptions.runOnUiThread { updateShowList(useManager.getShowRecordList()) } LogX.i(TAG, "changeConversionState result=$textContent}") diff --git a/AgoraCloudScene/src/main/res/values-zh-rTW/strings.xml b/AgoraCloudScene/src/main/res/values-zh-rTW/strings.xml index f791b3265..ad3dd9018 100644 --- a/AgoraCloudScene/src/main/res/values-zh-rTW/strings.xml +++ b/AgoraCloudScene/src/main/res/values-zh-rTW/strings.xml @@ -557,4 +557,6 @@ 西班牙语 土耳其语 越南语 + 忽視 + 查看 diff --git a/AgoraCloudScene/src/main/res/values-zh/strings.xml b/AgoraCloudScene/src/main/res/values-zh/strings.xml index bc8e11a8c..4fec9aa52 100644 --- a/AgoraCloudScene/src/main/res/values-zh/strings.xml +++ b/AgoraCloudScene/src/main/res/values-zh/strings.xml @@ -641,4 +641,6 @@ 西班牙语 土耳其语 越南语 + 忽视 + 查看 diff --git a/AgoraCloudScene/src/main/res/values/strings.xml b/AgoraCloudScene/src/main/res/values/strings.xml index 424f61c36..d7ce981a6 100644 --- a/AgoraCloudScene/src/main/res/values/strings.xml +++ b/AgoraCloudScene/src/main/res/values/strings.xml @@ -636,4 +636,6 @@ Delete your account will result in ….\n 西班牙语 土耳其语 越南语 + Ignore + View From a5466634d6d56d26d5811f7e141bd8acffbd3766 Mon Sep 17 00:00:00 2001 From: wangliang Date: Thu, 19 Sep 2024 11:44:31 +0800 Subject: [PATCH 30/32] =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dialog/AgoraUIRttConversionDialog.kt | 31 ++++++++++++++----- .../online/helper/FcrRttOptionsManager.kt | 25 +++++++++------ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt index e2e841c1c..5ec14114b 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttConversionDialog.kt @@ -72,9 +72,9 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago private fun initView() { binding.fcrOnlineEduRttConversionDialogChangeStatus.setOnClickListener { if (it.isActivated) { - closeConversion() + closeConversion(true) } else { - openConversion() + openConversion(true) } } binding.fcrOnlineEduRttConversionDialogOptionsSetting.setOnClickListener { @@ -125,7 +125,7 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setText(R.string.fcr_dialog_rtt_time_limit_end) binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setBackgroundResource(R.drawable.agora_options_rtt_time_limit_bg_disable) binding.fcrOnlineEduRttConversionDialogTimeLimitReduce.visibility = View.GONE - closeConversion() + closeConversion(true) } else { binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setText(R.string.fcr_dialog_rtt_time_limit) binding.fcrOnlineEduRttConversionDialogTimeLimitHint.setBackgroundResource(R.drawable.agora_options_rtt_time_limit_bg) @@ -164,12 +164,25 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago } } + /** + * 修改按钮状态 + */ + fun changeShowButton(toState:Boolean){ + if(toState){ + openConversion(false) + }else{ + closeConversion(false) + } + } + /** * 开始转写 */ - private fun openConversion() { + private fun openConversion(sendRequest:Boolean) { if (binding.fcrOnlineEduRttConversionDialogChangeStatus.isEnabled) { - optionsCallback?.openConversion() + if (sendRequest) { + optionsCallback?.openConversion() + } binding.fcrOnlineEduRttConversionDialogChangeStatus.isActivated = true binding.fcrOnlineEduRttConversionDialogChangeStatus.setText(R.string.fcr_dialog_rtt_dialog_conversion_close) binding.fcrOnlineEduRttConversionDialogChangeStatus.setTextColor(ContextCompat.getColor(context, R.color.fcr_text_red)) @@ -179,8 +192,10 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago /** * 关闭转写 */ - private fun closeConversion() { - optionsCallback?.closeConversion() + private fun closeConversion(sendRequest:Boolean) { + if (sendRequest) { + optionsCallback?.closeConversion() + } binding.fcrOnlineEduRttConversionDialogChangeStatus.isActivated = false binding.fcrOnlineEduRttConversionDialogChangeStatus.setText(R.string.fcr_dialog_rtt_dialog_conversion_open) binding.fcrOnlineEduRttConversionDialogChangeStatus.setTextColor(ContextCompat.getColor(context, R.color.fcr_white)) @@ -228,7 +243,7 @@ class AgoraUIRttConversionDialog(context: Context) : Dialog(context, R.style.ago fun show(list: List) { mAdapter.dataList.clear() mAdapter.dataList.addAll(list) - openConversion() + openConversion(true) super.show() } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 7692951bc..0363eb24a 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -593,13 +593,13 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { //用户信息 val localUserInfo = eduCore?.eduContextPool()?.userContext()?.getLocalUserInfo() //判断是否改变了转写状态 - if (checkUpdateConversion(it) || localIsChangeTranscribeState && operator.userUuid !== localUserInfo?.userUuid) { + if (checkUpdateConversion(it) || localIsChangeTranscribeState && operator.userUuid != localUserInfo?.userUuid) { this.localIsChangeTranscribeState = false conversionManager.addStateChangeTextMessage(it.transcribe, userInfo, localUserInfo, useManager) conversionManager.initOpenConversion(1 == it.transcribe) } //判断是否开启了翻译(仅当自己生效) - if (localIsChangeTarget && operator.userUuid === localUserInfo?.userUuid && !it.languages?.target.isNullOrEmpty() && this.localIsChangeTarget) { + if (localIsChangeTarget && operator.userUuid == localUserInfo?.userUuid && !it.languages?.target.isNullOrEmpty() && this.localIsChangeTarget) { this.localIsChangeTarget = false settingsManager.currentSettingInfo.setTargetLanList( it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() @@ -607,7 +607,7 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { conversionManager.addTargetLanguageChangeMessage(true, userInfo, useManager) } //判断是否修改了声源语言 - if (!it.languages?.source.isNullOrEmpty() && it.languages?.source != settingsManager.currentSettingInfo.getSourceLan().value || localIsChangeSourceLan && operator.userUuid === localUserInfo?.userUuid) { + if (!it.languages?.source.isNullOrEmpty() && it.languages?.source != settingsManager.currentSettingInfo.getSourceLan().value || localIsChangeSourceLan && operator.userUuid == localUserInfo?.userUuid) { this.localIsChangeSourceLan = false val sourceEnum = RttLanguageEnum.values().find { item -> item.value == it.languages?.source } if (sourceEnum != null) { @@ -857,10 +857,10 @@ class RttRecordItem { */ fun getShowText(): Array { //是否设置了翻译语言 - val enableTargetLan = "" !== currentTargetLan?.value + val enableTargetLan = "" != currentTargetLan?.value //语言显示 val leve2Text = - if (showDoubleLan && enableTargetLan && currentTargetLan != null && sourceLan != null && sourceLan!!.value !== currentTargetLan?.value) targetText else null + if (showDoubleLan && enableTargetLan && currentTargetLan != null && sourceLan != null && sourceLan!!.value != currentTargetLan?.value) targetText else null val leve1Text = if (!showDoubleLan && enableTargetLan && !targetText.isNullOrEmpty()) targetText else sourceText return arrayOf(leve1Text, leve2Text) } @@ -1393,9 +1393,16 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana */ fun initOpenConversion(state: Boolean) { openSuccess = state - if (openSuccess) { - listener.conversionViewReset() - } + rttOptionsManager.rttOptions.runOnUiThread{ + listener.conversionViewReset() + listener.conversionStateChange(openSuccess) + rttConversionDialog.changeShowButton(openSuccess) + if(openSuccess){ + rttOptionsManager.startExperience() + }else{ + rttOptionsManager.stopExperience() + } + } } /** @@ -1431,7 +1438,7 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open else R.string.fcr_dialog_rtt_toast_conversion_state_close }) }" - if (!rttOptionsManager.isOpenConversion() && toOpen) { + if (!rttOptionsManager.isOpenConversion() && toOpen && userInfo.userUuid != localUserInfo?.userUuid) { rttOptionsManager.rttOptions.runOnUiThread{ AgoraUIDialogBuilder(rttOptionsManager.rttOptions.getActivityContext()).title(appContext.resources.getString(R.string.fcr_dialog_rtt_conversion)) .message(textContent).messagePaddingHorizontal(appContext.resources.getDimensionPixelOffset(R.dimen.dp_10)) From 25b4ca5f45d75fcce253f7ae7fb2f57e976d2991 Mon Sep 17 00:00:00 2001 From: LorenWang Date: Tue, 3 Dec 2024 16:43:32 +0800 Subject: [PATCH 31/32] =?UTF-8?q?Rtt=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/FcrRttToolBoxComponent.kt | 7 + .../dialog/AgoraUIRttSettingDialog.kt | 3 +- .../online/helper/FcrRttOptionsManager.kt | 121 +++++++++++------- .../options/AgoraEduOptionsComponent.kt | 3 + .../online/widget/rtt/FcrRttToolBoxWidget.kt | 70 +++++++--- .../layout/activity_agora_online_class.xml | 18 +-- 6 files changed, 149 insertions(+), 73 deletions(-) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt index 422503e30..9de631c25 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/FcrRttToolBoxComponent.kt @@ -74,4 +74,11 @@ class FcrRttToolBoxComponent : AbsAgoraEduComponent { eduContext?.widgetContext()?.addWidgetActiveObserver(widgetActiveObserver, WIDGETS_RTT_ID) eduContext?.widgetContext()?.setWidgetActive(WIDGETS_RTT_ID, AgoraWidgetRoomPropsUpdateReq(state = 1)) } + + /** + * 如果体验结束的话,那么则隐藏弹窗view + */ + fun resetShowDialogIfEnd() { + widget?.resetShowDialogIfEnd() + } } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt index bb3cf00a8..aeda2ee03 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/component/dialog/AgoraUIRttSettingDialog.kt @@ -126,7 +126,8 @@ private class SelectListAdapter(var context: Context, var dataList: MutableList< } else { currentSelect } - dataList.find { it.code == lastSelect!!.code }?.select = false + dataList.forEach { it.select = false } +// dataList.find { it.code == lastSelect!!.code }?.select = false item.select = true currentSelect = item onSelectChangedListener?.onChanged(currentSelect!!) diff --git a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt index 0363eb24a..d539d1a7e 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/helper/FcrRttOptionsManager.kt @@ -141,7 +141,8 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { super.onRoomPropertiesUpdated(properties, cause, operator) LogX.i(TAG, "onRoomPropertiesUpdated properties=${GsonUtil.toJson(properties)}, cause=${ cause?.let { GsonUtil.toJson(it) } - }, operator=${operator?.let { GsonUtil.toJson(it) }}") + }, operator=${operator?.let { GsonUtil.toJson(it) }}" + ) } } @@ -306,8 +307,8 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { /** * 检测用户是否在分组中 */ - fun checkUserInSubRoom():Boolean{ - return eduCore?.eduContextPool()?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.GROUPING_CLASS.value + fun checkUserInSubRoom(): Boolean { + return eduCore?.eduContextPool()?.roomContext()?.getRoomInfo()?.roomType?.value == RoomType.GROUPING_CLASS.value } /** @@ -533,7 +534,8 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { } isSendRequesting = true val body = FcrRttChangeOptionsData.formatUseData(openRttConversion, openRttSubtitles, settingsManager.currentSettingInfo.getSourceLan().value, - if (openRttConversion || openRttSubtitles) settingsManager.currentSettingInfo.getTargetLanList().filter { "" != it.value }.map { it.value }.distinct().toTypedArray() else arrayOf()) + if (openRttConversion || openRttSubtitles) settingsManager.currentSettingInfo.getTargetLanList().filter { "" != it.value }.map { it.value }.distinct().toTypedArray() else arrayOf() + ) val call = AppRetrofitManager.instance().getService(IRttOptionsService::class.java) .buildTokens(eduCore?.config?.appId, eduCore?.config?.roomUuid, if (openRttConversion || openRttSubtitles) 1 else 0, body) AppRetrofitManager.exc(call, object : HttpCallback>() { @@ -603,7 +605,8 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { this.localIsChangeTarget = false settingsManager.currentSettingInfo.setTargetLanList( it.languages?.target?.map { mapItem -> RttLanguageEnum.values().find { it.value == mapItem }!! }?.toTypedArray() - ?: arrayOf()) + ?: arrayOf() + ) conversionManager.addTargetLanguageChangeMessage(true, userInfo, useManager) } //判断是否修改了声源语言 @@ -690,6 +693,12 @@ class RttOptionsManager(internal val rttOptions: IRttOptions) { }) " } + /** + * 体验时间是否用完 + */ + fun experienceReduceTimeZero(): Boolean { + return useManager.experienceReduceTimeZero() + } } /** @@ -878,13 +887,17 @@ class RttRecordItem { class RttSettingInfo( var rttOptionsManager: RttOptionsManager, val sourceListLan: ArrayList = arrayListOf(RttLanguageEnum.ZH_CN, RttLanguageEnum.EN_US, RttLanguageEnum.JA_JP), - val targetListLan: ArrayList = arrayListOf(RttLanguageEnum.NONE, RttLanguageEnum.ZH_CN, RttLanguageEnum.EN_US, - RttLanguageEnum.JA_JP), + val targetListLan: ArrayList = arrayListOf( + RttLanguageEnum.NONE, RttLanguageEnum.ZH_CN, RttLanguageEnum.EN_US, + RttLanguageEnum.JA_JP + ), private var sourceLan: RttLanguageEnum = RttLanguageEnum.ZH_CN, private var targetLanList: ArrayList = arrayListOf(RttLanguageEnum.NONE), - private var targetLan:RttLanguageEnum =RttLanguageEnum.NONE, - private var showDoubleLan: Boolean = SpUtil.getBoolean(rttOptionsManager.rttOptions.getApplicationContext(), - "${rttOptionsManager.eduCore?.config?.roomUuid ?: ""}_showDoubleLan", false), + private var targetLan: RttLanguageEnum = RttLanguageEnum.NONE, + private var showDoubleLan: Boolean = SpUtil.getBoolean( + rttOptionsManager.rttOptions.getApplicationContext(), + "${rttOptionsManager.eduCore?.config?.roomUuid ?: ""}_showDoubleLan", false + ), ) { init { val locale: Locale = MultiLanguageUtil.getAppLocale(rttOptionsManager.rttOptions.getActivityContext()) @@ -893,16 +906,24 @@ class RttSettingInfo( val sysCountry = locale.country // CN //默认源语言设置 sourceLan = - if (sysLanguage.equals(Locale.SIMPLIFIED_CHINESE.language, ignoreCase = true) && sysCountry.equals(Locale.SIMPLIFIED_CHINESE.country, - ignoreCase = true)) { + if (sysLanguage.equals(Locale.SIMPLIFIED_CHINESE.language, ignoreCase = true) && sysCountry.equals( + Locale.SIMPLIFIED_CHINESE.country, + ignoreCase = true + ) + ) { RttLanguageEnum.ZH_CN } else if (sysLanguage.equals(Locale.US.language, ignoreCase = true) && sysCountry.equals(Locale.US.country, ignoreCase = true)) { RttLanguageEnum.EN_US - } else if (Locale.getDefault().language.equals(Locale.SIMPLIFIED_CHINESE.language, - ignoreCase = true) && Locale.getDefault().country.equals(Locale.SIMPLIFIED_CHINESE.country, ignoreCase = true)) { + } else if (Locale.getDefault().language.equals( + Locale.SIMPLIFIED_CHINESE.language, + ignoreCase = true + ) && Locale.getDefault().country.equals(Locale.SIMPLIFIED_CHINESE.country, ignoreCase = true) + ) { RttLanguageEnum.ZH_CN } else if (Locale.getDefault().language.equals(Locale.US.language, ignoreCase = true) && Locale.getDefault().country.equals( - Locale.US.country, ignoreCase = true)) { + Locale.US.country, ignoreCase = true + ) + ) { RttLanguageEnum.EN_US } else { RttLanguageEnum.EN_US @@ -928,8 +949,10 @@ class RttSettingInfo( fun setShowDoubleLan(show: Boolean) { this.showDoubleLan = show - SpUtil.saveBoolean(rttOptionsManager.rttOptions.getApplicationContext(), "${rttOptionsManager.eduCore?.config?.roomUuid ?: ""}_showDoubleLan", - show) + SpUtil.saveBoolean( + rttOptionsManager.rttOptions.getApplicationContext(), "${rttOptionsManager.eduCore?.config?.roomUuid ?: ""}_showDoubleLan", + show + ) } } @@ -1242,7 +1265,7 @@ private class RttSubtitlesManager(private val rttOptionsManager: RttOptionsManag */ fun setShowCurrentData(recordItem: RttRecordItem?, currentSettingInfo: RttSettingInfo) { if (isOpenSubtitles()) { - listener.subtitlesViewReset(true) +// listener.subtitlesViewReset(true) //清除消息 this.clearHandlerMsg() @@ -1393,16 +1416,16 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana */ fun initOpenConversion(state: Boolean) { openSuccess = state - rttOptionsManager.rttOptions.runOnUiThread{ - listener.conversionViewReset() - listener.conversionStateChange(openSuccess) - rttConversionDialog.changeShowButton(openSuccess) - if(openSuccess){ - rttOptionsManager.startExperience() - }else{ - rttOptionsManager.stopExperience() - } - } + rttOptionsManager.rttOptions.runOnUiThread { + listener.conversionViewReset() + listener.conversionStateChange(openSuccess) + rttConversionDialog.changeShowButton(openSuccess) + if (openSuccess) { + rttOptionsManager.startExperience() + } else { + rttOptionsManager.stopExperience() + } + } } /** @@ -1418,7 +1441,8 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana fun updateShowList(list: List) { try { rttConversionDialog.updateShowList(list) - }catch (ignore:Exception){} + } catch (ignore: Exception) { + } } /** @@ -1432,14 +1456,16 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana .getString(if (toOpen) R.string.fcr_dialog_rtt_text_conversion_state_open else R.string.fcr_dialog_rtt_text_conversion_state_close) }" val toastContent = "${rttOptionsManager.formatRoleName(userInfo, localUserInfo)}${ - rttOptionsManager.rttOptions.getApplicationContext().getString(if (userInfo.userUuid == localUserInfo?.userUuid) { - if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open_me_show else R.string.fcr_dialog_rtt_toast_conversion_state_close_me_show - } else { - if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open else R.string.fcr_dialog_rtt_toast_conversion_state_close - }) + rttOptionsManager.rttOptions.getApplicationContext().getString( + if (userInfo.userUuid == localUserInfo?.userUuid) { + if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open_me_show else R.string.fcr_dialog_rtt_toast_conversion_state_close_me_show + } else { + if (toOpen) R.string.fcr_dialog_rtt_toast_conversion_state_open else R.string.fcr_dialog_rtt_toast_conversion_state_close + } + ) }" if (!rttOptionsManager.isOpenConversion() && toOpen && userInfo.userUuid != localUserInfo?.userUuid) { - rttOptionsManager.rttOptions.runOnUiThread{ + rttOptionsManager.rttOptions.runOnUiThread { AgoraUIDialogBuilder(rttOptionsManager.rttOptions.getActivityContext()).title(appContext.resources.getString(R.string.fcr_dialog_rtt_conversion)) .message(textContent).messagePaddingHorizontal(appContext.resources.getDimensionPixelOffset(R.dimen.dp_10)) .negativeText(appContext.resources.getString(R.string.fcr_dialog_rtt_oher_change_source_hint_cancel)) @@ -1477,8 +1503,10 @@ private class RttConversionManager(private val rttOptionsManager: RttOptionsMana }${rttOptionsManager.rttOptions.getApplicationContext().getString(R.string.fcr_dialog_rtt_text_change_source_language)}" val languageText = rttOptionsManager.rttOptions.getApplicationContext().getString(sourceEnum.textRes) val showContent = SpannableString("$useText${languageText}") - showContent.setSpan(ForegroundColorSpan(ContextCompat.getColor(rttOptionsManager.rttOptions.getApplicationContext(), R.color.fcr_blue_4262)), - showContent.lastIndexOf(languageText), showContent.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) + showContent.setSpan( + ForegroundColorSpan(ContextCompat.getColor(rttOptionsManager.rttOptions.getApplicationContext(), R.color.fcr_blue_4262)), + showContent.lastIndexOf(languageText), showContent.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ) AgoraUIToast.showDefaultToast(rttOptionsManager.rttOptions.getApplicationContext(), showContent) //通知处理 useManager.disposeDataChangeSourceLanguage(userInfo, showContent.toString()) @@ -1680,8 +1708,6 @@ private class RttUseManager( } else { paramsData = allRecordList.findLast { it.uid == rttMsgData.uid }?.apply { uuid = UUID.randomUUID().toString() - currentTargetLan = settingInfo.getTargetLan() - showDoubleLan = settingInfo.isShowDoubleLan() sourceText = sourceTextStr.toString() time = if (final) if (rttMsgData.time == 0L) rttMsgData.time else System.currentTimeMillis() else rttMsgData.time isFinal = final @@ -1701,10 +1727,10 @@ private class RttUseManager( LogX.i(TAG, "Translation:$lastItemByUid$$$$$$$$$text") transTextStr.append(text) } - tranList.find { transItem.lang == it.language?.value }.let {find-> - if(find != null){ + tranList.find { transItem.lang == it.language?.value }.let { find -> + if (find != null) { find.text = transTextStr.toString() - }else{ + } else { tranList.add(RttRecordItemTran().apply { language = RttLanguageEnum.values().find { it.value == transItem.lang } text = transTextStr.toString() @@ -1715,11 +1741,10 @@ private class RttUseManager( } paramsData = allRecordList.findLast { it.uid == lastItemByUid }?.apply { - currentTargetLan = settingInfo.getTargetLan() uuid = UUID.randomUUID().toString() showDoubleLan = settingInfo.isShowDoubleLan() targetInfo = tranList - targetText = tranList.find { item-> (item.language?.value ?:"") == currentTargetLan?.value }?.text + targetText = tranList.find { item -> (item.language?.value ?: "") == currentTargetLan?.value }?.text } } } @@ -1737,7 +1762,6 @@ private class RttUseManager( return paramsData } - /** * 格式化所有的用户信息 */ @@ -1782,6 +1806,13 @@ private class RttUseManager( lastRecord?.isFinal = true } + /** + * 体验时间是否用完 + */ + fun experienceReduceTimeZero(): Boolean { + return this.rttExperienceReduceTime <= 0 + } + } /** diff --git a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt index a78754640..4f9db43e5 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/options/AgoraEduOptionsComponent.kt @@ -42,6 +42,7 @@ import io.agora.online.component.toast.AgoraUIToast import io.agora.online.component.whiteboard.data.AgoraEduApplianceData import io.agora.online.config.FcrUIConfig import io.agora.online.databinding.FcrOnlineEduOptionsComponentBinding +import io.agora.online.helper.RttOptionsManager import io.agora.online.impl.chat.ChatPopupWidgetListener import io.agora.online.impl.whiteboard.bean.AgoraBoardGrantData import io.agora.online.impl.whiteboard.bean.AgoraBoardInteractionPacket @@ -464,6 +465,8 @@ class AgoraEduOptionsComponent : AbsAgoraEduConfigComponent, IWhite } private fun showItem(item: View?) { + //如果体验结束的话,那么则隐藏弹窗view,做收口处理 + rttToolBoxWidget?.resetShowDialogIfEnd() itemContainer.removeAllViews() itemContainer.addView(item) } diff --git a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt index c7f830723..460b1f79b 100644 --- a/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt +++ b/AgoraCloudScene/src/main/java/io/agora/online/widget/rtt/FcrRttToolBoxWidget.kt @@ -67,6 +67,13 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { contentView?.resetStatus() } + /** + * 如果体验结束的话,那么则隐藏弹窗view + */ + fun resetShowDialogIfEnd() { + contentView?.resetShowDialogIfEnd() + } + internal inner class AgoraRttToolBoxWidgetContent( val container: ViewGroup, agoraUIProvider: IAgoraUIProvider, @@ -96,8 +103,8 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { super.subtitlesStateChange(toOpen) agoraEduOptionsComponent?.hiddenRtt() resetStatus() - if(!toOpen){ - AgoraUIToast.showDefaultToast(container.context,container.context.getString(R.string.fcr_dialog_rtt_subtitles_myself_close_finish)) + if (!toOpen) { + AgoraUIToast.showDefaultToast(container.context, container.context.getString(R.string.fcr_dialog_rtt_subtitles_myself_close_finish)) } } @@ -114,17 +121,26 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { override fun audioStateNotAllowUse() { super.audioStateNotAllowUse() - subtitleView?.resetShowPosition() - subtitleView?.visibility = View.VISIBLE - subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, - text = container.context.getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use)) + if (rttOptionsManager.experienceReduceTimeZero()) { + subtitleView?.resetShowPosition() + subtitleView?.visibility = View.GONE + } else { + subtitleView?.resetShowPosition() + subtitleView?.visibility = View.VISIBLE + subtitleView?.setShowStatusInfo( + showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_time_limit_status_not_allow_use) + ) + } } override fun audioStateNoSpeaking() { super.audioStateNoSpeaking() subtitleView?.visibility = View.VISIBLE - subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, - text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking)) + subtitleView?.setShowStatusInfo( + showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_no_one_speaking) + ) } override fun audioStateNoSpeakingMoreTime() { @@ -136,21 +152,27 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { super.audioStateOpening() subtitleView?.resetShowPosition() subtitleView?.visibility = View.VISIBLE - subtitleView?.setShowStatusInfo(showProgress = true, showIcon = false, - text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening)) + subtitleView?.setShowStatusInfo( + showProgress = true, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening) + ) } override fun audioStateShowSettingHint() { super.audioStateShowSettingHint() - subtitleView?.setShowStatusInfo(showProgress = false, showIcon = false, - text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening_success_hint)) + subtitleView?.setShowStatusInfo( + showProgress = false, showIcon = false, + text = container.context.getString(R.string.fcr_dialog_rtt_dialog_subtitles_status_opening_success_hint) + ) } override fun audioStateSpeaking() { super.audioStateSpeaking() subtitleView?.visibility = View.VISIBLE - subtitleView?.setShowStatusInfo(showProgress = false, showIcon = true, - text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_listening)) + subtitleView?.setShowStatusInfo( + showProgress = false, showIcon = true, + text = container.context.getString(R.string.fcr_dialog_rtt_subtitles_text_listening) + ) } override fun onMessageChange(currentData: RttRecordItem?) { @@ -210,11 +232,13 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { runOnUiThread { val experienceReduceTime = rttOptionsManager.getExperienceReduceTime() binding.agoraRttDialogSubtitlesIcon.isActivated = rttOptionsManager.isOpenSubtitles() - binding.agoraRttDialogSubtitlesText.setText(if(rttOptionsManager.isOpenSubtitles())R.string.fcr_dialog_rtt_subtitles_close else R.string.fcr_dialog_rtt_subtitles) + binding.agoraRttDialogSubtitlesText.setText(if (rttOptionsManager.isOpenSubtitles()) R.string.fcr_dialog_rtt_subtitles_close else R.string.fcr_dialog_rtt_subtitles) binding.agoraRttDialogConversionIcon.isActivated = rttOptionsManager.isOpenConversion() binding.fcrOnlineEduRttConversionDialogTimeLimitHint.text = if (experienceReduceTime > 0) { - MessageFormat.format(container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), - rttOptionsManager.getExperienceDefaultTime() / 60000) + MessageFormat.format( + container.context.getString(R.string.fcr_dialog_rtt_time_limit_time), + rttOptionsManager.getExperienceDefaultTime() / 60000 + ) } else { container.context.getString(R.string.fcr_dialog_rtt_time_limit_end) } @@ -232,6 +256,16 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { binding.agoraRttDialogConversion.alpha = if (allowUse) 1F else 0.8F } + /** + * 如果体验结束的话,那么则隐藏弹窗view + */ + fun resetShowDialogIfEnd() { + if (!rttOptionsManager.isAllowUseRtt() || rttOptionsManager.experienceReduceTimeZero()) { + subtitleView?.visibility = View.GONE + } + } + + fun dispose() { container.removeView(binding.root) rttOptionsManager.removeListener(listener) @@ -271,4 +305,4 @@ class FcrRttToolBoxWidget : AgoraBaseWidget() { rttOptionsManager.onWidgetRoomPropertiesUpdated(properties, operator) } } -} \ No newline at end of file +} diff --git a/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml b/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml index a32e2ac30..54101b56b 100644 --- a/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml +++ b/AgoraCloudScene/src/main/res/layout/activity_agora_online_class.xml @@ -119,6 +119,14 @@ android:text="@string/fcr_rtt_content_conversion_status_tip"/> + + - - - \ No newline at end of file + From 0f03b6cd4797939d3fb31a314ae903a579aca495 Mon Sep 17 00:00:00 2001 From: LorenWang Date: Tue, 3 Dec 2024 16:44:30 +0800 Subject: [PATCH 32/32] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/agora/education/EduApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/agora/education/EduApplication.java b/app/src/main/java/io/agora/education/EduApplication.java index c198c88a6..0b1262d51 100644 --- a/app/src/main/java/io/agora/education/EduApplication.java +++ b/app/src/main/java/io/agora/education/EduApplication.java @@ -54,7 +54,7 @@ public void onCreate() { // } // TODO test XXX - setTestDev(); +// setTestDev(); setDevHost(); setDarkMode();