diff --git a/star_lock/android/app/src/main/AndroidManifest.xml b/star_lock/android/app/src/main/AndroidManifest.xml
index 62715a64..946195ed 100644
--- a/star_lock/android/app/src/main/AndroidManifest.xml
+++ b/star_lock/android/app/src/main/AndroidManifest.xml
@@ -39,6 +39,10 @@
+
+
+
+
>>AFKRiug[SOPkgNFB??=<>DKRXle]XUOORh~gSFDACCABIO[jg]WWUUXetj[MEDBFDEGOVdvk_[VVTY^ujbXMFECFFFFMO]cwhj[]Y^^`_VLHFFHGFIMPZdulc_]bhyeaVNIHFHHGHJMR[jtic_lkomfZRLKJKIHHJLOU_f}hc]^`gn^TMJIIGFDGILM\ck\YTY[hhYMJFGDDCEHJMVf|oaZWW\_zm[PLIGGGGHILNQYgxlcZXZ\d}m_VOKHGHHIIIKMOX_lsf^\\_f}ueZTNLIIIKJKKLOS[gszgb^_^fsoe[VQOONNOOPNOPZ`im|lgceeeyneYTRPOPOPRPOOQ[afm{michjtld[WTTTSPPQQQOOVagoqqovz~shZUSUUSQTY]\]Zb__\igydji|yr}jd_^^]^aglklunmrfe^][XURSY[a_mzz{|y}|rxoohead_b]_^d_hi||~q{lwiljpmxtw~yrwjoeqeshjjqu~{}wq|kl`g]jZj[o\_dlnq~nokjjnnpktozn{m~n~mli~htiolnnmnnooiwg|dydtescuj}m|sy}vvxqlqjoitlxpzu{xsvsqogiejhedhfjglnqrumrkpl~s{jomtknmvuyv{rws}ltjndifjcjeijmnv|upolrr|oxo}}|wnoniigjhojspwxxunoomlknlmijotx}zzyyvrry}}n}nvkmdlfkeccgjotn|w}}r~ojjvfi]h_c__^a_cani|isv|{|yqpr|{kmbd`_^^_a_a_heujymrotzwywtu|hjef`c^f_c^bdmmqnz|}st|~znpkiiad_a\`]h`ykmvzpj~k~jnuwhudmdfadeiiqo|}{uqi~n~vt}vy}mtjnhmhpdnnz|wwpztzomwlwnxp|qpjjinhkglfnkrwx}uoxvo~mxsx{zuuktktkulnijfghhiiigfhilur~xrzy~rtommoqsvpllmlllmnmlknltoyoxmwn{{torv{yznylsmqhpgsfre}d{dvfu_sgpklnoys|x{yz{tomwilhmllokyjimjn{nvqwuwrqrvs~}t{pvovnqrturyswpy{zvtxuspzp|y{~zwwz}}trnxnxtxtyy{|z{xxvyrsnnoy{~tznsjkgmhfdai_l^j\l^k_ffhjimnvw|~yullfa_^\_W_V^T[VYWY\^bfjoy{|}}umiba^e[Z[WYSWP\Y``gmyxupzx~{stjc^]ZWUSSRRQWVa_xr}mg`ZZVUPPNOOXXadnxyw{hi^\VUSTRQQQ[\hg|}uwhc[\VWQROROQWZhh|umofo~ic`ZXROMNKMLMRTY]e}{xlohdcip|{jbaZWTQONLLLLSX[aiuwogfllwgd]WTOMNLLMMX\gjynlmzype^VUQOOMMNS]bn|{nihjuvnaaUXPQNMLNO\^hn|noko|umb^VUOONLKLMVY_l{syfecgmoosuhb^]VRVOOMLMOS\Zmwj}ou{|upkg\]XVONMNMSZ[koyuuxzznf^[VPOONOWW^ltssrwy}fd]WOONMMSTW_dvhjgccnuwj^[UQNMKLMQNV[cw{jfg_a`hnwze_ZTNMKKIJNMNZXlohn\^^]bcnuli[WPOLLJJJMLNVTccnma_^Ygfif`YUPNLLKKNKTR\dpkt_nklqf]UUNMLJKNLOXRla|okxhu~me[WTOMLKKMNMVO_]mnzdsnu}vif\YVQPNMLNNLVM^Xjm{ikmhxnswlh_\WUSOOPSOZPc]runtvo{yj`]USONMNNMWNaXnvldnkjws|yjd]XVRPNOPNXP`^piqcgeilo|wwhaZVQONNOPNYRd`}saj]bbgovpd^XSOMLLONNXRh_gm]gYj^mnuc^XTNNKLKNMOTRd]}zogbf^gatm~{c_VSONLKLOLVP[_ojl\aZ^^ehuj_ZROMKKKNLNVSheif][][_droi_XROMLMMQN]Wntof`Z^_^jx|c^TSMNKMNSN\Zmvwiccqi|j_UTMNLMLRNX\hn]^\_`ftoaZROLLJLJQOU^cxia\[\_`jwwe^XUOMLKLJMMOUYes|nc_[ZY]_fmwune`\YXXVVUTXWZ]chm~orilfjgmkomptzzx{nnfd`a[][Z[\^_ejjlzw~~xnokjmffhehjjsssz~~}vzuryovrronmijjigiiljkknmoux}z}zxtqponolmmllnknlmnmmlsqwwy~z}~{|~vyxropnmknlkiknlnopuuoqsrqnvxqouutqrsv{}wvnpnjifefddgehjlontnqrsvqvnmjkjmjmnlny|zoljhicddfgjns}{uonpswz~zxnmmmmosz~}qpnonww}|spnpoorsz~xjtxujtzswxt|w{pnqrssjpuz~ysw~{zv|v|zokmotqklhdabbgjn{{vzr~mvoloqvu}zrrhgcegcdefjnot}~womlgc__][\Z[YZ[^djw{{uu}z~{phghd][Z[YY[\\^_edlnnxzxuuy{~ojcadbcda`^\YY\^cjv|w|urrtrpmjhg`]\\]_ehnyu~vonpofb]]\ZVTWX[gpz~||he`ZURPPSV`nzwqv}}j_[ZXXWY]`d|uoyw{|x{}mkb`_]XXVXZZ\myxzs{y|vnzoec\^\\Y[\]eqmy~xrumc][X\\\]p{}{{wysne^XWXXY]iowt{|uu}{yw{ykkgc\YTQQQS\kvuyxmnp~x}xwomjle]YZWTTUV[jowzzqoxqnilgrvroji]VTYYXXinwqopkkekzynsx{xu{so{~zyule]XUUUUYgjolns~nim}vvqll}~{z}wxssli_[XXWWT]kklguyoahn{vovw|tqfab_\\ZYXYkfhdmqoeit|qoyzymjc_[XUTRW\]ehq~xpkhia^ZWVTS]_entynrx|ngf_[XZY\Y^^\`_pwq{}~yxrllih`]XWVURV][`cknjj|{~|toj`^YVSPRRWYZ]`cfhop~}{urkc`\WUQPORUU[]_bcmu|}|~~{le^[XWTTRTXV[Z\cfnzwput~|~~ynqid_[VTPNMMONSW[deowtzmec][VTTSWVY_\abj|vh^\XUSOQPTZXbep|||ng_ZWROONNOPUY_kq|ng_YVPNNNNOVW\^iw}~wzqqhd^ZWQOMMLNOQWZdiz|uqqid^\YUQNMMMPOTXZemzv{vqogj^]XVQONNOSPWY]gv{snjhca\YTROPNSSY]gnx}uuqif]\XUSONOLONSV\it{wkj__[YTRNMMMKNNRUZag|}|ynmkea\\XWSQNNMMLOPU[_pwvojjeda`[[XUSPNOMQPTY]kp~vnlg_^YWSQRQPVV^cn|zoib[YVSUVV]]fk|xxqkeb\YXTQOOPOQXV^cvxwyqoilfb__ZYUROMNOMSUU_bx|wtpihgba^ZYUSOOPPPWX]fpz|tuojjc_][XTQONNOOTZ\dm{zvxpomgd_\YWRQOOPRZ_gr|rtwwutmgc\XVRPPRQSZ^doxwrni`]XUQOOOPQU[`gr~~{{trnhc_[YTQOOPPSVZ]bk|vokf_]YYVPONOOOOSX\bnvojea]YVTRQQQRSTY]aht{sqmjg`]ZXWURQQPPQRUX]dp~rnlfa`]ZWUSQQSUW[`ju|uolhf^\YVUSQRSSTW[_gw}ypljhd`]ZXVTSPPPQSUY^enz|wtqkfa]XUSRPPRRTW[_enzookga^[XUSRQQQQRUX[_hs~{wqnmklfd^[WTRPOOOPRUY\`jpwpkfb][YWUTTSSTX[^fo~xsnleb_\ZWVUUUVX[]bho~}sskffa]]ZZZXVVVWXZ^cho{~|ttpllhgc`]\ZZZ[\]]^_dhot~ztpjifdb`_`__begknrzvtokhejhfhifhjklmooppoorptw||tojhhehfgdefghjlmnonponooqqsutvyyy{}|}}{||zyxywomkljigebbcccfhjkkmnnussuuvwtyyuywxvtxx{~~z|~ysuppnlmkhjhhjggijmmnprqovrszz{~~~}{wupmmjkihiijjklloqy}{~||xzxxvwyy|~|}xrtpnllonmkntortzv}zvx~~y}}}}z}t{ontmyppxvmpq{z~~}}vv{r|o~{{v}{|~||vwpp}moxiluei~hixnrxuz|}|xumonpupjklngjrkhjslmrkyoiqkzmpvv{ng}l|w{mulxqtttn}m{|ws}|w||~y|xu|~tz|{{t|wsvmnvlnsn}wuwy~|w~}t}utupovttzz}z{|wvwstl}onmnihjghijnmtowlqvm{nvyzyuvoroosorpunsuslsrmwpurqvpokkmjjknmkjgkmnptswvtussmmsovtswtpwusuopoopsosnqjkolnpquuprvu{s~~~}}ux}t|tqxvxxw{{wuvysvwwxssv{x|}}{~{{}z~~xwusstopomjkmorrvuwrsusvvtvuyvzy~{yqnlifc___]]\\[ZZZZ[\^admu|tnmjifba^]ZWTRQPQQTWX_al~|ohb^[WTQONNNORUY^bku~rle_\ZXVTSTTWZY_eh~~xqmjjmosysle_]ZXWWVVWZZ^cdoz{znkjfghinmsy}vrlgg_][XVUSUWX[__gr~z{uuzzyonh_^[XXUVVXXZ^]iku|~xv{s{|usnd`]ZZXXY\Z_`frxxrjca^\[YYZ[]a_jty|t{zznkd]]ZVUSQRRTVY[ben}}ysmlmilllrsywqrmlga_]YXVTUVWXY\^djz{omjedbbeeiont||~uunmjd_\ZXUUVWX\]_hjwvmjjjjoqu{slhca_^]^^^cemv{wt|znia][WXXXYZ[^_clvpldha]YXWVXZ[\aioq}yrjgc_\YYXWYZ[^cms}}{pwxxvw{olifa][YXYZ^^abilmy|~{skgc`]][\]\\\admu~{|{{||{tnlea^\[[ZZZ\^_fiox~~}{wujdb^\ZYZZXZ\]^ddklrytohc`][YXXXWYY\acfmo{yoid_^]\[Z[[[]_djru~uolheb_]\[ZZZ[^_cins}}sonkic__]\]]^^`fioxwy|ururxvxvtolggbha_a_`deklv|~}}|{|}|pmkfdceegfhmux~}wunkjiffgjgijmtz{~}}~ywtnkjhebdceefehilro{zsvnmkiifdcc__cfehjnrw|{||~~|upqqmljjhhhhjjknosw~~|wxuomnkjlkhmmnt{~{~|{||vsuuqpqvottxrwwxyuz~}|~~|yysqomnmlnkmmkilkklnnortvrtqoosvvyw}zuruqoosopqttpoqooorpnrukqqononnnoonptw{x|{|{yy}|~}{}z|z~}|}~|~{ywuwtppssuxxxy{z~{vyyx{yxzyvz{xx~~zzytwwxvsvuxz}}}~~|~zw{ruomrurvw|}~xwv{}~~}|xvroklkljjjhgghhhklnqtvx|}}|tuponkmmnmnjjhhgjijlnnrsw{|}~|{y}wrtrnmmmklmljlkkkmmosruy{~yvvrqoponmmmmnpsvw{|}}wxurnnnmlmmlnonouz{{zztwuuvssvooolmkjjjiiiirklmnnorqvvtuusvy|yy~~~|xuoonmlonmmllllpnmonoppprtuswyw{}|{zzvwyqrpommmnmrrvtzyz}~~~~~}{}zz}~{|{|ywy{x}~~yyttqrtvpponloklmnnooooqonmnnnosquvvuwtwvy{wwyvuwvstvsrqonnooooopqprrsstvyutvxsqtrprqsoqssqtttsuxxuwwxxw|xtsrrprqqtrnqooqnooqnnorqqtvusuyuvxzy}~|~z}~~{zz||~{}zywxuwwxvruuosustvtuzuwz}z~{z{{sz{wy~~~zz}zx}~{|{|||}~}||~}~~zz}{}~}}}}}}|xwwvvvvvwyvwvvtvuvtswvttzvvxztx|yz{|}}|~~~}xy|y{}yzyzw{|vwxttxwvuwsuqpqqprooqurqspmmmlnopoonlmmmnrrwxxwvvz|{y{~~~x|}|{~~wvzvy}{|~~}}~~}~~~~~~}|~|||{xyzzzwwtrprpopqonnmkmmlklmlmnmmooprtrqsqoqqopossrsutstrruvsz{}}}}}}|~{zyzy|{xyzxxzyz}~~~||{xy|||{|{zz~}xx~~}|~|~zz}{y|~xxxvrtvttsssnpqopqrrttstrprpptquzrosknpnoonsqoomnnnnnooonmomlmnllmlmmlmmonnoqqpooqropoopopopposrrusssrqtttwwuwwstttsvvv||y{zx~}w~}~}}zvzzw{}{}|}z{z}{}~}|~~{|zz{{{yxz{z~~||{{|}|z{ysuwutwxy}|}|z}~}~|~|}z}|||~wxyzxvvvswvuttrqoonnpqooqpnqrnoqonqpuvyx{z{ywxwqtvqruqoonnoprtux|}{{|x{zy|~|{{xyvrrwxvwzyxvvwuvzyyxyy{z{zxx{yy}{yz{xyy{zzzyyzzzxz{{}|~|~|z{}{y{{~|yyzyzyy}~z}}}zzwxvrxurvqsrqqqponpppppqqopsqpqqoqrotspstrtwttwwvxwy{zzyvtuustttuvtvuuttutvtvxvxy|yzurtvtsstuvvxxyyxxyy}~~}}~~~|}}~~}}|~|~~~~}~~}|z~|y~|w|zzsspmnmlmmnonnopoomklmnqponmkjlkkkklkkigihghijkkljiiijjlmmooprsonmorpqusrqpnqqrtwyzuuzwzyz|z~}{{xy{~}{}vwyvtttuvuswx{~zx|{}{|{}|~~}|}z{||~|}{|{}|}~~~|{{x}~}{zxsruwxvxyuwvqrrsoqrptqoqtuttuusrsstxxwusropvuwuwz||{{zz{yz{xrxwrxutwzwuuuvzxwwyz{zx}zy|{}|~~}|~}|z{}zzy|zuzxw|~~}~}}zxwssvyzwww{vsnnjm}nurvt|~~yvyxyz|~|zvx|zvwy{~{vy{|~xusssppqwwxwjokjnooosroupqmlllnmnmlllsprtornqqwssww|x}zywuqommopvpouq}{otmxszwrwxswxy}xx{~y{|vv~zuw~sux}{~xz}zzyr~z{}v|w~s||z||}s{|{vn|oruoyx{vpvtlxioqj|lvtjylvtkqhiognlirdifdrhqljsfifbk`heamemocvckm`wcmoc|bnoo|i|tjinn~iuplorlpoyrzxv}t|sx|ow}qqtmvyttom~wniwhjrvfatxcco~_cnu^~cn~bgibsgaxc`y}d`ua_dcc_~~_^|\^|]`p\bp]go^ek^fk]gmalfd}q{kwkrkqjddmomliz{oxlsnsmjimruno|yoqkylvot}nm|ixxixqdvji|lfuhasf_ne`mg_tjc`sheq_ngmyi}nkrdlfjlkmigjcfcfiilhijekfllhmihidkilmiolkmfmejkinhnlfrevomcgtit{ilucktmqhgoxzu~ty}~~}~}{~zt}|{}y{}|~{}~x|||v{}~~|xwmsxuqq}pqx|vtswzpp{l~gqoh|gptfkvdi}ga}lg^`iu^_rmb_nnepnmvovuzhymovmzqnllsooqvw{pvqs}}vslhjlhngkgidfgieielhldnmktj}uskn}~jlwrh}f~mjxhskkvl~y{vvvx{}~uyzk~ou{rn|xtrvqy}uyzyzu~~{u}t~~~zxw}zvzz}p{ws}svvouourmquvxwwyq|qzyy{x|{qtwuqoxxz}{s}}~|~w}|~~zvzysszznv~~}~zuysnnmmkklmnnpmonstx}}zysvnklfgedfegffefjip|ztpkjieeba^]^\\[[\^`ddiq{qvnvqox~pjhdc^\\[ZYWWWUVRUTTZY_biw{~{nmffffll{rmjec_]^]\[[XXWWYZZ^baox}|wsx{p~~pjf_^^_]]]\^[Y[\]ejoxnnkxzzlhghgeaa`^^[\`^hloz~~rkjec`^_]`_^^__clm}|ojhhjjm~pjdb`]^\]^\\[\\\bejtofgfjihm~pli_^^\[[YWXVTVVX^_dqxvjhbijlkqtokc]ZZZYVUXYZZ]dluviegknqpphc_\[ZYXY[ZZ[_l{phhbhkmtziga`][XXXWWWUUWYbfmoomlnowmc]YXWVUUUVVUW[_grwnmnnmn|ohb_][XXXYYY\_gn|yomoyxtnif__][\Z[YWWX\\aahu|}|xqikjmmjlgffhifecbcehlt{tqoqmmnnihgibdaecfknrz}|x~zx{zwqwmjlkphljltv{nq}|||}wpllmfebf_faghk{r}qluhsmkkfeddjjpwk~qxpr{uwkmeldibae^fdgkeubxfytolmodkbgc^e[`d_j_lmu|~rmpvw}mvjjwby_eoauidzixx|oxq~~}osw}~tyx{{{zrqwmzmf{npzkyqosqm~lkvjvmt{t~}w~}{s|sn|mlviomjpkoly~u|~qzoepiekgdnmzp}t{~xxrpuwv{lylpmluhvorojmy}w|~y|whyljgdceebfkltvwykxr{owz|uk}}kp}~w{n~yq}z}wz{qowyo}utpy|nphsqimjfm|m}|y|u|yov}vwp~oourllyml}my|}y~xws}|y|}}xyv{yqnvqkuvrt{oq{}~x}nzozqnylmpgmpjomuqtzszz|sw|smonlpmo{xux||uuxlqmjkjomorpqw~}{ywpppyxv{}~~}~z{wyvx}x|{otksojnqpsuu|yvuvurlmupyuu~uy|z~~xy}uyyt~|y{{y~v~vsrpqzwu}|z{tnqqoonklmowt{}}pkida^\\\[ZZ[^`gq~jk\[WOONNNORX\]i}rkf_corrf\UQNMLLMNQ[]hnlc^bcsxbZSNLMKLMTYZesnhadeu}oc[QOLKLLNW[cld^Z[^bhe]UOMJIIJKOVYjvo][YSQTW[]v^WQKMKIJIIKO[[t|l\XYTTVZcjw^XONMKKKMOT^kum^Y\]\\asw_WUSQOOTY[aoprmzlfetoaZXZWVTWZ]`mzylfnhfdn|lf^YVTPRUV[\]_ddxxtpgf_dlwxogb\[]\[\VWSRSXZ\hx~l_^^YZUW^enxvocYUPRPQSUVXY^Z_psf_[Z\^golhab^[\ZUVYYchwsfkkeiwyvjab^ZXXXYX\gen~t_`dholoocb_]][]Z[\\\Z\hjz~x~}vmmkfp~xqkdhb]ihde`e^^`^e^]__]^\cdinhnmnnyx~{qvjnkgtmltootqxttoil`jegkntw}}{n}kqrxzzx}}|}tw~l|wr{wupk{z{pznkmoqm{~~u|||rxlqoimepqgphttip}v|wx~vsow|l}oor{o{xoqozlwjqnnsnynoviuolwkkmh|yeh|nnniwek~flyufpfimikpllzru|sy}y}jmmgjjmnoo|kmmgr|mflofvhel`lccmgtsgqsutpqjzoqnwzvzprvfkiikkoltilnmijgnhzm{wvsr{s{|~||~sz|sz{x|otsm|whzllw|xu|zuorskxsoyjc|nhydqlgkcpfpirpj{vgk}zomizyqwnsoomosqjp~s{t{t}w}upvk}km|m~{xy}uqzokypczbvpkwk|zou}xun~kvvypznzxvv}xty|z}tz{v|wz|y~yzousyu~}pmrxnwumrvtxt~{o}lomuwqzt~limpmunpuvmwz}pommtmkxot~hzsh|lmvlqxmxonvn}wmzrtzilvdrivv}wnrx}zwqtvwplzuplnosnnoukyvwx}uuxu||w~xxzn|~~}zs|w~yx}zz}uzyytwmkthnolynrsk}umtvgmehqow}zxxojginjnhhjcc^__addfkhwv{~p{tqlhpjde]bd^bZ^kgox|usv|zy{{hibdg`]^_b^]]fn}}yml|~xpjeebfa__^__]Z\\[^deuuqqp{||xtrnjhhd_\ZWWXYbbjy|{z|xxpjfbdda]ZZXWV[af|w~ussq~yofc^[[WVXVZ^]l~{|y{yptrmnnqqikc__ZUROQRTZ]eszlkkiklosyytsslf`\ZVUSUVY_dm~}qkc_]ZY[^aejs~nhb]ZXWVWWY[^dmz}zyqid`^[XXVVVVY]bl~{|yoornpjec^[XTTTSTVWY\`emuwnjfd_\[YXXXXY[^`jnsz}||{zyrunhd_\[YWWWXY\_fn|{xwnic^\YXVVVWZ]ckq{ymh_\ZVTSUVWZ\ajvzrlhe`][XVTRRUXY[`gp}}zohc^[XVTRSTTVW\_fo|tlgb_[ZXVWXWY[ajp~wnf_]ZZXXX[\]]`gnxumgd_]ZZYXZ`dcov{nqhx~prloiac^\YSUQRYZ]klrqrvum}|vkmih^[[ZVTW\h_h~|mfwontx~|mec_[XUVUT_`dzokcihljtxtttzxxwnjf]YTTUSZhbuv{vi\^_gji}{uyzxohb\WRTTR\ecrh|xkikyuyld^\_elns}~|~xkc\XSUSUZljovvv|x}sigo}~mf^[ZZZ[]mjwkor~usr}~tkg]YZWZ\inuvo}|{}ojlk|md`\XSQPRSV_ilwk~qigcbefoxoi_[VVTSPRSV\imtlw~wnmno|{}s~mhfe`^]\[\]]_cv~zsv~{}s}zsvpulic`bcdi~pwz~~~mg_\ZZ[]hzzyqwrmf`dgimkm|z|oqwupnlc^[WVUUWZ_kqxoojmpuvpgjfdijmtsuoijhc_[VTRQRTW_jz|wottznleegilry{}yxzsng_[[XYZZ^lwvx}nrmpv~zkjghd_[XXWX\_dvvuu{{xupld_]\]^^^bjtzuqx}wpoos~ypha_^_`a`ckv}z{}{okikpzrmib^ZVUUUVWXZ^ix}slkmv}xuuuoic_][YWVWY]`ku|umlmos}wkkikv~{qlfdbeijhed_^\YXXX[_fm{wqroz}}z{}|xvuttomkjggfddggkmt~ytomkihhijiikmoovx~|y~}|}x{vomjfdbaabchkms{ytolkjjkklponpqqrruxz}{zz|utqookigdba__abchjouy|zwuqnmklkjhdddefffhjimnpusw{~}}|zzyvzwwxwtwtqqrvxyxyy{{y|~~zxxutrostruusxwvy{z~~}~{{xvvpopopommkkk{}jrqrwkknjkhjiejheikjmnlromsonppqvurxrv|rvsnqoqvx}}z}zt{svzo{|}x}svwwwzuyvtrrrpzzy|||}~yzwxutwoonmnmlnkkllimllnmopouvx|yroljjigggfgfegeeggkqr{}slkfghfhcaa_`aabiosujolmsuxtoijhfjhkkhgeeaabaiov~tjlhglms~xnkhecaacccda^]\\\\adm{~mib_^_eht}nida_^^^^^^^]\ZXXXYZ_ep|roggdeikx|sjg`][YWUWXZ_mz}~smfea_^^^`em}~zplhfba^][\Z]^gsw|wmifca_^][[Z[\dkxxojmszzxqoliggda_\[WVUWX^ep}|}~}|umgehmu|wyupliec_\ZWVSTV[dm|lfdio|wxzttsuomkgb]ZVTSTV[amyv{rqtt}mheemw}vzx|~snf_[YVWY\fpxrlp|xle^[YY[^husnjnyqld^ZXUUWZ_goyv|u}trkow~{voga\ZXWWZ_gotpwq{vsknt}~}tnfc][YWXZ_hty{~x}{smnmt|xxutmic^][ZZ]dlu|xppoz{sljd_]ZZZ[ahpt~~~zsljiglo{y{{qled^]YXWVWZ^dglonrr}|sliggehjruzyxrtrw}ynjc`^]YZXWXZ]bglst{||smkijgjlpv{xzttvx|zplfb^]\[ZZ[]afknu{upjgd`_aaekotz{uoljijggehmpquxsomihhgfgiknpvw||{y{}|ytnljjijjklosy|}||~~zwyxwvsromjhfedccddfejjvzs~}{|}zupnlkiiijjlnrx~|{xvvtxx|}~~{zz{||~~wvsomlmllmnoquxz~zyxsonnlkjiiijkllnnnmmlmlooqtv{}~{tomkjhfecdefhjlnqw{}zupmlkkjhhghhhghijlnpuz{}|}{vomkhhedddcddehklnopuw}~wuqmkihfeccdeffhhkmnrsvx{||xsnlkhgfdeeffgikooty|}ywysqmnlkiiikjlmmoorrtuuyy{~~|yyvuwttqooommmnnplyt{xyy{{}~}{vvvwuvwvvyzy{}~~}|{{yzyywxxxwwz}~|v~wwyuuxvvtututtqqonnprtwy}~yvqonmlljjhhihjkmmoquwyyy||~{|{xwvutrsuuxvxxxvusstvxzyxvvxxwuurrqssvyyzywyxzzxxxzz{xywuuvvvuuxyz}~~~|zzxwyyy{{||{|~~~~|}}~~~~}||~~~}}~~}}|z{zwyyy~}~~~}{xxxxx|}|y{|{|~||~~|zztuwwustqsrrstutpsoqm|zrvrtrwttuuwwxvxz|||}~}|z}z}~}~}}{{zyyxzz{|}|}}{{{yyyywxxvvxxvwy{}~~~}{|vu|{|~}}|yyyxywx{zzyyvtqsrpqqsroonoqpqrorqstuttuqrppprtqqrrponnnnnnnnnnnnnmooppoorqprsrrstvuttstwuuvuvwxxyyyz{z{z|}{||{}{y{zzyvwvtxxxyxyvxwwwx{}}}~~~}}~~~|{{|yxxxzy{~~~~~|z{{yx{yxwwwyxwxvvstrrrqqqpppqnonoooosprq{~}|~}|}~|z{z{zyy||{}}}}~~~~}}|~~~}z|{||{|{wwwvtussvz{|{v|wy|sxuuxuuutrooonoonnooooonnonnnmmmmnlmlllklmnmnnnoooopooonnnnmnnnoooqrppttsstsqroqqpqsqrrpooponnmmmnnnmnmnnqttz{z||{}}~~}{}~}}~~~~~|{y{xvwwusutqsrtutttvurtsrrtowvuuuuvyz}~{}|yz|{zyzyxzxxwwzwyz{|~|}|yvwwvvvstssqqrrwuuvxx}~~}}{xz|}{|{{ywwx{z{{z{|~}~~}|z|}~~}||yy|ywwvwxx|}~~~|}~~}~~~~~{|~}}|{{y|~~~}~}|zzzzywyyyxtqrpopttvxzwwxuvttvvwuvuvsrpqtuvtttrsoqopprrssrqqqqpoqstqopqpqpsuvwyxz|z|}{{}~||y{zvvyxw{z}}}~}~}}}~~}}}}~}~||}{}~~|||}{}~~~||||~|~{zzxzxxvvwwyx{{{zz}}|zz|z{yzywwwwtvwxzzwuwwwvusprpqqopqrrrsprpooopqrtsrqpvrwnovrusqtustrrsqsqorptrtttuyuurrsrsrprqrttstvstx{~}~|||}z{|~}}~{{{z}~~}zywxxtutssstwvtvtqqtsssttrqpooqqstvvwwvvuxyz{{zyxwwstsqrrsrusrqponooonorqsuuspnmnnnmnoptrrrsqprrstwvxyzy|}|~}}~~~~}~~}{z}~}~}{~|{{yxvvxwzz}~~ztronnlkkjklmmnqqrtuy{{uqmjhhaaa^^^]^__`acefjmnut}ywpokkhfdc`__^^]]^__`egls{~x|~{twkjg`a^^]\]^^ddls|{yyv|}sohe`]]\[[[]^ahkxvutquw~ymic_][YXWWYZ\adpwxrikfhjkuzolea]ZXWUUTUWV\\blp{mlfbc`cdfkoyyrjd_\ZXWWVXZZadn~zylilglnsohb][XWXXZ]]eiqz{nlljnqunic^\\[[X\^^lk{nohggejlu}rid_][ZYYY^]dmoofccbbhgox{ne`\YWTSRSTXY]dgmgdaa`dgkstkc]ZUSPPOOOTUY`bz~vrhb_^]__egm~xmb^ZWURQPQRVW\diqnhehfmp}ukb^ZXVUSVU[[ckstnhdeihvuxkf^ZYUVTVWZ\ckxolgbcbeknxib\XURPOOOQTU]^lwynec^]^__ii{|ic]XWSRQQQUVY_bv|mfb^^__cinzlc_[WUQPOPQVT]]jvsnhddcfltrha[XUTQRPUU[_d{yvlomo}}~skc_\YXWWZZbfl|pmlipr|qjb]ZWVUWUX]^imzmnhkkmr{}}snhb^[XUSQQRQVV\^gooofidffeillwx~zzpoic_\ZXVUVUWX\`eq}}~tytvxzvpjhb_^^]]a`jl|{unlkkgnly|~yzxvpommhhehdhlltu~|}{zzltjhjegcdccfccbdcfhkmsx|}}}vuolkffa`a_a_`_^^]^^^_acdhkow|}}z~~wvnokjhfedbbbbddehjmux~ywspnkiigidhljrquyurklggcbdcbccehjos|tmkjhgghikloptyz{zsqnkhfeddffjmoy}{utpoononyux~}pwkjicea`_]^]]]]^`bginuw}~xvsopnooqvu|{yqnlifdca____`abdfjkosz~{xutrsrrx~xsnnklkkmmmoqz|ytpmmnnqrw{~}}xzyvursrstw}~}|}|{z~{}yyvrpnnmmmopqtx{}~}{zzzzuutqrrsruw{zz{~{}tuonlmmlkkllnoonmmnopsrqoonmlkjjihhgghjkmsw{~~}zvrnliggeffhgiknsw|}}}~}~~}{{vvuuwz}{tuolnklkllmmqsx~|zwvuttrrw|~}|{{zz{}~}zxwxyz{~{y||||}}}|xxssrqstvzyzy|wtspsswyx||}{yvsutttuutuwy|{~~~~}zxurqprrqstw{~~~|}|zxwwtutstuvvxyyxwzzz}}}~}}|}~~~yxsqoooojnonoonstwzz{{xyxwvvwwwwvvvtuuvwx{}~}~}~}}{zz{y||{|}}}~|zxuuqssppqprprrqsrqxxy{}{{{zzxxwuuvvwxyzy{|}}}zyyyz|~~~~~~~}|}}zzxyy||~}{xvsqoprppooomnnopqpooopnnmnknzmporqqsqprrtwwvwxzzxwvttqpnnmlklmnoortuuwwtvwwwxyxyzyyzz{{~}}~}~~}|zzwwwuussrstuuutttuvvyxwxuuuvtuvwxy|~}|zz{zyyxxyyy{~}~~~~~{zyz|zywyxzzy{zzxyzwvvvuwsqponnnmklkiiiiiiiijklmmooooqtstststuuwyyz{{~||}}}}|{yy{z|y|||~~~||zwyzzzzxxxyxy{{|{||{{{{{}~~}z|~}~}~}}|zyxwxxyyxvuuvuuttxwwuvuwwxxwy|~~~}{xtrqqqpqqpqsruuvy{{zzwuvwvy{{yxxxxxxwvvvwxz|~}}{yxywxvvxyxyzz||}}~}{{|}~~{yxwxx{z}}}|{z{||{xxusrponmmmllllmmmmquvwwvvuttxwuvwwuutsqqrqrtuvwvwsqpoppppppnnmlkkklllmoppooooopoqrroopppoppqoqqqrrsuwwyzzyyxusqporuuttqpponotvy}{|}~~{{yxvrqpquyy|}}zzxxxzxz{yxz||~}|~~}{{{{|z|~}}~}zwurtuvxyzzy~~~~}{}}}}}}}|yxvxvuvuu}~~}~~|zxxyyyvuuwvvtuttsqqtplqnnonknmmmnnopqssstrprrqooonlmkkkllkjlkkllllnnnmlmlmnoqsqsrqqopnnnnopqsrrqrw{|~|{zxyyywuutvw|}~~|{y|~|zyvv{{|}}|z}}|}}~|||}~}~{~yzyx{y|~~}{|||~~|{zzxyzuvyyzyyxuvuttstwvxy{~|}xu}|ssx|xunotzzytvyz{wxz}~|}}~yxxy||}yz~}~~}~}}|}|}~|}}|zututsuuuuvvwvvvwrtussutuuvtrqsruumvtrollnmnmmllkllkkljklmnmmnmooooruuuutpsroppsrrtroppnooonmquvutrrruv{{{yz{wvwvz{zywvpqsuwyyyxyxywusruuxzxwzyxvwwyy||}~|wz{}}{usu}~ypnoonpt{|{vvw{}|}xuytpqw|}zuou}rnopokgeeda^_dgjq}zupmjifd`___`fm~{y{z||{sounpleeedfq~{uohdeffeacjyqmjhea^\YWWZ\_hngegjnr{~unonjc]\[Z\^afsvlnry{uuqpkhb_\\]_bhm~xyoomf^ZW[]_]\^konu}zznnsoe^\\\ZZ]cjqrp||~w|qrpof_\[\]^]ajoowvvtwwml|{uzzmhjig_ZWUVUX[\_k|vcdeor{zx}{xru|sx~~sstne_\Z[Z]`dklmrukefeflnvyylkrnkb\[Z]`cfk|~xzx|zj_]`adegpy{qmrrf`XVYYY[]gptiinruvyv}yutxstzvigffc]XUVXYZ]akwmknuwz||z{w}~xtvwj`\Z[Z[\^ekovstx{~|}zyywqlc^]^^ZY]dms{~qlmo~|y|{|trmkhga][[^`dhksqtx|~}{yxst||yvmotume`^]^^_agmxxsz{wzzuv~ynhb`acabcjlxw|}|wqppkf`_beecchou|{}zsmjkifb^_dkp{}uvz}ysspookg`^\\^bgzorljhfgw|zsoljmqy{rmnomjknz}~voorsnkiijha^[ZYY[_hypkgikvxwuw|ssu~~~zyysnga^]`iq{v{}y}|{omnnljf`][ZY[^fk}|uxvlkn~|zuqqqqmhd`a_`emzunlikozyvtw~yxy}yrnjgba`dbck{wljjjkls|zwwvuu{}zw||vxljedbcdda``is{~{yrmkljfdaaca__`abdkv{z~{~~zurorpnmliihhiiijklnopssxzvpsqqysstllmouxzz|{~zxywvqnlllmmnoqrx|}~z}~|zuooorsuqnlllklklooollklmoqponnnnlklnnopsqty{{ywy|{|~}xx|}ywwz}|wwsoprtuuusrooopqtvvvxx{}}}~~}}{zyy|~}}~}}~|~|{wuvw{~|yuy{~~zwusruuvyyusrpopsvvvwvutsqqrqstuvupnmlmprruvsutrtx||zxvvsopomllklnnnoonmjijmpqqonnommorstpnnnnpssuy|}xsoqtuwzzxsnllmnrrqppsvxz{{z|z~{zz}~~yursvy~|yvrnnotxzzxvsttuvutsqoommoorrqpooooomlllkllkkkiffeefddeffeccba`abehkjihhiiiknommlllllkklnoqomkklnqrponooorvy{yvrqqquusqnmllmov}yuvz}~~|~~~~}}}}~zy|~{txzy|yxxxwvy{zxxwwxxvwwwvvtuvwywtrpqqrstuwx{|{|{xwxzz|{xvuvvuusurrvvvvuuy{yzzy{z{}~~yxz|}|||~{vtsvzzzxxwvwz~|yyzx{|{z{~}~~}~~|{||zz{~yxuruuttuutroonnnqqrtwwxwwz{zy}zwwvvyyxwxxwxwuttwyxyzxxyzywutsuvwvvvutsvvxz{z{~}}zz{xxwuturqpoonooptqqqrp}~~{xwssuvyz||}~|{~}~{|x|}}}}~|zwtqponmnoomoqsuwwxwxvxxz~|~~}{xyvvsqonlklmonnooruux|~}zy}~~{vspooooqqtuqssruvxz}||{wvutropppsrrprruxwwvzyxywx|~~~~|yzyxvvy|~yxz}}zzywvuuuvtvtvwyzx{}~}}~{yxwx{}}}~~}}~~~~~zyx}||ywx}~}~~~~|wvxyxxx|}yvwwxx{}|yy{zyyy|~|{{{{~}}||~|}~|{{wyz~|z}~~}~~w{z{|twvxxz{{|zxvxuvy{}~~|{{{}~}|xxvutww{wzuywvuuwz{zy{zzzzxzzwyvtvuwvtwtvuvstuwxxwwvxxwxvwwz}|~}y|}}}~|{zzrursrsorpoomplnmqrutqrpoopqsttwwvturspqqtuttqpooruuxyyzvuttxx|xyxvvrtrvv}|~{|zz|~}~|~~~~||z{z{|}~}|z}}}|{~}~}~{}}y{yy}{zwxxyv{}~~~~~{yyvttuwxsqonmllorvwz|||x{w~|}~z}wwppnpoqpoonmmmmotzrz|wyvotxxwvsqoljihiikklmnnomorqvy||{|{{yvvxttsroonnooptvwwxy{~|{zxvyswvxy~|zyyz{{~~~{xxxz||~~}}|{zzyxy}}{yxrrpponnoqqqssrtrvwz}xwstprnqoonmlkjkmnrquqvqtorrxvzwzpqmnmnmmlmlnmonnnortwx|~{z~}xzzwuuutvvz|{z~z~|{~~}{xvuswz{~~}{}}||{|}~}}}~~}~}|yxz{~~|~}~~~}xxxwuwwwwwvwupnmlihhijjhghjlllnqtuy}wnlhea_^^^^^]\\]]^_dinr{znhc_][YZ[\^`gnyssoiea^][\_cinyx~sopme^\XXVW[`ewfmz}~}rzvlmnja\WWTSUZ^gzmhs{uxs}{{}wslf^YXTUX[`dzzkwx~wzxwlmhc[YWUWWZ_gyk}y}}qed]]XWX[]_aw{kzp~rjcb[Z\_chenzr}~yyif^^YWW[]`_q{ium|~y~r{|sko}vhb^^YVUY\]^wmilxjil{t~zxvvzjbd_ZWW\``e}llnl~{~t{xrysnlylj`a[ZXZ[]^mvvgbhrjhn|zw}ul{xv{ye][\XXUZ_bex}lin{mxtuwq~oi`a]XVY_cfwqq{~uz|zthgm^\Y_fgiyqpt}~rjia^\^efesx}rmf_\ZZ^badsqovu|}~ymi`]ZZWY[]]fnrimyz{kp~{wmnrz|umlif^[YYXXWY]jnvfiltgih}s|wywz}yllda^\ZZXZ[\^gw{nw}|x{}xtnic_]]]_`ehu|xz}xspkg_\\[Z\]`iqv~une_\[Y[\`bjoypmr}}wu}yusxwyow{~xrprnjd^\ZXXZ]_clu{pr|{}~{}}~wjea]\^^eenwmif_`agmp}}}znie_]]^bdkq}xxyy||x}}y{}oomookikjhb^]^\^^^_ejpnju}{wqy{}{{z}|tpookga`__\\]`eijlnz|{|wtrutonmmmjiijljhecaa`dgr}|~}}yustrrvurolkigefjnwyzzz}zqlkjjihgdb_^_bglt{~||tmhfggeccdecbbcdehjmptvxustvx}zzwojfcb_^]]]^^_chlv}}xtslmulroppw}|wrsqqnmmmkiiiknr}~}}ywwttqqtutvyzz||}}}|}{{{|{}zvsqruttvvsonmmnnnttsz||vmokhhfeghhiikkkijmps{}yxvuuqnmmllkjjlmosw|~}yvtropoqsurpoononmow|yuqrrtwxxvoljijlou|}}wurtvywvvtuv{{{{}tonnnpruy|}|{}{yspopsutxyzvurtxz||~}}|||}}~|yxtssttsrssutqonnpqvwywvrpnnntwzy||{}~yxsqpqsssrrqoooptxzz{xwpmkkjjknoonmkihhjlouy~~~~{xvwxzvtsqnnnprtrponmoqwy}}|}}zywvvv{}}w|zy|yx|zyxxzvtqqrtrrww{}}~}~|{zz{{~~~~}~yurportponnmmnostuxyyxwuplkklmnnmlllkmnpnopoopquxwwtqnmmllmmnlllkkkjjjkmottspommllmmnoporuyzxuqrstrtx{}{{~|{}~}~z{|}|{|~}|zy{~|wvuvw{{wrnlkklnnpomlmmnoptvywxywuusstvwxvvuqopsuyzyupnmnrvxwtsrrutstusroorsrqopoqsvz{{wwvvz|u}zxzww~}|xxussuvy{}~~|vuuuuvuuvx|{|ywz}{wwy}}ywrrpqqssuwuuutttvwwy}~|xy|}}}zxvtsrpuvvxussrtwtuqnnoooouvxxz|~~}~~~|}~~}zzz{~|{zyz{|~~~zwvwx~}{{|}{wuwz}~}|}}|}~}~~}}{yxyyz|~||~~~}xurv|}{|}~{~xyz}~}~|z}}~}zyyxvyz{|ywvuxy||~|uqqrvyzsqnmoru{|zwwvrrsttrrqnkjlnpttpnmljjlnorqnmmllmortsssroonllmmnoqqqqpooopruvwx{}~~~{xxy|||}|z}}|}}}{{~|||}~}{}rwpyw|{y|w}z~~ywwxxyyxyy{yt|q{tvpspsrww}}{}{}{{}|y{szqxquryvyvwxwvwvzw{z|{|||y~yyxwututzy{wzu{yz|~}}}{}}~{~{~~wux~tx}~}}~}zv|wyy||xurqqpsqqsnponspwuyx|~{yv|vv}q|pzrvsvuzxx{y~wuysxwyw{z}{|wzw||~~||{~txsxovtzw{svs{|}xwzzxuwtsxvx|z|}}yuuy}rtsy}|z}ruw{|z}zyx}tzsvx|}}uvrsquqqswtwstour|wu{tvotrrqsrrpoqqusvuuvywyy|yxwq}x}uxtzx}{~vstttpssyxzqsoorqyqxosqssw{y{zxuvz{{z~y~}{{vpmmpu~w{{puztwxusljjln~ottvxtwu~qwwusv~ztqljjhg}efhfh}iklmottx}yunijhgd~dcgfhjkm{nwo{w}{|runyswpxpymvkwkxjskumyn~v}wyxyu|wxxroqvw~|||y~wy}vpnpoqwxz}}}w}~}yywzzyyssvwwsyy}y~w~v~uxx|wxzxvqnzuopopoqywz~y||~}y~w}svo{t{s}uu|uzryy|z{vsxsuut{v|uuw|{{{~z}z{x|~~||v|xzs}pwnsmqnqotvvyy}x{{|y}w}z|w}|~~}}|~zz}~~|~|ux{{|}{~z}}~}}{{{v}ussww}vxuuvw{y~||~{ywxwwxuwsvpssurwsuuvrwsvqxq|tvxs{r|tvt}p{nzoyq|sysxqwswvx{{~|~yuz|~~}~xxyyz|~|}z}x~twouotovnuptp{xys{mofibgagchekionz{|tkjefcb_a^^^]]achmq|zrnjifb^_^^]\\^aeimwvyqpmhfbcba_`afimtv}|zolgfa`_abcaabehmoxyvzomggbb^^^^]\_chouyxlolsznjg`_^^\[YYWWUXZ_elsrsjjflmz~pjda^][ZXXWWWX\^fis~|soqq{umhfca_]][\Z[Y]_gkuxskmjtv}umhc__^\\ZZ[ZZZ_bnnyvonlpxymjfc_a_^]]\[[Z[_aim{~}tsmnnt}wpljiihgdba`^]]ahs|wumqpyuokghgiefbc_`]^]dhquyzzz{sqmkjkjhiecb`_^^dhqs|yosmonxqsmlhgfhigfba^]\\[_cnu{|tuq{{}|~~wuqrongd`a^^\^cku~{~svy|w|s|xxuyurjf_]\[Y[^eq|pumywujb^[YY\^joz|t~{pic_][ZZ^bjo{~twr|~zuic^\[ZYXZ^aikvz|xzw|zwmgc_\\\[\^dgor}|vokhc_^]]]]]ahnty~xpnmligdcdab_khlouzztpnnnnnnmmmjifgiloqv~~~z{|~~}xwtpmmkjjhfecfkmor~{urqvwz~}~yusqplkhhffgjmus||}xyupoonrty~yywwuuoqqrpliihfeceglqw~zzwz{|xvrmkffghjmu}yyx|~}}{{ywsutrpoommljhkow~}{vvtx~~yuvsopoooqpsuw{{{}~yvomkighfghjkmmoqswy}{uqonolljmlnoqvwxyz~~}}}~{zywtsqxuutwvvtsppoonqrvy}~{|{xvuttqsruuvvxwuwwy{~}{}}~~|z|zzurqonpqstyz{|}}}~~xzztwwuttrstvz~|xwurpsuwyy|}~~}xtrqnnnnnoostuw|}zwtqnmmnmnoqqvy|~{zwwwvvwwyy{|~}}}|xutrpqprprqqqrtuvx{~~|yuqpqrsrrqonnptyz}~}{~|{|zxxvvuuututuxz}|{xvutuutsuuvxxy||}||}~}{xyzyz}|}~~~~}{zzzzyy}}}|zyzzyzywwvvxwwwvsqpoooooppqsuvyy}|{}}}}}|{|{zzyxwutssrqssrttttqsrqrrqqsrttsttuuvuvwwvyxzzzzzy{|z{zwyvwyz{}|~{{z{z{{{{zyy{{{||z~}}|}{zz{xxwuusuuxz|}}|||}|}~{xtponoppqpopqpoooooorsuvvurrrrqrqstyy{{|}~}~~}|{xusrrroopqpoonoooppstvwvvxzyyxxxzyyxxwxwvwwwwvttuvsuttrttstuvwxzxxxzz{{~}~~~~~|{|}}}}}}~~~}|{|~}||}~|y{zyzwvvwvvyz~~||zyzyyxz}|~|zyzzzyy}|~|zyyxvvuvxzzxxwxwwwyy{{z{|~uz}y{zsvrqrsutuuuvuwwy{~~|{zyxxwxzxz{|}||||{|z{{{z{{{|ywxvussrtuwvvxxzzyzzzz{|}~~~~~}}~|||~~||||zxywwuvvvvuux{{||~~~||||{|}}{|}{{~||{xurpponnmnnmmmmnopppqrrsttqttuwuuursrrrsstvvxzz{~}~~~}}}~}}~zyyxwvvxxyzxzz{{{|~}}~|{zzyyxzy|{{|}~}||}{yxxzyz{||}~|}||}|}}|{yyz||~}~}~~}~}}{ywwxy}}x~z~|y|{~~~}{{|{{{xyyxyz}~~~}~}~~~~}~~}|{{xvuttttuxz{||}}~~~~}||{zxvtsqrrsuuwxy{|{zyvtttutsrqppqrqrsttvxyzzxyyyzzzxvutrppqsttvwyz{||~~||{z|}~~}{{{{ywvuuuuvtrqqqrutvwwzy{|}|{}}{|{yxwwxz}}{|}}}{}}{{yywuusstvwyz{{~~~}}}}|{xxvsrpqppnnmot{{}~{yzyz{|~}|~~{{zz{|}}}|{||~~~~~~~}}}}~||zxyyxwvy|~|}|zz{yzzzzxwxxwvvttuvuvvwvyzzz{|~{|zywvwwvwyyx{}}{{{{zywvwwvurqpnmlmmmnooqtwy{}|{{|{z{}}}|z{}|xvvvuwz|||}}~~~~|zxyz{zy{|{{zyxwyyzz|||}~}~}~}}|z}~{wuustswxz}~~|{zxuutuuutvutvxwx{}~}zwwvtsttsssvvvvvuxzy{||||~}|ywtrqpooqqooooopqpqpqrstuuwxvxvtsrtvvvxyz{zz|}~~u|}y}|y|||}|}}{}~~~}~~~|}|{|}}}~~}~||~~}}~~~}|}~~|{|{{yz{{yyxwxyzzz|zwvtrsrqqqrpoonnnnooonnnnmmnnnnmmnnmllnnoomlljhhiihjjjjiiiijjjlmmmnnqvzz{zyxvwvvwyzyxwx{~|{zvtrrrqonnmmllllkllmnoruwwvvvwz||~~~~|}}}}||~}~}~}~{xwvwxxyz}{urngjjikifheddcdccdfhiknqtxzy|}~|wsokjigfdcccdefggffddcdefimpv|}||zyz{xwpnljhfb`_^^^^`ekt}zz}~}wpmigd`_^]]]^_cgn{~~}~xqmjgb_]\[[\]ago|}~zuqnjec`^]\\]_fmyyustv{zxuwy{wyrznqkifc_^]]^agm|zyxz}}zwtnjfa_]]^_em~wqnmmot|}}|zxtonmljiec`_]\]_dkw{spoqsu}~wsojljfgecgfea_]\[\^cmx{qliggims~|vromkghggeddc`^\ZYXXZ\airyqliilqztnjgecaa``_`aaa_^\ZZYZ\_gn||tmhfddflq~sljhhghghijjjjhfb_]\]^bju|qljijlr|vommmmnmmllmmljiea^]]_ckxzrnmnqxxqnlkjjhhhgfffeb_^\\\]_foxqnllos}|unmlkjjihhiiiigc`^]]^agn|yqljijls}yoljijkkkjjkkkjgd`^^^_cjryrlkjkmtvomlkkkkkjjkloooligeegksunmnqwxvnjihhhiihhhhiiifd`___cgm}umhfegjovxnjfddefeedddfghfd`^\[[\^bjs}pkgcbbeiozsmjiijiihikmnopoljfca`bhnyypkjknt}~wwvxyxxyxyz}yslieccfinv~rljklovwqooonmlmmmnnoomjfb___bfnyyoljlot{{wvuvutqpoooooonie`][[[]^jwwxolkkmpy|wttttqnkjijjkkjgc_^]]]^`ejnuuomnqv{~{yxy||{|yz{|}}|xqmieb_`cglpy|wwy~}yrihdab__dflq~}yz{~}~ysnjgdb`adgkowzwqmieb____bcdek]PFFM\y`WTOQOP`yqývt`_glpOJQah`cqxk|iVJEGKOUXRMJHHIKQdw}jUKHKNVlqaXUUTPMJHHLMORUOGA>=>?EIMU_~xmu~l]UZZSSSPNMKJKKLOUY`ly~soxphd^]]ZYZZVRPRUTROMLNSVYbsf[Z^hnjjovknu}md\ZY[]_ekjja^\\^bn}unjrf^\\[Z]aggin}kdimligcccehnytoonou{y{}qia`^_dntzvja_^^dmz~xrnty}uiedaadlv}{v}}xmic]\\][WVWYXZ]^_bejkloss|z~~ykaZXXZ^_cfeeitzssomotr{}qox~~{qoja]\`kzwrmily}vpmlgdcdjo~{tmha__^_aded`][\]^^`fjjjfjixy|~qmrwrtronmlhfgmzqlknw|trqrppw}sjihhhlnje_^ahn}wnnlkpwxvtvukmhdc^_ejnv}zsppoptqqtqsry{womnot~vlifba`bbceilp~|vtwxtqqqomszuljmnige_^^adiv|wononmr}|vvz}}nhfhjligggjmpvx|vqsz}nihggknw}umechknu|~}xvx}{of^\\_bdehjlmqxz|zwtxyxy{~~zwsoruv|}oklmlhddegghknsqoonnv~{~~zy|xvtrsvuqomklmosv{}||~}vnjijkmqy|yxurqonmlmnotyxmhfgijjkkmp|zxwzzvtqoruvtx{}{wvsrqsty~zuokkox~~~~z{{|zyvrlhgfffhhhlu}}~|yvutpnnmkjjlnqwyvtv|}vtsw~tspnnp|{xwx{{ywsqponnlkjgghjmrw|vqnkfcdffehlnptxvxzywvvssvuv|}~}|~}xwwurouxxy~}{}~|y{z{}~~{zutm|uososz~xvyzuor~wvwz{|~||vnmpnnouy~{xyuqouwuw{~~~~wwvz|zwttrty~}xsrqtz{z{~~}~~}z~|zvuz|{zyzyyx}}|~}wvuwtrpnnouvw~|wtqnlkihknnoqsry}yy}}}}}~~{xxtpqvvqpomklmpsvxvsqqrrrspopooqqsrpsuy|{x|{xqmjjjnqrrsrrsqqqppoooqqqussttutrpoooosvyz{}~~}{|{{y~~zuuwzyttsww|}|y{}|~~}{zzzz{y{}~}zxx}|}}xz{~~zwvsoooopsvyyuuvuw}~ywvvuuwtsrvxz}~}xxytvuuprqptrtsuy}}ytuornlnlnomnsuwtuz{}}~~~zuspqpprorx||~|{x{}|zxtqqooooppopprqrqquwz{{}|wpnljiijkkmptwvtvz|yyz{zxxxzy{~{tonlllkmonnmmkjjjkjklllmotz|}|}|~}{{|ywsommlkjiijklmpsy{||}ytolkklkkklmonoqx|||zrmjhijmorw}zwvrqtwyw{~zy{zywtqlheccefjlmtvolljjhhfedb_^]_]]__eihn{}yvxz|unjhd__`_^]]^^^^]_bcglwwsrvwuu|xmhd`][[[[ZYYYZZZZ]_bekwxnmlnoos~uoomgb`b_^]]^^]]^ddhisz~}ojifea__^^\[]_dknystrty{okhda]\Z[[Z[`ipzxoh`][ZXXXZ[^bhosib]a\YXWYZ^bjwxurnnormd^YWTSQQQSUZ\blvmkfdcioxqjb[WTRQOPPRTY]bjtnihhnxqf]YUSQQRTX]co|xmlmn~tc\WSPOOPSX^fyohejlx~sh^[YYYXXYXYZY\`ejp|zoox}wnjhigdc`^ZWVURRT^ksngaa]^dvymkhhfhlnmkd_\YWTTZcguymggbci|ymkifegknpqoi_ZUTPNOZ`l{jeba^cq~ummjklv}xkb[YVSS\juqicefj|}vuxzpe\VTQPQ^jy_][^`i}yquu~k_YSQOPTf}y_]Z]_h{qtw}re\UOLLLMNWluiZRSU]bq~z{|yh[VOMMNOVfj`VTSY_oxgfejjpwg\UPMKLMOX`sd`YXX^lxu||ob]TPMMMORYbmfa\]]ktd_[VRPQRX]ennfgdhfzq`]ZYWSRRY]cezee_gbju~qmh`]ZYWVTVY_emxggbkir|ojea^\\YXUVX\_eyifahhq|~xwqlf`^]\XVTTW\_erqkcfhrwqou{pngc__][XXY[^fkmjejm{qmhihiecaekqyxtwzxsrokhfhhmr~wmoo{}xrmlhda\]^]^cfs}njefeko~yqmljlpx{tokjfdba__^__dfmwsmkkknt|}~umljkhgecb`_^]\^`fr}tnnsxzqnmkijjigdbacdgp{toos|wmjhghijhgddcbacfkstpnmps~{{zwrmkgfeghkmnnmkihgggjmu}zvusx|~ytrvz}|unmkklkmmorwvtqnonlkklnq|yv||rljhhghjnx{wqsz~|sonkhhjmou|{||{{svspw{|wrmkjkkmgjpr}wqoqpstw|~|~yzvtspomkheeghhjlpx~~|~~}}}}|wuuuutuy|~yyzzyzzyusppnjiijkoy~}|{|~||ywvuusvx|{yzzwrqnmmmnoswz~ytnmmkjiijklnqux{||ywursusv{~~}ytqnlkjlmnptxz{|{wuromnopoootrronljiihhgjmoswzyyxxvsrpqrqtwxzy{~~}|zyyvvw{}}wurqnortwuuvvtqpqropqqrqpsuvx|~~~||~yy{}}~{||xutppux{zw}~|zxyx{}{{zz}~zwyzyyyyywwuqqooqsuxy{zz}|yyx{zzwwywtolkiiijjllmooooppooopoprtvy|~{xvuuutvyyxx|~{vwxxvw}~{yuw|~~}|~|vxrwv{|{zxtstvxxzz{|yyz{{|}}~~|z||zyvronnmlmmnnqw|}}xxxz{}zvwxvvvxyyz}}{zzz||}}{|wutooopsstvvtroonnopsrstuuustrtvwyywutrrrsttuwy{{{xvzxwyyw|{{}|zxy~~}~~||}|oy||}}zvx}~~~}{{}||zxwxsuyxvuuxwwxxyvtsstrstw{~~ywyy{zz}|xy{yvuuxxusrsqqtttstusqmlllmnnopooonnmonmopsuwzywwwxvtutqooonmmmmnnnoopponmmmmnpuzyz|{{|~~zyz}z{~~~|{z||}~~~|xwvtuxvxxxy{{|wuwxz{}}~yx}{|}|xwwvuvz|~zy{}|~}{znsqnomlppqqstvutrstsuy~}{zwvuuvsroopommnmmllkklmotvuvzzy}~zy{xuy|~}||zzwwxzxxyywx{}|}}~ztuxwrsvyxww{}}wssuwww{}||~~|~}}{z}}~zutuvsusvx{{|||zyz|yw|}|x|||{xz{{zyyyvw{||~||{zxwwwvvzzz{}~~~}{~}z|{yzz{||zxx}|y{}zwy|zvuxyyz{~|}||~|~yyyyvx~}}{zwvsrvutwyxwy|{|}{{{{xx|}}~}}{{|x}|y}y~|}~{xxyww{|xtutonsvsrsuyxx{|{~|{yvsw|vxv|}zwzx{w~|yyttprusuw{zyxz|~|}}}}~~|~{xsqvwwtx|zwwxxzx{}}}~}}~{{yyyxzvursrqtty}}~~}{~~}{yxvwwxz{}|~~||s|uvrrvrorrwpstutvvzz~}|xxs{x|~}z}}}yyywywyw{yzwwssmrqssvyzy{|xx}~}~~{y||z|z|zqwotntu~|~{||}{||z{~|zxy{|~zup{s~p~rystvw{~~}}ywr{s|u~yvtyuoqoqsrsvr|m|orxojilvupoy|||o|kqutvpopmwn|rvtrpw|qysumvo|l}nzqsxwtunpmlitgfmrsknuzspo||ur~x}{xxz{~~}~z{z~{}wx{xsuu~{tuv{x||xwssll~luqutr{stqpqrqzx~~toxootkolnmmkmjojtlnmiohljtl|tz|{|{y}|yut~{x}|{uwnunqrsyqvyzv}uzwvqo}nmlsyx~{|y{}{zyx}uwv}wz~{y~~~z}xzzsr}u{t|oo|p}s~xzvv{~}wy|~xupnpmmmlklinoqrys}oyruortszs}||{~~}yytu||~~{|}z|~}{|z{uyt~|}{|zyyxwx{xxtyvyw{zzzxzuysytz||z}{ysoomnlpprtv{wyv{|ywuzrwr|{}|~~}{}z{xwuvtvyu{uzwwsvrtoutxwy{z{v|}y|z{|~zy||{usoqnplpm{{osonmlnpu{{rokhdc_^]]^]_`bdgjn{|vwz}of^\ZXXVWXXY[\^gnwujfcefkt{ic\YWUUUUUWY[^enowj``^_bgtme^ZVTSSRSUVX[]gjn{mb]]\\_eoym`\YWUUSUUUVY[derwl`^]]\bj~ri_\YWUTSTTUVX\ecwsj^^\\\`jwre]YVUTSQSRTVW\bbwvi_][[\_gosj_[VTPONNNNOPRY\bnvk_\ZXXY]fl}k`\XUSQPOOPRTW]^kw}ma^][\^emzni_b^ZYWWVWZ[`jmtgca_ablvxkd_]Z[YYXYY_fhqgfc`dgtxkf^^[[ZYZ\[ahjzjgfacfluwkf^^[[YYXZ[]hiz{fc_]\_`imuh_]XVTTSSRUVW]^eown`]\ZY[\`fn{j`]YWTRRPPQSSW\^imxme\[ZYZ\afqld][XVSTSTUWXZ_blxync^]]]^emtxmd`]\YZYYYYZ\bel{}pf___`cjsqid`^[ZZYYZ[\_fiywod^]]^_dowvld^]ZZWXXXY[\_jo}oj`^^_`dkvqjc`^]ZZZZY\]^_knzokcbdegky|nf_]ZZXZYZ[]^ciwzyokfgjiln}uqnlhdb__^^_chvwy||wplkfad___\[\\]celw}|srvst|{ytoomjheeb`_a``fmo|wwwzuzxvspqmonnmjhgdbdegksx}{uuvwx{zttmlkihieec_`__beknsy{tqnlhjjijijjjlnr~||yyz|}~zxxsnmmlljihdddegkouz}}}}}~vppmkjikiijjjlmmonqprtxz|}|~z{z||zspnmnou{|wqoosuwzz|}wwtqorvy~~}zyywuqomkiihfijlqz~vzonnkjjjiighiijlnquw{~~|{xupmlkjjkkllllmnmllonortwttxwsstsnnonnpvvw||wttsruttuvyyz{}{~}~~~}||zvvrrqrsw|}~{z{yy~zzxyxyzy{yz}~~}}|~~}}}ztpollklos{}|||{|{{xxyyustrooqtwz~~}zyvttstsrsssqqwttxvzz{|~~{vrnnkiihiknqx}{vrnnjiihggilnrv~|{yzxyzz~xtrpot|{upmljlmoovyzyyzxvwustrpqquxy{}|xqllkllklmsx}xvsvwvwtvoozrqvqx~~~}y{~z~|{{z|}ytokmntxx|{}}}}~~yronoonnnopoqqopvy~~vtronmmosuwz|~|ysrtv~wurnmlnlnnt~{womjnsy~{yx|~|zyvvroponlljjijmowyz|xutspomlijjjmosw||wsmlhigihghfihiklpqrutptz{}}z}|~}zvwxvsnlkkknmmmrz|~~}zvvqnjklmsrv~|}~zyzw}{uportxxz}yqnomkjnt~}~}}~|z~xvvzww~}xpnmlnnrru}~uu{~}{|~zwutwuyvyqoloronmqosuvyzwv{|wrqswzzonquzx}~{onoppuw|||somorswz|upooptx}}xsqoqrssv{z{~xrtrxqxy}vrox}xomklknqvy~{utsuwtytyy|{z|~~}~|ynigc`_`dgggmt{tnnkihkmmnmuz~|wvvurommmrv|yssu{z{vxwszxz~{{zxv|zrmljiikovz|}}unjfemhfjmsy{}|y|}vtomihinpotw}~}~zyxzxsu|~{|}~}ww{~zz|{{}~~|xtportwttrv|{}ysttsrqswz{|~}rpmnu|vpmklntz}xpoonrsy|}x|}}}zsonkjknqx~~|{vonlgihikmmowyuzwrmmnmmnsty~~|wvoou||tljkmqqtuy~~zttwvtuw{zx||{y|z}}~}}|~}~z{}{uvyvu}{uuvtzx~~~~~|{urwwztmllntwy{}wzwupoosxxyysnlmqonoyz|}~|{~xwpqnnlommotw{y|xzywxvvu|{tqoss{|vvx{swvwuqqnmnpy~wrposoonrwuyy|yzrmoqvv{wvwvxz}{z|ytmmlllotzuqqonkovz}uomllnqqsu~vvvrommonnnoonouxzxvvursov}yywwz{~~}y{w~}z~}}z{{}}~wzxz{y{{|{xtnlklt|}zvqpqqrrz}}|}~||xvwyz{|{~}zztomkklmmnmmmmklnstyz}{ttw|sooooryyxxy{xwvxwvttstqonmknpoqtqmmnoooqspponoqutsspqnroqpotvwx~zywxxvw|}prmkkigmnpsosw}|zookkkllllfchmr{womotnjeab__agq~|tzoqvyw~yzzownfb_^]^^fxzvvijjnox}~{pkq|mib`^[Z\cmqmjojurpmtv{~{x}ywqtt|nc`_^]Z[]fh}rovlvrqmigb`a^__hvuxwrznx}tlflhf`][\agynlmkxoxx|pmzlzou}tgd__^]]ZXY^cljemgvovgdrxpzmx}}~~zlhdea_[Y\`k}sjum}w{~~zoifehhjgeflo|sl}q}vsxqleab^^\]^ck~wmz|qmmkgdkgga`^bf~hfhsjvpsz}vsx~spjb^]\]\VWVZZ|b_^elkyvcosu{iugtrvz|upr{{uuebceda\\[ZZ[_lmiboo~j}{|us~~zzsrohea__]\[[^ctvjkvxzyyk|w{ywric__h`ecgkzrpqs|xvstqnee_^\_e~vrljfeadgim}wpnvo~}y{xmld`c```ei~{mkq|}|}wwwvsnnnle_]\]`fdiuynv~}y}nv~~rnlnqha]][XXXY_uzsuxtz}~soospg_]\\]\Z\`ntmm{|{|myxu|{~sokfa]\]_drop}u|}xoopsliefc_]Z\_iojinopozu~vwtohc^_]^ZXZ_ivopuyx{wnhiiig`]]f}{zwysru|{~|sjijie_\^bci~|~u|zuomkkgdcabahrwprxs{y}~}wyyzxtnqvpxmlhjkljxzotz}vy{|xwvtvvnkgfgge``cjxzxzv|zuplnnqvwxwvwvqnkfghksw}}|ypnmsyskilkmmnnnrry~}{tuy~polmpoolposrsst}yvrstysv~uuqnwzwpjkjjjios{tliikklmmnqsy~}}vw{{ursz{tstopnovz}|xwnkjkmlsyy~{||~x{}|{~zvw{~~zxw~y|y{}{s~~tqorusonssuvvuwuwnnlmmlkhkjlknsstx}}zzxuuvzuzqljjjhkmmpuytuuprqsoonnlmihjollow~~|yy|}}}zuxuuquzy||}}|t}~{vrtry{z{y{}~}wtv{|wvtzv~|{zuouvyy|~}~}}{}~xuxuyx|~|{}~|}|tspmnppopponqt{z}}{~~~~wzyy{xsqmjlntqotx}zwvx{|zyquoqkkihkknlortvrw{{~yutttwporwxzyzvyz}~ytv{y}{{|vwv|}}~|v}qwqoooootvwyywx}y|{}{}~~{~||~}|~~{xuuxy|z~}|||w{}|yvrtu}||}}y{xytrooqnpmqnoomooys||vzusokmkmkijlomonrtpqpqturupkjjmkmlvywyy}~~z{wwvx|zxvvxw|{~{ytswzzvusssrsy~{}~}|~~|}}z~}xvyvxty~~z|y}z~~~~~{zvrptvsrty{zz|uonomjljmlklntuxtzz{}yqomlkjkknssyxp{yzz~xrtvvspsvz~|||yx{~~{x~}|{ywz|{~}|{ywuvy|{~{yvqropnqqvx||{}|~yuvuwuxxzxwssy}{zz|wxwttrqsqrqwwpopttopqupoonpnnorqnqrstrpqy{yxwtuttrvtuzzyprsyssqssnmlooqppooopvw|xzzswuvxwxwvuxzxxwwvwy{}{|zxvvwtxv||z}xzurpquw|yzz~{xxsvruty{x|y{y~yx{{}||{|}}{~~~}}|x|{z}|}~}~{|||}}z{{}}}~~~{y{~~~|{|y{|zxzx{vvrrspvu~wvqsrvuwyv|xxzvvtwz{}z~}~wyywxww}}ztoononsvx|yyttsswtyy~uvywwuurvqqstwxxww|zxvuuuvvvxtupnoooquuvqqsolknprpnpoqprswzzyxyywzw||wz|}xywy{z~~{{uxtsrnspuoqoqposu|}}~{|zy}y~~~}|}{~|}{~~}zspooopqu}wvosqtpprqstxy~}~}xyuuyz}xtqomkiiikmoty}z~~z||{z{tyxz~~~zwvrqswx|}~~z{|~x{uvzvt||~}}{x}}{zx|wsoorqpooponnuwxyy~}{y}{vtppsnpvz{|~}{{~}~~xxtwstxuwvzz~zux{vrvwvstvvz||||z{yttuvvyz}~|}|ywvuy|}}~~xx}}~~~}|~}~xvuuy}|}z||{{}~z}~vtyyx|zuvpnpnwuux{||wwzvtvxsuutryzvxzzzx|~vxxzw{~||zywsrqqtxwtwzwx{}~yxvuuvwuxwxuxxy}|}|{{}~~}uzxxvx~~}|}yxwyyw}{}~~yspoopnqtw}}z{ywvtrnmnopoqoqqooorononnnooqrqqusqstw|wtw{|y}xxyytssopoopuzzy}zvuvtpvqsvutrxpouvwuxzz~}{y~~|{zwtwytw{~~~{z}}wz|y{z||~~~~}{~~~~}~~}|}~|~~~{|zyuwu{s~}{|~||~|y{~~}}wyyxx}~uttvs{w~~|x|yyw}|~{}|{}y|}|}|z}x}xz{vr}{y}v|vztxw~z|w{uzn}rvwo}nxm{rzuxvzryq{uvv{{tupqooozq}uwsvqps|x|{|~w~uzwvswovlvloooqwvyqnlosot{~|yx|x{|{{|~y{v{~~}zw{{}vvxr{xzx~v~wzy~~|z~}}{~yw~q}x|ooov}{yu|}{~r}u~}~|uvqonmpsootouovwwzxxxoqnmoltvwuqoxrxpu{{zrwyw|{}|x|}y{tz}v|xtz~{~|x~|}{~}yy{|zxvrlkjigfiggefffgilnu{zw}ojjc^__b^_a_`_`caabkvnqloibhlvv~wonjhijllmonlie_\ZXXWVW_gcxh_[_]]_i~sififilrvy{z~rje`^ZUUVVU\jkyjd[]_`em}zqnw}xld\XWTQOPTY\ejd^Z[Z]gtvonjoqvsic\XTQQSSRW\bi{ia^\\]am}{z~}jb]YYVWVVY\^fgmyojfflpvxmifcaccaa_____ciokghlr{mmmptld_^_^___a`_^^]]_`o|vmginz}omttyna^]^[]]^^`_^\\[\]et}}tokjqxtmiilnrga\]\^_efjjiea^[ZYZZfnsjfb_`emvfdfm|oicbdglmvqyrng_\WXVXZdwld`__fmwj`]]_jz~meb_bfny~pc[VSRSW\jla^]_iusga`exshbaacfnwzmb\VTSUX^ssc\Z[^hw~md_^`ltf_]^`fmv~pe]XRPOPTYa}wf][\_ijb]^_jzwh`]]_dm}l_ZVSSUX^lqf_^_fvleacjyqjcbdioqg^YTSSW\iwjeehnvlhhmxwokmnzxxle^[YXXZ^h|xjc_`donideirwlfdbdhmszxle_[YWXY]dozka]\]`hqtib^adkysleb`behmossqlhc][YXXY]cm}yja]Z[\cj}vjd^`^dkuxliabaegjosxvwomea]\YZ[_fuvljkms{spv}}vonmmorwx}yvslieca_djs}}otstwokjhikmvy}ztqjrkiknsopq{zuonmllmnopqqspoonlllnq}~zsmkklnsx~yuonmllmmnllknnspsnollmoqz~{troppsw|~wzllnjkljjklnovvxuutrstvwvsnst}~{usqtw|~}{vrropoonnnosqsssrtonllllnntst}{xxwx}~~ywuqmjmmllmoqvz~{wwwsnnmmmnoosrvwrupronmnnlmllmmqu~~z~||wytrxvxz{zzvxxwwyzy|~~~}~{yyvxuruonmlmlnlklkkjlmlmnostz{~}~~~|trpmoootsuxy~{|}xzzyxz|~v~~}}|}y~{~}{yz}|{{||ppty{wsrotxxwvspttnnoosw{}|wyy}y}~~~~~zy}u}|~xuvvrrtpytxwstqrpnlkjkkmqv|yuxqvyywz{wtonmmnqpopopvtzyz}}zwtoolmnklllmmnmkmnrvt|yx~yrnnllhhiiiikkmopopqpqqootuz{{{}~{|z~||~}{z|~~|z{}{}yx{~|~~~~z}{}~~~~|}~~~ywww|}{wuxz{yzz{{{zxvtsuvuuuvz{~~z|{{zxz}}}|{~~{zzz}~~~}}{z{{~|}~}||}}zytrnmmlmooppstutv{{|zywzywxuuuuttsrpnnmnnopuyxxxzyz|{uqooooprwwtuvwvtsvuvyzzz|}|zzvuronnmmjlmmmnnnnnpoprpooporrooqrppqprtwz~|{~}~~|~~~~~~~~~~~~~~~zxxwyywvyxz~{zyxvw}|{}~|yw{{|~~~}~~{wy|}|~~}~}{|~}}~{zzz||{xzyz{~}{||~|ywuvvvvttrqpqstuy{~~~~}~}|}{}}}~{|zzz}~~{zxzywxxww{~}{{|{}|~|}~}|~~|}{zz}~|xtstuwxy{zy{}~}~}~|zz{z{||}~}}~~}yxz}~~}}|{}|~~||z|||{yvtssrpoquxwwz{yxyzyxy{{vqrspoooqstuvwwy||}|{zyxvwxwyxspopopqqstyyyxxxy{{|{ywtrponmmmnoqrtwwyzxyxxyzz{|{z|{{zwwwvwxz}yyxx{z~}|{{zyzzz{|~||{zz}~|yyxuxou{x~}x~}|xvssuwxxz|~}{vuponmnprux|}{wsrqqooooqsttuwy}~}}}~yy{vrtrqpprtttrrsutsuvxyzzzyzyxvxxyyyy{||}}|}~}{|{x|~~}|}|}~|||}~~}|yxy{~~~}{zzy{}~}{zzxy{~~}~~~~}|z{zyx{zwwwxxz||~|yzxxvx{zx{xy{{{}{~~|~|}zxusnusutwvy{}}yzxvvwxwwz}~~}|{|}|zzz{{|}|~}}~{||zy||z|~~}~|}|y|{zxyzypw{x}}~~|xvvutx{{yvwvuwuuruwx{}~~~~~yvvvxuwyxywwusqpoortvw{~~zxwutvwuusruwxtswyy{~{{zwtporpnpuusprzyuvz}{{}~}yvzzwruzzxwy{{yzzzwx|{vsvxwtu{||{y{vuz|{|~~|y{~|vyzxsu|{y{~{wwx|ywy}~||y}~~z~vyoylq~pvxzy~|vprromylwztvropirshwmso}oykhlubnvf|qouzoomzlz{s|nssqp~ippg}tt}yws|o{uljokz|lvm~ikmbgltiusjkzpopozn|prqkvgtlfudxhmqkjxwjojm~~hm~llwy{x}{{vywwtz~wz|wo}|t||y~{yw|xyxztx{z|wzs}spyzzymrt}yw|{yx||||z}{|v{upvwnynuspwo~x}}{|{vmywywspzzsvy|u}|q}v{s}n|wx~w}rzt|nsyw{{yx~o|}psruz}wu~ss|ssvmunoum{mrvxv~tw~snk{kot||ov}}{z{|{|~|ro}|}|u|q}~vxy~z{{{z{v~zqnstporsoornwuuuyz|mzytvszm|n|ou}sy|}ztvmov{nvpl|o~yr{}}su~lnnryt}q~w{}y}ovx~mluftuy|x~}~~ry~o~spqqmuux~}yxzqmxkrqovzpw~ttyxwtjwqzvmtmm}nmvvtklytll~jtupmg~qwvmmo|uurmhrudzn_bttjso|no}s{muzwjlmjt|iw{it}~|o}ulsa}gpttx|m{v{}z}otr~~y||~zrtuw~}urr}w}xy}|z{y~zr~z}tzotyxwu}w~||s{zx|znyqnzpytzv~{wzxousrntsq{uqz|~~w}zxrusmrqjmpkvutuyunonoontopyp{}}}~~~zz{}}~wxvtnomkmkjiggkikmnvxvommiba_b^]]\\\YYZ[]_ggtsolhijkou||zuypr~tnmggd]]][\[[]bklqoicghhilv{ztuoqvsplkhb`]Z[YYZZ^hkrojjjklnxzwtrwvqqnljhba_\ZZ[\cikzzmkoosy}ztrv|}yssvpljheb^]^_cjny}tjimmv~rnnknsonpmnjc`^\\YW[[^dckwojdb__fimt|rkkkkjhlnmmlkkf`^ZYXWVX\`bgstnhd^^chnt~}mnqpmilrrnlnnic][[XWWY_dguwmha]_dkqvrmkjjfeikollnkga]\[Z[Y^ehvthgd\]ahswlonkjeihdedcb_[ZXWX[aiquje__chprqnkkjmtonljjd^^]]^^cosxkec\\_blxxvrtnnjuyjhcaa^\\]^^euukgbbgkv~zusrljg`]ZYYZ[^io~pgd_]agpzsruyzxwxphe`]\ZYY[]hs{kc_\Y\_fswolgegjmmlotrkb^ZWURRU[afv|j`^\Y\_gs}xrjeaafjjknnmif`][YYWY`hrrmldadhn|ywsnmqtyusz|umecc_]^fnvtomjikq~ummnswv}|wnid`__]bjpznkq|}uppsu|~|tme`_^\[[\__`eltwuv{}yrooqx|z|zvsnlsytjdcaa____^beeejr{}x|xxwxy{|rnjjmiikptrr~yponmlkkljkns}zvqtwzzwsomjgggfedgiknou{~}}{u{xz|~~}{}~yyvyxplkkmlmnt}{{{~{}vvrqwvqqqsztrqttonmrwx{{xxxwty}ywsqvx{}}|}xwuptposupqrxxvuspqooux{}zyxuxwwx{~}z{y|}{{}xzxtvsz~{|uvywvomosz|~~|}zz}~ytwspnmnprtvryy{~~{|{|||{}{rqpnnjkkkiggihgfgkntyz~}{zxvwxy}{{rvzwzxxvwxzyyxxwxwxttsuuxz{|~}{vsommlnoqsvwxz|~~}xwusrqqsvx||}}||||~~|yuywwqozwvyuy|wuqtqnnlkkkjjkkmmmnrw{}~~~|}{yuurrtssrqpprrssvvwxx}~}ywy||||z~~zxvuusuvz}~~~~~~|{}}}}|{{~~~}ywxwxxyzz{|}}~{xyzyyuwzz|zwtqqomlkkllmnnnnnoopqrrstvxwuutrrrsqommnmmllmmlllmnnnmnnqrwx{vzux|y{x{{}|zzxyz|~~{yyxxyz{|~}~~}zxxxxxx{{~zxz{yssvvwvyx|}|}~~}|zzzwuvtsttwvtspoonnlllkklnooopsvx{}~}|ywtrppoqtuwx{yyyvurqqppruuvuuxy{}~|zzwwyyzy{}|}~}}{z{yxz}~~~~~||{{|~~~{{zzwvuwwwvxyyyxvtrrursrwx|{yy{wwxwwxyxyvvuvuusqrrqonnnmmmnopqttuuxy|z}}||y{{}{{zxvtssrrssttutwwxyy|~~}}zyvwr|~|}{{yzz|||~~~}}~}}~}}}}zyz{~}{|~}z{xttqonnnoqvwyzyvtsttsrpqpponmmllkllmnnooonoooopsvwywqoonnnnnmkklnmmlmnmmnoqqrpopooprssvyz{{{zyz{|{zzzz{{~~|||}~~~~~|{~~~}|~~~}{yxz|||||~}~~}}z{|~~~}zxvtvvwwwwzyyyz|zywwyz}||}|zyzzy{{|}}|}}||xx{}|{zzzyyzz{z{zxxvwwx|~~||{z}}u}vxy}|~}|||~~~{zzz|~}rtz{~z||yvutwyttvxvuvuvuttttsu|{xwyz{z|{{xpponnnnnnmnqrpnnmmmostvtuwwuuuwvtuuwvxyxwvvwwxx{~~|zzxxxuvxz|}}}||}~~~~~}}}~|}}}}}}~~}{{|{|}}~~~~}|}~}}}}}|z{|~}~}{~~}}zywtvvrppqrsrrrrswz~~|ytsrqrprqpnonxu|~|z{zz|}~}}}}~}}}|{y}}}}}~|{}}{|~~}|~}~~z{qs}{z|}{}zxyyz|~~|ywvyy{zvyywvxx{~~ywwxxtpoprqqrsuvxwwxz|~|~|xwvwvwuuttrppqnnnoqswxyy{yxwxxvrpopqponmlijjlnootuvvxyywyxxwxz{|vuvxyywwuuwz||{{z{~zz{|{{|}zxwywvrsy}}~}zwttustrstvuquwxwy{}}{{z{yvwuuvyx~~~}}~~}|}~}}~~}|{zywvssrvy{~~|yxy|~}~|zxvtuyxwy{{}|zuokjkmorvyz|}{soononrwz}|xyou{wx{x}~yussstvxw}}~{w{|}{wvwtvuuy{}}zxwwxzxy|}}}||}}yzz{||}}}zyxxxvwuwz}|~}||||{zyvvwtstvwz|{z}}~|yxwxxy{|}~~{}z{z||~|}~~~}|z{zzyzyx{{{zy{z{z|}}~|xvxyxwy}~}~zxvrqqttuy|~|zzyvutommmmnqswz{yz~{zzzysrqprtvvvwz~~}|zzyyxwvvx}|~|{yyx{}{xturpnnorttsutvuvy{zxxwvsomojjkjlnkotvwvvyzwvuwwvspoonlljijlnooprsx{}{xuusrqqqomlmommnpty}}~}~|zvsrqqorrurqpoorv|~}~{zxwvvxz}~~}||{|~}yuuxz}}~~}|yuqoopqruxz}|}}}{yyxz|~|}||||{xwwzx{|xuurqqrrqoopqstwv{|{}}zyzzzwusrrsqpoonmprrx}}ztrsqssuxwwvsqoopqssuvyzzzxssrrqqrqtuutttvyyz||||}|~}}|}~|yxoturxzy}}||}}zxspmlkkkjkmnoooppqtx{~zzzzwxxvvvxzyyz|yvw{|{}||{wxwwttywwxzxupoonoty~~~{|}|~}}zz}}~{wvuwxxzxyyvrtz}|wyzy}}~}xxtqqoosux{}}}{|||z{yzxy|||{y{|~}{yxvsrtxzzz{zy~~~y}|~~~}{yxxzyz|~zxvw{~{yvsosuuxvvxtqonptwy{||z{ywvxy|~~~~ysrqpqnoomlnrx{}zxxyyywutuvvttwuxy{z}}{~}zyvz|}~~~}}}|ywwuqooppruvwtrqommmlkjjjkkklmnooooooqomlllkklmnmnnooopoppsuuusrrsonnnonlkkllkllmmmllmmooprqqqrrqoosqtsstwxxz~~z{|zzxx{}~~~}zz}~|wxv}|zuwuuttwxzz}~||}~zwvsmknoqoosuusy~{{}~}{ywzzwssy{xx{}{xz}{{{~}{}}ytuvtsvy|xz~}||t{|wz{v|~{|uuxonosywuxx{yyz~~|{~zw|}}~~{xwwxwzz~~|}z}}}~~~}xtrpqonosqnnrusswyuqruwtuxz}{{yywsqnomnropqtusqqpoqqqrttvtu{zwvzywwxzwuvxxtrutrpppqooppqstzxxvwzuww{yy|}}~||zww|}wy|||yz~zwv{|}~|~~||~|{|}}~}|}~}{yxtvz}xz|~{z|~}|}|}{tooonooqqrutstsssqrtttvzzvxy|}xvxs|ztvupurotsurrssrtuupoppronmoqonnrsqsqqqqqoopvxqswyuonqqtssxxwx|{wuz}{y{||{wrtxy|{~~~{zzy~|xxtwvxuqvy}{vvz{~{~}~{z{yutsvyuqosvz}}~|{||~{z|~~~}}{yz||x||xwz}~}zzxyyw{{{xvtsuvvz{}}{x{{yx{~||~ywvuwtstwywyz}}}~}{vwxw{x{zz|zuwwrrrusw}~}~}{{~}}|{zz{~|z|~}|{~}z||~~}}|w{vtrvywstv|}~~zy{~~ysoonoononmkknqrtuwzyz|zyxxwz{||zxtroooopprssu}~~|}|wx{|~}{{{yww}~~}~}|~}xx|}|w{~}yy{z{{~~~{yywwxzzuqtsrpqsqqpooprsspruuwuutqoonmlloooosxzz|~|zuqppoooorw||}{~~~~|{}}~}{}~{{{|}}}~~}|}{||~~|~{xxyzxvwuxy{}|z~}~~{yyyz}}~||~y~{~zyy{{}|~}{ywx}|zxvvtqpquzwxyzzy~~z|~}xyz{||~}}~{|}~||}{|~~|}~~}}zxy{{~}}{yz|xuxtusnmmmmmonmllopoqtusopquvz|||~{xvutuwwuvsuqoqonnorqruuuwtux|}}}~{yxxyxz{}}|z|~~}|zyz{yz{{|}}yzzzzzxwx{~}|xvwwxwvxx{|{~~~}~~}|~}zyz{{yw|~}zyvwvvvx}|}~~}}|}|~{z|}~~}|~}~~}}~~~z{|z{z{|ywvxvwuuzyxwxwwvusrqtsponnnnooopsststxz}{
\ No newline at end of file
diff --git a/star_lock/images/main/icon_lockDetail_hangUp.png b/star_lock/images/main/icon_lockDetail_hangUp.png
new file mode 100644
index 00000000..5c3e9a8c
Binary files /dev/null and b/star_lock/images/main/icon_lockDetail_hangUp.png differ
diff --git a/star_lock/images/main/icon_lockDetail_monitoringAnswerCalls.png b/star_lock/images/main/icon_lockDetail_monitoringAnswerCalls.png
new file mode 100644
index 00000000..872d99a3
Binary files /dev/null and b/star_lock/images/main/icon_lockDetail_monitoringAnswerCalls.png differ
diff --git a/star_lock/images/main/icon_lockDetail_monitoringUnTalkback.png b/star_lock/images/main/icon_lockDetail_monitoringUnTalkback.png
new file mode 100644
index 00000000..e97963ab
Binary files /dev/null and b/star_lock/images/main/icon_lockDetail_monitoringUnTalkback.png differ
diff --git a/star_lock/ios/Podfile b/star_lock/ios/Podfile
index 573ef8f2..b09a90ba 100644
--- a/star_lock/ios/Podfile
+++ b/star_lock/ios/Podfile
@@ -90,6 +90,7 @@ post_install do |installer|
## dart: PermissionGroup.bluetooth
'PERMISSION_BLUETOOTH=1',
+ 'PERMISSION_MICROPHONE=1',
## dart: PermissionGroup.appTrackingTransparency
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
diff --git a/star_lock/ios/Runner.xcodeproj/project.pbxproj b/star_lock/ios/Runner.xcodeproj/project.pbxproj
index ce180cb5..1a0ea32a 100644
--- a/star_lock/ios/Runner.xcodeproj/project.pbxproj
+++ b/star_lock/ios/Runner.xcodeproj/project.pbxproj
@@ -10,7 +10,6 @@
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3364C3F42B0C902100AA5ABC /* lcokStarMain.png in Resources */ = {isa = PBXBuildFile; fileRef = 3364C3F32B0C902100AA5ABC /* lcokStarMain.png */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
- 3EF1E85D6F1EE0C0DCF8449F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09D8B2FA2B26BA5BFF31AB2A /* Pods_Runner.framework */; };
8297E4102AE75AC500E886FA /* XSFlutterManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8297E40E2AE75AC500E886FA /* XSFlutterManager.m */; };
8297E43A2AE75BEE00E886FA /* Msg.m in Sources */ = {isa = PBXBuildFile; fileRef = 8297E4132AE75BEE00E886FA /* Msg.m */; };
8297E43B2AE75BEE00E886FA /* udp_data_class.m in Sources */ = {isa = PBXBuildFile; fileRef = 8297E4172AE75BEE00E886FA /* udp_data_class.m */; };
@@ -125,6 +124,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ CD1142BE3A076363977FB03C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED1F1A04428235FB1B6FD471 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -141,9 +141,10 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 09D8B2FA2B26BA5BFF31AB2A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 0BEB3ADCCEC961E2916B9004 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 265F8968B3CDB02494B21491 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
3364C3F32B0C902100AA5ABC /* lcokStarMain.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lcokStarMain.png; sourceTree = ""; };
33BF41252A96174D009D92E2 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
@@ -303,9 +304,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- 99F6494C6B1A24363CCC6D32 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
- BDB5B400176768766E85D5EB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
- DC20461C9C4624BEB2DCD779 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ DAD8EE71BE7C05B99667C256 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ ED1F1A04428235FB1B6FD471 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -313,7 +313,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 3EF1E85D6F1EE0C0DCF8449F /* Pods_Runner.framework in Frameworks */,
+ CD1142BE3A076363977FB03C /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -323,9 +323,9 @@
780FAA17A040B9755AD6154A /* Pods */ = {
isa = PBXGroup;
children = (
- DC20461C9C4624BEB2DCD779 /* Pods-Runner.debug.xcconfig */,
- 99F6494C6B1A24363CCC6D32 /* Pods-Runner.release.xcconfig */,
- BDB5B400176768766E85D5EB /* Pods-Runner.profile.xcconfig */,
+ 0BEB3ADCCEC961E2916B9004 /* Pods-Runner.debug.xcconfig */,
+ 265F8968B3CDB02494B21491 /* Pods-Runner.release.xcconfig */,
+ DAD8EE71BE7C05B99667C256 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "";
@@ -348,18 +348,18 @@
isa = PBXGroup;
children = (
8297E4132AE75BEE00E886FA /* Msg.m */,
- 8297E4142AE75BEE00E886FA /* UI.h */,
- 8297E4152AE75BEE00E886FA /* HttpManager.h */,
- 8297E4162AE75BEE00E886FA /* UDP */,
- 8297E41B2AE75BEE00E886FA /* Sformat.m */,
- 8297E41C2AE75BEE00E886FA /* Pub.h */,
- 8297E41D2AE75BEE00E886FA /* sysInfo.h */,
8297E41E2AE75BEE00E886FA /* Msg.h */,
- 8297E41F2AE75BEE00E886FA /* Sformat.h */,
- 8297E4202AE75BEE00E886FA /* HttpManager.m */,
+ 8297E4142AE75BEE00E886FA /* UI.h */,
8297E4212AE75BEE00E886FA /* UI.m */,
- 8297E4222AE75BEE00E886FA /* sysInfo.m */,
+ 8297E41C2AE75BEE00E886FA /* Pub.h */,
8297E4232AE75BEE00E886FA /* Pub.m */,
+ 8297E41F2AE75BEE00E886FA /* Sformat.h */,
+ 8297E41B2AE75BEE00E886FA /* Sformat.m */,
+ 8297E4152AE75BEE00E886FA /* HttpManager.h */,
+ 8297E4202AE75BEE00E886FA /* HttpManager.m */,
+ 8297E41D2AE75BEE00E886FA /* sysInfo.h */,
+ 8297E4222AE75BEE00E886FA /* sysInfo.m */,
+ 8297E4162AE75BEE00E886FA /* UDP */,
8297E4242AE75BEE00E886FA /* Talk */,
);
path = Utils;
@@ -428,7 +428,6 @@
8297E4562AE75D4E00E886FA /* OpenPwd.m */,
8297E4572AE75D4E00E886FA /* P2pTest.h */,
8297E4582AE75D4E00E886FA /* Web.m */,
- 8297E4592AE75D4E00E886FA /* Call.h */,
8297E45A2AE75D4E00E886FA /* Setting.m */,
8297E45B2AE75D4E00E886FA /* FaceUpload.m */,
8297E45C2AE75D4E00E886FA /* EquAdd.m */,
@@ -439,8 +438,9 @@
8297E4612AE75D4E00E886FA /* P2pTest.m */,
8297E4622AE75D4E00E886FA /* EquConfig.h */,
8297E4632AE75D4E00E886FA /* Setting.h */,
- 8297E4642AE75D4E00E886FA /* Call.m */,
8297E4652AE75D4E00E886FA /* Web.h */,
+ 8297E4592AE75D4E00E886FA /* Call.h */,
+ 8297E4642AE75D4E00E886FA /* Call.m */,
);
path = NextPage;
sourceTree = "";
@@ -565,14 +565,6 @@
path = img;
sourceTree = "";
};
- 9304F75C378DB3447BB2408C /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- 09D8B2FA2B26BA5BFF31AB2A /* Pods_Runner.framework */,
- );
- name = Frameworks;
- sourceTree = "";
- };
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -591,7 +583,7 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
780FAA17A040B9755AD6154A /* Pods */,
- 9304F75C378DB3447BB2408C /* Frameworks */,
+ B8F749EDCB3FB55CC9253F00 /* Frameworks */,
);
sourceTree = "";
};
@@ -635,6 +627,14 @@
name = "Supporting Files";
sourceTree = "";
};
+ B8F749EDCB3FB55CC9253F00 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ ED1F1A04428235FB1B6FD471 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -642,14 +642,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- 03BC065FC3064C0B6EE97546 /* [CP] Check Pods Manifest.lock */,
+ 818C8F3353996877C27F8ACC /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- D9B107A6B141D5F15BC356F2 /* [CP] Copy Pods Resources */,
+ C87CD71185302EE14BA1323E /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -783,7 +783,23 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 03BC065FC3064C0B6EE97546 /* [CP] Check Pods Manifest.lock */ = {
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
+ };
+ 818C8F3353996877C27F8ACC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -805,22 +821,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
- isa = PBXShellScriptBuildPhase;
- alwaysOutOfDate = 1;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
- );
- name = "Thin Binary";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
- };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -836,7 +836,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
- D9B107A6B141D5F15BC356F2 /* [CP] Copy Pods Resources */ = {
+ C87CD71185302EE14BA1323E /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -989,6 +989,7 @@
ENABLE_BITCODE = NO;
GCC_NO_COMMON_BLOCKS = NO;
INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1106,6 +1107,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "cn.star-lock.starLock";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
+ STRIP_INSTALLED_PRODUCT = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
@@ -1238,6 +1240,7 @@
ENABLE_BITCODE = NO;
GCC_NO_COMMON_BLOCKS = NO;
INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1355,6 +1358,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "cn.star-lock.starLock";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
+ STRIP_INSTALLED_PRODUCT = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
@@ -1381,6 +1385,7 @@
ENABLE_BITCODE = NO;
GCC_NO_COMMON_BLOCKS = NO;
INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1498,6 +1503,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "cn.star-lock.starLock";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
+ STRIP_INSTALLED_PRODUCT = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
diff --git a/star_lock/ios/Runner/Info.plist b/star_lock/ios/Runner/Info.plist
index 0720c34d..2014a808 100644
--- a/star_lock/ios/Runner/Info.plist
+++ b/star_lock/ios/Runner/Info.plist
@@ -46,7 +46,7 @@
NSLocationWhenInUseUsageDescription
应用在前台的时候可以搜到更新的位置信息
NSMicrophoneUsageDescription
- 用于音频插件
+ 应用请求麦克风用来通话
NSPhotoLibraryUsageDescription
用于相册
UIApplicationSceneManifest
diff --git a/star_lock/ios/Runner/XSTalkManager/Utils/Talk/talk_Class.m b/star_lock/ios/Runner/XSTalkManager/Utils/Talk/talk_Class.m
index 8d1ee810..5fdbe11e 100755
--- a/star_lock/ios/Runner/XSTalkManager/Utils/Talk/talk_Class.m
+++ b/star_lock/ios/Runner/XSTalkManager/Utils/Talk/talk_Class.m
@@ -97,6 +97,7 @@ static bool isVedioStop;
[arecord AudioEnd];
}
+// 保持连接
- (void)connect_ini {
_connect_reg_time = [Sformat timestamp];
Byte connect_bb[512];
@@ -642,22 +643,24 @@ static bool isVedioStop;
isVedioStop= NO;
-
+ // 呼叫应答,呼叫成功
memcpy(bbsend, bb, 62);
bbsend[7] = 1;
bbsend[8] = 4;
NSLog(@"replay talk ip=%@;port=%d", ip, port);
[[Pub getApp].udp sendDataInMain:bbsend length:512 toHost:ip toPort:port];
+ // 门锁发送视频
memcpy(bbsend, bb, 62);
bbsend[7] = 1;
bbsend[8] = 5;
NSLog(@"replay talk ip=%@;port=%d", ip, port);
[[Pub getApp].udp sendDataInMain:bbsend length:512 toHost:ip toPort:port];
- //定时报告
+ // 定时报告
[self connect_ini];
[self connect];
+ // 设置定时器,得到连接时间
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self selector:@selector(connect)
userInfo:nil repeats:YES];
@@ -855,13 +858,19 @@ static char cnt;
}
else{//视频数据
NSData *data1 = [[NSData alloc] initWithBytes:bb length:60+32];
+ // 音视频数据开始下标
BAGLEN = [Pub getShortFromByte:bb at:POS_blen + 2];
+ // 获取帧序号 63
iframe_index = [Pub getShortFromByte:bb at:POS_iframe_index];
+ // 获取帧长度 65
alen = [Pub getShortFromByte:bb at:POS_alen];
- blen = [Pub getShortFromByte:bb at:POS_blen];
+ // 当前包号 71
bag_index = bb[POS_bag_index]&0xff ;
+ // 总包数 69
bag_num = bb[POS_bag_num]&0xff;
-
+ // 数据长度 73
+ blen = [Pub getShortFromByte:bb at:POS_blen];
+
if(iframe_index != iframe.iframe_index){
iframe = [[IframeInfo alloc] init];
iframe.iframe_index = iframe_index;
diff --git a/star_lock/ios/Runner/XSTalkManager/Utils/UDP/UdpHelper.m b/star_lock/ios/Runner/XSTalkManager/Utils/UDP/UdpHelper.m
index 81bcd528..c5764edd 100755
--- a/star_lock/ios/Runner/XSTalkManager/Utils/UDP/UdpHelper.m
+++ b/star_lock/ios/Runner/XSTalkManager/Utils/UDP/UdpHelper.m
@@ -42,15 +42,18 @@
{
_udp_datas = [[NSMutableArray alloc] init];
udp_data_class *list;
- _udp_list = [[udp_data_class alloc] init];
+// _udp_list = [[udp_data_class alloc] init];
for (int i=0; i-1){
[[Pub getApp] OpenDoorFail:[Pub getEquidFrombb:bb at:9]];
diff --git a/star_lock/lib/blue/blue_manage.dart b/star_lock/lib/blue/blue_manage.dart
index 5f703b58..ee1c4670 100644
--- a/star_lock/lib/blue/blue_manage.dart
+++ b/star_lock/lib/blue/blue_manage.dart
@@ -1,4 +1,3 @@
-
import 'dart:async';
import 'package:flutter_easyloading/flutter_easyloading.dart';
@@ -14,7 +13,7 @@ import 'reciver_data.dart';
//连接状态回调
typedef ConnectStateCallBack = Function(DeviceConnectionState connectionState);
-class BlueManage{
+class BlueManage {
FlutterReactiveBle? _flutterReactiveBle;
final List scanDevices = [];
@@ -25,7 +24,7 @@ class BlueManage{
// 用来写入的特征id
Uuid characteristicIdWrite = Uuid.parse("fff2");
- // 发送监听发送事件
+ // 监听发送事件
StreamSubscription? _sendStreamSubscription;
// 监听蓝牙扫描的事件
StreamSubscription? _scanSubscription;
@@ -37,13 +36,14 @@ class BlueManage{
// 当前连接设备的mac地址
String connectDeviceMacAddress = "";
// 监听蓝牙连接状态
- DeviceConnectionState? deviceConnectionState = DeviceConnectionState.disconnected;
+ DeviceConnectionState? deviceConnectionState =
+ DeviceConnectionState.disconnected;
static BlueManage? _manager;
BlueManage._init();
- static BlueManage? shareManager(){
- _manager ??= BlueManage._init();
+ static BlueManage? shareManager() {
+ _manager ??= BlueManage._init();
_manager!._initBlue();
return _manager;
}
@@ -51,34 +51,42 @@ class BlueManage{
factory BlueManage() => shareManager()!;
BlueManage? get manager => shareManager();
- void _initBlue(){
+ void _initBlue() {
_flutterReactiveBle ??= FlutterReactiveBle();
print("蓝牙功能初始化了");
_initSendStreamSubscription();
}
void _initSendStreamSubscription() {
- _sendStreamSubscription ??= EventBusManager().eventBus!.on().listen((
- EventSendModel model) {
- if (model.sendChannel == DataChannel.ble) {
- // managerAppWriteData(model.data);
- writeCharacteristicWithResponse(model.data);
- }
- });
+ _sendStreamSubscription ??= EventBusManager()
+ .eventBus!
+ .on()
+ .listen((EventSendModel model) {
+ if (model.sendChannel == DataChannel.ble) {
+ // managerAppWriteData(model.data);
+ writeCharacteristicWithResponse(model.data);
+ }
+ });
}
/// 开始扫描蓝牙设备
void startScan({List? idList}) {
scanDevices.clear();
- _scanSubscription = _flutterReactiveBle!.scanForDevices(withServices: idList??[]).listen((device) {
+ _scanSubscription = _flutterReactiveBle!
+ .scanForDevices(withServices: idList ?? [])
+ .listen((device) {
// 判断名字为空的直接剔除
- if(device.name.isEmpty){
+ if (device.name.isEmpty) {
return;
}
// print("startScanDevice:$device");
- if (((device.serviceUuids.isNotEmpty ? device.serviceUuids[0] : "").toString().contains("758824")) && (device.rssi >= -100)) {
+ if (((device.serviceUuids.isNotEmpty ? device.serviceUuids[0] : "")
+ .toString()
+ .contains("758824")) &&
+ (device.rssi >= -100)) {
// 查询id相同的元素
- final knownDeviceIndex = scanDevices.indexWhere((d) => d.id == device.id);
+ final knownDeviceIndex =
+ scanDevices.indexWhere((d) => d.id == device.id);
// 不存在的时候返回-1
if (knownDeviceIndex >= 0) {
scanDevices[knownDeviceIndex] = device;
@@ -93,10 +101,13 @@ class BlueManage{
}
/// 连接监听状态
- Future connect(String deviceName, ConnectStateCallBack connectStateCallBack, {bool? isFrist = false ,bool isShowLoading = true}) async {
+ Future connect(
+ String deviceName, ConnectStateCallBack connectStateCallBack,
+ {bool? isFrist = false, bool isShowLoading = true}) async {
connectDeviceName = deviceName;
// 判断数组列表里面是否有这个设备
- final knownDeviceIndex = scanDevices.indexWhere((d) => d.name == deviceName);
+ final knownDeviceIndex =
+ scanDevices.indexWhere((d) => d.name == deviceName);
if (knownDeviceIndex >= 0) {
// 存在的时候赋值
@@ -108,7 +119,7 @@ class BlueManage{
Timer.periodic(const Duration(milliseconds: 1000), (timer) {
///定时任务
print("timer index:$index");
- if(index >= 4){
+ if (index >= 4) {
// 当超过5秒的时候取消定时任务 弹窗显示连接失败
completer.complete();
timer.cancel();
@@ -116,9 +127,10 @@ class BlueManage{
deviceConnectionState = DeviceConnectionState.disconnected;
connectStateCallBack(deviceConnectionState!);
Toast.show(msg: "未扫描到要连接的设备,请确保在设备附近,设备未被连接,设备已打开");
- }else{
+ } else {
// 每秒判断数组列表里面是否有这个设备
- final knownDeviceIndex = scanDevices.indexWhere((d) => d.name == deviceName);
+ final knownDeviceIndex =
+ scanDevices.indexWhere((d) => d.name == deviceName);
if (knownDeviceIndex >= 0) {
// 存在的时候销毁定时器,赋值
completer.complete();
@@ -135,23 +147,33 @@ class BlueManage{
await completer.future;
// print("111111");
}
- print("connectDeviceId:$connectDeviceMacAddress connectDeviceName:$connectDeviceName");
+ print(
+ "connectDeviceId:$connectDeviceMacAddress connectDeviceName:$connectDeviceName");
- if(connectDeviceMacAddress.isEmpty){
+ if (connectDeviceMacAddress.isEmpty) {
return;
}
- _currentConnectionStream = _flutterReactiveBle!.connectToDevice(id: connectDeviceMacAddress, connectionTimeout: const Duration(milliseconds: 10000)).listen((connectionStateUpdate) async {
+ _currentConnectionStream = _flutterReactiveBle!
+ .connectToDevice(
+ id: connectDeviceMacAddress,
+ connectionTimeout: const Duration(milliseconds: 10000))
+ .listen((connectionStateUpdate) async {
// 获取状态
deviceConnectionState = connectionStateUpdate.connectionState;
- print('deviceConnectionState:$deviceConnectionState connectionStateUpdate.connectionState:${connectionStateUpdate.connectionState}');
- if(connectionStateUpdate.connectionState == DeviceConnectionState.connected){
+ print(
+ 'deviceConnectionState:$deviceConnectionState connectionStateUpdate.connectionState:${connectionStateUpdate.connectionState}');
+ if (connectionStateUpdate.connectionState ==
+ DeviceConnectionState.connected) {
// 如果状态是连接的开始发现服务
try {
- _subScribeToCharacteristic(QualifiedCharacteristic(characteristicId: characteristicIdSubscription, serviceId: Uuid.parse("fff0"), deviceId: connectDeviceMacAddress));
+ _subScribeToCharacteristic(QualifiedCharacteristic(
+ characteristicId: characteristicIdSubscription,
+ serviceId: Uuid.parse("fff0"),
+ deviceId: connectDeviceMacAddress));
print('Discovering services finished');
- if(isFrist == true){
+ if (isFrist == true) {
// 第一次添加锁的时候需要先获取公钥
IoSenderManage.getPublicKey(lockId: deviceName);
}
@@ -164,59 +186,68 @@ class BlueManage{
print('Error occurred when discovering services: $e');
rethrow;
}
- } else{
+ } else {
deviceConnectionState = connectionStateUpdate.connectionState;
connectStateCallBack(deviceConnectionState!);
}
- print('ConnectionState for device $connectDeviceMacAddress : ${connectionStateUpdate.connectionState}');
- }, onError: (Object e){
+ print(
+ 'ConnectionState for device $connectDeviceMacAddress : ${connectionStateUpdate.connectionState}');
+ }, onError: (Object e) {
deviceConnectionState = DeviceConnectionState.disconnected;
connectStateCallBack(deviceConnectionState!);
- print('Connecting to device $connectDeviceMacAddress resulted in error $e');
- }
- );
+ print(
+ 'Connecting to device $connectDeviceMacAddress resulted in error $e');
+ });
}
// 重新连接
- Future judgeReconnect(String deviceName, ConnectStateCallBack stateCallBack, {bool isShowLoading = true}) async {
- if(deviceConnectionState != DeviceConnectionState.connected){
- connect(deviceName, (state){
+ Future judgeReconnect(
+ String deviceName, ConnectStateCallBack stateCallBack,
+ {bool isShowLoading = true}) async {
+ if (deviceConnectionState != DeviceConnectionState.connected) {
+ connect(deviceName, (state) {
stateCallBack(deviceConnectionState!);
}, isShowLoading: false);
- }else{
+ } else {
stateCallBack(deviceConnectionState!);
}
}
// 听上报来的数据,参数来自前面扫描到的结果
var allData = [];
+ int? dataLen;
_subScribeToCharacteristic(QualifiedCharacteristic characteristic) {
- _flutterReactiveBle!.subscribeToCharacteristic(characteristic).listen((data) {
+ _flutterReactiveBle!.subscribeToCharacteristic(characteristic).listen(
+ (data) {
// code to handle incoming data
- print("subscribeToCharacteristic: deviceId = ${characteristic.deviceId} characteristicId =${characteristic.characteristicId}---上报来的数据data = $data");
- var dataLen = 0;// 高16位用来指示后面数据块内容的长度
-
- if((data[0] == 0xEF)&&(data[1] == 0x01)&&(data[2] == 0xEE)&&(data[3] == 0x02)){
+ print(
+ "subscribeToCharacteristic: deviceId = ${characteristic.deviceId} characteristicId =${characteristic.characteristicId}---上报来的数据data = $data");
+ if ((data[0] == 0xEF) &&
+ (data[1] == 0x01) &&
+ (data[2] == 0xEE) &&
+ (data[3] == 0x02)) {
// 当包有头时
// 判断是否需要分包
- dataLen = data[8] * 256 + data[9];
- // print("dataLen1111:$dataLen getDataLength:${data.length}");
- if(dataLen + 12 > data.length){
+ dataLen = data[8] * 256 + data[9]; // 高16位用来指示后面数据块内容的长度
+ print("dataLen1111:$dataLen getDataLength:${data.length}");
+ if (dataLen! + 12 > data.length) {
// 当前包的长度小于实际的包时 分包添加 不解析
allData.addAll(data);
- }else{
+ } else {
// 当前包的长度小于实际的包时 不分包 解析
allData.addAll(data);
+ print("dataLen2222:$dataLen getDataLength:${data.length}");
CommandReciverManager.appDataReceive(allData);
// 发送完解析初始化数组
allData = [];
}
- }else{
+ } else {
// 当包没有头时 是分包的包 直接添加
allData.addAll(data);
// var len = allData[8] * 256 + allData[9];
- // print("dataLen222:$dataLen");
- if(dataLen <= (allData.length - 14)){
+ print("dataLen3333:$dataLen");
+ if ((dataLen! + 14) <= allData.length) {
+ print("44444数据被解析了");
// 当长度小于等于当前包的数据时 直接解析数据
CommandReciverManager.appDataReceive(allData);
// 发送完解析初始化数组
@@ -231,17 +262,23 @@ class BlueManage{
// 写入
Future writeCharacteristicWithResponse(List value) async {
- QualifiedCharacteristic characteristic = QualifiedCharacteristic(characteristicId: characteristicIdWrite, serviceId: serviceId, deviceId: connectDeviceMacAddress);
+ QualifiedCharacteristic characteristic = QualifiedCharacteristic(
+ characteristicId: characteristicIdWrite,
+ serviceId: serviceId,
+ deviceId: connectDeviceMacAddress);
// print('Write with characteristicId:${characteristic.characteristicId} serviceId:${characteristic.serviceId} deviceId:${characteristic.deviceId} value : $value \nhexStr:${radixHex16String(value)}');
- int mtuLength = await _flutterReactiveBle!.requestMtu(deviceId: characteristic.deviceId, mtu: 250);
- print("mtuLength:$mtuLength");
+ int mtuLength = await _flutterReactiveBle!
+ .requestMtu(deviceId: characteristic.deviceId, mtu: 250);
+ // print("mtuLength:$mtuLength");
try {
List valueList = value;
List subData = splitList(valueList, mtuLength);
print('得到的分割数据:$subData');
for (int i = 0; i < subData.length; i++) {
- await _flutterReactiveBle!.writeCharacteristicWithResponse(characteristic, value: subData[i]).then((value) async {
+ await _flutterReactiveBle!
+ .writeCharacteristicWithResponse(characteristic, value: subData[i])
+ .then((value) async {
await Future.delayed(const Duration(milliseconds: 1))
.then((value) async {
print('分包发送成功了');
@@ -257,13 +294,17 @@ class BlueManage{
}
// 读取
- Future> readCharacteristic(QualifiedCharacteristic characteristic) async {
+ Future> readCharacteristic(
+ QualifiedCharacteristic characteristic) async {
try {
- final result = await _flutterReactiveBle!.readCharacteristic(characteristic);
+ final result =
+ await _flutterReactiveBle!.readCharacteristic(characteristic);
print("readListresult$result");
return result;
} on Exception catch (e, s) {
- print('Error occurred when reading ${characteristic.characteristicId} : $e',);
+ print(
+ 'Error occurred when reading ${characteristic.characteristicId} : $e',
+ );
rethrow;
}
}
@@ -271,8 +312,8 @@ class BlueManage{
Future writeCharacteristicWithoutResponse(
QualifiedCharacteristic characteristic, List value) async {
try {
- await _flutterReactiveBle!.writeCharacteristicWithoutResponse(characteristic,
- value: value);
+ await _flutterReactiveBle!
+ .writeCharacteristicWithoutResponse(characteristic, value: value);
} on Exception catch (e, s) {
// ignore: avoid_print
print(s);
@@ -299,9 +340,9 @@ class BlueManage{
}
}
- disposed(){
+ disposed() {
_sendStreamSubscription?.cancel();
_currentConnectionStream?.cancel();
_scanSubscription?.cancel();
}
-}
\ No newline at end of file
+}
diff --git a/star_lock/lib/blue/io_protocol/io_configuringWifi.dart b/star_lock/lib/blue/io_protocol/io_configuringWifi.dart
index d7865623..19500262 100644
--- a/star_lock/lib/blue/io_protocol/io_configuringWifi.dart
+++ b/star_lock/lib/blue/io_protocol/io_configuringWifi.dart
@@ -17,6 +17,8 @@ class SenderConfiguringWifiCommand extends SenderProtocol {
String? password;
int? numberOfServers;
List? listOfServers;
+ int? numberOfPhone;
+ List? listOfPhone;
List? token;
int? needAuthor;
List? publicKey;
@@ -29,6 +31,8 @@ class SenderConfiguringWifiCommand extends SenderProtocol {
this.password,
this.numberOfServers,
this.listOfServers,
+ this.numberOfPhone,
+ this.listOfPhone,
this.token,
this.needAuthor,
this.publicKey,
@@ -78,6 +82,16 @@ class SenderConfiguringWifiCommand extends SenderProtocol {
// listOfServers
subData.addAll(listOfServers!);
+ // NumberOfPhone
+ subData.add(numberOfPhone!);
+
+ // listOfPhone
+ listOfPhone!.forEach((element) {
+ int phoneLength = utf8.encode(element).length;
+ subData.addAll(utf8.encode(element));
+ subData = getFixedLengthList(subData, 20 - phoneLength);
+ });
+
// token
// subData.addAll(token!);
diff --git a/star_lock/lib/blue/io_reply.dart b/star_lock/lib/blue/io_reply.dart
index 05e9b7c2..bd63ff10 100644
--- a/star_lock/lib/blue/io_reply.dart
+++ b/star_lock/lib/blue/io_reply.dart
@@ -1,7 +1,6 @@
+
import 'io_type.dart';
-
-
abstract class Reply{
CommandType? commandType;
diff --git a/star_lock/lib/blue/io_tool/io_manager.dart b/star_lock/lib/blue/io_tool/io_manager.dart
index 4fa5cdbf..7b8e51e8 100644
--- a/star_lock/lib/blue/io_tool/io_manager.dart
+++ b/star_lock/lib/blue/io_tool/io_manager.dart
@@ -21,7 +21,7 @@ class IoManager {
///蓝牙传输协议
void bleTransmission() => _dataTransmissionMode = DataTransmissionMode.ble;
- ///割草机协议帧序号
+ ///协议帧序号
int _commandIndex = 1;
configCommandIdx(int idx) => _commandIndex = idx;
Future increaseCommandIndex() async {
diff --git a/star_lock/lib/blue/io_tool/io_model.dart b/star_lock/lib/blue/io_tool/io_model.dart
index 2c8f64cb..9651fff6 100644
--- a/star_lock/lib/blue/io_tool/io_model.dart
+++ b/star_lock/lib/blue/io_tool/io_model.dart
@@ -3,11 +3,13 @@ import 'package:uuid/uuid.dart';
///发送数据类
enum DataChannel{
- ble
+ ble,
+ udp
}
extension Extension on DataChannel {
bool get isBLE => this == DataChannel.ble;
+ bool get isUDP => this == DataChannel.udp;
}
class EventSendModel {
diff --git a/star_lock/lib/blue/sender_data.dart b/star_lock/lib/blue/sender_data.dart
index 33ad7b73..38ef5ad9 100644
--- a/star_lock/lib/blue/sender_data.dart
+++ b/star_lock/lib/blue/sender_data.dart
@@ -24,9 +24,7 @@ class CommandSenderManager {
bool canSendControlCommand = false;
//TODO:发送常规数据
- Future managerSendData ({
- required SenderProtocol command,
- CommandSendCallBack? callBack}) async {
+ Future managerSendData ({required SenderProtocol command, CommandSendCallBack? callBack}) async {
if (callBack != null) {
// if (!BluetoothManager().connected) {
print('❌ 蓝牙断开了');
diff --git a/star_lock/lib/blue/sender_manage.dart b/star_lock/lib/blue/sender_manage.dart
index 4a01ce4d..e1df2776 100644
--- a/star_lock/lib/blue/sender_manage.dart
+++ b/star_lock/lib/blue/sender_manage.dart
@@ -519,6 +519,8 @@ class IoSenderManage {
required String? password,
required int? numberOfServers,
required List? listOfServers,
+ required int? numberOfPhone,
+ required List? listOfPhone,
required List? token,
required int? needAuthor,
required List? publicKey,
@@ -532,6 +534,8 @@ class IoSenderManage {
password: password,
numberOfServers: numberOfServers,
listOfServers: listOfServers,
+ numberOfPhone: numberOfPhone,
+ listOfPhone: listOfPhone,
token: token,
needAuthor: needAuthor,
publicKey: publicKey,
diff --git a/star_lock/lib/main.dart b/star_lock/lib/main.dart
index b6118c6c..14669831 100644
--- a/star_lock/lib/main.dart
+++ b/star_lock/lib/main.dart
@@ -1,9 +1,12 @@
import 'package:aliyun_push/aliyun_push.dart';
+import 'package:audioplayers/audioplayers.dart';
// import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter_pcm_sound/flutter_pcm_sound.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_sound/flutter_sound.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:star_lock/tools/app_manager.dart';
@@ -52,6 +55,9 @@ final RouteObserver routeObserver = RouteObserver();
class _MyAppState extends State with WidgetsBindingObserver, BaseWidget {
final _aliyunPush = AliyunPush();
var _deviceId = "";
+ // final audioPlayer = AudioPlayer();
+ final FlutterSoundPlayer _audioPlayer = FlutterSoundPlayer();
+ late List allDataBytes;
@override
Widget build(BuildContext context) {
@@ -81,7 +87,8 @@ class _MyAppState extends State with WidgetsBindingObserver, BaseWidget {
}
}
// print("localelocalelocalelocalelocale locale:${locale} locale.languageCode:${locale.languageCode} locale.countryCode:${locale.countryCode} supportedLocales:${supportedLocales}");
- AppManager().setLanCode(code: '${locale!.languageCode}_${locale.countryCode}');
+ AppManager()
+ .setLanCode(code: '${locale!.languageCode}_${locale.countryCode}');
return locale;
},
// locale: StoreService.to.getLanguageCode().isNotEmpty ? appDept.deptSupportedLocales.where((element) => element.languageCode == StoreService.to.getLanguageCode()).first : Get.deviceLocale,
@@ -187,25 +194,25 @@ Future _setCommonServices() async {
// Get.log(PlatformInfoService.to.info.version);
}
-void openBlueScan(){
- if(Platform.isIOS){
+void openBlueScan() {
+ if (Platform.isIOS) {
print("有蓝牙权限开始扫描");
startScanAction();
- }else{
+ } else {
getMicrophonePermission().then((value) {
if (value) {
// 有权限
print("有蓝牙权限开始扫描");
startScanAction();
- }else{
+ } else {
//没有权限
- openAppSettings();//打开app系统设置
+ openAppSettings(); //打开app系统设置
}
});
}
}
-void startScanAction(){
+void startScanAction() {
BlueManage().startScan();
}
@@ -226,18 +233,3 @@ Future getMicrophonePermission() async {
}
return false;
}
-
-//播放本地音频
-// void playAudio() async {
-// const String audioPath = "assets/ring1.mp3";
-// AudioPlayer audioPlayer = AudioPlayer();
-// // int result = await audioPlayer.play(audioPath, isLocal: true);
-
-// if (result == 1) {
-// // 播放成功
-// print("Audio playback successful!");
-// } else {
-// // 播放失败
-// print("Error playing audio");
-// }
-// }
diff --git a/star_lock/lib/main/lockDetail/checkingIn/checkingInStaff/checkingInSetAddStaff/checkingInAddStaff_logic.dart b/star_lock/lib/main/lockDetail/checkingIn/checkingInStaff/checkingInSetAddStaff/checkingInAddStaff_logic.dart
index f1432dcf..a3eb457f 100644
--- a/star_lock/lib/main/lockDetail/checkingIn/checkingInStaff/checkingInSetAddStaff/checkingInAddStaff_logic.dart
+++ b/star_lock/lib/main/lockDetail/checkingIn/checkingInStaff/checkingInSetAddStaff/checkingInAddStaff_logic.dart
@@ -1,4 +1,3 @@
-
import 'dart:async';
import 'package:get/get.dart';
@@ -138,35 +137,49 @@ class CheckingInAddStaffLogic extends BaseGetXController{
have: state.appUnHaveAccount.value ? "2" : "1",
staffName: state.staffNameController.text,
countryCode: state.countryCode.value,
- usernameType:usernameType,
+ usernameType: usernameType,
);
- if(entity.errorCode!.codeIsSuccessful){
+ if (entity.errorCode!.codeIsSuccessful) {
eventBus.fire(RefreshCheckInStaffListDataEvent());
Get.close(2);
- }else if(entity.errorCode! == 425){
+ } else if (entity.errorCode! == 425) {
Toast.show(msg: entity.errorMsg!);
}
}
//获取密码请求
void getKeyboardPwdRequest() async {
- if(state.staffNameController.text.isEmpty){
+ if (state.staffNameController.text.isEmpty) {
Toast.show(msg: "请输入姓名");
return;
}
+ // var entity = await ApiRepository.to.getPasswordKey(
+ // "0",
+ // '0',
+ // state.staffNameController.text,
+ // 2.toString(),
+ // '0',
+ // state.getKeyInfosData.value.lockId.toString(),
+ // '0',
+ // "0",
+ // '0',
+ // 0,
+ // 0,
+ // 1);
var entity = await ApiRepository.to.getPasswordKey(
- "0",
- '0',
- state.staffNameController.text,
- 2.toString(),
- '0',
- state.getKeyInfosData.value.lockId.toString(),
- '0',
- "0",
- '0',
- 0,
- 0,
- 1);
+ endDate: "0",
+ isExclusive: '0',
+ keyboardPwdName: state.staffNameController.text,
+ keyboardPwdType: 2.toString(),
+ keyboardPwdVersion: '0',
+ lockId: state.getKeyInfosData.value.lockId.toString(),
+ operatorUid: '0',
+ startDate: '0',
+ timezoneRawOffSet: '0',
+ startHours: 0,
+ endHours: 0,
+ isCoerced: 1);
+
if (entity.errorCode!.codeIsSuccessful) {
print('获取密码成功');
if (entity.data != null) {
diff --git a/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_logic.dart b/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_logic.dart
new file mode 100644
index 00000000..0ef8c6dc
--- /dev/null
+++ b/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_logic.dart
@@ -0,0 +1,48 @@
+import 'package:get/get.dart';
+import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_state.dart';
+import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyDetail/keyOperationRecordEntity.dart';
+import 'package:star_lock/main/lockDetail/passwordKey/passwordKey_perpetual/passwordKeyEntity.dart';
+import 'package:star_lock/network/api_repository.dart';
+import 'package:star_lock/tools/baseGetXController.dart';
+import 'package:star_lock/tools/toast.dart';
+
+class ElectronicKeyDetailChangeDateLogic extends BaseGetXController {
+ final ElectronicKeyDetailChangeDateState state =
+ ElectronicKeyDetailChangeDateState();
+
+ //更新密码请求
+ Future updatePwdRequest() async {
+ PasswordKeyEntity entity = await ApiRepository.to.updatePasswordKey(
+ lockId: state.itemData.value.lockId!,
+ keyboardPwdId: state.pwdId.value.toString(),
+ keyboardPwdName: state.inputNameController.text,
+ newKeyboardPwd: state.inputPwdController.text,
+ startDate: state.startDate.value,
+ endDate: state.endDate.value,
+ changeType: state.changeType.value,
+ hoursStart: state.hoursStart.value,
+ hoursEnd: state.hoursEnd.value,
+ isCoerced: state.isCoerced.value);
+ if (entity.errorCode!.codeIsSuccessful) {
+ Toast.show(msg: "修改成功");
+ Get.back();
+ }
+ }
+
+ //修改钥匙名称请求
+ Future updateKeyDateRequest() async {
+ KeyOperationRecordEntity entity = await ApiRepository.to.updateKeyDate(
+ state.itemData.value.keyId.toString(),
+ state.itemData.value.lockId.toString(),
+ state.failureDateTime.millisecondsSinceEpoch.toString(),
+ state.endDay.value,
+ '',
+ state.effectiveDateTime.millisecondsSinceEpoch.toString(),
+ state.startDay.value,
+ state.weekDays.value);
+ if (entity.errorCode!.codeIsSuccessful) {
+ Toast.show(msg: "修改成功");
+ Get.back();
+ }
+ }
+}
diff --git a/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_page.dart b/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_page.dart
index 125126cf..52239b04 100644
--- a/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_page.dart
+++ b/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_page.dart
@@ -1,16 +1,9 @@
import 'package:flutter/material.dart';
-// import 'package:flutter_pickers/pickers.dart';
-// import 'package:flutter_pickers/time_picker/model/date_mode.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
-import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyDetail/keyOperationRecordEntity.dart';
-import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyList/entity/ElectronicKeyListEntity.dart';
-import 'package:star_lock/main/lockDetail/passwordKey/passwordKey_perpetual/passwordKeyEntity.dart';
-import 'package:star_lock/network/api_repository.dart';
-import 'package:star_lock/tools/baseGetXController.dart';
+import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_logic.dart';
import 'package:star_lock/tools/pickers/pickers.dart';
import 'package:star_lock/tools/pickers/time_picker/model/date_mode.dart';
-import 'package:star_lock/tools/toast.dart';
import '../../../../../app_settings/app_colors.dart';
import '../../../../../tools/commonItem.dart';
@@ -27,34 +20,11 @@ class ElectronicKeyDetailChangeDate extends StatefulWidget {
class _ElectronicKeyDetailChangeDateState
extends State {
- ElectronicKeyListItem itemData = ElectronicKeyListItem();
-
- String _selectEffectiveDate = ''; //生效时间
- String _selectFailureDate = ''; //失效时间
- late DateTime _effectiveDateTime;
- late DateTime _failureDateTime;
- late String endDay = '';
- late String startDay = '';
- late List weekDays = [];
- late String pwdId = '';
- late String lockId = '';
- late String fromType = ''; // 1 从指纹详情进入
+ final logic = Get.put(ElectronicKeyDetailChangeDateLogic());
+ final state = Get.find().state;
@override
Widget build(BuildContext context) {
- dynamic obj = ModalRoute.of(context)?.settings.arguments;
- if (obj != null && (obj["itemData"] != null)) {
- itemData = obj["itemData"];
- }
- if (obj != null && (obj["pwdId"] != null)) {
- pwdId = obj["pwdId"];
- }
- if (obj != null && (obj["lockId"] != null)) {
- lockId = obj["lockId"];
- }
- if (obj != null && (obj["fromType"] != null)) {
- fromType = obj["fromType"];
- }
return Scaffold(
backgroundColor: AppColors.mainBackgroundColor,
appBar: TitleAppBar(
@@ -69,12 +39,12 @@ class _ElectronicKeyDetailChangeDateState
style: TextStyle(color: Colors.white, fontSize: 24.sp),
),
onPressed: () {
- if (fromType == "1") {
+ if (state.fromType.value == "1") {
} else {
- if (lockId.isNotEmpty && pwdId.isNotEmpty) {
- updatePwdRequest();
+ if (state.lockId.value != 0 && state.pwdId.value.isNotEmpty) {
+ logic.updatePwdRequest();
} else {
- updateKeyDateRequest();
+ logic.updateKeyDateRequest();
}
}
},
@@ -88,77 +58,37 @@ class _ElectronicKeyDetailChangeDateState
Widget buildMainUI() {
return Column(
children: [
- CommonItem(
+ Obx(() => CommonItem(
leftTitel: TranslationLoader.lanKeys!.effectiveTime!.tr,
- rightTitle: _selectEffectiveDate,
+ rightTitle: state.selectEffectiveDate.value,
isHaveLine: true,
isHaveDirection: true,
action: () {
Pickers.showDatePicker(context, mode: DateMode.YMDHM,
onConfirm: (p) {
- setState(() {
- _selectEffectiveDate =
- '${p.year}-${intToStr(p.month!)}-${intToStr(p.day!)} ${intToStr(p.hour!)}:${intToStr(p.minute!)}';
- _effectiveDateTime = DateTime.parse(_selectEffectiveDate);
- });
+ state.selectEffectiveDate.value =
+ '${p.year}-${intToStr(p.month!)}-${intToStr(p.day!)} ${intToStr(p.hour!)}:${intToStr(p.minute!)}';
+ state.effectiveDateTime =
+ DateTime.parse(state.selectEffectiveDate.value);
});
- }),
- CommonItem(
+ })),
+ Obx(() => CommonItem(
leftTitel: TranslationLoader.lanKeys!.failureTime!.tr,
- rightTitle: _selectFailureDate,
+ rightTitle: state.selectFailureDate.value,
isHaveDirection: true,
action: () {
Pickers.showDatePicker(context, mode: DateMode.YMDHM,
onConfirm: (p) {
- setState(() {
- _selectFailureDate =
- '${p.year}-${intToStr(p.month!)}-${intToStr(p.day!)} ${intToStr(p.hour!)}:${intToStr(p.minute!)}';
- _failureDateTime = DateTime.parse(_selectFailureDate);
- });
+ state.selectFailureDate.value =
+ '${p.year}-${intToStr(p.month!)}-${intToStr(p.day!)} ${intToStr(p.hour!)}:${intToStr(p.minute!)}';
+ state.failureDateTime =
+ DateTime.parse(state.selectFailureDate.value);
});
- }),
+ })),
],
);
}
- //修改钥匙名称请求
- Future updateKeyDateRequest() async {
- KeyOperationRecordEntity entity = await ApiRepository.to.updateKeyDate(
- itemData.keyId.toString(),
- itemData.lockId.toString(),
- _failureDateTime.millisecondsSinceEpoch.toString(),
- endDay,
- '',
- _effectiveDateTime.millisecondsSinceEpoch.toString(),
- startDay,
- weekDays);
- if (entity.errorCode!.codeIsSuccessful) {
- print("修改要是名称成功啦啦啦啦啦");
- Toast.show(msg: "修改成功");
- setState(() {
- Navigator.pop(context);
- });
- }
- }
-
- //更新密码请求
- Future updatePwdRequest() async {
- PasswordKeyEntity entity = await ApiRepository.to.updatePasswordKey(
- lockId,
- pwdId,
- '',
- '',
- _effectiveDateTime.millisecondsSinceEpoch.toString(),
- _failureDateTime.millisecondsSinceEpoch.toString(),
- '');
- if (entity.errorCode!.codeIsSuccessful) {
- Toast.show(msg: "修改成功");
- setState(() {
- Navigator.pop(context);
- });
- }
- }
-
String intToStr(int v) {
return (v < 10) ? "0$v" : "$v";
}
diff --git a/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_state.dart b/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_state.dart
new file mode 100644
index 00000000..cac37195
--- /dev/null
+++ b/star_lock/lib/main/lockDetail/electronicKey/electronicKeyDetail/electronicKeyDetailChangeDate/electronicKeyDetailChangeDate_state.dart
@@ -0,0 +1,43 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyList/entity/ElectronicKeyListEntity.dart';
+
+class ElectronicKeyDetailChangeDateState {
+ final itemData = ElectronicKeyListItem().obs;
+ final TextEditingController inputPwdController = TextEditingController();
+ final TextEditingController inputNameController = TextEditingController();
+ final changeType = '1'
+ .obs; //1-通过APP走蓝牙修改,不传默认1,必需先通过APP SDK蓝牙修改后调用该接口 2-通过网关或WiFi锁修改,如果是WiFi锁或有连接网关,则可以传2,直接调用该接口修改生效
+ final isCoerced = '1'.obs; //胁迫指纹:1;非胁迫指纹:2(胁迫指纹开锁触发报警)
+ final hoursStart = 0.obs;
+ final hoursEnd = 0.obs;
+ final startDate = 0.obs;
+ final endDate = 0.obs;
+ final pwdId = ''.obs;
+ final lockId = 0.obs;
+ final fromType = ''.obs;
+ final selectEffectiveDate = ''.obs; //生效时间
+ final selectFailureDate = ''.obs; //失效时间
+ DateTime effectiveDateTime = DateTime.now();
+ DateTime failureDateTime = DateTime.now();
+ final endDay = ''.obs;
+ final startDay = ''.obs;
+ final weekDays = [].obs;
+
+ ElectronicKeyDetailChangeDateState() {
+ Map map = Get.arguments;
+ // itemData.value = map["itemData"];
+ if ((map["itemData"] != null)) {
+ itemData.value = map["itemData"];
+ }
+ if ((map["pwdId"] != null)) {
+ pwdId.value = map["pwdId"];
+ }
+ if ((map["lockId"] != null)) {
+ lockId.value = map["lockId"];
+ }
+ if ((map["fromType"] != null)) {
+ fromType.value = map["fromType"];
+ }
+ }
+}
diff --git a/star_lock/lib/main/lockDetail/lcokSet/configuringWifi/configuringWifi_logic.dart b/star_lock/lib/main/lockDetail/lcokSet/configuringWifi/configuringWifi_logic.dart
index 98395c11..2e86b05c 100644
--- a/star_lock/lib/main/lockDetail/lcokSet/configuringWifi/configuringWifi_logic.dart
+++ b/star_lock/lib/main/lockDetail/lcokSet/configuringWifi/configuringWifi_logic.dart
@@ -1,5 +1,7 @@
import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:network_info_plus/network_info_plus.dart';
@@ -14,12 +16,15 @@ import '../../../../blue/io_tool/io_manager.dart';
import '../../../../blue/io_tool/io_tool.dart';
import '../../../../blue/io_tool/manager_event_bus.dart';
import '../../../../blue/sender_manage.dart';
+import '../../../../login/login/entity/LoginData.dart';
+import '../../../../mine/mine/starLockMine_state.dart';
import '../../../../network/api_repository.dart';
import '../../../../tools/storage.dart';
import 'configuringWifi_state.dart';
class ConfiguringWifiLogic extends BaseGetXController{
final ConfiguringWifiState state = ConfiguringWifiState();
+ final StarLockMineState getInfostate = StarLockMineState();
Future getWifiLockServiceIpAndPort() async {
var entity = await ApiRepository.to.getWifiLockServiceIpAndPort();
@@ -58,7 +63,7 @@ class ConfiguringWifiLogic extends BaseGetXController{
// WIFI配网结果
Future _replySenderConfiguringWifi(Reply reply) async {
- int status = reply.data[6];
+ int status = reply.data[5];
print("status:$status");
switch(status){
@@ -97,6 +102,7 @@ class ConfiguringWifiLogic extends BaseGetXController{
serversList.add(type2);
}
+ var phoneList = [getInfostate.mobile()];
IoSenderManage.senderConfiguringWifiCommand(
keyID: state.lockSetInfoData.value.lockBasicInfo!.keyId.toString(),
userID: await Storage.getUid(),
@@ -104,6 +110,8 @@ class ConfiguringWifiLogic extends BaseGetXController{
password: state.wifiPWDController.text,
numberOfServers: state.configuringWifiEntity.value.data!.serviceNum,
listOfServers: serversList,
+ numberOfPhone: phoneList.length,
+ listOfPhone: phoneList,
token: tokenData,
needAuthor: 1,
publicKey: publicKeyDataList,
@@ -147,18 +155,35 @@ class ConfiguringWifiLogic extends BaseGetXController{
var serversList = [];
for(int i = 0; i addresses = await InternetAddress.lookup(item.serviceIp!);
+ var itemList = addresses.first.address.split(".");
+ for (var element in itemList) {
+ serversList.add(int.parse(element));
+ }
+ print('Resolved google.com to address: ${addresses.first.address} serversList:${serversList}');
- double typeDouble = int.parse(item.port!) / 256;
- int type1 = typeDouble.toInt();
- int type2 = int.parse(item.port!) % 256;
- serversList.add(type1);
- serversList.add(type2);
+ double typeDouble = int.parse(item.port!) / 256;
+ int type1 = typeDouble.toInt();
+ int type2 = int.parse(item.port!) % 256;
+ serversList.add(type1);
+ serversList.add(type2);
+ }
}
+ String? phone = '';
+ final data = await Storage.getString('userLoginData');
+ if (data != null && data.isNotEmpty) {
+ phone = LoginData.fromJson(jsonDecode(data)).mobile;
+ }
+ var phoneList = [phone!];
+ print("phoneList:$phoneList");
+
IoSenderManage.senderConfiguringWifiCommand(
keyID: state.lockSetInfoData.value.lockBasicInfo!.keyId.toString(),
userID: await Storage.getUid(),
@@ -166,6 +191,8 @@ class ConfiguringWifiLogic extends BaseGetXController{
password: state.wifiPWDController.text,
numberOfServers: state.configuringWifiEntity.value.data!.serviceNum,
listOfServers: serversList,
+ numberOfPhone: phoneList.length,
+ listOfPhone: phoneList,
token: getTokenList,
needAuthor: 1,
publicKey: publicKeyDataList,
diff --git a/star_lock/lib/main/lockDetail/lcokSet/lockSet/lockSet_logic.dart b/star_lock/lib/main/lockDetail/lcokSet/lockSet/lockSet_logic.dart
index 6659a444..c03654c0 100644
--- a/star_lock/lib/main/lockDetail/lcokSet/lockSet/lockSet_logic.dart
+++ b/star_lock/lib/main/lockDetail/lcokSet/lockSet/lockSet_logic.dart
@@ -427,7 +427,7 @@ class LockSetLogic extends BaseGetXController {
password: state.passwordTF.text,
);
if (entity.errorCode!.codeIsSuccessful) {
- // deletLockInfoData();
+ deletLockInfoData();
blockDeletNumberCheckPasswordCallback();
// if(state.currentDeviceUUid.value.isEmpty){
diff --git a/star_lock/lib/main/lockDetail/lcokSet/lockSet/lockSet_page.dart b/star_lock/lib/main/lockDetail/lcokSet/lockSet/lockSet_page.dart
index 4bd7da7a..6dd3a28e 100644
--- a/star_lock/lib/main/lockDetail/lcokSet/lockSet/lockSet_page.dart
+++ b/star_lock/lib/main/lockDetail/lcokSet/lockSet/lockSet_page.dart
@@ -1,4 +1,3 @@
-
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -49,49 +48,45 @@ class _LockSetPageState extends State with RouteAware {
children: [
Expanded(
child: Obx(() => ListView(
- children: getListWidget(),
- )),
+ children: getListWidget(),
+ )),
),
],
- )
- );
+ ));
}
// 根据权限显示不同的列表
- List getListWidget(){
-
- print("state.lockBasicInfo.value.isLockOwner:${state.lockBasicInfo.value.isLockOwner} state.lockBasicInfo.value.keyRight:${state.lockBasicInfo.value.keyRight}");
- if(state.lockBasicInfo.value.isLockOwner == 1 || state.lockBasicInfo.value.keyRight == 1){
+ List getListWidget() {
+ print(
+ "state.lockBasicInfo.value.isLockOwner:${state.lockBasicInfo.value.isLockOwner} state.lockBasicInfo.value.keyRight:${state.lockBasicInfo.value.keyRight}");
+ if (state.lockBasicInfo.value.isLockOwner == 1 ||
+ state.lockBasicInfo.value.keyRight == 1) {
// 超级管理员、授权管理员
return getAllWidget();
- }else{
+ } else {
return getNormalWidget();
}
}
// 普通用户
List getNormalWidget() {
- var showWidgetArr = [
+ var showWidgetArr = [
// 基本信息
CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.basicInformation!.tr,
+ leftTitel: TranslationLoader.lanKeys!.basicInformation!.tr,
rightTitle: "",
isHaveLine: false,
isHaveDirection: true,
action: () {
- Get.toNamed(Routers.basicInformationPage, arguments: {
- 'lockSetInfoData': state.lockSetInfoData.value
- });
+ Get.toNamed(Routers.basicInformationPage,
+ arguments: {'lockSetInfoData': state.lockSetInfoData.value});
}),
SizedBox(height: 10.h),
// 自动闭锁
Obx(() => Visibility(
- visible:
- state.lockFeature.value.autoLock == 1 ? true : false,
+ visible: state.lockFeature.value.autoLock == 1 ? true : false,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.automaticBlocking!.tr,
+ leftTitel: TranslationLoader.lanKeys!.automaticBlocking!.tr,
rightTitle: (state.lockSettingInfo.value.autoLock ?? 0) > 0
? "${state.lockSetInfoData.value.lockSettingInfo!.autoLockSecond ?? 0}s"
: TranslationLoader.lanKeys!.closed!.tr,
@@ -102,32 +97,27 @@ class _LockSetPageState extends State with RouteAware {
// ? "${state.lockSetInfoData.value.lockSetting!.autoLockSecond ?? 0}s"
// : TranslationLoader.lanKeys!.closed!.tr),
action: () {
- Get.toNamed(Routers.automaticBlockingPage,
- arguments: {
- 'lockSetInfoData': state.lockSetInfoData.value,
- // 'lockBasicInfo': state.lockBasicInfo.value
- });
+ Get.toNamed(Routers.automaticBlockingPage, arguments: {
+ 'lockSetInfoData': state.lockSetInfoData.value,
+ // 'lockBasicInfo': state.lockBasicInfo.value
+ });
}))),
// 常开模式
Obx(() => Visibility(
visible: state.lockFeature.value.passageMode == 1 ? true : false,
// visible:true,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.normallyOpenMode!.tr,
- rightTitle:
- (state.lockSettingInfo.value.passageMode ?? 0) ==
- 1
+ leftTitel: TranslationLoader.lanKeys!.normallyOpenMode!.tr,
+ rightTitle: (state.lockSettingInfo.value.passageMode ?? 0) == 1
? TranslationLoader.lanKeys!.opened!.tr
: TranslationLoader.lanKeys!.closed!.tr,
isHaveLine: true,
isHaveDirection: true,
action: () {
- Get.toNamed(Routers.normallyOpenModePage,
- arguments: {
- 'lockSetInfoData': state.lockSetInfoData.value,
- 'lockBasicInfo': state.lockBasicInfo.value
- });
+ Get.toNamed(Routers.normallyOpenModePage, arguments: {
+ 'lockSetInfoData': state.lockSetInfoData.value,
+ 'lockBasicInfo': state.lockBasicInfo.value
+ });
}))),
Visibility(
visible: true,
@@ -143,8 +133,7 @@ class _LockSetPageState extends State with RouteAware {
})),
SizedBox(height: 30.h),
Container(
- padding:
- EdgeInsets.only(left: 20.w, right: 20.w, bottom: 30.h),
+ padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 30.h),
child: SubmitBtn(
btnName: TranslationLoader.lanKeys!.delete!.tr,
isDelete: true,
@@ -165,25 +154,20 @@ class _LockSetPageState extends State with RouteAware {
var showWidgetArr = [
// 基本信息
CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.basicInformation!.tr,
+ leftTitel: TranslationLoader.lanKeys!.basicInformation!.tr,
rightTitle: "",
isHaveLine: false,
isHaveDirection: true,
action: () {
- Get.toNamed(Routers.basicInformationPage, arguments: {
- 'lockSetInfoData': state.lockSetInfoData.value
- });
+ Get.toNamed(Routers.basicInformationPage,
+ arguments: {'lockSetInfoData': state.lockSetInfoData.value});
}),
SizedBox(height: 10.h),
// 门磁
Obx(() => Visibility(
- visible: state.lockFeature.value.doorStatus == 1
- ? true
- : false,
+ visible: state.lockFeature.value.doorStatus == 1 ? true : false,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.doorMagnetic!.tr,
+ leftTitel: TranslationLoader.lanKeys!.doorMagnetic!.tr,
rightTitle: "",
isHaveLine: true,
isHaveDirection: true,
@@ -193,12 +177,9 @@ class _LockSetPageState extends State with RouteAware {
}))),
// 无线键盘
Obx(() => Visibility(
- visible: state.lockFeature.value.wirelessKeyboard == 1
- ? true
- : false,
+ visible: state.lockFeature.value.wirelessKeyboard == 1 ? true : false,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.wirelessKeyboard!.tr,
+ leftTitel: TranslationLoader.lanKeys!.wirelessKeyboard!.tr,
rightTitle: "",
isHaveLine: true,
isHaveDirection: true,
@@ -208,9 +189,7 @@ class _LockSetPageState extends State with RouteAware {
}))),
// 照明
Obx(() => Visibility(
- visible: state.lockFeature.value.lightingTime == 1
- ? true
- : false,
+ visible: state.lockFeature.value.lightingTime == 1 ? true : false,
child: CommonItem(
leftTitel: TranslationLoader.lanKeys!.illumination!.tr,
rightTitle: "",
@@ -233,11 +212,9 @@ class _LockSetPageState extends State with RouteAware {
SizedBox(height: 10.h),
// 自动闭锁
Obx(() => Visibility(
- visible:
- state.lockFeature.value.autoLock == 1 ? true : false,
+ visible: state.lockFeature.value.autoLock == 1 ? true : false,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.automaticBlocking!.tr,
+ leftTitel: TranslationLoader.lanKeys!.automaticBlocking!.tr,
rightTitle: (state.lockSettingInfo.value.autoLock ?? 0) > 0
? "${state.lockSetInfoData.value.lockSettingInfo!.autoLockSecond ?? 0}s"
: TranslationLoader.lanKeys!.closed!.tr,
@@ -248,17 +225,15 @@ class _LockSetPageState extends State with RouteAware {
// ? "${state.lockSetInfoData.value.lockSetting!.autoLockSecond ?? 0}s"
// : TranslationLoader.lanKeys!.closed!.tr),
action: () {
- Get.toNamed(Routers.automaticBlockingPage,
- arguments: {
- 'lockSetInfoData': state.lockSetInfoData.value
- });
+ Get.toNamed(Routers.automaticBlockingPage, arguments: {
+ 'lockSetInfoData': state.lockSetInfoData.value
+ });
}))),
// 锁声音
Obx(() {
var titleStr = "";
if ((state.lockSettingInfo.value.lockSound ?? 0) == 1) {
- switch (
- state.lockSettingInfo.value.lockSoundVolume ?? 0) {
+ switch (state.lockSettingInfo.value.lockSoundVolume ?? 0) {
case 1:
titleStr = TranslationLoader.lanKeys!.low!.tr;
break;
@@ -279,9 +254,7 @@ class _LockSetPageState extends State with RouteAware {
titleStr = TranslationLoader.lanKeys!.closed!.tr;
}
return Visibility(
- visible: state.lockFeature.value.lockSound == 1
- ? true
- : false,
+ visible: state.lockFeature.value.lockSound == 1 ? true : false,
child: CommonItem(
leftTitel: TranslationLoader.lanKeys!.lockSound!.tr,
rightTitle: titleStr,
@@ -295,14 +268,10 @@ class _LockSetPageState extends State with RouteAware {
}),
// 防撬报警
Obx(() => Visibility(
- visible: state.lockFeature.value.antiPrySwitch == 1
- ? true
- : false,
+ visible: state.lockFeature.value.antiPrySwitch == 1 ? true : false,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.burglarAlarm!.tr,
- rightTitle:
- (state.lockSettingInfo.value.antiPrySwitch ?? 0) == 1
+ leftTitel: TranslationLoader.lanKeys!.burglarAlarm!.tr,
+ rightTitle: (state.lockSettingInfo.value.antiPrySwitch ?? 0) == 1
? TranslationLoader.lanKeys!.opened!.tr
: TranslationLoader.lanKeys!.closed!.tr,
isHaveLine: true,
@@ -315,57 +284,41 @@ class _LockSetPageState extends State with RouteAware {
SizedBox(height: 10.h),
// 常开模式
Obx(() => Visibility(
- visible: state.lockFeature.value.passageMode == 1
- ? true
- : false,
+ visible: state.lockFeature.value.passageMode == 1 ? true : false,
// visible:true,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.normallyOpenMode!.tr,
- rightTitle:
- (state.lockSettingInfo.value.passageMode ?? 0) ==
- 1
+ leftTitel: TranslationLoader.lanKeys!.normallyOpenMode!.tr,
+ rightTitle: (state.lockSettingInfo.value.passageMode ?? 0) == 1
? TranslationLoader.lanKeys!.opened!.tr
: TranslationLoader.lanKeys!.closed!.tr,
isHaveLine: true,
isHaveDirection: true,
action: () {
- Get.toNamed(Routers.normallyOpenModePage,
- arguments: {
- 'lockSetInfoData': state.lockSetInfoData.value
- });
+ Get.toNamed(Routers.normallyOpenModePage, arguments: {
+ 'lockSetInfoData': state.lockSetInfoData.value
+ });
}))),
// 远程开锁
Obx(() => Visibility(
- visible: state.lockFeature.value.remoteUnlock == 1
- ? true
- : false,
+ visible: state.lockFeature.value.remoteUnlock == 1 ? true : false,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.remoteUnlocking!.tr,
- rightTitle:
- (state.lockSettingInfo.value.remoteUnlock ?? 0) ==
- 1
+ leftTitel: TranslationLoader.lanKeys!.remoteUnlocking!.tr,
+ rightTitle: (state.lockSettingInfo.value.remoteUnlock ?? 0) == 1
? TranslationLoader.lanKeys!.opened!.tr
: TranslationLoader.lanKeys!.closed!.tr,
isHaveLine: true,
isHaveDirection: true,
action: () {
- Get.toNamed(Routers.remoteUnlockingPage,
- arguments: {
- 'lockSetInfoData': state.lockSetInfoData.value
- });
+ Get.toNamed(Routers.remoteUnlockingPage, arguments: {
+ 'lockSetInfoData': state.lockSetInfoData.value
+ });
}))),
// 重置键
Obx(() => Visibility(
- visible: state.lockFeature.value.resetSwitch == 1
- ? true
- : false,
+ visible: state.lockFeature.value.resetSwitch == 1 ? true : false,
child: CommonItem(
leftTitel: TranslationLoader.lanKeys!.resetButton!.tr,
- rightTitle:
- (state.lockSettingInfo.value.resetSwitch ?? 0) ==
- 1
+ rightTitle: (state.lockSettingInfo.value.resetSwitch ?? 0) == 1
? TranslationLoader.lanKeys!.opened!.tr
: TranslationLoader.lanKeys!.closed!.tr,
isHaveLine: true,
@@ -484,32 +437,24 @@ class _LockSetPageState extends State with RouteAware {
title = TranslationLoader.lanKeys!.leisure!.tr;
}
return Visibility(
- visible: state.lockStatus.value.roomStatus == 1
- ? true
- : false,
+ visible: state.lockStatus.value.roomStatus == 1 ? true : false,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.markedHouseState!.tr,
+ leftTitel: TranslationLoader.lanKeys!.markedHouseState!.tr,
rightTitle: title,
isHaveLine: true,
isHaveDirection: true,
action: () {
- Get.toNamed(Routers.markedHouseStatePage,
- arguments: {
- 'lockSetInfoData':
- state.lockSetInfoData.value
- });
+ Get.toNamed(Routers.markedHouseStatePage, arguments: {
+ 'lockSetInfoData': state.lockSetInfoData.value
+ });
}));
}),
// 考勤
Obx(
- () => Visibility(
- visible: state.lockFeature.value.attendance == 1
- ? true
- : false,
+ () => Visibility(
+ visible: state.lockFeature.value.attendance == 1 ? true : false,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.checkingIn!.tr,
+ leftTitel: TranslationLoader.lanKeys!.checkingIn!.tr,
rightTitle: "",
isHaveLine: true,
isHaveRightWidget: true,
@@ -517,7 +462,7 @@ class _LockSetPageState extends State with RouteAware {
),
// 开锁提醒
Obx(
- () => Visibility(
+ () => Visibility(
visible: state.lockFeature.value.unlockReminder == 1 ? true : false,
child: CommonItem(
leftTitel: TranslationLoader.lanKeys!.unlockReminder!.tr,
@@ -527,38 +472,34 @@ class _LockSetPageState extends State with RouteAware {
rightWidget: _lockRemindSwitch())),
),
// APP开锁时是否需联网
- Obx(() =>
- Visibility(
- visible: state.lockFeature.value.appUnlockOnline == 1 ? true : false,
- child:
- CommonItem(
- leftTitel: TranslationLoader.lanKeys!.whetherInternetRequiredWhenUnlocking!.tr,
- rightTitle: "",
- isHaveLine: false,
- isHaveRightWidget: true,
- rightWidget: _openLockNeedOnlineSwitch()),
- ),
+ Obx(
+ () => Visibility(
+ visible: state.lockFeature.value.appUnlockOnline == 1 ? true : false,
+ child: CommonItem(
+ leftTitel: TranslationLoader
+ .lanKeys!.whetherInternetRequiredWhenUnlocking!.tr,
+ rightTitle: "",
+ isHaveLine: false,
+ isHaveRightWidget: true,
+ rightWidget: _openLockNeedOnlineSwitch()),
+ ),
),
SizedBox(height: 10.h),
// wifi配网
Obx(
- () => Visibility(
- visible:
- state.lockFeature.value.wifi == 1 ? true : false,
- child:
- CommonItem(
- leftTitel: TranslationLoader.lanKeys!.wifiDistributionNetwork!.tr,
+ () => Visibility(
+ visible: state.lockFeature.value.wifi == 1 ? true : false,
+ child: CommonItem(
+ leftTitel:
+ TranslationLoader.lanKeys!.wifiDistributionNetwork!.tr,
rightTitle: "",
isHaveLine: true,
isHaveDirection: true,
action: () {
- Get.toNamed(Routers.configuringWifiPage,
- arguments: {
- 'lockSetInfoData':
- state.lockSetInfoData.value
- });
- })
- ),
+ Get.toNamed(Routers.configuringWifiPage, arguments: {
+ 'lockSetInfoData': state.lockSetInfoData.value
+ });
+ })),
),
// Obx(() =>
// 锁时间
@@ -621,8 +562,7 @@ class _LockSetPageState extends State with RouteAware {
Visibility(
visible: true,
child: CommonItem(
- leftTitel:
- TranslationLoader.lanKeys!.lockEscalation!.tr,
+ leftTitel: TranslationLoader.lanKeys!.lockEscalation!.tr,
rightTitle: "",
isHaveLine: false,
isHaveDirection: true,
@@ -632,8 +572,7 @@ class _LockSetPageState extends State with RouteAware {
// ),
SizedBox(height: 30.h),
Container(
- padding:
- EdgeInsets.only(left: 20.w, right: 20.w, bottom: 30.h),
+ padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 30.h),
child: SubmitBtn(
btnName: TranslationLoader.lanKeys!.delete!.tr,
isDelete: true,
diff --git a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_logic.dart b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_logic.dart
index 7bd26bbc..b32bd345 100644
--- a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_logic.dart
+++ b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_logic.dart
@@ -1,4 +1,3 @@
-
import 'dart:async';
import 'dart:io';
@@ -28,15 +27,16 @@ import '../lockOperatingRecord/lockOperatingRecordGetLastRecordTime_entity.dart'
import 'lockDetail_state.dart';
import 'lockNetToken_entity.dart';
-class LockDetailLogic extends BaseGetXController{
+class LockDetailLogic extends BaseGetXController {
final LockDetailState state = LockDetailState();
// 监听设备返回的数据
late StreamSubscription _replySubscription;
void _initReplySubscription() {
- _replySubscription = EventBusManager().eventBus!.on().listen((reply) async {
+ _replySubscription =
+ EventBusManager().eventBus!.on().listen((reply) async {
// 开门
- if(reply is OpenDoorReply) {
+ if (reply is OpenDoorReply) {
_replyOpenLock(reply);
}
@@ -46,17 +46,17 @@ class LockDetailLogic extends BaseGetXController{
// }
// 获取星锁状态信息
- if(reply is GetStarLockStatuInfoReply){
+ if (reply is GetStarLockStatuInfoReply) {
_replyGetStarLockStatusInfo(reply);
}
// 开完锁之后上传记录
- if(reply is SenderReferEventRecordTimeReply) {
+ if (reply is SenderReferEventRecordTimeReply) {
_replyReferEventRecordTime(reply);
}
// 添加用户
- if(reply is AddUserReply) {
+ if (reply is AddUserReply) {
_replyAddUserKey(reply);
}
});
@@ -64,76 +64,76 @@ class LockDetailLogic extends BaseGetXController{
// 开门数据解析
Future _replyOpenLock(Reply reply) async {
- var tokenData = reply.data.sublist(2, 6);
- var saveStrList = changeIntListToStringList(tokenData);
- print("openDoorToken:$tokenData");
- Storage.setStringList(saveBlueToken, saveStrList);
+ var tokenData = reply.data.sublist(2, 6);
+ var saveStrList = changeIntListToStringList(tokenData);
+ print("openDoorToken:$tokenData");
+ Storage.setStringList(saveBlueToken, saveStrList);
- int status = reply.data[6];
- print("status:$status");
+ int status = reply.data[6];
+ print("status:$status");
- switch(status){
- case 0x00:
- //成功
- print("${reply.commandType}数据解析成功");
+ switch (status) {
+ case 0x00:
+ //成功
+ print("${reply.commandType}数据解析成功");
- // 电量
- int power = reply.data[7];
- state.electricQuantity.value = power;
+ // 电量
+ int power = reply.data[7];
+ state.electricQuantity.value = power;
- getLockRecordLastUploadDataTime();
- state.lockState.value = 2;
- state.animationController.reset();
- state.animationController.forward();
- // state.animationController.isCompleted;
- break;
- case 0x06:
- //无权限
- print("${reply.commandType}需要鉴权");
-
- var privateKey = await Storage.getStringList(saveBluePrivateKey);
- List getPrivateKeyList = changeStringListToIntList(privateKey!);
-
- var signKey = await Storage.getStringList(saveBlueSignKey);
- List signKeyDataList = changeStringListToIntList(signKey!);
-
- IoSenderManage.senderOpenLock(
- keyID: BlueManage().connectDeviceName,
- userID: await Storage.getUid(),
- openMode: 1,
- openTime: DateTime.now().millisecondsSinceEpoch~/1000,
- onlineToken:state.lockNetToken,
- token: tokenData,
- needAuthor: 1,
- signKey: signKeyDataList,
- privateKey: getPrivateKeyList,
- );
-
- break;
- case 0x07:
+ getLockRecordLastUploadDataTime();
+ state.lockState.value = 2;
+ state.animationController.reset();
+ state.animationController.forward();
+ // state.animationController.isCompleted;
+ break;
+ case 0x06:
//无权限
- print("${reply.commandType}用户无权限");
+ print("${reply.commandType}需要鉴权");
- break;
- case 0x09:
+ var privateKey = await Storage.getStringList(saveBluePrivateKey);
+ List getPrivateKeyList = changeStringListToIntList(privateKey!);
+
+ var signKey = await Storage.getStringList(saveBlueSignKey);
+ List signKeyDataList = changeStringListToIntList(signKey!);
+
+ IoSenderManage.senderOpenLock(
+ keyID: BlueManage().connectDeviceName,
+ userID: await Storage.getUid(),
+ openMode: 1,
+ openTime: DateTime.now().millisecondsSinceEpoch ~/ 1000,
+ onlineToken: state.lockNetToken,
+ token: tokenData,
+ needAuthor: 1,
+ signKey: signKeyDataList,
+ privateKey: getPrivateKeyList,
+ );
+
+ break;
+ case 0x07:
+ //无权限
+ print("${reply.commandType}用户无权限");
+
+ break;
+ case 0x09:
// 权限校验错误
- print("${reply.commandType}校验错误");
+ print("${reply.commandType}校验错误");
- break;
- default:
+ break;
+ default:
//失败
- print("${reply.commandType}失败");
+ print("${reply.commandType}失败");
- break;
- }
+ break;
+ }
}
// 获取锁状态数据解析
Future _replyGetStarLockStatusInfo(Reply reply) async {
int status = reply.data[2];
- switch(status){
+ switch (status) {
case 0x00:
- //成功
+ //成功
print("${reply.commandType}数据解析成功");
// 厂商名称
var vendor = reply.data.sublist(3, 23);
@@ -189,22 +189,22 @@ class LockDetailLogic extends BaseGetXController{
break;
case 0x06:
- //无权限
+ //无权限
print("${reply.commandType}需要鉴权");
break;
case 0x07:
- //无权限
+ //无权限
print("${reply.commandType}用户无权限");
break;
case 0x09:
- // 权限校验错误
+ // 权限校验错误
print("${reply.commandType}权限校验错误");
break;
default:
- //失败
+ //失败
print("${reply.commandType}失败");
break;
@@ -214,17 +214,17 @@ class LockDetailLogic extends BaseGetXController{
// 根据时间查解析数据
Future _replyReferEventRecordTime(Reply reply) async {
int status = reply.data[2];
- switch(status){
+ switch (status) {
case 0x00:
- //成功
+ //成功
print("${reply.commandType}数据解析成功");
- if(reply.data[5] > 0){
+ if (reply.data[5] > 0) {
reply.data.removeRange(0, 6);
// 把得到的数据按8位分割成数组 然后塞进一个新的数组里面
var getList = splitList(reply.data, 8);
// print("getList:$getList");
var uploadList = [];
- for(int i = 0; i getPrivateKeyList = changeStringListToIntList(privateKey!);
@@ -310,33 +310,32 @@ class LockDetailLogic extends BaseGetXController{
IoSenderManage.senderAddUser(
lockID: BlueManage().connectDeviceName,
- authUserID:state.senderUserId.toString(),
- keyID:state.keyInfos.value.keyId.toString(),
- userID:await Storage.getUid(),
- openMode:1,
- keyType:0,
- startDate:DateTime.now().millisecondsSinceEpoch,
- expireDate:0x11223344,
- role:0,
- password:"123456",
- needAuthor:1,
- publicKey:publicKeyDataList,
- privateKey:getPrivateKeyList,
- token: token
- );
+ authUserID: state.senderUserId.toString(),
+ keyID: state.keyInfos.value.keyId.toString(),
+ userID: await Storage.getUid(),
+ openMode: 1,
+ keyType: 0,
+ startDate: DateTime.now().millisecondsSinceEpoch,
+ expireDate: 0x11223344,
+ role: 0,
+ password: "123456",
+ needAuthor: 1,
+ publicKey: publicKeyDataList,
+ privateKey: getPrivateKeyList,
+ token: token);
break;
case 0x07:
- //无权限
+ //无权限
print("用户无权限");
break;
case 0x09:
- // 权限校验错误
+ // 权限校验错误
print("添加用户权限校验错误");
break;
default:
- //失败
+ //失败
print("领锁失败");
break;
@@ -349,9 +348,11 @@ class LockDetailLogic extends BaseGetXController{
// Toast.show(msg: "正在连接设备,请稍等。");
// return;
// }
- BlueManage().judgeReconnect(state.keyInfos.value.bluetooth!.bluetoothDeviceName!, (DeviceConnectionState deviceConnectionState) async {
+ BlueManage()
+ .judgeReconnect(state.keyInfos.value.bluetooth!.bluetoothDeviceName!,
+ (DeviceConnectionState deviceConnectionState) async {
print("77777777:$deviceConnectionState");
- if (deviceConnectionState == DeviceConnectionState.connected){
+ if (deviceConnectionState == DeviceConnectionState.connected) {
var privateKey = await Storage.getStringList(saveBluePrivateKey);
List getPrivateKeyList = changeStringListToIntList(privateKey!);
@@ -360,27 +361,29 @@ class LockDetailLogic extends BaseGetXController{
bool isHave = await Storage.ifHaveKey(saveBlueToken);
List getTokenList;
- if(isHave){
+ if (isHave) {
var token = await Storage.getStringList(saveBlueToken);
getTokenList = changeStringListToIntList(token!);
- }else{
- getTokenList = [0,0,0,0];
+ } else {
+ getTokenList = [0, 0, 0, 0];
}
- print("openDoorTokenPubToken:$getTokenList getPrivateKeyList:$getPrivateKeyList");
+ print(
+ "openDoorTokenPubToken:$getTokenList getPrivateKeyList:$getPrivateKeyList");
- print("millisecondsSinceEpoch/1000:${DateTime.now().millisecondsSinceEpoch~/1000}");
+ print(
+ "millisecondsSinceEpoch/1000:${DateTime.now().millisecondsSinceEpoch ~/ 1000}");
IoSenderManage.senderOpenLock(
keyID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
openMode: 1,
- openTime: DateTime.now().millisecondsSinceEpoch~/1000,
- onlineToken:state.lockNetToken,
+ openTime: DateTime.now().millisecondsSinceEpoch ~/ 1000,
+ onlineToken: state.lockNetToken,
token: getTokenList,
needAuthor: 1,
signKey: signKeyDataList,
privateKey: getPrivateKeyList,
);
- }else if (deviceConnectionState == DeviceConnectionState.disconnected){
+ } else if (deviceConnectionState == DeviceConnectionState.disconnected) {
// Toast.show(msg: "连接设备失败,请确保在设备附近,设备未被连接,设备已打开");
state.lockState.value = 4;
@@ -425,15 +428,15 @@ class LockDetailLogic extends BaseGetXController{
// });
// }
-
// 备用逻辑,进入管理钥匙界面获取锁状态
Future connectBlue(String bluetoothDeviceName) async {
// 进来之后首先连接
- BlueManage().connect(bluetoothDeviceName, (DeviceConnectionState state) async {
- if(EasyLoading.isShow){
+ BlueManage().connect(bluetoothDeviceName,
+ (DeviceConnectionState state) async {
+ if (EasyLoading.isShow) {
EasyLoading.dismiss();
}
- if (state == DeviceConnectionState.connected){
+ if (state == DeviceConnectionState.connected) {
// var privateKey = await Storage.getStringList(saveBluePrivateKey);
// List getPrivateKeyList = changeStringListToIntList(privateKey!);
// // IoSenderManage.senderGetLockStatu(
@@ -452,7 +455,8 @@ class LockDetailLogic extends BaseGetXController{
// 查询事件记录(时间查询)
Future senderReferEventRecordTime(int time) async {
- BlueManage().judgeReconnect(BlueManage().connectDeviceName, (DeviceConnectionState state) async {
+ BlueManage().judgeReconnect(BlueManage().connectDeviceName,
+ (DeviceConnectionState state) async {
if (state == DeviceConnectionState.connected) {
var privateKey = await Storage.getStringList(saveBluePrivateKey);
List getPrivateKeyList = changeStringListToIntList(privateKey!);
@@ -464,15 +468,15 @@ class LockDetailLogic extends BaseGetXController{
List getPublicKeyList = changeStringListToIntList(publicKey!);
IoSenderManage.senderReferEventRecordTimeCommand(
- keyID:BlueManage().connectDeviceName,
- userID:await Storage.getUid(),
- logsCount:20,
+ keyID: BlueManage().connectDeviceName,
+ userID: await Storage.getUid(),
+ logsCount: 20,
// time:DateTime.now().millisecondsSinceEpoch~/1000,
time: time,
- token:getTokenList,
- needAuthor:1,
- publicKey:getPublicKeyList,
- privateKey:getPrivateKeyList,
+ token: getTokenList,
+ needAuthor: 1,
+ publicKey: getPublicKeyList,
+ privateKey: getPrivateKeyList,
);
}
}, isShowLoading: false);
@@ -481,8 +485,9 @@ class LockDetailLogic extends BaseGetXController{
// 添加用户(普通用户接收电子钥匙)
Future addUserConnectBlue() async {
// 进来之后首先连接
- BlueManage().judgeReconnect(BlueManage().connectDeviceName, (DeviceConnectionState deviceConnectionState) async {
- if (deviceConnectionState == DeviceConnectionState.connected){
+ BlueManage().judgeReconnect(BlueManage().connectDeviceName,
+ (DeviceConnectionState deviceConnectionState) async {
+ if (deviceConnectionState == DeviceConnectionState.connected) {
// 私钥
var privateKey = await Storage.getStringList(saveBluePrivateKey);
List getPrivateKeyList = changeStringListToIntList(privateKey!);
@@ -491,27 +496,27 @@ class LockDetailLogic extends BaseGetXController{
List publicKeyDataList = changeStringListToIntList(publicKey!);
var token = await Storage.getStringList(saveBlueToken);
- List getTokenList = [0,0,0,0];
- if(token != null){
+ List getTokenList = [0, 0, 0, 0];
+ if (token != null) {
getTokenList = changeStringListToIntList(token);
}
- print("BlueManage().connectDeviceName:${BlueManage().connectDeviceName} authUserID:${state.senderUserId.toString()} keyID:${state.keyInfos.value.keyId.toString()} userID:${await Storage.getUid()}");
+ print(
+ "BlueManage().connectDeviceName:${BlueManage().connectDeviceName} authUserID:${state.senderUserId.toString()} keyID:${state.keyInfos.value.keyId.toString()} userID:${await Storage.getUid()}");
IoSenderManage.senderAddUser(
lockID: BlueManage().connectDeviceName,
- authUserID:state.senderUserId.toString(),
- keyID:state.keyInfos.value.keyId.toString(),
- userID:await Storage.getUid(),
- openMode:1,
- keyType:0,
- startDate:DateTime.now().millisecondsSinceEpoch,
- expireDate:0x11223344,
- role:0,
- password:"123456",
- needAuthor:1,
- publicKey:publicKeyDataList,
- privateKey:getPrivateKeyList,
- token: getTokenList
- );
+ authUserID: state.senderUserId.toString(),
+ keyID: state.keyInfos.value.keyId.toString(),
+ userID: await Storage.getUid(),
+ openMode: 1,
+ keyType: 0,
+ startDate: DateTime.now().millisecondsSinceEpoch,
+ expireDate: 0x11223344,
+ role: 0,
+ password: "123456",
+ needAuthor: 1,
+ publicKey: publicKeyDataList,
+ privateKey: getPrivateKeyList,
+ token: getTokenList);
}
}, isShowLoading: false);
}
@@ -521,14 +526,14 @@ class LockDetailLogic extends BaseGetXController{
state.lockState.value = 1;
state.animationController.forward();
- if(state.lockUserNo == 0){
+ if (state.lockUserNo == 0) {
// 电子钥匙lockUserNo为0 要先添加用户
addUserConnectBlue();
- }else{
- if(state.isOpenLockNeedOnline.value == 0){
+ } else {
+ if (state.isOpenLockNeedOnline.value == 0) {
// 不需要联网
openDoorAction();
- }else{
+ } else {
// 需要联网
getLockNetToken();
}
@@ -536,13 +541,12 @@ class LockDetailLogic extends BaseGetXController{
}
//关闭连接
- stopConnect() {
-
- }
+ stopConnect() {}
// 获取手机联网token,根据锁设置里面获取的开锁时是否联网来判断是否调用这个接口
void getLockNetToken() async {
- LockNetTokenEntity entity = await ApiRepository.to.getLockNetToken(lockId: state.keyInfos.value.lockId.toString());
+ LockNetTokenEntity entity = await ApiRepository.to
+ .getLockNetToken(lockId: state.keyInfos.value.lockId.toString());
if (entity.errorCode!.codeIsSuccessful) {
state.lockNetToken = entity.data!.token!;
print("state.lockNetToken:${state.lockNetToken}");
@@ -552,11 +556,13 @@ class LockDetailLogic extends BaseGetXController{
// 普通用户接收电子钥匙之后 更新锁用户NO
void updateLockUserNo() async {
- LockNetTokenEntity entity = await ApiRepository.to.updateLockUserNo(keyId: state.keyInfos.value.keyId.toString(), lockUserNo: state.lockUserNo.toString());
+ LockNetTokenEntity entity = await ApiRepository.to.updateLockUserNo(
+ keyId: state.keyInfos.value.keyId.toString(),
+ lockUserNo: state.lockUserNo.toString());
if (entity.errorCode!.codeIsSuccessful) {
- if(state.isOpenLockNeedOnline.value == 0){
+ if (state.isOpenLockNeedOnline.value == 0) {
openDoorAction();
- }else{
+ } else {
getLockNetToken();
}
}
@@ -564,33 +570,37 @@ class LockDetailLogic extends BaseGetXController{
// 查询锁记录最后时间
void getLockRecordLastUploadDataTime() async {
- LockOperatingRecordGetLastRecordTimeEntity entity = await ApiRepository.to.getLockRecordLastUploadDataTime(lockId: state.keyInfos.value.lockId.toString());
+ LockOperatingRecordGetLastRecordTimeEntity entity = await ApiRepository.to
+ .getLockRecordLastUploadDataTime(
+ lockId: state.keyInfos.value.lockId.toString());
if (entity.errorCode!.codeIsSuccessful) {
- senderReferEventRecordTime(entity.data!.operateDate!~/1000);
+ senderReferEventRecordTime(entity.data!.operateDate! ~/ 1000);
}
}
// 操作记录上传
void lockRecordUploadData(List list) async {
- KeyOperationRecordEntity entity =
- await ApiRepository.to.lockRecordUploadData(
- lockId: state.keyInfos.value.lockId.toString(),
- records: list);
+ KeyOperationRecordEntity entity = await ApiRepository.to
+ .lockRecordUploadData(
+ lockId: state.keyInfos.value.lockId.toString(), records: list);
if (entity.errorCode!.codeIsSuccessful) {
// mockNetworkDataRequest();
}
}
/// 锁设置里面开启关闭考勤刷新锁详情
- StreamSubscription? _lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent;
+ StreamSubscription?
+ _lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent;
void initLockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceAction() {
// 蓝牙协议通知传输跟蓝牙之外的数据传输类不一样 eventBus
- _lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent = eventBus.on().listen((event) {
- if(event.type == 0){
+ _lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent = eventBus
+ .on()
+ .listen((event) {
+ if (event.type == 0) {
// 0考勤
state.isAttendance.value = event.setResult;
state.keyInfos.value.lockSetting!.attendance = event.setResult;
- }else if(event.type == 1){
+ } else if (event.type == 1) {
// 1 开锁时是否需联网
state.isOpenLockNeedOnline.value = event.setResult;
state.keyInfos.value.lockSetting!.appUnlockOnline = event.setResult;
@@ -626,6 +636,8 @@ class LockDetailLogic extends BaseGetXController{
super.onInit();
// print("lockDetail_onInit()");
+ print("lockDetail_onInit()");
+
// 进来获取锁状态
// connectBlue();
// factoryDataResetAction();
@@ -639,4 +651,4 @@ class LockDetailLogic extends BaseGetXController{
_replySubscription.cancel();
_lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent!.cancel();
}
-}
\ No newline at end of file
+}
diff --git a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_page.dart b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_page.dart
index fba741a6..4ef15d4b 100644
--- a/star_lock/lib/main/lockDetail/lockDetail/lockDetail_page.dart
+++ b/star_lock/lib/main/lockDetail/lockDetail/lockDetail_page.dart
@@ -65,6 +65,7 @@ class _LockDetailPageState extends State with TickerProviderStat
var saveSignKeyList = changeIntListToStringList(signKeyData);
Storage.setStringList(saveBlueSignKey, saveSignKeyList);
+ Storage.setStringList(saveBlueToken, [0, 0, 0, 0]);
// logic.startScanAction();
listeningAnimations();
diff --git a/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_logic.dart b/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_logic.dart
index 4ddd39ef..09c52a9f 100644
--- a/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_logic.dart
+++ b/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_logic.dart
@@ -1,9 +1,402 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:math';
+
+import 'package:flutter/services.dart';
+import 'package:flutter_voice_processor/flutter_voice_processor.dart';
+import 'package:get/get.dart';
+import 'package:permission_handler/permission_handler.dart';
+
+import '../../../../talk/call/g711.dart';
+import '../../../../talk/udp/udp_manage.dart';
+import '../../../../talk/udp/udp_senderManage.dart';
import '../../../../tools/baseGetXController.dart';
+import '../../../../tools/eventBusEventManage.dart';
import 'lockMonitoring_state.dart';
class LockMonitoringLogic extends BaseGetXController {
final LockMonitoringState state = LockMonitoringState();
+ /// 初始化发送声音
+ initRecorder() {
+ state.voiceProcessor = VoiceProcessor.instance;
+ }
-}
\ No newline at end of file
+ /// 收到视频流数据
+ StreamSubscription? _getTVDataRefreshUIEvent;
+ void _getTVDataRefreshUIAction() {
+ // 蓝牙协议通知传输跟蓝牙之外的数据传输类不一样 eventBus
+ _getTVDataRefreshUIEvent =
+ eventBus.on().listen((event) {
+ if (event.tvList.isNotEmpty) {
+ // 预加载图片数据
+ Uint8List imageData = Uint8List.fromList(event.tvList);
+ // 更新状态
+ state.listData.value = imageData;
+ }
+ });
+ }
+
+ /// 收到UDP发送的状态
+ StreamSubscription? _getUDPStatusRefreshUIEvent;
+ void _getUDPStatusRefreshUIAction() {
+ _getUDPStatusRefreshUIEvent = eventBus.on().listen((event) {
+ state.udpStatus.value = event.udpStatus;
+ if(state.udpStatus.value == 8){
+ // 接听成功了,然后刷新界面的时间 60秒以后自动挂断
+ state.oneMinuteTimeTimer = Timer.periodic(const Duration(seconds:1), (Timer t) async {
+ state.oneMinuteTime.value++;
+ // Get.log('state.oneMinuteTime.value:${state.oneMinuteTime.value}');
+ if(state.oneMinuteTime.value >= 60){
+ // 超过60秒了
+ state.oneMinuteTimeTimer.cancel();
+ state.oneMinuteTime.value = 0;
+ // 挂断
+ UDPSenderManage.sendMainProtocol(
+ command: 150,
+ commandTypeIsCalling: 1,
+ subCommand: 30,
+ lockID: UDPManage().lockId,
+ lockIP: UDPManage().host,
+ userMobile: await state.userMobile,
+ userMobileIP: await state.userMobileIP,
+ endData: []);
+
+ // 关闭当前界面
+ Get.back();
+ }
+ });
+ }
+ });
+ }
+
+ /// 接听
+ udpAnswerAction() async {
+ UDPSenderManage.sendMainProtocol(
+ command: 150,
+ commandTypeIsCalling: 1,
+ subCommand: 6,
+ lockID: UDPManage().lockId,
+ lockIP: UDPManage().host,
+ userMobile: await state.userMobile,
+ userMobileIP: await state.userMobileIP,
+ endData: []
+ );
+ }
+
+ /// 挂断
+ udpHangUpAction() async {
+ UDPSenderManage.sendMainProtocol(
+ command: 150,
+ commandTypeIsCalling: 1,
+ subCommand: 30,
+ lockID: UDPManage().lockId,
+ lockIP: UDPManage().host,
+ userMobile: await state.userMobile,
+ userMobileIP: await state.userMobileIP,
+ endData: []);
+ }
+
+ /// 开门
+ udpOpenDoorAction() async {
+ UDPSenderManage.sendMainProtocol(
+ command: 150,
+ commandTypeIsCalling: 1,
+ subCommand: 10,
+ lockID: UDPManage().lockId,
+ lockIP: UDPManage().host,
+ userMobile: await state.userMobile,
+ userMobileIP: await state.userMobileIP,
+ endData: []);
+ Get.back();
+ }
+
+ Future _readG711Data() async {
+ String filePath = 'assets/s10-g711.bin';
+ List audioData = await G711().readAssetFile(filePath);
+ // Get.log('发送读取711文件数据为:$audioData');// 数据为:$audioData
+ // return;
+ // print('发送读取711文件数据长度为:${audioData.length}');// 数据为:$audioData
+ if (audioData.isNotEmpty) {
+ // 在这里处理你的音频数据
+ // pcmBytes = G711().convertList(audioData);
+ // print('发送转换pcmBytes数据长度为:${pcmBytes.length}');
+
+ int start = 0;
+ int length = 320;
+ while (start < audioData.length) {
+ // await Future.delayed(const Duration(milliseconds: 50));
+
+ int end = (start + length > audioData.length) ? audioData.length : start + length;
+ List sublist = audioData.sublist(start, end);
+ sendRecordData({
+ "bytes": sublist,
+ // "udpSendDataFrameNumber": 0,
+ "lockID": UDPManage().lockId,
+ "lockIP": UDPManage().host,
+ "userMobile": await state.userMobile,
+ "userMobileIP": await state.userMobileIP,
+ });
+ print(sublist);
+ start += length;
+ }
+ print('G711数据发送完成');
+ } else {
+ print('Failed to read audio data.');
+ }
+ }
+
+ Future startProcessing() async {
+ frameListener(List frame) async {
+ // Get.log('Get data.length:${frame.length} Received data:$frame');
+ for (int i = 0; i < frame.length; i++) {
+ frame[i] = linearToULaw(frame[i]);
+ }
+ // Get.log('change Get data.length:${frame.length} change Received data:$frame');
+ await Future.delayed(const Duration(milliseconds: 50));
+ sendRecordData({
+ "bytes": frame,
+ // "udpSendDataFrameNumber": 0,
+ "lockID": UDPManage().lockId,
+ "lockIP": UDPManage().host,
+ "userMobile": await state.userMobile,
+ "userMobileIP": await state.userMobileIP,
+ });
+ }
+
+ errorListener(VoiceProcessorException error) {
+ print("VoiceProcessorException: $error");
+ };
+ state.voiceProcessor?.addFrameListener(frameListener);
+ state.voiceProcessor?.addErrorListener(errorListener);
+
+ try {
+ if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) {
+ await state.voiceProcessor?.start(320, 8000);
+ bool? isRecording = await state.voiceProcessor?.isRecording();
+
+ } else {
+
+ }
+ } on PlatformException catch (ex) {
+ Get.log("PlatformException: $ex");
+ } finally {
+
+ }
+ }
+
+ Future stopProcessing() async {
+ try {
+ await state.voiceProcessor?.stop();
+ } on PlatformException catch (ex) {
+ Get.log("PlatformException: $ex");
+ } finally {
+
+ }
+ }
+
+ void onError(Object e) {
+ print(e);
+ }
+
+ sendRecordData(Map args) async {
+
+ List bytes = args["bytes"];
+ // int udpSendDataFrameNumber = args["udpSendDataFrameNumber"];
+ String? lockID = args["lockID"];
+ String? lockIP = args["lockIP"];
+ String? userMobile = args["userMobile"];
+ String? userMobileIP = args["userMobileIP"];
+
+ // int length = 320; // 每个子List的长度
+ // List list = state.listAudioData.value.sublist(0, 320);
+ // for (int i = 0; i < bytes.length; i += length) {
+ // int end = (i + length < bytes.length) ? i + length : bytes.length;
+ // bytes.sublist(i, end);
+ // // _sendRecordData(bytes.sublist(i, end));
+ // // // 刚进来是接听状态,然后改为长按对讲
+ // }
+
+ // while(list.isNotEmpty) {
+ state.udpSendDataFrameNumber++;
+ if (state.udpSendDataFrameNumber >= 65536) state.udpSendDataFrameNumber=1;
+ // 57
+ List topBytes = [];
+
+ // var cID = "XXXCID";
+ // List cIDData = utf8.encode(cID!);
+ // topBytes.addAll(cIDData);
+ // // topBytes = getFixedLengthList(cIDData, 20 - cIDData.length);
+ // for (int i = 0; i < 6 - cIDData.length; i++) {
+ // topBytes.add(0);
+ // }
+ //
+ // // 命令
+ // topBytes.add(150);
+ //
+ // // 命令类型
+ // topBytes.add(1);
+ //
+ // // 子命令
+ // topBytes.add(8);
+ //
+ // // lockID
+ // List lockIDData = utf8.encode(lockID!);
+ // topBytes.addAll(lockIDData);
+ // // topBytes = getFixedLengthList(lockIDData, 20 - lockIDData.length);
+ // for (int i = 0; i < 20 - lockIDData.length; i++) {
+ // topBytes.add(0);
+ // }
+ //
+ // // lockIP
+ // var lockIPList = lockIP!.split(".");
+ // lockIPList.forEach((element) {
+ // topBytes.add(int.parse(element));
+ // });
+ //
+ // // userMobile
+ // List userMobileData = utf8.encode(userMobile!);
+ // topBytes.addAll(userMobileData);
+ // // topBytes = getFixedLengthList(topBytes, 20 - userMobileData.length);
+ // for (int i = 0; i < 20 - userMobileData.length; i++) {
+ // topBytes.add(0);
+ // }
+ //
+ // // userMobileIP
+ // var userMobileIPList = userMobileIP!.split(".");
+ // userMobileIPList.forEach((element) {
+ // topBytes.add(int.parse(element));
+ // });
+
+ topBytes.addAll([
+ 1, 1, 1, 1, // 时间戳
+ 1, 0, // 音频
+ 1, 0, // 帧序号
+ 64, 0, 0, 0, // 帧长度
+ 1, 0, // 总包数
+ 1, 0, // 当前包号
+ 64, 1, // 数据长度
+ 176, 4, // 保留
+ ]);
+
+ topBytes[6] = (state.udpSendDataFrameNumber & 0x000000FF);
+ topBytes[7] = ((state.udpSendDataFrameNumber & 0x0000FF00) >> 8);
+
+ print("udpSendDataFrameNumber:${state.udpSendDataFrameNumber} topBytes[63]:${topBytes[6]} topBytes[64]:${topBytes[7]}");
+ topBytes.addAll(bytes);
+ Get.log("setVoiceBytes:$topBytes");
+
+ UDPSenderManage.sendMainProtocol(
+ command: 150,
+ commandTypeIsCalling: 1,
+ subCommand: 8,
+ lockID: lockID,
+ lockIP: lockIP,
+ userMobile: userMobile,
+ userMobileIP: userMobileIP,
+ endData: topBytes
+ );
+
+ // UDPManage().sendData(topBytes);
+ }
+
+ // 拿到的音频转化成pcm
+ int linearToULaw(int pcmVal) {
+ int mask;
+ int seg;
+ int uval;
+
+ if (pcmVal < 0) {
+ pcmVal = 0x84 - pcmVal;
+ mask = 0x7F;
+ } else {
+ pcmVal += 0x84;
+ mask = 0xFF;
+ }
+
+ seg = search(pcmVal);
+ if (seg >= 8) {
+ return 0x7F ^ mask;
+ } else {
+ uval = (seg << 4);
+ uval |= ((pcmVal >> (seg + 3)) & 0xF);
+ return uval ^ mask;
+ }
+ }
+
+ int search(int val) {
+ List table = [0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF];
+ int size = 8;
+ for (int i = 0; i < size; i++) {
+ if (val <= table[i]) {
+ return i;
+ }
+ }
+ return size;
+ }
+
+ double _calculateVolumeLevel(List frame) {
+ double rms = 0.0;
+ for (int sample in frame) {
+ rms += pow(sample, 2);
+ }
+ rms = sqrt(rms / frame.length);
+
+ double dbfs = 20 * log(rms / 32767.0) / log(10);
+ double normalizedValue = (dbfs + 50) / 50;
+ return normalizedValue.clamp(0.0, 1.0);
+ }
+
+ Future getPermissionStatus() async {
+ Permission permission = Permission.microphone;
+ //granted 通过,denied 被拒绝,permanentlyDenied 拒绝且不在提示
+ PermissionStatus status = await permission.status;
+ if (status.isGranted) {
+ return true;
+ } else if (status.isDenied) {
+ requestPermission(permission);
+ } else if (status.isPermanentlyDenied) {
+ openAppSettings();
+ } else if (status.isRestricted) {
+ requestPermission(permission);
+ } else {}
+ return false;
+ }
+
+ ///申请权限
+ void requestPermission(Permission permission) async {
+ PermissionStatus status = await permission.request();
+ if (status.isPermanentlyDenied) {
+ openAppSettings();
+ }
+ }
+
+ @override
+ void onReady() {
+ // TODO: implement onReady
+ super.onReady();
+ print("onReady()");
+
+ _getTVDataRefreshUIAction();
+ _getUDPStatusRefreshUIAction();
+
+ initRecorder();
+ }
+
+ @override
+ void onInit() {
+ // TODO: implement onInit
+ super.onInit();
+ }
+
+ @override
+ void onClose() {
+ // TODO: implement onClose
+ print("锁详情界面销毁了");
+ _getTVDataRefreshUIEvent!.cancel();
+ _getUDPStatusRefreshUIEvent!.cancel();
+ state.oneMinuteTimeTimer.cancel();
+ stopProcessing();
+ }
+}
diff --git a/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_page.dart b/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_page.dart
index c4cfd9c6..1bfca06f 100644
--- a/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_page.dart
+++ b/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_page.dart
@@ -1,13 +1,16 @@
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
-import '../../../../appRouters.dart';
import '../../../../app_settings/app_colors.dart';
+import '../../../../talk/udp/udp_manage.dart';
+import '../../../../talk/udp/udp_senderManage.dart';
+import '../../../../tools/showTFView.dart';
+import '../../../../tools/toast.dart';
import 'lockMonitoring_logic.dart';
-
class LockMonitoringPage extends StatefulWidget {
const LockMonitoringPage({Key? key}) : super(key: key);
@@ -21,133 +24,211 @@ class _LockMonitoringPageState extends State {
@override
Widget build(BuildContext context) {
- return Container(
+ return SizedBox(
width: 1.sw,
height: 1.sh,
- color: Colors.white,
- child: Column(
+ child: Stack(
children: [
- Stack(
- // alignment: Alignment.bottomCenter,
- children: [
- Image.asset("images/icon_test20231113.png", width: 1.sw, height: 1.sh, fit: BoxFit.cover),
- Positioned(
- top: ScreenUtil().statusBarHeight + 30.h,
- child: Row(
- children: [
- SizedBox(width: 30.w),
- GestureDetector(
- onTap: () {
- Get.back();
- },
- child: Container(
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(30.h)
- ),
- padding: EdgeInsets.all(10.w),
- child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/icon_left_black.png"),),
- ),
- ),
- ]),
- ),
- Positioned(
- bottom: 10.w,
- child: Container(
- width: 1.sw - 30.w*2,
- // height: 300.h,
- margin: EdgeInsets.all(30.w),
- decoration: BoxDecoration(
- color: const Color(0xC83C3F41),
- borderRadius: BorderRadius.circular(20.h)
- ),
- child: Column(
- children: [
- SizedBox(height: 20.h),
- bottomTopBtnWidget(),
- SizedBox(height: 20.h),
- bottomBottomBtnWidget(),
- SizedBox(height: 20.h),
- ],
- ),
- ))
- ],
+ Obx(() => state.listData.value.isEmpty
+ ? Container(color: Colors.transparent)
+ : Image.memory(
+ state.listData.value,
+ // key: ValueKey(state.listData.value.hashCode),
+ gaplessPlayback: true,
+ width: 1.sw,
+ height: 1.sh,
+ fit: BoxFit.cover,
+ )),
+ Positioned(
+ top: ScreenUtil().statusBarHeight + 30.h,
+ width: 1.sw,
+ child: Obx(() {
+ var sec = (state.oneMinuteTime.value % 60).toString().padLeft(2,'0');
+ var min = (state.oneMinuteTime.value ~/ 60).toString().padLeft(2,'0');
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text("$min:$sec", style: TextStyle(fontSize: 26.sp, color: Colors.white)),
+ // SizedBox(width: 30.w),
+ // GestureDetector(
+ // onTap: () {
+ // Get.back();
+ // },
+ // child: Container(
+ // decoration: BoxDecoration(
+ // color: Colors.white,
+ // borderRadius: BorderRadius.circular(25.h)),
+ // padding: EdgeInsets.all(10.w),
+ // child: Image(
+ // width: 40.w,
+ // height: 40.w,
+ // image: const AssetImage("images/icon_left_black.png"),
+ // ),
+ // ),
+ // ),
+ ]
+ );
+ }),
),
+ Positioned(
+ bottom: 10.w,
+ child: Container(
+ width: 1.sw - 30.w * 2,
+ // height: 300.h,
+ margin: EdgeInsets.all(30.w),
+ decoration: BoxDecoration(
+ color: const Color(0xC83C3F41),
+ borderRadius: BorderRadius.circular(20.h)),
+ child: Column(
+ children: [
+ SizedBox(height: 20.h),
+ bottomTopBtnWidget(),
+ SizedBox(height: 20.h),
+ bottomBottomBtnWidget(),
+ SizedBox(height: 20.h),
+ ],
+ ),
+ ))
],
),
);
}
- Widget bottomTopBtnWidget(){
- return Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- // 打开关闭声音
- GestureDetector(
- onTap: () {
- state.isOpenVoice.value = !state.isOpenVoice.value;
- },
- child: Container(
- width: 50.w,
- height: 50.w,
- padding: EdgeInsets.all(5.w),
- child: Obx(() => Image(
- width: 40.w,
- height: 40.w,
- image: state.isOpenVoice.value
- ? const AssetImage("images/main/icon_lockDetail_monitoringCloseVoice.png")
- : const AssetImage("images/main/icon_lockDetail_monitoringOpenVoice.png")
- )),
- ),
- ),
- SizedBox(width: 60.w),
- // 截图
- GestureDetector(
- onTap: () {
- // Get.toNamed(Routers.monitoringRealTimeScreenPage);
- },
- child: Container(
- width: 50.w,
- height: 50.w,
- padding: EdgeInsets.all(5.w),
- child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/main/icon_lockDetail_monitoringScreenshot.png")),
- ),
- ),
- SizedBox(width: 60.w),
- // 录制
- GestureDetector(
- onTap: () {
- // Get.toNamed(Routers.monitoringRealTimeScreenPage);
- },
- child: Container(
- width: 50.w,
- height: 50.w,
- padding: EdgeInsets.all(5.w),
- child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/main/icon_lockDetail_monitoringScreenRecording.png")),
- ),
- ),
- ]);
+ Widget bottomTopBtnWidget() {
+ return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
+ // 打开关闭声音
+ GestureDetector(
+ onTap: () {
+ state.isOpenVoice.value = !state.isOpenVoice.value;
+ },
+ child: Container(
+ width: 50.w,
+ height: 50.w,
+ padding: EdgeInsets.all(5.w),
+ child: Obx(() => Image(
+ width: 40.w,
+ height: 40.w,
+ image: state.isOpenVoice.value
+ ? const AssetImage(
+ "images/main/icon_lockDetail_monitoringCloseVoice.png")
+ : const AssetImage(
+ "images/main/icon_lockDetail_monitoringOpenVoice.png"))),
+ ),
+ ),
+ SizedBox(width: 60.w),
+ // 截图
+ GestureDetector(
+ onTap: () {
+ // Get.toNamed(Routers.monitoringRealTimeScreenPage);
+ },
+ child: Container(
+ width: 50.w,
+ height: 50.w,
+ padding: EdgeInsets.all(5.w),
+ child: Image(
+ width: 40.w,
+ height: 40.w,
+ image: const AssetImage(
+ "images/main/icon_lockDetail_monitoringScreenshot.png")),
+ ),
+ ),
+ SizedBox(width: 60.w),
+ // 录制
+ GestureDetector(
+ onTap: () {
+ // Get.toNamed(Routers.monitoringRealTimeScreenPage);
+ },
+ child: Container(
+ width: 50.w,
+ height: 50.w,
+ padding: EdgeInsets.all(5.w),
+ child: Image(
+ width: 40.w,
+ height: 40.w,
+ image: const AssetImage(
+ "images/main/icon_lockDetail_monitoringScreenRecording.png")),
+ ),
+ ),
+ ]);
}
- Widget bottomBottomBtnWidget(){
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- bottomBtnItemWidget("images/main/icon_lockDetail_monitoringTalkback.png", "点击对讲", Colors.white,(){
+ Widget bottomBottomBtnWidget() {
+ return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
+ // 接听
+ Obx(() => bottomBtnItemWidget(getAnswerBtnImg(), getAnswerBtnName(), Colors.white, () async {
+ //获取麦克风权限
+ await logic.getPermissionStatus().then((value) async {
+ if (!value) {
+ return;
+ }
- }),
- bottomBtnItemWidget("images/main/icon_lockDetail_monitoringUnlock.png", "长按开锁", AppColors.mainColor,(){
+ // state.isSenderAudioData.value = false;
+ print("发送接听了");
+ // 刚进来是接听状态,然后改为长按对讲
+ logic.udpAnswerAction();
+ });
+ },
+ longPress: (){
+ // 开始长按
+ print("onLongPress");
+ state.listAudioData.value = [];
+ if (state.udpStatus.value == 8) {
+ state.udpStatus.value = 9;
+ }
+ // logic.readG711Data();
+ logic.startProcessing();
+ },
+ longPressUp: () async {
+ // 长按结束
+ print("onLongPressUp");
+ if (state.udpStatus.value == 9) {
+ state.udpStatus.value = 8;
+ }
+ }
+ )
+ ),
+ bottomBtnItemWidget("images/main/icon_lockDetail_hangUp.png", "挂断", Colors.red, () async {
+ logic.stopProcessing();
- })
- ]);
+ // 挂断
+ logic.udpHangUpAction();
+ }),
+ bottomBtnItemWidget("images/main/icon_lockDetail_monitoringUnlock.png", "开锁", AppColors.mainColor, () {
+ showDeletPasswordAlertDialog(context);
+ })
+ ]);
}
- Widget bottomBtnItemWidget(String iconUrl, String name, Color backgroundColor, Function() onClick) {
+ String getAnswerBtnImg() {
+ switch (state.udpStatus.value) {
+ case 8:
+ return "images/main/icon_lockDetail_monitoringUnTalkback.png";
+ case 9:
+ return "images/main/icon_lockDetail_monitoringTalkback.png";
+ default:
+ return "images/main/icon_lockDetail_monitoringAnswerCalls.png";
+ }
+ }
+
+ String getAnswerBtnName() {
+ switch (state.udpStatus.value) {
+ case 8:
+ return "长按说话";
+ case 9:
+ return "松开发送";
+ default:
+ return "接听";
+ }
+ }
+
+ Widget bottomBtnItemWidget(String iconUrl, String name, Color backgroundColor, Function() onClick, {Function()? longPress, Function()? longPressUp}) {
var wh = 80.w;
return GestureDetector(
onTap: onClick,
+ onLongPress: longPress,
+ onLongPressUp: longPressUp,
child: SizedBox(
- height: 140.h,
+ height: 140.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@@ -156,15 +237,54 @@ class _LockMonitoringPageState extends State {
height: wh,
decoration: BoxDecoration(
color: backgroundColor,
- borderRadius: BorderRadius.circular((wh+ 10.w*2)/2)
- ),
+ borderRadius: BorderRadius.circular((wh + 10.w * 2) / 2)),
padding: EdgeInsets.all(20.w),
child: Image.asset(iconUrl, fit: BoxFit.fitWidth),
),
SizedBox(height: 20.w),
- Expanded(child: Text(name, style: TextStyle(fontSize: 20.sp, color: Colors.white), textAlign: TextAlign.center))
+ Expanded(
+ child: Text(name,
+ style: TextStyle(fontSize: 20.sp, color: Colors.white),
+ textAlign: TextAlign.center))
],
)),
);
}
+
+ void showDeletPasswordAlertDialog(BuildContext context) {
+ showDialog(
+ barrierDismissible: false,
+ context: context,
+ builder: (BuildContext context) {
+ return ShowTFView(
+ title: "请输入六位数字开锁密码",
+ tipTitle: "",
+ controller: state.passwordTF,
+ inputFormatters: [
+ LengthLimitingTextInputFormatter(6), //限制长度
+ FilteringTextInputFormatter.allow(RegExp("[0-9]")),
+ ],
+ sureClick: () async {
+ //发送删除锁请求
+ if (state.passwordTF.text.isEmpty) {
+ Toast.show(msg: "请输入开锁密码");
+ return;
+ }
+
+ // 开锁
+ logic.udpOpenDoorAction();
+ },
+ cancelClick: () {
+ Get.back();
+ },
+ );
+ },
+ );
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+
+ }
}
diff --git a/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_state.dart b/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_state.dart
index 991d59bf..1e3bec11 100644
--- a/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_state.dart
+++ b/star_lock/lib/main/lockDetail/monitoring/monitoring/lockMonitoring_state.dart
@@ -1,7 +1,35 @@
+import 'dart:async';
+import 'dart:typed_data';
+import 'package:flutter/material.dart';
+import 'package:flutter_voice_processor/flutter_voice_processor.dart';
import 'package:get/get.dart';
+import 'package:network_info_plus/network_info_plus.dart';
+
+import '../../../../tools/storage.dart';
class LockMonitoringState {
var isOpenVoice = false.obs;
+ var udpSendDataFrameNumber = 0;// 帧序号
+ // var isSenderAudioData = false.obs;// 是否要发送音频数据
-}
\ No newline at end of file
+ var userMobileIP = NetworkInfo().getWifiIP();
+ var userMobile = Storage.getMobile();
+
+ var udpStatus = 0.obs; //0:初始状态 1:等待监视 2: 3:监视中 4:呼叫成功 5:主角通话中 6:被叫通话 8:被叫通话中 9:长按说话
+ var passwordTF = TextEditingController();
+
+ var listData = Uint8List(0).obs; //得到的视频流字节数据
+ var listAudioData = [].obs; //得到的音频流字节数据
+
+ late final VoiceProcessor? voiceProcessor;
+
+
+ late Timer oneMinuteTimeTimer;// 定时器超过60秒关闭当前界面
+ var oneMinuteTime = 0.obs;// 定时器秒数
+
+ // 定时器如果发送了接听的命令 而没收到回复就每秒重复发送10次
+ late Timer answerTimer;
+ late Timer hangUpTimer;
+ late Timer openDoorTimer;
+}
diff --git a/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_logic.dart b/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_logic.dart
index 04a6c0d3..9bb42161 100644
--- a/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_logic.dart
+++ b/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_logic.dart
@@ -23,13 +23,16 @@ class PasswordKeyDetailLogic extends BaseGetXController {
//更新密码请求
Future updatePwdRequest() async {
PasswordKeyEntity entity = await ApiRepository.to.updatePasswordKey(
- state.itemData.value.lockId.toString(),
- state.itemData.value.keyboardPwdId.toString(),
- state.inputNameController.text,
- state.inputPwdController.text,
- '',
- '',
- '');
+ lockId: state.itemData.value.lockId!,
+ keyboardPwdId: state.itemData.value.keyboardPwdId.toString(),
+ keyboardPwdName: state.inputNameController.text,
+ newKeyboardPwd: state.inputPwdController.text,
+ startDate: state.startDate.value,
+ endDate: state.endDate.value,
+ changeType: state.changeType.value,
+ hoursStart: state.hoursStart.value,
+ hoursEnd: state.hoursEnd.value,
+ isCoerced: state.isCoerced.value);
if (entity.errorCode!.codeIsSuccessful) {
Toast.show(msg: "修改成功");
Get.back();
diff --git a/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_page.dart b/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_page.dart
index a175268a..1b89d35d 100644
--- a/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_page.dart
+++ b/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_page.dart
@@ -55,10 +55,19 @@ class _PasswordKeyDetailPageState extends State {
rightTitle: state.inputPwdController.text.isNotEmpty
? state.inputPwdController.text
: state.itemData.value.keyboardPwd,
- isHaveDirection: true,
+ isHaveDirection: (state.itemData.value.keyboardPwdType == 1 ||
+ state.itemData.value.keyboardPwdType == 4)
+ ? false
+ : true,
isHaveLine: true,
action: () {
- showCupertinoAlertDialog(context, state.inputPwdController);
+ if (state.itemData.value.keyboardPwdType == 1 ||
+ state.itemData.value.keyboardPwdType == 4) {
+ return;
+ } else {
+ showCupertinoAlertDialog(
+ context, state.inputPwdController);
+ }
}),
CommonItem(
leftTitel: TranslationLoader.lanKeys!.name!.tr,
diff --git a/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_state.dart b/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_state.dart
index b838e3c0..5745bef6 100644
--- a/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_state.dart
+++ b/star_lock/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_state.dart
@@ -6,6 +6,13 @@ class PasswordKeyDetailState {
final itemData = PasswordKeyListItem().obs;
final TextEditingController inputPwdController = TextEditingController();
final TextEditingController inputNameController = TextEditingController();
+ final changeType = '1'
+ .obs; //1-通过APP走蓝牙修改,不传默认1,必需先通过APP SDK蓝牙修改后调用该接口 2-通过网关或WiFi锁修改,如果是WiFi锁或有连接网关,则可以传2,直接调用该接口修改生效
+ final isCoerced = '1'.obs; //胁迫指纹:1;非胁迫指纹:2(胁迫指纹开锁触发报警)
+ final hoursStart = 0.obs;
+ final hoursEnd = 0.obs;
+ final startDate = 0.obs;
+ final endDate = 0.obs;
PasswordKeyDetailState() {
Map map = Get.arguments;
diff --git a/star_lock/lib/main/lockDetail/passwordKey/passwordKeyList/passwordKeyList_logic.dart b/star_lock/lib/main/lockDetail/passwordKey/passwordKeyList/passwordKeyList_logic.dart
index 4b920eb8..82b30cde 100644
--- a/star_lock/lib/main/lockDetail/passwordKey/passwordKeyList/passwordKeyList_logic.dart
+++ b/star_lock/lib/main/lockDetail/passwordKey/passwordKeyList/passwordKeyList_logic.dart
@@ -12,9 +12,8 @@ class PasswordKeyListLogic extends BaseGetXController {
//请求密码钥匙列表
void mockNetworkDataRequest() async {
PasswordKeyListEntity entity = await ApiRepository.to.passwordKeyList(
- '0',
+ state.keyInfo.value.keyStatus.toString(),
state.keyInfo.value.lockId.toString(),
- '0',
state.pageNum.toString(),
state.pageSize.toString(),
state.searchController.text);
@@ -35,9 +34,10 @@ class PasswordKeyListLogic extends BaseGetXController {
}
//删除密码请求 deleteType:1-蓝牙 2-网关
- Future deletePwdRequest(String lockId, String keyboardPwdId, int deleteType) async {
- PasswordKeyEntity entity = await ApiRepository.to.deleteKeyboardPwd(
- lockId, keyboardPwdId, deleteType);
+ Future deletePwdRequest(
+ String lockId, String keyboardPwdId, int deleteType) async {
+ PasswordKeyEntity entity = await ApiRepository.to
+ .deleteKeyboardPwd(lockId, keyboardPwdId, deleteType);
if (entity.errorCode!.codeIsSuccessful) {
Toast.show(msg: "删除成功");
mockNetworkDataRequest();
diff --git a/star_lock/lib/main/lockDetail/passwordKey/passwordKey_perpetual/passwordKey_perpetual_logic.dart b/star_lock/lib/main/lockDetail/passwordKey/passwordKey_perpetual/passwordKey_perpetual_logic.dart
index 49990907..3b5ed20a 100644
--- a/star_lock/lib/main/lockDetail/passwordKey/passwordKey_perpetual/passwordKey_perpetual_logic.dart
+++ b/star_lock/lib/main/lockDetail/passwordKey/passwordKey_perpetual/passwordKey_perpetual_logic.dart
@@ -90,18 +90,18 @@ class PasswordKeyPerpetualLogic extends BaseGetXController {
state.effectiveDateTime.value.millisecondsSinceEpoch.toString();
}
var entity = await ApiRepository.to.getPasswordKey(
- getFailureDateTime,
- '0',
- state.nameController.text,
- getKeyType,
- '0',
- lockId,
- '0',
- getEffectiveDateTime,
- '0',
- state.loopStartHours.value,
- state.loopEndHours.value,
- 1);
+ endDate: getFailureDateTime,
+ isExclusive: '0',
+ keyboardPwdName: state.nameController.text,
+ keyboardPwdType: getKeyType,
+ keyboardPwdVersion: '0',
+ lockId: lockId,
+ operatorUid: '0',
+ startDate: getEffectiveDateTime,
+ timezoneRawOffSet: '0',
+ startHours: state.loopStartHours.value,
+ endHours: state.loopEndHours.value,
+ isCoerced: 1);
if (entity.errorCode!.codeIsSuccessful) {
print('获取密码成功');
state.isSendSuccess.value = true;
diff --git a/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_logic.dart b/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_logic.dart
index 11d4b09b..b2cbf02d 100644
--- a/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_logic.dart
+++ b/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_logic.dart
@@ -1,13 +1,8 @@
-
import '../../../../tools/baseGetXController.dart';
import '../../../../tools/storage.dart';
-import 'demoModeLockDetail_state.dart';
class DemoModeLockDetailLogic extends BaseGetXController {
- DemoModeLockDetailState state = DemoModeLockDetailState();
-
-
@override
void onReady() {
diff --git a/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_page.dart b/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_page.dart
index 8da69913..50fe2a3e 100644
--- a/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_page.dart
+++ b/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_page.dart
@@ -19,7 +19,7 @@ class DemoModeLockDetailPage extends StatefulWidget {
class _DemoModeLockDetailPageState extends State {
final logic = Get.put(DemoModeLockDetailLogic());
- final state = Get.find().state;
+ // final state = Get.find().state;
@override
void initState() {
diff --git a/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_state.dart b/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_state.dart
deleted file mode 100644
index 4e3b435f..00000000
--- a/star_lock/lib/main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_state.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-class DemoModeLockDetailState {
-
-}
\ No newline at end of file
diff --git a/star_lock/lib/main/lockMian/lockMain/lockMain_logic.dart b/star_lock/lib/main/lockMian/lockMain/lockMain_logic.dart
index 2f84ecf2..6ee4cff0 100644
--- a/star_lock/lib/main/lockMian/lockMain/lockMain_logic.dart
+++ b/star_lock/lib/main/lockMian/lockMain/lockMain_logic.dart
@@ -1,10 +1,13 @@
import 'dart:async';
+import 'dart:typed_data';
import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
+import 'package:star_lock/talk/udp/udp_senderManage.dart';
import '../../../blue/io_tool/manager_event_bus.dart';
import '../../../network/api_repository.dart';
+import '../../../talk/udp/udp_help.dart';
import '../../../tools/baseGetXController.dart';
import '../../../tools/eventBusEventManage.dart';
import '../entity/lockListInfo_entity.dart';
@@ -106,7 +109,6 @@ class LockMainLogic extends BaseGetXController {
});
}
-
@override
void onReady() {
// TODO: implement onReady
@@ -114,6 +116,9 @@ class LockMainLogic extends BaseGetXController {
print("onReady()");
_initLoadDataAction();
+
+ // 开启UDP
+ UdpHelp().openUDP();
}
@override
@@ -133,6 +138,7 @@ class LockMainLogic extends BaseGetXController {
// refreshController.dispose();
_teamEvent.cancel();
+ // state.timer.cancel();
}
}
\ No newline at end of file
diff --git a/star_lock/lib/main/lockMian/lockMain/lockMain_state.dart b/star_lock/lib/main/lockMian/lockMain/lockMain_state.dart
index fb53a05b..a0d128eb 100644
--- a/star_lock/lib/main/lockMian/lockMain/lockMain_state.dart
+++ b/star_lock/lib/main/lockMian/lockMain/lockMain_state.dart
@@ -1,4 +1,6 @@
+import 'dart:async';
+
import 'package:get/get.dart';
import '../entity/lockListInfo_entity.dart';
@@ -7,4 +9,6 @@ class LockMainState {
// 0是无数据 1是有一条数据 2是有很多条数据
var dataLength = 100.obs;
var lockListInfoEntity = LockListInfoEntity().obs;
+
+ // late Timer timer;
}
\ No newline at end of file
diff --git a/star_lock/lib/mine/addLock/lockAddress/lockAddress/lockAddress_logic.dart b/star_lock/lib/mine/addLock/lockAddress/lockAddress/lockAddress_logic.dart
index b3589a61..d36aa1be 100644
--- a/star_lock/lib/mine/addLock/lockAddress/lockAddress/lockAddress_logic.dart
+++ b/star_lock/lib/mine/addLock/lockAddress/lockAddress/lockAddress_logic.dart
@@ -1,11 +1,8 @@
import '../../../../tools/baseGetXController.dart';
-import 'lockAddress_state.dart';
class LockAddressLogic extends BaseGetXController {
- final LockAddressState state = LockAddressState();
-
@override
void onReady() {
// TODO: implement onReady
diff --git a/star_lock/lib/mine/addLock/lockAddress/lockAddress/lockAddress_state.dart b/star_lock/lib/mine/addLock/lockAddress/lockAddress/lockAddress_state.dart
deleted file mode 100644
index 3926eed4..00000000
--- a/star_lock/lib/mine/addLock/lockAddress/lockAddress/lockAddress_state.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-class LockAddressState {
-
-}
\ No newline at end of file
diff --git a/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart b/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart
index 355fec18..f182c9b6 100644
--- a/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart
+++ b/star_lock/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart
@@ -1,4 +1,3 @@
-
import 'dart:async';
import 'package:flutter/material.dart';
@@ -23,37 +22,36 @@ import '../../../tools/dateTool.dart';
import '../../../tools/storage.dart';
import 'nearbyLock_state.dart';
-class NearbyLockLogic extends BaseGetXController{
-
+class NearbyLockLogic extends BaseGetXController {
final NearbyLockState state = NearbyLockState();
// 点击连接设备
- void connect(String lockId, String deviceName){
+ void connect(String lockId, String deviceName) {
// BlueManage().stopScan();
// BlueManage().judgeReconnect(lockId, deviceName, (DeviceConnectionState state) async {
// if (state == DeviceConnectionState.connected) {
// IoSenderManage.getPublicKey(lockId: deviceName);
// }
// }, isShowLoading: true);
- BlueManage().connect(deviceName, (state){
-
- }, isFrist: true, isShowLoading: true);
+ BlueManage()
+ .connect(deviceName, (state) {}, isFrist: true, isShowLoading: true);
}
// 获取解析后的数据
late StreamSubscription _replySubscription;
void _initReplySubscription() {
- _replySubscription = EventBusManager().eventBus!.on().listen((reply) {
- if(reply is GetPublicKeyReply) {
+ _replySubscription =
+ EventBusManager().eventBus!.on().listen((reply) {
+ if (reply is GetPublicKeyReply) {
_replyGetPublicKey(reply);
}
- if(reply is GetPrivateKeyReply) {
+ if (reply is GetPrivateKeyReply) {
_replyGetPrivateKeyKey(reply);
}
// 获取锁状态信息
- if(reply is GetStarLockStatuInfoReply) {
+ if (reply is GetStarLockStatuInfoReply) {
_replyGetStarLockStatusInfo(reply);
}
});
@@ -61,25 +59,25 @@ class NearbyLockLogic extends BaseGetXController{
Future _replyGetPublicKey(Reply reply) async {
// 获取公钥
- switch(reply.status){
+ switch (reply.status) {
case 0x00:
- //成功
- print("获取公钥成功");
- // 储存公钥
- var publicKey = reply.data.sublist(3);
- var saveStrList = changeIntListToStringList(publicKey);
- Storage.setStringList(saveBluePublicKey, saveStrList);
+ //成功
+ print("获取公钥成功");
+ // 储存公钥
+ var publicKey = reply.data.sublist(3);
+ var saveStrList = changeIntListToStringList(publicKey);
+ Storage.setStringList(saveBluePublicKey, saveStrList);
- IoSenderManage.getPrivateKey(
- lockId:BlueManage().connectDeviceName,
- keyID:"1",
- authUserID:await Storage.getUid(),
- nowTime:DateTime.now().millisecondsSinceEpoch~/1000,
- publicKeyData:publicKey,
- needAuthor:1);
+ IoSenderManage.getPrivateKey(
+ lockId: BlueManage().connectDeviceName,
+ keyID: "1",
+ authUserID: await Storage.getUid(),
+ nowTime: DateTime.now().millisecondsSinceEpoch ~/ 1000,
+ publicKeyData: publicKey,
+ needAuthor: 1);
break;
case 0x07:
- //无权限
+ //无权限
print("获取公钥无权限");
break;
case 0x0f:
@@ -87,14 +85,14 @@ class NearbyLockLogic extends BaseGetXController{
print("获取公钥用户已存在");
break;
default:
- //失败
+ //失败
print("获取公钥失败");
break;
}
}
Future _replyGetPrivateKeyKey(Reply reply) async {
- switch(reply.status){
+ switch (reply.status) {
case 0x00:
//成功
print('获取私钥成功');
@@ -115,8 +113,7 @@ class NearbyLockLogic extends BaseGetXController{
// 时间戳
List timestamp = reply.data.sublist(32, 36);
- state.timestampValue = (
- (0xff & timestamp[(0)]) << 24 |
+ state.timestampValue = ((0xff & timestamp[(0)]) << 24 |
(0xff & timestamp[1]) << 16 |
(0xff & timestamp[2]) << 8 |
(0xFF & timestamp[3]));
@@ -126,7 +123,7 @@ class NearbyLockLogic extends BaseGetXController{
_getStarLockStatus();
break;
case 0x07:
- //无权限
+ //无权限
print('获取私钥无权限');
break;
@@ -144,15 +141,15 @@ class NearbyLockLogic extends BaseGetXController{
// 获取星锁状态
Future _replyGetStarLockStatusInfo(Reply reply) async {
int status = reply.data[2];
- switch(status){
+ switch (status) {
case 0x00:
- //成功
+ //成功
print("${reply.commandType}数据解析成功");
// 厂商名称
var vendor = reply.data.sublist(3, 23);
print("vendor:$vendor reply.data:${reply.data}");
- var vendorStr = utf8String(vendor);
+ var vendorStr = utf8String(vendor);
state.lockInfo["vendor"] = vendorStr;
// print("vendor:$vendor vendorStr:$vendorStr vendorStr.length${vendorStr.length}");
@@ -163,38 +160,39 @@ class NearbyLockLogic extends BaseGetXController{
// 产品名称
var model = reply.data.sublist(24, 44);
- var modelStr = utf8String(model);
+ var modelStr = utf8String(model);
state.lockInfo["model"] = modelStr;
// print("model:$model modelStr:$modelStr modelStr:${modelStr.length}");
// 软件版本
var fwVersion = reply.data.sublist(44, 64);
- var fwVersionStr = utf8String(fwVersion);
+ var fwVersionStr = utf8String(fwVersion);
state.lockInfo["fwVersion"] = fwVersionStr;
// print("fwVersion:$fwVersion fwVersionStr:$fwVersionStr fwVersionStr:${fwVersionStr.length}");
// 硬件版本
var hwVersion = reply.data.sublist(64, 84);
- var hwVersionStr = utf8String(hwVersion);
+ var hwVersionStr = utf8String(hwVersion);
state.lockInfo["hwVersion"] = hwVersionStr;
// print("hwVersion:$hwVersion hwVersionStr:${hwVersionStr.length}");
// 厂商序列号
var serialNum0 = reply.data.sublist(84, 100);
- var serialNum0Str = utf8String(serialNum0);
+ var serialNum0Str = utf8String(serialNum0);
// state.lockInfo["serialNum0"] = serialNum0Str;
- state.lockInfo["serialNum0"] = "${DateTime.now().millisecondsSinceEpoch~/10}";
+ state.lockInfo["serialNum0"] =
+ "${DateTime.now().millisecondsSinceEpoch ~/ 10}";
// print("serialNum0Str:$serialNum0Str serialNum0Str:${serialNum0Str.length}");
// 成品商序列号
var serialNum1 = reply.data.sublist(100, 116);
- var serialNum1Str = utf8String(serialNum1);
+ var serialNum1Str = utf8String(serialNum1);
state.lockInfo["serialNum1"] = serialNum1Str;
// print("serialNum1Str:$serialNum1Str serialNum1Str:${serialNum1Str.length}");
// 蓝牙名称
var btDeviceName = reply.data.sublist(116, 132);
- var btDeviceNameStr = utf8String(btDeviceName);
+ var btDeviceNameStr = utf8String(btDeviceName);
state.lockInfo["btDeviceName"] = btDeviceNameStr;
// print("btDeviceName:$btDeviceName btDeviceNameStr:$btDeviceNameStr btDeviceNameStr:${btDeviceNameStr.length}");
@@ -205,32 +203,39 @@ class NearbyLockLogic extends BaseGetXController{
// 重置次数
var restoreCounter = reply.data.sublist(133, 135);
- state.lockInfo["restoreCount"] = restoreCounter[0] * 256 + restoreCounter[1];
+ state.lockInfo["restoreCount"] =
+ restoreCounter[0] * 256 + restoreCounter[1];
// print("restoreCounter:$restoreCounter");
// 重置时间
var restoreDate = reply.data.sublist(135, 139);
- int restoreDateValue = ((0xff & restoreDate[(0)]) << 24 | (0xff & restoreDate[1]) << 16 | (0xff & restoreDate[2]) << 8 | (0xFF & restoreDate[3]));
+ int restoreDateValue = ((0xff & restoreDate[(0)]) << 24 |
+ (0xff & restoreDate[1]) << 16 |
+ (0xff & restoreDate[2]) << 8 |
+ (0xFF & restoreDate[3]));
// String restoreDateStr = DateTool().dateToYMDHNSString(restoreDateValue.toString());
- state.lockInfo["restoreDate"] = restoreDateValue*1000;
+ state.lockInfo["restoreDate"] = restoreDateValue * 1000;
// print("restoreDate:$restoreDate restoreDateValue:$restoreDateValue");
// 主控芯片型号
var icPartNo = reply.data.sublist(139, 149);
- var icPartNoStr = utf8String(icPartNo);
+ var icPartNoStr = utf8String(icPartNo);
state.lockInfo["icPartNo"] = icPartNoStr;
// print("icPartNo:$icPartNo icPartNoStr:$icPartNoStr");
// 有效时间
var indate = reply.data.sublist(149, 153);
- int indateValue = ((0xff & indate[(0)]) << 24 | (0xff & indate[1]) << 16 | (0xff & indate[2]) << 8 | (0xFF & indate[3]));
+ int indateValue = ((0xff & indate[(0)]) << 24 |
+ (0xff & indate[1]) << 16 |
+ (0xff & indate[2]) << 8 |
+ (0xFF & indate[3]));
// String indateStr = DateTool().dateToYMDHNSString("$indateValue");
- state.lockInfo["indate"] = indateValue*1000;
+ state.lockInfo["indate"] = indateValue * 1000;
// print("indate:$indate indateValue:$indateValue");
// mac地址
var macAddress = reply.data.sublist(153, 173);
- var macAddressStr = utf8String(macAddress);
+ var macAddressStr = utf8String(macAddress);
state.lockInfo["mac"] = macAddressStr;
print("macAddress:$macAddress macAddressStr:$macAddressStr");
@@ -239,7 +244,8 @@ class NearbyLockLogic extends BaseGetXController{
var featureValueLength = reply.data[173];
// 锁特征值说明(本机能支持的功能)
// 获取到锁给的字符数组
- var featureValue = reply.data.sublist(index+1, index + featureValueLength+1);
+ var featureValue =
+ reply.data.sublist(index + 1, index + featureValueLength + 1);
String featureValueStr = asciiString(featureValue);
state.featureValue = featureValueStr;
// List allFeatureValueTwoList = charListChangeIntList(featureValue);
@@ -249,7 +255,8 @@ class NearbyLockLogic extends BaseGetXController{
// 使能特征值字符串长度
var featureEnValLength = reply.data[index];
// 使能锁特征值说明(本机启用的功能)
- var featureEnVal = reply.data.sublist(index+1, index + featureEnValLength+1);
+ var featureEnVal =
+ reply.data.sublist(index + 1, index + featureEnValLength + 1);
String featureEnValStr = asciiString(featureValue);
state.featureSettingValue = featureEnValStr;
// List allFeatureEnValTwoList = charListChangeIntList(featureEnVal);
@@ -264,7 +271,7 @@ class NearbyLockLogic extends BaseGetXController{
// print("featureParaTotalList:$featureParaTotalList");
Get.toNamed(Routers.lockAddressGaoDePage, arguments: {
- "pwdTimestamp": state.timestampValue*1000,
+ "pwdTimestamp": state.timestampValue * 1000,
"lockInfo": state.lockInfo,
"featureValue": state.featureValue,
"featureSettingValue": state.featureSettingValue,
@@ -273,22 +280,22 @@ class NearbyLockLogic extends BaseGetXController{
break;
case 0x06:
- //无权限
+ //无权限
print("${reply.commandType}需要鉴权");
break;
case 0x07:
- //无权限
+ //无权限
print("${reply.commandType}用户无权限");
break;
case 0x09:
- // 权限校验错误
+ // 权限校验错误
print("${reply.commandType}权限校验错误");
break;
default:
- //失败
+ //失败
print("${reply.commandType}失败");
break;
@@ -315,7 +322,8 @@ class NearbyLockLogic extends BaseGetXController{
Future _getStarLockStatus() async {
// print("connectDeviceMacAddress:${BlueManage().connectDeviceMacAddress} connectDeviceName:${BlueManage().connectDeviceName}");
// 进来之后首先连接
- BlueManage().judgeReconnect(BlueManage().connectDeviceName, (DeviceConnectionState state) async {
+ BlueManage().judgeReconnect(BlueManage().connectDeviceName,
+ (DeviceConnectionState state) async {
if (state == DeviceConnectionState.connected) {
var privateKey = await Storage.getStringList(saveBluePrivateKey);
List getPrivateKeyList = changeStringListToIntList(privateKey!);
@@ -325,21 +333,27 @@ class NearbyLockLogic extends BaseGetXController{
// privateKey:getPrivateKeyList,
// );
IoSenderManage.senderGetStarLockStatuInfo(
- lockID:BlueManage().connectDeviceName,
- userID:await Storage.getUid(),
- privateKey:getPrivateKeyList,
+ lockID: BlueManage().connectDeviceName,
+ userID: await Storage.getUid(),
+ privateKey: getPrivateKeyList,
);
}
}, isShowLoading: false);
}
- late StreamSubscription> _scanListDiscoveredDeviceSubscription;
+ late StreamSubscription>
+ _scanListDiscoveredDeviceSubscription;
void _scanListDiscoveredDeviceSubscriptionAction() {
- _scanListDiscoveredDeviceSubscription = EventBusManager().eventBus!.on>().listen((List list) {
+ _scanListDiscoveredDeviceSubscription = EventBusManager()
+ .eventBus!
+ .on>()
+ .listen((List list) {
state.devices.clear();
- for(int i = 0;i _replySubscription;
void _initReplySubscription() {
- _replySubscription = EventBusManager().eventBus!.on().listen((reply) {
- if(reply is AddUserReply) {
+ _replySubscription =
+ EventBusManager().eventBus!.on().listen((reply) {
+ if (reply is AddUserReply) {
_replyAddUserKey(reply);
}
});
@@ -45,16 +45,16 @@ class SaveLockLogic extends BaseGetXController {
// print("status:$status reply.data:${reply.data}");
print("status:$status");
- switch(status){
+ switch (status) {
case 0x00:
- //成功
+ //成功
print("添加用户数据解析成功");
state.lockUserNo = reply.data[47] + 1;
bindBlueAdmin();
break;
case 0x06:
- //无权限
+ //无权限
print("需要鉴权");
var privateKey = await Storage.getStringList(saveBluePrivateKey);
List getPrivateKeyList = changeStringListToIntList(privateKey!);
@@ -64,33 +64,32 @@ class SaveLockLogic extends BaseGetXController {
IoSenderManage.senderAddUser(
lockID: BlueManage().connectDeviceName,
- authUserID:await Storage.getUid(),
- keyID:"1",
- userID:await Storage.getUid(),
- openMode:1,
- keyType:1,
- startDate:DateTime.now().millisecondsSinceEpoch,
- expireDate:0x11223344,
- role:255,
- password:"123456",
- needAuthor:1,
- publicKey:publicKeyDataList,
- privateKey:getPrivateKeyList,
- token: token
- );
+ authUserID: await Storage.getUid(),
+ keyID: "1",
+ userID: await Storage.getUid(),
+ openMode: 1,
+ keyType: 1,
+ startDate: DateTime.now().millisecondsSinceEpoch,
+ expireDate: 0x11223344,
+ role: 255,
+ password: "123456",
+ needAuthor: 1,
+ publicKey: publicKeyDataList,
+ privateKey: getPrivateKeyList,
+ token: token);
break;
case 0x07:
- //无权限
+ //无权限
print("用户无权限");
break;
case 0x09:
- // 权限校验错误
+ // 权限校验错误
print("添加用户权限校验错误");
break;
default:
- //失败
+ //失败
print("领锁失败");
break;
@@ -98,15 +97,16 @@ class SaveLockLogic extends BaseGetXController {
}
// 保存事件,先调用蓝牙,蓝牙调用成功后再调用后台接口
- saveLockAction(){
+ saveLockAction() {
addUserConnectBlue();
}
// 添加用户
Future addUserConnectBlue() async {
// 进来之后首先连接
- BlueManage().judgeReconnect(BlueManage().connectDeviceName, (DeviceConnectionState state) async {
- if (state == DeviceConnectionState.connected){
+ BlueManage().judgeReconnect(BlueManage().connectDeviceName,
+ (DeviceConnectionState state) async {
+ if (state == DeviceConnectionState.connected) {
// 私钥
var privateKey = await Storage.getStringList(saveBluePrivateKey);
List getPrivateKeyList = changeStringListToIntList(privateKey!);
@@ -115,32 +115,31 @@ class SaveLockLogic extends BaseGetXController {
List publicKeyDataList = changeStringListToIntList(publicKey!);
var token = await Storage.getStringList(saveBlueToken);
- List getTokenList = [0,0,0,0];
- if(token != null){
+ List getTokenList = [0, 0, 0, 0];
+ if (token != null) {
getTokenList = changeStringListToIntList(token);
}
IoSenderManage.senderAddUser(
lockID: BlueManage().connectDeviceName,
- authUserID:await Storage.getUid(),
- keyID:"1",
- userID:await Storage.getUid(),
- openMode:1,
- keyType:1,
- startDate:DateTime.now().millisecondsSinceEpoch,
- expireDate:0x11223344,
- role:255,
- password:"123456",
- needAuthor:1,
- publicKey:publicKeyDataList,
- privateKey:getPrivateKeyList,
- token: getTokenList
- );
+ authUserID: await Storage.getUid(),
+ keyID: "1",
+ userID: await Storage.getUid(),
+ openMode: 1,
+ keyType: 1,
+ startDate: DateTime.now().millisecondsSinceEpoch,
+ expireDate: 0x11223344,
+ role: 255,
+ password: "123456",
+ needAuthor: 1,
+ publicKey: publicKeyDataList,
+ privateKey: getPrivateKeyList,
+ token: getTokenList);
}
});
}
- void bindBlueAdmin() async{
+ void bindBlueAdmin() async {
print("state.lockInfo:${state.lockInfo}");
var positionMap = {};
@@ -172,17 +171,17 @@ class SaveLockLogic extends BaseGetXController {
// print("addUser:publicKeyDataList$publicKeyDataList getPrivateKeyList:$getPrivateKeyList signKeyDataList:$signKeyDataList");
var entity = await ApiRepository.to.bindingBlueAdmin(
- lockAlias:state.aliName.value,
- position:positionMap,
- bluetooth:bluetooth,
- lockInfo: state.lockInfo,
- lockUserNo:state.lockUserNo.toString(),
- pwdTimestamp:state.pwdTimestamp.value.toString(),
- featureValue:state.featureValue,
- featureSettingValue:state.featureSettingValue,
- featureSettingParams:state.featureSettingParams,
+ lockAlias: state.aliName.value,
+ position: positionMap,
+ bluetooth: bluetooth,
+ lockInfo: state.lockInfo,
+ lockUserNo: state.lockUserNo.toString(),
+ pwdTimestamp: state.pwdTimestamp.value.toString(),
+ featureValue: state.featureValue,
+ featureSettingValue: state.featureSettingValue,
+ featureSettingParams: state.featureSettingParams,
);
- if(entity.errorCode!.codeIsSuccessful){
+ if (entity.errorCode!.codeIsSuccessful) {
eventBus.fire(RefreshLockListInfoDataEvent());
Get.offAllNamed(Routers.starLockMain);
}
@@ -202,7 +201,6 @@ class SaveLockLogic extends BaseGetXController {
// TODO: implement onInit
super.onInit();
print("onInit()");
-
}
@override
@@ -211,5 +209,4 @@ class SaveLockLogic extends BaseGetXController {
super.onClose();
_replySubscription.cancel();
}
-
-}
\ No newline at end of file
+}
diff --git a/star_lock/lib/mine/mine/starLockMine_state.dart b/star_lock/lib/mine/mine/starLockMine_state.dart
index 1008ae15..6470ce7e 100644
--- a/star_lock/lib/mine/mine/starLockMine_state.dart
+++ b/star_lock/lib/mine/mine/starLockMine_state.dart
@@ -37,7 +37,7 @@ class StarLockMineState {
}
String mobile() {
- return loginData.value.mobile ?? '-';
+ return loginData.value.mobile ?? '';
}
String email() {
diff --git a/star_lock/lib/nav/navLogic.dart b/star_lock/lib/nav/navLogic.dart
index b74d9453..ed2bd480 100644
--- a/star_lock/lib/nav/navLogic.dart
+++ b/star_lock/lib/nav/navLogic.dart
@@ -1,9 +1,6 @@
-
import '../tools/baseGetXController.dart';
-import 'navState.dart';
class NavLogic extends BaseGetXController {
- final NavState state = NavState();
}
\ No newline at end of file
diff --git a/star_lock/lib/nav/navState.dart b/star_lock/lib/nav/navState.dart
deleted file mode 100644
index ec631f3e..00000000
--- a/star_lock/lib/nav/navState.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-class NavState{
-
-}
\ No newline at end of file
diff --git a/star_lock/lib/network/api.dart b/star_lock/lib/network/api.dart
index ad6f23a6..a150c7c7 100644
--- a/star_lock/lib/network/api.dart
+++ b/star_lock/lib/network/api.dart
@@ -1,7 +1,9 @@
abstract class Api {
static String baseAddress = "https://pre.lock.star-lock.cn:8093"; //预发布环境
+ // static String baseAddress = "http://192.168.56.101:8099"; //联调环境
final String baseUrl = "$baseAddress/api";
+
// final String baseUrl = "http://test.lock.star-lock.cn/api"; // 葛工
// final String baseUrl = "https://lock.star-lock.cn/api"; // 测试环境
// final String baseUrl = "http://wenlin.lock.star-lock.cn/api"; //曾工
diff --git a/star_lock/lib/network/api_provider.dart b/star_lock/lib/network/api_provider.dart
index af5e9971..8985e9ee 100644
--- a/star_lock/lib/network/api_provider.dart
+++ b/star_lock/lib/network/api_provider.dart
@@ -362,19 +362,13 @@ class ApiProvider extends BaseProvider {
Future getWifiLockServiceIpAndPort() =>
post(getWifiServiceIpURL.toUrl, jsonEncode({}));
- Future passwordKeyList(
- String keyStatus,
- String lockId,
- String operatorUid,
- String pageNo,
- String pageSize,
- String searchStr) =>
+ Future passwordKeyList(String keyStatus, String lockId,
+ String pageNo, String pageSize, String searchStr) =>
post(
passwordKeyListURL.toUrl,
jsonEncode({
'keyStatus': keyStatus,
'lockId': lockId,
- 'operatorUid': operatorUid,
'pageNo': pageNo,
'pageSize': pageSize,
'searchStr': searchStr
@@ -436,14 +430,16 @@ class ApiProvider extends BaseProvider {
}));
Future updateKeyboardPwd(
- String lockId,
- String keyboardPwdId,
- String keyboardPwdName,
- String newKeyboardPwd,
- String startDate,
- String endDate,
- String changeType,
- ) =>
+ int lockId,
+ String keyboardPwdId,
+ String keyboardPwdName,
+ String newKeyboardPwd,
+ int startDate,
+ int endDate,
+ String changeType,
+ String isCoerced,
+ int hoursStart,
+ int hoursEnd) =>
post(
updatePasswordKeyURL.toUrl,
jsonEncode({
@@ -454,6 +450,9 @@ class ApiProvider extends BaseProvider {
'startDate': startDate,
'endDate': endDate,
'changeType': changeType,
+ 'isCoerced': isCoerced,
+ 'hoursStart': hoursStart,
+ 'hoursEnd': hoursEnd
}));
Future clearOperationRecord(String lockId) =>
diff --git a/star_lock/lib/network/api_repository.dart b/star_lock/lib/network/api_repository.dart
index ee55448d..6276ef48 100644
--- a/star_lock/lib/network/api_repository.dart
+++ b/star_lock/lib/network/api_repository.dart
@@ -323,15 +323,10 @@ class ApiRepository {
}
//密码列表
- Future passwordKeyList(
- String keyStatus,
- String lockId,
- String operatorUid,
- String pageNo,
- String pageSize,
- String searchStr) async {
+ Future passwordKeyList(String keyStatus, String lockId,
+ String pageNo, String pageSize, String searchStr) async {
final res = await apiProvider.passwordKeyList(
- keyStatus, lockId, operatorUid, pageNo, pageSize, searchStr);
+ keyStatus, lockId, pageNo, pageSize, searchStr);
return PasswordKeyListEntity.fromJson(res.body);
}
@@ -357,43 +352,47 @@ class ApiRepository {
}
// 获取所有锁设置信息
- Future getLockSettingInfoData({required String lockId}) async {
+ Future getLockSettingInfoData(
+ {required String lockId}) async {
final res = await apiProvider.getLockSettingInfoData(lockId);
return LockSetInfoEntity.fromJson(res.body);
}
// 删除锁
- Future deletOwnerLockData({required String lockId}) async {
+ Future deletOwnerLockData(
+ {required String lockId}) async {
final res = await apiProvider.deletLockInfo(lockId);
return LockListInfoEntity.fromJson(res.body);
}
// 删除钥匙
- Future deletOwnerKeyData({required String lockId, required String keyId}) async {
+ Future deletOwnerKeyData(
+ {required String lockId, required String keyId}) async {
final res = await apiProvider.deletOwnerKeyInfo(lockId, keyId);
return LockListInfoEntity.fromJson(res.body);
}
// 检查账户密码
- Future checkLoginPassword({required String password}) async {
+ Future checkLoginPassword(
+ {required String password}) async {
final res = await apiProvider.checkLoginPassword(password);
return LockListInfoEntity.fromJson(res.body);
}
//获取密码
Future getPasswordKey(
- String endDate,
- String isExclusive,
- String keyboardPwdName,
- String keyboardPwdType,
- String keyboardPwdVersion,
- String lockId,
- String operatorUid,
- String startDate,
- String timezoneRawOffSet,
- int startHours,
- int endHours,
- int isCoerced) async {
+ {required String endDate,
+ required String isExclusive,
+ required String keyboardPwdName,
+ required String keyboardPwdType,
+ required String keyboardPwdVersion,
+ required String lockId,
+ required String operatorUid,
+ required String startDate,
+ required String timezoneRawOffSet,
+ required int startHours,
+ required int endHours,
+ required int isCoerced}) async {
final res = await apiProvider.getKeyboardPwd(
endDate,
isExclusive,
@@ -427,16 +426,27 @@ class ApiRepository {
//修改密码
Future updatePasswordKey(
- String lockId,
- String keyboardPwdId,
- String keyboardPwdName,
- String newKeyboardPwd,
- String startDate,
- String endDate,
- String changeType,
- ) async {
- final res = await apiProvider.updateKeyboardPwd(lockId, keyboardPwdId,
- keyboardPwdName, newKeyboardPwd, startDate, endDate, changeType);
+ {required int lockId,
+ required String keyboardPwdId,
+ required String keyboardPwdName,
+ required String newKeyboardPwd,
+ required int startDate,
+ required int endDate,
+ required String changeType,
+ required String isCoerced,
+ required int hoursStart,
+ required int hoursEnd}) async {
+ final res = await apiProvider.updateKeyboardPwd(
+ lockId,
+ keyboardPwdId,
+ keyboardPwdName,
+ newKeyboardPwd,
+ startDate,
+ endDate,
+ changeType,
+ isCoerced,
+ hoursStart,
+ hoursEnd);
return PasswordKeyEntity.fromJson(res.body);
}
diff --git a/star_lock/lib/talk/call/callTalk.dart b/star_lock/lib/talk/call/callTalk.dart
new file mode 100644
index 00000000..33b12d26
--- /dev/null
+++ b/star_lock/lib/talk/call/callTalk.dart
@@ -0,0 +1,155 @@
+import 'dart:convert';
+import 'dart:typed_data';
+import 'package:flutter/foundation.dart';
+import 'package:get/get.dart';
+import 'package:star_lock/talk/call/g711.dart';
+import 'package:star_lock/talk/call/iFrameInfo.dart';
+import '../../tools/eventBusEventManage.dart';
+import 'package:flutter_pcm_sound/flutter_pcm_sound.dart';
+import 'package:flutter_sound/flutter_sound.dart';
+
+class CallTalk {
+ static CallTalk? _manager;
+ static int POS_iframe_index = 63;
+ static int POS_alen = 65;
+ static int POS_blen = 73;
+ static int POS_bag_index = 71;
+ static int POS_bag_num = 69;
+ static int POS_data = 77;
+ static int ABUF_NUM = 100;
+ static int FIRSTINDEX = 1;
+
+ int status = 0; // 假设有这个成员变量
+ IframeInfo? iframe; // 假设有这个成员变量
+ var growableList;
+ bool getFirstFrame = false; //是否得到了第一帧
+ List allDataBytes = []; //音频数据
+
+ CallTalk._init() {
+ iframe = IframeInfo();
+ FlutterPcmSound.setLogLevel(LogLevel.error);
+ FlutterPcmSound.setup(sampleRate: 8000, channelCount: 1);
+ FlutterPcmSound.setFeedThreshold(8000 ~/ 2);
+ }
+
+ static CallTalk _share() {
+ _manager ??= CallTalk._init();
+ return _manager!;
+ }
+
+ factory CallTalk() => _share();
+ CallTalk get manager => _share();
+
+ Future getAVData(Uint8List bb, int len) async {
+ // 音频数据
+ if (bb[61] == 1) {
+ Uint8List g711Data = bb.sublist(77, bb.length);
+
+ List pcmBytes;
+ try {
+ // 将 ALaw 转为 Linear
+ pcmBytes = G711().convertList(g711Data);
+ allDataBytes.addAll(pcmBytes);
+
+ // String filePath = 'assets/s10-g711.bin';
+ //
+ // List audioData = await G711().readAssetFile(filePath);
+ // pcmBytes = G711().convertList(audioData);
+ // allDataBytes = pcmBytes.sublist(0, 640);
+
+ _initializeAudioPlayer(pcmBytes);
+ } catch (e) {
+ print('Error decoding G.711 to PCM: $e');
+ }
+ }
+ // 视频数据
+ else {
+ // 音视频数据开始下标
+ var bagLen = bb[POS_blen + 2] + bb[POS_blen + 3] * 256;
+ // print('音视频数据开始下标 bagLen:$bagLen');
+
+ // 获取帧序号 63
+ int getIframeIndex =
+ bb[POS_iframe_index] + bb[POS_iframe_index + 1] * 256;
+ // print('获取帧序号 getIframeIndex:$getIframeIndex');
+
+ // 获取帧长度 65
+ // int alen = bb[POS_alen] & 0xff;
+ // var alenList = bb.sublist(POS_alen, POS_alen + 4);
+ // int alen = ((0xff & alenList[(0)]) << 24 |
+ // (0xff & alenList[1]) << 16 |
+ // (0xff & alenList[2]) << 8 |
+ // (0xFF & alenList[3]));
+ // print('获取帧长度 alen:$alen');
+
+ // 当前包号 71
+ int getBagIndex = bb[POS_bag_index] & 0xff;
+ // print('当前包号 getBagIndex:$getBagIndex');
+ // 总包数 69
+ int getBagNum = bb[POS_bag_num] & 0xff;
+ // print('总包数 getBagNum:$getBagNum');
+ // 数据长度 73
+ int blen = bb[POS_blen] + bb[POS_blen + 1] * 256;
+ // print('数据长度 blen:$blen');
+
+ // 这里判断是否是同一帧,如果不是同一帧就重新创建一个 IframeInfo
+ if (getIframeIndex != iframe!.iframeIndex) {
+ iframe = IframeInfo();
+ iframe!.iframeIndex = getIframeIndex;
+ iframe!.bagNum = getBagNum;
+ // iframe!.cur_len = alen;
+ // iframe!.bb = Uint8List(alen);
+ growableList = iframe!.bb!.toList(growable: true);
+ }
+
+ iframe!.bagReceive++;
+
+ // 如果是同一帧就添加起来
+ if (getIframeIndex == iframe!.iframeIndex) {
+ var getList = bb.sublist(77, bb.length);
+ growableList.addAll(getList);
+ }
+ // print('iframe.bagNum: ${iframe!.bagNum} iframe.bagReceive: ${iframe!.bagReceive}');
+
+ // 如果收到的包数等于总包数,说明这一帧数据已经接收完毕
+ if (iframe!.bagNum == iframe!.bagReceive) {
+ // print('播放第${iframe!.iframeIndex}帧 一帧图片的hexStringData: ${Uint8List.fromList(growableList)}');
+
+ //判断第一帧是否接收到
+ if (iframe!.iframeIndex == 0) {
+ getFirstFrame = true;
+ eventBus.fire(GetFirstFrameGoPush(getFirstFrame));
+ }
+
+ eventBus.fire(GetTVDataRefreshUI(growableList));
+ }
+ }
+ }
+
+ //音频相关处理
+ Future _initializeAudioPlayer(List audioData) async {
+ Get.log('_initializeAudioPlayer audioData:$audioData');
+
+ PcmArrayInt16 fromList = PcmArrayInt16.fromList(audioData);
+ // FlutterPcmSound.setFeedCallback(onFeed);
+ await FlutterPcmSound.feed(fromList);
+ FlutterPcmSound.play();
+ }
+
+ void onFeed(int remainingFrames) async {
+ int framesToFeed = 320;
+
+ if (allDataBytes.length >= framesToFeed) {
+ List frames = allDataBytes.sublist(0, framesToFeed);
+ allDataBytes.removeRange(0, framesToFeed);
+
+ // 将数据传递给 FlutterPcmSound
+ PcmArrayInt16 fromList = PcmArrayInt16.fromList(frames);
+ await FlutterPcmSound.feed(fromList);
+ FlutterPcmSound.play();
+ } else {
+ // 处理长度不足的情况,可以等待更多数据或者采取其他措施
+ print("Not enough data in allPcmData.");
+ }
+ }
+ }
diff --git a/star_lock/lib/talk/call/g711.dart b/star_lock/lib/talk/call/g711.dart
new file mode 100644
index 00000000..aaf3afcc
--- /dev/null
+++ b/star_lock/lib/talk/call/g711.dart
@@ -0,0 +1,43 @@
+import 'dart:async';
+import 'package:flutter/services.dart';
+
+class G711 {
+ Future> readAssetFile(String assetPath) async {
+ ByteData data = await rootBundle.load(assetPath);
+ List bytes = data.buffer.asUint8List();
+ return bytes;
+ }
+
+ int ALawToLinear(int aVal) {
+ // 取反
+ aVal = ~aVal;
+
+ // 计算偏移
+ int t = ((aVal & 0x0F) << 3) + 0x84;
+ t <<= (aVal & 0x70) >> 4;
+
+ // 根据符号位决定返回值的正负
+ return (aVal & 0x80) != 0 ? 0x84 - t : t - 0x84;
+ }
+
+//711解码为pcm数据
+ List convertList(List aLawList) {
+ // 将 ALawToLinear 函数应用于 List
+ List linearList = aLawList.map(ALawToLinear).toList();
+ return linearList;
+ }
+
+//List转为Uint8List
+ Uint8List convertToInt8ListLittleEndian(List intList) {
+ List int8List = [];
+
+ for (int intValue in intList) {
+ intValue = intValue * 2;
+ // 将 int 拆分为两个字节,采用小端序
+ int8List.add(intValue & 0xFF); // 低 8 位
+ int8List.add((intValue & 0xFF00) >> 8); // 高 8 位
+ }
+
+ return Uint8List.fromList(int8List);
+ }
+}
diff --git a/star_lock/lib/talk/call/iFrameInfo.dart b/star_lock/lib/talk/call/iFrameInfo.dart
new file mode 100644
index 00000000..f244b1c8
--- /dev/null
+++ b/star_lock/lib/talk/call/iFrameInfo.dart
@@ -0,0 +1,22 @@
+import 'dart:typed_data';
+
+class IframeInfo {
+ int iframeIndex = -1;
+ int iframeTime = 0;
+ int bagNum = 0;
+ int bagReceive = 0;
+ bool isFull = false;
+ int cur_len = 0;
+ int bb_len = 0;
+ Uint8List? bb;
+ int codecMode = 0;
+
+ IframeInfo() {
+ iframeIndex = -1;
+ bagNum = 0;
+ bagReceive = 0;
+ isFull = false;
+ cur_len = 0;
+ bb = Uint8List(0);
+ }
+}
diff --git a/star_lock/lib/talk/udp/io_protocol/udp_heart.dart b/star_lock/lib/talk/udp/io_protocol/udp_heart.dart
new file mode 100644
index 00000000..43b319ae
--- /dev/null
+++ b/star_lock/lib/talk/udp/io_protocol/udp_heart.dart
@@ -0,0 +1,90 @@
+
+import 'dart:convert';
+
+import 'package:star_lock/tools/toast.dart';
+
+import '../../../blue/io_tool/io_tool.dart';
+import 'package:fast_gbk/fast_gbk.dart';
+
+import '../io_udpSender.dart';
+import '../io_udpType.dart';
+import '../udp_reply.dart';
+
+class UDPSendHeartCommand extends UDPSenderProtocol {
+
+ String? userName;
+ List? ipList;
+ String? tokenStr;
+
+ UDPSendHeartCommand({
+ this.userName,
+ this.ipList,
+ this.tokenStr,
+ }) : super(CommandUDPType.heart);
+
+ @override
+ List messageDetail() {
+ List data = [];
+
+ // 命令
+ data.add(4);
+
+ // 命令类型
+ data.add(1);
+
+ // 用户名 P18682150237
+ // var pStr = "P18682150237";
+ data.addAll(utf8.encode(userName!));
+ data = getFixedLengthList(data, 20 - utf8.encode(userName!).length);
+
+ // data.add(0);
+
+ // mac地址
+ // var macAddress = "02:00:00:00:00:00";
+ // data.addAll(utf8.encode(macAddress));
+ // data = getFixedLengthList(data, 6 - utf8.encode(macAddress).length);
+ data.addAll([0, 0, 0, 0, 0, 0]);
+
+ // 预留4个字节
+ data.addAll([0, 0, 0, 0]);
+
+ // ip地址
+ // var ipStr = "192.168.9.7";
+ // data.addAll(utf8.encode(ipStr!));
+ // data = getFixedLengthList(data, 12 - utf8.encode(ipStr!).length);
+ data.addAll(ipList!);
+
+ // 预留8个字节
+ data.addAll([0, 0, 0, 0, 0, 0, 0, 0]);
+
+ // token
+ var token = "token";
+ data.addAll(gbk.encode(token));
+ data = getFixedLengthList(data, 10 - gbk.encode(token).length);
+ // print("gbk.encode(token):${gbk.encode(token)}");
+
+ // tokenStr
+ // var tokenStr = "b989fa15f75c2ac02718b7c9bb64f80e";
+ data.addAll(gbk.encode(tokenStr!));
+ // print("gbk.encode(tokenStr):${gbk.encode(tokenStr!)}");
+
+ var tokenStrLength = gbk.encode(tokenStr!).length;
+ List tokenStrLengthArr = [];
+ tokenStrLengthArr.add((tokenStrLength & 0xff));
+ tokenStrLengthArr.add((tokenStrLength & 0xff00) >> 8);
+ tokenStrLengthArr.add((tokenStrLength & 0xff0000) >> 16);
+ tokenStrLengthArr.add((tokenStrLength & 0xff000000) >> 24);
+ data.setRange(37, 41, tokenStrLengthArr);
+
+ // print("UDPSendHeartData:$data");
+
+ return data;
+ }
+}
+
+class UDPSendHeartReply extends UDPReply {
+ UDPSendHeartReply.parseData(CommandUDPType commandType, List dataDetail)
+ : super.parseData(commandType, dataDetail) {
+ data = dataDetail;
+ }
+}
diff --git a/star_lock/lib/talk/udp/io_protocol/udp_mainProtocol.dart b/star_lock/lib/talk/udp/io_protocol/udp_mainProtocol.dart
new file mode 100644
index 00000000..478e4e55
--- /dev/null
+++ b/star_lock/lib/talk/udp/io_protocol/udp_mainProtocol.dart
@@ -0,0 +1,85 @@
+
+import 'dart:convert';
+
+import '../../../blue/io_tool/io_tool.dart';
+
+import '../io_udpSender.dart';
+import '../io_udpType.dart';
+import '../udp_reply.dart';
+
+class UDPMainProtocolCommand extends UDPSenderProtocol {
+
+ int? command;
+ int? commandTypeIsCalling;
+ int? subCommand;
+ String? lockID;
+ String? lockIP;
+ String? userMobile;
+ String? userMobileIP;
+ List? endData;
+
+ UDPMainProtocolCommand({
+ this.command,
+ this.commandTypeIsCalling,
+ this.subCommand,
+ this.lockID,
+ this.lockIP,
+ this.userMobile,
+ this.userMobileIP,
+ this.endData,
+ }) : super(CommandUDPType.heart);
+
+ @override
+ List messageDetail() {
+ List data = [];
+
+ // 命令
+ data.add(command!);
+
+ // 命令类型
+ data.add(commandTypeIsCalling!);
+
+ // 子命令
+ data.add(subCommand!);
+
+ // lockID
+ List lockIDData = utf8.encode(lockID!);
+ data.addAll(lockIDData);
+ // topBytes = getFixedLengthList(lockIDData, 20 - lockIDData.length);
+ for (int i = 0; i < 20 - lockIDData.length; i++) {
+ data.add(0);
+ }
+
+ // lockIP
+ var lockIPList = lockIP!.split(".");
+ lockIPList.forEach((element) {
+ data.add(int.parse(element));
+ });
+
+ // userMobile
+ List userMobileData = utf8.encode(userMobile!);
+ data.addAll(userMobileData);
+ // topBytes = getFixedLengthList(topBytes, 20 - userMobileData.length);
+ for (int i = 0; i < 20 - userMobileData.length; i++) {
+ data.add(0);
+ }
+
+ // userMobileIP
+ var userMobileIPList = lockIP!.split(".");
+ userMobileIPList.forEach((element) {
+ data.add(int.parse(element));
+ });
+
+ data.addAll(endData!);
+
+ // print("datadatadata:$data");
+ return data;
+ }
+}
+
+class UDPMainProtocolReply extends UDPReply {
+ UDPMainProtocolReply.parseData(CommandUDPType commandType, List dataDetail)
+ : super.parseData(commandType, dataDetail) {
+ data = dataDetail;
+ }
+}
diff --git a/star_lock/lib/talk/udp/io_protocol/udp_openDoor.dart b/star_lock/lib/talk/udp/io_protocol/udp_openDoor.dart
new file mode 100644
index 00000000..139597f9
--- /dev/null
+++ b/star_lock/lib/talk/udp/io_protocol/udp_openDoor.dart
@@ -0,0 +1,2 @@
+
+
diff --git a/star_lock/lib/talk/udp/io_udpSender.dart b/star_lock/lib/talk/udp/io_udpSender.dart
new file mode 100644
index 00000000..6215271a
--- /dev/null
+++ b/star_lock/lib/talk/udp/io_udpSender.dart
@@ -0,0 +1,40 @@
+
+import 'dart:convert';
+
+import '../../blue/io_tool/io_tool.dart';
+import 'io_udpType.dart';
+
+abstract class IOData {
+ List messageDetail();
+}
+
+abstract class UDPSenderProtocol extends IOData {
+ // var uint8View1 = Uint8List(300);
+
+ CommandUDPType? commandType; //指令类型
+ // final List header = [0XEF, 0X01, 0XEE, 0X02]; //帧头 固定取值 0XEF01EE02,长度 4 字节
+ // final int ask = 0X01 ; // 包类型:0X01 表示请求包,0X11 表示应答包,长度 1 字节
+
+ List? commandData = []; //数据块
+
+ UDPSenderProtocol(this.commandType) {
+
+ }
+
+ //TODO:拼装数据
+ List packageData() {
+ commandData = messageDetail();
+ List commandList = [];
+
+ var cID = "XXXCID";
+ commandList.addAll(utf8.encode(cID));
+ commandList = getFixedLengthList(commandList, 6 - utf8.encode(cID).length);
+
+ // 数据块
+ commandList.addAll(commandData!); //数据块
+ // print("commandList:$commandList");
+ return commandList;
+ }
+
+
+}
\ No newline at end of file
diff --git a/star_lock/lib/talk/udp/io_udpType.dart b/star_lock/lib/talk/udp/io_udpType.dart
new file mode 100644
index 00000000..af2fa485
--- /dev/null
+++ b/star_lock/lib/talk/udp/io_udpType.dart
@@ -0,0 +1,50 @@
+
+//TODO:发送指令类型
+enum CommandUDPType {
+ heart, //心跳 = 4
+ mainProtocol, // 150
+}
+
+extension ExtensionCommandType on CommandUDPType {
+
+ static CommandUDPType getCommandType(int value){
+ CommandUDPType type = CommandUDPType.heart;
+ switch(value){
+ case 0x04:
+ {
+ type = CommandUDPType.heart;
+ }
+ break;
+ }
+ return type;
+ }
+
+ int get typeValue {
+ int type = 0x04;
+ switch(this){
+ case CommandUDPType.heart:
+ type = 0x04;
+ break;
+ case CommandUDPType.mainProtocol:
+ type = 0x96;
+ break;
+ }
+ // AppLog.log('数组组装指令类型:$name commandIndex:${IoManager
+ // ().commandIndex}');
+ return type;
+ }
+
+
+ String get typeName {
+ String t = '';
+ switch(typeValue){
+ case 0x04:
+ t = '心跳';
+ break;
+ case 0x96:
+ t = 'UDP主协议';
+ break;
+ }
+ return t;
+ }
+}
\ No newline at end of file
diff --git a/star_lock/lib/talk/udp/udp_help.dart b/star_lock/lib/talk/udp/udp_help.dart
new file mode 100644
index 00000000..63259acb
--- /dev/null
+++ b/star_lock/lib/talk/udp/udp_help.dart
@@ -0,0 +1,158 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:get/get.dart';
+
+import '../../network/api_repository.dart';
+import '../../tools/storage.dart';
+import 'udp_manage.dart';
+import 'udp_senderManage.dart';
+
+class UdpHelp {
+ Timer? timer;
+
+ openUDP() async {
+ // 从服务器获取ip跟端口
+ var entity = await ApiRepository.to.getWifiLockServiceIpAndPort();
+ if (entity.errorCode! == 0) {
+ UDPManage();
+ // UDPManage().initUdp();
+ UDPManage().port = int.parse(entity.data!.serviceList![0].port!);
+
+ var serversList = [];
+ for (int i = 0; i < entity.data!.serviceList!.length; i++) {
+ var item = entity.data!.serviceList![i];
+ if (item.serviceIp!.contains("192")) {
+ UDPManage().host = entity.data!.serviceList![0].serviceIp!;
+ var itemList = item.serviceIp!.split(".");
+ for (var element in itemList) {
+ serversList.add(int.parse(element));
+ }
+ } else {
+ List addresses =
+ await InternetAddress.lookup(item.serviceIp!);
+ UDPManage().host = addresses.first.address;
+ var itemList = addresses.first.address.split(".");
+ for (var element in itemList) {
+ serversList.add(int.parse(element));
+ }
+ print(
+ 'Resolved google.com to address: ${addresses.first.address} serversList:$serversList');
+ }
+ }
+
+ var mobile = await Storage.getMobile();
+ timer = Timer.periodic(1.seconds, (timer) async {
+ UDPSenderManage.sendHeart(
+ userName: mobile,
+ ipList: serversList,
+ tokenStr: "b989fa15f75c2ac02718b7c9bb64f80e",
+ );
+ });
+ }
+ }
+
+ // Future getWifiLockServiceIpAndPort() async {
+ // var entity = await ApiRepository.to.getWifiLockServiceIpAndPort();
+ // if(entity.errorCode! == 0){
+ //
+ // }
+ // }
+
+ dispose() {
+ timer!.cancel();
+ }
+}
+
+class UdpData {
+ bool isUsed = false;
+}
+
+// class UdpSendThread {
+// List udpDatas = [];
+// bool udpSendTag = true;
+// int udpSendTime = 0;
+//
+// UdpSendThread() {
+// for (int i = 0; i < BUFNUM; i++) {
+// udpDatas.add(UdpData());
+// }
+// udpSend();
+// }
+//
+// void udpSend() async {
+// while (udpSendTag) {
+// while (udpSendTime <= 0) {
+// await Future.delayed(Duration(seconds: 1));
+// }
+// bool hasDataSend = true;
+// while (hasDataSend) {
+// hasDataSend = false;
+// for (int i = 0; i < BUFNUM; i++) {
+// UdpData udpList = udpDatas[i];
+// if (udpList.isUsed) {
+// // Do something
+// } else {
+// // Do something else
+// }
+// }
+// await Future.delayed(Duration(seconds: 1));
+// }
+// udpSendTime--;
+// }
+// }
+// }
+
+
+
+
+
+
+
+
+// import 'dart:async';
+//
+// class UdpSendCondition {
+// bool udpSendTag = true;
+// int udpSendTime = 0;
+// List udpDatas = [];
+// Isolate isolate;
+//
+// void start() async {
+// final receivePort = ReceivePort();
+// isolate = await Isolate.spawn(_isolateEntry, receivePort.sendPort);
+//
+// receivePort.listen((message) {
+// if (message == 'done') {
+// udpSendTime--;
+// if (udpSendTime <= 0) {
+// stop();
+// }
+// }
+// });
+// }
+//
+// void stop() {
+// if (isolate != null) {
+// isolate.kill(priority: Isolate.immediate);
+// isolate = null;
+// }
+// }
+// }
+//
+// void _isolateEntry(SendPort sendPort) async {
+// bool hasDataSend = true;
+// while (hasDataSend) {
+// hasDataSend = false;
+// for (int i = 0; i < BUFNUM; i++) {
+// var udpList = udpDatas[i];
+// if (udpList.isUsed) {
+// // Do something
+// } else {
+// // Do something else
+// }
+// }
+// await Future.delayed(Duration(seconds: 1));
+// }
+// sendPort.send('done');
+// }
\ No newline at end of file
diff --git a/star_lock/lib/talk/udp/udp_manage.dart b/star_lock/lib/talk/udp/udp_manage.dart
new file mode 100644
index 00000000..98d27c9f
--- /dev/null
+++ b/star_lock/lib/talk/udp/udp_manage.dart
@@ -0,0 +1,116 @@
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:star_lock/talk/udp/udp_reciverData.dart';
+
+import '../../blue/io_tool/io_model.dart';
+import '../../blue/io_tool/manager_event_bus.dart';
+
+class UDPManage{
+ static UDPManage? _manager;
+
+ UDPManage._init() {
+ initUdp();
+ _streamSubscription = EventBusManager().eventBus!.on().listen((EventSendModel sendModel) {
+ // print("sendModel.sendChannel:${sendModel.sendChannel}");
+ if(sendModel.sendChannel == DataChannel.udp){
+ List data = sendModel.data;
+ sendData(data);
+ }
+ });
+ }
+
+ static UDPManage _share(){
+ _manager ??= UDPManage._init();
+ return _manager!;
+ }
+ factory UDPManage() => _share();
+ UDPManage get manager => _share();
+
+ StreamSubscription? _streamSubscription;
+ RawDatagramSocket? _udpSocket;
+ // String host = '47.106.143.213';
+ // int port = 8056;
+ // String? _mIp = '';
+ String host = '';
+ int port = 0;
+ String lockId = '';// 锁id 通过锁id来判断是哪把锁发对讲过来
+
+ void initUdp() async {
+ var listAddress = InternetAddress.lookup(host!);
+ listAddress.then((list) {
+ list.forEach((element) {
+ // print('Udp ----> element.address:${element.address} element.host:${element.host}');
+ host = element.address;
+ });
+ });
+ await _initUdp();
+ }
+
+ Future _initUdp() async {
+ if(port == 0){
+ print('❌ Udp ----> _port == 0');
+ return;
+ }
+ print('Udp ----> host:$host port:$port');
+ var addressIListenFrom = InternetAddress.anyIPv4;
+ int portIListenOn = 62288;
+ RawDatagramSocket.bind(addressIListenFrom, portIListenOn).then((RawDatagramSocket socket){
+ _udpSocket = socket;
+ ///广播功能
+ _udpSocket!.broadcastEnabled = true;
+ _onReceiveData(socket);
+ });
+ }
+
+
+ void _onReceiveData(RawDatagramSocket socket) {
+ socket.listen((RawSocketEvent event) {
+ if(event == RawSocketEvent.read){
+ Datagram? dg = socket.receive();
+ try {
+ // print('Did received data on the stream (length --> ${dg!.data.length}) dg!.data:${dg!.data}');
+ // EventBusManager().eventBusFir(EventReceiveModel(data: dg?.data,sendChannel: DataChannel.udp));
+ CommandUDPReciverManager.appDataReceive(dg!.data);
+ } catch (e) {
+ print('❌ Udp ----> $e');
+ }
+ }
+ });
+ }
+
+ void sendData(List data) {
+ if(null == _udpSocket || null == data || data.isEmpty || host == ''){
+ if(null == _udpSocket ){
+ print('❌ Udp ----> null == _udpSocket');
+ initUdp();
+ }
+ return;
+ }
+ try {
+ // print("sendData:$data");
+ var result = _udpSocket?.send(data, InternetAddress(host!), port!);
+ if(result != data.length) {
+ print('❌Udp ----> send data $result ${data.length}');
+ _udpSocket = null;
+ }
+ }catch (e){
+
+ }
+ }
+
+ bool exit() {
+ if(null != _udpSocket) {
+ print('❌ Udp ----> close');
+ _udpSocket?.close();
+ }
+ return true;
+ }
+
+
+ void disposed() {
+ _streamSubscription?.cancel();
+ }
+
+}
\ No newline at end of file
diff --git a/star_lock/lib/talk/udp/udp_reciverData.dart b/star_lock/lib/talk/udp/udp_reciverData.dart
new file mode 100644
index 00000000..aafcc339
--- /dev/null
+++ b/star_lock/lib/talk/udp/udp_reciverData.dart
@@ -0,0 +1,206 @@
+import 'dart:typed_data';
+
+import 'package:get/get.dart';
+import 'package:star_lock/talk/udp/udp_manage.dart';
+
+import '../../blue/io_tool/io_tool.dart';
+import '../../tools/eventBusEventManage.dart';
+import '../../tools/toast.dart';
+import '../call/callTalk.dart';
+import 'udp_talkClass.dart';
+
+class CommandUDPReciverManager {
+
+
+ static void appDataReceive(List data) async {
+ ///解析数据
+ if (data.isEmpty) {
+ return;
+ }
+ int dataSize = data.length;
+ if (dataSize < 4) {
+ return;
+ }
+ // print("appReceiveUDPData:$data");
+
+ Uint8List data1 = Uint8List.fromList(data);
+ if (data1.length == 1) {
+ if (data[0] == 0x30 || data[0] == 0x31) {
+ print("p2p打洞");
+ }
+ }
+
+ if (data[6] == 4) {
+ if (data[7] == 2) {
+ // print("心跳包反馈 在线状态");
+
+ } else if (data[7] == 3) {
+ [Toast.show(msg: "您已在其他设备登录")];
+ }
+ } else if (data[6] == 150) {
+ // if( [Pub getApp].isBack){
+ // [_udp receiveWithTimeout:-1 tag:0];
+ // return YES;
+ // }
+
+ // 对讲命令
+ var beiCallType = data[8] & 0xff;
+ // print("被呼叫类型$beiCallType");
+ switch (beiCallType) {
+ case 1:
+ {
+ //被叫
+ // lockId
+ var lockId = data.sublist(9, 29);
+ var lockIdStr = utf8String(lockId);
+ UDPManage().lockId = lockIdStr;
+ UDPTalkClass().status = 0;
+ UDPTalkClass().beCallW(data: data);
+ }
+ break;
+ case 6:
+ {
+ //接听
+ if ((data[7] & 0x3) == 2) {
+ //被叫 接听反馈
+ print("接听反馈");
+ UDPTalkClass().status = 8;
+ // 停止声音
+ UDPTalkClass().stopLocalAudio();
+ eventBus.fire(GetUDPStatusRefreshUI(UDPTalkClass().status));
+ }
+ }
+ break;
+ case 7:
+ case 8:
+ {
+ //音视频数据
+ // print("音视频数据");
+ CallTalk cllTalk = CallTalk();
+ cllTalk.getAVData(data1, data1.length);
+ }
+ break;
+ case 9:
+ {
+ if ((data[7] & 0x3) == 1) {
+ //对方保持连接
+ //print("对方保持连接");
+ data[7] = 2;
+ } else {
+ //print("保持连接反馈");
+ }
+ }
+ break;
+ case 10:
+ {
+ //开门反馈
+ print("appReceiveUDPData:$data");
+ if ((data[7] & 0x3) == 2) {
+ print("开门成功");
+ Toast.show(msg: "开门成功");
+ } else {
+ print("开门失败");
+ }
+ }
+ break;
+ case 30:
+ {
+ // 挂断
+ if ((data[7] & 0x3) == 1) {
+ // 对方结束对讲
+ print("对方结束对讲");
+ } else {
+ //结束对讲反馈
+ print("结束对讲反馈");
+ }
+ UDPTalkClass().status = 0;
+ UDPTalkClass().isBeCall = false;
+ Get.back();
+ UDPTalkClass().stopLocalAudio();
+ eventBus.fire(GetUDPStatusRefreshUI(UDPTalkClass().status));
+ }
+ break;
+ case 140:
+ {
+ // p2p测试
+ }
+ break;
+ case 141:
+ {
+ // p2p测试
+ }
+ break;
+ case 142:
+ {
+ // p2p测试
+ }
+ break;
+ case 143:
+ {
+ //p2p测试 NSAsk
+ print("p2pNSAskNSAsk");
+ }
+ break;
+ default:
+ break;
+ }
+ } else if (data[6] == 152) {
+ // 监视命令
+ switch (data[8] & 0xff) {
+ case 2:
+ {
+ //被叫
+ print("对方忙");
+ }
+ break;
+ case 4:
+ {
+ //监视成功
+ print("监视成功");
+ }
+ break;
+ case 7:
+ case 8:
+ {
+ //音视频数据
+ //print("音视频数据");
+ }
+ break;
+ case 9:
+ {
+ //保持连接
+ if ((data[7] & 0x3) == 1) {
+ //对方保持连接
+ //print("对方保持连接");
+ data[7] = 2;
+ } else {
+ //print("保持连接反馈");
+ }
+ }
+ break;
+ case 10:
+ {
+ //开门反馈
+ if ((data[7] & 0x3) == 2) {
+ print("开门成功");
+ } else {}
+ }
+ break;
+ case 30:
+ {
+ // 监视结束
+ if ((data[7] & 0x3) == 1) {
+ // 对方结束监视
+ print("对方结束监视");
+ } else {
+ //结束对讲反馈
+ print("结束监视反馈");
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/star_lock/lib/talk/udp/udp_reply.dart b/star_lock/lib/talk/udp/udp_reply.dart
new file mode 100644
index 00000000..152e2a88
--- /dev/null
+++ b/star_lock/lib/talk/udp/udp_reply.dart
@@ -0,0 +1,13 @@
+
+import 'io_udpType.dart';
+
+abstract class UDPReply{
+
+ CommandUDPType? commandType;
+
+ //command key flag
+ int status = 0;
+ List data = [];
+ UDPReply.parseData(this.commandType, List dataDetail);
+
+}
\ No newline at end of file
diff --git a/star_lock/lib/talk/udp/udp_senderData.dart b/star_lock/lib/talk/udp/udp_senderData.dart
new file mode 100644
index 00000000..1bf7c90b
--- /dev/null
+++ b/star_lock/lib/talk/udp/udp_senderData.dart
@@ -0,0 +1,46 @@
+
+import '../../app_settings/app_settings.dart';
+import '../../blue/io_tool/io_model.dart';
+import '../../blue/io_tool/manager_event_bus.dart';
+import 'io_udpSender.dart';
+
+typedef CommandUDPSendCallBack = void Function(ErrorType errorType);
+
+class CommandUDPSenderManager {
+
+ static final CommandUDPSenderManager _manager = CommandUDPSenderManager._init();
+ factory CommandUDPSenderManager()=>_manager;
+ static CommandUDPSenderManager getInstance()=>_manager;
+ CommandUDPSenderManager._init(){
+ init();
+ }
+
+ init(){
+
+ }
+
+ //TODO:发送常规数据
+ Future managerSendData ({required UDPSenderProtocol command, CommandUDPSendCallBack? callBack}) async {
+ if (callBack != null) {
+ // if (!BluetoothManager().connected) {
+ print('❌ 蓝牙断开了');
+ if (callBack != null) {
+ print('managerSendData ❌ callBack');
+ // EasyLoading.dismiss();
+ callBack(ErrorType.notConnected);
+ }
+ return;
+ }
+
+ List value = command.packageData();
+ // print("sendData:${value}");
+ _sendNormalData(value);
+ }
+
+ void _sendNormalData(List data) async {
+ if (data.isNotEmpty) {
+ EventBusManager().eventBusFir(EventSendModel(data: data, sendChannel: DataChannel.udp));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/star_lock/lib/talk/udp/udp_senderManage.dart b/star_lock/lib/talk/udp/udp_senderManage.dart
new file mode 100644
index 00000000..5899a888
--- /dev/null
+++ b/star_lock/lib/talk/udp/udp_senderManage.dart
@@ -0,0 +1,41 @@
+
+
+import 'io_protocol/udp_heart.dart';
+import 'io_protocol/udp_mainProtocol.dart';
+import 'udp_senderData.dart';
+
+class UDPSenderManage {
+
+ //todo:UDP心跳
+ static void sendHeart({String? userName, List? ipList, String? tokenStr, CommandUDPSendCallBack? callBack}) {
+ CommandUDPSenderManager().managerSendData(command: UDPSendHeartCommand(
+ userName: userName,
+ ipList: ipList,
+ tokenStr: tokenStr,
+ ), callBack: callBack);
+ }
+
+ //todo:主通讯协议
+ static void sendMainProtocol({
+ int? command,
+ int? commandTypeIsCalling,
+ int? subCommand,
+ String? lockID,
+ String? lockIP,
+ String? userMobile,
+ String? userMobileIP,
+ List? endData,
+ CommandUDPSendCallBack? callBack}) {
+ CommandUDPSenderManager().managerSendData(command: UDPMainProtocolCommand(
+ command: command,
+ commandTypeIsCalling: commandTypeIsCalling,
+ subCommand: subCommand,
+ lockID: lockID,
+ lockIP: lockIP,
+ userMobile: userMobile,
+ userMobileIP: userMobileIP,
+ endData: endData,
+ ), callBack: callBack);
+ }
+
+}
\ No newline at end of file
diff --git a/star_lock/lib/talk/udp/udp_talkClass.dart b/star_lock/lib/talk/udp/udp_talkClass.dart
new file mode 100644
index 00000000..7cb332e4
--- /dev/null
+++ b/star_lock/lib/talk/udp/udp_talkClass.dart
@@ -0,0 +1,152 @@
+import 'dart:async';
+
+import 'package:audioplayers/audioplayers.dart';
+import 'package:fast_gbk/fast_gbk.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:star_lock/tools/eventBusEventManage.dart';
+
+import '../../appRouters.dart';
+import '../../main/lockDetail/monitoring/monitoring/lockMonitoring_page.dart';
+import '../../tools/storage.dart';
+import 'udp_manage.dart';
+
+class UDPTalkClass {
+ static UDPTalkClass? _manager;
+ static UDPTalkClass _share() {
+ _manager ??= UDPTalkClass._init();
+ return _manager!;
+ }
+
+ factory UDPTalkClass() => _share();
+ UDPTalkClass get manager => _share();
+
+ UDPTalkClass._init();
+
+ // var status = 0; //0:初始状态 1:等待监视 2: 3:监视中 4:呼叫成功 5:主角通话中 6:被叫通话 8:被叫通话中
+ int status = 0;
+
+ var remoteEquid; // 手机号
+ late Timer timer;
+ // 该字段是为了判断是否跳转到接听界面 挂断或者退出接听界面要记得变更状态
+ var isBeCall = false;
+ final audioPlayer = AudioPlayer();
+
+ beCallW({List? data, String? ip, int? port}) async {
+ print("beCall status:$status");
+ // if (await isCallMe(data)) {
+ // return;
+ // }
+
+ if (status == 0) {
+ // 空闲
+ // 响铃
+ // [[Pub getApp] ring];
+ remoteEquid = getEquidFrombb(data, 9);
+ status = 6;
+
+ // 呼叫应答,呼叫成功
+ data![7] = 1;
+ data[8] = 4;
+ UDPManage().sendData(data);
+
+ // 门锁发送视频
+ data[7] = 1;
+ data[8] = 5;
+ UDPManage().sendData(data);
+
+ // Get.to(MaterialPageRoute(
+ // builder: (context) {
+ // return const LockMonitoringPage();
+ // },
+ // fullscreenDialog: true
+ // ));
+
+ if (isBeCall == false) {
+ isBeCall = true;
+ // 保持连接
+ timer = Timer.periodic(1.seconds, (timer) {
+ data[7] = 1;
+ data[8] = 9;
+ UDPManage().sendData(data);
+ });
+
+ // _getFirstFrameGoPushAction();
+ // Future.delayed(const Duration(seconds: 1), () {
+ // 在这里写要延迟执行的代码
+ Get.toNamed(Routers.lockMonitoringPage, arguments: {"lockId": "111"});
+ // });
+ }
+
+ playLocalAudio();
+ } else {
+ // 忙
+ }
+ }
+
+ void _getFirstFrameGoPushAction() {
+ // 蓝牙协议通知传输跟蓝牙之外的数据传输类不一样 eventBus
+ StreamSubscription _GetFirstFrameGoPushEvent =
+ eventBus.on().listen((event) {
+ if (event.isFirstFrame == true) {
+ Future.delayed(const Duration(seconds: 1), () {
+ // 在这里写要延迟执行的代码
+ Get.toNamed(Routers.lockMonitoringPage, arguments: {"lockId": "111"});
+ });
+ }
+ });
+ }
+
+ // 判断是否是call的本人
+ Future isCallMe(List? data) async {
+ final loginData = await Storage.getLoginData();
+ print("getEquidFrombb(data, 1000):${getEquidFrombb(data, 12)}");
+ if (loginData!.mobile == getEquidFrombb(data, 12)) {
+ return true;
+ }
+ return false;
+ }
+
+ String getEquidFrombb(List? bb, int pos) {
+ var equid = "";
+ int equlen = 8;
+
+ if (bb![pos] == 77) {
+ //M
+ equlen = 8;
+ } else if (bb[pos] == 87) {
+ //W
+ equlen = 5;
+ } else if (bb[pos] == 72) {
+ //H
+ equlen = 12;
+ } else if (bb[pos] == 83) {
+ //S
+ equlen = 12;
+ } else if (bb[pos] == 0x50) {
+ //P
+ equlen = 12;
+ } else if (bb[pos] == 0x54) {
+ //T
+ equlen = 16;
+ } else {
+ equlen = 12;
+ }
+
+ List tempbb = bb.sublist(pos, pos + equlen);
+ equid = gbk.decode(tempbb);
+ return equid;
+ }
+
+ //播放本地音频
+ void playLocalAudio() async {
+ audioPlayer.setReleaseMode(ReleaseMode.loop);
+ await audioPlayer.play(AssetSource('ring1.mp3'));
+ }
+
+ // 停止播放本地音频
+ void stopLocalAudio() async {
+ audioPlayer.setReleaseMode(ReleaseMode.loop);
+ await audioPlayer.stop();
+ }
+}
diff --git a/star_lock/lib/tools/eventBusEventManage.dart b/star_lock/lib/tools/eventBusEventManage.dart
index 8f1d6546..5bb02c60 100644
--- a/star_lock/lib/tools/eventBusEventManage.dart
+++ b/star_lock/lib/tools/eventBusEventManage.dart
@@ -6,52 +6,70 @@ import '../main/lockDetail/lcokSet/lockSet/lockSetInfo_entity.dart';
EventBus eventBus = EventBus();
/// 刷新锁列表数据
-class RefreshLockListInfoDataEvent{
+class RefreshLockListInfoDataEvent {
RefreshLockListInfoDataEvent();
}
/// 刷新考勤员工列表
-class RefreshCheckInStaffListDataEvent{
+class RefreshCheckInStaffListDataEvent {
RefreshCheckInStaffListDataEvent();
}
/// 传递当前锁信息
-class PassCurrentLockInformationEvent{
+class PassCurrentLockInformationEvent {
LockSetInfoData lockSetInfoData;
PassCurrentLockInformationEvent(this.lockSetInfoData);
}
/// 卡、密码、指纹修改之后刷新列表
-class OtherTypeRefreshListEvent{
+class OtherTypeRefreshListEvent {
OtherTypeRefreshListEvent();
}
/// 考情添加员工卡、指纹之后回调卡、指纹number
-class ChickInAddStaffCardAndFingerprintBlockNumberEvent{
+class ChickInAddStaffCardAndFingerprintBlockNumberEvent {
String number;
ChickInAddStaffCardAndFingerprintBlockNumberEvent(this.number);
}
/// Uuid删除当前锁时 有可能锁被初始化了 但后台没被初始化 当打开APP的时候扫描如果未被连接就传uuid过去 判断直接删除数据
-class ScanAllDeviceFindCurrentDeviceConnectedEvent{
+class ScanAllDeviceFindCurrentDeviceConnectedEvent {
String uuid;
ScanAllDeviceFindCurrentDeviceConnectedEvent(this.uuid);
}
/// 多语言 切换之后传值到上一级界面
-class ChangeLanguageBlockLastLanguageEvent{
+class ChangeLanguageBlockLastLanguageEvent {
String languageTitle;
ChangeLanguageBlockLastLanguageEvent(this.languageTitle);
}
/// 锁分组添加或者删除锁之后刷新首页数据
-class LockGroupEditGroupLockRefreshEvent{
+class LockGroupEditGroupLockRefreshEvent {
LockGroupEditGroupLockRefreshEvent();
}
/// 锁设置里面开启关闭刷新锁详情
-class LockSetChangeSetRefreshLockDetailWithType{
- int type;// 0 考勤 1开锁时是否需联网
+class LockSetChangeSetRefreshLockDetailWithType {
+ int type; // 0 考勤 1开锁时是否需联网
int setResult;
LockSetChangeSetRefreshLockDetailWithType(this.type, this.setResult);
-}
\ No newline at end of file
+}
+
+/// 获取到视频流数据然后刷新界面
+class GetTVDataRefreshUI {
+ List tvList;
+ GetTVDataRefreshUI(this.tvList);
+}
+
+/// 获取到UDP接收状态然后刷新界面
+class GetUDPStatusRefreshUI {
+ int udpStatus;
+ GetUDPStatusRefreshUI(this.udpStatus);
+}
+
+/// 获取到第一帧图片数据后跳转
+class GetFirstFrameGoPush {
+ bool isFirstFrame;
+ GetFirstFrameGoPush(this.isFirstFrame);
+}
diff --git a/star_lock/lib/tools/showTFView.dart b/star_lock/lib/tools/showTFView.dart
index c5827ae9..e9dc6ea2 100644
--- a/star_lock/lib/tools/showTFView.dart
+++ b/star_lock/lib/tools/showTFView.dart
@@ -1,5 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
@@ -9,6 +10,7 @@ class ShowTFView extends StatelessWidget {
String? title;
String? tipTitle;
TextEditingController? controller;
+ List? inputFormatters;
Function()? sureClick;
Function()? cancelClick;
@@ -17,6 +19,7 @@ class ShowTFView extends StatelessWidget {
this.title,
this.tipTitle,
this.controller,
+ this.inputFormatters,
this.sureClick,
this.cancelClick})
: super(key: key);
@@ -44,6 +47,7 @@ class ShowTFView extends StatelessWidget {
maxLines: 1,
controller: controller,
autofocus: false,
+ inputFormatters: inputFormatters,
decoration: InputDecoration(
contentPadding:
const EdgeInsets.only(left: 5, top: -8, bottom: 6),
diff --git a/star_lock/lib/tools/storage.dart b/star_lock/lib/tools/storage.dart
index b1253c7e..6de0fe52 100644
--- a/star_lock/lib/tools/storage.dart
+++ b/star_lock/lib/tools/storage.dart
@@ -136,13 +136,33 @@ class Storage {
}
static Future getUid() async {
- String? userId = '';
+ String? uid = '';
final data = await Storage.getString('userLoginData');
if (data != null && data.isNotEmpty) {
- userId = LoginData.fromJson(jsonDecode(data)).uid.toString();
+ uid = LoginData.fromJson(jsonDecode(data)).uid.toString();
}
- print("pubUserId:$userId");
- return userId;
+ // print("pubUid:$uid");
+ return uid;
+ }
+
+ static Future getMobile() async {
+ String? mobile = '';
+ final data = await Storage.getString('userLoginData');
+ if (data != null && data.isNotEmpty) {
+ mobile = LoginData.fromJson(jsonDecode(data)).mobile.toString();
+ }
+ // print("mobile:$mobile");
+ return mobile;
+ }
+
+ static Future getLoginData() async {
+ LoginData? loginData;
+ final data = await Storage.getString('userLoginData');
+ if (data != null && data.isNotEmpty) {
+ loginData = LoginData.fromJson(jsonDecode(data));
+ }
+ print("loginData:$loginData");
+ return loginData;
}
diff --git a/star_lock/linux/flutter/generated_plugin_registrant.cc b/star_lock/linux/flutter/generated_plugin_registrant.cc
index 7111d06f..05d9a9b9 100644
--- a/star_lock/linux/flutter/generated_plugin_registrant.cc
+++ b/star_lock/linux/flutter/generated_plugin_registrant.cc
@@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include
+#include
#include
#include
@@ -14,6 +15,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) aj_captcha_flutter_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AjCaptchaFlutterPlugin");
aj_captcha_flutter_plugin_register_with_registrar(aj_captcha_flutter_registrar);
+ g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
+ audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
diff --git a/star_lock/linux/flutter/generated_plugins.cmake b/star_lock/linux/flutter/generated_plugins.cmake
index 7fbf0d0d..b6e600e9 100644
--- a/star_lock/linux/flutter/generated_plugins.cmake
+++ b/star_lock/linux/flutter/generated_plugins.cmake
@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
aj_captcha_flutter
+ audioplayers_linux
file_selector_linux
url_launcher_linux
)
diff --git a/star_lock/macos/Flutter/GeneratedPluginRegistrant.swift b/star_lock/macos/Flutter/GeneratedPluginRegistrant.swift
index 127e12ed..64299ab3 100644
--- a/star_lock/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/star_lock/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -6,8 +6,12 @@ import FlutterMacOS
import Foundation
import aj_captcha_flutter
+import audio_session
+import audioplayers_darwin
import device_info_plus
import file_selector_macos
+import flutter_pcm_sound
+import just_audio
import network_info_plus
import package_info_plus
import path_provider_foundation
@@ -17,8 +21,12 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AjCaptchaFlutterPlugin.register(with: registry.registrar(forPlugin: "AjCaptchaFlutterPlugin"))
+ AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
+ AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
+ FlutterPcmSoundPlugin.register(with: registry.registrar(forPlugin: "FlutterPcmSoundPlugin"))
+ JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
diff --git a/star_lock/pubspec.yaml b/star_lock/pubspec.yaml
index 83d3537a..bba33406 100644
--- a/star_lock/pubspec.yaml
+++ b/star_lock/pubspec.yaml
@@ -117,13 +117,26 @@ dependencies:
video_player: ^2.7.2
#控制横竖屏控件
auto_orientation: ^2.3.1
+ audioplayers: ^5.2.1
+ g711_flutter: ^2.0.0
+ ffi: ^2.1.0
+ flutter_mjpeg: ^2.0.4
+ image_gallery_saver: ^2.0.3
+ convert: ^3.1.1
+ just_audio: ^0.9.36
+ flutter_sound: ^9.2.13
+# ffmpeg_kit_flutter: 5.1.0-LTS
+ fast_gbk: ^1.0.0
+ flutter_pcm_sound: ^1.1.0
+# flutter_audio_capture: <1.1.5
+ flutter_voice_processor: ^1.1.0
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
- # encourage good coding practices. The lint set provided by the package is
+ # encourage good coding practices. The lint fset provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
@@ -148,6 +161,7 @@ flutter:
- images/lan/
- images/mine/
- images/lockType/
+ - assets/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
diff --git a/star_lock/windows/flutter/generated_plugin_registrant.cc b/star_lock/windows/flutter/generated_plugin_registrant.cc
index e2cbdfa4..c3bf33a2 100644
--- a/star_lock/windows/flutter/generated_plugin_registrant.cc
+++ b/star_lock/windows/flutter/generated_plugin_registrant.cc
@@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include
+#include
#include
#include
#include
@@ -14,6 +15,8 @@
void RegisterPlugins(flutter::PluginRegistry* registry) {
AjCaptchaFlutterPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AjCaptchaFlutterPluginCApi"));
+ AudioplayersWindowsPluginRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
diff --git a/star_lock/windows/flutter/generated_plugins.cmake b/star_lock/windows/flutter/generated_plugins.cmake
index fda93fcf..f7d39f56 100644
--- a/star_lock/windows/flutter/generated_plugins.cmake
+++ b/star_lock/windows/flutter/generated_plugins.cmake
@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
aj_captcha_flutter
+ audioplayers_windows
file_selector_windows
permission_handler_windows
url_launcher_windows