From 02f60403934afae4e6ce4f8a3a737d8b6d9ee63c Mon Sep 17 00:00:00 2001 From: Salvatore Di Cara Date: Mon, 10 Nov 2025 15:06:17 +0000 Subject: [PATCH 01/14] Update iOS sample voice app to pass TestFlight validation --- contact-center/android-voice/app/build.gradle | 5 +- .../voicesampleapp/core/VoiceClientManager.kt | 2 +- contact-center/ios-voice/Podfile | 4 +- contact-center/ios-voice/Podfile.lock | 8 +- .../AppIcon.appiconset/Contents.json | 107 +++++++++++++++++- .../AppIcon.appiconset/icon_1024x1024.png | Bin 0 -> 85777 bytes .../AppIcon.appiconset/icon_120x120.png | Bin 0 -> 3823 bytes .../AppIcon.appiconset/icon_152x152.png | Bin 0 -> 4995 bytes .../AppIcon.appiconset/icon_167x167.png | Bin 0 -> 5645 bytes .../AppIcon.appiconset/icon_180x180.png | Bin 0 -> 6204 bytes .../AppIcon.appiconset/icon_29x29.png | Bin 0 -> 870 bytes .../AppIcon.appiconset/icon_40x40.png | Bin 0 -> 1231 bytes .../AppIcon.appiconset/icon_58x58.png | Bin 0 -> 1693 bytes .../AppIcon.appiconset/icon_60x60.png | Bin 0 -> 1833 bytes .../AppIcon.appiconset/icon_76x76.png | Bin 0 -> 2273 bytes .../AppIcon.appiconset/icon_80x80.png | Bin 0 -> 2473 bytes .../AppIcon.appiconset/icon_87x87.png | Bin 0 -> 2611 bytes .../AppIcon.appiconset/vonage_logo.png | Bin 0 -> 2274 bytes .../VonageSDKClientVOIPExample/Info.plist | 13 +++ 19 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_120x120.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_152x152.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_167x167.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_180x180.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_29x29.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_40x40.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_58x58.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_60x60.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_76x76.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_80x80.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_87x87.png create mode 100644 contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/vonage_logo.png diff --git a/contact-center/android-voice/app/build.gradle b/contact-center/android-voice/app/build.gradle index 087f075..ed9afa4 100644 --- a/contact-center/android-voice/app/build.gradle +++ b/contact-center/android-voice/app/build.gradle @@ -63,7 +63,10 @@ dependencies { implementation 'com.google.firebase:firebase-messaging-ktx' // Vonage Client SDK - implementation("com.vonage:client-sdk:2.1.2") + implementation("com.vonage:client-sdk:2.1.3") + + // Vonage Video SDK + //implementation("com.vonage:client-sdk-video:2.31.0") // Retrofit + Moshi (HTTP Client) def retrofit_version = "3.0.0" diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/core/VoiceClientManager.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/core/VoiceClientManager.kt index 437cf36..f540ecd 100644 --- a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/core/VoiceClientManager.kt +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/core/VoiceClientManager.kt @@ -39,7 +39,7 @@ class VoiceClientManager(private val context: Context) { private fun initClient(){ val config = VGClientInitConfig(LoggingLevel.Info) - config.rtcStatsTelemetry = false + //config.rtcStatsTelemetry = false client = VoiceClient(context, config) } diff --git a/contact-center/ios-voice/Podfile b/contact-center/ios-voice/Podfile index d2d6f2c..4e45c1e 100644 --- a/contact-center/ios-voice/Podfile +++ b/contact-center/ios-voice/Podfile @@ -1,10 +1,10 @@ # Uncomment the next line to define a global platform for your project -platform :ios, '13.0' +platform :ios, '15.0' target 'VonageSDKClientVOIPExample' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for VonageSDKClientVOIPExample - pod 'VonageClientSDKVoice', '2.1.2' + pod 'VonageClientSDKVoice', '2.1.3' end diff --git a/contact-center/ios-voice/Podfile.lock b/contact-center/ios-voice/Podfile.lock index 7a78c52..ff1c360 100644 --- a/contact-center/ios-voice/Podfile.lock +++ b/contact-center/ios-voice/Podfile.lock @@ -1,10 +1,10 @@ PODS: - - VonageClientSDKVoice (2.1.2): + - VonageClientSDKVoice (2.1.3): - VonageWebRTC (~> 99.14.95) - VonageWebRTC (99.14.100) DEPENDENCIES: - - VonageClientSDKVoice (= 2.1.2) + - VonageClientSDKVoice (= 2.1.3) SPEC REPOS: trunk: @@ -12,9 +12,9 @@ SPEC REPOS: - VonageWebRTC SPEC CHECKSUMS: - VonageClientSDKVoice: 9b5711d2209c72d6351d856343c30b4a3b10f81c + VonageClientSDKVoice: 08293daf1aa5c4f0adacbb69fa8cb83c6487e421 VonageWebRTC: c6c80961496762ac0b94aedf3ba15e91e9e22088 -PODFILE CHECKSUM: 2c0fc8a7028df1ccfeff6dbb7146a183dbd05199 +PODFILE CHECKSUM: 39f71c320e1edbc01f687879899bc4d045e7cda1 COCOAPODS: 1.16.2 diff --git a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/Contents.json index 13613e3..12749dc 100644 --- a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,8 +1,111 @@ { "images" : [ { - "idiom" : "universal", - "platform" : "ios", + "filename" : "icon_40x40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "icon_60x60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "icon_58x58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "icon_87x87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "icon_80x80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "icon_120x120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "icon_120x120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "icon_180x180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "icon_29x29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "icon_40x40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "icon_29x29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "icon_58x58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "icon_40x40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "icon_80x80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "icon_76x76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "icon_152x152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "icon_167x167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "icon_1024x1024.png", + "idiom" : "ios-marketing", + "scale" : "1x", "size" : "1024x1024" } ], diff --git a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png new file mode 100644 index 0000000000000000000000000000000000000000..119e734661030229bd803435c8a7eb66369582ca GIT binary patch literal 85777 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 literal 0 HcmV?d00001 diff --git a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_167x167.png b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_167x167.png new file mode 100644 index 0000000000000000000000000000000000000000..97ffc4c34439e545b5c2c4b56295a989d25618fd GIT binary patch literal 5645 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 L3e%{9I7I&sA=2D^ literal 0 HcmV?d00001 diff --git a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_180x180.png b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..fae1926842de67dcd1c2c62531cb40beeb1d91dd GIT binary patch literal 6204 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 Wvpx4#6$$;?Q4f-FA`O`H3 literal 0 HcmV?d00001 diff --git a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_29x29.png b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_29x29.png new file mode 100644 index 0000000000000000000000000000000000000000..a3e41f4834846691d82eae9c02c9f5f551825698 GIT binary patch literal 870 zcmeAS@N?(olHy`uVBq!ia0vp^vLMXC1SD^M{15@87>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSb^$Yj4N`coS}c%(f$4&$i(`lf z@78IC9#w%7M@_wbbc{CWZWOvDlO-TtD6+;eqp*4@AthfpTEl9-afScLfgNFn~r?* z=g)ut?j0X*X+DEsuiNuy&!*g8AGFd%N$}J5+i$mSHNE=w?b?eOO9DH3dwFyEK0W*X zyRs%JiOEOw)4~7_;aC58ld`g2y?f`Uxcy>APk;aCAJ3ktZIXTW^y$)r2@i@lM))X} zXJ>b_f32$B8~48GQEWrFvA-n=>UL-|g5d3nF(lYIorcK?0*HkaYc*RQ3CiH+_ueC)2fVxD~d zX*2Ukc|}c)O+&PX$PRg{$tNfL@4lV2_1W|1C%HCm+2XPoz6egAH##}yb8STJe1z4+E% z=f(|Z)2b^gA4--Odc<@bJ?bhW@J?{o`6|PSe#<8wtd8W8k&_b&u?aSsImM=V2R}c* z#T0XMbDQSP?}~PIa3^kzkdv3+*Er9mzJt-tOyq@&(H67UKqSm zWcim@@jKqXf4?)P?;mS}g_YH=&6_WCPjb8R;_%(=4|{8>s;UYK8cvB%H83`Aj(3{V uy06a8qC-1R^VR(HDK8vjRDaI5`Oh$EM=h7{?53}vH16r@=d#Wzp$P#0+kkKY literal 0 HcmV?d00001 diff --git a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_40x40.png b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_40x40.png new file mode 100644 index 0000000000000000000000000000000000000000..a94cda1d8e57af0a44d2e5cdcfcc36ed3b0c859d GIT binary patch literal 1231 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 literal 0 HcmV?d00001 diff --git a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_60x60.png b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_60x60.png new file mode 100644 index 0000000000000000000000000000000000000000..80ca648b891b4b0e9b7bdf99ef28372ee4d89adf GIT binary patch literal 1833 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 literal 0 HcmV?d00001 diff --git a/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_87x87.png b/contact-center/ios-voice/VonageSDKClientVOIPExample/Assets.xcassets/AppIcon.appiconset/icon_87x87.png new file mode 100644 index 0000000000000000000000000000000000000000..4ff9fa0beb03dfc753532ebf6b2e4d436ecfdb5b GIT binary patch literal 2611 zcmV-33e5G1P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91SD*s`1ONa40RR91R{#J20Hadu6aWATok>JNRCodHoN1^|T@=T;u6fQp zcg-2gObBI4h!3O)g+gRVN~DM=q|7s!A`u@9Wy(CuOc^tendjmC-L`w2d!93_b)MYxwWA_gQ=GeQwS_fBxk7-(M;Jdj!$}$VMR>g^Yoa?F;Ehq<;SVSvx`I z&6_thH8lgrto8f-`}dzef68^j_LP*A)B^_&j2JOO-VDmFUArz{zFgHLF~slQz3bAY z%cDn+RN@vcT$tn2r%$zN)lxMpSg_#r>(@ast6gi?u2mTVam|`FKYaL*QlLPA;lqb> zpHqAR+^SWpwDRIj|KrCG^F*|KHjKtNf#bm^y0pJpjv z*uQ^2BV1)gsZymLKYolA>CMWNDbufCKMKMNuwT4*v1ZL0A$?-V7A#os`}c2jlZ(UZ z^5x5?)0_UhapOk1V~EEEl#HnN@84$;cerxpO8)%$Rr*7J`SN9>|6dfi>C>l=oQr{c z{rdG;1ck9<$7+@D(W3{x<3!=$!GpPSiCx=FC@|aAs8K`3F<-uX=g*(Fb4+k~%a$!Fg(0q3vEr*&uk3neT1^!$TzK%{ z!O+RCuV25WN1#c%e*OAIuWE%47%-qji4vJw*&(6)@Zm#Z2`GfqB$P#p6uEQfj)PBP zZjT*1mOFQDm9_)~w{G2XsF%qXV28K#>eY*a@B%ExQL-YaWy_WkIGYJis4x>5k&ctt zfeN=5E?i)=3w^<8P^nTS$4HB08}pi*H*d18!ydV+I1cU;sY#~U(R$poXHOM(h?yO% z=7}jxoH$XdWb4+g2?!$9b4OwK?%i^LjXTtNNvo0-@EArwO@!OSD>^j`2#fA{X)qD70+Ob$8uyQ^2P z`tXeY(b=)iwk`E;K2j41d!`PDq+u_J^q|MXGhbF zq294$M>z_ni>6PX?k_d;(`nPDX_aZ*xG_sUe~AGo(0Kdy?OMUrs#Rly#9wfTr|^Wc zRvAWUpsZjN80y4@TG^{tuMU*0C9pPKi~=l3a%)j&)TmLQ9|kIC zCkPkLPM$p3&cv%MLV?|f3Kc47f!MWcmzUHK^~`WW7cgXKOXB19?b{Mres4+`nTxRYVIzod64a^xGf6mrZ z1q!s;ym_-$p>pNQv4dvG(-yP~d;|Ub`SVqRbSRK+HVIsW2`^BaIdi5;Cp$4&W-ab) z#y@}l+|DGhEP7L-hq*#*+t84K{kOgHhe&C~iWSO6=~FU?OmwXRadHrFc1pf&gDTN% z;SqNkW8{8EZl0IAL+8L--H0&kMvWSUauUrTV{IR9JD2xdhYlTtf{Zc{_wCy^(B?>j zFes3>5ymj6wQbwBNU4$eAH1kr3#3>-L6>j>@J zw-1*D4}$_g?%lhW2r3YTS+iz2P#4~^p~_6GMS&N&kiUa0Bnr&lO`A6L(UM2eq~6Al zAMe9F5`Bp4)T#6F<41>i!rPE2z}-LRfm$4>6e{5$<0K`qmKFtVq+}Nw9)-7W-&U3<_bGvTcx=4nu#+yoriIlEgyQa0=P9GHtFd8ys zh(l>^Lm!Lf>xLFBS~wW`K)@r2Ij?FrDMUG16waJE(&m;$Wcqrezp`4kQX4joyn?528^A#U5YZMaCuu3@w&z>75xS{m*7J7~}# zy8wCl^y$;QS-cE(tw&twNXf2P3{d#;<%@Nu0{?V9K4ll_RR%r-rK`b_l@Rkb@j9O3 zCB?Nq1}MPSY}=q#X#XT+X8S;k!kjsCT&>bp#s&rJtQ9AzwHg@uapT5mA&4@UXV)_} zD8O;_=+R_f2q^8`g=Ie5DOs|l76pJ4@((17848?PIDGhUpgdOsuNX9PE-*FVMUJ0S zpRU|ORK^U25VB1ZYczS;99a(voahcMF|wg9`-=FOUmG@TAeV`$LeAL0$Gsy*jug_1 z$RdGBWDN&1Lad#-b?X)`m+56%l6`@!B=1PMBbbimlW1*|0FUs3Cjs@Gn`SEw{US%c zR}I9?n>T0C6yjeCAlM*uEi0|mm#X24kq?ND1MK4HE>QJ&=yjS9sf40HdS3fkKhpsRIfTZ9CQBR&?8K{*2)GQ8CFTRg1fjqIOw}1; zAShO>7@zEh?p{^HM4-UhS?&ec&(*3`D^k)bOQIo;BMR(OvS-5)8)KO8FAHr(0eS6c ztkSm4NduvNLNXZg2|o7oU%@yoOD2+}h<~IEL0VcG`+EH6EzYBu|C1vW7Zb9$pg>T- z(Nj(tvG_BGhW&MA7BmN?{K?|?H1;UOPuKWyPTHC>ekO(BoQ*;{XxS)aqmVIp{sWf_ V=g|vL;YI)e002ovPDHLkV1h@J;Ew%F5>E=KTEp_V)JUHzP?&oT9}xaH#avxKtMD!G=_$T zoSd8_BqR+D4I3L9X=!OFC@5cFUlS7(i;IgH3T7Dq00 zT0Ho?)#lSBpTHG6(ekCVN4D^Ie#wXYMX9RoVEIzIC0n?7+zW8c$5`=FIwf29ZE?{N zS(W`z`BM5uBz!$Ku7HTwk>aJaP0pFy*UL`GCdL{z$7Bo7{#{2F<}gmYl-7`{c(WOo z;JUjw`Ot$-sy}ZoL`!oLCLep*>W3nDy{oGr=@L3HCe=ah(k-I(kAmb&={woNrN;zn zrn-m`4=L{VUcx$Aovjf0QhFp3zB?`+fQZKt;voaN>Bv7Lo46JrA98}89m6G%1^VnS z9(4?-?{5HH=NQ5Uyk*TF64Lr-4L7jHy}&W7;}^KYgSU7ool6|UI<`fgmm7eKH>u8> z@9_g{;tyZ(pkvtP9f0@1-*o#pP*psGe9H%IH?hG!=MbAf$YE!~2GPLnHrJFa)F(&z zptkR@n+ZAGWHp!p(ZIVMH$t}UrJH<^243&F6CsBuUg9C&uFo1dT1~_pfgZQmapLks2-Yh&6zBfv?> zVHd&%Y5SZYH6VxUp#dv^WRX_Kuns~FI}kQV+vh#c`fA16c8P?Uk3-1eueRb*ZQtq` z)`33ltQUU(khaez<9CfdRexwl2+uq|P8qvaUZfwcakp?oRbk;VR!e_uil z=f4^Ppr9}Dbc`rUsu2FYG#^(5OjQV9A#_@66E-LtSeh+~+{E9i@})F! z@xsxkpc`vLhrmzq-yq9ZH`WHSfd?%vP)UeG=*1U1$8f+OX(mSlgKD6EO2}cwSR2#! zgTh0I9M+4qnL36`@LS9`Fm?>#y!Lc@`o|M9~fE*r9*dT4MRTIJTqgEJnYp$`B<94XH++K+kd}SpoDH!78)(BJUN=Bc&R3VAaYplu^~rBw`ktS>t@g9FGXUu5wp;+Q*5ShBcV@_X>Zr}FEi6jl0}EM(69keR<@u-RUl<+m{^`?IZ6zw zpW)hmfgEyJ5(;KimuVl|KN%7V;_4_ElVAHILBH0QEHO#Fm{`}y_iBBKszAXQ>lRhL zUyQ)_CiIL$U-%w!SmJvldd9x;xQ84Ts0v2UIJEogtO%5lP>h~&=m5~!OiGfpkLVeX zE)JbqUl5dK^o&c_`wmY|lC6v4T0WBtI>=$cQBDjtw`PW|Az{f;j_4UH-+mdbX46o#{prVV^ z8pR9@nd2oSENG~J47Rv=eG76}kf=vyQw0s~nPZ{#1z~5D%NaC$X^wflkU&+C9Jbim zr*m>xkhzXB)???_K2{Rfl-b<2ar-{ZP7Fcc7GiUo$L;&@P;phkZ0>;3QZ<|lGL(qT z9Xk|sjxC;og|;wsu&WSYB9U$do*O{3K<9ZqN?<&9@Q`c~klPeK=v)9&tSPS%iv;TK z9BZ)2Kw5T28*DP5(w}I9O#&9GkoVGy1Mz%Uz+mHGp6?h9Hqw5=W5SC%SUz{iSml8L zLnUnLV6DN(i%5n|3c^1*&pFirDd``l&CRxOgf=%kNPPwms)8M)wiAddS}!N{L+hMHIk;s^sVsNqFaBJdEf zb`Z{WO&Zu+g%)g~|w1ojzP<%J(GR&gh)Mt;3yu`KkpTE=R^Vt>0W!nYiCMxH%o) w@v^qIIn6^@5mP}yK|w)5K|w)5L4h290k52K_J1Z%xBvhE07*qoM6N<$f~$(API_LOGIN_URL) API_REFRESH_URL $(API_REFRESH_URL) + CFBundleIconName + AppIcon UIApplicationSceneManifest UIApplicationSupportsMultipleScenes @@ -33,6 +35,17 @@ remote-notification voip + BGTaskSchedulerPermittedIdentifiers + + com.nexmo.nexmoclient-push-notifications-test-app-ios.refresh + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIUserInterfaceStyle Light From dcfaab75dc0c9ac903611cb8ceb379958ed5bc50 Mon Sep 17 00:00:00 2001 From: Salvatore Di Cara Date: Mon, 10 Nov 2025 22:22:32 +0000 Subject: [PATCH 02/14] Wip --- .vscode/settings.json | 3 + contact-center/android-voice/app/build.gradle | 39 +- .../voicesampleapp/activities/CallActivity.kt | 524 ++++++++++++------ .../activities/LoginActivity.kt | 198 ++++++- .../voicesampleapp/activities/MainActivity.kt | 211 +++++-- .../activities/fragments/DialerFragment.kt | 264 ++++++--- .../voicesampleapp/core/VoiceClientManager.kt | 62 +-- .../voicesampleapp/telecom/CallConnection.kt | 56 +- .../vonage/voicesampleapp/ui/theme/Color.kt | 36 ++ .../vonage/voicesampleapp/ui/theme/Theme.kt | 68 +++ .../vonage/voicesampleapp/ui/theme/Type.kt | 59 ++ .../voicesampleapp/utils/NavigationUtils.kt | 81 +-- .../app/src/main/res/layout/activity_call.xml | 146 ----- .../src/main/res/layout/activity_login.xml | 72 --- .../app/src/main/res/layout/activity_main.xml | 77 --- .../src/main/res/layout/fragment_dialer.xml | 178 ------ .../app/src/main/res/values/strings.xml | 1 + contact-center/android-voice/build.gradle | 1 + 18 files changed, 1144 insertions(+), 932 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Color.kt create mode 100644 contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Theme.kt create mode 100644 contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Type.kt delete mode 100644 contact-center/android-voice/app/src/main/res/layout/activity_call.xml delete mode 100644 contact-center/android-voice/app/src/main/res/layout/activity_login.xml delete mode 100644 contact-center/android-voice/app/src/main/res/layout/activity_main.xml delete mode 100644 contact-center/android-voice/app/src/main/res/layout/fragment_dialer.xml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/contact-center/android-voice/app/build.gradle b/contact-center/android-voice/app/build.gradle index 087f075..add09ad 100644 --- a/contact-center/android-voice/app/build.gradle +++ b/contact-center/android-voice/app/build.gradle @@ -6,17 +6,17 @@ plugins { // Add the Google services Gradle plugin id 'com.google.gms.google-services' id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' - id 'kotlin-kapt' + id 'org.jetbrains.kotlin.plugin.compose' } android { - namespace 'com.example.vonage.voicesampleapp' - compileSdk 36 + namespace = 'com.example.vonage.voicesampleapp' + compileSdk = 36 defaultConfig { - applicationId "com.example.vonage.voicesampleapp" - minSdk 26 - targetSdk 36 + applicationId = "com.example.vonage.voicesampleapp" + minSdk = 26 + targetSdk = 36 versionCode 1 versionName "1.0" @@ -35,7 +35,7 @@ android { } buildFeatures { - dataBinding = true + compose = true buildConfig = true } } @@ -50,11 +50,30 @@ dependencies { implementation 'androidx.core:core-ktx:1.17.0' implementation 'androidx.appcompat:appcompat:1.7.1' - implementation 'com.google.android.material:material:1.12.0' - implementation 'androidx.constraintlayout:constraintlayout:2.2.1' + + // Fragment support for DialogFragment + implementation 'androidx.fragment:fragment-ktx:1.8.6' + + // Jetpack Compose BOM + implementation platform('androidx.compose:compose-bom:2025.01.00') + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-graphics' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3' + implementation 'androidx.compose.material:material-icons-extended' + implementation 'androidx.activity:activity-compose:1.10.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.7' + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7' + + // Compose Tooling + debugImplementation 'androidx.compose.ui:ui-tooling' + debugImplementation 'androidx.compose.ui:ui-test-manifest' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0' + androidTestImplementation platform('androidx.compose:compose-bom:2025.01.00') + androidTestImplementation 'androidx.compose.ui:ui-test-junit4' // Firebase implementation platform('com.google.firebase:firebase-bom:34.2.0') @@ -63,7 +82,7 @@ dependencies { implementation 'com.google.firebase:firebase-messaging-ktx' // Vonage Client SDK - implementation("com.vonage:client-sdk:2.1.2") + implementation("com.vonage:client-sdk:2.1.3") // Retrofit + Moshi (HTTP Client) def retrofit_version = "3.0.0" diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/CallActivity.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/CallActivity.kt index 309c7d4..38daaab 100644 --- a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/CallActivity.kt +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/CallActivity.kt @@ -1,202 +1,156 @@ package com.example.vonage.voicesampleapp.activities -import android.content.BroadcastReceiver -import android.content.Context import android.content.Intent -import android.content.IntentFilter -import android.content.res.ColorStateList -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.telecom.Connection -import android.view.View -import androidx.core.content.ContextCompat -import androidx.localbroadcastmanager.content.LocalBroadcastManager +import android.telecom.DisconnectCause +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.example.vonage.voicesampleapp.App import com.example.vonage.voicesampleapp.R -import com.example.vonage.voicesampleapp.databinding.ActivityCallBinding +import com.example.vonage.voicesampleapp.ui.theme.* import com.example.vonage.voicesampleapp.utils.Constants import com.example.vonage.voicesampleapp.utils.showDialerFragment -import com.google.android.material.floatingactionbutton.FloatingActionButton -import com.vonage.clientcore.core.api.models.Username -class CallActivity : AppCompatActivity() { - private lateinit var binding: ActivityCallBinding +class CallActivity : FragmentActivity() { private val coreContext = App.coreContext private val clientManager = coreContext.clientManager - private var isMuteToggled = false - private var isHoldToggled = false - private var isNoiseSuppressionToggled = false - - /** - * When an Active Call gets disconnected - * (either remotely or locally) it will be null. - * Hence, we use these variables to manually update the UI in that case - */ - private var fallbackState: Int? = null - - private var fallbackUsername: Username? = null - - /** - * This Local BroadcastReceiver will be used - * to receive messages from other activities - */ - private val messageReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - // Handle the messages here - - // Call Is Muted Update - intent?.getBooleanExtra(IS_MUTED, false)?.let { - if(isMuteToggled != it){ - toggleMute() + + private var isMuteToggled = mutableStateOf(false) + private var isHoldToggled = mutableStateOf(false) + private var isNoiseSuppressionToggled = mutableStateOf(false) + private var callState = mutableStateOf(null) + private var fallbackUsername = mutableStateOf(null) + private var disconnectCause = mutableStateOf(null) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + handleIntent(intent) + + // Observe call state changes + observeCallState() + + setContent { + VoiceSampleAppTheme { + CallScreen( + username = coreContext.activeCall?.callerDisplayName ?: fallbackUsername.value, + callState = callState.value, + isMuted = isMuteToggled.value, + isOnHold = isHoldToggled.value, + isNoiseSuppression = isNoiseSuppressionToggled.value, + disconnectCause = disconnectCause.value, + onAnswer = ::onAnswer, + onReject = ::onReject, + onHangup = ::onHangup, + onMute = ::onMute, + onHold = ::onHold, + onNoiseSuppression = ::onNoiseSuppression, + onKeypad = ::onKeypad + ) + } + } + } + + private fun observeCallState() { + coreContext.activeCall?.let { call -> + // Capture username before it might become null + fallbackUsername.value = call.callerDisplayName + + // Observe connection state + lifecycleScope.launch { + call.connectionState.collect { state -> + callState.value = state + if (state == Connection.STATE_DISCONNECTED) { + disconnectCause.value = call.disconnectCause + // Give UI a moment to show disconnect reason before finishing + kotlinx.coroutines.delay(1000) + finish() + } } } - // Call Remotely Disconnected - intent?.getBooleanExtra(IS_REMOTE_DISCONNECT, false)?.let { - fallbackState = if(it) Connection.STATE_DISCONNECTED else null + + // Observe mute state from CallConnection (single source of truth) + lifecycleScope.launch { + call.isMuted.collect { muted -> + isMuteToggled.value = muted + } } - // Call State Updated - intent?.getStringExtra(CALL_STATE)?.let { callStateExtra -> - setStateUI(callStateExtra) + + // Observe hold state + lifecycleScope.launch { + call.isOnHold.collect { onHold -> + isHoldToggled.value = onHold + } } + + // Initialize states + callState.value = call.state + isMuteToggled.value = call.isMuted.value + isHoldToggled.value = call.isOnHold.value } } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityCallBinding.inflate(layoutInflater) - setContentView(binding.root) - handleIntent(intent) - setBindings() - LocalBroadcastManager.getInstance(this) - .registerReceiver(messageReceiver, IntentFilter(MESSAGE_ACTION)) - } - override fun onDestroy() { - super.onDestroy() - LocalBroadcastManager.getInstance(this).unregisterReceiver(messageReceiver) - } - - /** - * An Intent with extras will be received if - * the App received an incoming call while device was locked. - */ - private fun handleIntent(intent: Intent?){ + private fun handleIntent(intent: Intent?) { intent ?: return val from = intent.getStringExtra(Constants.EXTRA_KEY_FROM) ?: return - fallbackUsername = from - fallbackState = Connection.STATE_RINGING - } - - private fun setBindings(){ - setButtonListeners() - setUserUI() - setStateUI() - } - - private fun setButtonListeners() = binding.run{ - // Button Listeners - btnAnswer.setOnClickListener { onAnswer() } - btnReject.setOnClickListener { onReject() } - btnHangup.setOnClickListener { onHangup() } - btnMute.setOnClickListener { onMute() } - btnHold.setOnClickListener {onHold()} - btnKeypad.setOnClickListener { onKeypad() } - btnNoiseSuppression.setOnClickListener { onNoiseSuppression() } - } - - private fun setUserUI() = binding.run{ - // Username Label - userName.text = coreContext.activeCall?.callerDisplayName ?: fallbackUsername - } - - private fun setStateUI(callStateExtra: String? = null) = binding.run { - val callState = coreContext.activeCall?.state ?: fallbackState - //Buttons Visibility - if(callState == Connection.STATE_RINGING){ - btnAnswer.visibility = View.VISIBLE - btnReject.visibility = View.VISIBLE - btnHangup.visibility = View.GONE - btnMute.visibility = View.GONE - btnHold.visibility = View.GONE - btnKeypad.visibility = View.GONE - btnNoiseSuppression.visibility = View.GONE - } - else { - btnAnswer.visibility = View.GONE - btnReject.visibility = View.GONE - btnHangup.visibility = View.VISIBLE - btnMute.visibility = View.VISIBLE - btnHold.visibility = View.VISIBLE - btnKeypad.visibility = View.VISIBLE - btnNoiseSuppression.visibility = View.VISIBLE - } - // Buttons Toggled - coreContext.activeCall?.run { - if(isMuteToggled != isMuted && !isOnHold){ - toggleMute() - } - if(isHoldToggled != isOnHold){ - toggleHold() - } - } - //Background Color and State label - val (backgroundColor, stateLabel) = when(callStateExtra){ - CALL_RECONNECTING -> R.color.gray to R.string.call_state_reconnecting_label - else -> null - } ?: when(callState) { - Connection.STATE_RINGING -> R.color.gray to R.string.call_state_ringing_label - Connection.STATE_DIALING -> R.color.gray to R.string.call_state_dialing_label - Connection.STATE_ACTIVE -> R.color.green to R.string.call_state_active_label - Connection.STATE_HOLDING -> R.color.gray to R.string.call_state_holding_label - Connection.STATE_DISCONNECTED -> R.color.red to R.string.call_state_remotely_disconnected_label - else -> R.color.red to R.string.call_state_locally_disconnected_label - } - cardView.setCardBackgroundColor(getColor(backgroundColor)) - callStateLabel.text = getString(stateLabel) - if(callStateExtra == CALL_DISCONNECTED){ - finish() - } + fallbackUsername.value = from + callState.value = Connection.STATE_RINGING } - private fun onAnswer(){ + private fun onAnswer() { coreContext.activeCall?.let { call -> clientManager.answerCall(call) } } - private fun onReject(){ + private fun onReject() { coreContext.activeCall?.let { call -> clientManager.rejectCall(call) } } - private fun onHangup(){ + private fun onHangup() { coreContext.activeCall?.let { call -> clientManager.hangupCall(call) } } - private fun onMute(){ - coreContext.activeCall?.let { call -> - if(toggleMute()){ - clientManager.muteCall(call) - } else { - clientManager.unmuteCall(call) - } - } + private fun onMute() { + coreContext.activeCall?.toggleMuteState() } - private fun onHold(){ - coreContext.activeCall?.let { call -> - if(toggleHold()){ - call.onHold() - }else{ - call.onUnhold() - } - } + private fun onHold() { + coreContext.activeCall?.toggleHoldState() } - private fun onNoiseSuppression(){ + + private fun onNoiseSuppression() { coreContext.activeCall?.let { call -> - if(toggleNoiseSuppression()){ + isNoiseSuppressionToggled.value = !isNoiseSuppressionToggled.value + if (isNoiseSuppressionToggled.value) { clientManager.enableNoiseSuppression(call) } else { clientManager.disableNoiseSuppression(call) @@ -204,41 +158,243 @@ class CallActivity : AppCompatActivity() { } } - private fun onKeypad(){ + private fun onKeypad() { showDialerFragment() } +} - private fun toggleMute(): Boolean{ - isMuteToggled = binding.btnMute.toggleButton(isMuteToggled) - return isMuteToggled +@Composable +fun CallScreen( + username: String?, + callState: Int?, + isMuted: Boolean, + isOnHold: Boolean, + isNoiseSuppression: Boolean, + disconnectCause: DisconnectCause?, + onAnswer: () -> Unit, + onReject: () -> Unit, + onHangup: () -> Unit, + onMute: () -> Unit, + onHold: () -> Unit, + onNoiseSuppression: () -> Unit, + onKeypad: () -> Unit +) { + val isRinging = callState == Connection.STATE_RINGING + + val (gradientColors, stateLabel, stateColor) = when (callState) { + Connection.STATE_RINGING -> Triple( + listOf(Purple500, Teal200), + R.string.call_state_ringing_label, + Color.White + ) + Connection.STATE_DIALING -> Triple( + listOf(Purple500, Teal200), + R.string.call_state_dialing_label, + Color.White + ) + Connection.STATE_ACTIVE -> Triple( + listOf(Green.copy(alpha = 0.8f), Teal700.copy(alpha = 0.8f)), + if (isOnHold) R.string.call_state_holding_label else R.string.call_state_active_label, + Color.White + ) + Connection.STATE_HOLDING -> Triple( + listOf(Gray, Gray.copy(alpha = 0.7f)), + R.string.call_state_holding_label, + Color.White + ) + Connection.STATE_DISCONNECTED -> { + val label = when(disconnectCause?.code) { + DisconnectCause.LOCAL -> R.string.call_state_locally_disconnected_label + DisconnectCause.REJECTED -> R.string.call_state_rejected_label + else -> R.string.call_state_remotely_disconnected_label + } + Triple( + listOf(Red.copy(alpha = 0.7f), Red.copy(alpha = 0.5f)), + label, + Color.White + ) + } + else -> Triple( + listOf(Purple500.copy(alpha = 0.7f), Purple700.copy(alpha = 0.5f)), + R.string.call_state_reconnecting_label, + Color.White + ) } - private fun toggleHold(): Boolean { - isHoldToggled = binding.btnHold.toggleButton(isHoldToggled) - return isHoldToggled - } + Box( + modifier = Modifier + .fillMaxSize() + .background( + brush = Brush.verticalGradient( + colors = gradientColors + ) + ) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .statusBarsPadding() + .verticalScroll(rememberScrollState()) + .padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() + ) { + Spacer(modifier = Modifier.height(40.dp)) - private fun toggleNoiseSuppression(): Boolean { - isNoiseSuppressionToggled = binding.btnNoiseSuppression.toggleButton(isNoiseSuppressionToggled) - return isNoiseSuppressionToggled - } + // User Avatar + Box( + modifier = Modifier + .size(100.dp) + .clip(CircleShape) + .background(White.copy(alpha = 0.25f)), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.Person, + contentDescription = stringResource(R.string.user_avatar_description), + modifier = Modifier.size(56.dp), + tint = White + ) + } - private fun FloatingActionButton.toggleButton(toggle: Boolean): Boolean { - backgroundTintList = ColorStateList.valueOf(getColor(if(!toggle) R.color.gray else R.color.white)) - imageTintList = ColorStateList.valueOf(getColor(if(!toggle) R.color.white else R.color.gray)) - return !toggle - } + Spacer(modifier = Modifier.height(20.dp)) + + // Username + Text( + text = username ?: "Unknown", + style = MaterialTheme.typography.headlineMedium.copy( + fontWeight = FontWeight.Bold, + fontSize = 26.sp + ), + color = White + ) + + Spacer(modifier = Modifier.height(12.dp)) + + // Call State Badge + Surface( + shape = RoundedCornerShape(20.dp), + color = White.copy(alpha = 0.2f), + modifier = Modifier.padding(horizontal = 16.dp) + ) { + Text( + text = stringResource(stateLabel), + style = MaterialTheme.typography.bodyLarge.copy( + fontWeight = FontWeight.Medium + ), + color = stateColor, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) + ) + } + } + + Spacer(modifier = Modifier.height(32.dp)) - companion object { - const val MESSAGE_ACTION = "com.example.vonage.voicesampleapp.MESSAGE_TO_CALL_ACTIVITY" - const val IS_MUTED = "isMuted" - const val CALL_STATE = "callState" - const val CALL_ANSWERED = "answered" - const val CALL_ON_HOLD = "holding" - const val CALL_RECONNECTING = "reconnecting" - const val CALL_RECONNECTED = "reconnected" - const val CALL_DISCONNECTED = "disconnected" - const val IS_REMOTE_DISCONNECT = "isRemoteDisconnect" + // Action Buttons + if (isRinging) { + Row( + horizontalArrangement = Arrangement.spacedBy(48.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(bottom = 24.dp) + ) { + CallActionButton( + icon = Icons.Default.CallEnd, + contentDescription = stringResource(R.string.reject_button_description), + backgroundColor = Red, + onClick = onReject, + size = 68.dp + ) + CallActionButton( + icon = Icons.Default.Call, + contentDescription = stringResource(R.string.answer_button_description), + backgroundColor = Green, + onClick = onAnswer, + size = 68.dp + ) + } + } else { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.padding(bottom = 24.dp) + ) { + // First row - main controls + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + CallActionButton( + icon = if (isMuted) Icons.Default.MicOff else Icons.Default.Mic, + contentDescription = stringResource(R.string.mute_button_description), + backgroundColor = if (isMuted) White else White.copy(alpha = 0.3f), + iconTint = if (isMuted) Red else White, + onClick = onMute + ) + CallActionButton( + icon = if (isOnHold) Icons.Default.PlayArrow else Icons.Default.Pause, + contentDescription = stringResource(R.string.hold_button_description), + backgroundColor = if (isOnHold) White else White.copy(alpha = 0.3f), + iconTint = if (isOnHold) Color(0xFF2196F3) else White, + onClick = onHold + ) + CallActionButton( + icon = Icons.Default.Dialpad, + contentDescription = stringResource(R.string.keypad_button_description), + backgroundColor = White.copy(alpha = 0.3f), + onClick = onKeypad + ) + } + + // Second row - secondary controls + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + CallActionButton( + icon = Icons.Default.GraphicEq, + contentDescription = stringResource(R.string.noise_suppression_button_description), + backgroundColor = if (isNoiseSuppression) White else White.copy(alpha = 0.3f), + iconTint = if (isNoiseSuppression) Purple500 else White, + onClick = onNoiseSuppression + ) + // Hangup button - prominent + CallActionButton( + icon = Icons.Default.CallEnd, + contentDescription = stringResource(R.string.hangup_button_description), + backgroundColor = Red, + onClick = onHangup, + size = 64.dp + ) + // Spacer for balance + Spacer(modifier = Modifier.size(64.dp)) + } + } + } + } + } +} +@Composable +fun CallActionButton( + icon: ImageVector, + contentDescription: String, + backgroundColor: Color, + iconTint: Color = White, + onClick: () -> Unit, + size: androidx.compose.ui.unit.Dp = 64.dp +) { + FloatingActionButton( + onClick = onClick, + containerColor = backgroundColor, + modifier = Modifier.size(size) + ) { + Icon( + imageVector = icon, + contentDescription = contentDescription, + tint = iconTint, + modifier = Modifier.size(size * 0.5f) + ) } } \ No newline at end of file diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/LoginActivity.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/LoginActivity.kt index 477cf7f..c37856b 100644 --- a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/LoginActivity.kt +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/LoginActivity.kt @@ -1,36 +1,47 @@ package com.example.vonage.voicesampleapp.activities -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle -import android.view.View import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp import com.example.vonage.voicesampleapp.App import com.example.vonage.voicesampleapp.BuildConfig -import com.example.vonage.voicesampleapp.databinding.ActivityLoginBinding +import com.example.vonage.voicesampleapp.R +import com.example.vonage.voicesampleapp.ui.theme.VoiceSampleAppTheme import com.example.vonage.voicesampleapp.utils.navigateToMainActivity import com.example.vonage.voicesampleapp.utils.showToast -import com.example.vonage.voicesampleapp.R - -class LoginActivity : AppCompatActivity() { +class LoginActivity : ComponentActivity() { private val coreContext = App.coreContext private val clientManager = coreContext.clientManager - private var loginWithToken = true private val defaultToken = BuildConfig.VONAGE_API_TOKEN - private lateinit var binding: ActivityLoginBinding + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityLoginBinding.inflate(layoutInflater) - setContentView(binding.root) - binding.apply { - checkboxLoginWithToken.setOnCheckedChangeListener { _, isChecked -> - loginWithToken = isChecked - editText.setText(if(loginWithToken) defaultToken else null) - editTextLayout.hint = if(loginWithToken) getString(R.string.token_hint) else getString(R.string.login_code_hint) - } - editText.setText(defaultToken) - btnLogin.setOnClickListener { - login() + enableEdgeToEdge() + setContent { + VoiceSampleAppTheme { + LoginScreen( + defaultToken = defaultToken, + onLogin = { token, isToken, callback -> + performLogin(token, isToken, callback) + } + ) } } } @@ -42,25 +53,154 @@ class LoginActivity : AppCompatActivity() { } } - private fun login(){ - val tokenOrCode = binding.editText.text.toString() - val progressBar = binding.progressBar.apply { - visibility = View.VISIBLE - } + private fun performLogin(tokenOrCode: String, isToken: Boolean, callback: (Boolean) -> Unit) { val onErrorCallback = { error: Exception -> - progressBar.visibility = View.INVISIBLE showToast(this, "Login Failed: ${error.message}") + callback(false) } val onSuccessCallback = { sessionId: String -> - progressBar.visibility = View.INVISIBLE showToast(this, "Logged in with session ID: $sessionId", Toast.LENGTH_SHORT) + callback(true) navigateToMainActivity() } - if(loginWithToken) { + if (isToken) { clientManager.login(token = tokenOrCode, onErrorCallback, onSuccessCallback) - } - else { + } else { clientManager.loginWithCode(code = tokenOrCode, onErrorCallback, onSuccessCallback) } } +} + +@Composable +fun LoginScreen( + defaultToken: String, + onLogin: (String, Boolean, (Boolean) -> Unit) -> Unit +) { + var tokenOrCode by remember { mutableStateOf(defaultToken) } + var loginWithToken by remember { mutableStateOf(true) } + var isLoading by remember { mutableStateOf(false) } + + Scaffold( + modifier = Modifier.fillMaxSize() + ) { innerPadding -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + contentAlignment = Alignment.Center + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(horizontal = 32.dp, vertical = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + // Logo + Image( + painter = painterResource(id = R.drawable.vonage_logo_svg), + contentDescription = stringResource(R.string.logo_description), + modifier = Modifier.size(120.dp), + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary) + ) + + Spacer(modifier = Modifier.height(48.dp)) + + // Welcome Text + Text( + text = "Welcome", + style = MaterialTheme.typography.headlineMedium, + color = MaterialTheme.colorScheme.onBackground + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Sign in to continue", + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.7f) + ) + + Spacer(modifier = Modifier.height(32.dp)) + + // Text Field + OutlinedTextField( + value = tokenOrCode, + onValueChange = { tokenOrCode = it }, + label = { + Text( + stringResource( + if (loginWithToken) R.string.token_hint + else R.string.login_code_hint + ) + ) + }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + visualTransformation = if (loginWithToken) { + PasswordVisualTransformation() + } else { + VisualTransformation.None + }, + enabled = !isLoading, + shape = MaterialTheme.shapes.medium + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Login Button + Button( + onClick = { + isLoading = true + onLogin(tokenOrCode, loginWithToken) { success -> + isLoading = false + } + }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + enabled = !isLoading && tokenOrCode.isNotBlank(), + shape = MaterialTheme.shapes.medium + ) { + if (isLoading) { + CircularProgressIndicator( + modifier = Modifier.size(24.dp), + color = MaterialTheme.colorScheme.onPrimary, + strokeWidth = 2.dp + ) + } else { + Text( + stringResource(R.string.login_text), + style = MaterialTheme.typography.titleMedium + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // Checkbox + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + Checkbox( + checked = loginWithToken, + onCheckedChange = { isChecked -> + loginWithToken = isChecked + tokenOrCode = if (isChecked) defaultToken else "" + }, + enabled = !isLoading + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = stringResource(R.string.login_with_token_label), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onBackground + ) + } + } + } + } } \ No newline at end of file diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/MainActivity.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/MainActivity.kt index 7e10df6..30c8b92 100644 --- a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/MainActivity.kt +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/MainActivity.kt @@ -2,24 +2,33 @@ package com.example.vonage.voicesampleapp.activities import android.Manifest import android.content.pm.PackageManager -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.fragment.app.FragmentActivity +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Dialpad +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import com.example.vonage.voicesampleapp.App -import com.example.vonage.voicesampleapp.databinding.ActivityMainBinding +import com.example.vonage.voicesampleapp.R +import com.example.vonage.voicesampleapp.ui.theme.VoiceSampleAppTheme import com.example.vonage.voicesampleapp.utils.* -import com.example.vonage.voicesampleapp.utils.navigateToCallActivity -import com.example.vonage.voicesampleapp.utils.navigateToLoginActivity -import com.example.vonage.voicesampleapp.utils.showDialerFragment -class MainActivity : AppCompatActivity() { +class MainActivity : FragmentActivity() { companion object { private const val PERMISSIONS_REQUEST_CODE = 123 } - // Only permission with a 'dangerous' Protection Level - // need to be requested explicitly private val permissions = arrayOf( Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_PHONE_STATE, @@ -28,25 +37,34 @@ class MainActivity : AppCompatActivity() { Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.CALL_PHONE ) - private val arePermissionsGranted : Boolean get() { - return permissions.all { - ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED + + private val arePermissionsGranted: Boolean + get() { + return permissions.all { + ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED + } } - } private val coreContext = App.coreContext private val clientManager = coreContext.clientManager - private lateinit var binding:ActivityMainBinding - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding.root) - // Set Bindings - setBindings() - // Check if all permissions are granted + enableEdgeToEdge() checkPermissions() + + setContent { + VoiceSampleAppTheme { + MainScreen( + username = clientManager.currentUser?.displayName + ?: clientManager.currentUser?.name + ?: stringResource(R.string.logged_username_default), + onLogout = ::logout, + onCallUser = ::callUser, + onShowDialer = ::showDialerFragment + ) + } + } } override fun onResume() { @@ -57,47 +75,33 @@ class MainActivity : AppCompatActivity() { } } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if(arePermissionsGranted){ + if (arePermissionsGranted) { coreContext.telecomHelper } } - private fun setBindings(){ - binding.apply { - clientManager.currentUser?.let { - usernameLabel.text = it.displayName ?: it.name - } - logoutButton.setOnClickListener { - logout() - } - callUserButton.setOnClickListener { - callUser() - } - keypadButton.setOnClickListener { - showDialerFragment() - } - } - } - - private fun checkPermissions(){ + private fun checkPermissions() { if (!arePermissionsGranted) { - // Request permissions ActivityCompat.requestPermissions(this, permissions, PERMISSIONS_REQUEST_CODE) } else { coreContext.telecomHelper } } - private fun logout(){ + private fun logout() { clientManager.logout { navigateToLoginActivity() } } - private fun callUser(){ - val callContext = binding.editUsername.text.toString().trim().takeIf { it.isNotEmpty() }?.let { + private fun callUser(username: String) { + val callContext = username.trim().takeIf { it.isNotEmpty() }?.let { mapOf( Constants.CONTEXT_KEY_CALLEE to it, Constants.CONTEXT_KEY_CALL_TYPE to Constants.APP_TYPE @@ -105,4 +109,125 @@ class MainActivity : AppCompatActivity() { } clientManager.startOutboundCall(callContext) } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MainScreen( + username: String, + onLogout: () -> Unit, + onCallUser: (String) -> Unit, + onShowDialer: () -> Unit +) { + var usernameToCall by remember { mutableStateOf("") } + + Scaffold( + topBar = { + TopAppBar( + title = { + Column { + Text( + text = stringResource(R.string.app_name), + style = MaterialTheme.typography.titleLarge + ) + Text( + text = username, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSecondary.copy(alpha = 0.8f) + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primary, + titleContentColor = MaterialTheme.colorScheme.onPrimary + ), + actions = { + TextButton( + onClick = onLogout, + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.onPrimary + ) + ) { + Text(stringResource(R.string.logout_label)) + } + } + ) + }, + floatingActionButton = { + FloatingActionButton( + onClick = onShowDialer, + containerColor = MaterialTheme.colorScheme.primary + ) { + Icon( + imageVector = Icons.Default.Dialpad, + contentDescription = stringResource(R.string.keypad_button_description), + tint = MaterialTheme.colorScheme.onPrimary + ) + } + } + ) { innerPadding -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .align(Alignment.Center) + .padding(horizontal = 32.dp, vertical = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = "Make a Call", + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onBackground + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Enter username to start a voice call", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f) + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = "or tap the dialer button to call a phone number", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.5f) + ) + + Spacer(modifier = Modifier.height(32.dp)) + + OutlinedTextField( + value = usernameToCall, + onValueChange = { usernameToCall = it }, + label = { Text(stringResource(R.string.edit_username_hint)) }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + shape = MaterialTheme.shapes.medium + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Button( + onClick = { onCallUser(usernameToCall) }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + shape = MaterialTheme.shapes.medium + ) { + Text( + stringResource(R.string.button_call_user_label), + style = MaterialTheme.typography.titleMedium + ) + } + } + } + } } \ No newline at end of file diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/fragments/DialerFragment.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/fragments/DialerFragment.kt index bc48f4d..eb543a7 100644 --- a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/fragments/DialerFragment.kt +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/activities/fragments/DialerFragment.kt @@ -3,35 +3,46 @@ package com.example.vonage.voicesampleapp.activities.fragments import android.media.AudioManager import android.media.ToneGenerator import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Call +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.fragment.app.DialogFragment import com.example.vonage.voicesampleapp.App import com.example.vonage.voicesampleapp.R -import com.example.vonage.voicesampleapp.databinding.FragmentDialerBinding +import com.example.vonage.voicesampleapp.ui.theme.Gray +import com.example.vonage.voicesampleapp.ui.theme.Green +import com.example.vonage.voicesampleapp.ui.theme.Red +import com.example.vonage.voicesampleapp.ui.theme.VoiceSampleAppTheme import com.example.vonage.voicesampleapp.utils.Constants import com.example.vonage.voicesampleapp.utils.DialerType -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER private const val ARG_TYPE = "dialer_type" -/** - * A [Fragment] subclass to render a Dialer. - * Use the [DialerFragment.newInstance] factory method to - * create an instance of this fragment. - */ class DialerFragment : DialogFragment() { private val clientManager = App.coreContext.clientManager private var type: DialerType = DialerType.PHONE_NUMBER - private lateinit var binding: FragmentDialerBinding private val toneGenerator = ToneGenerator(AudioManager.STREAM_DTMF, DTMF_VOLUME) - private var dialedNumber : String get() { - return binding.dialedNumberTextView.text.toString() - } set(value) { - binding.dialedNumberTextView.setText(value) - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -41,52 +52,26 @@ class DialerFragment : DialogFragment() { } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle? ): View { - // Inflate the layout for this fragment - binding = FragmentDialerBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - setBindings() - } - - private fun setBindings() = binding.run { - buttonDismiss.setOnClickListener{ dismiss() } - buttonCall.visibility = if(type == DialerType.PHONE_NUMBER) View.VISIBLE else View.GONE - buttonCall.setOnClickListener { makeCall() } - listOf(button1, button2, button3, button4, button5, button6, button7, button8, button9, buttonStar, button0, buttonPound) - .forEach { button -> - button.setOnClickListener { - appendDigit(button.text.toString()) - if(type == DialerType.DTMF) - sendDtmf(button.text.toString()) + return ComposeView(requireContext()).apply { + setContent { + VoiceSampleAppTheme { + DialerDialog( + dialerType = type, + onDismiss = { dismiss() }, + onMakeCall = ::makeCall, + onSendDtmf = ::sendDtmf + ) } } - button0.setOnLongClickListener { appendDigit("+"); true } - buttonBackspace.apply { - setOnClickListener{ backspace() } - setOnLongClickListener{ clearNumber(); true } } } - private fun appendDigit(digit: String){ - dialedNumber += digit - } - - private fun backspace(){ - dialedNumber = dialedNumber.dropLast(1) - } - - private fun clearNumber(){ - dialedNumber = "" - } - - private fun makeCall(){ - val callContext = dialedNumber.takeUnless { it.isEmpty() }?.let { + private fun makeCall(number: String) { + val callContext = number.takeUnless { it.isEmpty() }?.let { mapOf( Constants.CONTEXT_KEY_CALLEE to it, Constants.CONTEXT_KEY_CALL_TYPE to Constants.PHONE_TYPE @@ -95,20 +80,20 @@ class DialerFragment : DialogFragment() { clientManager.startOutboundCall(callContext) } - private fun sendDtmf(digit: String){ - val toneType = when(digit){ - getString(R.string.dialer_btn_zero) -> ToneGenerator.TONE_DTMF_0 - getString(R.string.dialer_btn_one) -> ToneGenerator.TONE_DTMF_1 - getString(R.string.dialer_btn_two) -> ToneGenerator.TONE_DTMF_2 - getString(R.string.dialer_btn_three) -> ToneGenerator.TONE_DTMF_3 - getString(R.string.dialer_btn_four) -> ToneGenerator.TONE_DTMF_4 - getString(R.string.dialer_btn_five) -> ToneGenerator.TONE_DTMF_5 - getString(R.string.dialer_btn_six) -> ToneGenerator.TONE_DTMF_6 - getString(R.string.dialer_btn_seven) -> ToneGenerator.TONE_DTMF_7 - getString(R.string.dialer_btn_eight) -> ToneGenerator.TONE_DTMF_8 - getString(R.string.dialer_btn_nine) -> ToneGenerator.TONE_DTMF_9 - getString(R.string.dialer_btn_pound) -> ToneGenerator.TONE_DTMF_P - getString(R.string.dialer_btn_star) -> ToneGenerator.TONE_DTMF_S + private fun sendDtmf(digit: String) { + val toneType = when (digit) { + "0" -> ToneGenerator.TONE_DTMF_0 + "1" -> ToneGenerator.TONE_DTMF_1 + "2" -> ToneGenerator.TONE_DTMF_2 + "3" -> ToneGenerator.TONE_DTMF_3 + "4" -> ToneGenerator.TONE_DTMF_4 + "5" -> ToneGenerator.TONE_DTMF_5 + "6" -> ToneGenerator.TONE_DTMF_6 + "7" -> ToneGenerator.TONE_DTMF_7 + "8" -> ToneGenerator.TONE_DTMF_8 + "9" -> ToneGenerator.TONE_DTMF_9 + "#" -> ToneGenerator.TONE_DTMF_P + "*" -> ToneGenerator.TONE_DTMF_S else -> null } ?: return toneGenerator.startTone(toneType, DTMF_DURATION) @@ -120,13 +105,7 @@ class DialerFragment : DialogFragment() { companion object { private const val DTMF_VOLUME = 100 private const val DTMF_DURATION = 100 - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param type Dialer Type. - * @return A new instance of fragment DialerFragment. - */ + @JvmStatic fun newInstance(type: DialerType) = DialerFragment().apply { @@ -136,3 +115,140 @@ class DialerFragment : DialogFragment() { } } } + +@Composable +fun DialerDialog( + dialerType: DialerType, + onDismiss: () -> Unit, + onMakeCall: (String) -> Unit, + onSendDtmf: (String) -> Unit +) { + var dialedNumber by remember { mutableStateOf("") } + var lastDialedLength by remember { mutableStateOf(0) } + + // Detect when a new digit is added for DTMF + LaunchedEffect(dialedNumber) { + if (dialerType == DialerType.DTMF && dialedNumber.length > lastDialedLength) { + val newDigit = dialedNumber.last().toString() + onSendDtmf(newDigit) + } + lastDialedLength = dialedNumber.length + } + + Surface( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight(), + shape = RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp), + color = MaterialTheme.colorScheme.surface, + tonalElevation = 3.dp + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // Drag handle + Box( + modifier = Modifier + .width(32.dp) + .height(4.dp) + .clip(RoundedCornerShape(2.dp)) + .background(MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.4f)) + ) + + Spacer(modifier = Modifier.height(20.dp)) + + // Title + Text( + text = if (dialerType == DialerType.PHONE_NUMBER) "Dial Number" else "Send DTMF", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.onSurface + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Compact phone number input field + OutlinedTextField( + value = dialedNumber, + onValueChange = { dialedNumber = it }, + modifier = Modifier.fillMaxWidth(), + textStyle = MaterialTheme.typography.headlineMedium.copy( + textAlign = TextAlign.Center, + letterSpacing = 2.sp + ), + placeholder = { + Text( + text = if (dialerType == DialerType.PHONE_NUMBER) "Enter number" else "Digits", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + }, + singleLine = true, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Phone, + imeAction = if (dialerType == DialerType.PHONE_NUMBER) ImeAction.Done else ImeAction.Default + ), + keyboardActions = KeyboardActions( + onDone = { + if (dialerType == DialerType.PHONE_NUMBER) { + onMakeCall(dialedNumber) + onDismiss() + } + } + ), + shape = MaterialTheme.shapes.large, + colors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant, + focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant + ) + ) + + Spacer(modifier = Modifier.height(20.dp)) + + // Call button (only for PHONE_NUMBER type) + if (dialerType == DialerType.PHONE_NUMBER) { + Button( + onClick = { + onMakeCall(dialedNumber) + onDismiss() + }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary + ), + shape = MaterialTheme.shapes.medium + ) { + Icon( + imageVector = Icons.Default.Call, + contentDescription = null, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = stringResource(R.string.dialer_btn_call), + style = MaterialTheme.typography.titleMedium + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + } else { + // For DTMF, just show a dismiss button + TextButton( + onClick = onDismiss, + modifier = Modifier.fillMaxWidth() + ) { + Text("Done") + } + + Spacer(modifier = Modifier.height(8.dp)) + } + } + } +} diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/core/VoiceClientManager.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/core/VoiceClientManager.kt index 437cf36..6aea834 100644 --- a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/core/VoiceClientManager.kt +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/core/VoiceClientManager.kt @@ -7,9 +7,6 @@ import com.example.vonage.voicesampleapp.App import com.example.vonage.voicesampleapp.services.PushNotificationService import com.example.vonage.voicesampleapp.telecom.CallConnection import com.example.vonage.voicesampleapp.utils.* -import com.example.vonage.voicesampleapp.utils.notifyCallAnsweredToCallActivity -import com.example.vonage.voicesampleapp.utils.notifyCallDisconnectedToCallActivity -import com.example.vonage.voicesampleapp.utils.notifyIsMutedToCallActivity import com.google.firebase.messaging.RemoteMessage import com.vonage.android_core.PushType import com.vonage.android_core.VGClientInitConfig @@ -62,7 +59,7 @@ class VoiceClientManager(private val context: Context) { onErrorCallback = { // Cleanup any active call upon login failure coreContext.activeCall?.run { - cleanUp(DisconnectCause(DisconnectCause.MISSED), false) + cleanUp(DisconnectCause(DisconnectCause.MISSED)) } ?: navigateToMainActivity(context) } ) @@ -88,35 +85,35 @@ class VoiceClientManager(private val context: Context) { client.setOnCallHangupListener { callId, callQuality, reason -> println("Call $callId has been hung up with reason: ${reason.name} and quality: $callQuality") takeIfActive(callId)?.apply { - val (cause, isRemote) = when(reason) { - HangupReason.remoteReject -> DisconnectCause.REJECTED to true - HangupReason.remoteHangup -> DisconnectCause.REMOTE to true - HangupReason.localHangup -> DisconnectCause.LOCAL to false - HangupReason.mediaTimeout -> DisconnectCause.BUSY to true - HangupReason.remoteNoAnswerTimeout -> DisconnectCause.CANCELED to true + val cause = when(reason) { + HangupReason.remoteReject -> DisconnectCause.REJECTED + HangupReason.remoteHangup -> DisconnectCause.REMOTE + HangupReason.localHangup -> DisconnectCause.LOCAL + HangupReason.mediaTimeout -> DisconnectCause.BUSY + HangupReason.remoteNoAnswerTimeout -> DisconnectCause.CANCELED } - cleanUp(DisconnectCause(cause), isRemote) + cleanUp(DisconnectCause(cause)) } } client.setOnCallMediaDisconnectListener { callId, reason -> println("Call $callId has been disconnected with reason: ${reason.name}") takeIfActive(callId)?.apply { - cleanUp(DisconnectCause(DisconnectCause.ERROR), isRemote = false) + cleanUp(DisconnectCause(DisconnectCause.ERROR)) } } - + client.setOnCallMediaReconnectingListener { callId -> println("Call $callId is reconnecting") takeIfActive(callId)?.apply { - notifyCallReconnectingToCallActivity(context) + setInitializing() } } client.setOnCallMediaReconnectionListener { callId -> println("Call $callId has successfully reconnected") takeIfActive(callId)?.apply { - notifyCallReconnectedToCallActivity(context) + setActive() } } @@ -129,7 +126,7 @@ class VoiceClientManager(private val context: Context) { VoiceInviteCancelReason.RemoteCancel -> DisconnectCause(DisconnectCause.CANCELED) VoiceInviteCancelReason.RemoteTimeout -> DisconnectCause(DisconnectCause.MISSED) } - cleanUp(cause, true) + cleanUp(cause) } ?: stopForegroundService(context) } @@ -143,14 +140,8 @@ class VoiceClientManager(private val context: Context) { client.setOnMutedListener { callId, legId, isMuted -> println("LegId $legId for Call $callId has been ${if(isMuted) "muted" else "unmuted"}") takeIf { callId == legId } ?: return@setOnMutedListener - takeIfActive(callId)?.run { - // Update Active Call Mute State - toggleMuteState(isMuted) - takeUnless { it.isOnHold }?.run { - // Notify Call Activity - notifyIsMutedToCallActivity(context, isMuted) - } - } + // SDK confirms mute state change - no action needed + // onMuteStateChanged already handled the state update } client.setOnDTMFListener { callId, legId, digits -> @@ -278,7 +269,7 @@ class VoiceClientManager(private val context: Context) { client.answer(callId) { err -> if (err != null) { println("Error Answering Call: $err") - cleanUp(DisconnectCause(DisconnectCause.ERROR), false) + cleanUp(DisconnectCause(DisconnectCause.ERROR)) } else { println("Answered call with id: $callId") setAnswered() @@ -292,10 +283,10 @@ class VoiceClientManager(private val context: Context) { client.reject(callId){ err -> if (err != null) { println("Error Rejecting Call: $err") - cleanUp(DisconnectCause(DisconnectCause.ERROR), false) + cleanUp(DisconnectCause(DisconnectCause.ERROR)) } else { println("Rejected call with id: $callId") - cleanUp(DisconnectCause(DisconnectCause.REJECTED), false) + cleanUp(DisconnectCause(DisconnectCause.REJECTED)) } } } ?: call.selfDestroy() @@ -309,9 +300,10 @@ class VoiceClientManager(private val context: Context) { // If there has been an error // the onCallHangupListener will not be invoked, // hence the Call needs to be explicitly disconnected - cleanUp(DisconnectCause(DisconnectCause.LOCAL), false) + cleanUp(DisconnectCause(DisconnectCause.LOCAL)) } else { println("Hung up call with id: $callId") + // The onCallHangupListener will be invoked with the reason } } } ?: call.selfDestroy() @@ -384,8 +376,6 @@ class VoiceClientManager(private val context: Context) { println("Error enabling earmuff in holdCall with id: $callId") } ?: run { println("Call $callId successfully put on hold") - toggleHoldState() - notifyIsOnHoldToCallActivity(context, true) } } } @@ -404,8 +394,6 @@ class VoiceClientManager(private val context: Context) { println("Error disabling earmuff in unholdCall with id: $callId") } ?: run { println("Call $callId successfully removed from hold") - toggleHoldState() - notifyIsOnHoldToCallActivity(context, false) } } } @@ -440,13 +428,13 @@ class VoiceClientManager(private val context: Context) { private fun abortOutboundCall(callId: CallId, message: String?){ showToast(context, "Outgoing Call Error: $message") client.hangup(callId){} - notifyCallDisconnectedToCallActivity(context, false) + // Disconnect state is handled automatically through CallConnection StateFlow } private fun abortInboundCall(callId: CallId, message: String?){ showToast(context, "Incoming Call Error: $message") client.reject(callId){} - notifyCallDisconnectedToCallActivity(context, false) + // Disconnect state is handled automatically through CallConnection StateFlow } /** @@ -479,13 +467,13 @@ class VoiceClientManager(private val context: Context) { } private fun CallConnection.setAnswered(){ + // setActive() updates the connection state, which is observed via StateFlow this.setActive() - notifyCallAnsweredToCallActivity(context) } - private fun CallConnection.cleanUp(disconnectCause: DisconnectCause, isRemote: Boolean){ + private fun CallConnection.cleanUp(disconnectCause: DisconnectCause){ + // disconnect() updates the connection state, which is observed via StateFlow this.disconnect(disconnectCause) - notifyCallDisconnectedToCallActivity(context, isRemote) stopForegroundService(context) } } \ No newline at end of file diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/telecom/CallConnection.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/telecom/CallConnection.kt index 766e350..03bc1db 100644 --- a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/telecom/CallConnection.kt +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/telecom/CallConnection.kt @@ -4,6 +4,9 @@ import android.telecom.Connection import android.telecom.DisconnectCause import com.example.vonage.voicesampleapp.App import com.vonage.voice.api.CallId +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow /** * A Connection class used to initiate a connection @@ -12,10 +15,16 @@ import com.vonage.voice.api.CallId class CallConnection(val callId: CallId) : Connection() { private val coreContext = App.coreContext private val clientManager = coreContext.clientManager - var isMuted = false - private set - var isOnHold = false - private set + + // StateFlows for observing state changes + private val _isMuted = MutableStateFlow(false) + val isMuted: StateFlow = _isMuted.asStateFlow() + + private val _isOnHold = MutableStateFlow(false) + val isOnHold: StateFlow = _isOnHold.asStateFlow() + + private val _connectionState = MutableStateFlow(STATE_INITIALIZING) + val connectionState: StateFlow = _connectionState.asStateFlow() init { val properties = connectionProperties or PROPERTY_SELF_MANAGED @@ -28,6 +37,7 @@ class CallConnection(val callId: CallId) : Connection() { } override fun onStateChanged(state: Int) { + _connectionState.value = state when(state){ STATE_RINGING, STATE_DIALING -> { setActiveCall() } STATE_DISCONNECTED -> { clearActiveCall() } @@ -47,13 +57,13 @@ class CallConnection(val callId: CallId) : Connection() { } override fun onMuteStateChanged(isMuted: Boolean) { - // debouncing: toggle mute state only if it's different from current state - if (isMuted != this.isMuted) { + // Called by phone UI when user toggles mute from phone controls + println("onMuteStateChanged: $isMuted") + if (isMuted != this.isMuted.value) { val muteAction = if (isMuted) clientManager::muteCall else clientManager::unmuteCall muteAction(this) - this.isMuted = isMuted + _isMuted.value = isMuted } - println("isMuted: $isMuted") } override fun onPlayDtmfTone(c: Char) { @@ -62,14 +72,22 @@ class CallConnection(val callId: CallId) : Connection() { } override fun onHold() { - if(!isOnHold){ + // Called by phone UI when user puts call on hold from phone controls + println("onHold") + if(!isOnHold.value){ clientManager.holdCall(this) + _isOnHold.value = true + setOnHold() } } override fun onUnhold() { - if(isOnHold){ + // Called by phone UI when user takes call off hold from phone controls + println("onUnhold") + if(isOnHold.value){ clientManager.unholdCall(this) + _isOnHold.value = false + setActive() } } @@ -91,15 +109,19 @@ class CallConnection(val callId: CallId) : Connection() { } fun toggleHoldState(){ - isOnHold = !isOnHold - if(isOnHold) setOnHold() else setActive() + // Called by CallActivity when user taps hold button + println("toggleHoldState: ${!isOnHold.value}") + if(isOnHold.value){ + onUnhold() + } else { + onHold() + } } - fun toggleMuteState(isMuted: Boolean){ - // debouncing: toggle mute state only if it's different from current state - if(isMuted != this.isMuted){ - this.isMuted = isMuted - } + fun toggleMuteState(){ + // Called by CallActivity when user taps mute button + println("toggleMuteState: ${!isMuted.value}") + onMuteStateChanged(!isMuted.value) } private fun clearActiveCall(){ diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Color.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Color.kt new file mode 100644 index 0000000..d1f611a --- /dev/null +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Color.kt @@ -0,0 +1,36 @@ +package com.example.vonage.voicesampleapp.ui.theme + +import androidx.compose.ui.graphics.Color + +// Primary colors from original theme +val Purple200 = Color(0xFFBB86FC) +val Purple500 = Color(0xFF6200EE) +val Purple700 = Color(0xFF3700B3) +val Teal200 = Color(0xFF03DAC5) +val Teal700 = Color(0xFF018786) + +// Custom colors from original theme +val Red = Color(0xFFC23B22) +val Green = Color(0xFF0EBE2C) +val Gray = Color(0xFF808080) +val Black = Color(0xFF000000) +val White = Color(0xFFFFFFFF) + +// Material 3 color scheme colors +val Primary = Purple500 +val OnPrimary = White +val PrimaryContainer = Purple700 +val OnPrimaryContainer = White + +val Secondary = Teal700 +val OnSecondary = White +val SecondaryContainer = Teal200 +val OnSecondaryContainer = Black + +val Background = White +val OnBackground = Black +val Surface = White +val OnSurface = Black + +val Error = Red +val OnError = White diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Theme.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Theme.kt new file mode 100644 index 0000000..e427bed --- /dev/null +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Theme.kt @@ -0,0 +1,68 @@ +package com.example.vonage.voicesampleapp.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple200, + onPrimary = Black, + primaryContainer = Purple700, + onPrimaryContainer = White, + secondary = Teal200, + onSecondary = Black, + secondaryContainer = Teal700, + onSecondaryContainer = White, + background = Black, + onBackground = White, + surface = Black, + onSurface = White, + error = Red, + onError = White +) + +private val LightColorScheme = lightColorScheme( + primary = Primary, + onPrimary = OnPrimary, + primaryContainer = PrimaryContainer, + onPrimaryContainer = OnPrimaryContainer, + secondary = Secondary, + onSecondary = OnSecondary, + secondaryContainer = SecondaryContainer, + onSecondaryContainer = OnSecondaryContainer, + background = Background, + onBackground = OnBackground, + surface = Surface, + onSurface = OnSurface, + error = Error, + onError = OnError +) + +@Composable +fun VoiceSampleAppTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = false, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Type.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Type.kt new file mode 100644 index 0000000..de23785 --- /dev/null +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/ui/theme/Type.kt @@ -0,0 +1,59 @@ +package com.example.vonage.voicesampleapp.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +val Typography = Typography( + displayLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Bold, + fontSize = 32.sp, + lineHeight = 40.sp, + letterSpacing = 0.sp + ), + displayMedium = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Bold, + fontSize = 28.sp, + lineHeight = 36.sp, + letterSpacing = 0.sp + ), + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Bold, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + titleMedium = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + lineHeight = 24.sp, + letterSpacing = 0.15.sp + ), + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ), + bodyMedium = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.25.sp + ), + labelLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.1.sp + ) +) diff --git a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/utils/NavigationUtils.kt b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/utils/NavigationUtils.kt index 8f420f3..d3e3534 100644 --- a/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/utils/NavigationUtils.kt +++ b/contact-center/android-voice/app/src/main/java/com/example/vonage/voicesampleapp/utils/NavigationUtils.kt @@ -3,15 +3,15 @@ package com.example.vonage.voicesampleapp.utils import android.content.Context import android.content.Intent import android.os.Bundle -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import com.example.vonage.voicesampleapp.* +import androidx.activity.ComponentActivity +import androidx.fragment.app.FragmentActivity import com.example.vonage.voicesampleapp.activities.CallActivity import com.example.vonage.voicesampleapp.activities.LoginActivity import com.example.vonage.voicesampleapp.activities.MainActivity import com.example.vonage.voicesampleapp.activities.fragments.DialerFragment import com.example.vonage.voicesampleapp.services.AudioRecorderService -internal fun LoginActivity.navigateToMainActivity(extras: Bundle? = null){ +internal fun ComponentActivity.navigateToMainActivity(extras: Bundle? = null){ val intent = Intent(this, MainActivity::class.java) extras?.let { intent.putExtras(it) @@ -20,15 +20,20 @@ internal fun LoginActivity.navigateToMainActivity(extras: Bundle? = null){ finish() } -internal fun MainActivity.showDialerFragment(){ - // Add the fragment to the container - supportFragmentManager - .beginTransaction() - .add(R.id.bottom_fragment_container, DialerFragment.newInstance(DialerType.PHONE_NUMBER)) - .commit() +internal fun ComponentActivity.showDialerFragment(){ + // Show the fragment dialog + if (this is FragmentActivity) { + val dialerType = if (this is MainActivity) { + DialerType.PHONE_NUMBER + } else { + DialerType.DTMF + } + DialerFragment.newInstance(dialerType) + .show(supportFragmentManager, "DialerFragment") + } } -internal fun MainActivity.navigateToCallActivity(extras: Bundle? = null){ +internal fun ComponentActivity.navigateToCallActivity(extras: Bundle? = null){ val intent = Intent(this, CallActivity::class.java) extras?.let { intent.putExtras(it) @@ -36,7 +41,7 @@ internal fun MainActivity.navigateToCallActivity(extras: Bundle? = null){ startActivity(intent) } -internal fun MainActivity.navigateToLoginActivity(extras: Bundle? = null){ +internal fun ComponentActivity.navigateToLoginActivity(extras: Bundle? = null){ val intent = Intent(this, LoginActivity::class.java) extras?.let { intent.putExtras(it) @@ -45,14 +50,6 @@ internal fun MainActivity.navigateToLoginActivity(extras: Bundle? = null){ finish() } -internal fun CallActivity.showDialerFragment(){ - // Add the fragment to the container - supportFragmentManager - .beginTransaction() - .add(R.id.fragment_container, DialerFragment.newInstance(DialerType.DTMF)) - .commit() -} - internal fun navigateToMainActivity(context: Context, extras: Bundle? = null){ val intent = Intent(context, MainActivity::class.java) extras?.let { @@ -62,52 +59,6 @@ internal fun navigateToMainActivity(context: Context, extras: Bundle? = null){ context.startActivity(intent) } -internal fun sendMessageToCallActivity(context: Context, extras: Bundle? = null){ - val intent = Intent(CallActivity.MESSAGE_ACTION) - extras?.let { - intent.putExtras(it) - } - LocalBroadcastManager.getInstance(context).sendBroadcast(intent) -} - -internal fun notifyIsMutedToCallActivity(context: Context, isMuted: Boolean){ - val extras = Bundle() - extras.putBoolean(CallActivity.IS_MUTED, isMuted) - sendMessageToCallActivity(context, extras) -} - -internal fun notifyIsOnHoldToCallActivity(context: Context, isOnHold: Boolean){ - val extras = Bundle() - val state = if(isOnHold) CallActivity.CALL_ON_HOLD else CallActivity.CALL_ANSWERED - extras.putString(CallActivity.CALL_STATE, state) - sendMessageToCallActivity(context, extras) -} - -internal fun notifyCallAnsweredToCallActivity(context: Context) { - val extras = Bundle() - extras.putString(CallActivity.CALL_STATE, CallActivity.CALL_ANSWERED) - sendMessageToCallActivity(context, extras) -} - -internal fun notifyCallReconnectingToCallActivity(context: Context) { - val extras = Bundle() - extras.putString(CallActivity.CALL_STATE, CallActivity.CALL_RECONNECTING) - sendMessageToCallActivity(context, extras) -} - -internal fun notifyCallReconnectedToCallActivity(context: Context) { - val extras = Bundle() - extras.putString(CallActivity.CALL_STATE, CallActivity.CALL_RECONNECTED) - sendMessageToCallActivity(context, extras) -} - -internal fun notifyCallDisconnectedToCallActivity(context: Context, isRemote:Boolean) { - val extras = Bundle() - extras.putString(CallActivity.CALL_STATE, CallActivity.CALL_DISCONNECTED) - extras.putBoolean(CallActivity.IS_REMOTE_DISCONNECT, isRemote) - sendMessageToCallActivity(context, extras) -} - internal fun startForegroundService(context: Context, extras: Bundle? = null){ val intent = Intent(context, AudioRecorderService::class.java) extras?.let { diff --git a/contact-center/android-voice/app/src/main/res/layout/activity_call.xml b/contact-center/android-voice/app/src/main/res/layout/activity_call.xml deleted file mode 100644 index ea69390..0000000 --- a/contact-center/android-voice/app/src/main/res/layout/activity_call.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/contact-center/android-voice/app/src/main/res/layout/activity_login.xml b/contact-center/android-voice/app/src/main/res/layout/activity_login.xml deleted file mode 100644 index 0454f96..0000000 --- a/contact-center/android-voice/app/src/main/res/layout/activity_login.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - -