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