From 377d65b3dee9a4d67663222e7fe3d53dbde3266b Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 1 Sep 2014 01:14:15 +0400 Subject: [PATCH 01/72] Fix internal fields visibility; Introduce <> operator; Allow concurrent Inventory modifications --- .../onepf/oms/appstore/googleUtils/Inventory.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java b/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java index 10af9fc8..58d7de7d 100644 --- a/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java +++ b/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java @@ -17,17 +17,17 @@ package org.onepf.oms.appstore.googleUtils; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Represents a block of information about in-app items. * An Inventory is returned by such methods as {@link IabHelper#queryInventory}. */ public class Inventory { - public Map mSkuMap = new HashMap(); - public Map mPurchaseMap = new HashMap(); + private final Map mSkuMap = new ConcurrentHashMap<>(); + private final Map mPurchaseMap = new ConcurrentHashMap<>(); public Inventory() { } @@ -76,14 +76,14 @@ public void erasePurchase(String sku) { * Returns a list of all owned product IDs. */ public List getAllOwnedSkus() { - return new ArrayList(mPurchaseMap.keySet()); + return new ArrayList<>(mPurchaseMap.keySet()); } /** * Returns a list of all owned product IDs of a given type */ public List getAllOwnedSkus(String itemType) { - List result = new ArrayList(); + List result = new ArrayList<>(); for (Purchase p : mPurchaseMap.values()) { if (p.getItemType().equals(itemType)) result.add(p.getSku()); } @@ -94,7 +94,7 @@ public List getAllOwnedSkus(String itemType) { * Returns a list of all purchases. */ public List getAllPurchases() { - return new ArrayList(mPurchaseMap.values()); + return new ArrayList<>(mPurchaseMap.values()); } public void addSkuDetails(SkuDetails d) { From 17fb4e467fa1ea5ee7f231ae1bc3c13e08e93526 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 1 Sep 2014 04:07:55 +0400 Subject: [PATCH 02/72] Update Amazon billing --- library/libs/in-app-purchasing-1.0.3.jar | Bin 76680 -> 0 bytes library/libs/in-app-purchasing-2.0.0.jar | Bin 0 -> 99716 bytes .../oms/AppstoreInAppBillingService.java | 12 +- .../onepf/oms/appstore/AmazonAppstore.java | 15 +- .../AmazonAppstoreBillingService.java | 429 ++++++++++-------- .../org/onepf/oms/appstore/SamsungApps.java | 2 +- .../oms/appstore/googleUtils/Inventory.java | 10 +- .../life_openiab/src/main/AndroidManifest.xml | 8 +- .../trivialdrive/src/main/AndroidManifest.xml | 7 +- 9 files changed, 258 insertions(+), 225 deletions(-) delete mode 100644 library/libs/in-app-purchasing-1.0.3.jar create mode 100644 library/libs/in-app-purchasing-2.0.0.jar diff --git a/library/libs/in-app-purchasing-1.0.3.jar b/library/libs/in-app-purchasing-1.0.3.jar deleted file mode 100644 index 9660b21ffd59e41d6bb9ef90bff5578fb7584fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76680 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%0A#`R;&y-H{!eh_`PJQi;ZWsPuGIyVf*MrsWUx#>2 zrXkD$mJE}0NR>+TYs>uQXqaq*?X94{@dG&EKH4DrkTLx&8qf=BQ4@-YKA-yyQ?}#_ zHoiW|4KdlSMO@S&1)rZtWtzMIE~y^HHI9)-r5W8IdJF@))K|gqfgGWz571u-QK|U5 z4aCy@LjJ{ZLjFaL37JaY71Ao++(iHUZJkcl=?;!*99Akjp$J;b~qgoIHE zEMEt1BB!J}rNcLe(6HTml%3 ze-u<`Q&1iDu9z^6*PKcvomZMQluivjL6o&T04gp@&REvu98NPcju$qMWtbav2@y-{ ztSg9`ewgn1d!$)%TE*@T3k0P6Ic~)I51vcP+|~S#*&$(DYwOR#r82aj)mX4;UjB!($`F;#ch<$kxB!G6_|cB5pP?UoRx zTBE>EdQvZd|EPqPRhbLw9mI}*FfrtIXYbFqLtex2C=t)6B9&7#6?UH_p7StwbbROY z0_;5_rlL6~^gnHEH}Lh|2cvu`$I9^)|$W#uLSfCcMK34;%V*NJh>8tb#BzDzDk9kD~c(G zV@8vAyQB$+`kY>+5}=A^kdpva!=I&$JMKDi?9?AO@GYiXkhq zyCJ-R`;!4*ssM8$T^eDi6>DL6UZemROsQ8?2We})CDN+KuwK}YQz~;7pFK9BNk(>5J;E$yC%`DJvQ&K`9O9rQ$XFphIIuyh#nc>WuT(v6 zML~6h#KJ1!te#jA@I|AHJjRBDdssW^xsq3MRkI6-YMEqe(`vMVe~# z{@Tjq@JBo~$nP8AJdJYfV#1Q)to6bh#VO=MY2BI%&kB$-qGNVaNwBaZ!ifg9Uz)^N z4TG)NY=V$XAmkmXtoXL5I+^XdYSC`gNtiV?!Bb2&(Y}L8jP%km zTbDFuc}L`)V$reZ7NAWxW_T^ErF%FVa@0c*)bnD+CihNWz^Did1>J%X^AfGKI5ujZ z+vVdHHXhg2TQy_S#(>gTqs1`KM|Q|Jy!w@iX*+55&N+*k#~_Q{NY)k^>K-It^g~fs zmtAZ;w~DyVA&F(YcLk8lP#-5FZ%Di$Z}eoOB9?l;xe?qCA3~~=dQf)2jwn^O1GkA8 zl&xTs_@`mfnzoxB+@b+%QP()1Yx*}xS@EtBR7WGPi9q~13H{s(nE^^@%9XFiEXA4e z7lLqKa9Pfy5|>RH!P0nE0<*waGj=#TyIHF^NWUvdE8&5ss6(8gs;Bn1p5TQCpWum6 z7fTPcUt~^$;!9Xj=X6A~^Ex5YnIege6|MF_RUV==!dQj&duX6y(bix_O5Fo(HG`|= znmL~zxjfk$FV%p$Xc$PqE5d3qZMg&%o;L|eu%=E?xrm*K$%T6J#7)Dp$w#_KSr0DYsFN7wjLE^}YnS<)r zO~bBd){+@pKPEV$FPNM3SMOM(n`(vzcRdrEmWbCc;HH>4dj>D%iCG%yu{VD;VlFvB ze}>T{E3pu?6ro&U(SXdKR*tOru830%>kPwlpj2s?YL4Zwn$FP0rNbbN%c+{7FzOjB z6=;CjibP9>#e`4Bkru!J6n5Gk0BxbU^b$?O4-02R1us*kZ-8+JK8h5>GUP!T5FL|L zgj1MD$;CTf!kX0KF>@rK8w7!2_!FJqs|=>d;JPhxiRsz0yGd*Ew$I+jyr(r>eE1}V zd$6kI``&Ya+ZdJBU44tUHy#1!EWjI!jKEXl1Sl8aznh`bf;ghr1oUxw{3{> zKH*tfI6GH@r?yVNL~A(Suvr*4Jpl1CKM+Bq?LgJ^52qPuNk@;K0uOetOs z>x@-+72OPWxdp4@oZ4ub^|$o2un9Kj!H=h^c2rEM$j0QDlah5)8QvRipq4upI9+bFAj*|3jdy;8 z{1ljUuc2VwF>>f-_uQ-RqiamUtiS4nN1X5|#GY6zKgA_!fsl2!w$ORIJ^Ti0mn0#@ zd48tCwb$7rIHjNjk*3HUrRZC~8Bj?CuoI=uRWCabLhH5cHjd6S*>;A`Gx|0mMkkuO z%Z^UBImRYrrab`6tqDdCj_17B(*K*bI@so3Z+^E1MkUx}OEq|Tn;qUW8wQ)73`QkL zqjoby^L8(e-&5-~-}W1w7tpf{#wP6VuNXdb*LT{_zq;w|b})duhJ#J@yL8WcX_py! zZl)2g)w16@=6>zkLv*%Tb*Ani#0uS?-MF%MKoeJq(Tn2U?yk*wWvbs%mSXeBW*hEX z+i;6@9;O&7Z>)PnfkuU4{^S&UKO7ptFA+WCmPiP#Y_iwU7enHe{5>R9mAk}+jh)*Q zXp3uVWu7%0l2PBM8cW}|G(?K;9HJ)&k+p+0X_x#2Fg=GuD^^V{>a5wVmdEx%>A=EE z<2k2?$Ix+sv9=Mx>to|kDge9j$bksCf{;zFLQ5bN+R@A~NJOR(N4LT#YR&6|9 z(Vm{gy?P3l0Q4qLoDOSpa|txo*cn9|==`P|<2Z7G$dij>)xJ{KY47Umtb00BEMj%g zCc8H4$zFTM%I(9bnPg+bAH>k9Zi4MpgR7Ut;FXoiw^t?f(~<-wA|7GPmu1s2&rHyiKaymRn+vpkzAD<8tt;GTeafv$Ip<4Q;SVd2$O4_b+rZ`oXw@8arQ-{Ks#*slp} zIb^2O*3>e?Ew+S5Y9aOq+#a*wFNkCN zZ@*|swo^+wNUQxVt)Q&>k3g*h7AKAyJn{2str6s{nKNg;u-87GD$R2)uO6D0+uY+7 zSK2pxDdiHDXOY7LGgQ;e{;gk(9z=>)-P~{ zbPJZZ{sR+RM86%i*87{;$$oXG4U=4;Ezcy^NYk~(s>nhi2M6gi#0)^0bUGB&BLQM$ zB`|xJ;Kebpe_2xec28>{WI2L1`r|JJ%0If3z5~CB`Fv*K8bAApMgIp{=CdvRzY-z; z_bC2fl+C}V@(Yzc|Cq}Euy)thLeQi}02>HUc>x;jgV01QARt6%6p+5}D%4pEbgA03 zmb%Y){_-1$Dh)(V=L`SPW)kDM{d@alSL#<>Xe4Mj}at&r+#*X*C;b zwFUsF5*wr>ba(Ns)9Oj+Qxgv&5^3$05`OKAf$$AXS3sxWq?xE&SpMqes$UE)<#y9U zs+3$SL{4qdxx_P5`y2qCR|;{sPw0H74_~$nnXJ#szvs@3Ai-KF&Bh(5gUD!aAA5@E z?FI?yP9I0NzA#oQ3CLo;p6(|e&-mpFxKzsC!)xo>2ff^Tt8CD zrg6w?&i@)wqgWHcxfAygQoagN0JT;d@BKWj7+f7rN@IC5-VZ%XrdlHwCBrksqcc$` z%gpqd#C8;x+$o4$cMcvyEU(*GUBl2AKrAh5q*gpt$`7!>cwNZ3Sj?u*ER}V+Q8dEx zD6A-V#Xp5NT~$*WbNK$7eKAA(wmoJh2cuMlscuGnZF_=k9>VZ~hRNlfCvVEZ6*n7A ztHHxt>OxqY&{2dh-JGUSxm?0oLFXs9*9FtsZpUclPqPS-4F8~G#dPdGHyAS7<{AIc zbZQ&`wv%9Kd)YN;U~3DI=Fd4ZOpjySZi2TC**3t-_qlKzIS7N zF%`{3vCpP<5B5`&Tn@_gQorlTI6+8*J(h&|7KkqUm?2|@SR7V~=p z);i9{zh0vW`l zqJfO3tldRpEi}k56nN7d%pO?g2Nn7~)cXOdjoz^$agP+}=UusEUFVUEpqCrB@+zU_ z^~Et|K9l%;dmpv=G|xLb_6nZj@A>m=pp!iYRoC5}me=$wL1FZ7zuF7e<$kS%-^GT75Ry3o)_^au*k<8Dgs>d38-r z7tj2DIGdj)#bx2imA8T4n`vTPnSY4*R0sP+yP(tqfvCE;oRa41CR_0d;R7Qpy9N7U zeWhg+yY=@OgquLmR`UY$b!FE!D@Ml>#R^UyymODQ*NO`&W9TUrpKAceHUb>LiyyrG zZg(@T{#5o8S=00&f$b*iLs>JI4-K z`umLcA1SJsbSE8Db<2vK8QtIuM9D1CV;>I8{0KLVm$VHM<2v&cY_YlAxPpSR5KYD@%sQ{z=LKb*~?DD`?* z^(G~>fV66r*ou1AZ}G&+?&d#H*;tACdF-)tlOxTc2O8CGObq1Vr3plV|h$t;uFjk$xGq9r60HR?zEt~ zrUOmOa_kKSFzj+YG4z8ZCdZ4Ehc3NJI6be;LR~5c!^CnZ*kut$O+!eaQKp{Nywry- zoppwYnZ>lzuBG@Hc9p!c3hWuIlCCXlX@j~)l%11+zsfVYW(R#jhsK~1S%N#vf2QJl z0}LmfoOsCSNNc=NAYf_NK?OS3)qk8Txlh?K-Mhqyo)^agJ^)TumF3iq9Oq;yhYB z2bWW@_6M!fC|&uF9@63IZkc*D!9bqdoqQhT#;fRJsuUDD9+5fOx+rLzpG_)JwOrF0 zG*awk{aI+5wz_@4T@o{(lDz%2+7P@kiw>^U;(NPnm%qS~=Q@cdS%=*dX@Wr4>(yTf znX;|W4oRz0=aDwAq@vPy73IT{n4*FEx9yPqi9d z>MP;8X@sLxq>UPHwzXEf6oe4oIr>2Ho-zMMsyq?V8SqGd&ivqHB7QuamRBmy6NP{f zaAr?zb9rHXd320d5W=j}36VO`4ySEdoB8uQ!~O3qU5%=(wg*DW;dWkt%h>y>3;xz0 z2cK~9=9cW?(KmxYRMGa`N&7DN&3 zLSKv(Em7kbGkj6^7Ui;tT@MAyODsQhi9ez><+A^nR*QtxnV#c}&5}M9yOIoUN|d|8 z-s%8*ql52`$hZ^@RGHtU*o}3g@N$CgBQO4pdp9L)lPmFwWV_ZLIm$I5yi5g0UrX3u z4;%@K9bHY2nLGNF-wSf_u|1~%{?KP=Q0j0fr$ol!t-#&+kI!B_gg0AH^R7zmrxGY8W83` z<8@CVT-ehD$q`GFkzi|Qk zAjvW?aEzN+A}#lS%g|;K3OORzrA50e6~+n*?+jNn0q05=3+$bDMGGVC41+o7j?gRH z>WIJ@@^f9{BcR#hoIO#qDRcIfkD%^jH&I(M%zgDQiAhV+>3Ks44VSZ{vn`!629aZV zGM${%e%9&f!8$`{exff6x1!y8!a6HX4eZ6d+Oe#E1bChBK_dRf*K3~Nf`og8MPurf zvo7|fq}l6P5v_6qikmL0d7ybRyWD}}C>sXwFf=&0Im1Je&T&s;SeH;omET^S+D9IP z(3SBIoSZUl31r1#mCq#j+hyh;^^W#J&gq)3o^y=i*x&MtQw+ zZu~pv^8fcR;BVRG{{(My)qg4eKfI;U_a*w8rX?bfmut>i@`BqETq2)B;*&mW^dlsISfo!+g?@MEzUsqAUZkMjeKGkEk zs6(QuIOD5ec{(kI(?GFc!IKIO>}Mp9(hPnTA1}`ct2I{a4Gxw7ZSyioA%I?oSk5MX z#%-%JY~v#OP+DrZ8E)s(7i0%(Rrxf%fAAWMgvh29hcwGwo5Wn?Bn!PehrcyZ#^Hf= zmFhNW_i%2~UA+nXoPVEkjKSn!pi5l0`BiBRf61!pBIIa>`u$})zt+mmfrqi{g5;j}s0%;RfT-w=}*=PMX6SlzZ5Sj)<9LoMYeIKfCeC0Z&;+2gjVua8c82 z1D?FLEx-BJxP80N3^eT+gkl=`*gsE_o0Jhh*dgiIQotJT);OXaCMn*1e74MQX~#8o zBj650f|N*g(;#nqT)bmn*=Qr%w3VH{gmvj!QyK-N{((#`UPo8KBJXZI8-!2zWE?Il znPFJ}F?CJDD_CS%94vNT!<9&AhUoBDlGllxMK=D5*`tLJr zJuN*6%r}s}A~%699?fDDT0y{sqb&D_Qz7D!RQiX^FAoU`K}Ipjffkljub}rdcs-RUrb; zbE?)=Nt7UFv9fx7xp8E#R!*iRtR?3Y_F~mq^%2w}=lFMaG2cE`!u0G5^g5ql5xKp_ zp}K5Lq3^oVA*8Tt>t@y>Z86%fi5AmOVw6tXLRn2HO?;h$ z+5##b%TG*)r;mQhxrp}rfN~jz%SE(o7G3E+p6%34Ra|{b=mfJnIdcEreY@e~*(|Ro zwU57HYc!++F)Bx}3SFPUHJ%S?UMEKZ5O$OHNwNP?8+hbx#?OH)kKrqW)z(psRtXDP`c=|uN{F52CeECqkm6 z2fcBOv5le4RcVr?5u!Db$|!-kz+yAxQC9NDt>%Tj%|1r3|t= z)l)%}g~w!5&E_fYV>3A76E1@y7{n7k#4Q%LSzb2F+3ia(oUA-%9*-%@9TAcbw_D!U z=GC$iin3MPnD&O~-i<8s@qiDtDF6`Do-XgZZ}$LznbkmkFVx?3Bz-VKEbghE5L`^+ zxJE`LkgLpbX!C+A*xgPdd(rlkV0&JH%UeVh6ndrd8IfzVZ&h8T4xku!!2G__0yEFq zQq_5(&xACQ7V`j`4be5dqj=JaU#F!l(~UBXkd^1wG@n&Mtih5Y)wH;#fGC@cPgjRwP`JAd9^7Z!}`dfYh z2x5Cnj=HE0)ntUz?bmh>qi(wAQN2C>$`a>Fi*uN;>2_7!^h|KK6T|S%+tL$fU-##8 zsEx?iK(R+~AQe#O0;+cSSI3cVoPANpqR_XHJg<*G@RvmDVxoYdKNKNXHjSl4&Kwb` zaW$Arp;#2DM4}$C-J<+IBv1T=rt!!$S`=h>HXg{1jOElZ*EkBZ+Hp*1ihqbRxMPr$ z59E|QURx4lrJu&5j21lmMWhPV9Wed+PjQJqP4)Dd*L?rof~);rhWHJ(jL_hD?PmOBT3#f^}te^ zD0)aVg)RT^VNULH?yu+5Q%|r{FKsahIgOm&CMf-3q_}K?78`2Q$X(9iTu6h1*0F{@ z=lG(*nS&!IHvnxjUEft3EG{0O)J_fau5=qExB%!xZr_VEN-Al4=FW&t&S0D8z`|r^ z4BX!(qa~%RP*AL5VZ$_I^$umE>UiYnnAvGx*(Wg~<&uTI#RdADps(H>DNnwK$&grv z@8sUq#FqK<2Nz_3yuDs?wr1(>2gVbt9fDzhLX>)0d=|38;NN%O82b9%I(VCf*awyU zt_0M)Nnyu75-o?5su!=OI+_3^SNr&!_9K8z5kKX-)aE`AQvSegACwCXx(sva1bM&U zu|=hl4#r)^M22-y{z%>%E=w$n576SJjgh?ia;Iv6REr|@AnjHbprlSRDC=f6sQ|LI`zj+TFgzyH>^Qq|t$Hp-X* zjE)RVuwsXtWsPbrEQXr7j;^0Es4^|&v70l-nM$Xla;KA2^>-X^LM9=x)1M%Istman z9Fc}nPxw3d<{k1sIw`*H9=2S9X(pySfg=FL8%BUKq|%{i!(YS`_Tp)ft&suXbm-*t zPRbH%JFoWORiqbAZ?&I%KU^Sq^`O$GH8;bXgHlZv9LjaYUaj;hEso>tPfqltY;mQU zZw>7`EDY8;B?}`q z=bP0aF?H=)-OFEu0JV&?22eQb4<0iXI#cv?v-cwaa2**{<{l>-X1;G(R5MK39`S;- ziFGtcYzU%4JpO&m=)NCTvX`x3-g2(KY0bc@`H*K%2Y4Tk-g_)?*vl;!fNotXV)g)@ zbF*0rP0xJJTdz5BHLjOS`B`$QVnwoD`UL5LoLXy4hg=AbvP=Nn2~GNxu@aX{mRVx^ z-dS3AG0O}B7DT~8)HZxHoU#&i4 zW)^h7tpLt@&2Y{Tp5nJzEd+QEW`azg1ijBvRjLDf3<)iiP;u*BTMruMuI8XdhG9!* zsbJkdB2H=zRSX2#1mw`JY03P=x>x7gE{dKnOF(YtPCpZllY&+K%FF$`EuAFEVLB%> zj}Dbb;_f5SsmTsqu)pcg5C4$pp9-fU5-CjaZV=3QHek%eFtd8;Gw%;8zk}#Mi5w{qHr_(;lX>rAKEdL*$SfgIAI zEIbpH%&55S1|Jkcf@8g>#%(&>sF~Ba(ne-Rz-Fv+d_JiuM(!+u!Ek^#Qfn!uffsEfV02; zpe`Rx#ekNbSO>fB#bv^J@Mg>Jzc)3hKVvyKK?T37f@c>BmH^~zCAkhTwC(M5z#OF>>2twsqX_9OI*GTpS%xoyWq#u9-%up z#*7d$O++)As-mxJ5`o#KxFoUI)~}PZ}SHTU4 zBSl!v60ZQ5TNT67$`Y&D1bosZ4=tFB`DGpF1g!~7Uy`tFk9zX}H~6Gbkj!Qv{HQ+X zJ1_)bCSA?+e>`Km6g>16n(WvN4EP28F$#&R6Iat2%TpcFNvk&kUPy8s)c~ z)4^O|tQr=@$0)w%Dw}0(T{yd&7xn7(d8;6J7~TRm$3vv$gBqgiXthq%6?&j09@_fW zzDu!wjTyq)Y^FLdf4}HeV8oCi3-^wN zBD0%jCG_&=&~`4nymf`^8_G>ac&J89A+f2bM24&+PKvYOOf}}RZ8$$XpP8}(VnL`| zJnZ@r<_+2T%#T%ecxlu1p&7xgAgpTH!ZP?gYw0bXe&bVE+!xO$qIgW6YlkJQ!g!5) z6Ge4g5c*JFn_R>uJ6_A(<*7$+9S}*zS{2XmV<}0jdz5CsoLRXzj*{KS?_kgjK-@xf zCYLdE?2tow8y{aOGkfSuKuQH3NS>?T41cu9+-DU%RRC~2KA4&WDTmvpQHvvS;pu@( zxssf)13e`N)#VM$FQdmJuwBld*i?4475AuQPc&_nB#4!utXt~-s{NS+Vt&gbRd)6j zX05F~MY@)rjlPBKH`8ju(qr!d>ZFM7c`BMtLHhX*MSJ+9aKBb8rP=ZP`Gp$2Y^*%2 z;}<(Gg-jA98(f=2S5X_r`D5!G`>VRgK)Jwl`yV7AMm-y2Tzk4`R&K}JM9T~Z0Xlr4 zkEB`8hA(i+D8b(RE|W8t1tAv!`c!V8H2v=w(Ks;QK49~T4}%ja?`OwN;& zI+7>qZ@_v%)D*)p&EMh=O)}Yz$K?q=h43?cWSOrK)4#FdaEo7bY6InwdG&qmba4{( zORfgR5xX#Msxlva1aEpZL#+srRsyTlDp!?X2{N^e=NkBF zO=2kRwvZsf#JE>D2zv@=mgrDXWywshl&N?TcMh49-)T8lnjH7%VIf~q`j}>BYkR+c zpWV9C1s_*c;Yg$})^?Q{R;FP|L7o7bRUyi+6lsRI4A&HDwDxEmbV~X)7H9Bva*R<* zf;EnMt3p`S<29mqxX4iOMRC9*?g|%E?|qco*RZk>#Mzr={p(&v3?jc@%9OO)q$ek> zs*rRNBPFD13Twlx=Z{b=r00xO8JyQ(D1f*{Hk6yx@3ITuhNexo*f8R+c>1q4yf40u zfJNmdsd|l)KKgH;l7C~qDv7MS;^;?PZRj|%a%12=y?VfL6_$D$8fbZn13F#C9Zg@F z`Q;+!H(DWS))wXzO5o>itLFk2%lh?QQnfd)5!0L3Lkeqg#aE0y%(cQF28W+Mww&W_ zsjP_eX?{BUX^>b)v^lq$nCO8Zl8MOo_I3D{lrBW<6o zyj&D!L<&E74)&Q-g09oGj)u=%#jO}NYjPqhfmD-pcX@3|oYB|TB?V}OV{j_Oj~27~ zO51RZgOhH|bpN}~LUvVC`o;*2%x2Bi-CsN6oUGBH`3CH`=~olr#;Ne^CFr6QN#Cu-VMyl~rUk4D#L(U{dNxGm1=37~7K<;=X&94zKf$K4rBO>-|1 zU%hPwL9{#De%Fdb>pOXN1@(G*fbrq{Mzo9%Yv9 zZ_$>Zv2#oLP9pNPPh3}&z|P=a-7G?I72#Djx5*)`Ygyinj^NslZDJ&dz}8Z)49OGY zfgX(d{NOc!AmY*7yjEcD!uCG}ME^V#QpFkPsrb(FweN0#|Be&jA1wd3z|VgQ^S?CT zi{ozvVNQD6NVH0Zn(7F2^pC;6$mf|VIb;!`Wf$H!><$uAC*<4tWOVZjn5JQlAiNYM zR`PZsp5z4tx{~)IKS2c^wrv}7Tdi2+95D= zO+H5&UgWZ*l{5f2P#A}jMq}sqeA>)=in@g_gb7TZUrr642>KIhpJwjNL5?rF8@Jg3 zE%%j`nr-xsH&%<>y4NLjb*v-v4g6h3V2yf+Ayt*~1t_8JGOXt6R9x(j9tfSyqb(4? zZRq|ZhGVGjUNo=n|4X3_46z;>%n@}K1#W@9lf&DEgOquwb-ZQ zOa1YduN}`5xC8lxdk;*qDwW#s(ZT;gpI zrKG2+iwv!960vGp0TJ4yz|xPxbg|;?Lne3fTNo?9zTRW^gVhurkGt02t+@`&GBIBv z9t=AnClhYfL9z)q#rHP(=R6@{j23hQ1$=_MT)a>`EKtaNogSQ%%maqkvG>-%_aK6c ze+!>+(9XSat$bJ+EiemCdF9r~C_-uu-^=GF>~F+IO5cNp{@t4rc8fENF76Yh9^w|R zxJ%o$42fs!JQVm3koj|5w+fXqC*P@m`bcv_G7-){)PRi<$thWs8m!z zf&sm6+Ir~Cy4DHiZ`kh>^>z;K1C}jHW$E*2qoUMx@E!Y$oh={E)*Yt;UUP{b&!#pv z1HmFp`jMDG8v4T#QD7G|xEkBT8TcYGRZbs^Nn_w}D6!-#lv#E}#SP0!G7~9j1um-I zpTK}8qVX!+q6wK}Jkm7I0y1WD$f3O()8xqt@jLb7QmRtzg7n%G9s7qOtTegYeDi=LUNoiGT!?Y4nHY$jC(V`+TtUc3> zCT7v4dkSzJ#s`?=HW?vGVoWdWcl~9?hW%{x%<^AV7s{pkNz*U~II4{`&;_)vM8!TZ z0ic;+g9-w}FR!H&F|I)dByS~^hU1a&{mOkyY@Tq}*^*>ofkrI|0Z6vevdObo@N#&w z+GS20osytRx5#UC zW?_cSdVEbZ(igYen*1_sfvQ?94)&T}n#HHP+YII){t95Z#3Worn{wGJ%a1LiO)!RE z2F*I*xb1coLBQ%7`tMtv6WMvd()&(3zn}=KE8k-!VFB2K>k$>H-x0{Xug=NS;JWuS zUE=e~Px#tLsDv;HF#QZqivAnDk&5k%L%+<|$j*aR3)2EB{R?TG$b%Ek^d3*|A+4O*HlpJSPMf7^S6M`h8b?9q)JG0 zsWb;9xy`xBv?gqCVUTvPdUXw9G6`~MCO2E}zT2kfxkYrdN8?JDln_{2US7b(;Tzk% zm)#{ky0CChh%2SzhRfw;?d7-hZpWW*RG%206hyN_JpkEg=?ysXaz_cM*z4i|-iG)h zIS`DJ^b*sz^+KMHSyU7R^!;jHS-T0LmgME=$a2!M(%zp7q=qPqdE!Q{MAE0}zWKA( zm~p>AeIx0`+^qg6L_5MTvqTUVniKQN+~X`SPneF@0~eRfYIB{F;hnb&1-SQ0+@*+1 z2SoAWd$M?$cAb2}`(7@|o8m;jF3rW9A|?OFmv*em#uF-1myzMRFl%8}%pj0qh-J z7pJ+qeF@uDh-(?q)nij-sLY#D4b3URQmv%^xRq|sjfMAN=SX<)-R{N6cKEa}ryclP6=HCLEi%sajbC`UNjI zAXWIR-AlzShP+5W_H|_h+(tK}a!bu`sNRegk7Uno!bX9`NlwnjC+)13z|&<(2tajK zN4iHFm^V%Mh4v;VSR_Zo2|1sQo~_2Me6cj*OmSd!H{7?Wm@LIVqhS22v};swawC>s z&@KG80j&Ynf`!&$EI)=$jTsxna)7|Tr_QhDF}Z_)5H3JubCveM96(QEMj9BM5Z7oL& zu(q!Y%J^KVV-4$WenzSK)y748pWB3}f?ZBy*IJze$#ORDrJAisQ}U3nJyS>`i!z5q*{9$^OC4wCKe)| z9`;NqB_1%u)-+hLiMiTsKa<39t~*SbvpUnQg^1Uf#hLPh_BQ@GmQ3Y+yoW6Z2H(eR_yoIS@``#cPEP}u66EqHR3iY5Jr@wITtMh z%R27G&)25)gO<6%>hTBw)C%dIWnbwqAkRkkedPP-1x-S$JReS}xdP5QG1rVg4z(zS zLC|HHnj>8-c0uS6iaeeg)KEzBP;>;u&WkYHYI)0tL3WrC5vE(lN_KINLV8WGmMX!O7{j=0I z9Wn#VCw+OJr-8fXH}_cjgT$>chXBi+ZU_ySC~=&)6mm7Gs+#`M*`o{LWtVe0hWU2XXxwcJxu)6Z*Up*UWnx z5&a*u5Dh0Ypqshte|A6myHo19^?~22#*dm>v)aWzh%|y!LWLI31P#Kf@HfNx3ei{J ziS=gj%>kGzaOpj=tar!MpCAA_VhU)Bf~$k;F*|c}eDbVLXfyCb#=sbAUxc;um^frV zr30D64)O&wi%AbB(3m?S)G%4M_$%&01PjKlG=2s%v-y@_zj1E*yKy3&$=Rs02D=sg zt@5g>_71Awjqy@*wzE&DgY{h6u%vE^$%j}-3-T*#N-!!u!Dq&rQmrrM!s#j>@M$?a=Sbk+E~o5rh7{fg)j<=H}U z%)g!4SdNLNt_26dU=x`PiK6&0h&vC85b4|wrS^$59Jm$c>1T!E5{yC6+ilUhZ-6Jud^g7i~h zw&!*K^gtlzKX?Sr`^}hoSmPW(mrHh zHTN|(SA1)$^fm3P!sC9+8=+vu`l}?mMf8@_cQnThU)S!A?$?V>mtQ66;AcbK5}kSj zzT|5m$pHjCzQ3lOWJU1+krwR{&XLWH=!*TQ&XVFWN>k7Y{h?rLq8RjO-f#TFFxe=O zt{3VIwN2tBTxXowE_$-w<^EnljHMxWz>P4j+@Uu|dVtUr9+KJfqC6AhAk|v?jO73jNMy`Sf5J_wNngY=0VetS zNqA>^bU>KDZ(3a^P7Cq7MYyA|wTK$V^Ra&PHMM4hZ^lP}AsPuWOH_?FvYCVoW-2;mmLNM`BJ-%Pz0;3wOC^-2>> zV_b?eAA151px%;V&FiNCtr9U$;*Sj=rFHTa@>rVd58dv`0+_ZWKtJ}K`L&r{0P6>R zN(r#Rw%f=6iSZj?oT^fb{aBou(^vYz2Wx=V6*{0H2Q3I;FUJa0U{7+k9XEL7Bt->r zn!*xag~=B$*302i@XB%M7DZKOb!xpn(y@m=_@%Lc4VmXrnl=-+-rhcn?%5Q;v`fVr z5S2gr#1lZWoBo8g9~on`3jlqpQPQ=ZkV6M__c|V1C7AA{DHK$@p9eGUdcN-`#_FWq zdSrczSj4?p5vDf(2{+{u`$oT;>xI*QKmpIRR~gfN#k@|m9d|Th%W1iyA`=wn0`KPZ z?!`Cand{#62AFb_>|S~)3^{j(YzuP`Rvij!5kpWXsm#rv_vRdMxA$}W4vz`kF+$vq zZM1-`ffAJNp@^7lIz?JY$zB!-3oG#L8Z?EQm#gF~Y+xY6_>wbSF9>XV5p}|Spx@Ce zz;9`S`_*Zlk93SBI+Y~BT$x2jyW`{W^!!L1Ll8%aJ~OaQF-+}!5|c;ietPc;#UMrX z*}NfUcJ+;jx46(&BW{gB{Zz9}5U)wT^z25=?K9w_y4E4=`3SE!QfHZ-DIlO9(9rZX zkG<I!htCeSkos8ST(UQv>=plPdkem-HE}vr5s+u^Y@b zQzUioXqPc3^e}x!YbnZ`i}^3O8uNVD(646h{)$|U7uHkuDkf61&!cq%0dk&SshW%9 z=%qCy-CETN!|iMf`2}{a?D(g3-MzaTW+AmT$(=ChA*7xCh+;CB&2Yf ziBh=gwU$&BJGj!k7U?9*K71Z>G`7w;tJ!*}$wE1`@s4x&{N1x=Nl5D*??qh^+{9O_ zy3=BFEQ`>!PNK$SP6l*?oAj`5qFT8mm~Z;SNe~ zgT#?9E}O#pgSKQxPl!n3rio&R4)+gLFC|SRFkZu+avLsd+rid1lAZ%mwje-bHGf@X zO|*d#2??r)%2XhV|J{5c(&LW38``E+6&|FOK)sa$4i++_?xOJa#K(!#BcWgtAJ+_v z;Yc+G0Wpy<8jh~UE2x(4h?TK&gm0f#NKeX0>H*RvsR^*OJ8`5x(|deBy;`ruWZ~cW z?_!{0If?f`^q>;kqKV}UBqe06UzuWnGfs+Rfr=q9y^)bt>pmxvT%500LLSVpb4FX3 ztD(s{qB+v;V3VF`=++v$BDq`DL>ijQnG@CC8xwK?X?vyFB&>@HLXl=)epolV5}k|R z95@-6t8P}UHTTG54F6!!$dGtnMRR`N@}$ua(V{P&Puu(yeS|0{qCsn-$Bn9Py8xE& zsZb2HpsQtntH`>BAy%Q8+-a*?08P&>0Uv*!pF9`$t zE8kE;Ul_(G^&%{+`mWpst+xwFgrjF7DBpw)LIr6Y=-YbD>eH`qyTpP`Z$fpvDDJaR z70ZCs2pg6iVslIHF@XwPC-6`*Jyi%?mvB%$japK3Uet^1-;l#xkl`FHJz8zV+>~Kw z?~G#vkNjPj|3R)Be+gnV+B2zH&i6x{k5E=;?o0nS+wzXM=9?Eh-nybX@sT63y%8k0 zA_o8rGkAZUY4)dijwfc(FQ+ttaljNw8{ILuj#!sS$j?nC>>V*3fZgX~vg~|_&N-1^ z4uI(sb0JjNNfRUb)?5OYQL@VIT#wB1+>e4joKvR1am(O_LsCM2QOJUr=E_k`gk%Gi zreR)*FVuej3(smJ&TkFA+Y<%efBJL&H+c3h)6QQWv%ed5QdNO}!mR$jH3T^@3|Qh4 zANoj2a8Y76*(q00?W=n~XFNIkw#RrVsfkVXT)+(1)xf$)0W{%Jc$;zfvq-Z+y>l_z*FV{N`y9)BTuyMKVcHa920uv~e2-IYf5BkU~ zVR-!^zcsGpTMg*s>FiJV%UCy@e3hNZh^t5(Ok??RYod>cAaMKARQuT39BS$j7ovYO z^SzQf@BJK{Q8c327%d;`DEbux9D+~H^K?!#vmI^IU&pTw+NTJ{AonqPoR1f}g- z_CcHyYvJK8KIqM&n~NVuYK34RZrYjEcu0x&2OzJNgn|}K&P`-^G!M>e!G?wUlV%p4 zfl2%ZjXJen$vVG(x>XH3XIw9yXWTZ2qVh}V4lqOV9I;inKA86x(!OWx)Rm%BMb|Zy zDMWcV9p+d@FFm$34(f!^#MVEpefx9K871CITz(fu^#4v6fBjAV`}WxEVrNqV8(G;l zF5!-tw5DBj>JRM4dTz=*>#>{U6YVInA|&h~TK zavB2PULGKNcvrB-cPm*IW7-^wg7L8(Rx0}QmBw;_nj8`a1mm9%#h+ELx2K5@c#m#VBHr-5 zsauAt7xlvur!lL3%F|{FST0#l$O;;*JSQ)pAeJbi!-EPZv6j<#biGP~sab+HqpI^m zu`l?%K9z<xH@<`$?#oN4fGbUwc_)1XSM;2sZ+t4rx z25l)uQ^1F&q?X9qYH8r`@F0;elQ9|B>{{Z*R0txTN{iWcp;dpxrrjjQQ}n^&16KiWtUPMQ=I<^MeVq_j-T5kCklgb9$e62fKsh1118$De_n1uvW{ zcm{uLUUbxM5drpmGB9U$tVBJ1x`SSTZLLw}AL%z?8{g9zPKMbjMTXgBn)0oOzZl*I zR6z6^ci-Wau9~Iu6U7$_{a)6K+3%DmKSz-Q{v{#rdLPqXPx!r zimuW6bV{^>xLzUj*S`D|pC2`eM$etgx%8n5q+KpBDvAS!>QCk6zI}Xf zG(RQAm961MMI0noGt2d>dXaqw=18rhx8uM-U+A{N42xoMAfy}XWb~UO+?KQP|4Of1 z6m4mWFm#zvf@MOSKX*0Sj5slqIi8D69BPvoML^zbJtE zt+XM>(cV(_+CiE_D1#BuZ$hi?RDcb=`ux7hL&>b@lMH=`3OWdj2-1=vlFH}S5LuiW zi^l(Y_-ty47=tLXi2K+#YdTNJ!e^?ZLa|k7*&O{7w80%b$C!A7=2$pvLK#mSze3vE z`&zJD?T_=MHF$Rhq}_X*;l>w3s8Ek@s1lEmB}^m4Ac}dVnX1r<=ljcyW!HE(-*FBc`MZ9${&)Ie{}1{x)z1QfMBCW0 zpjFGZ;II26q)|y!(B9t*BPZf|0NL4I;?>Rli-JC3{;k}+D`&8Oe7w^ zLhxx-Y@Q7CoRb_C?+^S^lTfg-u?w>n&mClFNdtsq&%Q5q<^ph1LHpC7>|apNBh>au z7xO$esI&U^eZ-$j`YqMFxnJ9DO5Nl}jT>cclxV*vg)pUodYtnjYbHRb+q%ESiHD4> z?CgXb7vi1cSs#Jd-yfLT&bAWOA~V7U)a8w=7FKH^7l1A9c}s|!TsyEv7Xn4hNF zqUVrZl1n*8ub|Aw4K!f9GXIcW$1~IqSi>HPFBJ5<=nm8G1!(OGy8E9dJAVY!n5xb+ z_AVf`|DAw1{t}R?t?i$8?i*z~&fD#0t!kRnG&^kM2Wf)yX@fz*gAo)_Tf?IlOY$9j zjgxk&S4MDYQNcnz#1tF$m9Z!Zh~v47Jde&7ey?xObe~?WCi{l>XX&9bda;PLAk-TO zjG-3&s$aApopXT>)UzYsJt2;x)W9)e0V;$NFfyx+jz$3bKipWJNV`kb=E3I{&!(+~%U126^((rn-fUK^c~2c=cRME=iNc_QI+8>-!pv^t&d3tlRP#+guMix7ul4TYI9A zrVFBe+uYAL;QPkf!n=sAvh_*rS5OOFHSt%9%nQpuLyyts`z5|;==YKSu=)ct{=NT_ z1BCa5w-J1ohT?k;@V|PI_fLxA-&5{i|FhoxuoyC!0!TrbNaQBW!>R6P4axEF1E8=xn z)*WWLO|lk#FwlvnDQnW=W_7T{k1}<`P+^}H@!$bDeZSHPQ2M20rFO#eIXmks;pnM* z$A*&}-x`^yZv`NY_O>0J@%nMoxwz;+rhj0MQp~JCvy`4nfMhIM)ZVMlfbnpU7-^dZ zq=r$0&OvLa+PJk(N96rA{eq}9X9Uy88Cxa{v<1%THBSjfcINFV%4ZA1$8H6$8LU1AMTRcdFfEk z_igR8_3AcXbG>SAl0*-ElFs>~0LT^BocrCk?-8ukn+7ZIJdD+8XG{U2cVWrt)q$vD zsRrhHSkcjgq%w>%kk!_s@UnqmVs<*R+l=aMhj2=AWBAkq$^(**F^zcL%K{gbL-y|R z9i`6k9m58et+Rxoz^&a!L*EC{6sscges00xaA+769!75onEZ0sd+TiD*2Rl0&qXWA z{=&wlsC>Q?FNyT@fvh|HSv8PgNj`ve8>Xnqh7fslF=fYTInhUf`1^KI)f89pyn%ya z)#?j!<;p320NE{0R?(cmIoAW3{M_aZJK+u5-Ru*bQ1WktUmCvCN^IRk-WMXGkANE_ zy!p>o2mIb->|*{OhLe1aN5!WEeSVAX5#C93L+HcqID8c4fo+Tb)>4oD3(l1IJ+Lk) zW4IEMD)$ySGQar*Jk3Svlzud<=Wg50faP{5jMx4&BNmr2g+>)uAwZzFK%TyuBu^?M zx|&SzC6T(WL=gU8H&D4P*B0Bhw(N56K_qLf@B^ zWu!TK%2@#Id<)J-)olCbN9Yk61Bg7$6SMlA*a%DcDo3hm=3%VSYi_QHHw8v93%+bp z&TE)Zkqnam~iS*73a4@^mZVYo8(frSwP&4%uxV z^L??29$o|W|0C_2!tC7IY?HEW+jh#fowAuSQm&M3+qP}nw#}4MJS)9-_ul{i^mBIi zITzo}x?A)0obMRJHw|eGT#Skiat0R}S@n|Yz!~~&t)4Bq^{T#Ng8tmQUG9|KzsB?ft248vXaeH(9s2QZ`!C>xt4n3? z-k*(Ky=$+7hhSt3A>iSn%PQHoU$+tEdnlCPeZr2`cC%6q1v1n)yU*83`2Kn=`)%jJ z=jDhCfQ>@|`$;PQquYglwe!EP7yfB~MJH=(eS>fRsVJqasWiur@{R^&E;Ox}jfzk* zT!2ontsRQ4fk-8SKox3}qb-#$V-X9r$9FUE(d`!ygNg5X&Hg3QRP&U87HDKYeZ%2F z>v8NCfM($PC-#~h0d`-e*Je6H1om|ddqZ^^F{9b=Q3%3wQDIROL!?3Ddi`Elm1_xs zLZe%IspvJN%i-&t%5e8)dVP&vp+Hqrsiq(@%tA%Xx@-&3i#YS*)*RF@iy9;2MixzW z`_tUcyej)PGb}dU>F{rvnv6`DGq4-3{oO+7jk1VM(mZ7*(tSIDd1N44PN%3NP+ve4!WWj2i7Ps@q2^Z1Q>5Ig2;HclJjV&w!aX#*Fd48+dKfw-g~M`Y zs#1Z3=`!-k^6COWa;W1wDgLgY(Be!kH19e6R^%xO*IGS9iZDKpt70+}mdaPDh6*b; zv6)l$Qu(t}46c!Sb3I*NMSf6;*MdDvs#GE!{h_`p<&6E47WX6KkahCedgc?GXe0Tg z?TXcrOGujxG`iQjfs@64`rG5klCuoW3B#6+6IOx0!U}Cm_qt24u141MF?FfFiumEMRh1ol*1auZ0pmDi!blZTF&8irv9JqEwDI}}H;>o*2xXSjB^RznFj8ae) zKIX{s4BS!wGjZkybW`YwE%)VE3yX_?Nr!ZiHJ2%M&6|)&pFf{n2noMnEdo3k@e$d3 z-{SdibNBD~`lbDa#R-r{p#Z&&%>P4tDH{Jvy!~5TDUQkZ$|LY3p+Pkmh@c?gCpUMO zmb5N3>*oj~BcoYQG^}@%9wbgb2kRDu^!EqgZ;N3T{a{pJ%#k>9JWNlSlsUzk@$uH$L=NEyVRo-A(}7kR-|Jw6;_GTrAv7{(S!Mk3k_TQOkx@0*YhoL35x$nzcYm>rqfVhM za}8FV5H4dsw}47YP)>NW;eo!{iGy9&&ufuO{GFm6XbwhJw^ZW%=a(n z%n{7Q9sVR4$GQicwz(89=YScf0Fh}K{RDKAed?g)$n$by;|j2A@Oimc6^&_;bzMY8 zK&8hM7L*$yr_w-R4wOEd zZajY5go_2D%0GRQcDbBw_`G5qI8l6rSra%B8IJxS_Of+*6d!>zRz-TC0piM4KNw;^ z5W=3DgGy2uzp7v5UCm73@?f%QpMrcU?SG@w!Pu1Cei7;4CGK#=)8p0QEiG5PBTvTw z;aNFnF>cjj#mse%pj;)tWrc%e1pa`Sx6^-QlHZ!FuvUm%<(8mc-$%Ch;Nitdi@Vg{ zQ+nzGBh*7C;EpGPTjr8$2+O`({UbKA76LUM0xIbOIbUYT*rO=Hjku7hAxS|QA z`Fe<)0?j5tn7R^I4yBXk6RB3hwETiSfgWa7l)MWT6QB~>h0kVTU!08j5&^+ZJWE^L zJh3NzLHeCQ^a^}mUm|8QCzBBSvrHsE9grI*(64d1$b3pZH6ho~b-m_lNJ}dz~Mhx{oW9$F7yz$3?l%cArhNXt`9{Cx3aE2n} z##f@e05aDI`@LVXUgwCy;DP@sT}o5Y9c2mi}-Qu0D>+1Iit&tth$ zd)AF0M0!N}c+m8*&h(Bh&o4(;-XBlM-L$V10G+fJTebcp45z-Z;9y1oTyUqCmNz1_ zIYc-V2j5TdSuKzHsB zF`!PQYgS;4{NzgN!)7w{Fv*ymQ>~G4x-k*b5)|k_9pyqJo+3(<+WfQI*g^=G@o766 zMj@!O{=1*Py|#3?DMr`*YWu8T@<-fw<7=f=rr?=bqL9-_&lcOu6N2x1m5rQ<@MKJ8 zl}5~rh?uccLYV4h#L$^W%q-{_(?U&Vp&ja%46Zi35o8LO8G%l*!PHDv8UytiGFerH zZcAT#r-e%C(4lgCFVJvtR#`b?4GO;Acr@@B+9n`0BTAo-Gh{_0zXB=XFgh|nt& zxM}XB?FqiILD=z)%3hXV*4LLD$+wx1fL_5g z3<^d&qo>T`yp~PE=Ge)OPDj2uQl`)K`5L^A^jv&;I1wNZj^0Tw6eKpP?+ zb%{n~&>m8T{N5)>5p^Ux|e5ock*f5BeSO zb|Z*|lFS^Zt&{%Q(AWkHFj+-=;U4)8JXl8QMQVreM;PJ{wK(lHYb94z#ZXn6?+}n& zuknaO@zBP?7jF6x={dHhM%t4O=c~7&PR+@fqHkhcGTxpS*;gc&%`sK!7ph_AN|RKO z9kXm`nCWP`%vx3Y0s4Rx4V>=WIL12c5*3%F+{Jn*6n{D5 z_?K!N_PvEQOI(HGwO-Kog;;C#IiYMlg}ot21UDO`L?%yW&6$78>7R4cm2R!3XNs<$pU*ai@*(wr zk}(C*HW^d>BTKDaY+QD)gGTL=Wz!FCo9Pw=42*R_wdIwASLg|!t?gs`#F;nSI1b1}vIxaB>p6Y|p0qS2KP=v`1$!TBk3bsdFlA+lN3`uKN=@&(G z#@S@E9qJ|%YSeX~&|~KdP1!cg32H|Nm4tu9EN?C96F)X17*raOFa54jq49E{-?nJM zP)@U*J#*1aH<$jT>t+IUMkbIBOi83LeiowLa)0%sRb3Hfmr5uv0$T>Sg*@zKat2UIj>%bB6OG zPBo1u-p3-Jp920Y^n883K3N4`go3|OgHBv$l#5#X%Mj38|??dM@If6F>MKKf4Pm1LqsZv_?CZXXJ3shtL=;>fmQ+Le2s(7WpI zv_hrbKlMR4a}Uk?17oxTclhx7X6|%{kY0oS)eHQNrVTW#Z^ zK@-y=+sBU(_%Q%N-m}v=Wg9?(f>uv}1p-7F3?wYj1fW?FUrekc=t;YVV^PKdh3^(3 zG?6Aj_wS4Sz@75=7&m)+zj+4J176^UN**=DV(v&Dz!~F~VuU|BbMVO{X&P3D$s@%c zV^aljYXrhg25`)1eL+3mgq%v?r!&jwpH&j$q)aDG+2DXPoHQya)d=hh*O%67&}xfk zpS2RdXd5DftMB^7(}xT-BIZ(BPAkmxI|}S9 zgu4J69wI$?BJXuUrhlNb=FRZBGmnGevjsMlAHbGk@~}Bq9!ueQhgn4>6IcZFmM-@@|?p zTrdzQkT(=06uB~|iMfU3Zmw`p8L?mv;Q&;S)Ez!CqbtA`^7}}zezsa1F{&sB(of^7 zZN4CnfNs!dJj8Xv)a3#$EDTQ(h@D)3h_VpI{eNxp{9W$%bQ1^^gf@TPZk6+b z8Uj*pXc zdlq4EXj+p8W5cj6+meTXEfdRJg2mwQxLi*40vL>2B@}EB*|-!Ip^fD-jR11L=Z}5w z1W~QE;&%#9=FKYZr$6~o0o*UTq@0XZkhI=JCVM~RS0)TZD2{+97Mm(p0Ajz)V^7)A z>|wa&P~3eEpV3u*i~VZr!P0{)?m)93aF{^>82{q32>hPeK7-6GY8kSD5bL;4AKb8pA|vJz?WG$O;hqwL!Led@ebqw91!YdXp6; z8|y+kEWcxzbT8RcKJcf7vrU`da)CP^rBDQ2>KeekHt7|7zoCC2GlhT-RZzW1u0H-i zMt${;^$!p{aXB|xdb8WHliT}5%?&xHb%hDg`8C=7ObO8W9ouwoFS}WBThDfdgQ8R5 z4Df8G+<6-;&++j4+9}vF2Q$g9R=m6fdg;-pj431u>F|x3+YH*pfHdWD-J^%~U;SWM z5>MC~;0MEii2o;<-+%LiKSDo2aZM7C%Xuur&F9Q4>q8$k2kJ$TJS!k};|QprK(mZw zl2TKxE#mHasubl>K>50(kjO_H2T5=zXtZyT{D?5JZiQNS!InCdLlXTtdujtSh>!IHDZP?UOBb^SS|&G81_?% zI3o-8k9af4%5UNC)Y6X~FI$OVq$2ki&edP6ryE_LJ!;ANptOk67GLtuViIPQ&~NID@pfgeE z-66*=gXF4ScJt0UrsrytmlgV7{bju0ny+@D(4EWx!z}^k@qf~M{mXcN+~bk_^}~Ne z^}lMqZvAG0d#6LukZh`nz-KzL0cgH*I(71+A#e(PW2*y-d19bm903BQJn!imt?9>% zFqwJ>@W9cNSVPi>t&<&%EqOhrU!`xpyi7gne!|=P3W0Q4+8p%46wAa!MKpX{U8tu} z5OtD+`4Oalk_gV;<5! z^dXovw$U}<$(T;wx4v9Xo5PuToh982RJ(eO?qcm1zO9Cl*$2S38Y8xr`Uu9hfk7A| zhs!baFxK{xi%CKfmTyyGsD=zc{!$dv)7|0n`v}`6#2UE|A^V&qACN?7+(l>$W=?Z^ zW3I5DYy5QbdH;O%l+t%d&{itgTBrv%bnlzZ$Y1#wpzp(cYKFN#@OR0EdOO}?jm5miw6X(N~x95vF z^iKfE*K1yYFd{ZDm7)?v+iLVTC@c2Qw3THFYXCaltOA<{MWWc^yVYtzLU6fWTGWWJ_%Ra0 zupAT+Y_K5R)AQ-cD#s`+z+427Fr2|C<{WMpK4SU{Zp>SHkj&aE(5uh}$``7Cku(!q zEenmIz|?3+E#{;|G$1H&w1^@$7%*3c1y$&Lp{r)OU8h;@48{ z7<>OQOjA3yEus2QlI*2H)ww)h`o*fMd<|C%t=iyGfp@y%+`I(553%Xt$!tls4m|c^ z=DZi>;;J0JZ~p;%GXMS4tR1~@*=|0KAxc@ibDA(vEYo7OY`;^1UpPu7(%kCZEoXe3 z9i|XdU<-tv@E~fZ6*CT#VGrArhb@g!z>gfTQFb^eMS1BY07UV8ZD$ zfjYWjc@B89a$Bn9)MY(OA`o->x4dRiye0GeI63%}fMc<0^MW&wjn7fLH4E-{?E<=5 zH?uT_5F~kXu)#Cxz@298pDD4fS*Bki*D-xbf_gfM{qa7tgM_baHUEHI!|OF--v4Ai zs1)V(;|Z;LLaEY3cMM0ge}JZ8TtT`m8K85hj_nZ%H#T7>g2vaEbyaAnEB_NOGc>g+ zW4=8}O!X&FRCZ=|6{tf@zOdU5iz*s$ufIKS{~n==@I50&0QL72(1-mK?eu>}%AYW* zKQ~YQvmMJ&(zXHIM5N0IZg7G(7l3FWpokONWm{8|`17|b1Nt#k)EV&2%7+HKgY<|i zUyu)|kf>f@z90Aj%a&9kDk8#`BWFL|Or0;e0ZDsG_tRnx6EOTw-a@+-F<>>1@tsm= zXeXN8)SxpwYd3Ww=@l=F)H45|4i0=_djh@#PRRzE&|-{4JKa>}E0HQOsryFpd=(R^ z4ty%ZEQWXUQl*#h>tZJ~-v_pw#u)NYdD@^x&_p}6Z9EMiD}OvrpvRjZP4;et6RbcGxn&Jh>`dn2O#XVhUeO9=gGM<7LT%uKR;K=>|Vl!O2fuC&K9dL#t zyfjcE<|zFtdS7j_^Jzhc{1u3ke}boTXB}Qzh0#};VXS7*&>fN;hqSMwkNyezuNwc` zL8KuhbUXnLLI7|Oj(?c3|9c1dBcWyhp6GxWQFu^o1ND`Kd!D@Xh1Fq@kkK*NE2YD+ zB1l>poys|k&IxeUpHx!KSW*Efs`#5p7+zIjtGS1VhjoWFhnI)m}86? zMhk+l^gu>n$lvW6!$5Y18#=n=6U;B%wYJ(P)UKQS1M$so8WYZAo%^FPPL}k-Dwxmg zI44hOFk=B*Am#HIyPBIVdg2QK-qVS(SuOQrsH)Cb?R~UK$j@_fkTBM3f+Zd;S|%JP z8Ag0e0}4OWUj%W3&l=_9@7URyWBm1r0?gf%Nqhq_k?WET)nD8fU#WmG#;qttEXNKc zAyKENn3dlr2dkHdx_$!0}F{Cmv*u8vm9oVve$_uPQC zP4$0k(tqpT{xui;y@_j3+LD_CG;x=!GY!AL$ZPecOCjwBlXU}8mn{WV&|)6;=5=NNz@xsrSH#inj0ljwDfrMHadGIGDEp8 zd7!u|<AfSR!PNiq(6qrtu5SQrgv5A6V9295XZDVN>h;8IKCz@K=kHs z`;5PxElXIqOb*~^2>=4aFaK}O_D2GlP}Nq#Hb(K%5wA@%rZke$R05_xXZ2bEC`JHeG`^@!w9fF<=vuuVP*_SJK z-iP#~^mGTdu8;dUn@`q#@eKYVh*C(2H$K7eBIW*w1Es;?!R5gX3_3kx>H8cCp!=kE zhB3^bvWs7SCCAALqkw+1jfx`m2%RGFh>ZO~4`K$hw1{mw5S;`oR8>@#YJAcUcUVWtnx#2OlZaN^vHJTPt1pnUcjDvmQ~ zy;MQjvs_8kpH^CZ)UCio>BnbukG@J}rBZ8$3XK)U;ab_^tIWkoC*e4UjLFG3yTz%N(3>Ft*p?2f6Sh9}A^xvJqpsNo6{$EBYkCk9D&e6#Q`b_j?cVq! zQdQ?EikZf3;^xFPlL+Ezn(>N!uC!NG77guzIoR+Kj3W}C{BTHVCJ)r8uW`gf z*CwK(hKtAh;^f~lBcMn2xV4yxEBg%1D!V5JnVri9S|(;dI=;wy1)m>g>X4N~C!bIkbWhEFv-vn54zNz zN_H+(s9*|js`}F8$ZHun6g@A<`6jM{4n?iwz(EEtQ&(!Kthp226;ZbaleC9@!Ifv+ zljgBjj&BCyzG~XW%Nx~i`g0rY?F1vYi;<{u7b~#m8q1ES86u#GVFPt@U{WIDJbk{& z2;Je_8i{Xna44k6+WEXV=LBZ%JCsc{ok$bkj8x4J@)1)#WXUN+x1#t@GiKtDvo|O? zB{sIkiipiL*}<<6RQjB;$TH$1BV^C9N;1O|Xs{lzj95=-vs1_PynO*=VB%Q&vwCfg z44AO2h!Q$Tsq$Xg$1pZXJp9*R-n@}P*O6@3egV3u#Ge3lDr}%jUa@0*ST}LGN9pe- z6+j(AJy-gv6!c>LvZ4NZw_@U3z;KP2x=vX|nFHxj8V~bV2AwKr&i6mcoEh82c3(P8 z{4Px3#a|K3v-A+@mtS1=^He)6OVA^kM~IJ$lj6htcB?R`9u$GK6vIAeHfuOC$%heU z7HObx=rvH-^IO_XrOA@&pz3r#-$wp{evJc{lm-=YDg zc?Qh%|FnerXA}B2^DzCFfdEUmSPBR{e(?ztzQLghUT3AyWOUJ;`l&4WGSU{`EP8=$ zk`BPolH5lw1?RnueH(9b__Fy*PiX4zYm8&LQrs_7qPUp(ym&ppr+>tWfN7G!#Md&^ zSnX0eOUK`gcRK*n!?cqa?fT%aumV>>5U3YPj#Q%tQ^X?XypPNgtA+PRs8&|>yPQXo z=ddHhR6SoFbseV2=h6$c&Flx0ci!%O_ivh<-ArI3+?eH&P=_QBZHVKy#E(Zm{-QDn zyir#jnHxZ3k9He!k7WaMuCeTArMK1l8Ls7tv~JBwP@kf(7Z!whvQ6k@z<-zc&Nw&z z7)%SmSMW{HoC@eA#&I*kh$De8=OIh`d2i-*Xi8RWk3TX$ROH<9UcsdXt_t!CUn@fC z(t?_4*gIze+>T4jx_`SWddJwK(hhi-Y<`Ap!36N|bta;!K!6|<#%4E47WX0sZMDj- zyKnMB9+&(*wPKUFf9%)y!}t_|5H%;TRQ&_&*+-Y!?Skqa&WW&g$gm0`;=@fD(k9GY$z>?ZJtW)2^T$-f? zY|jsDt8q!@i~YIILnZ7@*R23U>=XEt7^bOaHJOinPD$3+XkQED_r)sYy4XM$YB<0#7|F#g@cH~7Hqq+B$yKe zb8tnUlfw*ezEa~+#}s-T4sL0OEE}qOV3u)s4_tF6vp3)PtdFDl6|m4>0G|X43A?4#%rnp_8}e)DhIOi_#(zMO1T5$#s^r@Lbm9h z)Ee)%+-I1yH#g~Xx7+yqN@>I7Gb(?WM50|`?<&Y@4H9u(OCJNOGD#oK@^jT&8irbU z5xEAcLDov<%=<>(yNH2N+0VN>?lJl_+{Hq`r9sL)Le8wO( zGhgMz8nJ2R8ETPRXb1W{hi~9CK#t$tz>{rCftw&n(Iqm#&qkd)z#*_;kus4_HV;aj z_-n3bsexZT6rqT86jzK?*XMst(+in8aP|N?BOCxL-v98`=&wBRM;n=-`s+6j-aB4) z9E_hJ!isWk@i$4~wO0O$&x+!(L?qcYQk*1F3`i!sjc#)%-!MdNSocYN=<+Ps?de4OC>c)w@=QTL=WNN~80(cK=-K!k**r5-=?1Dna3 z=(Lv>l77_Ia55mN|EDoyJ5gYAwTp`HX>V+}$$B(G(oCqaKvH}Skj$V}c_<<2+-fR_ zwkY~xe}8mzqMds7xxK4T7b!vE2iqjAj_BTjYD~Gu4AnD9`n&&CxC1{g6DKz$_mJ!=RD$Osx7}|T#J<~=s94Eb+ zFz!Pv+I93w>2JVcFtEs?zRdNcE!ra+EAKuVTz3dxy(CBKe`%-Xq$P`&nVUiCYSElF zPOXrLD=n+;l`&gz=F$~3`!`{(?oo4hrXHox8wKQi$;vfIKYIR-U5L=7n=hzj#XEIn z)GVT)Q$D%3908~7c7yBMNNG(Uourq`@1EU7l*pfkxqm z8ya3w;GzHSpS__^TTZq zrCAF_x1H--bY1WN9i<(bYB!UJAX)MzKQ&jd)^*!Dy~t`}8WTu?cu#vwy^&p)RcAp{ z2Y!>Z^IP(I*~!@;RfYQn>clOUxpk?p*pruB?+X-fcMuG%h%cs!T3jy3+V??T06!pP z&74w?_D#I*E!7hQe%KB&XqJIwkLgqTj(Q49SIT(#ObeyL_tRGMa!bg}ViL+S4fb!d zhYQRlD1KFqLc3<1UWuP!B;}V*2#S=)b`!<>{RJyN(*5aCJVL5SJSw)aJ<7JZ5Zmz4 zDa$Rqoblj6vi`|&U^X>;owDPgoFaGJ2GiGOnF_I;A7hbZkN-RjK67r>*qUA#&*$?j`-H^nq?| zm{uY58zQifE@)E10PLby+T2Adn{Z8IQtgzfqqdfPo}hybY!j>yu`v(4A;;6Qv0`?f zAJw>hGC@I2U44ce#=OvOuDOwl%P3vYz&9id^I2B19QhPll*P$@t&*qE?>9OAh|dr> z`B8Np$NdPh3R#>ob(}DHz`W^{<8ssJd*UPOA`YiIce-e5kUU&B(j)J#EVJV5uoYUl z321JL*!`J3GSx2#eCVQ#hu_#u*?j`Ri(lqeJkgReHVAW(_~7#{wg_wZwhO(ni_sfl zre-K|Dj5lOpa%BnT`q(?DXYz~VHKt_WzIbvyFQhJTjpFH+evd|Q_wL5OdQ;lGWD&czC$%Z#P`9jh_l+At@FYRPBwdU+BaByBek2R1*hJ= zw)GLk6O#tgZ&M*c_U%RXMwaVGG28_<+OcrfhZuqiQ@jwFLb$@#p#lKc`!_({0cQc@ zS+?^|ZnEEY_J!)L@}hvQx>A+Wq$0%O(5r&NHE}~b*MI&EXY>0$z?GYAOA^q=c>wMN z{t4gsS1TuH;9zX;{9pS!N`HW<=Fk!oZ&iv>KU)bnu2E5|AR|FQel|><=P%Z~9+3VX z)auC9wjztY3xE3+IY1cceg(f^Q=4i53j)6FyUFF3qsfdfKlhH3sy<1&U=1;#Az}1t z!-heU*va+15gtRr5LD$v^P>z9eDL=P!Aij(+eoWX*_AsFR0HTKC^K&%DE{ZATNhjxc7FG&(g( zvaZF7%Y_}nsJ*OmyaXv&Hr8e`P3=fEcsdf!tjeRrY9+$;SRjF;Fqo3GgprigO_Hxx z^3FMOCo^EXHm#AaS86D^joV@goA%mp*N{{jr6$?dXcH1DM;C=v8*$0ysbi!n88nfi zVxVm0FcK?g&|#Y_9RqN8n8P3an=Hk8NyJ1)GGUbV$KG);nM%Qf(iT5PRTAW4cXEnC zT6bI{t!}e>pyNLLt}G574ZpZGNVs*w#BA$PM$XXq+L_(S5QtF6)yL)xQ^29~`eYE? z;?BVHi>C{=Laz#j7k3NppfIX>M8z<};N*kJ<#mO&k_&!l0W%cj|&ST%<1HXuz z2M2}+rV$Vo+EIryC&TXHSO?v})#{Jy^HRKF|J7dn_RU9_Y9w?(_jv($Hl+NADeylw zGHw6dKT1&6R+?mhnH|6WgA=086sXd6B*OMaX_^~OXc?T*mx3XthMLgf4_kcQ zq(UZHt0xk*8lzBI2rW6)P%SBaX6w64IaZVcQ*cC&lBICNG+?Ggx4;V^3m5XZsAga*xMmW6FDHD?a}R7WXK z2X`Pg4uylp7b_K+K*tFLb0Niv`)uFJ-}mEV1%+{HVPZZOPf^VCY+#fwPV}gyl+Zb; zBQiT%Y17;ia*ncVRA*aLDig+F_Po1N2NymVSA6dbL?riPS3H>F3Mst`)EScQ@|6k34LS}Fp{xDebWHY$nYrx#1Vk5;r|p;v$BR|ghYj0&b}bTgb{q<( zT7zpB+X~f+s=vFrov#tLP@~8_+wkUQk}LErdI^K0%;F z5J0gw@0_w9w^tC^V>M=N31$i>=xDpKFD^IhBl1#^oV7@ds<~XUz*jp)`C5Hi(Wj0e zU+9iaAIQjaAN&R*DG|u~-<}#z5nViS0rOo3;QQz3`j4fMKU#Jx`7ywX4+_s*zC>D~ zoTaBDakF_Hrmd!3UYaifg%kB0Nq@W%(p-OlZoxJ*y4Mc?0Elssh=YOIbdtyPaQyAb z!yVlAM-C`3F0}UDe64~QswZ+985qo>%HbBuJ5!b$OvhY6Gb%R{(>A&*+NfTy9!?4D@A)+gow=lJrK_1*#e`18y`SP;JTwn-R zUuRmsR{t(t40k+{jH`yxWrKR)s1EPs^jBS1Hr*#Wg$kR};lAK0lR4yPW3v3D{)3#R z&>QOP{044Hr8D<=JU=WkK~%qJ(KsTcIRX~p0EB60o$hosWdH(8z?=WeHx_Sy?bU?p zQ*Oa7HC9}+oQmnc&CtJJ>q-Un4p0H-s}HEK|D?hA-!TV7L`z8ljyhm!K2<&IzQL|+ z+mLx%Qej12{*ARIf^vk!gbel7obL7WI|XA%!%f2WH#p`;57WzzDjUH46&p2L-euwm?HO1TG_UFs{N4^=&$Cf%YC2f5Uu%MHGD zR*xmyWkMk(FVo(lL3pDq8PN69mrSBKG8)@E;4g0x=_>u-4tUbVMyr`Jjuk)=6a4T$ zEaOwRrw(8Yl3>oNdSp=~p-w0jXVp-KnBK2V+RtHwTJ5hdt~8hI^6j0L@f}v41+?G$ zog_wKIS|c~@b520&oQ78%n0~YmGr=52AF-*Zit1JqJ42z#FuHa=7>SgbxBVnNv(#afLNl`NxeGQ_tn5rKRvpVvto<*o`Ck6dkF@8s>%9r%B4{V zU0a~vLcxl z31L)Uh&i4vy)(_x91_#$C$HJZ>|*Ie3=+o0E9fxTIopVjCj!0}Xs?DF>kIqY-Xwz|Khd>Sttc%K)hu&(fVfgXIul%%KJemDgRIIGYvbO7 zPub#l%wGTPD&sd^k`=pI{Wph@FvLF}wUWNIk%5hikd3v2qrSDH!yjoeAb!PpO&KMy zDV0O@u#nlo95Yq%T=B3T1?6jr$Tz74J&3sIgE-|7+7c{5rR|*NQw^=_PuwVBy0!c1 z{y?qO4^30NA9#~7AFpSRe8I{{yMoT5SMO7f2TNj)aiFJNM=1sWK=1`86bys+; z6EWE5tP?B`ejRN0?JUK1>KNHWKnU5 zxnI_Vf|K=EP2GgN5?0L~n@hFAg%16afCuGxf*)oK_Rt=olDlz4tNJU^QDQR@k3v@N zl#1vAvR;n@-fEy8r-5{q*vBHVv4WtBKDzi$BeLMo=LNz)P(Q)rhY;(9j1rqN?g1xf zV1a-h$+X8zK|>z#w{QWh&=O0Yc_uqhpo!$QM)4f}ItGR{@$A-|;n32&*P&rephN?j z19AoBBt3s;xl<_%vDNeKUkBg)b_hHaH(uLw_UMJM&zb3IJ97?g zm$`c#8a6gDqS%o@bo{IhbJxVb#<~$_ftvSy&l64g)MU`Yv_LRQ(4bh8SU`?za$1OK zRQ1tDW6^|+R@NNy^Q*Nq3%Y&VY?_p0#s|fe+)DF7;gf12bsbgJuB7$bM_PSeP8g{V zUHWZ&D!NA} zeu&IrPa1S-U3tro>N?3;k=HsnwYk11qy&ac&SJz1(7;@SM{MYqmt-`2P<$tWc6fMo zLXIrsA}4GaJs-?7YmNaXC8EK=89AKl`!0Cx-8V!oA$!KxP(x+gbvRW%Q+6JB9zJ_v z+fq2XV7fa`pwnSET_9d5durWPnX4aILux%$4j!-`8GG#AHh>?{hJJZi+qT1Xj@)o< zYvOc4TidRKY#l!FT;oP=fqQq~1Z_ugykopV4rTeWb96#?4&9h;zuk09rJ(8=~mE!a2@Li$J`M}mL<CB`=vaC+L+{jix73o=Q+X0nOp&fP|P_ z9w*7+YW75$>V*%;e0el4zk40u-PHZ)BVwaBKpMs0x03ip-W6t{F@Wom?{3E+=z?xb zvbbl5nQyXnC=*T7K=WoXyUBy(K@)rZFdk{GlwB)N`TCGeht9>akBj0bn3fHPM&AhV z_216=cZqMKLmS@*oU;+&j)LzW=DQ&i+1x$3?i$KcKdE*pK$|kJI^Q(2Y^Se9X%FC73C;~)+I?ezhp~6{Y)%dFCLROW6@;ZD5&lZ<2bQMxIu;K&mPXO?ZAQpwO7WZvX8$`FC-rAOOq!O@{CS zDDeOEPVuJ${W~x(;!ku-TAH?@8!-w+6Kw#C1h&?wOfE!;zqoRJzGN=ZvoBi>s#c5T zm@`#B`^_hMD^Mi-ZZSfW2+C91QqrmMNdUuSCg#WU+Z*{0Qf^i~gTZdlbw-A;Hpz9R z+{?E*uqmpXz#TLm+)!8|N+21v&?+1epHNhGOY-=vZDA1TTQ8v3r6zxURMavex zk>XU$nSDnpyIxPQe)T2Pto+8bf7*G$5ZOF)_=^Nm5B;K7!3;W0R3+7g1nOjjaK{%BMzHB}>$7a9&EL4^yKy@0 zX13lX#7l7rCx>as>@p0&y;(_0q!vL?@6oSL2%JH0CmT~D9sEGIac;`M^E5G64<)WK z^6#|N^jp415d?0S+SpPtP=a2egG~(Oh!wOvIyGI*GETKV|8QbN87pGJOMjRHp~b9C z9;6r?#w$V{)AOB-8=6dtOm~`PVZW7*VHHS?nN)l1=@j5$;nb4ZnL(M{@62CrAY8sf zIOnv^Uq;audV?Q)?fuLIKHT}D_IQ|*zD{_wn3C=>$~Z4th$D;R6txSFvUw%+e@Z(G zfGW19fun@P1rh1)?oOo}q#NWCQqtYs-QA6Zgn)oSw}41DA|V|LD&n{IJ$)$px4u=p zg6{lwX6MYFbM~B>^cTm>N`=l>yCwo6znVP-XE_Q=rzs`{1CFH%yafBzYO2qU_3be4 z%d7BA6?e#c(odFEHkJZ8>QPSKWiANA^GIJ4U@Ut#Hta0s z7}csUlU3)sF5NmzFg{Dn*2=6~_ew3xdOuoeMYnbHjmM*<84kk6*W6f%HKz)8^IRFk z9#V8e`@P!7Ep)ScH0^G0Z$4{yX)zJe0uI46>I4s;!jX*ENRQRx+PT}OPE5E=**oy- zuWO^#Q>iu@Q$2i=i^pe-mv=a=v<6EBFne8-!2MeN7;8ynoawiIiSF;IY-qK{@ zxI|&`VuH}`OU`jSv#`a3a^5~hYuiU6yn2~RUoy+6T4~;9E2d~!RMni~DYKVW%S89G zG>^@xrD{O0J+x7yI7b=&gsH@-Dp{HNTl&#q*Be3d(8>v8WnqNQ{v6pe&-z9{6{CA` z6eI>cjv)fii_DRs24(3JBxr*i>gQtUn}jjKz$dbrrTBIxn@?G>T2ke`#}N29?u0>Q zR;AiAZ*)pM-_r?5k}V6rjC!kqdydd8kM!v3BBG`)MQ?#)g3C1S9CU5hThwYh_u+DA zQRmzn=3H>v;}oWYhit*??_uc1J|e%Lf$mV+_arZ83PF|8<&NVp_`r-ZVjnV3iuVy@ z*|4$0JHk!+Fgs+UQRvJnjxO+F22Ul{O~LE6SW2gFq)4O=`W%q|_mP^{r4WvEHLjmRNmv=jQVE5ji|@8%P_Fg_wn!ta$Q9XwD+PB;28M$F+%Ob~RJ;|146c%n2%{hy)VPf>9*lqX2ZXDgij?qV* zP>Il2X*I;Sb$?)ZffWzf`_{^&USTIRKVYdANE}324H9y<;7hXzYcqp$)ShT&4Ua?5n(G3f31->oT-fY3wIBbc1@# zF!3Jhvh9w5vTDvK1@oOkyHFp-+RLp8VuFjdp>W)^Uxa{3y;6H)BZ-=OOu85`3xR>G z6(7H~75l1jaGzYb;iXO=;pekx-=n}jh>ZF@Bn8+FN$11$`T8tPimJb@0(9~c$gSRL!Z|%9nC-};*2*Dp8G=~w{1JVrSEn-d)kcmSz zTx-Wy1fy+~O89&g;2}+*Hdi016lelRf87K=XQ<9p*_OW!taWN&xC=A^SWy+5R>ZrZChPBYRsJ35DVaRHtWg+?QKo#?3vYu7_0X{EHT=>j%qlc9l( zhitVsy*xjn=)+B+k~T7k$%gTP9SqqcSUUW%)nnjAT1`=%x6BH5S zz}6z}&@>LA@~?;wOjYrMbf*NodPY^%xt{YZJ!305C}hY8Gv@UJvufW*d4B5ST{lo? zbs$ZlTRI)c>g-UPFmKb@O6&aY8Bf;f%v{Y*M-euwoPhl-{c6QkzGYoi?j2qMoVKJL zq^8p5aFG1z&C|^?v&au8bb#xt7LiUR1RLhq_{fcSFtA(~y z`P(TSi(mCe8ep^jI2S!sF)jz`ol)&7y7ujUbH@&9h4k9y~g6pMA#Hb|c}z ztp?ec+~U?T=8okzhKk-xdYcky6a!j?aRaE&Gj-FkI^IsXwB-6ykQ*CIcH(tBGUDCc z_8>Uh+DTjXuXMa)upW6E4lQpm@db7lF0+}A{p*FMnohZ$#07OUtNc5IsJY{~vJUx4 znos;+yj`_e&^OB+k*H3mWJB~OCr!ReEcao}Y+CYt*!%UHz>Haw5LILrbbtx z9V{N9FIY}Pufs$UdxN-GT%x>Co~&i_h|N%e#AcodxqABPv#W@3@54UEGpaDY|0R%_ zWB}?@L8$gHDrjs)50%X5K<21WR7%_1?a;{4?EH6z>mL$0a`l5^U%)M_N~MPj+l|@N zE9MyL63no63&@1M9%q@0L$B;*PVMWhAYWL{E|ApnOP20cn(D=PC^O#cO2e=0f@CK# z8Mnr!%68y3NT+4x5CA^axpGfurS`zE>r{bL*E-axeuMhZOh#iK)V?-}v1I|yB7KNh z>DvH0b4@J{-{&!IVz@0EimNV=H6wfY9Cv8CD99{mRV=o)&@PGdQQm+BkTeXgmX~*= zFdxGbs9vz~6q#UdePmCin{9xlE)y{<_oX+;-njqSz4%gDU^gvxLc9VE_HLnJW56PI z$Ry8YC0nyC4ZV?^BxZ*JL*Lxq9OAAgl*AjPS(`4t6v+L&ddnE#=dr?RuU$9nM5(X^ zBOlNQYz)lII?5%Jc=+9+Xa=wHn-5)AEJ6$bpR~AGMV-@P4Go)N4Y&5KUf@w&4zN2c)k46#M#1V8WQxdF-(M>>P?d^vPLAAly za02(Pz3$!I3vM^ZzO=lpDo>`l#9n$tJu$KnnO@)-uI5=+MKzB>Gz%Smbvu_aYto-- zeq+J^rkBrq-IC}fPPa17%{|qY0m9{JtPZm2t1$Bf#MlVi`6Oq^jvb`$N2Vq25=91^ z@`~DrA$y6n6Ni5M0Kcm?i!171B6BLP(N1tMBXjxY4rv&TR-j~IVcey&ua?6?9ul%p z9M|pxjYRFQ8_DM^;{P-fMbtBSV6Gg<9)T(}0!x>Wq{Qe)>;kPg9A_?c{iE|V;dEz> zwsV822ZHwps2fnBI(ea27uj%LW>_MJCLlsu$+OgVcOJ8`eKxQ^V}P}UuLb`BCl|pI ztLUh!1z%^wJBfZ-9Lg#crNpvKCfjc2@X`nqj-ebWB5iJreg~L#2j!|Y4#k#o^hUaj zyK|BHT61wrqTM(}X_w3GYh!9}7&SE(6eze^%Q$1Z0~17CtZDar8h|a)2zJt zEd1%x{R{;kCtM}#;_AZey6Abmqu4QwIEVKr!}A!owtPD!ju*qJju)eei0>o412Ta zq=-z?w8s-e?f!&Ez?W30PlqKf$t+KNKoV-x8HMlvl1wq?Dk%{+KA65D@xerM-%g(s zzOawYjeS`RRsK~TrWz#m{YxjpEs96wY8Em{2_@2LshKyt=0*kXd7pjQw1(PUoIq7d zL0qD=Vll$54BN28AqqzANQk%#4^jq=8f1V|kijU~0XA27f?W4)gd`dJGz?DI1E`Zc zb&WI!cEYlU0=CvzAM2keO+N09xpNZc%T~_3f>YT~9Waa(2u{rHvB?%P#x~JF0uvyC zaNFVh8QWtN8)sK#-idYI*t)i zWV$Tm!F*~|%qgZ~5M!EE*@Bf}VGHEz^RF?OLX+=|PD9s<%@{n~9BpAAQfCj$i?tEf zNTExA_hD9vA38Z5+tlj$$k1e87fi#$V(;EDrkS#&J{l*aS5X;SFCOZ0(j?@sv`4lP3}q%jUNPZI$%uuKOImiA(y&%^%1j{clWvSnBT!U+&Bhe4%sL0SMm~nQ+pr1>&e16?wigKD{z;)ED(mt3{OJ!>c zqpV%7(_tsmQkWZSx1aNEV6znCv%1MRn~aD-f+J!AE*$d%as7J5Ol3~$)#;3VVX*W| zF&R{&C{h(|HeJ=U*`o4TBhoAZJHvvR^`H$^qiG&Q4GbQls0K94BtPV)Ia5kMGUZER zDtQ5uOCP*uhuL`%&0W^q?rNirg=1KgN$q-siZ>P82A;u4x+LpObuaqMlIdn0tw5(a zj#7_cuP+21;V+Xd(FVchE+L1z|H~ zLQP$+#v(?Acj_M{io)t*IP8)&;R{rU$4{KHcps_27AG5=wIV*Fk98Qh-Fi=EXqZQ? zYiMU_*4~bwRiK<$ycWC`9mI6Nv}&vqzafQ7#yCp^gH>QVMI0r9Y#+ozZNNgmcg^ecbJ*F;O%{S znK=mST8Dl~E>{@$`@BYfBW!1m+3-9ZPw)|4YM23(S)p-4w?4WEc2rwONRT4(bzDlE zBg-9ffu&^K4Wyu6oz^+lcbK1+rAL!mbU6DlsuY$VGlZ}9r_t&qGZ4aiZ`;h!i`XmF zd|ZKV??}Bg{;=*~XDvUnpWQK_`0dcta}+{W?$UmkM)cRdT%gGCN-<+D7Yr#w=~S%= zXuXsBZT)!syYR@$plS+D8y$?hVlWgF&uwmZvlFhHiZn;TRBo&bCI{gV09(P=OQPd* z?c=TON0-VHp62@?Va!y&=sVRClV-)1E*t1_pJ3~IQ;nCsm1E?xY$$djG@i!dFz_-r zNP?H}WoQ&E2mNws5Cy@|UPjq)bp-vs{r;$26sr#}1zq7DCc(~!Hj@w{^_sK=^gtgH z+q=B9e&7@Q7cos~$AJwMCrhwi$eeD&%U2XOH!0!dLwYGeO7jyB@8jIQ5}|OyaRuG52*NoQ1vG6N|@hc)qQ*d5;k+ zG-Q-9eM~F7-`G?JCDqh$ln}JOD@awLM^%x&icD^}F~F$dW`mV`bOYBZ=Z0T!rAW8> zN>RN$LC!v%+jY5{Bd1P8TBI$(lJEJ1-9zH)gOi}wub2*24{+5PnbWB|m5nr+MGz;P z`ZorpqgMulA{zr|u&EfN8LQ0=PQU6XK-_*n`GG?Q;PzdA@Aelb7Io@%F}3NiV40BA zXc+GMgdm3WiIvg_`KGKtG2^QbH?H52A334C;d51xG}&|+`b4(NrO1bTn0;K%=~=4% zvy6AE9ww`6yVf_MWS3oG6er!VnXk3ygr-Xd*!DlQyuOVMGL-Bu2n83^C5>MrqZDVk z)7IMPivX<*x5KKz*OIX(rm$~BjNMh^YCW?wa~e-Ypnf3TS*r5UBlJooJuePLeRAXS zX7_$AXuE6$F4Q)tG>DF?k$$BmnhHtXR15R5-pw^>^+C3fl~3fRC_8&}LE~aof>$i} z@5OZ$a5`F1XL&dD(WUdNW5N~T`cwevc>k#|Rue;qZLjdv_n-j{ zAo-!lEl(fNBW;ghKI6)v91rP61;>8a?Nq%gX(>2unMbuB#}6%!L8c%4?@8A)&8v&$ zum@lf!Dujz99AX$NQviY>89nSlB&4ZIHZJUEE6=7N` zQ7!zAOeyUdX8A>)ALA*?)z;js>rQUGi zitLzy;Y9xI^thlz=K{7m8sr3U2}&ieWa;JnPvS$g$t`LTR(>kU9Bt$HqXGOoy@$bB zrXFjXv$rRYUU!y=F_EH&Z@9z96v#+18HSt@9>pEAntf!qCc%*_JbtP~<_=$oGF*f7 zkwr&n!FEvwaa5-G>d1Q>=>7U#Q*TcwGQxQY@;4Z{o+$;}z?9=S32!a^M_7UlvZ25^ zt$Ht9GI$Cvk~@pEh0WqYYC7G>vm}EBF{YM=rx(^2>4`j7$^?AA`|o}JznR3(lj;BX ze7{_4|39Jra^B^6!GJV0^cc}ka?K*m8h18#Fe9x2Uxy;M3k6(%$SA@DQ8K2e$f^c# zc%HTN-2oo9e`gZ$hsZ+OR79RcVZ%Ah3X(>o<63Q)b_!zqOH(?6NDAxf4zR~r{1gc= zyVz&PnM;>Y{Hxn#5N@3qtH1Ya?OUC*47WavuB^lF?Ki%svwtAA!&jrv5Vn}e)DrQ? z4WGGv7#E%3CQBEyCYM8`4Bwg;H-b){nUo8y*LbY_UJRJ+-AyRu1A=LN(=2C8=?A^{ zByr>WUOdL!JDoAs(_4EOH_Y6LsvGkz?w)>pTc?u`9 zzh=E3P)e$X9k^pADl?7lHgaLdJ5M_OD78k7=&Pso%;EryirnwY4Z8?r3RGS*{i#q;;3^P;S2(!0KeLC2`l1UY6{uMHa` z<_)`(SBiv-QASP?KJF)3n|gXeG5q87u(_V=fYWcIc9Bd2PLE@`k)MdvtXg;DAEzfR zT0(5qr4V7OT;ipa9POphn#2Bje;_2#3yT@PNcsN-`v0a6KLT%%fcK;C8sBo?t?-~e zvDHs}e)&pBiBAr(v>XCrhv=u7`HF4u)IDc)_Q$}C2y{%3#J?|)GAv2Rhf!ZsG_dgj zX_aT*4esndx#@K&rK2BvnR$#khs;-qpdmJFIVz}`cbWyeGbYZApuLBh;U1~DLxh2B zaG)*FyD4IyHDPvg;dvFFv)n7l9P!H1dS-d%G@Ob766niRJexb9)iEp79aYPTI)_;6 zf+@HpBiUp#o9+B%iWfa^=rszD*6fYF=TUE3;`f*Qbjxpb5#9+euDS%9?6o8VC5uS3&$8WYD_noG~33&;skD%!c}y_8@`C zORPs$`vg{r4|zq_mD@VYw4D-6+#bGLB8z2Aa4c)`B7IO$Zu-YQ;Cp>(5XX${Ll>DE=D-5^8hlZ zr&ag^H|v)CvjRyHf}jzHGxqq}b`x2N6(z%H_R&<8gFN_&XsJ8v(%FM@Qu(>+6FAet zut1Q%R0#(O@>uyMi86Y0;#@-JJ-A?tC^KPT7&0YtE5myYZx-oanS-%`JW(<0o_}lZR+g~#eg#>L4QaP`;WiB z1pJ-%kp2TQ!Bo@Qb1Mezn~^M{O5QQUHkr@oh(+kEmN9o?i>RHY%1bM`VMMUpF zP*6|Eex01@$Nl+#r$8QPAb6mU;e7HoayD^tx(p_XSz;A_njwlpW~D$(@>G~n@-hrM zK~4Pq`%}8di+gR_HH~xr#?I=9jy)X0+};aJJkAZx=q=_SE9|I6q*;*=r?? z#&UC#>u~kauIIw1PoEyUT4#BG=q(Jsc0UE*6}L@cwRe z{O&a){>nuJhyHa%1^?@a(fl+orAFTNxk%S#C-2vpJ|SMh!MUxnN8zD4dy{U74)I`5 z74~-fp?r%^mCQ*{gf(LSf)X>*Lef6Lh?jcTF}l}TUc_$yiSN3bRM*wc1R|-EYClqb z+Lu&$pNwH|cG4}cnHWT7H}4^xDs@$f4&7tbi{G<=ebQ=;LEpLC5wak2;_qjCl7GGG zAR+DwgsHT4>|n&%RRh(1=#yesFC5J4A*Ydm~!( zjuukISU-4-eic&1Qa|oQsXB!`yU=G8MjTaIGBy)CW0ZZ0AuwDpX66UzwE%KG)#D+jW7BXzQD$MOO z4dZF(swovj`zbk=N0zrT39ZJR;XrqS$HY@CHLdGrS+&}32;31>4j+`geb;9Yhd})9 zWGQm{ErM&wV31mx1AUxnsHhCjRdU{F=0Up!JT_kSO%{O%C+KhKA34u)>*AV+dh*=P zGe*-Dj6tKj#))3Ena_#7LA^j=oWGfl`x?(_hjUuI7W*E!3`0xiMTm=MDJtcmoydol zmmrI61!a77Mo_V60a%9*&+elFh6)l-fY+uSKQxj9~=MO~7%2o>B zlnW(tDliB|0_9wZ;PKGLN>y$l2Rz)G67H`JEML&jembO2f3&$2h=7Hhh-;P~_Q58E z+4^J&Ke019tleN8KItPuU|FL`1U>JgtD#3qw&5wv6os>13Gl8Qv69qr)~|hS2XDa* zgf>bQQktvT(#^M1n31!x8fxATf$AE@1L4NL_92W!yp5t1E}E`7Ev(HmqH11fxup$v z3Dqtb<~eF~;7gXodS(Cn_AIS9ER50idl5vsq$##GnNgkjwA(o{?-*aq6LL((wG&k! ziKQg;o02VbGW1AcS={2Z4edU?R5>xCD`(JxU%k>9|J0bc+L_R5?G19C-cn5r86W4!HI>7_UY15s8WPQ zk+iK=@pHXti5?s+7S-8E_s06Nx@;8GRZx%&cK=$+8?OxMZlwk>9d%~kwur7W6BIZJ(7}iIu{3zf8(4ID_m;i*nQtT~BSKHuB49Dz9!is< zZ6;Zg_m)uhMw@?XJtSE(JVZ#zA})3`>>O&dDn2hMcHE&^GIAmxFS}Y3OM+-?w;;-O z__h;0dy*%S>m6VFCI1HoE%Z)n zmOf|bc%aPccVk3EgPYU_fysb|YqDQEaJ5k!rh=<=L(j|r5o@;7(8N@sk|Nx+w?_tr zg_+^dJ&T#Hf;csB!XRy!J4#E9xu$nZWuL{O#(kL2K$Sete!oZV)u)_ZRl`yo*mi83 zMPJSLv!(7?*G3FUd?$VQ@z~wpq{P+gbF)39S&F{VmfNeu9C2+bojQKp%RsaPQOU5l zsK#9XhMepYS(B@MEbc^)lr?D589jP=R;BpL8)JB{ad{lxtxKfge(n--ca9M&1s18-eSBb*kd>u1tYif4$tT zu(YtLsi@RUE}R>BKmHO%19QH_#sf`{ZFua~s7Xe)a1Dwn1d(1YWxd=6>X*IIRO>eu zP)p*9+br`I(pj#G59|bWC<>ZI2k^!VGU2hcB;n|7m5vk+;1riR4O_5mJE1?ezHZ0% z%%iW!Q~26uIpJu+fVF|R`({cg?t_iIQKY8Z$@KzVmnzps5yQ<#k1wCQ!2-xDmRwi5;-^@L>K!W&gK zrVGRFOC!jJiEH{*^^x@Tb+zu|Bg;Bl?Ov!T`j_u4BI@)`s9*9h3D)*7T(wC2MAY%% zbcOeE-%As6w{UmCPAcXl1S0{seThawE^_g?K>Eo>%bS;Gz|>aFGq#tN%(#piYVo8! zQ)kAI-!SWR77Fz2w8gO=cc^q1Fb*)-uNCl8I)m*}XDpq1@(iW1YBs0Lt)>uUXAZEd z(jqG+qe?v-$*TBZmENYOkSrBtWN^jkQY_t};`2g6p}^O9x`S}V1U>mk-%Xe4Kr2wU z;_->CQKF+^7baZ^!m@ugM{t|X0qfHl6^S=1)yArm(S}9cLWH6Gc1(93BsH2i8_HjO zcEHV1%=ZM(WWts&dFFb`n(dzBrQ7azJy@RLA<=Gm*rFW2u!wp|%^!BpUGc@Vzd>|D zON^_i6qmF>SW$+srdlA~L?O)_wLAQ~w77n1#-oxnmGV*n>YJhVRQmmqjIUq2r>-%e z9qOcQqOY2$mOLz5WnFt^ROoHi)OOhiTX0 zNVNKtXA4G3HsIP|c z5%PfFNo+Sc2~XjzZrxW{2uFJOsK;R87OW(3SX3`F_a^T_auc5DCbc71SQ5sg>Ib5= ziMA@`WrTOpO7LYAoVeKr z9tMz2fyM32+{lk#`gUYQ+vBq~2+_s$!@;W!5l@P4+w8LYo4?G#x5`$CExnb5_EKl3 znkvzdJaLpU!EoZPz`7(&F-qje)>mZrqWSRs*^ctj2xUu`Ncabyv~AvyOzGK<6$Kk* z_rB~Yi;tHR5b1-*l@pOU*gwN@fOD4Igzvo~e8+A_=2z4IjOm zirgzxc1F!)B41HmR1p;x@@A}9wZTH;W8&Ag35|wDM{d`wUk&smYM5mZxNk~Fr|wc+ z)4YDylR3sxysON6IAY6ib(hS@iGahp=>)4st6kj_Khu5^fzndH3?X~*)6o7Kw{dC0 z@+Zcj^*kdxl66e_Gz@yBOO7t328H6Z{S)om4t1k;RtR_-d5P1Xz8X$Jwm&Ak!4A0( z?EB~krVxIWn?l*qz~0c#?4SKL3dVM3)<*vhr7RV7W$=JPzwvV(jdSa1)6->RDWa1d z!-bC@B>7EVMVt=73s@Avj<@8E8wi?{B05#YOi1zrw%e#|5O9@JG%>rc4e&Vc2sm4v zoj!)U9~c(Mj%gCp&i2tgt6d4R|GiCT8&dh5>aIn0Jj*ApC-3@;-*$H`oyo`N-goYM zVqZ06Jani^Ze*QK@&vC>!?rZa#T%Vj!%{fbZ3yEMRWCNp$ng=vhW)mn({;{6HObt0 zmV`O8Ird6ImXv02a?O@r&S^6mY=hgA5V5qY@vJ-;lyj`~vK^n?@3REwA>+B?)%LKd z%tkiqotnIqtFF72{JPV20iitZVRJezRk4u7l#5@nX88UsC3K3dg! z;povRToN-h5-1wJP`_RPz-xw!TY}ptS_|)gj^wfnP z^O>>{`sf3sf@j@FZT@vExn_EXZt-cOM_R1|IwW(fB|2T!sqa~zIHs?B66TcZxPAVD z46?EY##vi*2=D-LVB-eeFLwT@f$y3bfgP+r?|gNP`zP~Q_mfe~9qQoAn47owWBNwA zbe8?ECZQ(o>F1p&2>V&jY`)T&-O;Ud7yWqa^oqxEpt#i0+|4lN3C?R9)xA4+@0i?Z z{*d|3{cMxV$HY{1DaUFlDzI5KL2)SAopWdHtq=wUe?~Vg;gk(SxuyoyfRzlxAWH6* zB>@_3vn><(EdMp>w*d&phPBx=Vv|SngyGkh=yJ(Ec^-3vWIx0e8!DCNJ4I=%V_)-d zql$wbQ611~>6qT6T1Cp`@HG~v_2yCwGh-c}fLh~aecon7)2@eUV%1P|yFod`{=N@h zz#DG~Yiu-xH?)y8UL}L`Ys9j4l(}9UpByPwnPdt0yWqgR+a}O2Ur#p94Uf^s`@?0e zaNX6~!@rv=rE$hYi;px6zx5XQ^1+iBu9CQKt{n{?%@`P8y)(v$_s*6pCZ? z-V3^ug#?R(70y=9{YkMB*HK^mY9-N?%P+#nsWj}EOR0{ZU1>~N+v%4`rMd^&A)9BY zrl@BdBw6pKe%iI%lSy<_BJcy6M*gaEc~-82L(stpYT`J}og@=io>w^OJRDU@Y9tx7!?uixrY%K7fI)E>9$p8L)6!=11 z?7vgxgyf~f#gtVT<;2e=KtXB5C<=Z#1M>L=4Fv^(?*KnBw6(akAFD|eDi)GNf>Nz{J&BMo!bc>3ew;S$qdB%0eNqM z1`l~7{^cm}#RGowm7V@o_p9p#c4IpjTRDhYJ6qkg1{+<-hJTS0lzxA-0^n!@Qoq&a z2JnLB&pH1-Y%WqLaR=Nq0ee{jG9j70zZ?a=LRbEf@;T4szlG{rL+Nr70^DJzA%8#0$)a8LiA@Caj@B4M>}H$ zJ8R1?`Fk$v&`yuFkP7I)0NgD|N8rm*;5&}`bKw%k4yr(^Dyd)j1FQPZ&K@8(2B1JP zEqpl&d|T1}jPSjAAg$-vZ~*!ZAlm^jk@Oegz-NK)B_IU-%sgLd@Yx5x`pI{tdcL+C zgli!GD8du)Kp(&ZAv+(x90k65n19IqUNK06Ewc@La{@?J1C)bgIQeoE_|oJ29PoJs zi;xl6#^FmQv9B*5B0V9#<(edL(^-J#@~w0`+~1P^IVJFquRTwXzZ2rK;RydOSUkUW zc*37c|8{$^IM@*AcDVi+A~xfiTLegk0SxqQq-iAm6Nr+riJ85_k0_A)yh_Q30t4LV zJ-}GsHorQuUr-#3Ey<;TKh7(Ce|;Aq!b?%28zFhP!h!Y8lwXVld=~h^0^k13c_DIt zY?fok;>IU{Ns<7$-#Vr~kc{Y8GDX1-;9o>|7t7+SU_iJ%aM6&wFkg-WUmM^t{|A8Y zuODI)=SaG2LcrkFfd4>hZGAZke2D@3|DO2I?HOXEuN3-U!5l)Z-URcn0!noO9{X*W zDgbo+6YBR`LE2l%>nbi+AYQ8geFaF)-!Dgj?=y;1J*Tm-H3> zj`U-VHsA3YzXF=82Eh7Oqn9eb=6oA2|92#VtRi1~az#xQuw4j1`ZnZ~s{fjFaohey zS5OoR6{G+fPzDec0C)TEDDdsn{I&GYy;$SVq31X5&vFGr;UJ*R>~}nA-TxQw^8&bw z`ejryCbR&SLk9*Kg8yj;XUI7_h+~bULmItg^2{f*68&<6GpV0mdgugH+ z1h|-YD;2UGZ3!^vw?IL&{~z!N_JjaWXotvFfL5FYWVjak#-7%9|6joG4GM`=Z4_?^ z+yMPX0Hp9YxEj0P$9*mW^b31Jc*rD98Y#fd^8+HI;5WPo$NvxS$J%LtUMzM3K|}y> z8P0ELexARkeQtCY7Cv8W0KG(uu~Pse{5wXv_a8HU;1m$6*}n;Rumv#Ezhm_H{xRe8 zs=kYQ>~Hvjw*dwuC)u~Qf)Ds(#^*&;7a6sQDol{Uw(@rjsh~e*eBQ!#k%8!rXnq5b zFAqGRz8$)mhWshxd#{2t(OGIO`glN#H$c0AY&-gL6!@Nm{|@2b;PVr=f;p9Q{)K=MJu^ZA?a<|QBMXWXA(l z0OknzqW=b$p7C?suX;3dn7EVSfFcNhBGlhVPRRVFWT0BkkFWP)<@Yaw3w({Ue=haw zI`9{R!Nn@$=TdLw{#@#Li}|MJ_(j0Q^3~@6_542qd~X%V@atlM<#X}qg}*EQ^Pum7 zLO&byU96saPOT{ZGwSzRL7KwFnxp6PC(3?L{J|JN|=V(H3r&Pn;-aK6_Da*r-n zQ#_~iSN_7p$IL(YEzEf_mLJ_S2;yR; zr*nj7!=EDltNZkItA#)=RvJ2o7(D!A$d65Ru~N-BBCz=n5#IwKZg;Vc!Z|>)^>+c^ zR#o`gN)W=uZ20GdmG++#zR8XMwOB~&znBL6T&!^Suf%?p6#SytiKtP-`!|^XF+RPR80ag^!rx&2 z#}M^mYL#i+=FKdZz5 diff --git a/library/libs/in-app-purchasing-2.0.0.jar b/library/libs/in-app-purchasing-2.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..34b5dd6e4d6ffbb6505030e01ca66e99c54eb1f2 GIT binary patch literal 99716 zcmbrlV~}Lq)&*L2b=kJfF59+k+eVhF%eHOXHo9!Hi(RgIeeOBmi}&N*8#m(SjvbK^ z`6F}9HP;w(j2v4*8Wao_218*3iq|j={v#&C=ME!P3xyLBZ9@*uv1+(#~AQ(%Hq-&eVzC z*v8P=IXP}x3RDno^sC%jUAq@i#3$IxvCIR}3r#*WQt6i?`w~?r%j8TGB*C;WK^_8` z9EM;Aor<%BP=bC>iheFEP%EdbV_Jc5+XZ|x4JyX_Ue$z&T@&SCF|`hPyH|w{`{G_8 zD1Q_~_9JtfAK8PuL8DY}V=CM6r~Oph6leI?Y+HS*mGSUoE?`6sFalYxv?b#?6EYJO zMdiubj_9?4V?Pkmh*sm&g2LvN8Tl;)*1WYV+yVa}h5)g~FF1z8I?q!yQ^3NIOj3wb zkee%n{JPM#;4*EAUf{oqx2&iNz4xay2f+WScy#|o@sv%S{wnD|wNvqT zMiIsIYoU{M;$26sUxjg^3rTCdeSg!%gSI- z`v6Siyodf;W4o163#Id1OvynG9M$biF-#+nvVLaUTj$;{RcKNXbe=_iqVgZHP4#x& zPz-!HN5z+&Rwv#xUzI{@SBLe{Y^>ya@tD}2J8(|MGHS$wSSIj{QbIG00wW$fbxB`x ztaRTwyAyErx8O~(i+RVq1AYFoZb9piFS=ka>%*Ie(M7X;UC(13^ejSYl@qm}p5JAh{`)D&>_ZhU(&^n|#xSEGJ7n@SUa0~-6Dh9dQyUEE zgfStG;;+=9vcALe}%oltCf?{o0brtvFmE`7pYRQ6PLnOK8w$E(a zx}24x1_BCv)UFrlx|7mVe*Mv^Kj<@|)cb*&4{ZBUe|PkE@75 z;t|gd{wB@(K&JQndp*J4NL*y;E6ryp^=Go5`*zNYQR{yBp#}~?&%hAUOi$1}Lzh57 zCF)xA@3v2dzYaE+-{^=RC=iez!2%!d7g{{GIGuLMfDi6maOA>(u3k|1@*;fXD zh7E4BB?^^kSWf7nD3C(vrP=d;Z|8gGLz9ST zKYLXgfBr6P)##16`i)1BzXwHBP$jMcvCMZ5#*>yE22NPDd`PcS5n~>>_Dy8!@nZJ0 z#{v*&6=`SHR3HhbMrR>fvzpAsq8-)#wg->UnVbO0pvTNufacxViUz*v?8)+}`~&Qs z5p|+4glo{|OYwJw0i_OyGhl%&NsZPw?2vii3_(;UF=h_KyGyVqe^Srpt!vE$v*+P5 ztBp9?G!Jv+i-{NTAlhlk<9Ndzsj3wQW(zl1O(L^<3#3Xl>1THA>pngpa8A3G2+BaVOW1a&*0|d2?i%-RWd3$BjB|td9`5 z;~3MH$YM}Ns}fEv&%H_n1Y=w}YXu@28b(gvg2qh>=uD>}21h^cdRd)VhcL76{a>9a zbgro@00IPr1@@mhlls0#rie*EHDzrj6WeU;Xyi> zR8M?RADu+tCj#~WbBf}i*hdCz52g_G2_!X-6hPbeV+Gs=S^*TwK%g%#U=ldVm@)vS zzj8{jH2@!2eL@i+2fo-=v5TjJ;-K3X63~iDYoOF83m%oGTt_M}SQnrP?kEEtPAy>| z78s}qK8dLwHv$iiK7zW6nL4+DKE37AWK)6kz#B@M^|h`Qd%%9 zFTN;gO=v6XT~JOnEme_7W$VgjWerU->E0~9y%kl9*`dsc>)cS1eCQx)zS0$iFtk6K z9g9jkNQI#%yP;p-+UU@vFgTQvJ^{_Y&%tn0^ynRR6Z?k88~8O5-!87{ts7Mu9baP! z{ZZ&V7#bLjL2Bq?XJ_J56h+z3QBf*0>7qekTKEERUy#E@#x7kN`8oF$*vXe;< z(nWG+{kV-z8J|tj5J4VU6Vl4FL~A~)86?)h;)Pqw72uP>*spR?`0 zqQgNJ5_J?Xk5*U~vG2D({4um_;>hpi7&#Ubp4~D&W_D%6Y03Gm5r@56RlL!Kq-#o$ zIfYE+ZY!UJPVl&|D&_dF!kCRB|9Z$;Kxn%EdHRP735it&kpNi@dZfZAvXIyz;AmQ?VU9#Us0#oW(&!^$~yHfCAG@hbGRd=m9`pAQfGCD z`!c&Y*+QCR7U?qr9{NeA)u=8(A4qS-^Ze=N26B5Wv+NC6nCNR^YsdNO<{Nen@iR!V z^sHiUXa~GXSHRSa^`(d#$8MCETc>`)do&-a-#o;bC8D~{h2x4%@Q+oYKtpCN#ld)p zEOAKsU1F~CLL4UMpquM_VX@FJKy*d#o+%6e}~XkP43nh zJ!H!~?$tNR4GT@mHKFFYlj0*3r{x(DH*dYj_J{2d{IatBS|sC#O%bwzSjXvl-^alz zbjG16U9+Ftq~4gWm_*)L^*Y6$)(!3uUJL>aKDTP9LF6NAsLRFoBx`8j$XXT-Nelub6Oo40@T;h5 z=@8kP8*ZANi0G$V8o59S7FH3jGpv)6v+}(ZvNZGkeUQ>3OO$jZ^`pQ978YkP%i=w< zv{aN57FJu(y>wK9@)omm0IpbGk&u&Ljh~U7k*Z&gpOlrRnNx+InNgshnVzJTqMlK# zA2LZWPzUH*fEHF7ra(Y^XnbIBAl8^U=v^A7MgXlFV-(uIWZdHf&Pd-M&`0}^O60$w z1;f8R0oJH5r&uxvkKi4YWXejRYYdAp|{^SwZq!VZYR>0v$78 zE)7=48{B--i7@)<{kB3WFvT#L;ntvj=lP>t4*(5X0$AXJ;q(nWY*5?| zumH6-1A07@d6mx)xBv_+J;ht)HaPs?S;k?;X@~ed=0SBVJu{EwJxFT5*b_{8rmnnw z-847`79aUxZx7|+u;~)6{8XXg1k@>!?0?r!>Jt39CDwuQK*ei%=EnEv@jGbD zQ*}T3M5&9^P8;Pi|mp$ z_sIHI{dqzARt`Z$zJ6wXjt0Ev*7BzAqSsYh%gO?OFCa_mO$GkNe6>utd2?(f2E>O{H0q35(|GZp&16t>F{;K_XGt z8pdB`kq<_St4)teDWA?NVi#9)uhb*2)JZ#mt|~zXrc33mExoGd;R%QS3p-pr9axHm zld!)`>MhdS1G>mkv?OwvU`3^i9zoV7njp^%#Fz$a`I1eFKVNCPRTAd3;ve*94>DdLF8vGPHG8a{EjL2tTvpx=87 zanFyh!;^I}K8b1*v6cx*@je?*!PzG$ye}k6j0vnuywN?hF1yyR#zr94jxJ4AGqJ23 zva2mH1?CnjoNkox_2QK%K>le+=@GEN6X|{$GgTnyQ#`krNov&W%u#0#+$m;$E1;6z zf0Dq$$rU_1OVnc{R1z_?5R&wc7?}j6W{(qNmo?m-J7W2It-VShzb#}=mgE};{R2sS zlqwHDvKIr?1JjZx2u=rOKrNE!y9l&(geT8xfJkT| zr6y=86lC5$>&Fxqiph)5>6uHKsW@?m<+reu7aTKxNvGAtaFby}oMaeAk;Bq*m6-() z`MJ+%Fgg9gyw$T_i|=~BuS8VHN`c+tOB0a+iT55F18BH;{UpervA=$rSm<1VHhC2=+*!CW^Ae^ngA{Qp2}byy`*83Ml!?QysY$ z{t_ERdgpX__Uh|f5z-=nv9zrvc6s8Ft0HXb5=PILRpflYX~S&uqH-|>m~5w}313P{ zD58{RM2o4}X~vnm2c=!v4xD4(L;1db@XFHY6f{2bT#vv?>B^@V;v#*GFVE+WIiz9=QS zS_r+z4SM52LWV4>Y~-^Az7G(A)Km#9KDAQj7%XS9vxrej)z#FRpV!0&FJJg~mmvZ+&IKKPk+`lcl~ zNY)9#o~JfpT?*k4?wbW$?zVz%1XXLeXM7d)$Mi9+Mnqn7{8iyKm*Sake`dRcKhpo- z&pDKTrl9|SoBscm{YDJ`OugBv+UnY(sGooh=)Jjp%RnJ}0a*B=LjkHX9W6^3TdOvS z02n<7C+3jAtSAbK0KONF2ZZOP<{@c;&Xn4kqfY+5uRWzFQYIJR>h-WoUhm@#=N{km zOZ~6+GjkyK-OwA#kWnHYXhSkE{DBmJD!e>fSM8oKJb~r2#vlM7&nf}Gf$he6cs`DG z`4rE9Js(|$T!JiYXyL&UjrclfF{FVh+lB*(mB2h<%oCxFzQ=?(yO8$I`L8AG3nPW4(K1B;fC5} zEw|!HgHyz{Xe4_|mRXKEg4Qe~nnRUJrA|v7ZDqlAES7|Et5OUw_|N;|oWs1k<9D>XB1OF`?O z>Y?~hD#k~j;Myw!J0cWLUWBSU>=>#@S2NAIAIhO8_tu1{2r|@Co0z$=pn>XgT-@B) zh)RALMqRElU+1w$c8XKqR$(LPS6GOhTmm?%VsZY0B?~wKuD4f?Bdi*u#o#I{WX=XGW=0Zw=~pban#ZNy85Hp^2j zttlf*bqSgo9oYp|@kMrb*^DO3%s3$}#-WD5kT`ZSy0h$@v-s`Now@tW<%uwxXu{8r zhv})zU(-|G_uJaD$zM6WPzF?=XZ?%#JO}-a_&g{5H2Cj7gLk}dAQ3(f`}KLBGo$Ut zPE#DkMl`W%$4}$q6Xt0OVm9;#WADw6GsQDhK!#g)~N^3 z8{`Inil&9~f+WCnIJ5@5f%D|=hG6C(4x)Hy^c{fYpx&T?Qw(2LdI34Tf>3;&#LAs&8R=M$= ze*^wT^_+kDA<_5a))kz;KNYQuV%L}H9sXMN#(cAMm%&4(f5-7hkpFI3$k7p+ED;N~ zLg6{mB2<|er``EpUrrnL^0$oJ839^5F=I7@$}QGPzxX#VbsDAHp5SPknIa)ph5Jl5t{@>zYL~P_ zerMu=ss@L#2Kz%w#@kp}=|{*F7%f)BEWCOQVQ)N%NmuR#67xIzTZ_|}=!u?lbijF5 z+8~+jneDn$nHra@Q|bmx4OtXAN-E40MdZ&$cGP+Jt%_uYx}Rrl7aD`4GIrsk(;k)* zF>&*XR@i4iW%S9rSMmoTA&$hg*)p1>vDAiJ67A@FXVGA(Fsy65(-9j)?GqUojkSh$ zcA=b*88>~a{c~b>YAG!r4@pP0uoQhcwAdUG_5$Z%0-Uz5}H(h zSaxC4GqW8Sv-Hc$FEurN!tQs&q+%P=ug>U83=|>Ji*M$Km~tqMlhL+cC-~@c%{*xe zO@~$NB3tq6s7R&xP8(Ryh87(O?YmBOLX5{sT~%~d`HPY!oxCDpWpf>R_r)RS4=bZO zkgyTM2z4y38m2i`%gH*(tqf_6@kylE?1^-i69<+-1Qa% z_q);M)D6~zGhyVy{QBo}smRTCgwiyvzj0wn=MU_FnnhXS(DC|d1){x80 zRQja1tqG8=7m@#FCifg7T8_wMnb1|7zZY8;{>Y(bjuGsX!(y$VznVQ?z*hS$wCF$_ z_|^9NsZ#pjx2ZX$9M~#qDSrs@C}-W`CYm%d>;b6YscZ=M8^W>NB^$yWjY+zt@s_F2 zZS=`TO?WMC569|#gm~yyj@t-E_HWtJqbLw$JtKYu{e!e0u${9M!CQCB2CcuoU%j>X zfVg(tQ0+6t&J2KaY<%-)K!0QCYpukP_F~-mbso5*zZT5wZwf=BRCd;H^EH4!b*uhp zK%dSsZgbkt^JSfOa*u4DE8Ganed!oKdlbFNtFfEvr9huokf|-{5w9_QLZmQ0E&U>v zH7mX=BqNA+Y=lIONJ!sr#pjCSh~j2|Wj6{_rV+ubF^u+1?+lkFJb1S{@oBj~RQkqo zv%b$ZHmaq)Cn#>%Qm8;!Me}QBeL z-TTE6lJv#mEtJZx%`W%><5@_CN+|;k2R9|&lbx39tVv8LCEp?CPbQG?A1F=T9k2Cf zwqrcIPP8^hfx2>u?ttt}=dOoJ(`gWx@f+bYL*Bm>`^gGLMo_mSqAituP+)IF z>cJ4kM zMWEKdt+)+(u-}(=Hm&tKpn7B6!UAXYyA)|`ygISd%!w& z*=~>zJ~G&(Yh0q6o*QZd#*p_K;=bvGy{ZHiUe-v`&e-A`4%DR721*p7>blHA3`Lt-L zDs?TZK@E!f(KfF^Ovs8TmZUs#sg8IJTedBYm>;TsJcz7Jd~b!(?$RVJ)-sUqo z-m}wd>H59BA3!%i?4!1^T;jpT*$3am6ODt76A$P|yFttt^+oWLiDAl;6he|iz)>d4 zGRpQ!BGA!gZ44v&En2o2momLa_eEBFpm@ATF@pQuXhIpQzNEdEc~ffs1gi)E;$K8^ zny*7=JaPwDO967Fjazc^p5Isw@mACW7i^huR7i8o!vbQ-pq4Cm5d&A zylivxVbw)AgPS=phm&n9$%^7GzUhR**%~xEenhXNHx9UtN^ZpgCRGDHcOdx(;a=!Z zgPcxB%E?fS3A)aMIdoq8#+a+(cOvZzY6bR{0+W=8J-pz>lM0>DMxBds+j7Ep^ts_f zaPn3S4g&~)i9>s{v|h%?bj&UiW2^~dG38FbD#zSdPkcBmuhc|gSmBTiRk4A2p-$Q-XbJBFCP-q%N+wP2j09k}RVzR`k`$n$qC|;V%Q= zE?|Xj|BnH1|0k95KlY8;-+hy;Z2foN2({{>3sMHbv>g!@^FZ8&3djI4VN=Hm$=pPx zUw?CT=-i0bmUx%-Q;_MC*hjb#$NEJdlNJ<7a6FaGeUioP?SB0Bqjm>q%7`f@o^=F% zlf_}D4;0iH0^c-FL>_E~*+R5dQjt9f2k9X*snd=))DC5Y>Os_DaljT%EUqyQvXv39 zAC8=MGthRA9ze?z?RLUsU|Brz#r>?22lR)IpR6* zr0ZF;RCLuIE^p_Iama8WlVY5js1!fcH65qt!gEx4!z64~kJMBJR#Q~HtYlgE;ANaG zll%5tsoKo`XY;*S7YU^I#drpwD;TuxZn0J>en>lPGnX(FZoM7H=N0suaL!4?pRvI zKY?Fda~^#PyC|~b@}Cee0(&UnB+l+gN-^`LX4ZR^U~)x zwK&B_DPT?$p_Lt(g*k(@;jR3G*PX(r!pO}a_bx@JTv%9eyuWdzd3xzfW>gEOp(N@% z96?=~moml^;)-Zr^1N{*FIQocFGCGKK;TqhO^9>*QZTy3&TDcGEYpiI3LZe|5@gCv z#E+cTip7ke1^z}Mb?-nPwRnSTY&WzGQD&u3c!vI~KYN;IVx<0i@=| zG9_jD#|Bi>Rmb_0gM?6Gr;RemBTTl^%7+KePe0WSVfi&PRqOBnMG|2B zyb$%B!G(cOt*(l&yicL7z(6&yoUmAcvJSNf%>=cIN<;l7J_sIMzEUw1ADV+~Ght3Y zCUuT1UwatqK@w9TpRi~ksuH=Tyley>%@tEc@rN9=AsXM>eZKmDE3`1%&8{qTD_IxW zUMv7B+A?kQ4I5uNoQT*^YM?0ufpw@@66A?g#%i~XVNVxc+M{R>8bHRgWMHfk9E=2C zbICinfz8*?jm0lhLIO6O-9;lvvjwjOl10N|DsTI@K_E=2GBdMIgBACD7h;Zu+BN57 z;X{pxWwJmCnxIv`+%zofHLt>He3t}pU!$et3)j6HnOe}C_)|{G$kovsLGdL^4!7}^ zj>=>h4HC@&=q3+42)Xr{PT;JT3gBMah(4?eYw-26mlnMpd7K;XS>H-L>#3_;_gbII zZaZ--`TF`S_R(I69ji(gMernz9!9W0jvD$M>RB;FL@f(ja-59FCYhKq*QGH{bq13n zLgm_X8C|l?Xbew32s$Mq1Y_+8n@pU}QZTzFH>Xv<2McXc@rq8QMny(gU5_CNd=1U- z7X)!5PMd{{t*G2Xzk#V<1)9-YYF~K1i5a$!ER6VdE6iXe8s#<*)nC3!=nWZ82G)gB zIZ+-LF3il4cGo1!Bl&Utz7SODQ`TodFbL4%vUjG#Yj02arqf}oGRGb4*pM^5#Y=ta z1(XHrW|#kcwK;Ue;0b*OLYqh10> zCwXTtnO5tp@z%U-M~tD!|evok7Bh5k5MD^O`eG)(2&NirMw zM20R{QG+jJ?I+OApl#P)>Y(Ctx}PeN2ivyHp3L9g@q-tUm@wk9JOvg$fJr> z9?Z+h;HDSfGvr_Rl;%PDQ{oRcNx=LkeEP2p^#4m~t7$85h@pO(*i2FqV?s(y(H$e- zfER}7B>>54%Pb-Xf6c;yi0|(MkrO0r9fPN78o>@yq8BWL$ET(^!x_POog^#AL zKSuX9a=l%(Uujl zyen{a&F+DIb0>4z%K>93z(prrAmf~pLPSbL?28Vu+e{%On%U$1Mj+J|R0M|QjO^v0 zKcG5uQ9Xe7?H(81lt(af5GHKaT43^i;N_a~=~g9p)pHQy~AzN1EJzEQ*=3zjBN zvSWxmrw^6zs5mnm?POhCisDE^z@7zd_4!Le{!wyP{)S9zGU^{QI|>BC$_!pw5(h9Y zRg_ESJ3~orrTEc5m(0Wc(@ADtMcGq{{^*;E^y1TEhUib+R~P9Hu^g!mEro^&V5HJ@ zlzWk=<|4Eh9vVvROVxDE-_$3+QJxfd)sllMe$ilcisZ+4&|rI$8!)bu^77ppH~IG2 z)Q{Yl{f;}j9GF*5;gxmUp}6nE(2* zg#9Pifo85(YigJ5symIoYGhMN|D%=WljKJ_`TH>+sC0W!iS=(#;7>C5U0Fpc@UcoR zTf>SxE%Zh(u$87?tgT(no9Jv#VISL)<&>wfo!uPOXEud8Qw`ZQh)O)9Js$;5c%Mj7GORUwgpBqj?q?=r_mUM%ftyz0u5i$4J%-gA6g)u>iz zQ6=Xk{Q0&%DHW|{*7O_-P|ep9FcEHEK@jqF0!3;^?}epoIU76|p#?CJ_3l2fO)Ful z4XqO%Js&-Odi{lwOBQe4<%jdhs@JUfNV7oOGSU5q9&5e$K4`^)tYuQMkSgE6lC;SiB2b{KHLD_#qmUOTDk$GdM5ZqU|}+cU1mC z5gy0Lo@a274RcH`QA+x({dMSR7!Ns4D1trsJERg=H>eNHl6ffJU;Fvi_CZyS-hOtK zX4Qul_6eckgZi#7i8cOAIf&G4ioFI23mH!6JZ>28BG~-n7!h$NxE!-gYW=4G5;63y zaSo`EZhxZ(wjhktVjgl~lP9%;pmLa!S)}jn0d08lA>EDUPz=xXyIXQ0{`E%S~NEKI>YX(4~R;ARoL-X+j>tU}lK@Slv zo|X=qrfy;HXwhGh2Hksz#SKTSAp({v{@ijw>Hh!HgW8_JI6{ALN$r2fC5!*lgW7-1 z^`B)bVf5Iew8Gz;QF-sfI{RIUMAIgS2EhX_0lvLV8Q$sf;=5b&nVceGaY*kGqvsc43 zq!vutVYmE!a17;E?((J;-+j|{(&fe5UC)UT&ITsY-m{vzH5X@@JQXc>%UM*}->PYR z;O=bVTokSviN2>%(X*vnbpShs7_8Zc9$E@vLqL;THEtaX_QA*!X?<31M-Ics5gJYn88DWBJ@}kday} zCFb3y9XShiX|hwY6>C2#g{68)pm~#7WY}EfXm~f5$Se1LZutnKER&?goIhf5wBlDY z(05T+&je~e;VzdDKk(d+yfpcYd*BsNfxsf-rV?L)7bsAqE}lvj2>1iOODQ048A4Cy zTkr@_xTH@)*J!9Una>oF?`r2vRD8<449cW0$PuYj!Cj`K(-s2UveOHNKZ&3pYn8Ju z_*97misTWoj=H`??|MTx$vf)E2*6C7ClHFh<%#hVW4}a-%sPnE9phXD7i^V6lYaN) zc_jFYWM^y{^xgl6fX@FY0{$zK{l6qYinjWO7~<&HKKULtD7ioz8#(-tPBgZVMUbu# z6CzY<5hp2I_gr%`W+3hN29dm-qFUcm?z8W1a^Fh{@^Z^dOB;m*tg`lx`x^_lzTVx$ zxQ$-!f9-q$F-91J>(i{NIOOH!?KXpG3n~T{_Nh9Q?Z!mgV(pAS>y02VyP3RG29J-8 zjZMs%IA8&yyWo@o%h75)aD?G&Kw6AcQ!+qAn;3vDb{<4PmzxZ%u%5>0z`#Xc662xk zeiTqg98?~TK7<3x6UeC`Pk`Ru<921qwYqvPYOL~E zWvV`GvPgb6w|ie$ZHL&M9fpVHC9--hF^v=WAH7>T;v7q;{874JZL34eL*~5O8r5C4 z`Pb1sqn;^iCz=yh+Cn^9N8LZ6QW$mEI;8lAt&nFrJMtYfAKWWXD>(uZ#;`lexf_u) zkb4&6ie$=<$tKyF!>6ObCu#4(opp9ukf42Oj)_&wu z8$*7wZ5`s+@eN*K&>GKu+p}tBFMY@w$6X;8Bs6VzOJO(@+DdTJ_-&Al_HGf8rn}rr zLJdHJ*<2#sEce6YIXT>Kv25`mwtScJ0GT>ZYLxfpTWmbh>a9&BI7Q!cc>rmfB6WJ68ebmBT! zvSYC%oC=@Fsu3Sxo4j+>TQcK*{@K?`oZl^g*D@OEGupFSyw)?-bL8VC?$RETL)^TK za?R69H$BY-J7wrsy%cj|&F55^#}uZVQ2jZe7QaCp#7EE6X!!m0`qtZ@hRh}W<^CDA zZePRP@&JD;nL`i*;lpc~O%$U+h;Pf%BzJKUCkAz@#y7B{jrJWE7ItC+$@+h=S0pLx(dg+*~^PI{91` z!kt_uDYQkgloY1%fV5~q3N=TD2#hc1(U^n5~(HUNHJgB-op z>XL`$e&(*nzpDqBQ>ZZ93kin|F;Y^4iX=D)9JUf2cNN{L4*Joo(w*c)Nu~%ZN)XqQ zq zMN0la8f%OBd*{(Bh}GG@?6f}Bx4t~DsYMgsVTvk0I>pf8BnOiIpIH2Fs=7ATct zV-YuC2zI^%7qc=|pv_Ao^lvGK94t1N+9M1Uj&x*0aw*?y99^eeW1WY2Jp{LH8RA@@ zFvfV9NUBP`EeSkTJZa`D97hGuqz%JZ9A`Rb$N1=1V5n>ys!GIZa{t-~*J~49=09HM z&VLN6|5r5ePi#9zVe(I443+on!Em&`d6A2+PgJ-0fMi#W)=orySd?bVd_jXaTL!B{ z^qm?!YWJ_)`cNj)xex}uliYRt_osyW+n3+pc2PUgY}D(N!3Q%!E5KJofzc4yJn*$F zJXRYJ^ZMt`UEZFIh@Mr_pb5;%*%O^1N*D2%vRwV1J_|MM+e>3+=rJQx$2FLi-qy=9 zJ#ot))I^k(-h0toA*HB!C&j3j-L)cRD2~qsE7H5OG(bPh)25WmYnrnMBn*)7#vKR~ z)1owZ$N@i=g`|g@(;RJ?ld+kOkO){VKWy-(WJ`>jGk&latL72nJ+o7Gys%eh1!uuN z6ae4{xKw#7qK2w((2i~#pecB?3NY~*6uro^4HFp!BIb%yFUkfcKV2rBQN?KuCBTk>Docg76=aCddW--pB8+Ga6L3rD;6 zTu`M>?IMVy{=r}vfz~VB1KA{z*3h~k-eocPYa$rt@AHw6BT^sKz{tYg^>*1a+11_K z1=2@Pdr?;;+zkW^u0cBhh6w>E0LDHgPcobc@l2B&qz!Bon~f2g5OO1sJipC%_>OiN z3sdevt0{mt@#O8xZo+D?P}&k1`p|o3qld)#fb=K^c8J9GqjG_H|DLqJG3+M}q;?w-`e!)H7zpfr2- z7^>=HKL#wa<%UdIG8%3$HevNhoERLr%gpz3r)`O>j!TUkD(!P@E+#B??s%6S%YufJ z89Npw(29#P&AM%JFV=uQIN}jb&AUv>wfQ(jv?Vux&W3luWO1T7hwv*7m^NCCydof0 z71~i%hr|FH%~@0RS=o{Cs!zR&&~aHA7`e*Sd-)qaxYeXaR4@q^F(RS+l@wifi4<$@ z+~)?+UiV#uZ}0Pg{@-ux!_xK;Vafa`Z$pOU$#RlBr1s5`<)z$uYb)Y=IAfaJ)M#h6Mz}hsB6#-Rk(^GDJ6Ao_FQN}HEM%w~H13bVA2ZJ~Q?^t64GIvTO+%w6lD*tq6x@q20*R!GJk z$wA@{7=z+i5U4(oKd7I9d&+m=u1zsbFpZv5f<_&3g9!E(;i$m*N3Q-nnX#jAgVL3{ z=L`2s{f3h-Z?~29mU}aQSN=IEi2tAw<}(O^=9z}Cbl3hlE~uB~9W+<*M&3uH-`}B> zSn5_#lrw^S;wFH2!m*dw%t!;N(Lo8x&PNBS@ivc`+M$)0`c_hOs-FUtDOLT1$piEF zfJl_K&r(!n9)BMhAj(O`IA|@_UD3*hb+)kYSKD`Yo{?g}y01)fep|K&Yx&`&aDQes zRfan22VvTTCHO$&jRkmG3t}lx> z_+V3(Xtv}aZ4Oz(CEkYFzfyiv)z@J2@_FlcWwk!=aq5e{5JQMRDH@klMtI9Od#J#Q zSsbY93hlOvwisS!!$ZeMURBi%uN}2dFJ#S2ztG%Uw=A1L+4Kbt!WqeI8dq|&U^Z>f z&tbN|D`=xD4->}rzE3Gn`VsZIP@e`Nvn)ei((YJOP=&ODVKNkGTo21{zVo$1oLR~+ zXw%Q7vvH@#%VjtLTw&7o*yoEZ!aLz?O56Y_{x<8D*=-R}2A5$AgJUu*m1^-Bm=m3xJ&+ za-BUU&T584XkQTCtq1FMO!+OCX^^|SfqrFcCR8Br)wW|L)9(fjLo)bkE_YmbD<6(u|Y|>GAsWbR`W`-LW2=|6Im=vhj55b4F5!iVXp0cYY&rxP01pE?I%*MPj zUP8jtreP~eQpQ|d#2@EG`mBPj0;E<}SoVsjOBymP;nFV9NiL347PzPy*$3%OLDy~A z^RXfaVZK^g!S+A=L}uq(T_)58=T=lPaj+cEmh7X2(!9X2OLe;*47vig<-WVdgA2>= zP_b%23q$Jj92H9TaZH1iR-P3|1GYQwR&>O#7UC7Y7vVOCjX~H^ z)x@Phjr>2Xy>oY_@sj=Bv2Ay3+a24sZQIEm+qP|YoOH~Nb;q_kNhiOYGv~~D{&{B2 zT4&zFb=9}(Q+w}vY<$jm)U+wpE+{(qli|EpO`%;`xhP5Lv$1bv?aAX-G{1j0=e?(L zXrUAOJDMu8IpxSv^IU02XPAU4%NxMwJy%pv_e+&zU6jtFOlabA^zP!)RcMdDEIdUx$8tP=ntX8AW5eKJAGr!Y zd0aZLNrCE8bW2DCwLP{X8MoGakGUXE2h^Q^;nijvX(T(=CCMg#NKQB3aABPm3T-6G zHbP)cO_acn?G+B4g3FQ5%q^Q7l`rWBk{v=KQ856IWD!vg1MRib;Se?uc=`yB^c6rO z=N}Q-a}LllihU7Tv!22xW7<^W<2fsRMb~Cy-n!-^oTtElJcE5c28nav3I@j)LK~E< zv1DN_wzss+=&1?@sy*vW8Bn^S!YP75o!#i+VgSLJ^9T5gi}u7*=5TLpw~*`5R(()u z!-1Fh{n1-b=%A0(Ev1fz$kRibLQB=!;f*mr!{6K1q2`!E?Lh(}%T1wI;5Or^;(wJR znI!J$UpFEZRzrIf^aO=#qafyCdrg_#uiEgtvot$F7aA8C#TuA%Im0(3f)KV1>P?Ym z3e)=0ySV3Lch+)*P5vbxsvtX}P>_PHR$28z{fS;@D`}J-KI~D_u;p;v%3fnxdr9aV z9k<^BqjvDkea^lEMnlM$y+<>|x`sTUUIeeMpZ%s7?_A0lZQPc$a=HUY$C0*ePd_Vv z7!lr>l28IJZ$RHSpY3;{DL9^CI#s&ncO_CK8=&(p{uwzY1XW|CAAWF30l zBd`1(OF-ofFMnys$qkn>*NF#$iBoxa+A~7IRz184hu4+Z)LiBu1|c~_0POku3|hr= zxAUIj;I@Y&K6t$3fDDUM!C&<4Rk09d!i06$ByEHALCmK==5ZV1NH{%_9;*|38_KJ@ zjQ1&9$L$EkRjj>84)aV^Uf+C(NDOR6P$e3{8Tca0f3+49+lYjPG2s6o=W&EVR9@ww zw$)JTuPgnoCy_*>Js(z$LPoGvLM}?YYVFI}K zD|NTFgd#q(R~w$H#dy6uVIuvsz_`%J|L5wc7)>7>^~-B)h50|Nj{cXO*1rbFT1^;V zbPJ5nfN}G_aZ^wu;$3ZR)B|W@qfTj~P8f+nlmzlZp>>uuf+U#k=8a4(h7s%E+Skq2 zjoVO!Y7;=v*w)7??RjtY+U4r)XFo$bUkCT(>7-%Xf_MM-TW>BD{G3{vkBd(@Ev)QWL5 z8SvxIx?m#YRoTP?yHy#16;a>t0U>(u#cF*1@?Kb6T zFnA}xN@d0j$?O0M2CJtVrU?K2*)1plOKn#hzph^v-=w1!v^mk7YxrglcBwEN7=}yO z&kuiX%2f)CeyIUOzaW44TbtpLY+hgmYJf^PhA$m}dSEPkIgUU*@Z6JiNDtm&{sy5Z z6=;tjXx5q!l*9E8L$ZEh->w64a#aG`dtP|~JNqWMAAug0FRXDkFO1t|K&#F2NK{`@ zw9;RegW>VkMex`2p+v`Q#J2)+RGxhl6Sw+u6+PMm1PM8^dC8op}oZC6X z>}@IgrwZqBO`fx)gQ7jMmGiovi;l`tlSXeY;`8fPn^_$A?d0N3$&zgnf)(L=UB5Bb zx7SQOi5N^JbT?YMBKSM=bKR#sE5h9CyQQ)*DbVR`7gdI7YqJ-xfP_IuUB<@z56K}h z;_x2ac)Bc*DiBfG^*@|O6w}qcMxSC#Z5^v5YO{ms>SVHSGX~~+{MhzIngmGG7bB{> ztt?`Q2yLmZhkq93th$l89ll-s`ON>da?0A6KN=iQz(%)UQ5hbQI#;Vnl@uqAFh<lUvs-Bn)?N;BM-5`|kdGXuR$IfzV1;kD;@M zY8>d6EwMz9(J(?tqT^hK+O>uqIlTuh z-o6Ncj8UJG(Oz3q*+z2BByh*}$P#kUg|v}%6Ql@YfwW=Ypa zH9`c?{r0T1`7C)?+$V-!R{1%vQuyc+(efW! zlor7!75#fcZ`Yg+JIiRJg(ZI9O&@fi?U<1=LoTMVA1~qo4|~JEB2SBV$f8irFg2Nu zMhk6#7_yF4In}ly^M2D$jY1gcZdtLOQEuj##UcY$1dlxA$han_i-m4#)51ePf8EE* z7aB8SvB?%mP5zdfTrXS#X@obH-+a4y1vMl^*X522P;vZaTZbo_~pDQ9_onHhzu8WC>m|FUD;tlVS>3;- zJSb$-+Z#!B;=%A{9|aNYIBjH{lK*zrIVRh&EniaPW;b6LE@wRCZ9JQRR55{kBS3gx z0Wb0u6%6z!1b+`N&V109p1phR3`zpHXgW@yec%^Lll0qCRScy^&BWO&;cXUNHi&FLVj99S*zB8Q0+vyyvtBCg2M)M+7k?t^F*Bii~UGOaJEf_A@QG}ID1z3R^Uq=v9*2x+`RlbK{Yb!-nngJhIOWthi#HrbHtUE}d zLt0peUDlp{wPAq{Z!K^)SY3&>BKTXWO63&0c91)?3`Grx!4)roPLwYImNAqU$_{gm%0Xr;y_(cOZzM6u3RC$#7&~cz={xE70z^_!afleWj0B~4 zAshtkwD|~T9Mo5cO*BV?ACiT1N+4B%I1os;ngc8(pvIu)7-S<>5Gt_hDC!N-QV_E! zU-?pOL#kw19I#$;v**{aZE5=!WdrOgg1==q_Ty@GwAFd#nWi+zj^McMtr&BK>nuI9 zyUK|ipIZ#Ob5!SPs=z+le(5o$lG<}RDLk<1HM_n^`XurL0TNeTk`L@d!X@6Bd`^{& z-esj`9qGt}jtIid!@dTQTDK4)8iy8qTeMw~Rk=0Tmc5qR+*flOdGDPO&V3&eEUibT zOL-&O<9#@T+kIB)_7@_fF0yY_iF709k?&p9gF79j>t*St6*~p}rVjO`3p&~GA-tR2 zzoV1~aZo~qq}Dh*IOw+AIwu>P$Jh-mWwJST7>}Aay-F?UX-W<;!-zAO<866(9&&T2 z(FsBvvdKx9V>o?YPg4_={OBg<5-?PXn+K4qy5<07E8>&O;>E*gzXSYR6XI-$Guc9U z?Kwl9(p5|~e=crXaV$}c9Bx1Iu(qU6%V3(H+SiA_EX_X(Ld9NvdNsM9mSKIsu)D{_ z!8M4uz2Z;5-NJ!OON>aQ<37*d1!=-7K>J96wV<{YBM@MVQ;yDul^-DNf;6U~kfYyP zjH4A;gr5~}NgQLbmHy6%Uv})%PJq1j=5ON4QX;a)CJ%Wsr@cQFNQ4;Tl@z0p2jk_O>0H%*M&>TcmQRgy27Q z9hG~xfR-;btNK4C2>!2z22;j=4-M)v8t4+}A5ocw1y-WystJKWZX|$uq|gtq2$E!Y zOHZ6%%@$>#u_8$F&&cH09>&{~9lw^{blHeqJL6a5*Sl;Ubc{5P-a?LL<2yL@Oy44ob>m+<`3~Mv zk6GZia|%zDa@w5ebVtCYzG+6boh`CN@$MgRHO8OswW~6cfYLQ0s|@!5}hJ9tqD-zeO=u z^Npd#FpyL;*b~Dr0776GnCOmhW|-bUh%f`545{*_=xkn_ex@P?#^M|Y8%t1EZW*GL)Fn##(|r5BPI$VcIE8?7~>JK02Z zm(-tWYja|?&_Rzs;b^?f-AM+{hpySFzN(|n5icQR&tX3zjq2i(EP^Ua4?m~p0S#<d{F47BQ7P5c61qA>FKs5 zVQV7rXz#NYOK&h+idYV_ocmeek=g4**6H8iQ*IDHWk>`L4@h``> z-Vi*!4-ux6QO&Aq@rADNLTtp3`wGK~?JnU_SDVB- zl&&%NZF_jAwjJAtd)sc`lLjU|mn!ltsvIj*XPYjZ!X1`nDlIejqub07l_jJ<#6(1~ zNuHTZVyw~LMc;a%h7#S_j!-h->Wi&NvLwcJJB$br5Vh~9QWwztK6nBM1vEau$BW(k zp=yB@A;p3TumJKw^RuczZDsEi?`9LOIPVq|_hOJ<%i(WX5mv@!yZuDNbfwR@(cuL6 z9&^4#sr$KzBz+{r26$W}H~i-J6?r-mu6vCaWUamjGmu@_KSdAUM&5`D)5$Aj}e6>iAdW68IU7Q;fh552>W{IdK+bHdH)*3avXpqJ1^ zV9bM7p;UIcDGkXTVyC(k=4kKnR1M#t_Nk654l004=tcJ+(u9i5zbY~};9X;vUQf1k zpjqWXXc7$+y(EtG3Rl-HOSgr_`0<^PUFQV+vCL31EIZZ667WtPv^Te=b zlJP>=aGn2F1z!^*y5QFYKnr$?<44fh{I z5!3&Uq)7g!a&7EW#k!qta%4j8V8NN%%m+uT$Jn=FU|4Yu{4ofNb&J5L;LFO?{o|d& zA2eU81)C^l_A8ah0v5MOZarg~JM;ZGz~AS|cIG+X;3uf&NC=rdr4wW18)OuuG-O9m zHl#g{Cc9%fkp`2IGVY4if+*;U95yz4;xKz|ne2nf)F7xi{Xya~b@5`4d|Mc(y{6_+ zOIUY?(XS(QnmB{wB8KfhgdT^9&SV-z1F2XO{b=m^ht9nc`vN#`&b0M794@_vv`~AJ zH(57NFY?$_4)q~LR!E=8ZPn4uorrTX3-@P1u7i(X$t{saogudd1^ym;iFc|m8N~We zdgF%nIb2OBG&?pd@ER$U?Zw_|kc?uj;wvtUMg417a(}9TQHmUs{=E!*&oI>F)0F*j z3YdRXt=@L!sd9$s=R2q@y3tLI)c~zBV5FkEwCQ~aqI!gH1H|cl8@t(Ont!dLj2h2# z^yDA@T=f-WE&4N3j5I8bpodkJZAfz|%{_o+1x_;EJqsE);0A4SCCa;gG?Kkb=`{ux%NY5+I1V`#rd#C$^t`Sz{&2B*@FcO;5l zv;2~f%_nM^FoR;0j{XgQG?9Wogyg7Em~$xN?~lw2T!`DyK5UNH z5EI|eAIN-k9&di;xmoO%=z3X-=P!_lOM^qy8iTuAutU0-sSoz60Y?Y-2S7tE^!8q z?@9X__@8?(oMSHdpTHN!v3qerCVSEhkMXC~Qe{6&- zy3D99KKczR&f_ZOK8YvU6UjbA#JDCG2sKXhjWkHODcz`zDcwM|Dcy*S866sA8Zoh9-#3;1 zY%{tsT2s$qTR2w`MywlakoK;*i1f}maFHLIVdk4fyUd%kyMCZ?)SmbTiZ@ce;sfb7 zS`Z3Adyr>n&maAyprh!%7)UfPxB_*%=brU}@z5_B!4A8%poEw=5rMO`FBIDpyO*Hb zmAg8g;lVq*3=n-oP7ncu;TZ2Y*XlP|Ux5T#z{`5i^x(e8`JG!rh)?9n4=EC`D^=|9 zS|%7ujH6)*42u1~ur4sU!Dz76!%AUlKrq7#LsCTUW>alXz6T&T))E++sP)#2r})CSS&5y3r8?t=p&fZR9UF987jsD@51pc0 z)v?w;GtA-t2urbA!zZris+A|rXVmjrMdYyQzj}E3h$uQ)f2qaJJ zSTRNB6AwMSJRz}Eu*OO=!4Sw>O($r6|6v=w8^AtBqA(C^K6E^IM|3IW()rMievdr^ z_%l`gkz_BzK{24hKs-;=Op7Al$buXuXQV2U{llJHEsZC@=;Z9QqA+ruFRl;-&VQ_a zAC|Ux1lOL*pHLJdZ)E!#%b1y$Z;wyR&&|!RwS4ek`UqB}l2@I-9(+dah#4_voC37m zl+@{LbBt^A0=9d1TR4a0)WVjfv8gH0S-p`9P-OFgHDlPVRbkNLlsp18mm(-QnN(|L z;U~aMg9@99d+YB3KYSbtn!B4rDsp@M27W#$UiEO-P!0n%;ic!8Tv?G9>0~IK09dUB zjI7}1g##?A9uf$>oa`(W*?#MWF~S}Sk7d7Q>%Ie`DLbKMU9&&bXu(sJ6%V4G2-Y#7 zT>-*+8(m?@<-ff(R6T3Rq&baQcbRJ@SI;H9JnU?74Eu z>I%Rr-E%5p73{WTVBoXtl@ZPjQ=C6p&TERWY$wdg$~Yj``cMw-8P{^7RHQ>@I%xaT z9Mf5-au)j_%F5B;El}gib2WxZ;~wTiosp6~D{ihMkL-~UC!^T}<3`1i8GF}r2sg_U zm%punv*u%2h{G=Ea}!7-UB>a4{6r{wW4&arAAfX|o;YwHXL888h7qH91x&xlp&`^W z?C5UDsqlW+R|iz>m8ZF@J_JDL%E)p8l9q6ENwYc?6ex_fHQ8>8!rAN+fJG!`LN7^~ zlNqe(UMyB;#kUPtYA|Or?5YhmHnictgE+EQ6PrV@w_p7A1@Q}>j@Pr$% z>_oTW1nPIi&C%v#_psqPkL}%ab(W0nx~rnOe(pxQ*4Vdsp5`)`??ta+f zi%-w1>JQ5MS9y)cqE{u=w&z7)vrzg`9!hGAF5#bd2l3RAQE*=W0T?WyZBd9;H^dZAR@H6^?4% zuS)m2sd1w|#za8NMgs#+Z>(WZ9IAQV3!c^v+*WtLzC=#doU?JT9vxg!C@w73?y5aTqo(x}(~{%zN!%Ronuy@kBurrMx|rJh-HZ0kprQ-$fnx;kFA8c$6@tlY<$(-di` zL2Z*$W-P+kZnIOXww+0_^h|wKeM{rEcy^yTyha5(M_sd1Z!GQKR^ip6<-AaFr|84; z(4#x`-|T>^JE#ilB(7jyrR0wmrR)#Bu)~_f$7X^XWwA(UA87he0mm9qF!f}Agt}pE zS0Ib+0LH4=0`j`$qgn?9o+Dlz1+Si|7wMHSZ`@)=Qv@^k4O^^2A=qpITDjHqJv0kW zwibV#MJ#k9=0l@Be1rc*8MJ#-?&=8Q{HBrRg*|-J8;UuSu*FzoIDZiF6CFI%kiP_m z?3k}+oR}nV65S5@PU|9UN@;BV*Zfo?+uKIUC?=#Rw>4Xw_N&g%6YeSQ5Qxmq#noRx zI|>2%~)_2}c-AU}P*!9@$zD z@SW#LKq4W|KcKul5dt8VZ=#yNDNtTRu+Qzp$bqMsSb94iwuT_ zN+I}5-R|F`j*^kF*9-_DXc$R~?BnAvBlmBi$|B?o3lDj7+wqH&;U^_5G=MlEtV{%% z!%Id;Aq(h&U5TIp{8l4;-1wsQQRSsP7023dO5!XMlO6j)T~L2g0MB`ThA;B`jIWpW z{4s`f~g!sFwACO z(*E7d2BY;s7B3cjxab+!$bHexMk?l1`3TdOA2U($_chovWF7QPA|%;T*;=PXZkd@P zdz$j5mwqsNkm*1~i?G)0jhqS%6t`f7TgRkq6JUiM&Jng7CwiAlTO$1Py=-~)cto@W zx-Q!0%6fX+=pdSwbAuoc7ZY|ivQxHYq9J*f-swX`3t^3AlMH_!wwn~W&K-}Ko&9lB z?j+$x(rA~9I14e|dQ0bnv2=K|GNOtMFJ0b^^q+IhT?grmy_A8WrPMuh68!0reG=O8 z!zA}|_=M>JFUp+w*k#&o-60v&~fk7dGg8h2_mh^-|7xq&LNdWX4+#(u>v?ESiH!(ZWj}T- zvIftj?rhZ+n=K$vi-Ohs0MwAql^G3)cbZAQuakbn%LeB477Q3Mwp%3QuxFU7rC}1` zKHg8F#l1s*usup9QziZ~S5#RPGDRy1b!QQT`6btK+pWTBRbC`Q!Z)(w8LCRgEw`(i zVtBxD{6=dNwNK@TVKyCmK4R%VmyXNOAZ(=hM^cIn9}hEs4E&DvUBb-Z;<7J|pVd2MFBR>Z zYKb^fDjavnkiu#vxN;Xh|)VK6^Y9fyh4|aL;)Ckvj~DD#4yEoaX$PHd11-+j-3^96)WQgAq4bJ+3(YUE ztyWQ}Me6*q@7;eL~*!|^Yi=U78#5)8USd8=+fmJveVYZT*` zLf^NIi#ZN7q7f8yB>K@Ra>Z>z!`{_K{Miql(ebteJs^1rHoNp}T;?!urw0|b1s8$4 zM?Ydi2oT#O9<Hs&4K`OY5XmW{o}?DzthKx2|sY!Ic7s^2r|)30fDA z51AF$3~cq7+D(hVt$;}|e5-am0Tm~;&3kQmw(snmR_&ska|7wdVzR!EIX`C3nq+au z!m;3+2>T?2Tj~*$u`in5g3Q-(KC$=8(mz+L)9tnV-V?&HEP?dtk4qp*itJl1ueRv1~Rg=2B!1eyE6Y5Rg? z9bKd!?x0c&drU3a7qtH;V_UNH{r>fwVDkx4TblXODf}Qf-;p#26=8WVyB+F=U}cW= zzt&D^s84H)kl((=|Bvaj|0~pP_HWc)s`jr?9ZbwL`cmi=2V@$%_ArRP(gxq0xV z(6Li@(CACdZXLpy0BNE$QPlnH$i8vx>m>k5RINMF%v7(>Rqyf4)Qtb%k6X0B4bLh= ztpPL$T3B_Af22w@6dI}ws8jMV7X=$>h9Q}tj*@StFqIC*6JvnD*kP;9$H8F0LXyi$ zXQUzjW{M95*Bh)67rVC76x9`04lX37w~&dj@{GYj*yt!Vf}%#inPQ$AM$>9mW%Eqz zqhAx(&(SVpqVqS_Y@?mvV8rI$roPXzI(`{Xsn0yT7t0>y+ALc=!PgV#)tU=9zdz1frcozm zoLCCkb|p`Gyt+Q9Rx4^f@k{TH|Squ1f9Mt3QC4?cz^BYuGk6WU@_nu)@ zv#zYyKwOE1r69+%e?oKG0GrbXlMR@h5f69a{SGdlm~KF-!Xv^zysdZ0!-`Y9W7y(I z0ahi*Bcylkfqo*gh4bOvrjrIzlv+sYfSZOa;LJa}zq}6144*He74h|1|EFmx|4mnw z{%ObX+bEI3Rk7KJf`%o5t&M`)E*S}d5QEpR(l<+{Gb3M<%zD`ELq!UP3J4UG&Ji65 z5grKh;_bfQ-Y|OlyZ?yLkFtzkv##0JxF8JH1_zb~g;WPcBm*yAA28{-30FjSNP-cc zdDNIwDd|j3*s%~c)hg-v>+pLA7V7|7A*HYGfwTf->eF5Rdf!%#Q3_RU6dI(>OKwIn zT^juPaZ^X;#5)P0nH7zzFHPB9gR{{Ixb$Q-3xb(H{b1d4x4R=K%h}^7ONS;1b++khU8(;T+f%A9`9(% zhv^Ab?1*ZWsx%6Jl zFdAzOXOq}RdCClLkZhye)DNP7%x*(-D5)_JE9XJ45AdG7EZKhY6y)9 zj0ShY1tbO6uxU@wv6*l1v8gXW!*p{nC1#eHdLIsP2k3^gv!4}1bdwZA#7w$R$?%4g z45}ScI4{akT!G`pT}><1RzaRoRLL#2NN;{(tsO~QQ!l-Tk0Qd0%DP4-LyLCdotLT3 zCbek@ag{P2BlKH2a$S$yEjY8Y#@;y{Ps&VRPc^lrsyrpb%+fn*_$_*`r?fPcnO&l3 z-Oy6If83D0b9ZXHDr3XrxT`YPm1s&~Bs4w#U0Gj-GE+!tTEMqF_TtLBIQzGqs;VvZ zFm|<3c7+f33WR_06^yBe{Q`0r$Cx_n8d#jo|x`Wf8xjrO)Pi^l#vozUGS$=HoZ z$7&eAp;pRO(l&sQ`nxs@6h z(9OSLLA1g^=mda0ndZ#G$u}#f7h_jfOT{1eX6Cm)ipPJw7Ro)lI=z7&RMfGOCaKFM zCyIaCpUL)bE6G{x|B=nXNw(&b`*@kyLPj9>K5(_ALB)w00hEjXA2sIWBRNeum#?o+Ek=2&k zhK{PnQV3^0t%d$6PU9y4yhW9Q`u5Cn+`27?7NPuXuk3znTJHOge=8(h-`r_uNgu1?Ut z*n(JfJcXrgEfIct!t<8-reJeCg>^2%x{WgD2-SBt4&f3kcV2Tp28QpHpfgL?6h3vl z=n}DLmpI-dptU!o5#NMQ#P9%faVV~apt(#`$(W7yKstXhL3QT9J|{v7tIHrqqO=Gw z5?rx~SaBB4wOY~#L(Ii6U83TEkNeji4xWIqfoaq3+){I8Y2M26aNH+ij{yZd7c8ug ze;!%d@9$Ch$B`uz`2T|!=wJMyRQ+57O#=OsK3-OnLm)V?QGKAON2{bo`dc()1C^mn zD2<;z2N)e$c1Jge%;lTW&UO0VxxRkJ*5ev&d7szL@_e7ym6qOS;sj_(I&SX1%+C96 zkE>iC3-^zg#Gy5~quIK9m-2DEVAnCObd6Kv)v3k>=-$*Y)DBemgaG6qTlEzq$g zsU+bVZiBzc{g4f;`yO%((NkiGvJWZ?A>*mt9~xZkDLvrRQLyXBSH26Tt9nCR1S^1g zL$0rSgTX-kg1ZR&bHEH@2X@UPLYixNiLa_WsVZ$LO{EO?I2tgcCL&V}ICY#{LbaAX z>=i>jj#%v>XU>ee59JxCH|@BL8IIg&LGV~OS}r)U6uqQN;u*MfooET0e3LZO*I8N$ zHJ_ACu8HlXvPX&Q$csxKNI$Fr`_^-w%9<};k#qS zAw@aH6k~V!OkH;O7=zQJwJ*#u9q>;ZDvgdOk!La2<#bipEEnvBG5>>V6cCzq(@~5Z@i3U`Y)AM8JiT zBt3AMm$f!J(n3gs1L0ObMi7aaO5G}1mF^*cUYx|V6osCy5P|caiz03@0=?`2PjxRw zTbY+bnnim-p}eJiy`_`~GQ#(hOoveYNtYCW%q)y5OenVt#gY9#FOC-f zZqHLyzl4rI&_CMf5#-<`?Sh%}@23mr7Udr1mUYa4HO$lD8_@g)myI9K|+!lcIH0lzqPp4MAP zlqS1ziy8MpeoxX}Zo~GOWl=R)Mq5gz8!0)stu@4Q;RQL1+G%l9%8{z7t)!lYbD#q` zF;{|}_EUet;&D%g-6l!DL77SK9AabD>oY#sMYlL->bp5?X;E1@eBE3^qncfS9Dj%6 zgF_uOZk&Jf5*y&>S$nuq0gaW!%(C!C>hE>SHEj~ss${k+ zCjaUSCN_iKG_}VY`d)0F%>aF5kzT!*Wbzmlppoi;F7%zXQ1N$0w54tsm5D zTEE;L4KgywbwPqHXh?(xG9z^GFrHFPQ%lXbIcRbAR>#`co%a`{pO#iNcLNec?-Y|w zlpwG$nDgrsYnj`NpuqE&%_E)2aaOH<1b7IiER)@J`GCCrt z(Kx>%u3`XIXwAPeIwDg5FbL^vx*SeWX_4JrcE&hXfI?Fm8y|88>Al+HyI@_Fwgc&u zPG_*w?m?gz17$W@9cp8C#Oz4cZk(frC&Nu@xA0M#sdc6i^O|gn_@{WZdiEz&E0ZGP z?D#IDvciXmb(NC!E8`t$z^{5Kjh|}4$(3ZAptE6Z^t7quS}?hWERPw$AI{M;TkkGb zE0-eU1P^$X@I$=F9tkB&+R5>cwPwDqF-Ef~&T33@%Q*GlarT<)?X(b6j@7Z(kLt`^ zn&G<%)gpZf7^q^OYMc*q8&c9gz!E(q8*OATSNLZW2g&t~%hRlO)VToz6tMk{7y2g~ z)kP%7!sQkC5}4*^P`!KtRRq|^+^u_2-ya@nkNAovxl5Zj(&0k??zK_XTTDjaqs(Hz zj&D7y2Kiw8B$Ltq&Zz*<^a4cETQm*OzZQ`hczwVzaQ^aD>Q&a!g>t{<0VH=Psdre4 z+qB{&6>-?q^*t!St^LCfn8MEK-T z2_xhu#X0>838F5?2=#F0K+<1xi{6g%#P)`^{HB6`#9MkF|CY@Vc7^mXEG?tF;+PiR z%2oK?rL;YbYpUT_6wwM*5=@o#6SEPn9+ zy@(;HW&OLJ`GNF7ncH?vfszQrwzzq)w&jt3-23eJJofj`vqK+PNAMWTrsVhhetI|= z%quM_9nCh!ok1`-B>Ecdmzlx!?{ueeHL)jSp` z>#cAEAq`i$rCdFpz1Hcv28YYwaAOhVY-6nAJ(${aD!msP^=6++n0NgeEF8aA4BGLK z7cW;h%sL*qYMJn>b~ul+$WlzSQQ>`z{-axxP&FS`!2`ygIL%)!^bgp({DH)6dG}4X zXTh&yALFZLI{UAK(`E(Qcgd_l*2k$cQxSH+5$zII__3qXq*u%#(?ZT*r7Iv5 zab20qm2soMOvHf3j6o?-yNmTS@4EOCzi#1z@3G8kQ{hsQ;~BN`VkCXZCro8_d{~i; zPnM9m6P zaCg$JVsGuMRl6&n|0I$*sjqDTUq3e6e{Ll({_n?CT}MOZ%Su22E)NaGhyp_0Hct}z zjgZk$CRn>J&Dk!vMX_2w2?Q$1Wv{c~vHSt*VLaS= zCs%3*Y>VSd;(@hZ8VE&1f&sK?e!gSw0LAmNv&Gk2OFJ`7xpdfl>Zb7EdL-Kbnx8a7 z;J{y}r&C8sZnA2uNfSzY zfyFtiiCRXI`fMequp0+nq#_1+H8`K1xyi@XN~ZLfh~VhPn1JKH@~feO z25^1;5#_iq%zS?AH|!Z23Zhps%9eC8sU%yx@Ny;?W>MEL9>X=Xu*rw8*#%c>VP+i(cd{Co z!&Rp{;V+D ziPda(hZc%mqNkU!Qu-GhEKX!OZyrWc4Rz*BiCH}t<1I{ynVC^4z}#0-@-bh{tfsM? z;H9-Y=x(52p))H6C8VW=8SB$2%U25QU=;`86QhQH?oG2sZ)z5E01KvtFmAVf$2n}x&YUTJ-mpB&U z^);LP#n!QnLYCY|p-5swx?iXS9`M%p6U$!eEg1{%F=ClZB*lGoiG(BOO?KiDhy8Z; zpVcvnAD;7_47}dwKlhfdajqiMM!Xir1(H0CC043g6z2VBvD52|BtnAjIO-P z`mETtZQHhO+qNsZvF(a&+jdg1ZB?8~C6njrx8GSaYo=Gv>iK-X-TyiJ?7e?73~ACK zm2kp=(l$dzu)5koqy~NB5NCa>WI8z^6-+&5A&L}G8W-hgK1V51^!IN)C*N7WCb?0K z$0%1@9`S_2h!u=@k&(8+@-7!%$DNmXdBJe);s(7bl0+|qQ>jHenyu&^Nco2*03twP zTj^^oZvL0S?Ee-J`#)nU6E~#)a@hN9Uiyitqwwfk1)+_$63L-_Cr%wsEL{AYWxBLv zuCkyy-11xP4A~-zLa_{Utd8YAfF)_VtBaoxWVO#2dJx^5Zc1xRE4?*AFpdce zN2mkIsw%?P5rlk)IJw`y%JYoBj zKuUkPM1wC$cS*)KG3`vysYM`h92QCUu zx-UTf$((1}irImKyfa*|_pgs1K!k&o!l=ePqM1L!V!jM3p-&*Z&Z@~}7`D$MHA+nF zPNi_Z1bt=8p~q4mk~9l!?V-8NF-}qAd%AnadFy#wZ)-2AMvg|=Rt#uC<+Ykb(=i7d z#dK}$r3I(&TmDE1xRj9m+xz*w$GOM$OtuZ0Zm@U-Hismcrb~Ei!x9 z79FfJXUHUM7gmGta=TA%c~7$WPMn`N;uP|LXm>M0(4Z=jmr1K+#-vu`>!^%m29o?A zNQi@pXa_0!;&6cNq0^BOQ|5!6u)5RZk^1PckZWMlQ5|*qnu5GYLCd}*v75twU|2(7 zq+nB2W06ZHQO+7Sd9;48A-H5sXcIH~&c zk=k5?j2=|=+BA2OPYr8~6CKkY?RvFZ>=aT3!KP`wcFr5)Y2iG=n<9TCSExJOesojf zI}Z}I3gmiD*J0l1lFVi=-f+P!a*sjUO$9I8GVK_QbiDwTDopOUxRzR3aJSs;Q(nk5 zD(}O2{|xReb>OJHkgYh8{h3mw@oV^sAm)=xeGkD@ZqhXs%i9?>?6SJ~%eOjMXj})~ z;gGt+&LwNXg9Qa(*x&Rb->7{Uqv2gI93j4*iSDMJJj(czl_<1bi4^CMAraS22r|I5@C|woG00VpFoX(ru@;L|{s5q3`_^I>m}-*D=p)W@nzCEyqwR*| z>~sY420dOIo-Md8FaqfnOh3Xe?Cvx@Y$+tyfr(7ycR~$i^6fxX0+dOXSge^gp%vkh z#~69buT7%G6YMeR(UL8CSUn`{A8J=O1b%P8S7qq?m$T=;Rr!tnSsjYiZ2nq^^1;}F z5Ow8=sUDiaeBspAFyW~E!d{T)(RtZ+ox+TA)``FR?-X9irpBTL`Us9ZTNi`4L)F(a za!qf(QmwYTJsJf9{62vU@oRPJig!bU*ubQ~E}-?PR~75ZcT*zCqsuHu^q>lmse-s6<(9 zO@u_SU9jD66$j~E;e66p>f*ov<&vy9&G+21_Y5t?Cqv~4XdV499Y$UQ^=;)At_fvH zp0Y#jG!b4r6taH3r>1f3H1Huhb#xjdvCEEJhkCq0tk?MXXbW|_fWx@RYcqM#Wg0bC8s*}J#|Sla4=Uv< zoTXjreDQBZO1iD)t!; zAT-MrSmZJQdIGW6>yOoq)n_M+*#g7~v1J%d7v8lmvVQ-e7IT>?WdL!Rm~`+jF7wk4 zT=o5ygz`j85gV`M2{7?9cv#_&%+ua*2t!X{ha^`l_Qggw?~<_LnYp_d@P|ioRhggq zO+Vr!Cp~rD>&Lf+XQ!*#Ax(o@)ad~^h}n-kQWxly=%cL-PbNALvBWq%#=GD9Hz?rx zr!=bS>kfSU=VO}DKS67THWZ#)0`ZZ`b zpjfRM6VZKyG}IGCBrUU8<+9kVlGvY@SmK+m0~hJZ?H6&JAM9iEeyMy!zAw0nr<%e7 z)2$K&^mwy4#B$u5Z*M%$d~SQ9^r8En4nKE!E=OT8Y(MSeF?<{k?{vQi?v?@zproU| zwD&Q9`cvJ~1SSEi0N292YwvOZxAaj3S_Liw2cW-1^%;Q8KxU(P%IxY9_;T(Js+~UX zS;I2IP7L9gwZ-hogNp#7^|AbiF!z~JMX`DaNxth~*{9V*iogysW7oxfg@2)nWD*U= zr_7Hv#>`ftSdj&;3g1?SqJCr@RoiY8>x_jO3Eihr*1euQD>#mY?CR z&a&t=D19MCSAvBH4>pgoToP0Qe_LU$H~MzG>C z5}z<>zQCovrYK@I*4Alw_=^uJ(XN_^Cy}LU%R3jOD^e8U45cIW=c0)=t3$>alQ9E* zBa9bI`Qw?^{!*(wW=dVp(zt%zA-hB(hPTDmZ-x>bJ^>qV``ET_S#V~lHys?@w+x%8`x{W3lkszX6|p(k9Kj0+=5Dl6eB3yV;6+To)y zy0Jf@r6zWOuIpT=4GG=H!d>(k4AI~WlpR~VkI@^7GI>*$x0k8OPH4}aQ;)M@^#M#0 z#XkDN)Z~>9ma7T7V#v<&wQ5HMlcGFq1E51y4}!p7-j_?Y9cG2RTS27Sp4!(x2d z%SWx2^XesAv*$~wOPND$Q(Vp(C-O9lG8PfA3%wf*VR4I052Jl|+5tzpWLU`=#7atb z*(8&>e}<^jcwVWLXfLNNCh~ot#qik|e=C=i)`Xb7|13y|8d2Dj37JJ~Mt>|TG;q-I zQREI?P7+&7gE}G(U;|yA3(vJDi)q~hr1?)jvD}8u2!_lKxG6`rnb%ndyrA00wyAl{ z*&U>YOFYURl!Pk|qb}o)GB4p-=ZQ-UvBfz0NV=`9Vzt!YfvSt?s#smU6}wMDVT;LLZRnOt3V>>yBSpEQ{H0yF%3uN5>|&Ch5Llb2UUcvn8vh zI~{$*q^QnA9dxEST_vkC<|^Jt;}vD6s^S(1Z4NhfuoH;Ehw?kAD$QL+p=LO7v14C` z7`D2sF4Q>r^`SVYgD;_*|D#g4O+-i`^fqLckP$9sVC#Vwdu)UoCxmE)^rY;bi`HI6 zuY!j+bcwyDGB24p=)T;WGhcs+CrdH!{2*kaLRa=weW|izAX~`^SPtpa7e%E_9gW=` zH*5jD1dF*j6;I-w#j>c6e~>G-%=C+2G%KEr#fs?Bc-~syPrlI*gQGe;5@UPhjiW;h zlxw2`!oIGT)I^h1=hV?agTe}Yr1t}bZdKmL5hccbj{b6p(w!tQD`A9g%$Oi{u7@{- zpEp{FH%RA>6mW}N&?GWw>Xv+oPdM3X1m1c;`=m0zB{ciQbBE;W8;!{?q-zbJsvg|i zuX*afr{Y2c<`1+1RfndneFym<%TvaBshokPMf-v`bm1b-3-Y06Q{GIm)ZUUf)hpg@ z&eA@8h4NzbY75P3w0MWh%_oLnhOv{zmXY5b!y*Y#^uh?<|Be)qpoNh-anpU|CT?pqG&Vs))Mo1pSyXUdorF+0(w8d ze;}oq;wCw1kE}4yjK7GFv@(lh{qH0s71$fbuE|^c!QZe1jM+vn*?Y8u7_bJ+zQc~& zU|7#>yTkNAooL89piMBVrG24zUk)nJr^>q{_+Zr6=#F{c`GF81Jr(Sf(nS7jaR+HL#FPL&>aSYEqeh9ruG-@jm$Hwc+ry84IwhBt}P+S4nGs0!ju|% zgd|`e)a8gNvL9jLMdK*&mOR<8LxzZG^A|U!McPyHGhj!BV(RY-%_1&lb<@(#%3}?rEtV*3QKA4Wkh+ z4R-YX@;~B~(n{UK(8jNiv6&PdHMR9Zb!46;TqVsAK3Q2hLH*0y@I<6z*{zuaykt0S>OJs3gA{>|VV;dC<3~)R}LKAPcvCL3e_9wFO zbtNHM*D=RuAa zlEuoghFYv+B!YTON#gBgnEr%Ztl~)^-tZlvj>1)KDpuv!s8RjUQ%JQc%zMU~r@gr@ z{fxGrcIc%?qQrgbsuY>P{H*^e;X#NgjLXOrm zOtzdcug#evm1P9OIq2%i2JyhQxH1TqGn1?6@oZ}sb%>B{tSgx8;HYv~!@pe07EGs= z1OSgf?)WJ{&#o9v*%jY?aPhS(3`l(RD(mJ9^Z9?oijZAtPum$ahhodsD^`}HpR=0x zTNvr}ksMdB#f7rP!X)RbxIS}fZ>qZQa7)&ZQGQyY$gW3gHD(e27a2;MnEyeAAm z3LbcoU8!*U#XDf^3V5c?e>H{r+EF;@IH7NxE+hZ=Jpun7l=A{J{gT!79_-GmfV8gU zpOVc8o?!$u_+<CeRCYuV%j++(~`T-dl6pHRF)&8-79z+)!6eIUdN>Ih_(OGL?{t($EZRYeP?ggSO zZ!#>(#2i=PEII(?>cu|XHGWx_EQ5D#%je5?_i4++;kj1htKRQ|PvCOJj^Y|P`i|d) z98isF;2OAK;(Ea+74{Kvw)o@|QWGu1pk)FBCZRh>K^@fwqC+l_?I|IRwS_opC{O#7 zfn(c(5M9B#(~}+L1|X36Rp-KVpaL`uGhAAW~Jww#ca2lE; z;z((wUNnbOZT$dTXn7|3Vn`mUmxk)0p&^law3VI!2x{rJNk=p8OlBIhElQjnHQR2& zT%D3Ry21*b78>gorG@)9`5>Yvb>ntrL^8i_b*i6o9KY?<5Pxe^xObahW5%u`$~PPH zESOdVj{_J()!ZVmuEyR==y5r;2(J;HJYr4&`plOFJ*hirB|f&C2g|gbl7oR~r>Uw7 zTx7&V8I3*Zqa;!rRt7C`f1n$*<$D8g?x(HtY)h#Ru@AODRki2tTqZ-RGD!l zI92m*>XeQZ8uVfRP)(v&K053FY!L4eeG~t%TV&5jz%qm+xD#Ap$%@J-jyd#ZZ*Q6S zVM=XEuTFbLElYZ@wO-bu$I@Wo1-S0g1-H(EO~PBWJOv17Zxh4-8=QAJy}ztKVL0n> z{0Z5-6KSv`eZyOJ{f)>$SX(jKnP$8Vam=g&1zeDdV}FhsVdD3bHS&7kL^*tQ~)&? z;_b?d{RF~)$LjCfBKLM)w*(n<>7OS++z=5M?sy;>@WG6)&U2(kwk_Z(O(<-{C&vz# z6swn<8^kIvuPl}-tb|0&?;K=&V-h~6Pb^@1i(PD_L`N#b=~v_Jhiw?*jl1@Pb$qxI zIf7)UOG`Jz%@ocE=Hc*&o!J*g>23!XM<9*4+T%kwz^5Kg8e-94KN=!(`ZXn+5;VWp|6rPAN@S~(xP~`= zkz;4i)O-PXR~q3W#Y90t=E&H};BeTUG8N$8*#^Qjp@^`<5yQ!1Z^0dkcSQ`#5yqB> z#*u`|PR^>~NVYC3$xYzPv`i~fCn=Yc>`?+)OHp;;FN;V7F?(ykds~0FNYM$b3|RiC zc7Kqu69$xMO922A52iks9jKsm&?CWh`e%@uNAjwvoJ-Lc^1otdHhwFg30{GW*1PG?7@6v{maxitUD-p-)G=kX%S(bvkIDN$mumk!hSV({` zkUnz65(`=dj3#%BL@rWUe(iz}^E940I*PA|?#%wqFbh^)=!~jx;9eQY%EDAeSP% z(}b{sBD{a|N8L9^g;OQ1GyVOx>2m$}_}F^$<^JUv;zQ4_yXodQ*e?gsif7w8KNJ}T z?i*Jf8|HFKUl@ANPj56mN?!A}D!hAuuI{(l-_fUCE5NVLLWW{i;%p3KS0WO)WZ-D# zMr=@?UH>I=BfV|nh;2} zi$bIzxa3E5d@c~hvCD&MdO8+&NggJh!?O|jt%j|9ryUAK(!T?}!CS;UD=QU-nvko+ zJF%de@nTu0(+JijrH+QwJ?945-}fXi=|}UKgbwXmVtPWAM>E-h22RdBjk2y`r8l}( zED=F1WBxu5=^xqE0DKj+t){x3#st231s(t$NaGf_rfr<1Gjn0a&u8v%Ww6su5U$|p zAwLJ7itjt(4C25)6)Jc_0R_*l>5W7`3v-XC)3s8F$EvXF7ve&0lhe`Br7tk;d-b;! zzSlb#M8?+#FZ}=U!T(DY67x^ubG00f3ROZ>Y8IjJx;4mS5M+U}AOWH|<*&Y|MmY?~ zW97vrmN$iWF#bxG;k&q>SLg22F{pz&!uOANJysGz_^q)46QhRLEU!c7z-jZ$#2sKixC9reh@(oqknRX67p z`;q|==}U}ZRj6E4k=XeAsQ@ooOU(f}I0ZvX&qyh_*#wJp=7x*Zo6OCl8^77A$PzoK z#bdU`Ct)PdO(vQW%~RwrlPZ2-{9oi8c-L21M~rpde#{avm+*sK^K#Aicg)MD)b1L> zzhhXa*lEHBpfMUcol)?V^TL>AJ-)e1N)>}gZ#812`t2ZXCgISdn`0tJrJDG-w+$g0 z@G2RT^-^PcFWV{DvTjB~ws;K_O)kYoJjlDo+DCX#Kc5@RXb#v%T913^U8{LSf<>4s zPWqG74Q0}ylui(}Zg(9vCkfYIRclnjMmr*XWF|uo4hV)kGh|cC(C@8{Ulv}Vx@K4qk zoIP@L56R8O$N7l-K=&4CE;N_a=Mq;e;eRmxUd7r*aR>BD+%$8fJ*U%FAffH5{!1YK zHCnYNvYT`{R`2Ng?^Odce%5K|*TKI0qA>i25Vz$& zpPo=KroLJBhkK!UzF;pm(e)W|a0-i(}_XLApC|M&MNygn6Ix1X-I=c^0z^M5m|0AEb1PEa@w zxFBoj5+FPnaOpNRaXcBbDr2tDFf-nPEoN;^Q_UvrU=b#?O|*}6gagIb1ALKf>u$5v zW^nmkr@e@Sz7rBIwb(;x*|uLMSZ@PI>}lPT?~@6t)%RiRs&BtEq2!4aUP$<5aV5m( zd!ms!uAAX8*{eROds`5SQ7k6FE(qCPDYiyo-&zi+4Tfj2A~#Cq^9iC!gX{0d6OuH+ z{DJ~s_)VZHH&$9U0I2kn19%`0fE?e zg!{i2V2r)=g&Ip1Z5JLF_lOx3_~CH( zpsU>#e;{aDIb?z*`H2fJ}3YsgpPHAyA9l%m;R3l`>9w z*Cp2~+{9he-Mi;2>3T&lBu^ovjjo0Y(PwWFq90;R!Fb}?36VL+P`P(7)PMib{tA2R_ zk>ub6+?-(JzD|~>LyiLqftv5jG{ULCEZJ~jA=>!`0{87aS*eU0+=jtlfA&IwU@*ql zGd27#$rk_CpZ(vDl-@tU=wd1|UrkgrG#cuba<)hVK{Qa>RcwCSDEYkf_z`ScsZwxP z-&+E%V3viMvFM)Pg}66z>9NKY{FiG!H@V$D?l;%|pLa(z-=%ot2vPb6A&n=wV@C)i9A_Cc;9C zNROb}Gj5uJ?QH zHp)BE-JjpU&OV9hFSVN^*T!7uq^BpYnt!CWcF@nSvD;`^I zW;bVSmB;~Stos)wdifz>i;UGBIL~OakdV~omsTxZT4VI)E*Hhq#^v!kdgz$9!L#fQ z!wcru^@0Kr((P!!PBfOm-d1-g&0)4rf0(ZwpI^_Uu2I%YPGGAp?(Mj38bU`faCgED z>Na@l2h1p^PIG)~sEzBIeTh2^)A9V9@+YhYncTmSx@ z5tL~mWbsh%hrZ%Yq3_5XKNj%41nDmAUXtK>pA$90v17N~m9+|l0Dhr9aZ&GSct?nR zBuJq&%%Jr7w?7k)Ah48YAh1*<`5>_V*0ThU=^BT}7(RqE*xI{9GPoLWLrev8IWgq_m0Ix>@IauzGv7?Umu zUuKnZ3)zTLU37UQ6AiFdmn%Q8qizZ<@8ppo*^JPD>Y-a>-}$PM4{I*5R$A{hi3gqf zzGZQK2C3eaznOEa3~-3}ewWMkNwD4@_^1y~FNvI;12Sb4N0Z+t8c(-j*p|3rTGP{f zhx+S2T7`4Na(@-Ri2sxxV)8$Q2{CF{f30&q>56P6;i2-u>Ra%^NQveeAK~(szibrP z^CiEpTQ_G-CgSQ|?;i=vR2Op}1ngq;o=TW=7qaBkvz|sDaNvEC;O*qf&7li}+aLRP z{=U5D{oQrN-}~p!pu#u#gL^{qpmf;%Zo)3YWiC(&fJp?&Xu?SyXDXL(Eb}P!C|&FZ zt_`jSC;wjCMx=UI(QXQ&HY2_;A_ZDk)h-)YAyEip!R`_wE9RFhkuyXRPM(Uhde;{c z&yx=}mI=&a;B+VpdM4Bf(;?Ie)Pjw6w*ipVRc%g%NpYowJ6@a5pT@<~l1ab5{HY;af` zX=32p>%_Nny!OVQ_ct&d#6ni)mhw(i*0eVKBNR3RxLMi1;f6d8Kgu{-iBFT({W#oq(PwWG^of7ppvWUVUD)R$D5$ITW z(a~6jGZ9I*S3>=GRbk`8_(FoqLbCciLvaX+^8!z~uQ6otR)fU^OscV+%s zv!S`IK2pH8ABzwZ@-Q);LIv|f7A(cnbCB;=Uis%wAy>Rp!3KLnf|B#@qVKNhW^uar zC)Gi+S}k@7QL7p3BK<*+c1yt|0pXNjBW4__*q2bzX4>gVtq3fHj4#Wq6idcKXFac`o1jMaq8NI`vztD`$C^h^W01QYLOv`}`pMsHEZk7aEkK~i zbY>^cwLaTh15L(D)5jNjhREfAzThh(iaTLkKqhG*+|m_@Ds3w$a)2-V>^v#Ir$<~- zlU3rxpi1VqkB^p8CQyp%ig1YU2d^d|l)xtK8ASFM%5@il+6yrH9_-@!5g``)1QmNr zfccIm#~Ssdh+Kng@Lk>;WW^G_K(n>r$xl;mBjz9U<$OuxY5A|-zv9bw@jq*1)V!gBtPJMMlBs~4N9aIqqlN8O1;khe! z&Wzo;Z@~s?c4YAWTOEHo_g7~8lj{^k566}(Lyk+s&1M#>Rn~gPj>A#fO}5XB$Hz&) zN39Ekg1`Gt@Hg~Gt@h%Xs2|2*krHa}vcrqi{`vb@XuWE8JZKDMFWr#>l`mP5y_GLv zkpUGilEb6aAI1AS)ck691ZuLX!=!4c<;ESyJBDrRGwP>G|>7Qw}2oHw65Y^ zJFpu{PmRBW-7eP;ivl9 zAtvJE9aBT9;isB+eIX{CQbKMx6^S@1M1KO+UV1szgxr&EUiPIqCV;@)(m|k4tdWJI z|G-R>Y7-tvLGID{j`flmazVC9xHkff8(2x^Tl+2QRt{S}!7(NzEBl4(I@vKU zL?24PZU39Q^eUZEZHN!qgLIq1fIn26q~~^ofMFa;?Y=#fo_rVJMrM`Ds25Zj{>#wU zgX<1s^Lz-RO>RIQN`d<>|L3j<@5PY89S8+~Lh|gb2?hKfeCfAq4``u=v4r?2{?fYf zTs#CWG5k7h!u#(j2>cwb7T4ZQ%5G0a(9H<-WHG4T)w6ZbgjW(&M zvwk+FZn3yCalyDWbqJ9N$>@i*A_!tT{c1TL9%Ij7ZwbmDJ|fybarW2K^FzCVYQ>Gu z#)b&3P^31h+$^{2%$vLw*4wlYNCB`Ma~KjMY`feT%PI;J;zcDc4bcc} zahul82{TgmlZw3Cqx`>z#Mz~W7EhG&Ez4-C`$I}=Jsi~N~vyaIz~%_NPf za4zvJl8OjEI8_hY9lmvO%(QuK?@gEHl~|#B)0PAsP3q7qm*0x z7OpZ@W;jg<}gRrJU&9h} z)O4i8$X#eGsntHLyeMeysHzxD|KjVQ7O0^&J^grUiSe$IO2ww4iwZ8SnU5jtloH#2 z%*<~-uYXOXj^DwwW@{4liJruZNqEu}6yPT{i?Ncm45X_%mRj3~UA|_a<^?vP^Q**J zuC*%T?@4f%bOQxDw(cji*<)nWGCU49qElpFpF2rV`6;>Cnfa$~rk|G%`zXJBPW%HH zn2ZF|wUC38IYPbEL7Y5=9y6v&$r3GI+e^G-3=Ezi&=_6YF1c%d!bEZkTa0`j%d|Ok zRSCEplM=RR^mJ5IrA)3CA#{-FO;^%GsZ0(*`j=)mUFK@4G^)5>J{#hN0!6HsYk6X+ zn!?X%6F;i>^tdwzySAqISG3S)aJTYUdn29+UUj(F|wd-kp2!%a)Ic|d56bjR1dK@uE zN&8;-hHN_% z+pHh^$;R$d&XhsMIW}Q1zr|9s)$;Y%8ZQKf!&hwe;#a=#YtR^X3ca+s7b>Q}ON_MP z#CMHSP>@1z8^JhoVI-4p0?mtuM*6bgiLJvZ-dTMi4~mN=nuoNN1!yHZ!s<46axn|s zHTe#0ym#MQH-Ki33%cmu*c%Uw4BbxiaOB7miTysjgmf~A;l4C8!jg?xCFN1zx}jL+ zS@B1)hZUBj^o>mGxvl~HK2(jz&nFmh8JDHUzp7l6gn3=raoCW~@~k=TQCVK~boy&O zc}m6{xSvYK5I7q7EL-4T%gy=kMPjZnAzzOqeX|u7*Pz?|^*Q9%CS&_thIn1-!acts z=&0@m2l>YGZ$wn^duDakFCU!?H*Sc3)n5AS<=`Y5-9_)Zk_EQV)0XyBRI9)b>UIu^R$mY_WxI6`$^*@EI-D2T?_~?CE(fcQy^glZYHotVDj1$M)Lo5+*PEiG ze;{k1*f7CU7$cuE!TC>(m`{zO(+aP;8aOy_Y{XAe0u5jtQ_5yA*ul$Wyg{pEL>3vm zr=)7wx@t3MJD(zE9mT^MC51uD*zjz?=D4FC(5}1E*m=XV@usEn#x1YQZ_Sp@V9Y7z z?JgIMq|M7hZ&Uz*7x<~CdJI^Mz@Rx(y>`#pY8o>%oEZ3l;NJ5#oG|Ge zTbMMkI#+*IaLQ>G`cLbtuin(Sy=taByxQ}6-zwNO8u@Hs*Xt(K~5M=M$Z*U)| z*Ml&ph3^z?gMU0>5cc_aNuh5+J=PgU4vdsSB!-OnHTINJFGj^!a*<09temKFQC9Z6 zoKXDjM%oC@0}L6v&}ctk@kiQ%#Gqq2L&!O&`tls)o@UcY?!ytG%>k&m<2yldd#|8e zG`BP?BHrm{5VwP-sj^>TkbTP$We^>Ti*$D3$*&$vqWF~cfv1kMr;<(z!I)+4iB;Sq<2^7(_0yCMRnj&> zXgwK>a%cL3Y>U%u?^Spp_5Usrl8OIKrM_q$rkF{KmkJh@M9!ldt#R~O4bLXOWO#s@ z;Khh@UuHXn;5eyxywDl8c}R_a(Hn@&Tm4??lH!_gWw1WrYnyCjV7TT?5R1)wAUXY_ zYfO;(lpnGa9Xe?amDjDBR&RAQGUlsri2=F!S;eRgM?i+-!e#e9QJJzrT2n%ln4E z-xJ|Oeol@}Zi_Naiy7IG?5qmSMOv2`oZpEG?e&EbrLnWNi+3FK_CE(B@~nJ7&Sc#B;_YWM4~^zfV3$5OESgFD?pU$i#XN^PLGlw; zsg<2~+k}xy8^W6yZkz?Hs!xW66r`pMD|jy`?=KbC?SJFbojtc*UcE%Em6OFZI_8Z>1U> za$C1VLCYMg7bRv3`jBq6P_~IuRtK&vk&#AIu1HkA5~h;rdy}=|;Rl!W4|;X-7v>hn zaUD8-zm>j(Je=)6J)W{iUo3|3s_3l$+!FX}r!-@kjRo_yP;dU13-y1)?!wN&)Xesu zcukCwp3K3Q=xDLX4tb;E(PXJv&7g=H^TCO3zd@W*RZEsC*-x{?iIya(d}s*bGv$r2 z>hj}TK=Ngd;_72{NVbcD`85~EX6v&-z&Bju$^M|=VP^J;RaYaU5y4@>C6Zg-)m!TH zQCqMJ&6*!ZKfcT?)NM*<>o(7~Z+)(5Y9##A$(7?`xsUs-B&4k~@L7`AlLkK6dyrwT zt>&&Cf+u&A^ZCmm1bUym`(K}PUso?rztScv8)fxmijDIZWt`H=U3Ws5nmXn=pS-3B zM%H_gpP7++4qJP&A6}WjsPYJyrre9sF|>G5rQ#6Kh67$5_604Re6$-)8eBxU7I>$T znSTv9rHby$Vz1;7EY1#{Vywn*ha35H&;!;-%N5`)1=#K8xwY6Tfx>YBC(Bbor$vGY zcCmHx*1^|0Lq}WPj~H!ns?5H`v`9&C>i4PGxKl_CU1!8`-<}MN;LfMK1Mj1a>7m`} zrH6Nlswhwg1OJ35zeN8MxY5QxE0E5$QN!}R zK{3O5t#v1!9@QT1Gth?O&XJ{sGLbV6?e(M`k+%!r#Y1>i4JiY$-_DCotxYz*M@(w3 zS&<|MaYoV4InEAo6fmOv;;&qUlvnI=mW+P=oA#lrSj&io8Axi}WyL!JkZSS_oWj3*w<7om zY5{2Q`4Z*8tv((Jj4j>lDr4h^r4-zF;V2sNQJ2*jMkaW8zDoXfID8wPT2NP!`ct+P zr*37jfRWkrx6}#@l$);p>r^cM%e&9@@1F_>Q#TXW|Mgs`+BqyJqVi)<)~4*S8UXwEOf?K7oRYzry(4eZEGzP;CCpd+e0k zvbU52!MZmxa5MU~G!ZmFsn`1EE+eonOu;scRhWaNEoOn?X(pc0a@}OR?b2?x|M_!% ztMnk06ndKsc4mZoSgk-YU`1kdKfe-d#A%Y*V0}bzdAG)rnB)A9*S#u6hO)zM`dQ3| z8D2mO%Y^&;+Dx7;Mf}zKbS!reF%QZ~A`QG1oC((uC^89HThJD>IzWxuzj%s*mr)Z&M@jY@$Js=D2S89s&aZULW^R$d?*aFgXbXGc zUNRao$@mP$P*mRMNGy@}1j3a?77+n5m9Q;Pnv9nS9kEKVE^;E40tT|^J0U_pICbY9 z)cAM7z#rcy3LS;030DC>q9^n^(Az#|lWz?y!88jhiGH@_b->Vubd0?Jq^=A9ji5{> z@c69_?>zHC1tIWA(RxCG57^$bckKOdS$L-KEuP6&ZNvJPqCv6$$CE5-!-*J`TBkayKcRWni*o)cvC3YvIX6dO;ddC9ULd$Z_({5xTe(o@XF& zUB6fe1LFnvL0%|_bWJixlW9xOwo z2pNN9N)?B7#&(JxYm0UfmYryeuwk}05+AWUG_@5pYAZ!`x2Z0vU~BfEQ>ni9>mE_l zv#Fd`&%rrosZ{f@nQ1w;G_!BjDftoN$vV*bD|;-|rJzDW(mar3(!s|oK!D0qwP)drRO`+qPP{g^tsK2OF zdx7JdHrB4YI1*)hNmj>Ki17}w3AooNYkRYqWy`c3VA^Ch?!C{2u(2}hJz}HE`J!E& ztxYh}nN8LitW(Rw~%Atlvj=>VNmqt>)^Mc^Lr8S~RbyFYT8O1NtSs8B$=8OudGh~9L(Nc!sK6Y|ubJ*U5n zzzCkQ8o1>pss)kV8KF9Ppm?%Fwn!qPH)0Gk(e1LAowD8O=+qXBjf;`FPtL&J=5rri z*SjD*@vRoDXp*wYO(S!ob0hXOiUbmbv%*XlKqMq*Oa?e|M&iRtdPtrkr&I_)jtA;O zV0ye_3x-dr&@6fhwJtGB`Ced=Ic3I}RkAlWDJ%ado)st5S;?Uqe3?OhxHSX zi3XsN)c;7TJaq2+<@Z7r&2Hxmx!uUzas1Wq5x&1?wSL`{>3>NK75=wxikq{ErICx- z|M8j>r)=1Nv33BT)8i2r3(h6+!Xin>bI2Il@C9mMbix&?K+-@FlX4B98nGx{?q8D~ z7&0UjDgO5`;pi92uw~vg+OY>hB&(C?^u6F~25`!9oB7ylp09LcvcT>?8 z=4wMf$|Vh@MbOiDU9<+N(OEae=AS^ zh6Ykb0(QzdmAUAP2cN0^m9gYdk^K$t`0M?Nvx zZSpp%IooWqx@TA8DIa?KL!ru905eV<*a$pgtR}rlnD_-h1L~qTq+J#k2 z6sjHFRWNI-;a6w62a`Fo_MosHqOkT`sV-6<%h~KK_C4`UJN=?gVErGIy>oD7?Yak; zj%~AJ+qP||W7|$gJ2pDDlaB3V$F^-dous45IrrX~nyI;GzS&jl|6S{S^gio{_0g$$ z`y4*f+$!JTzv-CpQKqW?Gp~wNdwM^QkU#O>16T0+S#w36=~glH!c9Md)UY4(bh&sz z)ca2`>ecn|f0h6(Q!iMikc5(qTmvt7sv%EE+$bds%IN~cE9Sz~1+yekAtE;(kE z-QpNUXJ}d-$kr$Hy@7hx^_`)iu529p!VT5GncEPXqV?|z6qS}fy|(*wSd`u$Ur%p~ zu?cgB4q;>{S*ql>aWS`(iFcqAsJP0^rsP2vqb|r)$UK$nMiQo&ORHMtu+W%U2^B+l z=grX|uy|)o*`kk;%QK!5ji+iHyh;Sx01Rej4oNthG^_PJYv8)|sm~CxGWFEPQg_}n z--5?O8xBumY3rlS8IGVPCaC2E=e$u?9;jFB`YtKk3&GYPPI9_yY^qJt)&Ru#*vDtl zaHRtbj)i-sSbcWV7F(XqVY51a!844wH=11=UUX~P0on)d8a!{liIw*YpG6f}j06%I z_ofxUp=k}vy?P7%0T|bIJf2kjcW=}uFIAMZRp3i=yhd%24A`l=bS&1BfxS}tEp%T@ zD1B%K>_d?@X2|KcMWn39OoweIx{rSP!?t&w>CBPN!Vko>wi|?*N}HM~=@<}t^3M*n=0OJx(2GY-Y>ea?n>_-X_wK8Lnb>JOu&- zPJG#E=amal5o?jV>wx^oJ{Woui_#WjUvJtH8YLutKWM>dof8^c?4EePmCd2CqTGC< zJj*wfzcp}Ip{lmaJ(7h&)sgSi&1g4QszdI5FdhzDp`+)Loz^3hDNGsc*S|Cn3UI)B$CcgZH0{+s-3j@4;gD`f*k z)0wOU?^^Ui$tBG2(b5ABlHdFptc9k|kLh{|pEUK)TqGmcJn{8(&Sf-uU#NTYv5WNP zQ+~tbwXmPFpPh$y`BqV-{yn5wq+4}63`1R7#D5KF=iS(fekynK-w=rMahO%OO8SFY zIj$q8vVA4YELq{5;G1JwXwJa#zq*T=^S>tk|NH$_MPC;5Yg@X%d}OX; zu2Y_}0MwWfc|esw!VorkoPm1ast%66tgfpTc&LPp%*!L1`BH?iV4+SLdXZ}?$$?V=Rm@fIhuJuD^1HcC*9y;194#aAi6BjWB9e+oRX-MV`|nP% zcm+pK6|^>S%Fs6RghDAuWk}KT<4V4ljR}8oA!1+#>3)wT;~YK@(Kb@jV4J^^fl^~K zvy1sjyfKR{QZvLB4fU(Jxqd4bXcFPT$ncdsNR=b&f1n#Q`?{`I_QinMncRo2A&q#0 z9FMvL#uv{9t;FX^1)&nV^uZne7qzJ$@8l)!YinWkAGa3&c!K?(WB-3`_Qh)deAd#@0=KuAt1oGg!mPhq`IyMA-TMsGVZ5B767kBYPDNTJ2D5X<`&IBUWxf>J$C%YbdkbIXb^+!j=9i>VCkGI%&k)vBn# zhD?v224$rE<3p@+k1bfyPR{ye$inK@9UJE>VUwz}usCDFg59WO2Z!oj+*FL8sf^-X zokYm-T4rf@M1Q~@OrKu{xQcbYR63aGLl zO3%r8XCbt$>>)aGDwNRjatQg>(r0%4#n>24GV0V0FHp{uuG=b!0-~(|AAau`nW=rygtXzA-m15EGf?te5-`{epa>+(o+OulQg) zQlxQ-Z^bOlbtxik(G>2`ciHn$LFN{GCbAM>6r~Hwb@B5C6}oQDxjRi*Yo#ep^#!cljml_0&(~%_ckiD36 zbaK@}`euv6(d1Nh!28D^uwK$M{7ig=Sc+lHVg6XmVUA(MVZT@?aXLe3$#3ce;%c%p z%CZycU^ZGDO2hPEc2(FbgEYZQ);&g+jfjc^a8|2gbBUF zdMAnsQCjQzi(@l#Oj8~-djvr9EV&sN?&W;FQ{$dfGq$7=Ow4E0OQL$zPJWS^;w$>N zh`d-uPn?5fDG*R(kICopn}?VEl^sAVab5Nbe;Bcz=L+QN7xg}cV4|CAN$s?`!+VH2 zaKzJzq)+OSmZ}GiFxuz#JQ8;QX-!m=R` zarO4^{daK?QsmxO%>4O}o#Y?I&3_{mV-m-|HXhia)5&ZKG5Pr+shF_#feX^AbXuSy zdn97wty03@S7P>+)5dUghsu$s2S7ROAW)HdMTw`1QfZpXNBS-7w*!t^{65~^U+{YM zU9Gj;YOi)ugG+)_gPWsp^lIyO3WB__8n~_tys6x1vH11+$dPQc?i|5>@!J}%iW-`s zAp}ayoL!CR<50}O_ZuYGu(mQfQdoB3ol6FqJ0_-SHL_zodln^W9UgE}+@Rq%mr|@c zKL&ENdr~QRB@RVSI^caOr@lByczq_l$xU4I`|Ucs@LTOsz3=sqxld!IU0W9#!~hb; zw#TT9PK_g6=lfiyP*b*>HDO!3lI_pZo0E4WvWLAY_8rwu$jGJzOjD`R?)gw}UQd~l zMPJz2jjG(;euV0n=W~RP#mI^1RDB}V#n<|GHuyiB)f5seGJ>Znyi9;Lk??OALDR@V z2hiTD9gdhP)i^heDLZD_T?60yB6^!pLG{jXF=btg#VlyHW1SGrCaGHw>$YV^*>QfX z$9Jr9_ilX4dXjuvw+yb!F06SJ_+8>&Kw&5!<#NjHYGV^NE(KnQdKY>w_us5|L%bt}nEH8K`ZP0py3PM*!E%97bDApS6POv2%$;wt#w4V#?w zYnfk@Cx?nBXO2a!-y>wdBzHe|TtB5+!J$8cG4u)pB_S*t%c|Tojl&?;_NW;7p!j+s zOt|~t3{4~o0tW*OV-(h|^V{z;xh2O~yO;*82{`(~%U0cJQ>~VPI#WOUvw42?yEneW zAfsuCKj+8Vn}uKIHkM$o2c+$y1?)dl!NtTtmN`S=rKL0;^P)#LOtKhbZz@1A<9`qh&i9V~ zZ8r+1m)7T2gU@PrDKMNmY|o6UZeSmsK+wc&!lD8^|D6}6G?#BwQmJUa=Fdb&S~Se# zkW;4|Jx87Eps0e&gnsN}4&Pk(x1?MFe2Y;Kyb166U2|J<7;x^74zzKgjPf zX?KZtDdV;%t*78>OITRD;@mE*u(%gr<-c-_;*`MXmP7axzeVTN!zQ`ScNibFXSPQ@ zEADX4%6I9TKaf7d{>^ZN`=Obl5i_ULM=RmgR$*JI-28jTtT3yB#)N%r1@>=@^NoOe zKwPZs(~Yr(DvnP~b94^NpP9VqUsr|an15NPwV73-lV^3+x2gy35$yX*u;oG|sU3Q2 zYH#rV-tq!&peSyBvOFVW-G*;<3g+KpIpv_9Gj|W7ZS=8rO48wV_SDokY@>&5%4%~f z)MNg=9_C$W7+FSC3!tQZ$$j}Z9~+ls(JbXF(J`R^Q?mb~M)2Q6w_h)D=@sqfv8@%}qy`y})oy42PQ~XHT#*3hbt4 z^;(0+vZgR~pH*v{rbp|plZk)}`@~7%yfy#VkEdxL2lK5b^P~6vsBOU)cn~@C>7s|i zA2@=^x1>0q4EutnZ(2hEW6usaf=Rb7I6d*VvN%17w}LDmnnN{aZxTb-W_%2A@uBLN z9NI-=#vye6-{DH>rs4;sw4)OTZM6@^3$!q0&FVxjY0aE+=rl%+o9O!MEfWVdwDriM zqRgBk=onH4-L>^7q6q3+^)NL@_pG(`NTZ0x_H?xMD5K8JoPz1>lLzJNTeUG;M)owc zFH#2!>$#|+MC(2EFqO3%NA|+$a+1X>>2eYWgrwmU2Mnd*6GB8$3(1{I$cChhtH|ca znH5lR5=WB7QpxJc6G$WUq@#ZgfJ@6I50FXAr4FD=%cTskNoSEqG>|7yMw}+55l6U? zF{`2;%NVDUElKs0N8m~?$n?|0#F3qmDKkbiCEmywXOrQQ?Iwv8lD#EDOOa4TOeVGp zq3TvC#hU!Cg&x-G3k|N?8S2w00}{iR>gInVfETr6B06T7FliltgmVM|asfaAEC6;6 zs=g2SVt59;jsidtyvs8MysAU#z~~;=z&a|r41?#I#L7O z@xWi~kXf-=aO$z^aV+QV)4vnzSp`Fnvm50ja5&na0X6VPl=_thYbPMrp9eVzn-QY0 zFCCjIYJ-f}foA2kD74tAMg8V-ilK0h>NK`}t`LHQyx4@so@u+w@Yl4@*~swJ z@Dz{@By(lrLiD^J@w?vu$JnvVoqabpfNiEu;0*&13S7iE9>WP&peFpA=Pwe4VIOS& zp&JU|HaatUjWJiR5E?x29NV2)Z`u(IC=bucx-nL(ivraC#qCKC6c9(hG5PipyZaPL zbwD1aRoUj*Lvplh6PY$m0(WCAp1i||p&aQzTL539JRs63k8UG3nO0kd5sg48uSR zdL6z~!@x_(ouW`at$`BFR<1B)Svl zhc?R7jxazpvh`Q!HEp40n^K06WXiTlIcGjtaVzJ|1@ zD2`SFJ42v);Lsxs)WE~uu9xWcnY8WYt%I3`z{s$FU5Lv=A!+;a-t~eJ20SPS`O!i- zz%Gw@dGTZo|K_-z|GcXyN3Vu9O?L~uLjN4r=lzj#3 zwJPSFKhMtNq7QP;CLJkot(n%V)zbV)bdKVB7&(qiJ4p9$`yF`2t0&UfDl0%@b>SA; zJs3PvNlniwhr1Bc6co)OYPoy4^G!Z}N14#lrG12Y)gd?9@-wvw-NdaJTlEz* z8-Qid6qw}*&$72>_4HxQw7youRNey%6w@ZA%9Zg!Gabd6L#d2l-GoaYlxiz(P>q^HrTFz77)iOp-ygM^hTJS!i zpoogyVYrg_K=v((vO@lv0leMWrqqKEkw31XbL?(PLpnNd6LO!UF`Ot^wmq{GEM{%6 z1x=UBsh}0r#=B@>Prh$!-RtbvOt6D*1pOx6>=pduT->F31Y>~Nbm}#aZgw8#B}EkN z{;3WQsZ)02?0LKHu#`_}P|0Jdcb&rI-Dj@GnQMXD);d(&R|gQbbA52w3?KSC7e?n~kNpHxm(0duW_ZnIhEmeoHALnl9SeBqMBGY%?aF1;J; z6rAG7n{bPu5@GnGmI0u6jVt1CDdc!La<4i5pE`MpD<_dvliB;f8E+Q6@$|cMpv1gU zC=*Hf6WG7y#;M&xzEvC!o{nNKcDNmtw3O;O1lzmq$@wY^m@SHk?@bg}?@+g~HKwc4 zKPmJT4UD3dD!tk4c0L@Ij#&UbE2j3dMdna2g{O$C2Fr^4G18gxJCRIR- z!iwe#jkGNtdL&8lPga~}1n(>uc)d^64qxY-M+|_qw6jFOJCLg2-?BCP1(;_(nFWTo z*eC7czw_)kd8AS1z{Pk;Q2G(M)DUxspqVJv(HqPSOOr5I!77kegv#rzgH!JE)bE4jF7TI-W z3i8W^mA23*O-*%7jVcBPE&aw?V>d5#@sG~cl+en`F*tKZhAKUM17$6>&SVxvB=U5G zLH{1g}@YIVv<^T>(r-lNZ02CfZX{$Q3n@`bculTG$p zlMX_!cp!2yvNv-D-A>iqpDu=(0}od2%r$x$6}a=WgOiTyGzjTr-VLE84w3F^ZB{Ab9nA;~ni%zM&zJIm9ShCD-EKY*P20%C zb5o?hIw*uSo3RH}LUojcIbRWu%?3{J_hm_Vyd{kVC0F=7bAu3{i`A2h7+fdWlF3XfFQocywl?!icR1NClY<@h>b$c^zJ#&Kx%u=r5!dJ( zDv?rrjNk>Jw)bxq3AhO`_`$-IzQ$mCxgED1p z@WU|#jlj&pJMxEavvJ%yKb_3PbFcm+2iH?r2oA8)fv5nq}+9FTs0 zp$#^aY89(~R}@;IJ$0%F?4>)=M^tKtgX78$q_Z?J2r<44;V~dM_9d8`t{L~}&^COc1Q~F}ox!T;MTIEYM)?HhL;ntx&+N2i#fCn_+XbCPiWO|1`~oj=pjy|E{0C zw|S^HLrLYV^LAI^|FN4TPp&w84Qd*&4kv&@y2U(=VIaGx^`#q}`-kabhVkJ8GA?uQ zw0gr_!S1k;gY$u7`V2%h*aB@^-Ip-HFNA}&X%@r!Ml47G3vK#9vt<@l$wqt6>F1o> z_nrIJ5w%c4k&^Y_`Co|GJf;dNertipmid5Z$1q>V-C2163y!-3@PFRjm;fcQ|7mpjZ@- zg7e!u3Msj&1!(PY0Y09;a;g$t;;y0stmc_U{Z%vC5DzB5vROMI>Y?gBAl1#%(&Nj; z=}Bs{_!>DsPJ}FRs`*wuFcL8;2tH##-u5w1-xej?BT%xbo*N0AVtj79)bLpH65WNS z&XuT|P;7-Sx^sE^IjbscT-K}%MSww%f4e>>RzJRwzm&7q5h9W&A9nPod$)DGvB~RB zt6p^vsnf6gcDVMIg)kdPP|y(CPJ@@xXhuVg{t|g=u8whMrNi(?Vj&8hz4Fa9r%Rx_ zY4&Grvzka1;;;#OoLYp)1pNI$NiB~EOpCTqeid4znzzWgy5`Eluw&D&!eH_$T1y1Z z#Sk}=OwtSo_QX9VI!(vVotMFgop`vPLMh(0*FOwtYE@WcB*<}|3Zm*AIEn);i~CaLb^ z5R?35%+c+>9G-q*gwj6z(ROETslRj~D4{Bm*!`=aRXcHI38=iAKx`l=baZP1W)Xzi zR5surQR!)S5hbfE&8>m<2o|kr!MQmSpPDGkpUY~2q9&_U!UK9m?!>uGlZH!#AtnL` zA4(!W^g(I!Glm#SXJzvVQ-wh^!avy7VE1Utl6FM2A7_)=6MiHnT%3%1J*FR!POh+Q zuJ9!p(bEdh$~MhRm;<4_ee{}*+E#0`eBA4x z^Z}E#?B+fwwSOwhTT*R3k}e7ByAWAM+RJNGZJ!b?mJ*r-5-md@_mtAAw%~~~$ffSH zka)l2KlLr8A?1Mf2?(*i2$d`^H(;{1-wD&&>ZdNkZzs`b^BQraFK!VOuC?yFx`!Ra zyLw0gc#Fh5R*bJ&;oXNen29bz@XDS*orf`#*$FMrefHJ`xs90I;B~d!ubbyQYj`|XO5EZZvCAJ%7 z)FI4r38gB7F3>QK=|2l=b9$J&N^m2N-fjFI^X*tgvw3Nfl1W$4KZD)f?M^=)jI&R# zHJ#%QQ@xV-JEkl$pTZ4xL{p;PjXnle58= zabu!k!XA-;wB%yWtW|H|$`{o!>WQV<0l0RZnf^3s#KOTqKRr-UG~{j%_0oPhwJF^7 zq*T_1h3`ld9nxj`Dv*Zwnh%0FGqtJ6`vh{iA=>DIG(W{JvQJd(g*Yz!G~HH zqQ7C+?`WsN3kJIEl%iV%NT6JB<354L@W~tzf?>Vsv5z9AIv6E`2l~Hv0~)ze+17do z0%k>9E)|&OdqARCFI9DSTDd@AeL`#!8LW?%Gu|;M zkve6$o0u|h4Qz8Xamz|J8EFDxRJ{nmnA^c(0x<|Sy5W%vE zuoNt1vn0*S(xNs$BPg+3gQyDkMiETtKCDhqe)nmj@`$##P|5`QA%6CWXAZO&C}yN) z_bslFzP-|=O6e696pG9r8_V|;a;Z8I^=ouVS@pX%$o6ljXly?oq7Ax1T)&MOV#67X2d&u2 z;j$|a`Rf6dj2B1PB~@~XoIF9=S*>^i;wW91ouM^4v!at03%(5Nh7JgskjM zdbVE)wyvM*;~%w+fVcAZ8Ae6p{y#2K6Dt~85_5vYL;};2PgH}&CRxEbcv4p%+UWk? z@n@m@u82frGEc>q0sInnf(bY~Hr}5IFK4=A>D#kM0Z4w-nsGuQPE>}l8HN-(h795* z&dH70Plvpf5P-F!}z&C5O)kck)is5T+jx2ADT|!ow2|jd3?m{0@cl? z7(;G_U0r3;9Z=XHR8K2wnY~Q%6^UW7_ic^29WH`~#;bvoKoj%*Wu^-*WmOpxq~5St zsmXxf(t@tqrmR@fuxKw=bJ$>Luv8NuaOT1^=JO-mt%`Be4)LXPQb#W4i$<+v(;+%i zQ;l&;)WwkI!77;9D%Z|d;&(o~?HVg$ynhIa_YfXl>76FO{KhJfvEuqqlFRReNHS$o z`jQ2qP^V*w`uqptFIkv(7|8@EB3YPK1Yljl0hUt@JXMlP!Yc@9ttkNXY|sJzbZ*My zVskFr*e_wUY%YtE5@+z&66F2Zo&nc-rtH65bhz0S(&dyR#Pq%faZIHd5vqz3#NP3U zNh0}|gV@0$=wcwls-9^e6j{(3S>Uh=q4``0Lp$0`DANGGiC}In0?ZxmT5OAc7Cx*q zM@+F34_3_HVl8OiI_T=bwpzU7J_9;{q@zIN4yY%h><%_V5S9~hXY}C>hejD4jS=Q= zaGQR2hlF18J_s7#G=U$$n`^_Le_L<4QczN3>z&M3ZuR*Gn&v)A=B|si|FAB`_|(vi z=b(ru>EOmGb#(6%2nOKrW6is;zJ&$LV6?vc$Zu5Iq{(2Dre+!o{7m)%5X&LCb0dns|YTIj93&B|6*8_gld+CVnA;fy6R-Oo1 z;$YMXe%>9JI7;DGvpq|2PbUtK7vWpBJmjuQ(Xo#NdfyA**~l*uT6h*&gT*Y zVPS*mYg8)#8U%Lv%e`-QZNiUQ2fzV84<~&TuF)qg0U-&sA`rS-km6X+@0S-brnG@% z9;SWGGRDE3kN@jp|7BrVrgc}ZUm1KxmWx(3Le{~?_%s`{BeVN9{m^)ThSj*hEO|WmB2poHtjyRj@4uibA2AxreN1HcfTX56+emAg-77n`xo?$9i zKIq@KDF;6RPn8BwKTo;zYKWBRwWcK>!(xBHNZREO-tHHvV5v~oSq5Z9P9~>)hw^K` zh5n!|^OpOJmYPZdBGR*5&s1}z|MBwHIvO{iqsx4(eM`VhW!#3sStzCO?uIOnFknIL z3PtNGqVz^glY{UH2-Aj3qm@39HwNnBGX;ouJh-F#kJin>R+hors!HvNqFWG_XuUuW zj)^jXBLS&=;hk@t(g2FRbZdRawg7ckBFEY6p46*Ge_t85Q>*wp{;D~ZOmGB=lZ;%x z@^>mP6`$CtDPrvs+3lZ z920?s?3qoXo9eE%-Kt{U@OHUDvXATNr!0 zkmcxU;MRj0rmKc$6^44)DsaG5c*^&o- zpIJKo3{t(l8Lk`S2o~W81PMHQIPMHPLVlpJ+^VLD@ucn^SLmCpRBL~)bRRq`#l={@ z_XUT=SjW3#j@duKfIJ;&_|yzGsoN z_m(MK*8Z?l=oY{%L-ARyhM|LJoL z?f*CM&&}1!_J0JEQ`EkE^1e7dzfz0jgN=oSwywJ)ej=fR7YAurm7|+JeDT0F;pZvj zGFW1J#)jqcdG?WRB)RQnzCi(%lG3|;S}dlw_}eyr^z?j!GlgY?q=Jor*V6CRg;SDf zh*w2uVcp6Nl!SMNYYKxik@dw%!;nbmu~HC+3&WsNkcBXP7ZzX!R!4-)#5yg@g?=|& zV4r8XBBnNhV6E0pOc=7mQAA0(#izz$ME-S)`enN+H8NtXA%S?$(#%9z_!Ar`#MYdj z(Qb#+UZ7n9l$Vt5IG40YAU?gd*$uXK;di)y+Bhm1qD``C-__#iqlBr}HWR0>%%xME zeaX*v8dO3;4|cXwrKjfUYlmsBA*_&*W`9Z!GQm3{4E38`IS2TSBS(CMuFze%OlrSz z4i73y^6wcul&AizR4*)YX-S7b@-2bjreeQNI8%QUEPCXTkBX7hktL@GlhqkzLa;~| z*0SdLXe@RCoS#Z4>1!pnK+n35(7x&YKY%s~?RHjC4Y-vQ1k0JPE z%g|`4>+YWeqL8-^Tb7pK;G@HR7~A6DO*CPLknPmfDk=!^9aT#!!~wIF3=F^pjCh)e zLUi2$lw6_KdSSnaHLI}be*bFDAfJuVax<0EOujgUVEa~L$_W2z$9+*ISA)0D_}X83 z?gcz$Im!_D=L;AW81@#f(X)-PvyLVw9>?%r{&!Y?;=2?(h;?v&U*3|s`dNWsjl#wl z62&xOP{~_j3U~*Wd#-py;|-rw&BjQFtXn2MMEnXM5h@0hO2u zRCYDO4^FdG+O|@`ideah-RUWB7=?7Jm)4hL6lK6rT$sHE+E>Kt!gm}?5|#+ViJgAO|>X5I& zEX;ushf6j(0Q>)qWl%!bt6ctqs|o!_xY|F>7qkCj{DvlsEA$JY13q^(s9W|RuAyOf zjf;kgu{kNKm9Vg}u1d^AAoI!PjdnAi5$*M07<@s9#ym@iiBS;j{!XXQU%&D5-#@R8rLxfK@Rk zS2U$wBFYKuOjnz@4`{9RC;M8;3}fX0Unv||LMofWgotox7B%AH+E$MhmTg~iT-zrH zqxVd(<`O>0MGwXez}#KxO=!LZz!_2S&;b~JeR|4}YVm&~iCLP)2OYZveE(@7s&+OE5k z_y**#qLUgrEpbW9AN(%43R9pufMXD_i_?Gqxf3DIht+y){>;seUBJ#Od;vjCQ$eJd zAC{j13t?s@@)I@Xmr-62|5|8Y^iD&-v!zS!LBTZ43){%C>lLsTPl)&5=tG9$@Fw>! zXiERrk^b+{lz%|x{&)G4H*@`BbGt~H**g9YnY&oSMs-~Roqt?bgqbl(1oitjN^lb< z*aPYBNW&uo;P6fOsDGW!yqv_$R#Oh-N(J*+-^t&z+=rOVN(A!+E9Plef7;0XJpI+- zQ+?HSd~u~;^Z9W+-2*~rs3@j4&>ZeTvK94g5W+yhxFh@JmsIgH`ezGhH_FZLpV#1~ z5SE+qj+#N?(lDE?`{~pXzV#ur;Zo3s5+XOv zPoSQ$5Yrn^6Tu)H@zCE7UpQ`OSmVG}`i((&J<)qL(~x#-rVnn&euAcgF)>X$vh#Iy zm-;Q3sy4vZpREDZwTvy16dhPQVQS8c3#u@ha%EGqj)@Sm#{n9t2;+y%#p%upN3x~& ziawpz2@Mq(vR?LMRv#vzy!I^DP?A6daSj(BJ&#`sw0`Q$6MQYwH!7_Vrw}h0E3plX z!%H#_qYNWHh;=pPndR-jjLz;?5p%fs zJLa;w(l1jIIIVdCay&3{T9btX+Af)KqI={Zgi_woVM>SF`h1lY`eJIhoGRk z=`}gY(sJAh4N%gFpe-Wn_+69~Vaj)OE;W#NJ_b8V4B!%N$-y5xP zc}DUfn4Tv~pd;j# z&nfQYsQ_}i_1l1d7FBz3hP_;`k2J_P&RPICwgIa3y;J=+4-^Bd-s}()gw~jwO2`f2 zXE!kOkkiwAFRaN^d6L(l8SI$op{Wr834z%V;c}=zLZMHXiZB6+Id@!1#Mv$vXeUbN zpEj@9S?Rw?8sjR}eVtT1GD|2OACaJN+KgXW(}9C zI3_U zqE(;b&3OSwxIFSa;QPu3%e(3JGeOH=#(xcH_9qW={9h7~G5?WdO8*ZBG^78)&&4Fl zE3SX>Nw?W46gGt5i^+?91IQngqGZ&O#Jj#LDZ@yC_XWd~mH ztKWkCEu=B02Va)jvN&3t=(MqUyZ+4e0U?@+uEjSZ@3#(VfM~CO&-RJd!fPfYd)CmB?C&inAGgR06UI`R=aCAlRaKMZ06()Xo*z^eaczdTmk2< z@wC~I)9{D%`6xfzL9b+ERO)d=8I-O5kC|R>%%xA^~JV8cw zY3t=Z%xc?bNAa<)_t62HYA!B0ge!l9^e$-18wif8Or&z6!m?$lodF~5atXrqQXK@v zzZ;6KT8>$hZ($j!yD8j6Y0c3E-z9uMIX@!ivIE9?ZbPJw}znImG3cvbO>woM||8(5=Z)ELnEf_y6yGT4By@Kz?qG<_UCcoTtnJR%+8eJhbXzpab;T`{7Rtgcv6k1Cu3oL5maZ`6 zrr%kmDU4Jg&rO{_bgz7#x_zeou6w+$CWmvtYqgZweC2?{?0Pl3-w=?jJ;g+G0saWD zYUM{HW2eud=(0uSZ*bWg>(%`ZW9ww_a`A)a#lwyR z=w|{s{?{E41|9`{#~p%@-_C6~Mow`I{tVW9Quc@1PNp{HCv1u0g0f0i-s%uLtaSxe zqZn%4PZU-LxdAZ!BMw?)NIFuSaPsyEOG7)F*+sO`#wUtNGuVpQ?|#iV%)dH`bUCu2MNiMggoa^Z1e2&!xxr>dontLAs^zhZ6=-aXRVnRQk6_?e za{bhg`GY>G5BtFCE;eC0Br`C-ahI|0#M(q-T{KTosV?qwWa;cA6NSO4>tmd4UZIcO z?^mI;Q3?Y970}Y1V6_o<%#x?c|1HOP?)DulP0EntH=c z3#^>7H(1}=SQ>K!pz)W7x2>UuCIN9s>)B1oTp;Cm;&dMtK%Xkhb=$V5jlR73RD)@L#s=_8**G|lf zqf?kGN~FNrI#;DKwY`vZoq5QL&Dv`*WF;mwNGZIFx|<{ebz$B8XW>)V=}#FN<`R|7 zm&dl~j@f8UhMe0aSo^myMw(_*k1Q81I0{Nj+kK49`B>`{sHli?rA#sa@@dR{bz@IB z9h4?lBP+ZgXIZ)Cu^ct*DpstD`^<&Z#U38jnL=_GQm zolTlg`RG;QF1F>)i?D%AWW!>E*<*0h-nJp*bNj&=0(51k%))NPVZmJW7`OTi*>QrJY=M-m9XXW2gd?rp-S=G@7r3^Qw_5&xwBe zVy+en!`uKM47^s3YRUE58p<0{;jGpp zI+f^wdR052R_qSa!O2u%``;_nCzc+AmDFRCxY;VQN8xt#GP(m+^vqB!R9DxJpA=4k zfTi|tSzdT&PkH{x>ICmv*KSX{n@w-mKp)5%z4Jw?dSm47Se@c(BH2Gq*`*Pq^FdR> z6BlKVPez5)t8qzBhvsFwhBd)Eq>bhlusnVWQ!aC;nmH6TBV6J-#0v=LwiS+-0}nbr zLK@6b1=_wqP42A7luitDzsAH$EXhi(Xcq5MSjv1tN>!Ny+!Z1*Qhv-KXR^~K0^2Tv z(p`qESpAF5af91@Mc-Dn!8ZzwCMabEM#6r`C5-iSh6*%`Gv}HK@eg$Yav|!TmY{0e zTc7b)NB@!Q$D7%(Ki{gX9(}g^7+}sp6uJ;9>fthy6_I$}w^UA@U-Z268BSH8oZtx_ z7vnX zzvr5Sh$!U8+a!xHMlvZmQ_XgqdU12F10A^rltg*vRS& zR9}T%V^V!W+~L9d(^7=ApqR98!QAT+s>u1KoMtUb-mIb~%GR_weH@(<65X)|oq=!| z7+9-)-q>rzFcWXguK32?9d~=b-lqbA{tCyMIH)C<$WF15O#Py1AAjUrk>&gI>t1fh zpv_VpE7Rr18hhxIZ&R5ozg~YL#NrzroVEY8Y5Llw%kg2Nu5*O%VsYhyX2!BQjed?^ zhV(NYSySpO+bEkp%Vg4IBaD`gO7$8YxiA4)8LOm{W^A5yY#xWQsEAtT&#(lCwNL48 z%9{n~mnq84pR&^3<5Jz@+8sd$9f=1Wk%`NB5X%Jw%L%c|NdV3r;|;lTUC)cnF|VsV zIgcHyjnU`L!sOj=X3T_YxHs{0;bAFmo&yx-nuN?@t*2-ER6(Q{^r9PKS^L9Or0{~_ zvP1WyAL5K2OpIokStS^ep9XT~Y*VMAo|I}i;<1Tg63t@2=o^B=3Q~L7u;%zV;#HB9iDxw zie3GK^A}r}$YM%RT=dE1oL?xyV&$po_p6p1Pl$;+cUqgH!R^JP#<(|D^s4F8{r9nI zN4Qd~`HBXddE+F1(QpSy3+HT~T2GIdY#us>jV`DtiRr*9;c1lIW9}PJnj4ZB?+8W& z#(RhzjP>tv*azd@`p0dzGijQHFRfh-e{0$PCY-Q7yN6D+}EG z_}6TDAeMX~`Xy~Zfc2ktC;xOd{U28wm21U$C3JoVyXERiuy4Z1Z-FHGIR6J_@4%i3 zn`{er(n&f_$F^;&W81cECmlVpZQHhO+crDqmw9LQIp;gqo_F^74|U(GYSpT><|kwT zfx16oMsh=lB~Ut-ooL4hHz-S}ao@@u1;v;!cwa@(ugb#ocY`|iGhU^y+K$#6ABU!< zw!YP0FZXgoRR*>~Oa<8FD*5`JK^`KFQ#R-5&Hy4o)1>QIMw9JqF`JOeIhK1{{Q-Vf zpq#4q*WfPLOgr>c&G(aLt~f7tWI-QgALLQBT3{0uF6dlM-m@XGF}RVvE+Kb6-ZTZ* zQFl^k4F?s_2fZ^xmZORVU7{2xay^7-hvo5u+Jy8^d_o8e8uC;{V=n09*W&!WqoLWp zbLN_4^r6bYxkINZJTlC|$99d2cxB=Qy(b4`YSQz@KNoWsEaH?*bREK@ny4ie*$5H1 z#3Kk0{}ezsb`w8!j@~!x&w?^iI%PqwM_WJX!XXq|EW)NolS>yxSWPZXHV3Oe@xItn zS+|+xiZ_c7e>Y+V{WdU2WL*{RM}*pGIJ zm>8BW49R2|aO&;fEIo76s7x#OXOXA(97^fdP4%hjr$*mI$R6gYYK4i8 z?%iQ>q1Nsy=(t~kV~4JC4^tnRYQbAVF$)0w?vB1Z+a&yCNi+LpX@vw=)e%4X}w zGS%+jUqh~oeVF6z*O+7Tw-;jn(^C4^;i*&HUja#gE#d+QVx(fcU|@1taz5}~(q1|; ze@3=oRHx*gHmf!T|L5a?0J+bQ%`(y-uhY|y z@7)wEOqvU}QhoWMO2|!0>vDD~1Kh}ujG}r>{@US&(Srrhis55)F}@^47&K$H1-M>l zb2Z-t0PNV5WXH`RYS>NDOEmy;Y&g0K1ME3y_P&TnfxbTic~sCli+!xneMf?{_E>-r zmWgZsMJRDQy(GQ-=4zLIndSrAD&vIS?g#U;n_WIE9lCMUPX{ha+z|2R&5y`CT4c^+ ztJ%C^=Td@fs=SQ1A6j=|2}JHDz52W1{A)FhUPC##R$FJgo2TcSs+KmMbx;HydSBUn zBhjl5Wqx|LQxE;#`-wu==}Fk~FXGMqD;hGIp z+>U>YF(7}>^GRJg>n&qIqUf}+OX|AkE~Ah>53RP;ZnC6B@*T=BhTB)T*vmNNn|h;k zO;RtjJJOR4)7%NPT|QwiSt09FO{&YkDxL$AUx~an-;n^LzSD?CilDi5 zWSx!f(x!VpQD8D-5hIK%b%T6|;-1!%mmJC!Ml}Lu47rEEcQ+kp{6kncIql&Jc5#j5 z9|i+`;7-Ow=NbBug)oO`P%BIJpdg*WU|b`~2s$3zvV?f4-J8MOMewIGquh}cZth}I z&X+(sbl>k8h8+NFxtPH%Tx>V4mphQz7qKiK?tm0~x+EIo!8|$+hen4v z%tCjKV}6P#KYG+Peh4@;P{tU|t|fl-BG+#E_-knIO)&~{`Wo0b|2DOc@!wK5|1<^_ zcCs`!v$V7_vUdFPQ*g3${O7J9L1F95@#W`7B4;zMCV69nx@CoO>5m9*t6ju=asnbE z)Kpjg*(XQMs{PcCzHdYRJ_)n^7!ie2A98~$c_F5+z1p`E&&n!fH zzI4c+3=NmTuyiR@lN1N6Vx=aHdd<2$=Ls|a)i0_?y*b{9Gx*IJ>)~yOvuTK2?xL%* z49Xq{q3jUkeHeR4V0SKqkL&Ku7;m^P!?{AkVx?ip*?(tX7d7eO36CK62nv*8|HCS@ z&|>#?nU^PzH^Ve9FNrDB48!=uRA8L&S1p-gCmu9px&{gDo z8AEz5f^i}f3@_sKt_L4K#o9EjK+O-xJ;>XM-|?<+0R*~fI1z&KfudnjY-+q$Ho2WspCgk zTBU_r8+~3U0tZ+g8=4}vB-n1*-KH$#r4-Slv%#z4i}80M-ru<5TqR!kR(3G^`$W$N zT%66 zGFsNduHt&ch_#3g1kP|J7E#1%hD>oW*GN9)6u1TLYzA||7x|I$!#%ZqgoN9#x-OhT zU~A;*TQhi*SrWxbdsHT4)+Pf^{mM2 zHx!549Da)nLGS%uQE)**FlH$HOUCTkIr*W$i}nQqna>M>l^XnQr z5KiM9ax)2{Qz((*HEWu%XB7^KWai=&Cuf1aRXkguV`7zfG|n<^nIo}qxkBPefr{8I zYmnfbe;eqYI5*6;LK5elpvrHENu!%kd?upduf3LnmyP$-SCx$aZIzJzPpaf!-;VN$ z^4A{gV-Rg@7}$Rq0Rs;Ne9$^Cwq8s@L7k4sNk36P@mG@Ku!4wSVlma+LEuXfZ*1J) ze!RoWH?QJG7el@tl$-;s%klNA%SZD;pO5=j+;7%>5qoA>kpZL#&P3oDfD3Q$)!K51c=Tr1<3h))Qv{8h-0B@O|}oSEJE%#E2aD!6bR#_ZSTMkR%~* zJHw^(K}Uu~A@M>UK~m(tTu%KAK~q$!1N{7~JWIF9J7u%?GX1zci@Uo`AjO-@w%JjT zRQ*(XL?A6|>TYU)e^@}m(oIb#rcN^66%<0fzT{M@wQ4PfW?HNsQ1vAsItx8!nn4-6 z#b6o>HGyNW`I={%nYE;z*{?l?{N@A3YAU}_!Pf9l)YMYjSUUnF-D_FV-xb~Fbb`r) zZqT$us?DaCHWs#lHP!O#(LoPG5n%$5po?-(kl#)0b#{Xadzck?rzxaEG*(k3jzZA4 zE;r^ua|ZNQV@mVk}^VeTy-v&=Pa#|+t?+X&vVFj2(S{NJ;vD!{g8k-6Eut% z#G*JbQW^TLH6(iI{&W?^>fIt>^X?(^Ef+W$qkY2R{+x(}pO4CE>n{UXT-ssaM6KaS z3FeM>te5<{9WS9b0iJq|BjFl6L4wwF`D2=8z|0}(1${;fBVgfThBoPiUZgNSjd?pV zP3$B~NTI+ePP(a|Nk~K{J;lD$fXlKg^|qag!@J}BQUp^pqdY!MOzY1>Q*QtcS1tZJ zbz&4U%@I(+cm%F!7dEGj?7~=~GVm_#Iy02y#ksZnJr^#7QIHZ*bgfDdT{(ErbCNlC z%YY@iO{CST9@coaE2k(jhU2AQxxyg1kya+5&9jD{$oBhpG*V`FQ(!`NaXkJr(--io zd`73c&Yok7yZoq}^WZ)B$_~Iw6@3EIuN9FK7^z)Y;kV`!Ur1=!A2Us1+1gCRc^WmzCY zBz7L~&l=$3!4e=0A+Q?+w=%%IguYp-91a1Lv}ur~^7-uRaE3$gmcIS;9AsNLD#zB! z$wVE_Di3CIfxhr+5Xc0>X@YecG30kXMq2*Q~%32`+q~5 zmIWdo5@5?LcEeE1s(i6(xzsFF3@Q@dhN(f;FOa`JFqBMN$~lP5$VJ$g75QDsea&Q3+wQV<9B;eH>E@ zMQE!2f;M4^I+Z@zh1xvBNjMRs!?vu@kCTvoMn z`TX6CTq*Hg1duH`_qK`29j}xfT!nm&PttqA>YMKcYIkB@QPVF-TBOrCo63983u=uL zmWY%N=MU&_?1_@vMV*p12+e#}fyWH%Dh2QzWL1DQCXHU|+T)kOza%(K`_et=h` z=lh<_x3ud9Oa<$7mpwmB)Qx24z1Z+L2JOU~&~O!_c9K0Obemx|;yo$!t1&jxz3=F_ zqff=)#LHj((MT@_+gce`Dx`Y-=GWdSl{9@mGJyb8oUHa_g)XSS0Y-Z=<6YE(P za42$0tbbCB&d!csZ&A!$C|&yT6=C-B-Htf{l6XEjaeqBm%|VZ`1TzmwMIl`@@?}Wg zv5RzlX^oYkP-sG#at0qF%&RBeS*bJHy#MQn^iU@M?cJQAbU-7JvbJ9#C>!Wh5I z!nB5K>7ZIgWHLB#cBdSD{am*?aVrC&`E%GjktLgiys!+(M9kDj0g!NIXEEpSFdPFsUB>} zO+5)xY^pg)EJA2oYoD*Lt`E`j5@pYv7eT31GDib38DHOYtTHllEv0=@b&uVEqBE~% zo)vzBxm8i#3us31P>GkCC~N)Z-BWO4cyKzAt3yU$?}x8$cACT$?(rE0v><@+$1C-s zb0c5!HCNyxtT-9WqhkPnU(YX{tLyDw!Rk9bli}(pf%kF(%yVL3e*zHA5)GPY2$BM% znBB`oGHe;h!$An~g=3Ce^`${qSlP=c^i~AQsP+DQvI(_M;pio>+&A-*=p7I?1-~}` zhDl}BUmWO(Ywf;Bl*O5opULORt+|kzpj+t}XdFJZy4)x>Hr9k|%%)W6lk%+#l@A-Y zy~@S&q?)Ki=x@e|`jH1=K!@HmLC{LxSVVP3qOZOZFBKN}*mBlj9!E0g{# zfH2j{N)-z-o>oWtE+Vq(LcK{wPvMZ}kNkGSa$(-+1(1NLdm}P0x?-ML>xU4QnwvjR zFskzU8~O0HgqVwHpM^WYi%FYz=m|r#b~Pr#tvT#u>zN!6`CMqJ2t$IYQX=c zZurXvX47v?RU&sY%?6ROBUY;Xrqxovp((oGi3-_VCEHf@U?NX6e{Q=xdsED(Ac2QD zyfCD&c?N$;|jOTEuUc9RVTjpSmkGA(Y8mXFYfY<%CZBmy@t=AoH>$g zSM2B5W0LJ~E5mO{@_8nIVK*@9oGjUCfg^;XP`6ad44RT%Euc5?OV-gUheHaNcR<7I ze2au{$E&s|1Mz`w(Ur?CmCJ5(YN}0-3w4?D!MG=yo=DSXDUz(`kAiZgIT)%*7So*3 zVy7Pm^yN=2a{WHzN&)o?ONjsMPQ1{zRB^sEpB*rNM|KPR$ARiE(D^^Z5QyZB?3|1o z92Fe(9Gx8g2}cDATmK-zKQ6uNwa9tXgMX7r9xZ<>G4%w7$I_@Z)N|PQQlv{RX^DM# zi>%&m>l6^e=gnN7;~H@_{RG_!ESKtZnCjT#-eP~;{7RN;%KDbM)8ZHIm+t2mj*MQ$ z;D|1x5KV`VN4?$nTMQXuUCzu=BG3~|0pn6%sE_N?Vt6|SkpA26H)-->HNXSf-r1&Y zl>!|)?wS9-n~T9|$no)B{phzw4~GDk&a#1P25ozVy0#3f$Dzp7LfhMLW3zQyko(k` zy-3w#hqWmvr?DnC>qU^GTrK0~X9MJJ>By?ezBl98lNot4rs!u_l@*n%l?7;zt2 zQzRM=hT27>I1cQqtL8IUsr7Yfh$emtPNW5&VlSKv_TSFdg|V=BfqwX3@UEK zTLrT@eTq9{4KpDJ1YR0@6T2R%vCt9(nEZTCovte06~5X{$#*iH>9eQub6AE4u^)%V*BlsfR=%mCWX}X7JNVgC=06LlLFISRYaPG?m^N0YQg<{0@B2AETtDdZO`BEc^PQan5LvZ9AAfIKSm1jqQ6{*wy7Mc zVrA1ybHs!3@k6*W=G7RYYOq!Y{vrb8pJIYVzPQNizvUvC|KIEOpOxC6Vx@qn@@2!u z&k_LB0}ifC)rkj@U9yS_ie!L*hXHJiNTx24WEdD&9g+s4w5G11vGwv%(kl`m^ zl;Le|^3w3md=Y-PE(PwUMv1+K?{LXImFaQGKE>_wc)b>f>kVuRqO)wf3W$fnU9nvQ zw6U!3)$|ii5ccgcz(t584z+P@?%8Dwy>eM!Km8sW;<8@I1A3Q)X0Y>v(BTHbs_g?1 zN@5x98j)!0?dD_OU$5#7MQ5B#uk3X5k(e$9C!O0eG_ccxVN3_8q_A!NwqhIauK{bm zPW{a`2r|CY{tfvE8|B^=a0&)bxo@V<)rQjf5pg9U!>zP7vyDZLeFncLd!>D5=GkW=mPgX(=IA%0$tx zm_^`NQhLyl7=d#tgup1bt&3NFCoN<7X@p{^LTp?Mq|xSoKD(Ok zf-ljQ4nIUVz+|zJDJGD4^wXA?7)_{vONspc4v{TGL0(J^Q+E=!x$K##pn0von#23h zzT1jFeT3wY9fwuTYbVQqD$`|Ni|=I)(&7xut{Rr2G&gHs_a=`1M^IMgS!}6{cnQ|Y z!*)VI`V2BZJ2<{l0`r&agt3u)Q0A+>qs_B9TVE)Jh`mHzEsB9livL(Rp^4H?)Q5}F zRXN3rmj;EK9svhV@T&nMkk0r}&O|_pfF*tnHcV#NkAY=LvhEx^0eFK>Rn7c5^1P_> zf#xk-q=6mRWPy(2@sa?G^XYy^X^G)J$h{H1!w@0yj&2PF%XBVbftZ+-otxuiT47nP z-L$*F-p;w5?Qqw`i-wJS3U*thy=ey&><)^|ot|t)v2*JkT{hQGBY1nZ8%-k#mqU4V zjabvORrVA^!jV`d$4w9LVPR6_ zIm{W^druprgt62?vDFYc%H30+iCFILgfk~YtJoi83HP!eig7sU!@{=dgw$cE^2dl< z6YdN{v_;%Y1*`DAY6OdtRonO+P|s*Yvsi6I98Z5ir(?ts@O;lavMhv~j)`Pnm01)OOwdYT#xop`J}3uN|eeZ{O};jdY> z+2SR}o6bD@5iT+FpHl5jimBQ(u5dBJ8@fHOI*$G4CvK=Fukev4>KwsdyY+zu1k#uu zAHY8_m1~Z8PBj~L>k~k;MAnpRs(#;E&T*~E3*zd1`hQXNY>6-0sLih#p}CQRm{Bv54bFJ$XAF}L$QueBm8qVUmgW<2 zGu%paSYt;Q27MUr?BzL80{hz->sXzDAd-24&+tKS2_4S8hJuY+KqiLYdD*dH=4I9A z%vtRn`UmUDX^s!YZw*D8Ch{WAWhvGPfu>WshWjD1TPVlyu_uJ3n*_cSmU*ro@TjkI z+qt7U{q~LRZ(EmtuOR;a5i5#r zw*NX>Ns#|5j?9~;zJA_Xp#Q`+bQ~hEgDLe1LUZsZVk|5QltgfrcuBqlIh0Mv2Zd1x zBgO>JYhL(KqR`C6_cm6yN$#R4_Nfl;qob-S-fvt%G=#*2)P#iiqW~mx<~L2&D=D1aKY|&+$g^tZB~M8Qb+y_+xr;4!rId?Kc>f3bL(F|H5BdNQzompj*ofsPtk!y8Lt2)sjYqQUbd zirRtDBYGl%_xS<$aV@~77!vkOtJ?Y7H34^bPiFX;JittWsjI;|vOq>SAJ#j7b5QWnz^!zUSo9umr z3y|2mR7=~blqZtK>XjBlQqeV5-pHdOhf;{&l+{jfsKF190yVJkPYyCiNvHCl0)&C)}7R zB|4)mcSb|M4RARG`5iTjDE!bC$=k#9u%4d7)2xnTGQ-C$AK z!tKzGY-9R}(FsTlMVqPO*hcllh2f6eZIu9iVz4Smf-#C@N7SHo%~iUQeOy6oK}N9T z#gMbX6wN*2;%)#mvR z!Uh(=iFq}sE9QBXr%vx$ee$d;iE>7zIjjrr25~9oIUA*&vsTK6&UJb#mbJs1jVTwV zk(c#Wp$@F>WbU)`RvtU9t$oP(70wxNkTY@P(n_P#V)$85!Rgsk*OvvZ3rtjfY6w4r zT~a;-8oZXQ2vp zI@lKl98@P3q?xbLOFAiZ-}{V^ZTEblirNGVZqzd}m(A#lWelCYzxECv9>?Xg8lKUv zq*^zseGrRD=dD}LQrf={RBXNc9$TSg+#Uk~FNgla4fHePZ1$NZ<6@Sr=$+7WEjkF7 zZ{H64?uht=LGEJ&JK(|g5VIdU))bhl(St?!q22=aoV!vPc5DvU?hMTF1|qL8|Z^4#(^{JTb~gB@j`d%O}{gJk*7I-d&9`^|M)`x zHM%Nms9=6EsbKsh0XaQ8i}D(Tfe19jjoMJEQ22_`8jKLhxdZqqv%Y!=q;aRW3qN!` zZ%AFsc$AZ-X|!IB{ds$E3B65Xf70l|U#ORRO>Q1>zIvKuaX&wuW$Ao=v;F@3O&e}I z;u{V~HFCG}H|1~aUjywd!{vx-+>LKIoY8txOoHCR>zqKK|zH?FBq_JHlXx-T+%Aw82$6v;YcCqbA2OEOp7D>Bug ze7~scL||-IWXo+SQ1ozeIy;RXluf1T2sO~e1;U>BAgtI)nnyiG>aQoLW^XG->2hH1xjlHiMmy-s}L4n4ae^ZE@+< zan`}P>7uKlad>gbQ66VYxt>rG90KYsdzP+9 znYxP;KPULcEbfV%L=$UX>RIvwN#6$aQToKOvhV9reZfFM#222rG6{pJ5J?J4vv=2P z_0>3?#RE$sOHo>|?GX(9+Mgadcq45A-*??NeB0lgL)$8^@u*mQz;L>jNp3vZjNK0V z3dvA6Xb&t~y9~8PKKZeiWgOma!9c;6(qPpWqUN5XJDuU$xp-VvM-O&t9aeVFB4oN* zA2F~y3D_aJhY5RBv_LNU^Mox7D(aUWJhWRkCYDGF-<1|>F>=3R2603YnV2qF$b|A| zI3Z+FzD8AZ&RG6W?+ASxy+P=n2E>RZ?eQ~1M&mw09g#8WRrwSZF$#&BqJwxC>0%LS zTpYrx1y>bF$UI4Ak;OBKi6w1oA@YPc+A}RJt={=3YiIzaQ+-H4r+a4R`v>09`Emab zR+vZKx?T1IVg36u>p(#$fdwk7p)wyL#Zz2wdFPp0B1i0JbGS~@PW_0flrG+9f{Js+ zW@b(${fbFBWp7r}Mse`wmeCSl{S=Z)g%EzEF5^30&_D4(X6`W2C$u=XUUx}w&)zQ* zL2o24B?8{x6T%4kTPPfdG0A^ziU#hIA#w(4)YH53x_f-HIwse~+3W*tI6>Fm&lkB* zBIV6{rRQMhta=g82`e2RiT-_+D-e$AGDAH0r_B`AV2Xyr=N7y}gmZKZSWMeEu(mCf zCLex~8(Kf&)CbofevzEw3@ebCPfH2`+pm6V01Ta)p^OM5zEs25D}<04SxTTQdoQGk zZ1To~FD$WE^ZTk3)(ZqRXa`vRJKLRuI)yik>5<)WQoZ5_!yQy-Ot5q@jPDk_IBV@} zhx#gMv4t6JPnIci9}dmuZ;}Q%iC0iHYHBEgRYpxWd)d)h2pu=#Rsg^Y`j>vlcH9gJ zVK;B06vaSlDg7kc!6%U4H7nds$y;SHG|%XkaBmS0#4< zC~ryqF6L?JtW#r@ymj*w|C#z(=Od+j84C&fJ&5QZyw52U zA_c>R`|7?TZ`S!8!8?zw5VJv^kp;Zt zV2qRgN#p^}X~H7B&1)L;j^*X=ob-tfrqzy0Yo`hqZk4NR#I^~iHIBu$4p^7F)9fv8 z1Gjd&^%~F1g=Uv^2ky+uo2KuV&xJP6-=liDry_~SITDYoBB_$Sx18ZvMFQj3`I0t0 zVmd~wutJS(g4;Yb~CUbh0dv8;KpAx?<#Vj;jDWjEC!{+&HA26}F!cb? z^ONyHAZhVc)AEk&C+!;l+odZ7>8i6kP2k#7kS=uKXJR*<+dZM|pLEs9-zwS)d?1WA zq%zcG3Rl9;hk`{wh6+h6gy)X(js@4h!a2Ag07Ci_h6A!evON5pBKzF*0Hz$IbLwax zoIhP}0zIXv(aJn}r&zO2pV6jXy#v1kPk{Cl7M>X;y_*l)`O$i@khqB2MSI2a(E@3s zQ$uW@pl=i;K>~z;5uilLFn%h%h*NR(2dsocm*Aw%;4Af#$8Vh=@C`HVI&zA=z)=4f zP3#vN7T`xxYb}AIkE+2~KFIn9YX8}C0G0UatABota{s=5|9zU=zs9(K_0|7Z{r(fY zD^$)LzSf|hR%y;nC&J>{y5xcKFQT|iz!cL2v4!L?4?rvV%tF*_j^rA%Qp=X+J*dUO zp>UoAJZ@x0!s&cNi3E9jr0F*{*Ks}oM)j!;BB`|eM*nfcN$#WdE$+v3hpQHk+gD!R zcVJ!2N~minaPVmmNpLp&?fl|o z4wYBuErlwDmjeui$kox=bq6rcYxJoRar^lwN6}Rxr70=URDfs+U&G|U;0RwsYhba% z?}E_=vV)J{k#8sfLg{_M!A;lk!A&!?yV!RjuBbAhY*BsT?NqzmgB?)<1Uy2r{VL$} zxpek|{lxu3nuB)mD#W=e1FCV<=7XoXOLOg6_uuX_KL3{g$C1alRX)7 z*@F`7nDg0`*iW&-pegl^e4BA{nZ@xqs4&IHi~4insS6cf4{bI^bG#uqJL9hcoTz+Z zhzRkH&HW-r_i7obEC?qzbL5B(4NHgxR2tyrtHet702t2hFhWTY!P1Bipom)L;pi&~ z1?wGx;Kl?RkJGCS^(?tjY$65$>$LX|<1L9-#aE0c--6^t-g7iBT{uuyL>Golq+mZ? zZr66rT~%i^R)k?i9>CBmTO74`_}Uf^ejA&_pj$Z^o+Ygt)3gGx%6_1HzZwOajHwff z^w90>q(*?CtTh&H)kF+zQ<7qSu&r(;-@~%B7O%#)5YEDijI+F#42UnziB0sMK8Xcu z^YFrf%~hF0+Q3dUpbs=&e=Go;CTj6Dj8$YIYn2w>^$!7%HfZ4j$q76vnc@vqh9W&G zBksfc*T$jcQM_=PhCI4y<($1T{ehzPaqk)(T=9rSy_0x<)ir`X#$89(h8QS{uCAsWib#@TQLC8!0;urNm1`BK=eoixv(u6bgt zTu1}WsoxG|zEkH|v{q9cA52RwQK@rb6*8~L&~ht6#XHDAw#4qqQmoJ#_so50v=erO}&ga^X^EA&) z7r6~_vj-X>JsJrl%E~1DLnJdEM?D`V7bVc1(ZGHvsujU5|6Q;dRdSteWS$Y8Hrg6V zKrkxi?uT2vh(YrXN+XkW-k@~`A2)r&gTB6ZxACBk06C14Gd;bE6)i!bZ%>iz$lxlu zrM|%iQt7hlJEo;eHH#}0oRptQD7mE}VkEic)8-wS@k0}V9IMcdFAVlPk8>`9_IzH@ zA#5%vPd#6+_Y*w-Z?4NeiTYfW=Q@z#J-J>54akIKH54)V7_=q1@Oe%W!iwye7cL~G zx8Tt({|&LQVJ^~WTp zUJ638L1oARrLcMYKz3xsxiqg&8={X8dBMCGZ;^sXWLI@UWnq!bi+LWbN#P_ba$0Le z{9q-0+ZxC*8M8XywuL|FKi|KmJf}`9WEf+@D6Hx5v45BnZYv=`H-3UOf6gfC4H5Dep#TuD)RXgNwX;TB~knj*yJ`RBOIr$m`*Pwi?VDx6%4tO zH~&E1olW_X++^N4ZR?gn{W5>`eZ5_oiSq0pE2d9s2+QCx%IN+dp#v(S(Zx@~ir_VV?L^rNrOg4zC;|v^ z5lUxu4+2GVv>6X^dv^*6iww#YzZX&(vrZ*H3#Cu4E@@;8vh>)#U}%av>I;r8-!Gz) zc!8FEqqLfjdyP=7n{DR;*J##b?Dz(Q!{yLh!{H1yW9*a$nZNWn&D!Dm0bx7B{&;Jk z?0BMectIfS=Ya4GzjGABC_E3)l_LPu+PdTPexX(vCZ#OxFbyjb6J&1)!ny62ixQ|A zl3^EKIvMKV0UMcUnI9Sl$cely!WE_LO{SmT9mRA28-N8A;*Jb$NDfpCOD>vUkxxB1 z>AKP{98XQp1TS|RU~8*T-VwM5X}wqP$LZ%8;0)?AL*4^lY{6<46d;>j$(V@&SGITB zpvb^guC=(QuBtM(b2DF^2>xg+-`CPSd39W@0KChACytLn`U6?#DWvxm3V7jUWC1 zwi#QO$y6^XFSR`KXl5rRgIU4OO7S77~D)8^)Ue*5LDQm_QJ)pC0}s% ziz|oJX)ik<_dCm12E{CPh%;Aaxyu)gzjZFr;D6YHAt zVhL%(<@!@};)zNS!rkFWko9xgTEX4JeLale%a2!?dxoaiP2bMpnMBq3$%PHn2*EXv z*B@&sG5UQQcX1Lm@LEVeuzjP~p*0m^J)MjccETW|onPLH2Y&0OCHBtmyL_ zK}+y$ijY0fL0y<3>A{B(16*)3`-n&Xfmvj`>cmYU`V`@%NhG9|+f^*=x7a8P?DC(E?q_zd1;0*njI+c0P0$XPnhqPjtF%FYTZa$d73=*% zoH2ZnsKs*$u&3mxjI6RgHPG6Cep@vFRI#qOA$V?=YQ3)5PXM5?%NK79kwT3mDu4AE z9|GH@$0AIRnK#rkEr7&9Do`bNoD40!m+|ux7aac%YF!J(UYt8fXQu!(#T~JC;&B`~ zAi{p(yT-Np;3m6)O-IO9d(BkpI&isr zsGHI{&{j{ta_fEY>qBx}{G23qXk|s)sC%X4iXnFQNbUNX z?17_2fAE-+hpwRECe=`UBC(xu&YoRM_STFzIW2H$s^j4cxkLA+C;C$ks9+$Inmyf}GXEcIpzJO@e2p z|E{CF3y{`2k5%S3v+6VE;rFd7$~bDc0KXb#BIZMl{QO#!DVOcYe|B#6Te^^^-RZF zTMC^Use~5=^*fxY^a7Kyz0_1uT~9<^emd|PmL7DW!s3S7$(o>R*D=Qq0>$&-kH{Yyd&7tI<@`h`9`(0_+M|7p|pFF}r&;r|v9 zTl^m);x*NZrsUN^2oe~!)1?4$2o&HOKV-glrWNen#0~U}4aGZ^lOz2yC&CaY*CQJY~pp5ZFFdY&H zig`z7h?HS?c*Z6w05T$yzf$2tGWpkFhxBE$i?WRl{+%syicFDPmx;{z9U-#O59k4) zXY_U77=k%~-d+r(eul95t*Wt&)9^N2Lag!6Fs1}@pghqL(m6w_2`KtVvp;7CSV@2C zOdTp`r`*02D2w-9b8X6jIrdmm@xz6H_znZ^f^Cr)DFcfF5`p)w7P@+SVu4T6BTz=g z@0c1m=oxSD`2_=%`ec;PJJV=4@Q=mf61u8+<}`A7;sq-x5vTKvZLL0$qo^2k>v(75 zC>3$yZjmfx!7$ri0y|1c8GX;~-B-~CrJtmClGP8`fkO34+(h*pB25tW#dSKI0&X!C zh-=-42y5K_ZT<4GOU1OaP@t6xYecQ!uL)*?RbwUFC=-jeai(ia-hWwNF1&c?&3);J zkN;Lj{GSBp|EpU5*{wt<|5Fgd;%iw;h2I_+il?3f5Zu_6m;qLYQ5E=321I3EmSzYc z5vR3oSv>bScLRm%XxT%~_?H$Y&pfM5Ejz#zA}-D&%Olg_Yj8eS`}jC&+5*z#;|UL9 zFgEfeB&0Nu9&8T>MWya=_>q%g~DBxt9Js?&R%bh?ztt zIZcCUA%>aSpi_ASb27HNXVQGpdglA0ksSqoETcr?=?3^@qBfzk;Ia!z%<3D@2#XL4B61J~kphF;6SXX)}H@!TDs zZV&<;4CYUjL^xaIrh@)4eA|%IH1WWqE??O7eTbnh8@*{6@v`Q#GeGvhfpWiInwYOat`!8uS@lx$3Nj|aO3SJXs2W? zO>2$5Zn&#y&fh`VpCH9M^s~6KFxZ$hWcXr;{IAxSwOjU6a0Iux-G9cz#~$;$Pgy*r z{r+~%9j5S@^0*oa)AYm13ALzh+-YN>J5R+lMWUndD@!ssZ{iW7h;|!vhT?|$QtNJ~ z;HX8zHInd`Nv)}TN;#V9Qdwsg;|I3D$UIH!CeuEz zO=wlSB*+sj;3*&_4$nG};AwSiZoKOsLzPdNeX{Hqb3O65k&0CRfw}&#aQ6Qe)izd_ z!_N8QXb$)ya3?H>D+f196?d+BYxZyxGZ)5{(CQ5@4xNy^&%_jg+B$dymdJ#F( zm+5qlYa$sHJJq z>B_kk^vV@V2B(A`q(z`VV&r?F^)m|zh;_*lTs&_=~Q+2{a1{e%=#2x!X1;S%#Df@Y0E+8M&eDs4%{p7=pKC~79H%XkvZTmorG z{b1LfIkv4sG&`P|dufIx>z@$Z$mL!ldN>;uI6u`ha@Ii9=T8=CWHU`L`&nu_yDzo6 zwRFixFqu$JEPz={st~mTtc-4tb=Z;TO&k5zni;Xr{welR$lbx8{Q|X}zXi2_pSAl> zmiWJF<)4_Ap=6SJ{teoZdU`FxmOI*7^F#>+=S>&B6m`KpCzD>IRAl z2?=>g>6*Q*07?vM1iA+4Odv}~xkjj&f5a7NMVsvsoj=L{T% zfPkSK6Kku`hh1*AEGXY60HHB#{setxUOYTRk;pV3y;x0&?xCs&PLdP~tuRtBi|;e) z6sIB8amoQ$7^fTmx#FDqqPNy~H*xx~_|TJ6+=eb=6{W|ahE7-|z-#d;cEN8#?!%p0(t>6#JXUIBhzd|03qd)EmbsN5-0i0k>Oy_E@ba#5;}VciKJyVyncERL_TR!Ika zK#l{LX}4<7vTpHMKL;n@2ESZ|^;fQ^+S!-dv<_qG>wN~k`L00Z=?1yqb4Fa7L}6l_ zK^2hzEn(@T{-d`<3=z79&kKkAhyo6(Aw!-kk8qHtg%QefA7;WGO36)=xo(c1oz zKOW_&F>2i)``p^hm#$DXHp|i1-vVMf3`R0Q_qb|^e)*PVrz@&iC<@I=!YK{PU+6M(U!>v#(b}i_Vz9sXxouCP2W{LYFNcKd#pEKYJ-Y8L;guO=x z{6vWu+ASWvSETu1tRnU(JHIgKGoTe$@*Plf`tcKc#{4Vc$zzm)9jKBZlD&0i@eZck ztp|9n#9%ysRxA87lKwNiQ!z>|^4I^@*_p>f)x80HsL7t}X&PG+l}3^uDnzzI%1(%} zjD+l=cteOOTCHWtlJt^SMApbsiSSBU5@ijE$`bh}Li>zu-|XoS8&t!LQt6=rSskf1TQ;@ z{O>OwmE(?F`>0@C6pL^AtM~^o$#sTzFlc9l3QNh_r4{R^4k;YzifEh2Wt>aq)128C$}+G&P)Dbr>nLmNdso^UXLn8b6(s4+tk-S$ zz7>-y*P;3>s$^7lwW_&*=Jkv7VU2$0(d^UZZWsamX(rt($_1FPcX(&Qr@5ae_BteB zM%zKSn4(+htw|AK_X2WNoN{(@v!V1wlyqMM_#&#jpEKRfT_U4-N{B-cFD(2#{~qRP zPvBtn^=t0ftWG|V+(+0-YQwELuxOAwdDiE0-czs9!pV=*^|B@XzxHF+FrmfJyy!>7 zc%nXUFwt(OdNgKg*#4!3RVAEtqQ|su)+}>3V1l~)%qnMU&G@qs%m@W4eJs(qTC>8heF{>Oh;rGqTsR{86!Q^`o z+ZWW`Jb|Tk078;7ZhW1C?0j?3!Zk#!Jm^Vt%9k$r8nn3PmeFRIP1Z$1HAF zp809;KXluiHyyq$+{~;h-S^}P%39YBd#Pk+u>6hEV9OVEQ^F5NAWS>wu9K@4brHcKBGZ*QCK^;Z+JBXffU>-Ilh zRiVqKtr2E7u_whqXrsL9EIr4$N}+QcOz}EWr}PHtyt~;%>I|$87$$RJ8*O9)#t-jc z3yN2cx9-c}KO~GzJ8_5INiHz0 zETJST6%~UjU9JYsds~eRBae<<44`9dV&vO;I^|tXz!vPl0lhE{F?p6v#GPW*snPWB z+2t9OE3a`jTe@pBUStz1`1qKx{*j)?0jav~;NaaIO~IeO~7#L^&#`J2kLG5T`eJlr!H;9BwWD$Jy6q z+x@#fdIB3RWc7vn2Itw$>A7EY{C@uGLzbGYyuMYAMaNSdochs1&N-bjuG-C2OtEQ> zWqR&seWlLEi(|huKF<1DA<1~72p@f}zhF-g9Rp7Z7T=oQ%C#%+R$E)%1Wnlk)#NzZ zoLX5+?0@d>Yuy^pF>n4x>}31f;x9YdWVxqn=Z$wXxyrk8EBJofTb-+=tzhq2tbuLg z+1Sd!jaE8TrZJ{jZE%+>(R~n@yjLJ`O_j;^JZV0c zi@GIotU&>S!Z~0huK4$Pf8|jAX48xFEMBVuF#9~wK$6LRJDleP`(y+Q^`?uZ>JEhmajOvTJa znZyX2mJB#1&fB36UB+52Y*dxE+1HRgm;5E==)B9n-kp7OG`y>zqp3BDo$&^wuY*T zuHFtURh>gnLdy|-hHmh0@4>Kq2jt%q!7seCBhJ#%(#P2eXKmwYkGH|uTY^2Yzm{p( zJK1U4ySdvqEgmO8&X)75srCb!`+>fDVP13T>IBlLv2|@+7bO0Qd*o=PA!9Q|5My~k zj9qxQT)OhX3KYud#Yp<|1D+?G(wc_B?b(3(5oCH0S;B%R9Z&(3$@Wg}Hm**VCvh;^ zetm|iE(ZiIP;4ku7i@c=!nC4nfsQk`%2EKc8Qf5&9r(OSh50+zHn~-=YyY!H5>yt3 zjht!0N|pILu^l<{jeBbST2SsQpthiT_Ht8Y{?5Bd&fFif)y)djQ6#7lQBeM+OKm+> zrq$v>K;+cgPMfkKKr93;2}+&ch=6KOxt_-VY8CGR9| zsJ5XvBwWhc);r=Xynqdgmf1i+3IUY>Lw(JlssV_rm+3kpjerW{u^yw=W|F`Qy1)&! z0C5ijDolG7QlaJC2Z%$8mx;z3Z9tUHC+Z^Vr*+Epb z2kjopHC9B#g^7fG4Wdzv^e7bLGH#^?A})+|GkQJ;KL9q+0}&Cb@93fNFC-Vp)U2F|{Z=W=Q{fp+m7+)%FcDMVZt z`@SzP)?FBJuz?T5P;QP3A})-5Rn7U^s{nWRGH!to2^kjlOt^pe%fxP5+zxG?P^{`yS5Kj2D%b_^XWo(Mp|wZi@HTpIFzjn4n@+atij zc_6AmYfd>B8JTh)biHcL;UM5;{lLml?r<0)E{vyrD;iB00G)c|G7qjLBI3e$ga@m< zPcYE6d|A!WUqZx%@nHA4^zeIt`(s%I?u$ggwS?=5)V4Fc`~h@zWhb}q&{afS82j$7 zlkU3(-ao#85jeEo6yp$aVLUjfB>P)3@Zj`iekPrShzn!i>6<%c^?@&FF7q>qR0LdG zxJc8YK+}q2zooR@;F!q(?iyJr(n8TpWLG4?A2N4&>f8_(NeLw@Q6RtR!BDD4HUcV)4U*K^UuXln z-lZqE0Xi1}6*fj5y%&)_$%sN-25)9iZ8IMt;Zlx}^@Mm{CQxH}zz$IAR0RSmjDO*5 zr?iQnuEGHs+EW-;QKkO+I0@5&(W~Xea{##+=(})Wp zF)SXa&I`ij!d9E5OaCciAwP3Kttdudb~f%I5c2{=Bb6l?;EBKP&%}_Xnp#n0-iE-t z=0M~GD47yOq(%c$kz{-JSCp60weE8}kVgj`R|6uCr|A#nk?mz#QS6Mv)S&}FtQlz8 z&hu-GRA2_CBY-p7180r^peoN2ZdjTcz#6tNf%9!ysKLHPK!pk7cAFYDRs-t7 zz!a*h{yPLzm@uatwIKdnaCwQ?-ClunbF9=6i(qK*PirPMd-A776URNu$?jK zsW?!>++#wBrd*YzXQV*E>fFe{FvAkk<0_!wtLu@0VHzOmQ3VU9T`XOe7&!2+E*|FW z3-W3t&2lfcLl4hdb0>OUZM}VbVeWY27p;nF(rUv~vV1Nr8q?sw9 zMkp6S1g5MnY2rX-h!J*+Bf^r7JSZ&&A-6hdnyQ6-giF^5?vDjhE)!{LB51Q8+l@%X zQuZry?MZX0C@!fEo-Aajpvgu}NNh!#%Rmw$jb`tkNuz8i(!J(t5dNehGCtXu3`wC# z_YOjbW)GGBFQJsnM!HD^+Fp(wTv-fcTOzpwknSdc`c|6`HSEs>uuJ<*$kC+hf1#m+ zUKb&nvMERxi$Y( moreItemSkus, List moreSubsSkus) throws IabException; + /** + * Blocking request, must not be called from UI thread. + * + * @param querySkuDetails + * @param moreItemSkus + * @param moreSubsSkus + * @return relevant for current user inventory object, or null if request was interrupted. + * @throws IabException + */ + @Nullable Inventory queryInventory(boolean querySkuDetails, List moreItemSkus, List moreSubsSkus) throws IabException; void consume(Purchase itemInfo) throws IabException; diff --git a/library/src/org/onepf/oms/appstore/AmazonAppstore.java b/library/src/org/onepf/oms/appstore/AmazonAppstore.java index 3987c665..527d665b 100644 --- a/library/src/org/onepf/oms/appstore/AmazonAppstore.java +++ b/library/src/org/onepf/oms/appstore/AmazonAppstore.java @@ -24,6 +24,8 @@ import android.content.Context; +import com.amazon.device.iap.PurchasingService; + /** * Analize whether app is installed from Amazon Appstore. *

@@ -35,8 +37,6 @@ public class AmazonAppstore extends DefaultAppstore { private static final String AMAZON_INSTALLER = "com.amazon.venezia"; - private volatile Boolean sandboxMode;// = false; - private final Context context; private AmazonAppstoreBillingService mBillingService; @@ -47,13 +47,10 @@ public AmazonAppstore(Context context) { @Override public boolean isPackageInstaller(String packageName) { - if (sandboxMode != null) { - return !sandboxMode; - } - boolean amazonIsInstaller = OpenIabHelper.isPackageInstaller(context, AMAZON_INSTALLER); - sandboxMode = !amazonIsInstaller && !hasAmazonClasses(); - Logger.d("isPackageInstaller() sandBox: " ,sandboxMode); - return !sandboxMode; + final boolean amazonIsInstaller = OpenIabHelper.isPackageInstaller(context, AMAZON_INSTALLER); + final boolean result = amazonIsInstaller || hasAmazonClasses(); + Logger.d("isPackageInstaller() sandBox: ", PurchasingService.IS_SANDBOX_MODE); + return PurchasingService.IS_SANDBOX_MODE || result; } /** diff --git a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java b/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java index b97ed2d1..e02070d0 100644 --- a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java +++ b/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java @@ -16,12 +16,22 @@ package org.onepf.oms.appstore; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CountDownLatch; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; + +import com.amazon.device.iap.PurchasingListener; +import com.amazon.device.iap.PurchasingService; +import com.amazon.device.iap.model.FulfillmentResult; +import com.amazon.device.iap.model.Product; +import com.amazon.device.iap.model.ProductDataResponse; +import com.amazon.device.iap.model.ProductType; +import com.amazon.device.iap.model.PurchaseResponse; +import com.amazon.device.iap.model.PurchaseUpdatesResponse; +import com.amazon.device.iap.model.Receipt; +import com.amazon.device.iap.model.RequestId; +import com.amazon.device.iap.model.UserData; +import com.amazon.device.iap.model.UserDataResponse; import org.json.JSONException; import org.json.JSONObject; @@ -35,28 +45,20 @@ import org.onepf.oms.appstore.googleUtils.SkuDetails; import org.onepf.oms.util.Logger; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; - -import com.amazon.inapp.purchasing.BasePurchasingObserver; -import com.amazon.inapp.purchasing.GetUserIdResponse; -import com.amazon.inapp.purchasing.Item; -import com.amazon.inapp.purchasing.ItemDataResponse; -import com.amazon.inapp.purchasing.Offset; -import com.amazon.inapp.purchasing.PurchaseResponse; -import com.amazon.inapp.purchasing.PurchaseUpdatesResponse; -import com.amazon.inapp.purchasing.PurchasingManager; -import com.amazon.inapp.purchasing.Receipt; -import com.amazon.inapp.purchasing.SubscriptionPeriod; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; /** * Amazon billing service impl * - * @author Ruslan Sayfutdinov, Oleg Orlov + * @author Ruslan Sayfutdinov, Oleg Orlov, Roman Zhilich * @since 16.04.13 */ -public class AmazonAppstoreBillingService extends BasePurchasingObserver implements AppstoreInAppBillingService { +public class AmazonAppstoreBillingService implements AppstoreInAppBillingService, PurchasingListener { // ======================================================================== // PURCHASE RESPONSE JSON KEYS @@ -68,13 +70,16 @@ public class AmazonAppstoreBillingService extends BasePurchasingObserver impleme public static final String JSON_KEY_USER_ID = "userId"; public static final String JSON_KEY_RECEIPT_PURCHASE_TOKEN = "purchaseToken"; - private Map mRequestListeners = new HashMap(); + private final Map requestListeners = + new HashMap(); + + private final Context context; /** * Only for verification all requests are for the same user *

Not expected to be undefined after setup is completed - *

Initialized at {@link #onGetUserIdResponse(GetUserIdResponse)} if GetUserIdRequestStatus.SUCCESSFUL - * durint startSetup(). + *

Initialized at {@link #onUserDataResponse(UserDataResponse)} if GetUserIdRequestStatus.SUCCESSFUL + * during startSetup(). */ private String currentUserId; @@ -82,22 +87,22 @@ public class AmazonAppstoreBillingService extends BasePurchasingObserver impleme * To process {@link #queryInventory(boolean, List, List)} request following steps are done: *

* {@link #queryInventory(boolean, List, List)} - initialize inventory object, request purchase data by - * initiatePurchaseUpdatesRequest() and locks thread on inventoryLatch. - * After whole purchase data is recieved request SKU details by initiateItemDataRequest() + * getPurchaseUpdates() and locks thread on inventoryLatch. + * After whole purchase data is received request SKU details by getProductData() *
* {@link #onPurchaseUpdatesResponse(PurchaseUpdatesResponse)} - triggered by Amazon SDK. * Handles purchases data chunk by chunk. Releases inventoryLatch lock after last chunk is handled *

- * {@link #onItemDataResponse(ItemDataResponse)} - triggered by Amazon SDK. + * {@link #onProductDataResponse(ProductDataResponse)} - triggered by Amazon SDK. * Handles items data chunk by chunk. Releases inventoryLatch lock after last chunk is handled *

*

NOTES:

* Amazon SDK may trigger on*Response() before queryInventory() is called. It happens * when confirmation of processed purchase was not delivered to application (when applications - * crashes or relaunched). So inventory object must be not null as well as inventoryLatch + * crashes or relaunched). So inventory object must not be null. */ - private Inventory inventory = new Inventory(); - private CountDownLatch inventoryLatch = new CountDownLatch(0); + private final Inventory inventory = new Inventory(); + private volatile CountDownLatch inventoryLatch; /** * If not null will be notified from @@ -106,43 +111,46 @@ public class AmazonAppstoreBillingService extends BasePurchasingObserver impleme public AmazonAppstoreBillingService(Context context) { - super(context); + this.context = context.getApplicationContext(); } /** - * @param listener - is triggered when {@link #onGetUserIdResponse(GetUserIdResponse)} happens + * @param listener - is triggered when {@link #onUserDataResponse(UserDataResponse)} happens */ @Override public void startSetup(IabHelper.OnIabSetupFinishedListener listener) { - PurchasingManager.registerObserver(this); this.setupListener = listener; + PurchasingService.registerListener(context, this); + PurchasingService.getUserData(); } @Override - public void onSdkAvailable(final boolean isSandboxMode) { - Logger.v("onSdkAvailable() isSandBox: ", isSandboxMode); - PurchasingManager.initiateGetUserIdRequest(); - } + public void onUserDataResponse(final UserDataResponse userDataResponse) { + Logger.d("onUserDataResponse() reqId: ", userDataResponse.getRequestId(), + ", status: ", userDataResponse.getRequestStatus()); - @Override - public void onGetUserIdResponse(final GetUserIdResponse userIdResponse) { - Logger.d("onGetUserIdResponse() reqId: ", userIdResponse.getRequestId(), - ", status: ", userIdResponse.getUserIdRequestStatus()); - - if (userIdResponse.getUserIdRequestStatus() == GetUserIdResponse.GetUserIdRequestStatus.SUCCESSFUL) { - final String userId = userIdResponse.getUserId(); - Logger.d("Set current userId: ", userId); - this.currentUserId = userId; - if (setupListener != null) { - setupListener.onIabSetupFinished(new IabResult(IabHelper.BILLING_RESPONSE_RESULT_OK, "Setup successful.")); - setupListener = null; - } - } else { - Logger.d("onGetUserIdResponse() Unable to get user ID"); - if (setupListener != null) { - setupListener.onIabSetupFinished(new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ERROR, "Unable to get userId")); - setupListener = null; - } + final IabResult iabResult; + switch (userDataResponse.getRequestStatus()) { + case SUCCESSFUL: + final UserData userData = userDataResponse.getUserData(); + final String userId = userData.getUserId(); + this.currentUserId = userId; + iabResult = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_OK, "Setup successful."); + Logger.d("Set current userId: ", userId); + break; + case FAILED: + // Fall through + case NOT_SUPPORTED: + iabResult = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ERROR, "Unable to get userId"); + Logger.d("onUserDataResponse() Unable to get user ID"); + break; + default: + iabResult = null; + } + //noinspection ConstantConditions + if (setupListener != null && iabResult != null) { + setupListener.onIabSetupFinished(iabResult); + setupListener = null; } } @@ -150,16 +158,20 @@ public void onGetUserIdResponse(final GetUserIdResponse userIdResponse) { public Inventory queryInventory(boolean querySkuDetails, List moreItemSkus, List moreSubsSkus) { Logger.d("queryInventory() querySkuDetails: ", querySkuDetails, " moreItemSkus: ", moreItemSkus, " moreSubsSkus: ", moreSubsSkus); - inventory = new Inventory(); + inventoryLatch = new CountDownLatch(1); - PurchasingManager.initiatePurchaseUpdatesRequest(Offset.BEGINNING); + PurchasingService.getPurchaseUpdates(true); try { inventoryLatch.await(); } catch (InterruptedException e) { + Logger.e("queryInventory() await interrupted"); return null; + } finally { + inventoryLatch = null; } + if (querySkuDetails) { - Set querySkus = new HashSet(inventory.getAllOwnedSkus()); + final Set querySkus = new HashSet(inventory.getAllOwnedSkus()); if (moreItemSkus != null) { querySkus.addAll(moreItemSkus); } @@ -167,16 +179,19 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk querySkus.addAll(moreSubsSkus); } if (!querySkus.isEmpty()) { - inventoryLatch = new CountDownLatch(1); - HashSet queryStoreSkus = new HashSet(querySkus.size()); + final HashSet queryStoreSkus = new HashSet(querySkus.size()); for (String sku : querySkus) { queryStoreSkus.add(SkuManager.getInstance().getStoreSku(OpenIabHelper.NAME_AMAZON, sku)); } - PurchasingManager.initiateItemDataRequest(queryStoreSkus); + inventoryLatch = new CountDownLatch(1); + PurchasingService.getProductData(queryStoreSkus); try { inventoryLatch.await(); } catch (InterruptedException e) { Logger.w("queryInventory() SkuDetails fetching interrupted"); + return null; + } finally { + inventoryLatch = null; } } } @@ -186,153 +201,168 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk @Override public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse purchaseUpdatesResponse) { - Logger.v("onPurchaseUpdatesResponse() reqStatus: ", purchaseUpdatesResponse.getPurchaseUpdatesRequestStatus(), + final PurchaseUpdatesResponse.RequestStatus requestStatus = purchaseUpdatesResponse.getRequestStatus(); + Logger.v("onPurchaseUpdatesResponse() reqStatus: ", requestStatus, "reqId: ", purchaseUpdatesResponse.getRequestId()); - if ((currentUserId != null) && !currentUserId.equals(purchaseUpdatesResponse.getUserId())) { - Logger.w("onPurchaseUpdatesResponse() Current UserId: ", currentUserId, ", purchase UserId: ", - purchaseUpdatesResponse.getUserId()); - inventoryLatch.countDown(); - return; - } - - if (Logger.isLoggable()) { - // TODO: do something with this - for (final String sku : purchaseUpdatesResponse.getRevokedSkus()) { - Logger.v("Revoked Sku:", sku); - } - } - - switch (purchaseUpdatesResponse.getPurchaseUpdatesRequestStatus()) { + switch (requestStatus) { case SUCCESSFUL: + for (final String sku : inventory.getAllOwnedSkus()) { + inventory.erasePurchase(sku); + } + final UserData userData = purchaseUpdatesResponse.getUserData(); + final String userId = userData.getUserId(); + if (!userId.equals(currentUserId)) { + Logger.w("onPurchaseUpdatesResponse() Current UserId: ", currentUserId, + ", purchase UserId: ", userId); + break; + } for (final Receipt receipt : purchaseUpdatesResponse.getReceipts()) { - final String storeSku = receipt.getSku(); - Purchase purchase; - switch (receipt.getItemType()) { - case ENTITLED: - purchase = new Purchase(OpenIabHelper.NAME_AMAZON); - purchase.setItemType(IabHelper.ITEM_TYPE_INAPP); - purchase.setSku(SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, storeSku)); - inventory.addPurchase(purchase); - Logger.d("Add to inventory SKU: ", storeSku); - break; - - case SUBSCRIPTION: - final SubscriptionPeriod subscriptionPeriod = receipt.getSubscriptionPeriod(); - if (subscriptionPeriod.getEndDate() == null) { - purchase = new Purchase(OpenIabHelper.NAME_AMAZON); - purchase.setItemType(IabHelper.ITEM_TYPE_SUBS); - purchase.setSku(SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, storeSku)); - inventory.addPurchase(purchase); - Logger.d("Add subscription to inventory SKU: ", storeSku); - } - - break; - - } + inventory.addPurchase(getPurchase(receipt)); } - - final Offset newOffset = purchaseUpdatesResponse.getOffset(); - if (purchaseUpdatesResponse.isMore()) { - Logger.v("Initiating Another Purchase Updates with offset: ", newOffset); - PurchasingManager.initiatePurchaseUpdatesRequest(newOffset); - } else { - inventoryLatch.countDown(); + if (purchaseUpdatesResponse.hasMore()) { + PurchasingService.getPurchaseUpdates(false); + Logger.v("Initiating Another Purchase Updates with offset: "); } - return; + break; case FAILED: - inventoryLatch.countDown(); - return; + break; + } + if (inventoryLatch != null) { + inventoryLatch.countDown(); + } + } + + private Purchase getPurchase(final Receipt receipt) { + final String storeSku = receipt.getSku(); + + final Purchase purchase = new Purchase(OpenIabHelper.NAME_AMAZON); + purchase.setSku(SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, storeSku)); + + switch (receipt.getProductType()) { + case CONSUMABLE: + // TODO Make sure this behavior is intended + case ENTITLED: + purchase.setItemType(IabHelper.ITEM_TYPE_INAPP); + Logger.d("Add to inventory SKU: ", storeSku); + break; + case SUBSCRIPTION: + // TODO Make sure cancelDate is always available + purchase.setItemType(IabHelper.ITEM_TYPE_SUBS); + purchase.setSku(SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, storeSku)); + Logger.d("Add subscription to inventory SKU: ", storeSku); + break; } - inventoryLatch.countDown(); + return purchase; } @Override - public void onItemDataResponse(final ItemDataResponse itemDataResponse) { - Logger.v("onItemDataResponse() reqStatus: ", itemDataResponse.getItemDataRequestStatus(), - ", reqId: ", itemDataResponse.getRequestId()); - switch (itemDataResponse.getItemDataRequestStatus()) { - case SUCCESSFUL_WITH_UNAVAILABLE_SKUS: - // Skus that you can not purchase will be here. - if (Logger.isLoggable()) { - for (final String s : itemDataResponse.getUnavailableSkus()) { - Logger.v("Unavailable SKU:", s); - } - } + public void onProductDataResponse(final ProductDataResponse productDataResponse) { + final ProductDataResponse.RequestStatus status = productDataResponse.getRequestStatus(); + Logger.v("onItemDataResponse() reqStatus: ", status, + ", reqId: ", productDataResponse.getRequestId()); + + switch (status) { case SUCCESSFUL: - // Information you'll want to display about your IAP items is here - // In this example we'll simply log them. - final Map items = itemDataResponse.getItemData(); - for (final String key : items.keySet()) { - Item i = items.get(key); - final String storeSku = i.getSku(); - if (Logger.isLoggable()) { - Logger.v(String.format("Item: %s\n Type: %s\n SKU: %s\n Price: %s\n Description: %s\n", - i.getTitle(), i.getItemType(), storeSku, i.getPrice(), i.getDescription())); - } - String itemType = i.getItemType() == Item.ItemType.SUBSCRIPTION ? IabHelper.ITEM_TYPE_SUBS : IabHelper.ITEM_TYPE_INAPP; - SkuDetails skuDetails = new SkuDetails(itemType, - SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, i.getSku()), - i.getTitle(), i.getPrice(), i.getDescription()); - inventory.addSkuDetails(skuDetails); + final Map productData = productDataResponse.getProductData(); + for (final String key : productData.keySet()) { + final Product product = productData.get(key); + inventory.addSkuDetails(getSkuDetails(product)); } break; + case FAILED: + // Fall through + case NOT_SUPPORTED: + break; } - inventoryLatch.countDown(); + if (inventoryLatch != null) { + inventoryLatch.countDown(); + } + } + + private SkuDetails getSkuDetails(final Product product) { + final String sku = product.getSku(); + final String price = product.getPrice().toString(); + final String title = product.getTitle(); + final String description = product.getDescription(); + final ProductType productType = product.getProductType(); + Logger.v(String.format("Item: %s\n Type: %s\n SKU: %s\n Price: %s\n Description: %s\n", + title, productType, sku, price, description)); + + final String openIabSkuType = productType == ProductType.SUBSCRIPTION + ? IabHelper.ITEM_TYPE_SUBS + : IabHelper.ITEM_TYPE_INAPP; + final String openIabSku = SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, sku); + return new SkuDetails(openIabSkuType, openIabSku, title, price, description); } @Override - public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode, IabHelper.OnIabPurchaseFinishedListener listener, String extraData) { - String requestId = PurchasingManager.initiatePurchaseRequest(sku); - storeRequestListener(requestId, listener); + public void launchPurchaseFlow( + final Activity activity, + final String sku, + final String itemType, + final int requestCode, + final IabHelper.OnIabPurchaseFinishedListener listener, + final String extraData) { + requestListeners.put(PurchasingService.purchase(sku), listener); } @Override public void onPurchaseResponse(final PurchaseResponse purchaseResponse) { - Logger.v("onPurchaseResponse() PurchaseRequestStatus:", purchaseResponse.getPurchaseRequestStatus()); - - IabResult result = null; - Purchase purchase = new Purchase(OpenIabHelper.NAME_AMAZON); - - if ((currentUserId != null) && !currentUserId.equals(purchaseResponse.getUserId())) { - Logger.w("onPurchaseResponse() userId: ", currentUserId, ", purchase.userId: ", purchaseResponse.getUserId()); - result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ERROR, "userId doesn't match purchase.userId"); - } else { - switch (purchaseResponse.getPurchaseRequestStatus()) { - case SUCCESSFUL: - final Receipt receipt = purchaseResponse.getReceipt(); - final String storeSku = receipt.getSku(); - - purchase.setOriginalJson(generateOriginalJson(purchaseResponse)); - purchase.setSku(SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, storeSku)); - switch (receipt.getItemType()) { - case CONSUMABLE: - case ENTITLED: - purchase.setItemType(IabHelper.ITEM_TYPE_INAPP); - break; - case SUBSCRIPTION: - purchase.setItemType(IabHelper.ITEM_TYPE_SUBS); - break; - } - //printReceipt(purchaseResponse.getReceipt()); - result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_OK, "Success"); - break; - case ALREADY_ENTITLED: - result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED, "Already owned"); - break; - case FAILED: - result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_USER_CANCELED, "Purchase failed"); - break; - case INVALID_SKU: - result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ERROR, "Invalid sku"); + final PurchaseResponse.RequestStatus status = purchaseResponse.getRequestStatus(); + final RequestId requestId = purchaseResponse.getRequestId(); + Logger.v("onPurchaseResponse() PurchaseRequestStatus:", status, + ", reqId: ", requestId); + + final Purchase purchase = new Purchase(OpenIabHelper.NAME_AMAZON); + final IabResult result; + boolean shouldNotifyFulfillment = false; + switch (status){ + case SUCCESSFUL: + final UserData userData = purchaseResponse.getUserData(); + final String userId = userData.getUserId(); + if (!userId.equals(currentUserId)) { + Logger.w("onPurchaseResponse() Current UserId: ", currentUserId, + ", purchase UserId: ", userId); + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ERROR, + "Current UserId doesn't match purchase UserId"); break; - } + } + final Receipt receipt = purchaseResponse.getReceipt(); + final String storeSku = receipt.getSku(); + purchase.setOriginalJson(generateOriginalJson(purchaseResponse)); + purchase.setSku(SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, storeSku)); + final String openIabSkuType = receipt.getProductType() == ProductType.SUBSCRIPTION + ? IabHelper.ITEM_TYPE_SUBS + : IabHelper.ITEM_TYPE_INAPP; + purchase.setItemType(openIabSkuType); + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_OK, "Success"); + shouldNotifyFulfillment = true; + break; + case INVALID_SKU: + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE, "Invalid SKU"); + break; + case ALREADY_PURCHASED: + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED, "Item is already purchased"); + break; + case FAILED: + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ERROR, "Purchase failed"); + break; + case NOT_SUPPORTED: + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "This call is not supported"); + break; + default: + result = null; } - IabHelper.OnIabPurchaseFinishedListener listener = getRequestListener(purchaseResponse.getRequestId()); + final IabHelper.OnIabPurchaseFinishedListener listener = requestListeners.remove(requestId); if (listener != null) { listener.onIabPurchaseFinished(result, purchase); + if (shouldNotifyFulfillment) { + final Receipt receipt = purchaseResponse.getReceipt(); + PurchasingService.notifyFulfillment(receipt.getReceiptId(), FulfillmentResult.FULFILLED); + } } else { - Logger.e("Something went wrong: PurchaseFinishedListener is null"); + Logger.e("Something went wrong: PurchaseFinishedListener is not found"); } } @@ -344,28 +374,33 @@ public void onPurchaseResponse(final PurchaseResponse purchaseResponse) { * "orderId" : "purchaseResponse.getRequestId" * "productId" : "receipt.getSku" * "purchaseStatus" : "purchaseRequestStatus.name" - * "userId" : "purchaseResponse.getUserId()" // can be null + * "userId" : "purchaseResponse.getUserId()" // if non-null * "itemType" : "receipt.getItemType().name()" // if non-null - * "purchaseToken" : "receipt.purchaseToken" + * "purchaseToken" : "receipt.getReceiptId()" * } * * @param purchaseResponse Purchase to convert. * @return Generate JSON from purchase. */ private String generateOriginalJson(PurchaseResponse purchaseResponse) { - JSONObject json = new JSONObject(); + final JSONObject json = new JSONObject(); try { Receipt receipt = purchaseResponse.getReceipt(); json.put(JSON_KEY_ORDER_ID, purchaseResponse.getRequestId()); json.put(JSON_KEY_PRODUCT_ID, receipt.getSku()); - if (purchaseResponse.getPurchaseRequestStatus() != null) { - json.put(JSON_KEY_PURCHASE_STATUS, purchaseResponse.getPurchaseRequestStatus().name()); + final PurchaseResponse.RequestStatus requestStatus = purchaseResponse.getRequestStatus(); + if (requestStatus != null) { + json.put(JSON_KEY_PURCHASE_STATUS, requestStatus.name()); } - json.put(JSON_KEY_USER_ID, purchaseResponse.getUserId()); - if (receipt.getItemType() != null) { - json.put(JSON_KEY_RECEIPT_ITEM_TYPE, receipt.getItemType().name()); + final UserData userData = purchaseResponse.getUserData(); + if (userData != null) { + json.put(JSON_KEY_USER_ID, userData.getUserId()); } - json.put(JSON_KEY_RECEIPT_PURCHASE_TOKEN, receipt.getPurchaseToken()); + final ProductType productType = receipt.getProductType(); + if (productType != null) { + json.put(JSON_KEY_RECEIPT_ITEM_TYPE, productType.name()); + } + json.put(JSON_KEY_RECEIPT_PURCHASE_TOKEN, receipt.getReceiptId()); Logger.d("generateOriginalJson(): JSON\n", json); } catch (JSONException e) { Logger.e("generateOriginalJson() failed to generate JSON", e); @@ -385,19 +420,11 @@ public boolean subscriptionsSupported() { @Override public void dispose() { - + // Nothing to do here } @Override public boolean handleActivityResult(int requestCode, int resultCode, Intent data) { return false; } - - private void storeRequestListener(String requestId, IabHelper.OnIabPurchaseFinishedListener listener) { - mRequestListeners.put(requestId, listener); - } - - public IabHelper.OnIabPurchaseFinishedListener getRequestListener(String requestId) { - return mRequestListeners.get(requestId); - } } diff --git a/library/src/org/onepf/oms/appstore/SamsungApps.java b/library/src/org/onepf/oms/appstore/SamsungApps.java index ffd44893..d61d3111 100644 --- a/library/src/org/onepf/oms/appstore/SamsungApps.java +++ b/library/src/org/onepf/oms/appstore/SamsungApps.java @@ -130,7 +130,7 @@ public void run() { Inventory inventory = getInAppBillingService() .queryInventory(true, SkuManager.getInstance() .getAllStoreSkus(OpenIabHelper.NAME_SAMSUNG), null); - if (inventory != null && !CollectionUtils.isEmpty(inventory.mSkuMap)) { + if (inventory != null && !CollectionUtils.isEmpty(inventory.getAllOwnedSkus())) { isBillingAvailable = true; } } catch (IabException e) { diff --git a/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java b/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java index 58d7de7d..b6dd6d13 100644 --- a/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java +++ b/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java @@ -26,8 +26,8 @@ * An Inventory is returned by such methods as {@link IabHelper#queryInventory}. */ public class Inventory { - private final Map mSkuMap = new ConcurrentHashMap<>(); - private final Map mPurchaseMap = new ConcurrentHashMap<>(); + private final Map mSkuMap = new ConcurrentHashMap(); + private final Map mPurchaseMap = new ConcurrentHashMap(); public Inventory() { } @@ -76,14 +76,14 @@ public void erasePurchase(String sku) { * Returns a list of all owned product IDs. */ public List getAllOwnedSkus() { - return new ArrayList<>(mPurchaseMap.keySet()); + return new ArrayList(mPurchaseMap.keySet()); } /** * Returns a list of all owned product IDs of a given type */ public List getAllOwnedSkus(String itemType) { - List result = new ArrayList<>(); + List result = new ArrayList(); for (Purchase p : mPurchaseMap.values()) { if (p.getItemType().equals(itemType)) result.add(p.getSku()); } @@ -94,7 +94,7 @@ public List getAllOwnedSkus(String itemType) { * Returns a list of all purchases. */ public List getAllPurchases() { - return new ArrayList<>(mPurchaseMap.values()); + return new ArrayList(mPurchaseMap.values()); } public void addSkuDetails(SkuDetails d) { diff --git a/samples/life_openiab/src/main/AndroidManifest.xml b/samples/life_openiab/src/main/AndroidManifest.xml index 462bbd9c..c973de68 100644 --- a/samples/life_openiab/src/main/AndroidManifest.xml +++ b/samples/life_openiab/src/main/AndroidManifest.xml @@ -45,11 +45,11 @@ - + + - + diff --git a/samples/trivialdrive/src/main/AndroidManifest.xml b/samples/trivialdrive/src/main/AndroidManifest.xml index 050c2777..28094ccc 100644 --- a/samples/trivialdrive/src/main/AndroidManifest.xml +++ b/samples/trivialdrive/src/main/AndroidManifest.xml @@ -53,11 +53,10 @@ - + - + From 19a92ebc8d7c3d8734ee5ce3518241663e9c5007 Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Mon, 1 Sep 2014 12:15:03 +0300 Subject: [PATCH 03/72] Remove code for cut Fortumo using. --- library/build.gradle | 18 -- library/proguard-unity.txt | 49 ----- library/src/org/onepf/oms/OpenIabHelper.java | 217 +++++++++---------- 3 files changed, 99 insertions(+), 185 deletions(-) delete mode 100644 library/proguard-unity.txt diff --git a/library/build.gradle b/library/build.gradle index 87401794..2427f22f 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -48,27 +48,9 @@ android { } } - buildTypes { - debug - release - unity { - runProguard true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-unity.txt' - } - } - lintOptions { abortOnError false } - - productFlavors { - noFortumo { - buildConfigField "boolean", "FORTUMO_ENABLE", "false" - } - complete { - buildConfigField "boolean", "FORTUMO_ENABLE", "true" - } - } } dependencies { diff --git a/library/proguard-unity.txt b/library/proguard-unity.txt deleted file mode 100644 index 553e94e0..00000000 --- a/library/proguard-unity.txt +++ /dev/null @@ -1,49 +0,0 @@ --optimizations !class/merging/* --optimizationpasses 5 --dontpreverify --dontnote --dontobfuscate - --dontwarn ** - - --keepclassmembers class org.onepf.oms.appstore.googleUtils.* { - public (...); - *** get*(...); -} - --keepclassmembers class org.onepf.oms.OpenIabHelper { - public (...); - public *** mapSku(...); - public *** startSetup(...); - public *** dispose(); - public *** subscriptionsSupported(); - public *** queryInventoryAsync(...); - public *** consumeAsync(...); - public static *** enableDebugLogging(...); - public *** handleActivityResult(...); - public *** launchPurchaseFlow(...); - public *** launchSubscriptionPurchaseFlow(...); -} - --keep class org.onepf.oms.OpenIabHelper$Options$Builder { - public *** !setSupportFortumo(...); -} - --keep class org.onepf.oms.OpenIabHelper { - public static final *** !NAME_FORTUMO; -} - --keep class org.onepf.oms.appstore.googleUtils.IabHelper { - public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE; -} - --keep class org.onepf.oms.OpenIabHelper$Options { - public static int VERIFY_ONLY_KNOWN; -} - -# AMAZON --dontwarn com.amazon.** --keep class com.amazon.** {*;} --keepattributes *Annotation* --dontoptimize \ No newline at end of file diff --git a/library/src/org/onepf/oms/OpenIabHelper.java b/library/src/org/onepf/oms/OpenIabHelper.java index 843222cc..3f87f20d 100644 --- a/library/src/org/onepf/oms/OpenIabHelper.java +++ b/library/src/org/onepf/oms/OpenIabHelper.java @@ -42,7 +42,6 @@ import org.onepf.oms.appstore.googleUtils.IabException; import org.onepf.oms.appstore.googleUtils.IabHelper; import org.onepf.oms.appstore.googleUtils.IabHelper.OnIabSetupFinishedListener; -import org.onepf.oms.appstore.googleUtils.IabHelper.QueryInventoryFinishedListener; import org.onepf.oms.appstore.googleUtils.IabResult; import org.onepf.oms.appstore.googleUtils.Inventory; import org.onepf.oms.appstore.googleUtils.Purchase; @@ -359,14 +358,12 @@ public void run() { //todo redo boolean hasFortumoInSetup; - if (BuildConfig.FORTUMO_ENABLE) { - hasFortumoInSetup = false; - for (Appstore store : stores2check) { - if (store instanceof SamsungApps) { - samsungInSetup = (SamsungApps) store; - } else if (store instanceof FortumoStore) { - hasFortumoInSetup = true; - } + hasFortumoInSetup = false; + for (Appstore store : stores2check) { + if (store instanceof SamsungApps) { + samsungInSetup = (SamsungApps) store; + } else if (store instanceof FortumoStore) { + hasFortumoInSetup = true; } } @@ -379,7 +376,7 @@ public void run() { if (!equippedStores.isEmpty()) { mAppstore = selectBillingService(equippedStores); } - if (BuildConfig.FORTUMO_ENABLE && mAppstore == null) { + if (mAppstore == null) { if (!hasFortumoInSetup && options.supportFortumo) { mAppstore = FortumoStore.initFortumoStore(context, true); } @@ -392,7 +389,7 @@ public void run() { } else { // found no equipped stores. Select store based on store parameters mAppstore = selectBillingService(stores2check); - if (BuildConfig.FORTUMO_ENABLE && mAppstore == null) { + if (mAppstore == null) { if (!hasFortumoInSetup && options.supportFortumo) { mAppstore = FortumoStore.initFortumoStore(context, false); } @@ -410,7 +407,7 @@ public void run() { fireSetupFinished(listener, result); } else { // no inventory check. Select store based on store parameters mAppstore = selectBillingService(stores2check); - if (BuildConfig.FORTUMO_ENABLE && null == mAppstore) { + if (mAppstore == null) { if (!hasFortumoInSetup && options.supportFortumo) { mAppstore = FortumoStore.initFortumoStore(context, false); } @@ -476,109 +473,104 @@ public static void checkOptions(Options options) { private static void checkSettings(Options options, Context context) { checkOptions(options); checkSamsung(context); - if (BuildConfig.FORTUMO_ENABLE) { - checkFortumo(options, context); - } + checkFortumo(options, context); checkNokia(options, context); } private static void checkFortumo(Options options, Context context) { - if (BuildConfig.FORTUMO_ENABLE) { - boolean checkFortumo = options.supportFortumo; - if (!checkFortumo && options.availableStores != null) { - for (Appstore store : options.availableStores) { - if (store instanceof FortumoStore) { - checkFortumo = true; - break; - } + boolean checkFortumo = options.supportFortumo; + if (!checkFortumo && options.availableStores != null) { + for (Appstore store : options.availableStores) { + if (store instanceof FortumoStore) { + checkFortumo = true; + break; } } - if (checkFortumo) { - StringBuilder resultBuilder = new StringBuilder(); - //is Fortumo lib available? - StringBuilder jarResultBuilder = new StringBuilder(); - try { - FortumoStore.class.getClassLoader().loadClass("mp.MpUtils"); - } catch (ClassNotFoundException e) { - jarResultBuilder.append(" \n - Fortumo classes CAN'T be loaded."); - } + } + if (checkFortumo) { + StringBuilder resultBuilder = new StringBuilder(); + //is Fortumo lib available? + StringBuilder jarResultBuilder = new StringBuilder(); + try { + FortumoStore.class.getClassLoader().loadClass("mp.MpUtils"); + } catch (ClassNotFoundException e) { + jarResultBuilder.append(" \n - Fortumo classes CAN'T be loaded."); + } - //manifest - StringBuilder manifestResultBuilder = new StringBuilder(); - checkPermission(context, "android.permission.INTERNET", manifestResultBuilder); - checkPermission(context, "android.permission.ACCESS_NETWORK_STATE", manifestResultBuilder); - checkPermission(context, "android.permission.READ_PHONE_STATE", manifestResultBuilder); - // checkPermission(context, "android.permission.RECEIVE_SMS", manifestResultBuilder); - // checkPermission(context, "android.permission.SEND_SMS", manifestResultBuilder); - - Intent paymentActivityIntent = new Intent(); - paymentActivityIntent.setClassName(context.getPackageName(), "mp.MpActivity"); - if (context.getPackageManager().resolveActivity(paymentActivityIntent, 0) == null) { - formatComponentStatus(" - Required mp.MpActivity is NOT declared.", manifestResultBuilder); - } + //manifest + StringBuilder manifestResultBuilder = new StringBuilder(); + checkPermission(context, "android.permission.INTERNET", manifestResultBuilder); + checkPermission(context, "android.permission.ACCESS_NETWORK_STATE", manifestResultBuilder); + checkPermission(context, "android.permission.READ_PHONE_STATE", manifestResultBuilder); + // checkPermission(context, "android.permission.RECEIVE_SMS", manifestResultBuilder); + // checkPermission(context, "android.permission.SEND_SMS", manifestResultBuilder); + + Intent paymentActivityIntent = new Intent(); + paymentActivityIntent.setClassName(context.getPackageName(), "mp.MpActivity"); + if (context.getPackageManager().resolveActivity(paymentActivityIntent, 0) == null) { + formatComponentStatus(" - Required mp.MpActivity is NOT declared.", manifestResultBuilder); + } - Intent mpServerIntent = new Intent(); - mpServerIntent.setClassName(context.getPackageName(), "mp.MpService"); - if (context.getPackageManager().resolveService(mpServerIntent, 0) == null) { - formatComponentStatus(" - Required mp.MpService is NOT declared.", manifestResultBuilder); - } + Intent mpServerIntent = new Intent(); + mpServerIntent.setClassName(context.getPackageName(), "mp.MpService"); + if (context.getPackageManager().resolveService(mpServerIntent, 0) == null) { + formatComponentStatus(" - Required mp.MpService is NOT declared.", manifestResultBuilder); + } - Intent statusUpdateServiceIntent = new Intent(); - statusUpdateServiceIntent.setClassName(context.getPackageName(), "mp.StatusUpdateService"); - if (context.getPackageManager().resolveService(statusUpdateServiceIntent, 0) == null) { - formatComponentStatus(" - Required mp.StatusUpdateService is NOT declared.", manifestResultBuilder); - } + Intent statusUpdateServiceIntent = new Intent(); + statusUpdateServiceIntent.setClassName(context.getPackageName(), "mp.StatusUpdateService"); + if (context.getPackageManager().resolveService(statusUpdateServiceIntent, 0) == null) { + formatComponentStatus(" - Required mp.StatusUpdateService is NOT declared.", manifestResultBuilder); + } - //xml - StringBuilder xmlStringBuilder = new StringBuilder(); - try { - final List strings = Arrays.asList(context.getResources().getAssets().list("")); - final boolean hasProductFile = strings.contains(FortumoStore.IN_APP_PRODUCTS_FILE_NAME); - final boolean hasFortumoDetailsFile = strings.contains(FortumoStore.FORTUMO_DETAILS_FILE_NAME); + //xml + StringBuilder xmlStringBuilder = new StringBuilder(); + try { + final List strings = Arrays.asList(context.getResources().getAssets().list("")); + final boolean hasProductFile = strings.contains(FortumoStore.IN_APP_PRODUCTS_FILE_NAME); + final boolean hasFortumoDetailsFile = strings.contains(FortumoStore.FORTUMO_DETAILS_FILE_NAME); + if (!hasProductFile) { + xmlStringBuilder.append(" - Required file " + FortumoStore.IN_APP_PRODUCTS_FILE_NAME + " NOT found in /assets."); + } + if (!hasFortumoDetailsFile) { if (!hasProductFile) { - xmlStringBuilder.append(" - Required file " + FortumoStore.IN_APP_PRODUCTS_FILE_NAME + " NOT found in /assets."); - } - if (!hasFortumoDetailsFile) { - if (!hasProductFile) { - xmlStringBuilder.append('\n'); - } - xmlStringBuilder.append(" - Required file " + FortumoStore.FORTUMO_DETAILS_FILE_NAME + " NOT found in /assets."); - } - } catch (IOException e) { - if (xmlStringBuilder.length() > 0) { xmlStringBuilder.append('\n'); } - xmlStringBuilder.append("- Xml files CANNOT be parsed."); + xmlStringBuilder.append(" - Required file " + FortumoStore.FORTUMO_DETAILS_FILE_NAME + " NOT found in /assets."); + } + } catch (IOException e) { + if (xmlStringBuilder.length() > 0) { + xmlStringBuilder.append('\n'); } + xmlStringBuilder.append("- Xml files CANNOT be parsed."); + } - final boolean noJar = jarResultBuilder.length() > 0; - final boolean smthWrongWithManifest = manifestResultBuilder.length() > 0; - final boolean smthWrongWithgXmlFiles = xmlStringBuilder.length() > 0; - if (noJar || smthWrongWithManifest || smthWrongWithgXmlFiles) { - resultBuilder.append("\nFortumo setup failed for the following reasons:"); - if (noJar) { - resultBuilder.append('\n'); - resultBuilder.append(jarResultBuilder); - } - if (smthWrongWithgXmlFiles) { - resultBuilder.append('\n'); - resultBuilder.append(xmlStringBuilder); - } - if (smthWrongWithManifest) { - resultBuilder.append('\n'); - resultBuilder.append(manifestResultBuilder); - } + final boolean noJar = jarResultBuilder.length() > 0; + final boolean smthWrongWithManifest = manifestResultBuilder.length() > 0; + final boolean smthWrongWithgXmlFiles = xmlStringBuilder.length() > 0; + if (noJar || smthWrongWithManifest || smthWrongWithgXmlFiles) { + resultBuilder.append("\nFortumo setup failed for the following reasons:"); + if (noJar) { + resultBuilder.append('\n'); + resultBuilder.append(jarResultBuilder); } - if (resultBuilder.length() > 0) { - resultBuilder.append('\n') - .append("********************************************************************************************************\n") - .append("* To support Fortumo follow the instructions of https://github.com/onepf/OpenIAB/blob/master/README.md *\n") - .append("********************************************************************************************************"); - throw new IllegalStateException(resultBuilder.toString(), null); + if (smthWrongWithgXmlFiles) { + resultBuilder.append('\n'); + resultBuilder.append(xmlStringBuilder); } + if (smthWrongWithManifest) { + resultBuilder.append('\n'); + resultBuilder.append(manifestResultBuilder); + } + } + if (resultBuilder.length() > 0) { + resultBuilder.append('\n') + .append("********************************************************************************************************\n") + .append("* To support Fortumo follow the instructions of https://github.com/onepf/OpenIAB/blob/master/README.md *\n") + .append("********************************************************************************************************"); + throw new IllegalStateException(resultBuilder.toString(), null); } } - } private static void checkNokia(Options options, Context context) { @@ -1746,28 +1738,17 @@ public Options build() { Collections.unmodifiableMap(this.storeKeys); String[] preferredStoreNames = CollectionUtils.isEmpty(this.preferredStoreNames) ? null : this.preferredStoreNames.toArray(new String[this.preferredStoreNames.size()]); - if (BuildConfig.FORTUMO_ENABLE) { - return new Options( - availableStores, - storeKeys, - checkInventory, - checkInventoryTimeout, - discoveryTimeout, - verifyMode, - supportFortumo, - preferredStoreNames, - samsungCertificationRequestCode); - } else { - return new Options( - availableStores, - storeKeys, - checkInventory, - checkInventoryTimeout, - discoveryTimeout, - verifyMode, - preferredStoreNames, - samsungCertificationRequestCode); - } + + return new Options( + availableStores, + storeKeys, + checkInventory, + checkInventoryTimeout, + discoveryTimeout, + verifyMode, + supportFortumo, + preferredStoreNames, + samsungCertificationRequestCode); } } } From faeba9c1c31bf5e7a5e992830a948fd93d5d0c14 Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Mon, 1 Sep 2014 12:19:54 +0300 Subject: [PATCH 04/72] Remove description "Work with multiple build variant" --- README.md | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/README.md b/README.md index 799f587c..d67b110d 100644 --- a/README.md +++ b/README.md @@ -519,51 +519,6 @@ Basic Principles * **Open In-App Billing protocol** - OpenIAB is designed to provide a lightweight solution that supports hundreds of appstores. When app store implements OpenIAB protocol on app store side all applications with OpenIAB become fully compatible with new app store without recompiling. * **No middle man** -How to Work with multiplе build variants in library -===== - -[New build system (NBS)][2] is using in library for build version of jar with or without -support Fortumo (for build Unity plugin). -Version without fortumo doesn't include fortumo library and remove all code, that contains fortumo code. - -In library described two flavors: - -```groovy - noFortumo { - buildConfigField "boolean", "FORTUMO_ENABLE", "false" - } - complete { - buildConfigField "boolean", "FORTUMO_ENABLE", "true" - } -``` - -and three build types: -```groovy - debug - release - unity { - runProguard true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-unity.txt' - } -``` - -Flavors need for boolean generate FORTUMO_ENABLE constant in BuildConfig class. -We use this constant in condition for remove code about fortumo. - -Build types debug and release are the same that in default building. -Only `Unity` build type run proguard in project. - -If you don't wanna remove fortumo code, you must use `completeDebug` or `completeRelease` build -variant. For remove fortumo code you must use `noFortumoUnity` build variant.
-
- -In you project you must add dependencies on library project
- -```groovy - flavor1Compile project(path: ':OpenIab Library', configuration: 'completeRelease') - flavor2Compile project(path: ':OpenIab Library', configuration: 'noFortumoUnity') -``` - No Middle Man ===== OpenIAB is an open source library that handles OpenIAB protocol and wraps some already existing IAB SDKs as well. From 9503cc2f1317d1db8d9c46874580c5b225476ebd Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 1 Sep 2014 22:21:14 +0400 Subject: [PATCH 05/72] Fix synchronization issues with Amazon purchaseUpdates --- library/src/org/onepf/oms/OpenIabHelper.java | 2 +- .../AmazonAppstoreBillingService.java | 45 ++++++++++--------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/library/src/org/onepf/oms/OpenIabHelper.java b/library/src/org/onepf/oms/OpenIabHelper.java index 843222cc..1f57c77f 100644 --- a/library/src/org/onepf/oms/OpenIabHelper.java +++ b/library/src/org/onepf/oms/OpenIabHelper.java @@ -796,7 +796,7 @@ public void run() { }); } try { - storeRemains.await(options.checkInventoryTimeoutMs, TimeUnit.MILLISECONDS); + storeRemains.await(); Logger.dWithTimeFromUp("inventory check done"); } catch (InterruptedException e) { Logger.e(e, "selectBillingService() inventory check is failed. candidates: ", candidates.size() diff --git a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java b/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java index e02070d0..099be9c4 100644 --- a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java +++ b/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java @@ -50,6 +50,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; /** @@ -91,10 +92,10 @@ public class AmazonAppstoreBillingService implements AppstoreInAppBillingService * After whole purchase data is received request SKU details by getProductData() *
* {@link #onPurchaseUpdatesResponse(PurchaseUpdatesResponse)} - triggered by Amazon SDK. - * Handles purchases data chunk by chunk. Releases inventoryLatch lock after last chunk is handled + * Handles purchases data chunk by chunk. Releases inventoryLatch lock after last chunk is handled. *

* {@link #onProductDataResponse(ProductDataResponse)} - triggered by Amazon SDK. - * Handles items data chunk by chunk. Releases inventoryLatch lock after last chunk is handled + * Handles items data. Releases inventoryLatch lock when all data is handled. *

*

NOTES:

* Amazon SDK may trigger on*Response() before queryInventory() is called. It happens @@ -102,7 +103,7 @@ public class AmazonAppstoreBillingService implements AppstoreInAppBillingService * crashes or relaunched). So inventory object must not be null. */ private final Inventory inventory = new Inventory(); - private volatile CountDownLatch inventoryLatch; + private final Map inventoryLatchMap = new ConcurrentHashMap(); /** * If not null will be notified from @@ -159,15 +160,14 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk Logger.d("queryInventory() querySkuDetails: ", querySkuDetails, " moreItemSkus: ", moreItemSkus, " moreSubsSkus: ", moreSubsSkus); - inventoryLatch = new CountDownLatch(1); - PurchasingService.getPurchaseUpdates(true); + final RequestId purchaseUpdatesRequestId = PurchasingService.getPurchaseUpdates(true); + final CountDownLatch purchaseUpdatesLatch = new CountDownLatch(1); + inventoryLatchMap.put(purchaseUpdatesRequestId, purchaseUpdatesLatch); try { - inventoryLatch.await(); + purchaseUpdatesLatch.await(); } catch (InterruptedException e) { Logger.e("queryInventory() await interrupted"); return null; - } finally { - inventoryLatch = null; } if (querySkuDetails) { @@ -183,15 +183,14 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk for (String sku : querySkus) { queryStoreSkus.add(SkuManager.getInstance().getStoreSku(OpenIabHelper.NAME_AMAZON, sku)); } - inventoryLatch = new CountDownLatch(1); - PurchasingService.getProductData(queryStoreSkus); + final RequestId productDataRequestId = PurchasingService.getProductData(queryStoreSkus); + final CountDownLatch productDataLatch = new CountDownLatch(1); + inventoryLatchMap.put(productDataRequestId, productDataLatch); try { - inventoryLatch.await(); + productDataLatch.await(); } catch (InterruptedException e) { Logger.w("queryInventory() SkuDetails fetching interrupted"); return null; - } finally { - inventoryLatch = null; } } } @@ -202,8 +201,9 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk @Override public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse purchaseUpdatesResponse) { final PurchaseUpdatesResponse.RequestStatus requestStatus = purchaseUpdatesResponse.getRequestStatus(); + final RequestId requestId = purchaseUpdatesResponse.getRequestId(); Logger.v("onPurchaseUpdatesResponse() reqStatus: ", requestStatus, - "reqId: ", purchaseUpdatesResponse.getRequestId()); + "reqId: ", requestId); switch (requestStatus) { case SUCCESSFUL: @@ -221,15 +221,18 @@ public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse purchaseUpda inventory.addPurchase(getPurchase(receipt)); } if (purchaseUpdatesResponse.hasMore()) { - PurchasingService.getPurchaseUpdates(false); + final RequestId purchaseUpdatesRequestId = PurchasingService.getPurchaseUpdates(false); + inventoryLatchMap.put(purchaseUpdatesRequestId, inventoryLatchMap.remove(requestId)); Logger.v("Initiating Another Purchase Updates with offset: "); + return; } break; case FAILED: break; } - if (inventoryLatch != null) { - inventoryLatch.countDown(); + final CountDownLatch countDownLatch = inventoryLatchMap.remove(requestId); + if (countDownLatch != null) { + countDownLatch.countDown(); } } @@ -259,8 +262,9 @@ private Purchase getPurchase(final Receipt receipt) { @Override public void onProductDataResponse(final ProductDataResponse productDataResponse) { final ProductDataResponse.RequestStatus status = productDataResponse.getRequestStatus(); + final RequestId requestId = productDataResponse.getRequestId(); Logger.v("onItemDataResponse() reqStatus: ", status, - ", reqId: ", productDataResponse.getRequestId()); + ", reqId: ", requestId); switch (status) { case SUCCESSFUL: @@ -275,8 +279,9 @@ public void onProductDataResponse(final ProductDataResponse productDataResponse) case NOT_SUPPORTED: break; } - if (inventoryLatch != null) { - inventoryLatch.countDown(); + final CountDownLatch countDownLatch = inventoryLatchMap.remove(requestId); + if (countDownLatch != null) { + countDownLatch.countDown(); } } From f58bab635b0302b165e4ae60715d25e0a350b228 Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Tue, 2 Sep 2014 15:12:18 +0300 Subject: [PATCH 06/72] Add fortumo classes. --- library/libs/FortumoInApp-android-9.1.2-o.jar | Bin 0 -> 529921 bytes .../oms/appstore/FortumoBillingService.java | 540 ++++++++++++++++++ .../org/onepf/oms/appstore/FortumoStore.java | 147 +++++ .../fortumoUtils/InappBaseProduct.java | 215 +++++++ .../InappSubscriptionProduct.java | 79 +++ .../fortumoUtils/InappsXMLParser.java | 278 +++++++++ 6 files changed, 1259 insertions(+) create mode 100644 library/libs/FortumoInApp-android-9.1.2-o.jar create mode 100644 library/src/org/onepf/oms/appstore/FortumoBillingService.java create mode 100644 library/src/org/onepf/oms/appstore/FortumoStore.java create mode 100644 library/src/org/onepf/oms/appstore/fortumoUtils/InappBaseProduct.java create mode 100644 library/src/org/onepf/oms/appstore/fortumoUtils/InappSubscriptionProduct.java create mode 100644 library/src/org/onepf/oms/appstore/fortumoUtils/InappsXMLParser.java diff --git a/library/libs/FortumoInApp-android-9.1.2-o.jar b/library/libs/FortumoInApp-android-9.1.2-o.jar new file mode 100644 index 0000000000000000000000000000000000000000..79746115c49b2bc9764699ce2ef6c379516b366d GIT binary patch literal 529921 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�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,q$00v6Jo$3oW1DQc4ht|ru|txIxf)lJ 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%0Aj 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 XJTNnXBkW;u!|cN0mcTxk+fx1yPYkK2 literal 0 HcmV?d00001 diff --git a/library/src/org/onepf/oms/appstore/FortumoBillingService.java b/library/src/org/onepf/oms/appstore/FortumoBillingService.java new file mode 100644 index 00000000..948f0f4a --- /dev/null +++ b/library/src/org/onepf/oms/appstore/FortumoBillingService.java @@ -0,0 +1,540 @@ +/* + * Copyright 2012-2014 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.oms.appstore; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.text.TextUtils; +import android.util.Pair; + +import org.onepf.oms.AppstoreInAppBillingService; +import org.onepf.oms.OpenIabHelper; +import org.onepf.oms.appstore.fortumoUtils.InappBaseProduct; +import org.onepf.oms.appstore.fortumoUtils.InappSubscriptionProduct; +import org.onepf.oms.appstore.fortumoUtils.InappsXMLParser; +import org.onepf.oms.appstore.googleUtils.IabException; +import org.onepf.oms.appstore.googleUtils.IabHelper; +import org.onepf.oms.appstore.googleUtils.IabResult; +import org.onepf.oms.appstore.googleUtils.Inventory; +import org.onepf.oms.appstore.googleUtils.Purchase; +import org.onepf.oms.appstore.googleUtils.SkuDetails; +import org.onepf.oms.util.Logger; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.IOException; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import mp.MpUtils; +import mp.PaymentRequest; +import mp.PaymentResponse; + +/** + * @author akarimova@onepf.org + * @since 23.12.13 + */ +public class FortumoBillingService implements AppstoreInAppBillingService { + private static final String SHARED_PREFS_FORTUMO = "onepf_shared_prefs_fortumo"; + private boolean isNook; + + private int activityRequestCode; + private Context context; + private Map inappsMap; + private IabHelper.OnIabPurchaseFinishedListener purchaseFinishedListener; + private String developerPayload; + + public FortumoBillingService(Context context, boolean isNook) { + this.context = context; + this.isNook = isNook; + } + + @Override + public void startSetup(IabHelper.OnIabSetupFinishedListener listener) { + final IabResult setupResult = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_OK, "Fortumo: successful setup."); + Logger.d("Setup result: ", setupResult); + listener.onIabSetupFinished(setupResult); + } + + @Override + public void launchPurchaseFlow(final Activity act, String sku, String itemType, int requestCode, IabHelper.OnIabPurchaseFinishedListener listener, String extraData) { + this.purchaseFinishedListener = listener; + this.activityRequestCode = requestCode; + this.developerPayload = extraData; + final FortumoProduct fortumoProduct = inappsMap.get(sku); + if (null == fortumoProduct) { + Logger.d("launchPurchaseFlow: required sku ", sku, " was not defined"); + purchaseFinishedListener.onIabPurchaseFinished(new IabResult(IabHelper.BILLING_RESPONSE_RESULT_DEVELOPER_ERROR, String.format("Required product %s was not defined in xml files.", sku)), null); + } else { + final String messageId = getMessageIdInPending(context, fortumoProduct.getProductId()); + if (fortumoProduct.isConsumable() && !TextUtils.isEmpty(messageId) && !messageId.equals("-1")) { + final PaymentResponse paymentResponse = MpUtils.getPaymentResponse(context, Long.valueOf(messageId)); + IabResult result; + Purchase purchase = null; + final int billingStatus = paymentResponse.getBillingStatus(); + if (billingStatus == MpUtils.MESSAGE_STATUS_BILLED) { + purchase = purchaseFromPaymentResponse(context, paymentResponse); + result = new IabResult(OpenIabHelper.BILLING_RESPONSE_RESULT_OK, "Purchase was successful."); + removePendingProduct(context, sku); + } else if (billingStatus == MpUtils.MESSAGE_STATUS_FAILED || billingStatus == MpUtils.MESSAGE_STATUS_USE_ALTERNATIVE_METHOD) { + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ERROR, "Purchase was failed."); + removePendingProduct(context, sku); + } else { + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_ERROR, "Purchase is in pending."); + } + purchaseFinishedListener.onIabPurchaseFinished(result, purchase); + } else { + PaymentRequest paymentRequest = new PaymentRequest.PaymentRequestBuilder().setService(isNook ? fortumoProduct.getNookServiceId() : fortumoProduct.getServiceId(), + isNook ? fortumoProduct.getNookInAppSecret() : fortumoProduct.getInAppSecret()). + setConsumable(fortumoProduct.isConsumable()). + setProductName(fortumoProduct.getProductId()). + setDisplayString(fortumoProduct.getTitle()). + build(); + Intent localIntent = paymentRequest.toIntent(act); + act.startActivityForResult(localIntent, requestCode); + } + } + } + + @Override + public boolean handleActivityResult(int requestCode, int resultCode, Intent intent) { + if (activityRequestCode != requestCode) return false; + if (intent == null) { + Logger.d("handleActivityResult: null intent data"); + purchaseFinishedListener.onIabPurchaseFinished(new IabResult(IabHelper.IABHELPER_BAD_RESPONSE, "Null data in Fortumo IAB result"), null); + } else { + int errorCode = IabHelper.BILLING_RESPONSE_RESULT_ERROR; + String errorMsg = "Purchase error."; + Purchase purchase = null; + if (resultCode == Activity.RESULT_OK) { + PaymentResponse paymentResponse = new PaymentResponse(intent); + purchase = purchaseFromPaymentResponse(context, paymentResponse); + purchase.setDeveloperPayload(developerPayload); + if (paymentResponse.getBillingStatus() == MpUtils.MESSAGE_STATUS_BILLED) { + errorCode = IabHelper.BILLING_RESPONSE_RESULT_OK; + } else if (paymentResponse.getBillingStatus() == MpUtils.MESSAGE_STATUS_PENDING) { + Logger.d("handleActivityResult: status pending for ", paymentResponse.getProductName()); + errorCode = IabHelper.BILLING_RESPONSE_RESULT_ERROR; + errorMsg = "Purchase is pending"; + if (inappsMap.get(paymentResponse.getProductName()).isConsumable()) { + addPendingPayment(context, paymentResponse.getProductName(), String.valueOf(paymentResponse.getMessageId())); + purchase = null; + } + } + } + developerPayload = null; + final IabResult result = new IabResult(errorCode, errorMsg); + Logger.d("handleActivityResult: ", result); + purchaseFinishedListener.onIabPurchaseFinished(result, purchase); + } + return true; + } + + @Override + public Inventory queryInventory(boolean querySkuDetails, List moreItemSkus, List moreSubsSkus) throws IabException { + Inventory inventory = new Inventory(); + SharedPreferences sharedPreferences = context.getSharedPreferences(SHARED_PREFS_FORTUMO, Context.MODE_PRIVATE); + final Map preferenceMap = sharedPreferences.getAll(); + if (preferenceMap != null) { + final SharedPreferences.Editor editor = sharedPreferences.edit(); + final Set keySet = preferenceMap.keySet(); + for (String key : keySet) { + final String value = (String) preferenceMap.get(key); + if (value != null) { + final Long messageId = Long.valueOf(value); + final PaymentResponse paymentResponse = MpUtils.getPaymentResponse(context, messageId); + if (paymentResponse.getBillingStatus() == MpUtils.MESSAGE_STATUS_BILLED) { + Purchase purchase = purchaseFromPaymentResponse(context, paymentResponse); + inventory.addPurchase(purchase); + } else if (paymentResponse.getBillingStatus() == MpUtils.MESSAGE_STATUS_FAILED) { + editor.remove(key); + } + } else { + preferenceMap.remove(key); + } + } + editor.commit(); + } + final Collection inappProducts = inappsMap.values(); + for (FortumoProduct fortumoProduct : inappProducts) { + if (!fortumoProduct.isConsumable()) { + final List purchaseHistory = MpUtils.getPurchaseHistory(context, fortumoProduct.getServiceId(), fortumoProduct.getInAppSecret(), 5000); + if (purchaseHistory != null && purchaseHistory.size() > 0) { + for (Object response : purchaseHistory) { + PaymentResponse paymentResponse = (PaymentResponse) response; + if (paymentResponse.getProductName().equals(fortumoProduct.getProductId())) { + inventory.addPurchase(purchaseFromPaymentResponse(context, paymentResponse)); + if (querySkuDetails) { + String fortumoPrice = getSkuPrice(fortumoProduct); + inventory.addSkuDetails(fortumoProduct.toSkuDetails(fortumoPrice)); + } + break; + } + } + } + } + } + + if (querySkuDetails && moreItemSkus != null && moreItemSkus.size() > 0) { + for (String name : moreItemSkus) { + final FortumoProduct fortumoProduct = inappsMap.get(name); + if (fortumoProduct != null) { + String fortumoPrice = getSkuPrice(fortumoProduct); + inventory.addSkuDetails(fortumoProduct.toSkuDetails(fortumoPrice)); + } else { + throw new IabException(IabHelper.BILLING_RESPONSE_RESULT_DEVELOPER_ERROR, String.format("Data %s not found", name)); + } + } + } + return inventory; + } + + private String getSkuPrice(FortumoProduct fortumoProduct) throws IabException { + String fortumoPrice = fortumoProduct.getFortumoPrice(); + if (!TextUtils.isEmpty(fortumoPrice)) { + final String serviceId = isNook ? fortumoProduct.getNookServiceId() : fortumoProduct.getServiceId(); + final String appSecret = isNook ? fortumoProduct.getNookInAppSecret() : fortumoProduct.getInAppSecret(); + MpUtils.fetchPaymentData(context, serviceId, + appSecret); + final List fetchedPriceData = MpUtils.getFetchedPriceData(context, serviceId, appSecret); + if (fetchedPriceData != null && !fetchedPriceData.isEmpty()) { + fortumoPrice = (String) fetchedPriceData.get(0); + } + } + return fortumoPrice; + } + + @Override + public void consume(Purchase itemInfo) throws IabException { + removePendingProduct(context, itemInfo.getSku()); + } + + @Override + public boolean subscriptionsSupported() { + return false; + } + + @Override + public void dispose() { + purchaseFinishedListener = null; + } + + boolean setupBilling(boolean isNook) { + try { + inappsMap = FortumoBillingService.getFortumoInapps(context, isNook); + } catch (Exception e) { + Logger.d("billing is not supported due to ", e.getMessage()); + return false; + } + return true; + } + + private static Purchase purchaseFromPaymentResponse(Context context, PaymentResponse paymentResponse) { + Purchase purchase = new Purchase(OpenIabHelper.NAME_FORTUMO); + purchase.setSku(paymentResponse.getProductName()); + purchase.setPackageName(context.getPackageName()); //todo remove? + purchase.setOrderId(paymentResponse.getPaymentCode()); + Date date = paymentResponse.getDate(); + if (date != null) { + purchase.setPurchaseTime(date.getTime()); + } + purchase.setItemType(OpenIabHelper.ITEM_TYPE_INAPP); + return purchase; + } + + static Map getFortumoInapps(Context context, boolean isNook) throws IOException, XmlPullParserException, IabException { + final Map map = new HashMap(); + final InappsXMLParser inappsXMLParser = new InappsXMLParser(); + final Pair, List> parse = inappsXMLParser.parse(context); + final List allItems = parse.first; + final Map fortumoSkuDetailsMap = FortumoProductParser.parse(context, isNook); + int itemsNotSupportedCount = 0; + for (InappBaseProduct item : allItems) { + final String productId = item.getProductId(); + final FortumoProductParser.FortumoDetails fortumoDetails = fortumoSkuDetailsMap.get(productId); + if (fortumoDetails == null) { + throw new IabException(IabHelper.IABHELPER_ERROR_BASE, "Fortumo inapp product details were not found"); + } + final String serviceId = isNook ? fortumoDetails.getNookServiceId() : fortumoDetails.getServiceId(); + final String serviceInAppSecret = isNook ? fortumoDetails.getNookInAppSecret() : fortumoDetails.getServiceInAppSecret(); + List fetchedPriceData = MpUtils.getFetchedPriceData(context, serviceId, serviceInAppSecret); + String price = null; + if (fetchedPriceData == null || fetchedPriceData.size() == 0) { + final boolean supportedOperator = MpUtils.isSupportedOperator(context, serviceId, serviceInAppSecret); + if (supportedOperator) { + fetchedPriceData = MpUtils.getFetchedPriceData(context, serviceId, serviceInAppSecret); + } + } + if (fetchedPriceData != null && !fetchedPriceData.isEmpty()) { + price = (String) fetchedPriceData.get(0); + } + if (TextUtils.isEmpty(price)) { + price = item.getPriceDetails(); + if (TextUtils.isEmpty(price)) { + Logger.d(productId, " not available for this carrier and the price is not specified in the inapps_products.xml"); + itemsNotSupportedCount++; + continue; + + } + } + FortumoProduct fortumoProduct = new FortumoProduct(item, fortumoDetails, price); + map.put(productId, fortumoProduct); + } + + if (itemsNotSupportedCount == allItems.size()) { + throw new IabException(IabHelper.IABHELPER_ERROR_BASE, "No inventory available for this carrier/country."); + } + + return map; + } + + + static class FortumoProduct extends InappBaseProduct { + private FortumoProductParser.FortumoDetails fortumoDetails; + private String fortumoPrice; + + public FortumoProduct(InappBaseProduct otherProduct, FortumoProductParser.FortumoDetails fortumoDetails, String fortumoPrice) { + super(otherProduct); + this.fortumoDetails = fortumoDetails; + this.fortumoPrice = fortumoPrice; + } + + public String getServiceId() { + return fortumoDetails.getServiceId(); + } + + public String getInAppSecret() { + return fortumoDetails.getServiceInAppSecret(); + } + + public SkuDetails toSkuDetails(String price) { + return new SkuDetails(OpenIabHelper.ITEM_TYPE_INAPP, getProductId(), getTitle(), price, getDescription()); + } + + public String getFortumoPrice() { + return fortumoPrice; + } + + public String getNookServiceId() { + return fortumoDetails.getNookServiceId(); + } + + public String getNookInAppSecret() { + return fortumoDetails.getNookInAppSecret(); + } + + + public boolean isConsumable() { + return fortumoDetails.isConsumable(); + } + + @Override + public String toString() { + return "FortumoProduct{" + + "fortumoDetails=" + fortumoDetails + + ", fortumoPrice='" + fortumoPrice + '\'' + + '}'; + } + } + + + static void addPendingPayment(Context context, String productId, String messageId) { + final SharedPreferences fortumoSharedPrefs = getFortumoSharedPrefs(context); + final SharedPreferences.Editor editor = fortumoSharedPrefs.edit(); + editor.putString(productId, messageId); + editor.commit(); + Logger.d(productId, " was added to pending"); + } + + static String getMessageIdInPending(Context context, String productId) { + final SharedPreferences fortumoSharedPrefs = getFortumoSharedPrefs(context); + return fortumoSharedPrefs.getString(productId, null); + } + + static void removePendingProduct(Context context, String productId) { + final SharedPreferences fortumoSharedPrefs = getFortumoSharedPrefs(context); + final SharedPreferences.Editor edit = fortumoSharedPrefs.edit(); + edit.remove(productId); + edit.commit(); + Logger.d(productId, " was removed from pending"); + } + + + static SharedPreferences getFortumoSharedPrefs(Context context) { + return context.getSharedPreferences(SHARED_PREFS_FORTUMO, Context.MODE_PRIVATE); + } + + + static class FortumoProductParser { + private static final Pattern skuPattern = Pattern.compile("([a-z]|[0-9]){1}[a-z0-9._]*"); + + //TAGS + private static final String FORTUMO_PRODUCTS_TAG = "fortumo-products"; + private static final String PRODUCT_TAG = "product"; + //ATTRS + private static final String ID_ATTR = "id"; + private static final String SERVICE_ID_ATTR = "service-id"; + private static final String NOOK_SERVICE_ID_ATTR = "nook-service-id"; + private static final String SERVICE_INAPP_SECRET_ATTR = "service-inapp-secret"; + private static final String NOOK_SERVICE_INAPP_SECRET_ATTR = "nook-service-inapp-secret"; + private static final String CONSUMABLE_ATTR = "consumable"; + + private FortumoProductParser() { + + } + + static Map parse(Context context, boolean isNook) throws XmlPullParserException, IOException { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser parser = factory.newPullParser(); + parser.setInput(context.getAssets().open(FortumoStore.FORTUMO_DETAILS_FILE_NAME), null); + + HashMap map = new HashMap(); + FortumoDetails sku = null; + boolean insideFortumoProducts = false; + + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + String tagName = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + if (tagName.equals(FORTUMO_PRODUCTS_TAG)) { + insideFortumoProducts = true; + } else if (tagName.equalsIgnoreCase(PRODUCT_TAG)) { + if (!insideFortumoProducts) { + throw new IllegalStateException(String.format("%s is not inside %s", PRODUCT_TAG, FORTUMO_PRODUCTS_TAG)); + } + final String skuValue = parser.getAttributeValue(null, ID_ATTR); + if (!skuPattern.matcher(skuValue).matches()) { + throw new IllegalStateException(String.format("Wrong SKU: %s. SKU must match \"([a-z]|[0-9]){1}[a-z0-9._]*\".", skuValue)); + } + + final String serviceIdAttr = parser.getAttributeValue(null, SERVICE_ID_ATTR); + final String serviceInAppSecretAttr = parser.getAttributeValue(null, SERVICE_INAPP_SECRET_ATTR); + if (!serviceInfoIsComplete(serviceIdAttr, serviceInAppSecretAttr)) { + throw new IllegalStateException(String.format("%s: service data is NOT complete", skuValue)); + } + + final String nookServiceIdAttr = parser.getAttributeValue(null, NOOK_SERVICE_ID_ATTR); + final String nookServiceInAppSecretAttr = parser.getAttributeValue(null, NOOK_SERVICE_INAPP_SECRET_ATTR); + if (!serviceInfoIsComplete(nookServiceIdAttr, nookServiceInAppSecretAttr)) { + throw new IllegalStateException(String.format("%s: service data is NOT complete", skuValue)); + } + + if (isNook) { + if (TextUtils.isEmpty(nookServiceIdAttr) || TextUtils.isEmpty(nookServiceInAppSecretAttr)) { + throw new IllegalStateException("fortumo nook-service-id attribute and nook-service-inapp-secret values must be non-empty!"); + } + } else { + if (TextUtils.isEmpty(serviceIdAttr) || TextUtils.isEmpty(serviceInAppSecretAttr)) { + throw new IllegalStateException("fortumo service-id attribute and service-inapp-secret values must be non-empty!"); + } + } + sku = new FortumoDetails(skuValue, Boolean.parseBoolean(parser.getAttributeValue(null, CONSUMABLE_ATTR)), + serviceIdAttr, serviceInAppSecretAttr, nookServiceIdAttr, nookServiceInAppSecretAttr); + } + break; + + case XmlPullParser.END_TAG: + if (tagName.equals(PRODUCT_TAG)) { + if (sku != null) { + map.put(sku.getId(), sku); + sku = null; + } + } else if (tagName.equals(FORTUMO_PRODUCTS_TAG)) { + insideFortumoProducts = false; + } + break; + + default: + break; + } + eventType = parser.next(); + } + return map; + } + + private static boolean serviceInfoIsComplete(String serviceId, String appServiceId) { + return !(TextUtils.isEmpty(serviceId) && !TextUtils.isEmpty(appServiceId)) + || (TextUtils.isEmpty(appServiceId) && !TextUtils.isEmpty(serviceId)); + } + + static class FortumoDetails { + private String id; + private String serviceId; + private String serviceInAppSecret; + private String nookServiceId; + private String nookInAppSecret; + private boolean consumable; + + public FortumoDetails(String id, boolean consumable, String serviceId, String serviceInAppSecret, + String nookServiceId, String nookInAppSecret) { + this.id = id; + this.consumable = consumable; + this.serviceId = serviceId; + this.serviceInAppSecret = serviceInAppSecret; + this.nookServiceId = nookServiceId; + this.nookInAppSecret = nookInAppSecret; + } + + public String getId() { + return id; + } + + public boolean isConsumable() { + return consumable; + } + + public String getServiceId() { + return serviceId; + } + + public String getServiceInAppSecret() { + return serviceInAppSecret; + } + + public String getNookServiceId() { + return nookServiceId; + } + + public String getNookInAppSecret() { + return nookInAppSecret; + } + + @Override + public String toString() { + return "FortumoDetails{" + + "id='" + id + '\'' + + ", serviceId='" + serviceId + '\'' + + ", serviceInAppSecret='" + serviceInAppSecret + '\'' + + ", nookServiceId='" + nookServiceId + '\'' + + ", nookInAppSecret='" + nookInAppSecret + '\'' + + ", consumable=" + consumable + + '}'; + } + } + } + +} diff --git a/library/src/org/onepf/oms/appstore/FortumoStore.java b/library/src/org/onepf/oms/appstore/FortumoStore.java new file mode 100644 index 00000000..2bf17374 --- /dev/null +++ b/library/src/org/onepf/oms/appstore/FortumoStore.java @@ -0,0 +1,147 @@ +/* + * Copyright 2012-2014 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.oms.appstore; + +import android.content.Context; +import android.os.Build; + +import org.onepf.oms.Appstore; +import org.onepf.oms.AppstoreInAppBillingService; +import org.onepf.oms.DefaultAppstore; +import org.onepf.oms.OpenIabHelper; +import org.onepf.oms.appstore.googleUtils.IabException; +import org.onepf.oms.appstore.googleUtils.IabHelper; +import org.onepf.oms.appstore.googleUtils.IabResult; +import org.onepf.oms.appstore.googleUtils.Inventory; +import org.onepf.oms.util.Logger; + +import java.util.concurrent.CountDownLatch; + +/** + * Fortumo, an international mobile payment provider, is not actually an app store. + * This class was made to provide in-app purchasing compatibility with other, "real", stores. + * + * @author akarimova@onepf.org + * @since 23.12.13 + */ +public class FortumoStore extends DefaultAppstore { + private boolean isNookDevice; + private Boolean isBillingAvailable; + + /** + * Contains information about all in-app products + */ + public static final String IN_APP_PRODUCTS_FILE_NAME = "inapps_products.xml"; + + /** + * Contains additional information about Fortumo services + */ + public static final String FORTUMO_DETAILS_FILE_NAME = "fortumo_inapps_details.xml"; + + private Context context; + private FortumoBillingService billingService; + + public FortumoStore(Context context) { + this.context = context.getApplicationContext(); + isNookDevice = isNookDevice(); + } + + /** + * Fortumo doesn't have an app store. It can't be an installer. + * + * @return false + */ + @Override + public boolean isPackageInstaller(String packageName) { + return false; + } + + @Override + public boolean isBillingAvailable(String packageName) { + if (isBillingAvailable != null) { + return isBillingAvailable; + } + billingService = (FortumoBillingService) getInAppBillingService(); + isBillingAvailable = billingService.setupBilling(isNookDevice); + Logger.d("isBillingAvailable: ", isBillingAvailable); + return isBillingAvailable; + } + + @Override + public int getPackageVersion(String packageName) { + return Appstore.PACKAGE_VERSION_UNDEFINED; + } + + @Override + public String getAppstoreName() { + return OpenIabHelper.NAME_FORTUMO; + } + + @Override + public AppstoreInAppBillingService getInAppBillingService() { + if (billingService == null) { + billingService = new FortumoBillingService(context, isNookDevice); + } + return billingService; + } + + //todo check for different devices + private static boolean isNookDevice() { + String brand = Build.BRAND; + String manufacturer = System.getProperty("ro.nook.manufacturer"); + return ((brand != null && brand.equalsIgnoreCase("nook")) || + manufacturer != null && manufacturer.equalsIgnoreCase("nook")); + } + + //todo rename the method + public static FortumoStore initFortumoStore(Context context, final boolean checkInventory) { + final FortumoStore[] storeToReturn = {null}; + final FortumoStore fortumoStore = new FortumoStore(context); + if (fortumoStore.isBillingAvailable(context.getPackageName())) { + final CountDownLatch latch = new CountDownLatch(1); + fortumoStore.getInAppBillingService().startSetup(new IabHelper.OnIabSetupFinishedListener() { + @Override + public void onIabSetupFinished(IabResult setupResult) { + if (setupResult.isSuccess()) { + if (checkInventory) { + try { + final Inventory inventory = fortumoStore.getInAppBillingService().queryInventory(false, null, null); + if (!inventory.getAllPurchases().isEmpty()) { + storeToReturn[0] = fortumoStore; + } else { + Logger.d("Purchases not found"); + } + } catch (IabException e) { + Logger.e("Error while requesting purchases", e); + } + } else { + storeToReturn[0] = fortumoStore; + } + } + latch.countDown(); + } + }); + try { + latch.await(); + } catch (InterruptedException e) { + Logger.e("Setup was interrupted", e); + } + } + return storeToReturn[0]; + } + +} diff --git a/library/src/org/onepf/oms/appstore/fortumoUtils/InappBaseProduct.java b/library/src/org/onepf/oms/appstore/fortumoUtils/InappBaseProduct.java new file mode 100644 index 00000000..0640bb47 --- /dev/null +++ b/library/src/org/onepf/oms/appstore/fortumoUtils/InappBaseProduct.java @@ -0,0 +1,215 @@ +/* + * Copyright 2012-2014 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.oms.appstore.fortumoUtils; + +import android.text.TextUtils; + +import java.util.Currency; +import java.util.HashMap; +import java.util.Locale; + +/** + * @author akarimova@onepf.org + * @since 14.02.14 + */ +public class InappBaseProduct { + public static final String PUBLISHED = "published"; + public static final String UNPUBLISHED = "unpublished"; + //publish state + boolean published; + //product id + String productId; + //title + String baseTitle; + final HashMap localeToTitleMap = new HashMap(); + //description + String baseDescription; + final HashMap localeToDescriptionMap = new HashMap(); + //price + boolean autoFill; + float basePrice; + final HashMap localeToPrice = new HashMap(); + + public InappBaseProduct() { + } + + public InappBaseProduct(InappBaseProduct otherProduct) { + this.published = otherProduct.published; + this.productId = otherProduct.productId; + this.baseTitle = otherProduct.baseTitle; + this.baseDescription = otherProduct.baseDescription; + this.basePrice = otherProduct.basePrice; + this.localeToTitleMap.putAll(otherProduct.localeToTitleMap); + this.localeToDescriptionMap.putAll(otherProduct.localeToDescriptionMap); + this.localeToPrice.putAll(otherProduct.localeToPrice); + } + + public String getProductId() { + return productId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + + public boolean isPublished() { + return published; + } + + public void setPublished(String published) { + if (!published.equals(PUBLISHED) && !published.equals(UNPUBLISHED)) { + throw new IllegalArgumentException("Wrong \"publish-state\" attr value " + published); + } + this.published = published.equals(PUBLISHED); + } + + public String getBaseTitle() { + return baseTitle; + } + + public void setBaseTitle(String baseTitle) { + this.baseTitle = baseTitle; + } + + public String getBaseDescription() { + return baseDescription; + } + + public void setBaseDescription(String baseDescription) { + this.baseDescription = baseDescription; + } + + public void addTitleLocalization(String locale, String title) { + localeToTitleMap.put(locale, title); + } + + public String getTitleByLocale(String locale) { + String mapValue = localeToTitleMap.get(locale); + if (!TextUtils.isEmpty(mapValue)) { + return mapValue; + } else { + return baseTitle; + } + } + + public String getTitle() { + return getTitleByLocale(Locale.getDefault().toString()); + } + + public void addDescriptionLocalization(String locale, String description) { + localeToDescriptionMap.put(locale, description); + } + + public String getDescriptionByLocale(String locale) { + String mapValue = localeToDescriptionMap.get(locale); + if (!TextUtils.isEmpty(mapValue)) { + return mapValue; + } else { + return baseDescription; + } + } + + public String getDescription() { + return getDescriptionByLocale(Locale.getDefault().toString()); + } + + public void addCountryPrice(String countryCode, float price) { + localeToPrice.put(countryCode, price); + } + + public float getPriceByCountryCode(String countryCode) { + Float mapValue = localeToPrice.get(countryCode); + if (mapValue != null) { + return mapValue; + } else { + return basePrice; + } + } + + public String getPriceDetails() { + Locale defaultLocale = Locale.getDefault(); + Float mapValue = localeToPrice.get(defaultLocale.getCountry()); + float price = mapValue != null ? mapValue : basePrice; + String symbol = mapValue != null ? Currency.getInstance(defaultLocale).getSymbol() : Currency.getInstance(Locale.US).getSymbol(); + return String.format("%.2f %s", price, symbol); + } + + public float getBasePrice() { + return basePrice; + } + + public void setBasePrice(float basePrice) { + this.basePrice = basePrice; + } + + public boolean isAutoFill() { + return autoFill; + } + + public void setAutoFill(boolean autoFill) { + this.autoFill = autoFill; + } + + public void validateItem() { + //todo add own string builder with dividers + StringBuilder builder = getValidateInfo(); + if (builder.length() > 0) { + throw new IllegalStateException("in-app product is not valid: " + builder.toString()); + } + } + + protected StringBuilder getValidateInfo() { + StringBuilder builder = new StringBuilder(); + if (TextUtils.isEmpty(productId)) { + builder.append("product id is empty"); + } + if (TextUtils.isEmpty(baseTitle)) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append("base title is empty"); + } + if (TextUtils.isEmpty(baseDescription)) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append("base description is empty"); + } + if (basePrice == 0) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append("base price is not defined"); + } + return builder; + } + + @Override + public String toString() { + return "InappBaseProduct{" + + "published=" + published + + ", productId='" + productId + '\'' + + ", baseTitle='" + baseTitle + '\'' + + ", localeToTitleMap=" + localeToTitleMap + + ", baseDescription='" + baseDescription + '\'' + + ", localeToDescriptionMap=" + localeToDescriptionMap + + ", autoFill=" + autoFill + + ", basePrice=" + basePrice + + ", localeToPrice=" + localeToPrice + + '}'; + } +} diff --git a/library/src/org/onepf/oms/appstore/fortumoUtils/InappSubscriptionProduct.java b/library/src/org/onepf/oms/appstore/fortumoUtils/InappSubscriptionProduct.java new file mode 100644 index 00000000..147c6858 --- /dev/null +++ b/library/src/org/onepf/oms/appstore/fortumoUtils/InappSubscriptionProduct.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012-2014 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.oms.appstore.fortumoUtils; + + +import android.text.TextUtils; + +/** + * @author akarimova@onepf.org + * @since 17.02.14 + */ +public class InappSubscriptionProduct extends InappBaseProduct { + public static final String ONE_MONTH = "oneMonth"; + public static final String ONE_YEAR = "oneYear"; + + private String period; + + public InappSubscriptionProduct(InappBaseProduct otherProduct, String period) { + super(otherProduct); + this.period = period; + } + + + public String getPeriod() { + return period; + } + + public void setPeriod(String period) { + if (!period.equals(ONE_MONTH) && !period.equals(ONE_YEAR)) { + throw new IllegalStateException("Wrong period value!"); + } + this.period = period; + } + + @Override + public void validateItem() { + final StringBuilder validateInfo = getValidateInfo(); + if (TextUtils.isEmpty(period) || !(period.equals(ONE_MONTH) || period.equals(ONE_YEAR))) { + if (validateInfo.length() > 0) { + validateInfo.append(", "); + } + validateInfo.append("period is not valid"); + } + if (validateInfo.length() > 0) { + throw new IllegalStateException("subscription product is not valid: " + validateInfo); + } + } + + @Override + public String toString() { + return "InappSubscriptionProduct{" + + "published=" + published + + ", productId='" + productId + '\'' + + ", baseTitle='" + baseTitle + '\'' + + ", localeToTitleMap=" + localeToTitleMap + + ", baseDescription='" + baseDescription + '\'' + + ", localeToDescriptionMap=" + localeToDescriptionMap + + ", autoFill=" + autoFill + + ", basePrice=" + basePrice + + ", localeToPrice=" + localeToPrice + + ", period='" + period + '\'' + + '}'; + + } +} diff --git a/library/src/org/onepf/oms/appstore/fortumoUtils/InappsXMLParser.java b/library/src/org/onepf/oms/appstore/fortumoUtils/InappsXMLParser.java new file mode 100644 index 00000000..ed2aad0d --- /dev/null +++ b/library/src/org/onepf/oms/appstore/fortumoUtils/InappsXMLParser.java @@ -0,0 +1,278 @@ +/* + * Copyright 2012-2014 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.oms.appstore.fortumoUtils; + +import android.content.Context; +import android.util.Pair; + +import org.onepf.oms.appstore.FortumoStore; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author akarimova@onepf.org + * @since 17.02.14 + */ +public class InappsXMLParser { + private static final Pattern countryPattern = Pattern.compile("[A-Z][A-Z]"); + private static final Pattern localePattern = Pattern.compile("[a-z][a-z]_[A-Z][A-Z]"); + private static final Pattern skuPattern = Pattern.compile("([a-z]|[0-9]){1}[a-z0-9._]*"); + + + //TAGS + private static final String TAG_INAPP_PRODUCTS = "inapp-products"; + private static final String TAG_SUBSCRIPTIONS = "subscriptions"; + private static final String TAG_SUBSCRIPTION = "subscription"; + private static final String TAG_ITEMS = "items"; + private static final String TAG_ITEM = "item"; + private static final String TAG_SUMMARY = "summary"; + private static final String TAG_SUMMARY_LOCALIZATION = "summary-localization"; + private static final String TAG_SUMMARY_BASE = "summary-base"; + private static final String TAG_PRICE_BASE = "price-base"; + private static final String TAG_PRICE_LOCAL = "price-local"; + private static final String TAG_COMMON_TITLE = "title"; + private static final String TAG_COMMON_DESCRIPTION = "description"; + private static final String TAG_PRICE = "price"; + + //ATTRS + private static final String ATTR_PUBLISH_STATE = "publish-state"; + private static final String ATTR_ID = "id"; + private static final String ATTR_LOCALE = "locale"; + private static final String ATTR_COUNTRY = "country"; + private static final String ATTR_PERIOD = "period"; + private static final String ATTR_AUTOFILL = "autofill"; + + + /** + * Make sure that {@link org.onepf.oms.appstore.FortumoStore#IN_APP_PRODUCTS_FILE_NAME} is present in the assets folder. + * @param context to get access to assets + * @return a set of items and subscriptions + * @throws org.xmlpull.v1.XmlPullParserException + * @throws java.io.IOException + */ + public Pair, List> parse(Context context) throws XmlPullParserException, IOException { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser parser = factory.newPullParser(); + parser.setInput(context.getAssets().open(FortumoStore.IN_APP_PRODUCTS_FILE_NAME), null); + + List itemsList = new ArrayList(); + List subscriptionList = new ArrayList(); + + InappBaseProduct currentProduct = null; + String title = null; + String text = null; + String description = null; + String currentLocale = null; + String currentCountryCode = null; + String currentSubPeriod = null; + + boolean insideInapps = false; + boolean insideItems = false; + boolean insideItem = false; + boolean insideSubs = false; + boolean insideSub = false; + boolean insideSummary = false; + boolean insideSummaryBase = false; + boolean insideSummaryLocal = false; + boolean insidePrice = false; + + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + String tagName = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + if (tagName.equals(TAG_INAPP_PRODUCTS)) { + insideInapps = true; + } else if (tagName.equals(TAG_ITEMS)) { + if (!insideInapps) { + inWrongNode(TAG_ITEMS, TAG_INAPP_PRODUCTS); + } + insideItems = true; + } else if (tagName.equals(TAG_SUBSCRIPTIONS)) { + if (!insideInapps) { + inWrongNode(TAG_SUBSCRIPTIONS, TAG_INAPP_PRODUCTS); + } + insideSubs = true; + } else if (tagName.equals(TAG_ITEM) || tagName.equals(TAG_SUBSCRIPTION)) { + if (tagName.equals(TAG_SUBSCRIPTION)) { + if (!insideSubs) { + inWrongNode(TAG_SUBSCRIPTION, TAG_SUBSCRIPTIONS); + } + currentSubPeriod = parser.getAttributeValue(null, ATTR_PERIOD); + if (!(InappSubscriptionProduct.ONE_MONTH.equals(currentSubPeriod) || InappSubscriptionProduct.ONE_YEAR.equals(currentSubPeriod))) { + throw new IllegalStateException(String.format("Wrong \"period\" value: %s. Must be \"%s\" or \"%s\".", currentSubPeriod, InappSubscriptionProduct.ONE_MONTH, + InappSubscriptionProduct.ONE_YEAR)); + } + insideSub = true; + } else { + if (!insideItems) { + inWrongNode(TAG_ITEMS, TAG_ITEMS); + } + insideItem = true; + } + currentProduct = new InappBaseProduct(); + final String sku = parser.getAttributeValue(null, ATTR_ID); + final Matcher matcher = skuPattern.matcher(sku); + if (!matcher.matches()) { + throw new IllegalStateException(String.format("Wrong SKU ID: %s. SKU must match \"([a-z]|[0-9]){1}[a-z0-9._]*\"", sku)); + } + currentProduct.setProductId(sku); + final String publishState = parser.getAttributeValue(null, ATTR_PUBLISH_STATE); + if (!(InappBaseProduct.UNPUBLISHED.equals(publishState) || InappBaseProduct.PUBLISHED.equals(publishState))) { + throw new IllegalStateException(String.format("Wrong publish state value: %s. Must be \"%s\" or \"%s\"", publishState, InappBaseProduct.UNPUBLISHED, InappBaseProduct.PUBLISHED)); + } + currentProduct.setPublished(publishState); + } else if (tagName.equals(TAG_SUMMARY)) { + if (!(insideItem || insideSub)) { + inWrongNode(TAG_SUMMARY, TAG_ITEM, TAG_SUBSCRIPTION); + } + insideSummary = true; + } else if (tagName.equals(TAG_SUMMARY_BASE)) { + if (!insideSummary) { + inWrongNode(TAG_SUMMARY_BASE, TAG_SUMMARY); + } + insideSummaryBase = true; + } else if (tagName.equals(TAG_SUMMARY_LOCALIZATION)) { + if (!insideSummary) { + inWrongNode(TAG_SUMMARY_LOCALIZATION, TAG_SUMMARY); + } + currentLocale = parser.getAttributeValue(null, ATTR_LOCALE); + if (!localePattern.matcher(currentLocale).matches()) { + throw new IllegalStateException(String.format("Wrong \"locale\" attribute value: %s. Must match [a-z][a-z]_[A-Z][A-Z].", currentCountryCode)); + } + insideSummaryLocal = true; + } else if (tagName.equals(TAG_COMMON_TITLE)) { + if (!(insideSummaryBase || insideSummaryLocal)) { + inWrongNode(TAG_COMMON_TITLE, TAG_SUMMARY_BASE, TAG_SUMMARY_LOCALIZATION); + } + } else if (tagName.equals(TAG_COMMON_DESCRIPTION)) { + if (!(insideSummaryBase || insideSummaryLocal)) { + inWrongNode(TAG_COMMON_DESCRIPTION, TAG_SUMMARY_BASE, TAG_SUMMARY_LOCALIZATION); + } + } else if (tagName.equals(TAG_PRICE)) { + if (!(insideItem || insideSub)) { + inWrongNode(TAG_PRICE, TAG_ITEM, TAG_SUBSCRIPTION); + } + currentProduct.setAutoFill(Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_AUTOFILL))); + insidePrice = true; + } else if (tagName.equals(TAG_PRICE_BASE)) { + if (!insidePrice) { + inWrongNode(TAG_PRICE_BASE, TAG_PRICE); + } + } else if (tagName.equals(TAG_PRICE_LOCAL)) { + if (!insidePrice) { + inWrongNode(TAG_PRICE_LOCAL, TAG_PRICE); + } + currentCountryCode = parser.getAttributeValue(null, ATTR_COUNTRY); + final Matcher matcher = countryPattern.matcher(currentCountryCode); + if (!matcher.matches()) { + throw new IllegalStateException(String.format("Wrong \"country\" attribute value: %s. Must match [A-Z][A-Z].", currentCountryCode)); + } + } + break; + case XmlPullParser.TEXT: + text = parser.getText(); + break; + case XmlPullParser.END_TAG: + if (tagName.equals(TAG_INAPP_PRODUCTS)) { + insideInapps = false; + } else if (tagName.equals(TAG_ITEMS)) { + insideItems = false; + } else if (tagName.equals(TAG_SUBSCRIPTIONS)) { + insideSubs = false; + } else if (tagName.equals(TAG_ITEM)) { + currentProduct.validateItem(); + itemsList.add(currentProduct); + currentProduct = null; + } else if (tagName.equals(TAG_SUBSCRIPTION)) { + InappSubscriptionProduct subscriptionProduct = new InappSubscriptionProduct(currentProduct, currentSubPeriod); + subscriptionProduct.validateItem(); + subscriptionList.add(subscriptionProduct); + currentSubPeriod = null; + currentProduct = null; + insideSub = false; + } else if (tagName.equals(TAG_SUMMARY)) { + insideSummary = false; + } else if (tagName.equals(TAG_COMMON_TITLE)) { + int length = text.length(); + if (!(length >= 1 && length <= 55)) { + throw new IllegalStateException(String.format("Wrong title length: %d. Must be 1-55 symbols", length)); + } + title = text; + } else if (tagName.equals(TAG_COMMON_DESCRIPTION)) { + int length = text.length(); + if (!(length >= 1 && length <= 80)) { + throw new IllegalStateException(String.format("Wrong description length: %d. Must be 1-80 symbols", length)); + } + description = text; + } else if (tagName.equals(TAG_SUMMARY_BASE)) { + currentProduct.setBaseTitle(title); + currentProduct.setBaseDescription(description); + title = null; + description = null; + insideSummaryBase = false; + } else if (tagName.equals(TAG_SUMMARY_LOCALIZATION)) { + currentProduct.addTitleLocalization(currentLocale, title); + currentProduct.addDescriptionLocalization(currentLocale, description); + title = null; + description = null; + currentLocale = null; + insideSummaryLocal = false; + } else if (tagName.equals(TAG_PRICE_BASE) || tagName.equals(TAG_PRICE_LOCAL)) { + float price; + try { + price = Float.parseFloat(text); + } catch (NumberFormatException e) { + throw new IllegalStateException(String.format("Wrong price: %s. Must be decimal.", text)); + } + + if (tagName.equals(TAG_PRICE_BASE)) { + currentProduct.setBasePrice(price); + } else { + currentProduct.addCountryPrice(currentCountryCode, price); + currentCountryCode = null; + } + } else if (tagName.equals(TAG_PRICE)) { + insidePrice = false; + } + break; + default: + break; + } + eventType = parser.next(); + } + + return new Pair, List>(itemsList, subscriptionList); + } + + + private static void inWrongNode(String childTagName, String rightParentTag) { + throw new IllegalStateException(String.format("%s is not inside %s", childTagName, rightParentTag)); + } + + private static void inWrongNode(String childTagName, String rightParentTag, String otherRightParentTag) { + throw new IllegalStateException(String.format("%s is not inside %s or %s", childTagName, rightParentTag, otherRightParentTag)); + } +} From 18c8976f1b47fbdb984dd6d4a737374154b2ba1a Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Tue, 2 Sep 2014 15:23:54 +0300 Subject: [PATCH 07/72] Add robolectric test. --- build.gradle | 1 + library/build.gradle | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/build.gradle b/build.gradle index 29eec403..268ce117 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:0.12.+' + classpath 'org.robolectric:robolectric-gradle-plugin:0.12.+' } } diff --git a/library/build.gradle b/library/build.gradle index ce8244f5..56b3d810 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -15,6 +15,7 @@ */ apply plugin: 'com.android.library' +apply plugin: 'robolectric' android { compileSdkVersion project.compileSdkVersion @@ -50,7 +51,22 @@ android { } } +robolectric { + // configure the set of classes for JUnit tests + include '**/*Test.class' + exclude '**/espresso/**/*.class' + + // configure max heap size of the test JVM + maxHeapSize = '2048m' + + // configure whether failing tests should fail the build + ignoreFailures true +} + dependencies { compile fileTree(dir: 'libs', include: '*.jar') compile 'com.intellij:annotations:12.0' + + androidTestCompile 'org.robolectric:robolectric:2.3' + androidTestCompile 'junit:junit:4.11' } \ No newline at end of file From 227c008b8f88c63729e28c9f9dfbad76d023f22c Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Tue, 2 Sep 2014 22:46:23 +0400 Subject: [PATCH 08/72] Resolve synchronization issue with queryInventory() request; Work around Amazon IAB returning wrong sku value for subscriptions --- .../AmazonAppstoreBillingService.java | 57 +++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java b/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java index 099be9c4..b961abbe 100644 --- a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java +++ b/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java @@ -49,8 +49,9 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; /** @@ -103,7 +104,12 @@ public class AmazonAppstoreBillingService implements AppstoreInAppBillingService * crashes or relaunched). So inventory object must not be null. */ private final Inventory inventory = new Inventory(); - private final Map inventoryLatchMap = new ConcurrentHashMap(); + /** + * Since {@link RequestId} returned by {@link PurchasingService#getPurchaseUpdates(boolean) } doesn't match + * the one from {@link PurchasingListener#onPurchaseUpdatesResponse(PurchaseUpdatesResponse)}, we'll just + * assume separate requests are equal and use simple queue for synchronization + */ + private final Queue inventoryLatchQueue = new ConcurrentLinkedQueue(); /** * If not null will be notified from @@ -160,9 +166,9 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk Logger.d("queryInventory() querySkuDetails: ", querySkuDetails, " moreItemSkus: ", moreItemSkus, " moreSubsSkus: ", moreSubsSkus); - final RequestId purchaseUpdatesRequestId = PurchasingService.getPurchaseUpdates(true); final CountDownLatch purchaseUpdatesLatch = new CountDownLatch(1); - inventoryLatchMap.put(purchaseUpdatesRequestId, purchaseUpdatesLatch); + inventoryLatchQueue.offer(purchaseUpdatesLatch); + PurchasingService.getPurchaseUpdates(true); try { purchaseUpdatesLatch.await(); } catch (InterruptedException e) { @@ -183,9 +189,9 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk for (String sku : querySkus) { queryStoreSkus.add(SkuManager.getInstance().getStoreSku(OpenIabHelper.NAME_AMAZON, sku)); } - final RequestId productDataRequestId = PurchasingService.getProductData(queryStoreSkus); final CountDownLatch productDataLatch = new CountDownLatch(1); - inventoryLatchMap.put(productDataRequestId, productDataLatch); + inventoryLatchQueue.offer(productDataLatch); + PurchasingService.getProductData(queryStoreSkus); try { productDataLatch.await(); } catch (InterruptedException e) { @@ -221,8 +227,7 @@ public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse purchaseUpda inventory.addPurchase(getPurchase(receipt)); } if (purchaseUpdatesResponse.hasMore()) { - final RequestId purchaseUpdatesRequestId = PurchasingService.getPurchaseUpdates(false); - inventoryLatchMap.put(purchaseUpdatesRequestId, inventoryLatchMap.remove(requestId)); + PurchasingService.getPurchaseUpdates(false); Logger.v("Initiating Another Purchase Updates with offset: "); return; } @@ -230,7 +235,7 @@ public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse purchaseUpda case FAILED: break; } - final CountDownLatch countDownLatch = inventoryLatchMap.remove(requestId); + final CountDownLatch countDownLatch = inventoryLatchQueue.poll(); if (countDownLatch != null) { countDownLatch.countDown(); } @@ -279,7 +284,7 @@ public void onProductDataResponse(final ProductDataResponse productDataResponse) case NOT_SUPPORTED: break; } - final CountDownLatch countDownLatch = inventoryLatchMap.remove(requestId); + final CountDownLatch countDownLatch = inventoryLatchQueue.poll(); if (countDownLatch != null) { countDownLatch.countDown(); } @@ -301,15 +306,24 @@ private SkuDetails getSkuDetails(final Product product) { return new SkuDetails(openIabSkuType, openIabSku, title, price, description); } + /** + * As for Amazon IAP 2.0, {@link Receipt#getSku()} differs from requested one for subscription. + *
+ * This map is intended to workaround this issue. + */ + private final Map requestSkuMap = new HashMap(); + @Override public void launchPurchaseFlow( final Activity activity, final String sku, - final String itemType, + final String itemType, final int requestCode, final IabHelper.OnIabPurchaseFinishedListener listener, final String extraData) { - requestListeners.put(PurchasingService.purchase(sku), listener); + final RequestId requestId = PurchasingService.purchase(sku); + requestSkuMap.put(requestId, sku); + requestListeners.put(requestId, listener); } @Override @@ -319,10 +333,11 @@ public void onPurchaseResponse(final PurchaseResponse purchaseResponse) { Logger.v("onPurchaseResponse() PurchaseRequestStatus:", status, ", reqId: ", requestId); + final String requestSku = requestSkuMap.remove(requestId); final Purchase purchase = new Purchase(OpenIabHelper.NAME_AMAZON); final IabResult result; boolean shouldNotifyFulfillment = false; - switch (status){ + switch (status) { case SUCCESSFUL: final UserData userData = purchaseResponse.getUserData(); final String userId = userData.getUserId(); @@ -333,15 +348,25 @@ public void onPurchaseResponse(final PurchaseResponse purchaseResponse) { "Current UserId doesn't match purchase UserId"); break; } + + purchase.setOriginalJson(generateOriginalJson(purchaseResponse)); + final Receipt receipt = purchaseResponse.getReceipt(); + final ProductType productType = receipt.getProductType(); + final String storeSku = receipt.getSku(); - purchase.setOriginalJson(generateOriginalJson(purchaseResponse)); - purchase.setSku(SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, storeSku)); - final String openIabSkuType = receipt.getProductType() == ProductType.SUBSCRIPTION + final String sku = SkuManager.getInstance().getSku(OpenIabHelper.NAME_AMAZON, + productType == ProductType.SUBSCRIPTION ? requestSku :storeSku + ); + purchase.setSku(sku); + + final String openIabSkuType = productType == ProductType.SUBSCRIPTION ? IabHelper.ITEM_TYPE_SUBS : IabHelper.ITEM_TYPE_INAPP; purchase.setItemType(openIabSkuType); + result = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_OK, "Success"); + shouldNotifyFulfillment = true; break; case INVALID_SKU: From 7b225bd6e38de90cd1e4b1de59aeb0d50fa73089 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Tue, 2 Sep 2014 23:22:05 +0400 Subject: [PATCH 09/72] Fix review comments --- .../appstore/AmazonAppstoreBillingService.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java b/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java index b961abbe..33c454ce 100644 --- a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java +++ b/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java @@ -152,10 +152,9 @@ public void onUserDataResponse(final UserDataResponse userDataResponse) { Logger.d("onUserDataResponse() Unable to get user ID"); break; default: - iabResult = null; + iabResult = new IabResult(IabHelper.BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Unknown response code"); } - //noinspection ConstantConditions - if (setupListener != null && iabResult != null) { + if (setupListener != null) { setupListener.onIabSetupFinished(iabResult); setupListener = null; } @@ -208,7 +207,7 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse purchaseUpdatesResponse) { final PurchaseUpdatesResponse.RequestStatus requestStatus = purchaseUpdatesResponse.getRequestStatus(); final RequestId requestId = purchaseUpdatesResponse.getRequestId(); - Logger.v("onPurchaseUpdatesResponse() reqStatus: ", requestStatus, + Logger.d("onPurchaseUpdatesResponse() reqStatus: ", requestStatus, "reqId: ", requestId); switch (requestStatus) { @@ -228,7 +227,7 @@ public void onPurchaseUpdatesResponse(final PurchaseUpdatesResponse purchaseUpda } if (purchaseUpdatesResponse.hasMore()) { PurchasingService.getPurchaseUpdates(false); - Logger.v("Initiating Another Purchase Updates with offset: "); + Logger.d("Initiating Another Purchase Updates with offset: "); return; } break; @@ -268,7 +267,7 @@ private Purchase getPurchase(final Receipt receipt) { public void onProductDataResponse(final ProductDataResponse productDataResponse) { final ProductDataResponse.RequestStatus status = productDataResponse.getRequestStatus(); final RequestId requestId = productDataResponse.getRequestId(); - Logger.v("onItemDataResponse() reqStatus: ", status, + Logger.d("onItemDataResponse() reqStatus: ", status, ", reqId: ", requestId); switch (status) { @@ -296,7 +295,7 @@ private SkuDetails getSkuDetails(final Product product) { final String title = product.getTitle(); final String description = product.getDescription(); final ProductType productType = product.getProductType(); - Logger.v(String.format("Item: %s\n Type: %s\n SKU: %s\n Price: %s\n Description: %s\n", + Logger.d(String.format("Item: %s\n Type: %s\n SKU: %s\n Price: %s\n Description: %s\n", title, productType, sku, price, description)); final String openIabSkuType = productType == ProductType.SUBSCRIPTION @@ -330,7 +329,7 @@ public void launchPurchaseFlow( public void onPurchaseResponse(final PurchaseResponse purchaseResponse) { final PurchaseResponse.RequestStatus status = purchaseResponse.getRequestStatus(); final RequestId requestId = purchaseResponse.getRequestId(); - Logger.v("onPurchaseResponse() PurchaseRequestStatus:", status, + Logger.d("onPurchaseResponse() PurchaseRequestStatus:", status, ", reqId: ", requestId); final String requestSku = requestSkuMap.remove(requestId); @@ -450,7 +449,7 @@ public boolean subscriptionsSupported() { @Override public void dispose() { - // Nothing to do here + setupListener = null; } @Override From 3b9478016cb89660f2ffb5b9b675b8e255d42341 Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Wed, 3 Sep 2014 10:52:43 +0300 Subject: [PATCH 10/72] Change source directories structure to NBS default. --- library/build.gradle | 16 ---------------- library/{ => src/main}/AndroidManifest.xml | 0 .../vending/billing/IInAppBillingService.aidl | 0 .../nokia/payment/iap/aidl/INokiaIAPService.aidl | 0 .../aidl}/com/sec/android/iap/IAPConnector.aidl | 0 .../com/sec/android/iap/IAPServiceCallback.aidl | 0 .../aidl}/org/onepf/oms/IOpenAppstore.aidl | 0 .../org/onepf/oms/IOpenInAppBillingService.aidl | 0 .../{ => main/java}/org/onepf/oms/Appstore.java | 0 .../onepf/oms/AppstoreInAppBillingService.java | 0 .../java}/org/onepf/oms/DefaultAppstore.java | 0 .../java}/org/onepf/oms/OpenIabHelper.java | 0 .../java}/org/onepf/oms/SkuManager.java | 0 .../java}/org/onepf/oms/SkuMappingException.java | 0 .../org/onepf/oms/appstore/AmazonAppstore.java | 0 .../appstore/AmazonAppstoreBillingService.java | 0 .../java}/org/onepf/oms/appstore/GooglePlay.java | 0 .../java}/org/onepf/oms/appstore/NokiaStore.java | 0 .../org/onepf/oms/appstore/OpenAppstore.java | 0 .../org/onepf/oms/appstore/SamsungApps.java | 2 +- .../oms/appstore/SamsungAppsBillingService.java | 0 .../onepf/oms/appstore/googleUtils/Base64.java | 0 .../googleUtils/Base64DecoderException.java | 0 .../oms/appstore/googleUtils/IabException.java | 0 .../oms/appstore/googleUtils/IabHelper.java | 2 +- .../oms/appstore/googleUtils/IabResult.java | 0 .../oms/appstore/googleUtils/Inventory.java | 0 .../onepf/oms/appstore/googleUtils/Purchase.java | 0 .../onepf/oms/appstore/googleUtils/Security.java | 0 .../oms/appstore/googleUtils/SkuDetails.java | 0 .../oms/appstore/nokiaUtils/NokiaResult.java | 0 .../appstore/nokiaUtils/NokiaStoreHelper.java | 0 .../org/onepf/oms/util/CollectionUtils.java | 0 .../java}/org/onepf/oms/util/Logger.java | 0 .../java}/org/onepf/oms/util/Utils.java | 0 35 files changed, 2 insertions(+), 18 deletions(-) rename library/{ => src/main}/AndroidManifest.xml (100%) rename library/src/{ => main/aidl}/com/android/vending/billing/IInAppBillingService.aidl (100%) rename library/src/{ => main/aidl}/com/nokia/payment/iap/aidl/INokiaIAPService.aidl (100%) rename library/src/{ => main/aidl}/com/sec/android/iap/IAPConnector.aidl (100%) rename library/src/{ => main/aidl}/com/sec/android/iap/IAPServiceCallback.aidl (100%) rename library/src/{ => main/aidl}/org/onepf/oms/IOpenAppstore.aidl (100%) rename library/src/{ => main/aidl}/org/onepf/oms/IOpenInAppBillingService.aidl (100%) rename library/src/{ => main/java}/org/onepf/oms/Appstore.java (100%) rename library/src/{ => main/java}/org/onepf/oms/AppstoreInAppBillingService.java (100%) rename library/src/{ => main/java}/org/onepf/oms/DefaultAppstore.java (100%) rename library/src/{ => main/java}/org/onepf/oms/OpenIabHelper.java (100%) rename library/src/{ => main/java}/org/onepf/oms/SkuManager.java (100%) rename library/src/{ => main/java}/org/onepf/oms/SkuMappingException.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/AmazonAppstore.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/AmazonAppstoreBillingService.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/GooglePlay.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/NokiaStore.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/OpenAppstore.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/SamsungApps.java (99%) rename library/src/{ => main/java}/org/onepf/oms/appstore/SamsungAppsBillingService.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/Base64.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/Base64DecoderException.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/IabException.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/IabHelper.java (76%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/IabResult.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/Inventory.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/Purchase.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/Security.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/googleUtils/SkuDetails.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/nokiaUtils/NokiaResult.java (100%) rename library/src/{ => main/java}/org/onepf/oms/appstore/nokiaUtils/NokiaStoreHelper.java (100%) rename library/src/{ => main/java}/org/onepf/oms/util/CollectionUtils.java (100%) rename library/src/{ => main/java}/org/onepf/oms/util/Logger.java (100%) rename library/src/{ => main/java}/org/onepf/oms/util/Utils.java (100%) diff --git a/library/build.gradle b/library/build.gradle index 56b3d810..9a679b85 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -28,22 +28,6 @@ android { versionName "2.7" applicationId 'org.onepf.oms' - - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - resources.srcDirs = ['src'] - aidl.srcDirs = ['src'] - jni.srcDirs = ['jni'] - jniLibs.srcDirs = ['libs'] - renderscript.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - java.srcDirs = ['src'] - } - - androidTest.setRoot('tests') - } } lintOptions { diff --git a/library/AndroidManifest.xml b/library/src/main/AndroidManifest.xml similarity index 100% rename from library/AndroidManifest.xml rename to library/src/main/AndroidManifest.xml diff --git a/library/src/com/android/vending/billing/IInAppBillingService.aidl b/library/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl similarity index 100% rename from library/src/com/android/vending/billing/IInAppBillingService.aidl rename to library/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl diff --git a/library/src/com/nokia/payment/iap/aidl/INokiaIAPService.aidl b/library/src/main/aidl/com/nokia/payment/iap/aidl/INokiaIAPService.aidl similarity index 100% rename from library/src/com/nokia/payment/iap/aidl/INokiaIAPService.aidl rename to library/src/main/aidl/com/nokia/payment/iap/aidl/INokiaIAPService.aidl diff --git a/library/src/com/sec/android/iap/IAPConnector.aidl b/library/src/main/aidl/com/sec/android/iap/IAPConnector.aidl similarity index 100% rename from library/src/com/sec/android/iap/IAPConnector.aidl rename to library/src/main/aidl/com/sec/android/iap/IAPConnector.aidl diff --git a/library/src/com/sec/android/iap/IAPServiceCallback.aidl b/library/src/main/aidl/com/sec/android/iap/IAPServiceCallback.aidl similarity index 100% rename from library/src/com/sec/android/iap/IAPServiceCallback.aidl rename to library/src/main/aidl/com/sec/android/iap/IAPServiceCallback.aidl diff --git a/library/src/org/onepf/oms/IOpenAppstore.aidl b/library/src/main/aidl/org/onepf/oms/IOpenAppstore.aidl similarity index 100% rename from library/src/org/onepf/oms/IOpenAppstore.aidl rename to library/src/main/aidl/org/onepf/oms/IOpenAppstore.aidl diff --git a/library/src/org/onepf/oms/IOpenInAppBillingService.aidl b/library/src/main/aidl/org/onepf/oms/IOpenInAppBillingService.aidl similarity index 100% rename from library/src/org/onepf/oms/IOpenInAppBillingService.aidl rename to library/src/main/aidl/org/onepf/oms/IOpenInAppBillingService.aidl diff --git a/library/src/org/onepf/oms/Appstore.java b/library/src/main/java/org/onepf/oms/Appstore.java similarity index 100% rename from library/src/org/onepf/oms/Appstore.java rename to library/src/main/java/org/onepf/oms/Appstore.java diff --git a/library/src/org/onepf/oms/AppstoreInAppBillingService.java b/library/src/main/java/org/onepf/oms/AppstoreInAppBillingService.java similarity index 100% rename from library/src/org/onepf/oms/AppstoreInAppBillingService.java rename to library/src/main/java/org/onepf/oms/AppstoreInAppBillingService.java diff --git a/library/src/org/onepf/oms/DefaultAppstore.java b/library/src/main/java/org/onepf/oms/DefaultAppstore.java similarity index 100% rename from library/src/org/onepf/oms/DefaultAppstore.java rename to library/src/main/java/org/onepf/oms/DefaultAppstore.java diff --git a/library/src/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java similarity index 100% rename from library/src/org/onepf/oms/OpenIabHelper.java rename to library/src/main/java/org/onepf/oms/OpenIabHelper.java diff --git a/library/src/org/onepf/oms/SkuManager.java b/library/src/main/java/org/onepf/oms/SkuManager.java similarity index 100% rename from library/src/org/onepf/oms/SkuManager.java rename to library/src/main/java/org/onepf/oms/SkuManager.java diff --git a/library/src/org/onepf/oms/SkuMappingException.java b/library/src/main/java/org/onepf/oms/SkuMappingException.java similarity index 100% rename from library/src/org/onepf/oms/SkuMappingException.java rename to library/src/main/java/org/onepf/oms/SkuMappingException.java diff --git a/library/src/org/onepf/oms/appstore/AmazonAppstore.java b/library/src/main/java/org/onepf/oms/appstore/AmazonAppstore.java similarity index 100% rename from library/src/org/onepf/oms/appstore/AmazonAppstore.java rename to library/src/main/java/org/onepf/oms/appstore/AmazonAppstore.java diff --git a/library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java b/library/src/main/java/org/onepf/oms/appstore/AmazonAppstoreBillingService.java similarity index 100% rename from library/src/org/onepf/oms/appstore/AmazonAppstoreBillingService.java rename to library/src/main/java/org/onepf/oms/appstore/AmazonAppstoreBillingService.java diff --git a/library/src/org/onepf/oms/appstore/GooglePlay.java b/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java similarity index 100% rename from library/src/org/onepf/oms/appstore/GooglePlay.java rename to library/src/main/java/org/onepf/oms/appstore/GooglePlay.java diff --git a/library/src/org/onepf/oms/appstore/NokiaStore.java b/library/src/main/java/org/onepf/oms/appstore/NokiaStore.java similarity index 100% rename from library/src/org/onepf/oms/appstore/NokiaStore.java rename to library/src/main/java/org/onepf/oms/appstore/NokiaStore.java diff --git a/library/src/org/onepf/oms/appstore/OpenAppstore.java b/library/src/main/java/org/onepf/oms/appstore/OpenAppstore.java similarity index 100% rename from library/src/org/onepf/oms/appstore/OpenAppstore.java rename to library/src/main/java/org/onepf/oms/appstore/OpenAppstore.java diff --git a/library/src/org/onepf/oms/appstore/SamsungApps.java b/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java similarity index 99% rename from library/src/org/onepf/oms/appstore/SamsungApps.java rename to library/src/main/java/org/onepf/oms/appstore/SamsungApps.java index 5caa302a..70b01b68 100644 --- a/library/src/org/onepf/oms/appstore/SamsungApps.java +++ b/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java @@ -17,7 +17,7 @@ package org.onepf.oms.appstore; import android.app.Activity; -import android.util.Log; + import org.onepf.oms.Appstore; import org.onepf.oms.AppstoreInAppBillingService; import org.onepf.oms.DefaultAppstore; diff --git a/library/src/org/onepf/oms/appstore/SamsungAppsBillingService.java b/library/src/main/java/org/onepf/oms/appstore/SamsungAppsBillingService.java similarity index 100% rename from library/src/org/onepf/oms/appstore/SamsungAppsBillingService.java rename to library/src/main/java/org/onepf/oms/appstore/SamsungAppsBillingService.java diff --git a/library/src/org/onepf/oms/appstore/googleUtils/Base64.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/Base64.java similarity index 100% rename from library/src/org/onepf/oms/appstore/googleUtils/Base64.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/Base64.java diff --git a/library/src/org/onepf/oms/appstore/googleUtils/Base64DecoderException.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/Base64DecoderException.java similarity index 100% rename from library/src/org/onepf/oms/appstore/googleUtils/Base64DecoderException.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/Base64DecoderException.java diff --git a/library/src/org/onepf/oms/appstore/googleUtils/IabException.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/IabException.java similarity index 100% rename from library/src/org/onepf/oms/appstore/googleUtils/IabException.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/IabException.java diff --git a/library/src/org/onepf/oms/appstore/googleUtils/IabHelper.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/IabHelper.java similarity index 76% rename from library/src/org/onepf/oms/appstore/googleUtils/IabHelper.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/IabHelper.java index b7b39aa5..04374954 100644 --- a/library/src/org/onepf/oms/appstore/googleUtils/IabHelper.java +++ b/library/src/main/java/org/onepf/oms/appstore/googleUtils/IabHelper.java @@ -1 +1 @@ -/* * Copyright 2012-2014 One Platform Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onepf.oms.appstore.googleUtils; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; import android.content.pm.ResolveInfo; import org.json.JSONException; import org.onepf.oms.Appstore; import org.onepf.oms.AppstoreInAppBillingService; import org.onepf.oms.OpenIabHelper; import org.onepf.oms.SkuManager; import org.onepf.oms.appstore.GooglePlay; import org.onepf.oms.util.Logger; import android.app.Activity; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.text.TextUtils; import com.android.vending.billing.IInAppBillingService; /** * Provides convenience methods for in-app billing. You can create one instance of this * class for your application and use it to process in-app billing operations. * It provides synchronous (blocking) and asynchronous (non-blocking) methods for * many common in-app billing operations, as well as automatic signature * verification. *

* After instantiating, you must perform setup in order to start using the object. * To perform setup, call the {@link #startSetup} method and provide a listener; * that listener will be notified when setup is complete, after which (and not before) * you may call other methods. *

* After setup is complete, you will typically want to request an inventory of owned * items and subscriptions. See {@link #queryInventory}, {@link #queryInventoryAsync} * and related methods. *

* When you are done with this object, don't forget to call {@link #dispose} * to ensure proper cleanup. This object holds a binding to the in-app billing * service, which will leak unless you dispose of it correctly. If you created * the object on an Activity's onCreate method, then the recommended * place to dispose of it is the Activity's onDestroy method. *

* A note about threading: When using this object from a background thread, you may * call the blocking versions of methods; when using from a UI thread, call * only the asynchronous versions and handle the results via callbacks. * Also, notice that you can only call one asynchronous operation at a time; * attempting to start a second asynchronous operation while the first one * has not yet completed will result in an exception being thrown. * * @author Bruno Oliveira (Google) */ public class IabHelper implements AppstoreInAppBillingService { /** * TODO: move to Options? * Google Play doesn't support getSkuDetails for more than 20 SKUs at once */ public static final int QUERY_SKU_DETAILS_BATCH_SIZE = 20; // Is setup done? boolean mSetupDone = false; // Are subscriptions supported? boolean mSubscriptionsSupported = false; // Is an asynchronous operation in progress? // (only one at a time can be in progress) boolean mAsyncInProgress = false; // (for logging/debugging) // if mAsyncInProgress == true, what asynchronous operation is in progress? String mAsyncOperation = ""; // Context we were passed during initialization Context mContext; // Connection to the service IInAppBillingService mService; ServiceConnection mServiceConn; /** * for debug purposes */ ComponentName componentName; // The request code used to launch purchase flow int mRequestCode; // The item type of the current purchase flow String mPurchasingItemType; // Public key for verifying signature, in base64 encoding String mSignatureBase64 = null; // Billing response codes public static final int BILLING_RESPONSE_RESULT_OK = 0; public static final int BILLING_RESPONSE_RESULT_USER_CANCELED = 1; public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3; public static final int BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE = 4; public static final int BILLING_RESPONSE_RESULT_DEVELOPER_ERROR = 5; public static final int BILLING_RESPONSE_RESULT_ERROR = 6; public static final int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7; public static final int BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED = 8; // IAB Helper error codes public static final int IABHELPER_ERROR_BASE = -1000; public static final int IABHELPER_REMOTE_EXCEPTION = -1001; public static final int IABHELPER_BAD_RESPONSE = -1002; public static final int IABHELPER_VERIFICATION_FAILED = -1003; public static final int IABHELPER_SEND_INTENT_FAILED = -1004; public static final int IABHELPER_USER_CANCELLED = -1005; public static final int IABHELPER_UNKNOWN_PURCHASE_RESPONSE = -1006; public static final int IABHELPER_MISSING_TOKEN = -1007; public static final int IABHELPER_UNKNOWN_ERROR = -1008; public static final int IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE = -1009; public static final int IABHELPER_INVALID_CONSUMPTION = -1010; // Keys for the responses from InAppBillingService public static final String RESPONSE_CODE = "RESPONSE_CODE"; public static final String RESPONSE_GET_SKU_DETAILS_LIST = "DETAILS_LIST"; public static final String RESPONSE_BUY_INTENT = "BUY_INTENT"; public static final String RESPONSE_INAPP_PURCHASE_DATA = "INAPP_PURCHASE_DATA"; public static final String RESPONSE_INAPP_SIGNATURE = "INAPP_DATA_SIGNATURE"; public static final String RESPONSE_INAPP_ITEM_LIST = "INAPP_PURCHASE_ITEM_LIST"; public static final String RESPONSE_INAPP_PURCHASE_DATA_LIST = "INAPP_PURCHASE_DATA_LIST"; public static final String RESPONSE_INAPP_SIGNATURE_LIST = "INAPP_DATA_SIGNATURE_LIST"; public static final String INAPP_CONTINUATION_TOKEN = "INAPP_CONTINUATION_TOKEN"; // Item types public static final String ITEM_TYPE_INAPP = "inapp"; public static final String ITEM_TYPE_SUBS = "subs"; // some fields on the getSkuDetails response bundle public static final String GET_SKU_DETAILS_ITEM_LIST = "ITEM_ID_LIST"; public static final String GET_SKU_DETAILS_ITEM_TYPE_LIST = "ITEM_TYPE_LIST"; /** * TODO: IabHelper for Google and OpenStore must not be same */ private Appstore appstore; /** * Creates an instance. After creation, it will not yet be ready to use. You must perform * setup by calling {@link #startSetup} and wait for setup to complete. This constructor does not * block and is safe to call from a UI thread. * * @param ctx Your application or Activity context. Needed to bind to the in-app billing service. * @param base64PublicKey Your application's public key, encoded in base64. * This is used for verification of purchase signatures. You can find your app's base64-encoded * public key in your application's page on Google Play Developer Console. Note that this * is NOT your "developer public key". * @param appstore TODO */ public IabHelper(Context ctx, String base64PublicKey, Appstore appstore) { mContext = ctx.getApplicationContext(); mSignatureBase64 = base64PublicKey; this.appstore = appstore; Logger.d("IAB helper created."); } /** * Callback for setup process. This listener's {@link #onIabSetupFinished} method is called * when the setup process is complete. */ public interface OnIabSetupFinishedListener { /** * Called to notify that setup is complete. * * @param result The result of the setup process. */ public void onIabSetupFinished(IabResult result); } /** * Starts the setup process. This will start up the setup process asynchronously. * You will be notified through the listener when the setup process is complete. * This method is safe to call from a UI thread. * * @param listener The listener to notify when the setup process is complete. */ public void startSetup(final OnIabSetupFinishedListener listener) { // If already set up, can't do it again. if (mSetupDone) throw new IllegalStateException("IAB helper is already set up."); // Connection to IAB service Logger.d("Starting in-app billing setup."); mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Logger.d("Billing service disconnected."); mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Logger.d("Billing service connected."); mService = getServiceFromBinder(service); componentName = name; String packageName = mContext.getPackageName(); try { Logger.d("Checking for in-app billing 3 support."); // check for in-app billing v3 support int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP); if (response != BILLING_RESPONSE_RESULT_OK) { if (listener != null) listener.onIabSetupFinished(new IabResult(response, "Error checking for billing v3 support.")); // if in-app purchases aren't supported, neither are subscriptions. mSubscriptionsSupported = false; return; } Logger.d("In-app billing version 3 supported for ", packageName); // check for v3 subscriptions support response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS); if (response == BILLING_RESPONSE_RESULT_OK) { Logger.d("Subscriptions AVAILABLE."); mSubscriptionsSupported = true; } else { Logger.d("Subscriptions NOT AVAILABLE. Response: ", response); } mSetupDone = true; } catch (RemoteException e) { if (listener != null) { listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION, "RemoteException while setting up in-app billing.")); } Logger.e("RemoteException while setting up in-app billing", e); return; } if (listener != null) { listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful.")); } } }; Intent serviceIntent = getServiceIntent(); final List infoList = mContext.getPackageManager().queryIntentServices(serviceIntent, 0); if (infoList != null && !infoList.isEmpty()) { // service available to handle that Intent mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); } else { // no service available to handle that Intent if (listener != null) { listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing service unavailable on device.")); } } } /** * IabHelper code is shared between OpenStore and Google Play, but services has different names */ protected Intent getServiceIntent() { final Intent intent = new Intent(GooglePlay.VENDING_ACTION); intent.setPackage(GooglePlay.ANDROID_INSTALLER); return intent; } /** * Override to return needed service interface */ protected IInAppBillingService getServiceFromBinder(IBinder service) { return IInAppBillingService.Stub.asInterface(service); } /** * Dispose of object, releasing resources. It's very important to call this * method when you are done with this object. It will release any resources * used by it such as service connections. Naturally, once the object is * disposed of, it can't be used again. */ public void dispose() { Logger.d("Disposing."); mSetupDone = false; if (mServiceConn != null) { Logger.d("Unbinding from service."); if (mContext != null) mContext.unbindService(mServiceConn); mServiceConn = null; mService = null; mPurchaseListener = null; } } /** * Returns whether subscriptions are supported. */ public boolean subscriptionsSupported() { return mSubscriptionsSupported; } public void setSubscriptionsSupported(boolean subscriptionsSupported) { mSubscriptionsSupported = subscriptionsSupported; } public void setSetupDone(boolean setupDone) { mSetupDone = setupDone; } /** * Callback that notifies when a purchase is finished. */ public interface OnIabPurchaseFinishedListener { /** * Called to notify that an in-app purchase finished. If the purchase was successful, * then the sku parameter specifies which item was purchased. If the purchase failed, * the sku and extraData parameters may or may not be null, depending on how far the purchase * process went. * * @param result The result of the purchase. * @param info The purchase information (null if purchase failed) */ public void onIabPurchaseFinished(IabResult result, Purchase info); } // The listener registered on launchPurchaseFlow, which we have to call back when // the purchase finishes OnIabPurchaseFinishedListener mPurchaseListener; public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener) { launchPurchaseFlow(act, sku, requestCode, listener, ""); } public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener, String extraData) { launchPurchaseFlow(act, sku, ITEM_TYPE_INAPP, requestCode, listener, extraData); } public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener) { launchSubscriptionPurchaseFlow(act, sku, requestCode, listener, ""); } public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener, String extraData) { launchPurchaseFlow(act, sku, ITEM_TYPE_SUBS, requestCode, listener, extraData); } /** * Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase, * which will involve bringing up the Google Play screen. The calling activity will be paused while * the user interacts with Google Play, and the result will be delivered via the activity's * {@link android.app.Activity#onActivityResult} method, at which point you must call * this object's {@link #handleActivityResult} method to continue the purchase flow. This method * MUST be called from the UI thread of the Activity. * * @param act The calling activity. * @param sku The sku of the item to purchase. * @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or ITEM_TYPE_SUBS) * @param requestCode A request code (to differentiate from other responses -- * as in {@link android.app.Activity#startActivityForResult}). * @param listener The listener to notify when the purchase process finishes * @param extraData Extra data (developer payload), which will be returned with the purchase data * when the purchase completes. This extra data will be permanently bound to that purchase * and will always be returned when the purchase is queried. */ public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode, OnIabPurchaseFinishedListener listener, String extraData) { checkSetupDone("launchPurchaseFlow"); flagStartAsync("launchPurchaseFlow"); IabResult result; if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) { IabResult r = new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE, "Subscriptions are not available."); if (listener != null) listener.onIabPurchaseFinished(r, null); flagEndAsync(); return; } try { Logger.d("Constructing buy intent for ", sku, ", item type: ", itemType); if (mService == null) { Logger.e("In-app billing error: Unable to buy item, Error response: service is not connected."); result = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Unable to buy item"); if (listener != null) listener.onIabPurchaseFinished(result, null); flagEndAsync(); return; } Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, itemType, extraData); int response = getResponseCodeFromBundle(buyIntentBundle); if (response != BILLING_RESPONSE_RESULT_OK) { Logger.e("In-app billing error: Unable to buy item, Error response: " + getResponseDesc(response)); result = new IabResult(response, "Unable to buy item"); if (listener != null) listener.onIabPurchaseFinished(result, null); flagEndAsync(); return; } PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT); Logger.d("Launching buy intent for ", sku, ". Request code: ", requestCode); mRequestCode = requestCode; mPurchaseListener = listener; mPurchasingItemType = itemType; act.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); } catch (SendIntentException e) { Logger.e("In-app billing error: SendIntentException while launching purchase flow for sku " + sku); e.printStackTrace(); result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent."); if (listener != null) listener.onIabPurchaseFinished(result, null); } catch (RemoteException e) { Logger.e("In-app billing error: RemoteException while launching purchase flow for sku " + sku); e.printStackTrace(); result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow"); if (listener != null) listener.onIabPurchaseFinished(result, null); } flagEndAsync(); } /** * Handles an activity result that's part of the purchase flow in in-app billing. If you * are calling {@link #launchPurchaseFlow}, then you must call this method from your * Activity's {@link android.app.Activity#onActivityResult} method. This method * MUST be called from the UI thread of the Activity. * * @param requestCode The requestCode as you received it. * @param resultCode The resultCode as you received it. * @param data The data (Intent) as you received it. * @return Returns true if the result was related to a purchase flow and was handled; * false if the result was not related to a purchase, in which case you should * handle it normally. */ public boolean handleActivityResult(int requestCode, int resultCode, Intent data) { IabResult result; if (requestCode != mRequestCode) return false; checkSetupDone("handleActivityResult"); // end of async purchase operation flagEndAsync(); if (data == null) { Logger.e("In-app billing error: Null data in IAB activity result."); result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result"); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return true; } int responseCode = getResponseCodeFromIntent(data); String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA); String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE); if (resultCode == Activity.RESULT_OK && responseCode == BILLING_RESPONSE_RESULT_OK) { processPurchaseSuccess(data, purchaseData, dataSignature); } else if (resultCode == Activity.RESULT_OK) { // result code was OK, but in-app billing response was not OK. processPurchaseFail(responseCode); } else if (resultCode == Activity.RESULT_CANCELED) { Logger.d("Purchase canceled - Response: ", getResponseDesc(responseCode)); result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); } else { Logger.e("In-app billing error: Purchase failed. Result code: " + Integer.toString(resultCode) + ". Response: " + getResponseDesc(responseCode)); result = new IabResult(IABHELPER_UNKNOWN_PURCHASE_RESPONSE, "Unknown purchase response."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); } return true; } public void processPurchaseFail(int responseCode) { IabResult result; Logger.d("Result code was OK but in-app billing response was not OK: ", getResponseDesc(responseCode)); if (mPurchaseListener != null) { result = new IabResult(responseCode, "Problem purchashing item."); mPurchaseListener.onIabPurchaseFinished(result, null); } } public void processPurchaseSuccess(Intent data, String purchaseData, String dataSignature) { IabResult result; Logger.d("Successful resultcode from purchase activity."); Logger.d("Purchase data: ", purchaseData); Logger.d("Data signature: ", dataSignature); Logger.d("Extras: ", data.getExtras()); Logger.d("Expected item type: ", mPurchasingItemType); if (purchaseData == null || dataSignature == null) { Logger.e("In-app billing error: BUG: either purchaseData or dataSignature is null."); Logger.d("Extras: ", data.getExtras()); result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature"); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return; } Purchase purchase; try { purchase = new Purchase(mPurchasingItemType, purchaseData, dataSignature, appstore.getAppstoreName()); String sku = purchase.getSku(); purchase.setSku(SkuManager.getInstance().getSku(appstore.getAppstoreName(), sku)); if (!isValidDataSignature(mSignatureBase64, purchaseData, dataSignature)) { Logger.e("In-app billing error: Purchase signature verification FAILED for sku " + sku); result = new IabResult(IABHELPER_VERIFICATION_FAILED, "Signature verification failed for sku " + sku); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, purchase); return; } Logger.d("Purchase signature successfully verified."); } catch (JSONException e) { Logger.e("In-app billing error: Failed to parse purchase data."); e.printStackTrace(); result = new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return; } if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Success"), purchase); } } public Inventory queryInventory(boolean querySkuDetails, List moreSkus) throws IabException { return queryInventory(querySkuDetails, moreSkus, null); } /** * Queries the inventory. This will query all owned items from the server, as well as * information on additional skus, if specified. This method may block or take long to execute. * Do not call from a UI thread. For that, use the non-blocking version {@link #queryInventoryAsync}. * * @param querySkuDetails if true, SKU details (price, description, etc) will be queried as well * as purchase information. * @param moreItemSkus additional PRODUCT skus to query information on, regardless of ownership. * Ignored if null or if querySkuDetails is false. * @param moreSubsSkus additional SUBSCRIPTIONS skus to query information on, regardless of ownership. * Ignored if null or if querySkuDetails is false. * @throws IabException if a problem occurs while refreshing the inventory. */ public Inventory queryInventory(boolean querySkuDetails, List moreItemSkus, List moreSubsSkus) throws IabException { checkSetupDone("queryInventory"); try { Inventory inv = new Inventory(); int r = queryPurchases(inv, ITEM_TYPE_INAPP); if (r != BILLING_RESPONSE_RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying owned items)."); } if (querySkuDetails) { r = querySkuDetails(ITEM_TYPE_INAPP, inv, moreItemSkus); if (r != BILLING_RESPONSE_RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying prices of items)."); } } // if subscriptions are supported, then also query for subscriptions if (mSubscriptionsSupported) { r = queryPurchases(inv, ITEM_TYPE_SUBS); if (r != BILLING_RESPONSE_RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying owned subscriptions)."); } if (querySkuDetails) { r = querySkuDetails(ITEM_TYPE_SUBS, inv, moreSubsSkus); if (r != BILLING_RESPONSE_RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying prices of subscriptions)."); } } } return inv; } catch (RemoteException e) { throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while refreshing inventory.", e); } catch (JSONException e) { throw new IabException(IABHELPER_BAD_RESPONSE, "Error parsing JSON response while refreshing inventory.", e); } } /** * Listener that notifies when an inventory query operation completes. */ public interface QueryInventoryFinishedListener { /** * Called to notify that an inventory query operation completed. * * @param result The result of the operation. * @param inv The inventory. */ public void onQueryInventoryFinished(IabResult result, Inventory inv); } /** * Asynchronous wrapper for inventory query. This will perform an inventory * query as described in {@link #queryInventory}, but will do so asynchronously * and call back the specified listener upon completion. This method is safe to * call from a UI thread. * * @param querySkuDetails as in {@link #queryInventory} * @param moreSkus as in {@link #queryInventory} * @param listener The listener to notify when the refresh operation completes. */ public void queryInventoryAsync(final boolean querySkuDetails, final List moreSkus, final QueryInventoryFinishedListener listener) { final Handler handler = new Handler(); checkSetupDone("queryInventory"); flagStartAsync("refresh inventory"); (new Thread(new Runnable() { public void run() { IabResult result = new IabResult(BILLING_RESPONSE_RESULT_OK, "Inventory refresh successful."); Inventory inv = null; try { inv = queryInventory(querySkuDetails, moreSkus); } catch (IabException ex) { result = ex.getResult(); } flagEndAsync(); final IabResult result_f = result; final Inventory inv_f = inv; handler.post(new Runnable() { public void run() { listener.onQueryInventoryFinished(result_f, inv_f); } }); } })).start(); } public void queryInventoryAsync(QueryInventoryFinishedListener listener) { queryInventoryAsync(true, null, listener); } public void queryInventoryAsync(boolean querySkuDetails, QueryInventoryFinishedListener listener) { queryInventoryAsync(querySkuDetails, null, listener); } /** * Consumes a given in-app product. Consuming can only be done on an item * that's owned, and as a reslibs res src unity_src wp8_dll_srcult of consumption, the user will no longer own it. * This method may block or take long to return. Do not call from the UI thread. * For that, see {@link #consumeAsync}. * * @param itemInfo The PurchaseInfo that represents the item to consume. * @throws IabException if there is a problem during consumption. */ public void consume(Purchase itemInfo) throws IabException { checkSetupDone("consume"); if (!itemInfo.mItemType.equals(ITEM_TYPE_INAPP)) { throw new IabException(IABHELPER_INVALID_CONSUMPTION, "Items of type '" + itemInfo.mItemType + "' can't be consumed."); } try { String token = itemInfo.getToken(); String sku = itemInfo.getSku(); if (token == null || token.equals("")) { Logger.e("In-app billing error: Can't consume ", sku, ". No token."); throw new IabException(IABHELPER_MISSING_TOKEN, "PurchaseInfo is missing token for sku: " + sku + " " + itemInfo); } Logger.d("Consuming sku: ", sku, ", token: ", token); if (mService == null) { Logger.d("Error consuming consuming sku ", sku, ". Service is not connected."); throw new IabException(BILLING_RESPONSE_RESULT_ERROR, "Error consuming sku " + sku); } int response = mService.consumePurchase(3, getPackageName(), token); if (response == BILLING_RESPONSE_RESULT_OK) { Logger.d("Successfully consumed sku: ", sku); } else { Logger.d("Error consuming consuming sku ", sku, ". ", getResponseDesc(response)); throw new IabException(response, "Error consuming sku " + sku); } } catch (RemoteException e) { throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while consuming. PurchaseInfo: " + itemInfo, e); } } public String getPackageName() { return mContext.getPackageName(); } /** * Callback that notifies when a consumption operation finishes. */ public interface OnConsumeFinishedListener { /** * Called to notify that a consumption has finished. * * @param purchase The purchase that was (or was to be) consumed. * @param result The result of the consumption operation. */ public void onConsumeFinished(Purchase purchase, IabResult result); } /** * Callback that notifies when a multi-item consumption operation finishes. */ public interface OnConsumeMultiFinishedListener { /** * Called to notify that a consumption of multiple items has finished. * * @param purchases The purchases that were (or were to be) consumed. * @param results The results of each consumption operation, corresponding to each * sku. */ public void onConsumeMultiFinished(List purchases, List results); } /** * Asynchronous wrapper to item consumption. Works like {@link #consume}, but * performs the consumption in the background and notifies completion through * the provided listener. This method is safe to call from a UI thread. * * @param purchase The purchase to be consumed. * @param listener The listener to notify when the consumption operation finishes. */ public void consumeAsync(Purchase purchase, OnConsumeFinishedListener listener) { checkSetupDone("consume"); List purchases = new ArrayList(); purchases.add(purchase); consumeAsyncInternal(purchases, listener, null); } /** * Same as {@link org.onepf.oms.appstore.googleUtils.IabHelper#consumeAsync(Purchase, org.onepf.oms.appstore.googleUtils.IabHelper.OnConsumeFinishedListener)}, * but for multiple items at once. * * @param purchases The list of PurchaseInfo objects representing the purchases to consume. * @param listener The listener to notify when the consumption operation finishes. */ public void consumeAsync(List purchases, OnConsumeMultiFinishedListener listener) { checkSetupDone("consume"); consumeAsyncInternal(purchases, null, listener); } /** * Returns a human-readable description for the given response code. * * @param code The response code * @return A human-readable string explaining the result code. * It also includes the result code numerically. */ public static String getResponseDesc(int code) { String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" + "3:Billing Unavailable/4:Item unavailable/" + "5:Developer Error/6:Error/7:Item Already Owned/" + "8:Item not owned").split("/"); String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" + "-1002:Bad response received/" + "-1003:Purchase signature verification failed/" + "-1004:Send intent failed/" + "-1005:User cancelled/" + "-1006:Unknown purchase response/" + "-1007:Missing token/" + "-1008:Unknown error/" + "-1009:Subscriptions not available/" + "-1010:Invalid consumption attempt").split("/"); if (code <= IABHELPER_ERROR_BASE) { int index = IABHELPER_ERROR_BASE - code; if (index >= 0 && index < iabhelper_msgs.length) return iabhelper_msgs[index]; else return String.valueOf(code) + ":Unknown IAB Helper Error"; } else if (code < 0 || code >= iab_msgs.length) { return String.valueOf(code) + ":Unknown"; } else { return iab_msgs[code]; } } /** * Checks that setup was done; if not, throws an exception. *

*

OpenIAB specific: NOT USED

*

* setupDone state is tracked by {@link OpenIabHelper}, so check here duplicates * already existed logic. At the same time we discovered race condition problem based on end-user * crash reports *

Time to time when common onSetupSuccessfulListener calls queryInventory() IabHelper.setupDone is false * We tried to solve it with volatile modifier and synchronized blocks. Both approaches failed. * Reasons are still unclear. The same flow in wrapper works perfect (OpenIabHelper) *

     *  java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: queryInventory
     * at org.onepf.oms.appstore.googleUtils.IabHelper.checkSetupDone(IabHelper.java:806)
     * at org.onepf.oms.appstore.googleUtils.IabHelper.queryInventory(IabHelper.java:566)
     * at org.onepf.oms.OpenIabHelper.queryInventory(OpenIabHelper.java:930)
     * at org.onepf.oms.OpenIabHelper$5.run(OpenIabHelper.java:957)
     * at java.lang.Thread.run(Thread.java:864)
     * 

* * @see
https://github.com/onepf/OpenIAB/issues/199 */ void checkSetupDone(String operation) { } // Workaround to bug where sometimes response codes come as Long instead of Integer int getResponseCodeFromBundle(Bundle b) { Object o = b.get(RESPONSE_CODE); if (o == null) { Logger.d("Bundle with null response code, assuming OK (known issue)"); return BILLING_RESPONSE_RESULT_OK; } else if (o instanceof Integer) return ((Integer) o).intValue(); else if (o instanceof Long) return (int) ((Long) o).longValue(); else { Logger.e("In-app billing error: ", "Unexpected type for bundle response code."); Logger.e("In-app billing error: ", o.getClass().getName()); throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName()); } } // Workaround to bug where sometimes response codes come as Long instead of Integer int getResponseCodeFromIntent(Intent i) { Object o = i.getExtras().get(RESPONSE_CODE); if (o == null) { Logger.e("In-app billing error: Intent with no response code, assuming OK (known issue)"); return BILLING_RESPONSE_RESULT_OK; } else if (o instanceof Integer) return ((Integer) o).intValue(); else if (o instanceof Long) return (int) ((Long) o).longValue(); else { Logger.e("In-app billing error: Unexpected type for intent response code."); Logger.e("In-app billing error: ", o.getClass().getName()); throw new RuntimeException("Unexpected type for intent response code: " + o.getClass().getName()); } } void flagStartAsync(String operation) { if (mAsyncInProgress) throw new IllegalStateException("Can't start async operation (" + operation + ") because another async operation(" + mAsyncOperation + ") is in progress."); mAsyncOperation = operation; mAsyncInProgress = true; Logger.d("Starting async operation: ", operation); } void flagEndAsync() { Logger.d("Ending async operation: ", mAsyncOperation); mAsyncOperation = ""; mAsyncInProgress = false; } int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException { // Query purchases Logger.d("Querying owned items, item type: ", itemType); Logger.d("Package name: ", getPackageName()); boolean verificationFailed = false; String continueToken = null; do { Logger.d("Calling getPurchases with continuation token: ", continueToken); if (mService == null) { Logger.d("getPurchases() failed: service is not connected."); return BILLING_RESPONSE_RESULT_ERROR; } Bundle ownedItems = mService.getPurchases(3, getPackageName(), itemType, continueToken); int response = getResponseCodeFromBundle(ownedItems); Logger.d("Owned items response: ", response); if (response != BILLING_RESPONSE_RESULT_OK) { Logger.d("getPurchases() failed: ", getResponseDesc(response)); return response; } if (!ownedItems.containsKey(RESPONSE_INAPP_ITEM_LIST) || !ownedItems.containsKey(RESPONSE_INAPP_PURCHASE_DATA_LIST) || !ownedItems.containsKey(RESPONSE_INAPP_SIGNATURE_LIST)) { Logger.e("In-app billing error: Bundle returned from getPurchases() doesn't contain required fields."); return IABHELPER_BAD_RESPONSE; } ArrayList ownedSkus = ownedItems.getStringArrayList(RESPONSE_INAPP_ITEM_LIST); ArrayList purchaseDataList = ownedItems.getStringArrayList(RESPONSE_INAPP_PURCHASE_DATA_LIST); ArrayList signatureList = ownedItems.getStringArrayList(RESPONSE_INAPP_SIGNATURE_LIST); for (int i = 0; i < purchaseDataList.size(); ++i) { String purchaseData = purchaseDataList.get(i); String signature = signatureList.get(i); String sku = ownedSkus.get(i); if (isValidDataSignature(mSignatureBase64, purchaseData, signature)) { Logger.d("Sku is owned: ", sku); Purchase purchase = new Purchase(itemType, purchaseData, signature, appstore.getAppstoreName()); String storeSku = purchase.getSku(); purchase.setSku(SkuManager.getInstance().getSku(appstore.getAppstoreName(), storeSku)); if (TextUtils.isEmpty(purchase.getToken())) { Logger.w("In-app billing warning: BUG: empty/null token!"); Logger.d("Purchase data: ", purchaseData); } // Record ownership and token inv.addPurchase(purchase); } else { Logger.w("In-app billing warning: Purchase signature verification **FAILED**. Not adding item."); Logger.d(" Purchase data: ", purchaseData); Logger.d(" Signature: ", signature); verificationFailed = true; } } continueToken = ownedItems.getString(INAPP_CONTINUATION_TOKEN); Logger.d("Continuation token: ", continueToken); } while (!TextUtils.isEmpty(continueToken)); return verificationFailed ? IABHELPER_VERIFICATION_FAILED : BILLING_RESPONSE_RESULT_OK; } /** * @param inv - Inventory with application SKUs * @param moreSkus - storeSKUs (processed in {@link OpenIabHelper#queryInventory(boolean, List, List)} */ int querySkuDetails(String itemType, Inventory inv, List moreSkus) throws RemoteException, JSONException { Logger.d("querySkuDetails() Querying SKU details."); final SkuManager skuManager = SkuManager.getInstance(); final String appstoreName = appstore.getAppstoreName(); Set storeSkus = new TreeSet(); for (String sku : inv.getAllOwnedSkus(itemType)) { storeSkus.add(skuManager.getStoreSku(appstoreName, sku)); } if (moreSkus != null) { for (String sku : moreSkus) { storeSkus.add(skuManager.getStoreSku(appstoreName, sku)); } } if (storeSkus.isEmpty()) { Logger.d("querySkuDetails(): nothing to do because there are no SKUs."); return BILLING_RESPONSE_RESULT_OK; } // Split the sku list in blocks of no more than QUERY_SKU_DETAILS_BATCH_SIZE elements. ArrayList> batches = new ArrayList>(); ArrayList tmpBatch = new ArrayList(QUERY_SKU_DETAILS_BATCH_SIZE); int iSku = 0; for (String sku : storeSkus) { tmpBatch.add(sku); iSku++; if (tmpBatch.size() == QUERY_SKU_DETAILS_BATCH_SIZE || iSku == storeSkus.size()) { batches.add(tmpBatch); tmpBatch = new ArrayList(QUERY_SKU_DETAILS_BATCH_SIZE); } } Logger.d("querySkuDetails() batches: ", batches.size(), ", ", batches); for (ArrayList batch : batches) { Bundle querySkus = new Bundle(); querySkus.putStringArrayList(GET_SKU_DETAILS_ITEM_LIST, batch); if (mService == null) { Logger.e("In-app billing error: unable to get sku details: service is not connected."); return IABHELPER_BAD_RESPONSE; } Bundle skuDetails = mService.getSkuDetails(3, mContext.getPackageName(), itemType, querySkus); if (!skuDetails.containsKey(RESPONSE_GET_SKU_DETAILS_LIST)) { int response = getResponseCodeFromBundle(skuDetails); if (response != BILLING_RESPONSE_RESULT_OK) { Logger.d("getSkuDetails() failed: ", getResponseDesc(response)); return response; } else { Logger.e("In-app billing error: getSkuDetails() returned a bundle with neither an error nor a detail list."); return IABHELPER_BAD_RESPONSE; } } ArrayList responseList = skuDetails.getStringArrayList(RESPONSE_GET_SKU_DETAILS_LIST); for (String thisResponse : responseList) { SkuDetails d = new SkuDetails(itemType, thisResponse); d.setSku(SkuManager.getInstance().getSku(appstoreName, d.getSku())); Logger.d("querySkuDetails() Got sku details: ", d); inv.addSkuDetails(d); } } return BILLING_RESPONSE_RESULT_OK; } void consumeAsyncInternal(final List purchases, final OnConsumeFinishedListener singleListener, final OnConsumeMultiFinishedListener multiListener) { final Handler handler = new Handler(); flagStartAsync("consume"); (new Thread(new Runnable() { public void run() { final List results = new ArrayList(); for (Purchase purchase : purchases) { try { consume(purchase); results.add(new IabResult(BILLING_RESPONSE_RESULT_OK, "Successful consume of sku " + purchase.getSku())); } catch (IabException ex) { results.add(ex.getResult()); } } flagEndAsync(); if (singleListener != null) { handler.post(new Runnable() { public void run() { singleListener.onConsumeFinished(purchases.get(0), results.get(0)); } }); } if (multiListener != null) { handler.post(new Runnable() { public void run() { multiListener.onConsumeMultiFinished(purchases, results); } }); } } })).start(); } boolean isValidDataSignature(String base64PublicKey, String purchaseData, String signature) { if (base64PublicKey == null) return true; boolean isValid = Security.verifyPurchase(base64PublicKey, purchaseData, signature); if (!isValid) { Logger.w("In-app billing warning: Purchase signature verification **FAILED**."); } return isValid; } } \ No newline at end of file +/* * Copyright 2012-2014 One Platform Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onepf.oms.appstore.googleUtils; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; import android.content.pm.ResolveInfo; import org.json.JSONException; import org.onepf.oms.Appstore; import org.onepf.oms.AppstoreInAppBillingService; import org.onepf.oms.OpenIabHelper; import org.onepf.oms.SkuManager; import org.onepf.oms.appstore.GooglePlay; import org.onepf.oms.util.Logger; import android.app.Activity; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.text.TextUtils; import com.android.vending.billing.IInAppBillingService; /** * Provides convenience methods for in-app billing. You can create one instance of this * class for your application and use it to process in-app billing operations. * It provides synchronous (blocking) and asynchronous (non-blocking) methods for * many common in-app billing operations, as well as automatic signature * verification. *

* After instantiating, you must perform setup in order to start using the object. * To perform setup, call the {@link #startSetup} method and provide a listener; * that listener will be notified when setup is complete, after which (and not before) * you may call other methods. *

* After setup is complete, you will typically want to request an inventory of owned * items and subscriptions. See {@link #queryInventory}, {@link #queryInventoryAsync} * and related methods. *

* When you are done with this object, don't forget to call {@link #dispose} * to ensure proper cleanup. This object holds a binding to the in-app billing * service, which will leak unless you dispose of it correctly. If you created * the object on an Activity's onCreate method, then the recommended * place to dispose of it is the Activity's onDestroy method. *

* A note about threading: When using this object from a background thread, you may * call the blocking versions of methods; when using from a UI thread, call * only the asynchronous versions and handle the results via callbacks. * Also, notice that you can only call one asynchronous operation at a time; * attempting to start a second asynchronous operation while the first one * has not yet completed will result in an exception being thrown. * * @author Bruno Oliveira (Google) */ public class IabHelper implements AppstoreInAppBillingService { /** * TODO: move to Options? * Google Play doesn't support getSkuDetails for more than 20 SKUs at once */ public static final int QUERY_SKU_DETAILS_BATCH_SIZE = 20; // Is setup done? boolean mSetupDone = false; // Are subscriptions supported? boolean mSubscriptionsSupported = false; // Is an asynchronous operation in progress? // (only one at a time can be in progress) boolean mAsyncInProgress = false; // (for logging/debugging) // if mAsyncInProgress == true, what asynchronous operation is in progress? String mAsyncOperation = ""; // Context we were passed during initialization Context mContext; // Connection to the service IInAppBillingService mService; ServiceConnection mServiceConn; /** * for debug purposes */ ComponentName componentName; // The request code used to launch purchase flow int mRequestCode; // The item type of the current purchase flow String mPurchasingItemType; // Public key for verifying signature, in base64 encoding String mSignatureBase64 = null; // Billing response codes public static final int BILLING_RESPONSE_RESULT_OK = 0; public static final int BILLING_RESPONSE_RESULT_USER_CANCELED = 1; public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3; public static final int BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE = 4; public static final int BILLING_RESPONSE_RESULT_DEVELOPER_ERROR = 5; public static final int BILLING_RESPONSE_RESULT_ERROR = 6; public static final int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7; public static final int BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED = 8; // IAB Helper error codes public static final int IABHELPER_ERROR_BASE = -1000; public static final int IABHELPER_REMOTE_EXCEPTION = -1001; public static final int IABHELPER_BAD_RESPONSE = -1002; public static final int IABHELPER_VERIFICATION_FAILED = -1003; public static final int IABHELPER_SEND_INTENT_FAILED = -1004; public static final int IABHELPER_USER_CANCELLED = -1005; public static final int IABHELPER_UNKNOWN_PURCHASE_RESPONSE = -1006; public static final int IABHELPER_MISSING_TOKEN = -1007; public static final int IABHELPER_UNKNOWN_ERROR = -1008; public static final int IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE = -1009; public static final int IABHELPER_INVALID_CONSUMPTION = -1010; // Keys for the responses from InAppBillingService public static final String RESPONSE_CODE = "RESPONSE_CODE"; public static final String RESPONSE_GET_SKU_DETAILS_LIST = "DETAILS_LIST"; public static final String RESPONSE_BUY_INTENT = "BUY_INTENT"; public static final String RESPONSE_INAPP_PURCHASE_DATA = "INAPP_PURCHASE_DATA"; public static final String RESPONSE_INAPP_SIGNATURE = "INAPP_DATA_SIGNATURE"; public static final String RESPONSE_INAPP_ITEM_LIST = "INAPP_PURCHASE_ITEM_LIST"; public static final String RESPONSE_INAPP_PURCHASE_DATA_LIST = "INAPP_PURCHASE_DATA_LIST"; public static final String RESPONSE_INAPP_SIGNATURE_LIST = "INAPP_DATA_SIGNATURE_LIST"; public static final String INAPP_CONTINUATION_TOKEN = "INAPP_CONTINUATION_TOKEN"; // Item types public static final String ITEM_TYPE_INAPP = "inapp"; public static final String ITEM_TYPE_SUBS = "subs"; // some fields on the getSkuDetails response bundle public static final String GET_SKU_DETAILS_ITEM_LIST = "ITEM_ID_LIST"; public static final String GET_SKU_DETAILS_ITEM_TYPE_LIST = "ITEM_TYPE_LIST"; /** * TODO: IabHelper for Google and OpenStore must not be same */ private Appstore appstore; /** * Creates an instance. After creation, it will not yet be ready to use. You must perform * setup by calling {@link #startSetup} and wait for setup to complete. This constructor does not * block and is safe to call from a UI thread. * * @param ctx Your application or Activity context. Needed to bind to the in-app billing service. * @param base64PublicKey Your application's public key, encoded in base64. * This is used for verification of purchase signatures. You can find your app's base64-encoded * public key in your application's page on Google Play Developer Console. Note that this * is NOT your "developer public key". * @param appstore TODO */ public IabHelper(Context ctx, String base64PublicKey, Appstore appstore) { mContext = ctx.getApplicationContext(); mSignatureBase64 = base64PublicKey; this.appstore = appstore; Logger.d("IAB helper created."); } /** * Callback for setup process. This listener's {@link #onIabSetupFinished} method is called * when the setup process is complete. */ public interface OnIabSetupFinishedListener { /** * Called to notify that setup is complete. * * @param result The result of the setup process. */ public void onIabSetupFinished(IabResult result); } /** * Starts the setup process. This will start up the setup process asynchronously. * You will be notified through the listener when the setup process is complete. * This method is safe to call from a UI thread. * * @param listener The listener to notify when the setup process is complete. */ public void startSetup(final OnIabSetupFinishedListener listener) { // If already set up, can't do it again. if (mSetupDone) throw new IllegalStateException("IAB helper is already set up."); // Connection to IAB service Logger.d("Starting in-app billing setup."); mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Logger.d("Billing service disconnected."); mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Logger.d("Billing service connected."); mService = getServiceFromBinder(service); componentName = name; String packageName = mContext.getPackageName(); try { Logger.d("Checking for in-app billing 3 support."); // check for in-app billing v3 support int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP); if (response != BILLING_RESPONSE_RESULT_OK) { if (listener != null) listener.onIabSetupFinished(new IabResult(response, "Error checking for billing v3 support.")); // if in-app purchases aren't supported, neither are subscriptions. mSubscriptionsSupported = false; return; } Logger.d("In-app billing version 3 supported for ", packageName); // check for v3 subscriptions support response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS); if (response == BILLING_RESPONSE_RESULT_OK) { Logger.d("Subscriptions AVAILABLE."); mSubscriptionsSupported = true; } else { Logger.d("Subscriptions NOT AVAILABLE. Response: ", response); } mSetupDone = true; } catch (RemoteException e) { if (listener != null) { listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION, "RemoteException while setting up in-app billing.")); } Logger.e("RemoteException while setting up in-app billing", e); return; } if (listener != null) { listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful.")); } } }; Intent serviceIntent = getServiceIntent(); final List infoList = mContext.getPackageManager().queryIntentServices(serviceIntent, 0); if (infoList != null && !infoList.isEmpty()) { // service available to handle that Intent mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); } else { // no service available to handle that Intent if (listener != null) { listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing service unavailable on device.")); } } } /** * IabHelper code is shared between OpenStore and Google Play, but services has different names */ protected Intent getServiceIntent() { final Intent intent = new Intent(GooglePlay.VENDING_ACTION); intent.setPackage(GooglePlay.ANDROID_INSTALLER); return intent; } /** * Override to return needed service interface */ protected IInAppBillingService getServiceFromBinder(IBinder service) { return IInAppBillingService.Stub.asInterface(service); } /** * Dispose of object, releasing resources. It's very important to call this * method when you are done with this object. It will release any resources * used by it such as service connections. Naturally, once the object is * disposed of, it can't be used again. */ public void dispose() { Logger.d("Disposing."); mSetupDone = false; if (mServiceConn != null) { Logger.d("Unbinding from service."); if (mContext != null) mContext.unbindService(mServiceConn); mServiceConn = null; mService = null; mPurchaseListener = null; } } /** * Returns whether subscriptions are supported. */ public boolean subscriptionsSupported() { return mSubscriptionsSupported; } public void setSubscriptionsSupported(boolean subscriptionsSupported) { mSubscriptionsSupported = subscriptionsSupported; } public void setSetupDone(boolean setupDone) { mSetupDone = setupDone; } /** * Callback that notifies when a purchase is finished. */ public interface OnIabPurchaseFinishedListener { /** * Called to notify that an in-app purchase finished. If the purchase was successful, * then the sku parameter specifies which item was purchased. If the purchase failed, * the sku and extraData parameters may or may not be null, depending on how far the purchase * process went. * * @param result The result of the purchase. * @param info The purchase information (null if purchase failed) */ public void onIabPurchaseFinished(IabResult result, Purchase info); } // The listener registered on launchPurchaseFlow, which we have to call back when // the purchase finishes OnIabPurchaseFinishedListener mPurchaseListener; public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener) { launchPurchaseFlow(act, sku, requestCode, listener, ""); } public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener, String extraData) { launchPurchaseFlow(act, sku, ITEM_TYPE_INAPP, requestCode, listener, extraData); } public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener) { launchSubscriptionPurchaseFlow(act, sku, requestCode, listener, ""); } public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener, String extraData) { launchPurchaseFlow(act, sku, ITEM_TYPE_SUBS, requestCode, listener, extraData); } /** * Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase, * which will involve bringing up the Google Play screen. The calling activity will be paused while * the user interacts with Google Play, and the result will be delivered via the activity's * {@link android.app.Activity#onActivityResult} method, at which point you must call * this object's {@link #handleActivityResult} method to continue the purchase flow. This method * MUST be called from the UI thread of the Activity. * * @param act The calling activity. * @param sku The sku of the item to purchase. * @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or ITEM_TYPE_SUBS) * @param requestCode A request code (to differentiate from other responses -- * as in {@link android.app.Activity#startActivityForResult}). * @param listener The listener to notify when the purchase process finishes * @param extraData Extra data (developer payload), which will be returned with the purchase data * when the purchase completes. This extra data will be permanently bound to that purchase * and will always be returned when the purchase is queried. */ public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode, OnIabPurchaseFinishedListener listener, String extraData) { checkSetupDone("launchPurchaseFlow"); flagStartAsync("launchPurchaseFlow"); IabResult result; if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) { IabResult r = new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE, "Subscriptions are not available."); if (listener != null) listener.onIabPurchaseFinished(r, null); flagEndAsync(); return; } try { Logger.d("Constructing buy intent for ", sku, ", item type: ", itemType); if (mService == null) { Logger.e("In-app billing error: Unable to buy item, Error response: service is not connected."); result = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Unable to buy item"); if (listener != null) listener.onIabPurchaseFinished(result, null); flagEndAsync(); return; } Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, itemType, extraData); int response = getResponseCodeFromBundle(buyIntentBundle); if (response != BILLING_RESPONSE_RESULT_OK) { Logger.e("In-app billing error: Unable to buy item, Error response: " + getResponseDesc(response)); result = new IabResult(response, "Unable to buy item"); if (listener != null) listener.onIabPurchaseFinished(result, null); flagEndAsync(); return; } PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT); Logger.d("Launching buy intent for ", sku, ". Request code: ", requestCode); mRequestCode = requestCode; mPurchaseListener = listener; mPurchasingItemType = itemType; act.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); } catch (SendIntentException e) { Logger.e("In-app billing error: SendIntentException while launching purchase flow for sku " + sku); e.printStackTrace(); result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent."); if (listener != null) listener.onIabPurchaseFinished(result, null); } catch (RemoteException e) { Logger.e("In-app billing error: RemoteException while launching purchase flow for sku " + sku); e.printStackTrace(); result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow"); if (listener != null) listener.onIabPurchaseFinished(result, null); } flagEndAsync(); } /** * Handles an activity result that's part of the purchase flow in in-app billing. If you * are calling {@link #launchPurchaseFlow}, then you must call this method from your * Activity's {@link android.app.Activity#onActivityResult} method. This method * MUST be called from the UI thread of the Activity. * * @param requestCode The requestCode as you received it. * @param resultCode The resultCode as you received it. * @param data The data (Intent) as you received it. * @return Returns true if the result was related to a purchase flow and was handled; * false if the result was not related to a purchase, in which case you should * handle it normally. */ public boolean handleActivityResult(int requestCode, int resultCode, Intent data) { IabResult result; if (requestCode != mRequestCode) return false; checkSetupDone("handleActivityResult"); // end of async purchase operation flagEndAsync(); if (data == null) { Logger.e("In-app billing error: Null data in IAB activity result."); result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result"); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return true; } int responseCode = getResponseCodeFromIntent(data); String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA); String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE); if (resultCode == Activity.RESULT_OK && responseCode == BILLING_RESPONSE_RESULT_OK) { processPurchaseSuccess(data, purchaseData, dataSignature); } else if (resultCode == Activity.RESULT_OK) { // result code was OK, but in-app billing response was not OK. processPurchaseFail(responseCode); } else if (resultCode == Activity.RESULT_CANCELED) { Logger.d("Purchase canceled - Response: ", getResponseDesc(responseCode)); result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); } else { Logger.e("In-app billing error: Purchase failed. Result code: " + Integer.toString(resultCode) + ". Response: " + getResponseDesc(responseCode)); result = new IabResult(IABHELPER_UNKNOWN_PURCHASE_RESPONSE, "Unknown purchase response."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); } return true; } public void processPurchaseFail(int responseCode) { IabResult result; Logger.d("Result code was OK but in-app billing response was not OK: ", getResponseDesc(responseCode)); if (mPurchaseListener != null) { result = new IabResult(responseCode, "Problem purchashing item."); mPurchaseListener.onIabPurchaseFinished(result, null); } } public void processPurchaseSuccess(Intent data, String purchaseData, String dataSignature) { IabResult result; Logger.d("Successful resultcode from purchase activity."); Logger.d("Purchase data: ", purchaseData); Logger.d("Data signature: ", dataSignature); Logger.d("Extras: ", data.getExtras()); Logger.d("Expected item type: ", mPurchasingItemType); if (purchaseData == null || dataSignature == null) { Logger.e("In-app billing error: BUG: either purchaseData or dataSignature is null."); Logger.d("Extras: ", data.getExtras()); result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature"); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return; } Purchase purchase; try { purchase = new Purchase(mPurchasingItemType, purchaseData, dataSignature, appstore.getAppstoreName()); String sku = purchase.getSku(); purchase.setSku(SkuManager.getInstance().getSku(appstore.getAppstoreName(), sku)); if (!isValidDataSignature(mSignatureBase64, purchaseData, dataSignature)) { Logger.e("In-app billing error: Purchase signature verification FAILED for sku " + sku); result = new IabResult(IABHELPER_VERIFICATION_FAILED, "Signature verification failed for sku " + sku); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, purchase); return; } Logger.d("Purchase signature successfully verified."); } catch (JSONException e) { Logger.e("In-app billing error: Failed to parse purchase data."); e.printStackTrace(); result = new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return; } if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Success"), purchase); } } public Inventory queryInventory(boolean querySkuDetails, List moreSkus) throws IabException { return queryInventory(querySkuDetails, moreSkus, null); } /** * Queries the inventory. This will query all owned items from the server, as well as * information on additional skus, if specified. This method may block or take long to execute. * Do not call from a UI thread. For that, use the non-blocking version {@link #queryInventoryAsync}. * * @param querySkuDetails if true, SKU details (price, description, etc) will be queried as well * as purchase information. * @param moreItemSkus additional PRODUCT skus to query information on, regardless of ownership. * Ignored if null or if querySkuDetails is false. * @param moreSubsSkus additional SUBSCRIPTIONS skus to query information on, regardless of ownership. * Ignored if null or if querySkuDetails is false. * @throws IabException if a problem occurs while refreshing the inventory. */ public Inventory queryInventory(boolean querySkuDetails, List moreItemSkus, List moreSubsSkus) throws IabException { checkSetupDone("queryInventory"); try { Inventory inv = new Inventory(); int r = queryPurchases(inv, ITEM_TYPE_INAPP); if (r != BILLING_RESPONSE_RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying owned items)."); } if (querySkuDetails) { r = querySkuDetails(ITEM_TYPE_INAPP, inv, moreItemSkus); if (r != BILLING_RESPONSE_RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying prices of items)."); } } // if subscriptions are supported, then also query for subscriptions if (mSubscriptionsSupported) { r = queryPurchases(inv, ITEM_TYPE_SUBS); if (r != BILLING_RESPONSE_RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying owned subscriptions)."); } if (querySkuDetails) { r = querySkuDetails(ITEM_TYPE_SUBS, inv, moreSubsSkus); if (r != BILLING_RESPONSE_RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying prices of subscriptions)."); } } } return inv; } catch (RemoteException e) { throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while refreshing inventory.", e); } catch (JSONException e) { throw new IabException(IABHELPER_BAD_RESPONSE, "Error parsing JSON response while refreshing inventory.", e); } } /** * Listener that notifies when an inventory query operation completes. */ public interface QueryInventoryFinishedListener { /** * Called to notify that an inventory query operation completed. * * @param result The result of the operation. * @param inv The inventory. */ public void onQueryInventoryFinished(IabResult result, Inventory inv); } /** * Asynchronous wrapper for inventory query. This will perform an inventory * query as described in {@link #queryInventory}, but will do so asynchronously * and call back the specified listener upon completion. This method is safe to * call from a UI thread. * * @param querySkuDetails as in {@link #queryInventory} * @param moreSkus as in {@link #queryInventory} * @param listener The listener to notify when the refresh operation completes. */ public void queryInventoryAsync(final boolean querySkuDetails, final List moreSkus, final QueryInventoryFinishedListener listener) { final Handler handler = new Handler(); checkSetupDone("queryInventory"); flagStartAsync("refresh inventory"); (new Thread(new Runnable() { public void run() { IabResult result = new IabResult(BILLING_RESPONSE_RESULT_OK, "Inventory refresh successful."); Inventory inv = null; try { inv = queryInventory(querySkuDetails, moreSkus); } catch (IabException ex) { result = ex.getResult(); } flagEndAsync(); final IabResult result_f = result; final Inventory inv_f = inv; handler.post(new Runnable() { public void run() { listener.onQueryInventoryFinished(result_f, inv_f); } }); } })).start(); } public void queryInventoryAsync(QueryInventoryFinishedListener listener) { queryInventoryAsync(true, null, listener); } public void queryInventoryAsync(boolean querySkuDetails, QueryInventoryFinishedListener listener) { queryInventoryAsync(querySkuDetails, null, listener); } /** * Consumes a given in-app product. Consuming can only be done on an item * that's owned, and as a reslibs res src unity_src wp8_dll_srcult of consumption, the user will no longer own it. * This method may block or take long to return. Do not call from the UI thread. * For that, see {@link #consumeAsync}. * * @param itemInfo The PurchaseInfo that represents the item to consume. * @throws IabException if there is a problem during consumption. */ public void consume(Purchase itemInfo) throws IabException { checkSetupDone("consume"); if (!itemInfo.mItemType.equals(ITEM_TYPE_INAPP)) { throw new IabException(IABHELPER_INVALID_CONSUMPTION, "Items of type '" + itemInfo.mItemType + "' can't be consumed."); } try { String token = itemInfo.getToken(); String sku = itemInfo.getSku(); if (token == null || token.equals("")) { Logger.e("In-app billing error: Can't consume ", sku, ". No token."); throw new IabException(IABHELPER_MISSING_TOKEN, "PurchaseInfo is missing token for sku: " + sku + " " + itemInfo); } Logger.d("Consuming sku: ", sku, ", token: ", token); if (mService == null) { Logger.d("Error consuming consuming sku ", sku, ". Service is not connected."); throw new IabException(BILLING_RESPONSE_RESULT_ERROR, "Error consuming sku " + sku); } int response = mService.consumePurchase(3, getPackageName(), token); if (response == BILLING_RESPONSE_RESULT_OK) { Logger.d("Successfully consumed sku: ", sku); } else { Logger.d("Error consuming consuming sku ", sku, ". ", getResponseDesc(response)); throw new IabException(response, "Error consuming sku " + sku); } } catch (RemoteException e) { throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while consuming. PurchaseInfo: " + itemInfo, e); } } public String getPackageName() { return mContext.getPackageName(); } /** * Callback that notifies when a consumption operation finishes. */ public interface OnConsumeFinishedListener { /** * Called to notify that a consumption has finished. * * @param purchase The purchase that was (or was to be) consumed. * @param result The result of the consumption operation. */ public void onConsumeFinished(Purchase purchase, IabResult result); } /** * Callback that notifies when a multi-item consumption operation finishes. */ public interface OnConsumeMultiFinishedListener { /** * Called to notify that a consumption of multiple items has finished. * * @param purchases The purchases that were (or were to be) consumed. * @param results The results of each consumption operation, corresponding to each * sku. */ public void onConsumeMultiFinished(List purchases, List results); } /** * Asynchronous wrapper to item consumption. Works like {@link #consume}, but * performs the consumption in the background and notifies completion through * the provided listener. This method is safe to call from a UI thread. * * @param purchase The purchase to be consumed. * @param listener The listener to notify when the consumption operation finishes. */ public void consumeAsync(Purchase purchase, OnConsumeFinishedListener listener) { checkSetupDone("consume"); List purchases = new ArrayList(); purchases.add(purchase); consumeAsyncInternal(purchases, listener, null); } /** * Same as {@link org.onepf.oms.appstore.googleUtils.IabHelper#consumeAsync(Purchase, org.onepf.oms.appstore.googleUtils.IabHelper.OnConsumeFinishedListener)}, * but for multiple items at once. * * @param purchases The list of PurchaseInfo objects representing the purchases to consume. * @param listener The listener to notify when the consumption operation finishes. */ public void consumeAsync(List purchases, OnConsumeMultiFinishedListener listener) { checkSetupDone("consume"); consumeAsyncInternal(purchases, null, listener); } /** * Returns a human-readable description for the given response code. * * @param code The response code * @return A human-readable string explaining the result code. * It also includes the result code numerically. */ public static String getResponseDesc(int code) { String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" + "3:Billing Unavailable/4:Item unavailable/" + "5:Developer Error/6:Error/7:Item Already Owned/" + "8:Item not owned").split("/"); String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" + "-1002:Bad response received/" + "-1003:Purchase signature verification failed/" + "-1004:Send intent failed/" + "-1005:User cancelled/" + "-1006:Unknown purchase response/" + "-1007:Missing token/" + "-1008:Unknown error/" + "-1009:Subscriptions not available/" + "-1010:Invalid consumption attempt").split("/"); if (code <= IABHELPER_ERROR_BASE) { int index = IABHELPER_ERROR_BASE - code; if (index >= 0 && index < iabhelper_msgs.length) return iabhelper_msgs[index]; else return String.valueOf(code) + ":Unknown IAB Helper Error"; } else if (code < 0 || code >= iab_msgs.length) { return String.valueOf(code) + ":Unknown"; } else { return iab_msgs[code]; } } /** * Checks that setup was done; if not, throws an exception. *

*

OpenIAB specific: NOT USED

*

* setupDone state is tracked by {@link OpenIabHelper}, so check here duplicates * already existed logic. At the same time we discovered race condition problem based on end-user * crash reports *

Time to time when common onSetupSuccessfulListener calls queryInventory() IabHelper.setupDone is false * We tried to solve it with volatile modifier and synchronized blocks. Both approaches failed. * Reasons are still unclear. The same flow in wrapper works perfect (OpenIabHelper) *

     *  java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: queryInventory
     * at IabHelper.checkSetupDone(IabHelper.java:806)
     * at IabHelper.queryInventory(IabHelper.java:566)
     * at OpenIabHelper.queryInventory(OpenIabHelper.java:930)
     * at OpenIabHelper$5.run(OpenIabHelper.java:957)
     * at java.lang.Thread.run(Thread.java:864)
     * 

* * @see https://github.com/onepf/OpenIAB/issues/199 */ void checkSetupDone(String operation) { } // Workaround to bug where sometimes response codes come as Long instead of Integer int getResponseCodeFromBundle(Bundle b) { Object o = b.get(RESPONSE_CODE); if (o == null) { Logger.d("Bundle with null response code, assuming OK (known issue)"); return BILLING_RESPONSE_RESULT_OK; } else if (o instanceof Integer) return ((Integer) o).intValue(); else if (o instanceof Long) return (int) ((Long) o).longValue(); else { Logger.e("In-app billing error: ", "Unexpected type for bundle response code."); Logger.e("In-app billing error: ", o.getClass().getName()); throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName()); } } // Workaround to bug where sometimes response codes come as Long instead of Integer int getResponseCodeFromIntent(Intent i) { Object o = i.getExtras().get(RESPONSE_CODE); if (o == null) { Logger.e("In-app billing error: Intent with no response code, assuming OK (known issue)"); return BILLING_RESPONSE_RESULT_OK; } else if (o instanceof Integer) return ((Integer) o).intValue(); else if (o instanceof Long) return (int) ((Long) o).longValue(); else { Logger.e("In-app billing error: Unexpected type for intent response code."); Logger.e("In-app billing error: ", o.getClass().getName()); throw new RuntimeException("Unexpected type for intent response code: " + o.getClass().getName()); } } void flagStartAsync(String operation) { if (mAsyncInProgress) throw new IllegalStateException("Can't start async operation (" + operation + ") because another async operation(" + mAsyncOperation + ") is in progress."); mAsyncOperation = operation; mAsyncInProgress = true; Logger.d("Starting async operation: ", operation); } void flagEndAsync() { Logger.d("Ending async operation: ", mAsyncOperation); mAsyncOperation = ""; mAsyncInProgress = false; } int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException { // Query purchases Logger.d("Querying owned items, item type: ", itemType); Logger.d("Package name: ", getPackageName()); boolean verificationFailed = false; String continueToken = null; do { Logger.d("Calling getPurchases with continuation token: ", continueToken); if (mService == null) { Logger.d("getPurchases() failed: service is not connected."); return BILLING_RESPONSE_RESULT_ERROR; } Bundle ownedItems = mService.getPurchases(3, getPackageName(), itemType, continueToken); int response = getResponseCodeFromBundle(ownedItems); Logger.d("Owned items response: ", response); if (response != BILLING_RESPONSE_RESULT_OK) { Logger.d("getPurchases() failed: ", getResponseDesc(response)); return response; } if (!ownedItems.containsKey(RESPONSE_INAPP_ITEM_LIST) || !ownedItems.containsKey(RESPONSE_INAPP_PURCHASE_DATA_LIST) || !ownedItems.containsKey(RESPONSE_INAPP_SIGNATURE_LIST)) { Logger.e("In-app billing error: Bundle returned from getPurchases() doesn't contain required fields."); return IABHELPER_BAD_RESPONSE; } ArrayList ownedSkus = ownedItems.getStringArrayList(RESPONSE_INAPP_ITEM_LIST); ArrayList purchaseDataList = ownedItems.getStringArrayList(RESPONSE_INAPP_PURCHASE_DATA_LIST); ArrayList signatureList = ownedItems.getStringArrayList(RESPONSE_INAPP_SIGNATURE_LIST); for (int i = 0; i < purchaseDataList.size(); ++i) { String purchaseData = purchaseDataList.get(i); String signature = signatureList.get(i); String sku = ownedSkus.get(i); if (isValidDataSignature(mSignatureBase64, purchaseData, signature)) { Logger.d("Sku is owned: ", sku); Purchase purchase = new Purchase(itemType, purchaseData, signature, appstore.getAppstoreName()); String storeSku = purchase.getSku(); purchase.setSku(SkuManager.getInstance().getSku(appstore.getAppstoreName(), storeSku)); if (TextUtils.isEmpty(purchase.getToken())) { Logger.w("In-app billing warning: BUG: empty/null token!"); Logger.d("Purchase data: ", purchaseData); } // Record ownership and token inv.addPurchase(purchase); } else { Logger.w("In-app billing warning: Purchase signature verification **FAILED**. Not adding item."); Logger.d(" Purchase data: ", purchaseData); Logger.d(" Signature: ", signature); verificationFailed = true; } } continueToken = ownedItems.getString(INAPP_CONTINUATION_TOKEN); Logger.d("Continuation token: ", continueToken); } while (!TextUtils.isEmpty(continueToken)); return verificationFailed ? IABHELPER_VERIFICATION_FAILED : BILLING_RESPONSE_RESULT_OK; } /** * @param inv - Inventory with application SKUs * @param moreSkus - storeSKUs (processed in {@link OpenIabHelper#queryInventory(boolean, List, List)} */ int querySkuDetails(String itemType, Inventory inv, List moreSkus) throws RemoteException, JSONException { Logger.d("querySkuDetails() Querying SKU details."); final SkuManager skuManager = SkuManager.getInstance(); final String appstoreName = appstore.getAppstoreName(); Set storeSkus = new TreeSet(); for (String sku : inv.getAllOwnedSkus(itemType)) { storeSkus.add(skuManager.getStoreSku(appstoreName, sku)); } if (moreSkus != null) { for (String sku : moreSkus) { storeSkus.add(skuManager.getStoreSku(appstoreName, sku)); } } if (storeSkus.isEmpty()) { Logger.d("querySkuDetails(): nothing to do because there are no SKUs."); return BILLING_RESPONSE_RESULT_OK; } // Split the sku list in blocks of no more than QUERY_SKU_DETAILS_BATCH_SIZE elements. ArrayList> batches = new ArrayList>(); ArrayList tmpBatch = new ArrayList(QUERY_SKU_DETAILS_BATCH_SIZE); int iSku = 0; for (String sku : storeSkus) { tmpBatch.add(sku); iSku++; if (tmpBatch.size() == QUERY_SKU_DETAILS_BATCH_SIZE || iSku == storeSkus.size()) { batches.add(tmpBatch); tmpBatch = new ArrayList(QUERY_SKU_DETAILS_BATCH_SIZE); } } Logger.d("querySkuDetails() batches: ", batches.size(), ", ", batches); for (ArrayList batch : batches) { Bundle querySkus = new Bundle(); querySkus.putStringArrayList(GET_SKU_DETAILS_ITEM_LIST, batch); if (mService == null) { Logger.e("In-app billing error: unable to get sku details: service is not connected."); return IABHELPER_BAD_RESPONSE; } Bundle skuDetails = mService.getSkuDetails(3, mContext.getPackageName(), itemType, querySkus); if (!skuDetails.containsKey(RESPONSE_GET_SKU_DETAILS_LIST)) { int response = getResponseCodeFromBundle(skuDetails); if (response != BILLING_RESPONSE_RESULT_OK) { Logger.d("getSkuDetails() failed: ", getResponseDesc(response)); return response; } else { Logger.e("In-app billing error: getSkuDetails() returned a bundle with neither an error nor a detail list."); return IABHELPER_BAD_RESPONSE; } } ArrayList responseList = skuDetails.getStringArrayList(RESPONSE_GET_SKU_DETAILS_LIST); for (String thisResponse : responseList) { SkuDetails d = new SkuDetails(itemType, thisResponse); d.setSku(SkuManager.getInstance().getSku(appstoreName, d.getSku())); Logger.d("querySkuDetails() Got sku details: ", d); inv.addSkuDetails(d); } } return BILLING_RESPONSE_RESULT_OK; } void consumeAsyncInternal(final List purchases, final OnConsumeFinishedListener singleListener, final OnConsumeMultiFinishedListener multiListener) { final Handler handler = new Handler(); flagStartAsync("consume"); (new Thread(new Runnable() { public void run() { final List results = new ArrayList(); for (Purchase purchase : purchases) { try { consume(purchase); results.add(new IabResult(BILLING_RESPONSE_RESULT_OK, "Successful consume of sku " + purchase.getSku())); } catch (IabException ex) { results.add(ex.getResult()); } } flagEndAsync(); if (singleListener != null) { handler.post(new Runnable() { public void run() { singleListener.onConsumeFinished(purchases.get(0), results.get(0)); } }); } if (multiListener != null) { handler.post(new Runnable() { public void run() { multiListener.onConsumeMultiFinished(purchases, results); } }); } } })).start(); } boolean isValidDataSignature(String base64PublicKey, String purchaseData, String signature) { if (base64PublicKey == null) return true; boolean isValid = Security.verifyPurchase(base64PublicKey, purchaseData, signature); if (!isValid) { Logger.w("In-app billing warning: Purchase signature verification **FAILED**."); } return isValid; } } \ No newline at end of file diff --git a/library/src/org/onepf/oms/appstore/googleUtils/IabResult.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/IabResult.java similarity index 100% rename from library/src/org/onepf/oms/appstore/googleUtils/IabResult.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/IabResult.java diff --git a/library/src/org/onepf/oms/appstore/googleUtils/Inventory.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/Inventory.java similarity index 100% rename from library/src/org/onepf/oms/appstore/googleUtils/Inventory.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/Inventory.java diff --git a/library/src/org/onepf/oms/appstore/googleUtils/Purchase.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/Purchase.java similarity index 100% rename from library/src/org/onepf/oms/appstore/googleUtils/Purchase.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/Purchase.java diff --git a/library/src/org/onepf/oms/appstore/googleUtils/Security.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/Security.java similarity index 100% rename from library/src/org/onepf/oms/appstore/googleUtils/Security.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/Security.java diff --git a/library/src/org/onepf/oms/appstore/googleUtils/SkuDetails.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/SkuDetails.java similarity index 100% rename from library/src/org/onepf/oms/appstore/googleUtils/SkuDetails.java rename to library/src/main/java/org/onepf/oms/appstore/googleUtils/SkuDetails.java diff --git a/library/src/org/onepf/oms/appstore/nokiaUtils/NokiaResult.java b/library/src/main/java/org/onepf/oms/appstore/nokiaUtils/NokiaResult.java similarity index 100% rename from library/src/org/onepf/oms/appstore/nokiaUtils/NokiaResult.java rename to library/src/main/java/org/onepf/oms/appstore/nokiaUtils/NokiaResult.java diff --git a/library/src/org/onepf/oms/appstore/nokiaUtils/NokiaStoreHelper.java b/library/src/main/java/org/onepf/oms/appstore/nokiaUtils/NokiaStoreHelper.java similarity index 100% rename from library/src/org/onepf/oms/appstore/nokiaUtils/NokiaStoreHelper.java rename to library/src/main/java/org/onepf/oms/appstore/nokiaUtils/NokiaStoreHelper.java diff --git a/library/src/org/onepf/oms/util/CollectionUtils.java b/library/src/main/java/org/onepf/oms/util/CollectionUtils.java similarity index 100% rename from library/src/org/onepf/oms/util/CollectionUtils.java rename to library/src/main/java/org/onepf/oms/util/CollectionUtils.java diff --git a/library/src/org/onepf/oms/util/Logger.java b/library/src/main/java/org/onepf/oms/util/Logger.java similarity index 100% rename from library/src/org/onepf/oms/util/Logger.java rename to library/src/main/java/org/onepf/oms/util/Logger.java diff --git a/library/src/org/onepf/oms/util/Utils.java b/library/src/main/java/org/onepf/oms/util/Utils.java similarity index 100% rename from library/src/org/onepf/oms/util/Utils.java rename to library/src/main/java/org/onepf/oms/util/Utils.java From 67ead292ac230e4b285df4405bdaec7cfa7ff530 Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Wed, 3 Sep 2014 11:36:16 +0300 Subject: [PATCH 11/72] Add check SKU for Nokia Store. --- .../main/java/org/onepf/oms/SkuManager.java | 5 + .../org/onepf/oms/appstore/NokiaStore.java | 236 ++++++++++-------- .../nokiaUtils/NokiaSkuFormatException.java | 12 + 3 files changed, 143 insertions(+), 110 deletions(-) create mode 100644 library/src/main/java/org/onepf/oms/appstore/nokiaUtils/NokiaSkuFormatException.java diff --git a/library/src/main/java/org/onepf/oms/SkuManager.java b/library/src/main/java/org/onepf/oms/SkuManager.java index ebf9ce79..95dbf178 100644 --- a/library/src/main/java/org/onepf/oms/SkuManager.java +++ b/library/src/main/java/org/onepf/oms/SkuManager.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.onepf.oms.appstore.NokiaStore; import org.onepf.oms.appstore.SamsungApps; import org.onepf.oms.util.Logger; @@ -110,6 +111,10 @@ private static void checkSkuMappingParams(String storeName, String storeSku) { if (OpenIabHelper.NAME_SAMSUNG.equals(storeName)) { SamsungApps.checkSku(storeSku); } + + if (OpenIabHelper.NAME_NOKIA.equals(storeName)){ + NokiaStore.checkSku(storeSku); + } } private static void checkSkuMappingParams(String sku, String storeName, String storeSku) { diff --git a/library/src/main/java/org/onepf/oms/appstore/NokiaStore.java b/library/src/main/java/org/onepf/oms/appstore/NokiaStore.java index d3309d12..31d484c8 100644 --- a/library/src/main/java/org/onepf/oms/appstore/NokiaStore.java +++ b/library/src/main/java/org/onepf/oms/appstore/NokiaStore.java @@ -19,10 +19,13 @@ import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.text.TextUtils; + import org.onepf.oms.Appstore; import org.onepf.oms.AppstoreInAppBillingService; import org.onepf.oms.DefaultAppstore; import org.onepf.oms.OpenIabHelper; +import org.onepf.oms.appstore.nokiaUtils.NokiaSkuFormatException; import org.onepf.oms.appstore.nokiaUtils.NokiaStoreHelper; import org.onepf.oms.util.Logger; @@ -36,114 +39,127 @@ public class NokiaStore extends DefaultAppstore { public static final String NOKIA_BILLING_PERMISSION = "com.nokia.payment.BILLING"; private final Context context; - private NokiaStoreHelper billingService = null; - - //This is the expected SHA1 finger-print in HEX format - private static final String EXPECTED_SHA1_FINGERPRINT = "C476A7D71C4CB92641A699C1F1CAC93CA81E0396"; - - public static final String NOKIA_INSTALLER = "com.nokia.payment.iapenabler"; - public static final String VENDING_ACTION = "com.nokia.payment.iapenabler.InAppBillingService.BIND"; - - public NokiaStore(final Context context) { - Logger.i("NokiaStore.NokiaStore"); - - this.context = context; - } - - /** - * Tells whether in-app billing is ready to work with specified app - * For OpenStore app: if any in-app item for this app published in store - */ - @Override - public boolean isBillingAvailable(final String packageName) { - Logger.i("NokiaStore.isBillingAvailable"); - Logger.d("packageName = ", packageName); - - final PackageManager packageManager = context.getPackageManager(); - final List allPackages = packageManager.getInstalledPackages(0); - - for (final PackageInfo packageInfo : allPackages) { - - if (NOKIA_INSTALLER.equals(packageInfo.packageName)) { - return verifyFingreprint(); - } - } - - return false; - } - - /** - * Returns true only if actual installer for specified app - */ - @Override - public boolean isPackageInstaller(final String packageName) { - Logger.d("sPackageInstaller: packageName = ", packageName); - - final PackageManager packageManager = context.getPackageManager(); - final String installerPackageName = packageManager.getInstallerPackageName(packageName); - - Logger.d("installerPackageName = ", installerPackageName); - - return NOKIA_INSTALLER.equals(installerPackageName); - } - - @Override - public int getPackageVersion(final String packageName) { - Logger.d("getPackageVersion: packageName = " + packageName); - return Appstore.PACKAGE_VERSION_UNDEFINED; - } - - @Override - public String getAppstoreName() { - return OpenIabHelper.NAME_NOKIA; - } - - @Override - public AppstoreInAppBillingService getInAppBillingService() { - if (billingService == null) { - billingService = new NokiaStoreHelper(context, this); - } - return billingService; - } - - /** - * Checks SHA1 fingerprint of the enabler - * - * @return true if signature matches, false if package is not found or signature does not match. - */ - private boolean verifyFingreprint() { - - try { - PackageInfo info = context - .getPackageManager() - .getPackageInfo(NOKIA_INSTALLER, PackageManager.GET_SIGNATURES); - - if (info.signatures.length == 1) { - byte[] cert = info.signatures[0].toByteArray(); - MessageDigest digest; - digest = MessageDigest.getInstance("SHA1"); - byte[] ENABLER_FINGERPRINT = digest.digest(cert); - byte[] EXPECTED_FINGERPRINT = hexStringToByteArray(EXPECTED_SHA1_FINGERPRINT); - - if (Arrays.equals(ENABLER_FINGERPRINT, EXPECTED_FINGERPRINT)) { - Logger.i("isBillingAvailable", "NIAP signature verified"); - return true; - } - } - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - return false; - } - - private static byte[] hexStringToByteArray(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); - } - return data; - } + private NokiaStoreHelper billingService = null; + + //This is the expected SHA1 finger-print in HEX format + private static final String EXPECTED_SHA1_FINGERPRINT = "C476A7D71C4CB92641A699C1F1CAC93CA81E0396"; + + public static final String NOKIA_INSTALLER = "com.nokia.payment.iapenabler"; + public static final String VENDING_ACTION = "com.nokia.payment.iapenabler.InAppBillingService.BIND"; + + public NokiaStore(final Context context) { + Logger.i("NokiaStore.NokiaStore"); + + this.context = context; + } + + /** + * Tells whether in-app billing is ready to work with specified app + * For OpenStore app: if any in-app item for this app published in store + */ + @Override + public boolean isBillingAvailable(final String packageName) { + Logger.i("NokiaStore.isBillingAvailable"); + Logger.d("packageName = ", packageName); + + final PackageManager packageManager = context.getPackageManager(); + final List allPackages = packageManager.getInstalledPackages(0); + + for (final PackageInfo packageInfo : allPackages) { + + if (NOKIA_INSTALLER.equals(packageInfo.packageName)) { + return verifyFingreprint(); + } + } + + return false; + } + + /** + * Returns true only if actual installer for specified app + */ + @Override + public boolean isPackageInstaller(final String packageName) { + Logger.d("sPackageInstaller: packageName = ", packageName); + + final PackageManager packageManager = context.getPackageManager(); + final String installerPackageName = packageManager.getInstallerPackageName(packageName); + + Logger.d("installerPackageName = ", installerPackageName); + + return NOKIA_INSTALLER.equals(installerPackageName); + } + + @Override + public int getPackageVersion(final String packageName) { + Logger.d("getPackageVersion: packageName = " + packageName); + return Appstore.PACKAGE_VERSION_UNDEFINED; + } + + @Override + public String getAppstoreName() { + return OpenIabHelper.NAME_NOKIA; + } + + @Override + public AppstoreInAppBillingService getInAppBillingService() { + if (billingService == null) { + billingService = new NokiaStoreHelper(context, this); + } + return billingService; + } + + /** + * Checks SHA1 fingerprint of the enabler + * + * @return true if signature matches, false if package is not found or signature does not match. + */ + private boolean verifyFingreprint() { + + try { + PackageInfo info = context + .getPackageManager() + .getPackageInfo(NOKIA_INSTALLER, PackageManager.GET_SIGNATURES); + + if (info.signatures.length == 1) { + byte[] cert = info.signatures[0].toByteArray(); + MessageDigest digest; + digest = MessageDigest.getInstance("SHA1"); + byte[] ENABLER_FINGERPRINT = digest.digest(cert); + byte[] EXPECTED_FINGERPRINT = hexStringToByteArray(EXPECTED_SHA1_FINGERPRINT); + + if (Arrays.equals(ENABLER_FINGERPRINT, EXPECTED_FINGERPRINT)) { + Logger.i("isBillingAvailable", "NIAP signature verified"); + return true; + } + } + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return false; + } + + /** + * Validate item SKU in Nokia Store. + * Nokia store item's SKU can contain only digits. + * + * @param sku SKU for validate. + * @throws org.onepf.oms.appstore.nokiaUtils.NokiaSkuFormatException If sku in wrong format for Nokia Store. + */ + public static void checkSku(String sku) { + if (!TextUtils.isDigitsOnly(sku)) { + throw new NokiaSkuFormatException(); + } + } + + private static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } } diff --git a/library/src/main/java/org/onepf/oms/appstore/nokiaUtils/NokiaSkuFormatException.java b/library/src/main/java/org/onepf/oms/appstore/nokiaUtils/NokiaSkuFormatException.java new file mode 100644 index 00000000..d2ea3992 --- /dev/null +++ b/library/src/main/java/org/onepf/oms/appstore/nokiaUtils/NokiaSkuFormatException.java @@ -0,0 +1,12 @@ +package org.onepf.oms.appstore.nokiaUtils; + +import org.onepf.oms.SkuMappingException; + +/** + * Created by krozov on 03.09.14. + */ +public class NokiaSkuFormatException extends SkuMappingException { + public NokiaSkuFormatException() { + super("Nokia Store SKU can contain only digits."); + } +} From 1075b324438f9e6db4cd00026a8bb9ba3ab6aebe Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Wed, 3 Sep 2014 10:39:59 +0300 Subject: [PATCH 12/72] Add sku manager test. --- .../org/onepf/oms/appstore/SamsungApps.java | 6 +- .../appstore/SamsungSkuFormatException.java | 12 +++ .../java/org/onepf/oms/SkuManagerTest.java | 89 +++++++++++++++++++ .../onepf/oms/appstore/SamsungAppsTest.java | 32 +++++++ 4 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 library/src/org/onepf/oms/appstore/SamsungSkuFormatException.java create mode 100644 library/test/java/org/onepf/oms/SkuManagerTest.java create mode 100644 library/test/java/org/onepf/oms/appstore/SamsungAppsTest.java diff --git a/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java b/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java index 70b01b68..2fef83b1 100644 --- a/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java +++ b/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java @@ -179,15 +179,15 @@ public String getAppstoreName() { public static void checkSku(String sku) { String[] skuParts = sku.split("/"); if (skuParts.length != 2) { - throw new SkuMappingException("Samsung SKU must contain ITEM_GROUP_ID and ITEM_ID."); + throw new SamsungSkuFormatException("Samsung SKU must contain ITEM_GROUP_ID and ITEM_ID."); } String groupId = skuParts[0]; String itemId = skuParts[1]; if (TextUtils.isEmpty(groupId) || !TextUtils.isDigitsOnly(groupId)) { - throw new SkuMappingException("Samsung SKU must contain numeric ITEM_GROUP_ID."); + throw new SamsungSkuFormatException("Samsung SKU must contain numeric ITEM_GROUP_ID."); } if (TextUtils.isEmpty(itemId)) { - throw new SkuMappingException("Samsung SKU must contain ITEM_ID."); + throw new SamsungSkuFormatException("Samsung SKU must contain ITEM_ID."); } } } diff --git a/library/src/org/onepf/oms/appstore/SamsungSkuFormatException.java b/library/src/org/onepf/oms/appstore/SamsungSkuFormatException.java new file mode 100644 index 00000000..b53dab42 --- /dev/null +++ b/library/src/org/onepf/oms/appstore/SamsungSkuFormatException.java @@ -0,0 +1,12 @@ +package org.onepf.oms.appstore; + +import org.onepf.oms.SkuMappingException; + +/** + * Created by krozov on 02.09.14. + */ +public class SamsungSkuFormatException extends SkuMappingException { + public SamsungSkuFormatException(String detailMessage) { + super(detailMessage); + } +} diff --git a/library/test/java/org/onepf/oms/SkuManagerTest.java b/library/test/java/org/onepf/oms/SkuManagerTest.java new file mode 100644 index 00000000..609d494f --- /dev/null +++ b/library/test/java/org/onepf/oms/SkuManagerTest.java @@ -0,0 +1,89 @@ +package org.onepf.oms; + +import junit.framework.Assert; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.onepf.oms.appstore.SamsungSkuFormatException; +import org.robolectric.RobolectricTestRunner; + +import java.util.List; + +/** + * Created by krozov on 01.09.14. + */ +@RunWith(RobolectricTestRunner.class) +public class SkuManagerTest { + + private static final String STORE_SKU_GOOGLE = "test_sku_google"; + private static final String STORE_SKU_SAMSUNG = "2014/test_sku_samsung"; + private static final String ITEM_SKU = "test_sku"; + + @Test + public void testMapSku() throws Exception { + SkuManager sm = SkuManager.getInstance(); + sm.mapSku(ITEM_SKU, OpenIabHelper.NAME_GOOGLE, STORE_SKU_GOOGLE); + + String storeSku = sm.getStoreSku(OpenIabHelper.NAME_GOOGLE, ITEM_SKU); + Assert.assertNotNull(storeSku); + Assert.assertEquals(STORE_SKU_GOOGLE, storeSku); + + String sku = sm.getSku(OpenIabHelper.NAME_GOOGLE, STORE_SKU_GOOGLE); + Assert.assertNotNull(sku); + Assert.assertEquals(ITEM_SKU, sku); + + List googlePlayStoreSkus = sm.getAllStoreSkus(OpenIabHelper.NAME_GOOGLE); + Assert.assertNotNull(googlePlayStoreSkus); + Assert.assertEquals(1, googlePlayStoreSkus.size()); + Assert.assertNotNull(googlePlayStoreSkus.get(0)); + Assert.assertEquals(STORE_SKU_GOOGLE, googlePlayStoreSkus.get(0)); + + Assert.assertNull(sm.getAllStoreSkus(OpenIabHelper.NAME_AMAZON)); + } + + @Test + public void testMapSamsungSku() { + SkuManager sm = SkuManager.getInstance(); + sm.mapSku(ITEM_SKU, OpenIabHelper.NAME_SAMSUNG, STORE_SKU_SAMSUNG); + + String storeSku = sm.getStoreSku(OpenIabHelper.NAME_SAMSUNG, ITEM_SKU); + Assert.assertNotNull(storeSku); + Assert.assertEquals(STORE_SKU_SAMSUNG, storeSku); + + String sku = sm.getSku(OpenIabHelper.NAME_SAMSUNG, STORE_SKU_SAMSUNG); + Assert.assertNotNull(sku); + Assert.assertEquals(ITEM_SKU, sku); + + List samsungStoreSkus = sm.getAllStoreSkus(OpenIabHelper.NAME_SAMSUNG); + Assert.assertNotNull(samsungStoreSkus); + Assert.assertEquals(1, samsungStoreSkus.size()); + Assert.assertNotNull(samsungStoreSkus.get(0)); + Assert.assertEquals(STORE_SKU_SAMSUNG, samsungStoreSkus.get(0)); + } + + @Test(expected = SamsungSkuFormatException.class) + public void testIllegalFormatSamsungSKUMapping() { + SkuManager sm = SkuManager.getInstance(); + sm.mapSku("wrong_sku", OpenIabHelper.NAME_SAMSUNG, "test_group/test_item_id"); + } + + @Test(expected = SkuMappingException.class) + public void testMapNullSkuMapping() { + final SkuManager sm = SkuManager.getInstance(); + sm.mapSku(null, OpenIabHelper.NAME_GOOGLE, STORE_SKU_GOOGLE); + } + + @Test(expected = SkuMappingException.class) + public void testMapNullStoreNameMapping() { + final SkuManager sm = SkuManager.getInstance(); + sm.mapSku(ITEM_SKU, null, STORE_SKU_GOOGLE); + } + + @Test(expected = SkuMappingException.class) + public void testMapEmptyStoreSkuMapping() { + final SkuManager sm = SkuManager.getInstance(); + sm.mapSku(ITEM_SKU, OpenIabHelper.NAME_GOOGLE, ""); + } +} diff --git a/library/test/java/org/onepf/oms/appstore/SamsungAppsTest.java b/library/test/java/org/onepf/oms/appstore/SamsungAppsTest.java new file mode 100644 index 00000000..8ddbb27b --- /dev/null +++ b/library/test/java/org/onepf/oms/appstore/SamsungAppsTest.java @@ -0,0 +1,32 @@ +package org.onepf.oms.appstore; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** + * Created by krozov on 02.09.14. + */ +@RunWith(RobolectricTestRunner.class) +public class SamsungAppsTest { + + @Test + public void testCorrectSku() throws Exception { + SamsungApps.checkSku("2014/test_sku_samsung"); + } + + @Test(expected = SamsungSkuFormatException.class) + public void testEmptyGroupIdSku() throws Exception { + SamsungApps.checkSku("/test_sku_samsung"); + } + + @Test(expected = SamsungSkuFormatException.class) + public void testEmptyItemIdSku() throws Exception { + SamsungApps.checkSku("2014/"); + } + + @Test(expected = SamsungSkuFormatException.class) + public void testNotNumericGroupIdSku() throws Exception { + SamsungApps.checkSku("test_group/test_item_id"); + } +} From b9b5d81993aa6e725588a4d7c1655b2e9df648d8 Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Wed, 3 Sep 2014 12:00:09 +0300 Subject: [PATCH 13/72] Add sku mapping test. --- .../androidTest}/java/org/onepf/oms/SkuManagerTest.java | 5 +++-- .../java/org/onepf/oms/appstore/SamsungAppsTest.java | 2 ++ .../org/onepf/oms/appstore/SamsungSkuFormatException.java | 0 3 files changed, 5 insertions(+), 2 deletions(-) rename library/{test => src/androidTest}/java/org/onepf/oms/SkuManagerTest.java (96%) rename library/{test => src/androidTest}/java/org/onepf/oms/appstore/SamsungAppsTest.java (90%) rename library/src/{ => main/java}/org/onepf/oms/appstore/SamsungSkuFormatException.java (100%) diff --git a/library/test/java/org/onepf/oms/SkuManagerTest.java b/library/src/androidTest/java/org/onepf/oms/SkuManagerTest.java similarity index 96% rename from library/test/java/org/onepf/oms/SkuManagerTest.java rename to library/src/androidTest/java/org/onepf/oms/SkuManagerTest.java index 609d494f..7ff45f62 100644 --- a/library/test/java/org/onepf/oms/SkuManagerTest.java +++ b/library/src/androidTest/java/org/onepf/oms/SkuManagerTest.java @@ -2,18 +2,19 @@ import junit.framework.Assert; -import org.junit.Rule; +import org.junit.Ignore; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.onepf.oms.appstore.SamsungSkuFormatException; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; import java.util.List; /** * Created by krozov on 01.09.14. */ +@Config(emulateSdk = 18, manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class SkuManagerTest { diff --git a/library/test/java/org/onepf/oms/appstore/SamsungAppsTest.java b/library/src/androidTest/java/org/onepf/oms/appstore/SamsungAppsTest.java similarity index 90% rename from library/test/java/org/onepf/oms/appstore/SamsungAppsTest.java rename to library/src/androidTest/java/org/onepf/oms/appstore/SamsungAppsTest.java index 8ddbb27b..e635e77a 100644 --- a/library/test/java/org/onepf/oms/appstore/SamsungAppsTest.java +++ b/library/src/androidTest/java/org/onepf/oms/appstore/SamsungAppsTest.java @@ -3,10 +3,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; /** * Created by krozov on 02.09.14. */ +@Config(emulateSdk = 18, manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class SamsungAppsTest { diff --git a/library/src/org/onepf/oms/appstore/SamsungSkuFormatException.java b/library/src/main/java/org/onepf/oms/appstore/SamsungSkuFormatException.java similarity index 100% rename from library/src/org/onepf/oms/appstore/SamsungSkuFormatException.java rename to library/src/main/java/org/onepf/oms/appstore/SamsungSkuFormatException.java From f481aa9ffaa604446da6d754b01b0f04b528b444 Mon Sep 17 00:00:00 2001 From: Kirill Rozov Date: Wed, 3 Sep 2014 12:09:12 +0300 Subject: [PATCH 14/72] Add Nokia Store SKU tests. --- .../java/org/onepf/oms/SkuManagerTest.java | 49 ++++++++++++++----- .../onepf/oms/appstore/NokiaStoreTest.java | 25 ++++++++++ 2 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 library/src/androidTest/java/org/onepf/oms/appstore/NokiaStoreTest.java diff --git a/library/src/androidTest/java/org/onepf/oms/SkuManagerTest.java b/library/src/androidTest/java/org/onepf/oms/SkuManagerTest.java index 7ff45f62..8c8f4efc 100644 --- a/library/src/androidTest/java/org/onepf/oms/SkuManagerTest.java +++ b/library/src/androidTest/java/org/onepf/oms/SkuManagerTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.onepf.oms.appstore.SamsungSkuFormatException; +import org.onepf.oms.appstore.nokiaUtils.NokiaSkuFormatException; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @@ -20,6 +21,7 @@ public class SkuManagerTest { private static final String STORE_SKU_GOOGLE = "test_sku_google"; private static final String STORE_SKU_SAMSUNG = "2014/test_sku_samsung"; + private static final String STORE_SKU_NOKIA = "12451"; private static final String ITEM_SKU = "test_sku"; @Test @@ -35,13 +37,32 @@ public void testMapSku() throws Exception { Assert.assertNotNull(sku); Assert.assertEquals(ITEM_SKU, sku); - List googlePlayStoreSkus = sm.getAllStoreSkus(OpenIabHelper.NAME_GOOGLE); - Assert.assertNotNull(googlePlayStoreSkus); - Assert.assertEquals(1, googlePlayStoreSkus.size()); - Assert.assertNotNull(googlePlayStoreSkus.get(0)); - Assert.assertEquals(STORE_SKU_GOOGLE, googlePlayStoreSkus.get(0)); + List googlePlaySkus = sm.getAllStoreSkus(OpenIabHelper.NAME_GOOGLE); + Assert.assertNotNull(googlePlaySkus); + Assert.assertEquals(1, googlePlaySkus.size()); + Assert.assertNotNull(googlePlaySkus.get(0)); + Assert.assertEquals(STORE_SKU_GOOGLE, googlePlaySkus.get(0)); - Assert.assertNull(sm.getAllStoreSkus(OpenIabHelper.NAME_AMAZON)); + } + + @Test + public void testMapNokiaStoreSku(){ + SkuManager sm = SkuManager.getInstance(); + sm.mapSku(ITEM_SKU, OpenIabHelper.NAME_NOKIA, STORE_SKU_NOKIA); + + String storeSku = sm.getStoreSku(OpenIabHelper.NAME_NOKIA, ITEM_SKU); + Assert.assertNotNull(storeSku); + Assert.assertEquals(STORE_SKU_NOKIA, storeSku); + + String sku = sm.getSku(OpenIabHelper.NAME_NOKIA, STORE_SKU_NOKIA); + Assert.assertNotNull(sku); + Assert.assertEquals(ITEM_SKU, sku); + + List samsungAppsSkus = sm.getAllStoreSkus(OpenIabHelper.NAME_NOKIA); + Assert.assertNotNull(samsungAppsSkus); + Assert.assertEquals(1, samsungAppsSkus.size()); + Assert.assertNotNull(samsungAppsSkus.get(0)); + Assert.assertEquals(STORE_SKU_NOKIA, samsungAppsSkus.get(0)); } @Test @@ -57,11 +78,11 @@ public void testMapSamsungSku() { Assert.assertNotNull(sku); Assert.assertEquals(ITEM_SKU, sku); - List samsungStoreSkus = sm.getAllStoreSkus(OpenIabHelper.NAME_SAMSUNG); - Assert.assertNotNull(samsungStoreSkus); - Assert.assertEquals(1, samsungStoreSkus.size()); - Assert.assertNotNull(samsungStoreSkus.get(0)); - Assert.assertEquals(STORE_SKU_SAMSUNG, samsungStoreSkus.get(0)); + List samsungAppsSkus = sm.getAllStoreSkus(OpenIabHelper.NAME_SAMSUNG); + Assert.assertNotNull(samsungAppsSkus); + Assert.assertEquals(1, samsungAppsSkus.size()); + Assert.assertNotNull(samsungAppsSkus.get(0)); + Assert.assertEquals(STORE_SKU_SAMSUNG, samsungAppsSkus.get(0)); } @Test(expected = SamsungSkuFormatException.class) @@ -70,6 +91,12 @@ public void testIllegalFormatSamsungSKUMapping() { sm.mapSku("wrong_sku", OpenIabHelper.NAME_SAMSUNG, "test_group/test_item_id"); } + @Test(expected = NokiaSkuFormatException.class) + public void testIllegalFormatNokiaSKUMapping() { + SkuManager sm = SkuManager.getInstance(); + sm.mapSku("wrong_sku", OpenIabHelper.NAME_NOKIA, "test_nokia_store_sku"); + } + @Test(expected = SkuMappingException.class) public void testMapNullSkuMapping() { final SkuManager sm = SkuManager.getInstance(); diff --git a/library/src/androidTest/java/org/onepf/oms/appstore/NokiaStoreTest.java b/library/src/androidTest/java/org/onepf/oms/appstore/NokiaStoreTest.java new file mode 100644 index 00000000..1eafbb2b --- /dev/null +++ b/library/src/androidTest/java/org/onepf/oms/appstore/NokiaStoreTest.java @@ -0,0 +1,25 @@ +package org.onepf.oms.appstore; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.onepf.oms.appstore.nokiaUtils.NokiaSkuFormatException; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +/** + * Created by krozov on 02.09.14. + */ +@Config(emulateSdk = 18, manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class NokiaStoreTest { + + @Test + public void testCorrectSku() throws Exception { + NokiaStore.checkSku("12351"); + } + + @Test(expected = NokiaSkuFormatException.class) + public void testWrongFormatSku() throws Exception { + NokiaStore.checkSku("test_sku"); + } +} From 681af96cf2f1b1d28e6b9fbd639809fb984e3120 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Fri, 5 Sep 2014 20:16:10 +0400 Subject: [PATCH 15/72] Start implementing new store picking algorithm --- .../java/org/onepf/oms/OpenIabHelper.java | 334 +++++++++++++----- 1 file changed, 249 insertions(+), 85 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index f6e4ae65..47406ddc 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -17,6 +17,7 @@ package org.onepf.oms; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -30,8 +31,6 @@ import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.onepf.oms.appstore.AmazonAppstore; -import org.onepf.oms.appstore.GooglePlay; import org.onepf.oms.appstore.NokiaStore; import org.onepf.oms.appstore.OpenAppstore; import org.onepf.oms.appstore.SamsungApps; @@ -55,6 +54,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -141,12 +141,57 @@ public class OpenIabHelper { public static final String NAME_GOOGLE = "com.google.play"; public static final String NAME_AMAZON = "com.amazon.apps"; public static final String NAME_SAMSUNG = "com.samsung.apps"; - public static final String NAME_YANDEX = "com.yandex.store"; public static final String NAME_NOKIA = "com.nokia.nstore"; + + public static final String NAME_YANDEX = "com.yandex.store"; public static final String NAME_APPLAND = "Appland"; public static final String NAME_SLIDEME = "SlideME"; public static final String NAME_APTOIDE = "cm.aptoide.pt"; + private static enum Wrapper { + Google("com.google.play") { + @Override + Appstore instantiate(final Context context) { + return super.instantiate(context); + } + }, + Amazon("com.amazon.apps") { + @Override + Appstore instantiate(final Context context) { + return super.instantiate(context); + } + }, + Samsung("com.samsung.apps") { + @Override + Appstore instantiate(final Context context) { + return super.instantiate(context); + } + }, + Nokia("com.nokia.nstore") { + @Override + Appstore instantiate(final Context context) { + return super.instantiate(context); + } + }; + + Wrapper(final String wrappedPackage) { + this.wrappedPackage = wrappedPackage; + } + + private final String wrappedPackage; + + Appstore instantiate(final Context context) { + return null; + } + } + + private static final List WRAPPED_PACKAGES = Arrays.asList( + NAME_GOOGLE, + NAME_AMAZON, + NAME_SAMSUNG, + NAME_NOKIA + ); + /** * @param sku - application inner SKU @@ -314,103 +359,222 @@ public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { } Logger.init(); setupState = SETUP_IN_PROGRESS; - new Thread(new Runnable() { - public void run() { - List stores2check = new ArrayList(); - if (options.availableStores != null) { - stores2check.addAll(options.availableStores); - } else { // if appstores are not specified by user - lookup for all available stores - final List openStores = discoverOpenStores(context, null, options); - Logger.dWithTimeFromUp("startSetup() discovered openstores: ", openStores.toString()); - stores2check.addAll(openStores); - if (options.getVerifyMode() == Options.VERIFY_EVERYTHING && !options.hasStoreKey(NAME_GOOGLE)) { - // don't work with GooglePlay if verifyMode is strict and no publicKey provided - } else { - final String publicKey = options.verifyMode == Options.VERIFY_SKIP ? null - : options.storeKeys.get(OpenIabHelper.NAME_GOOGLE); - stores2check.add(new GooglePlay(context, publicKey)); - } - // try AmazonApps if in-app-purchasing.jar with Amazon SDK is compiled with app - try { - OpenIabHelper.class.getClassLoader().loadClass("com.amazon.inapp.purchasing.PurchasingManager"); - stores2check.add(new AmazonAppstore(context)); - } catch (ClassNotFoundException ignored) { - } - if (!CollectionUtils.isEmpty(SkuManager.getInstance().getAllStoreSkus(NAME_SAMSUNG))) { - // SamsungApps shows lot of UI stuff during init - // try it only if samsung SKUs are specified - stores2check.add(new SamsungApps(activity, options)); - } - //Nokia TODO change logic - stores2check.add(new NokiaStore(context)); - if (!Utils.hasRequestedPermission(context, NokiaStore.NOKIA_BILLING_PERMISSION)) { - Logger.w("Required permission \"" + - NokiaStore.NOKIA_BILLING_PERMISSION + "\" NOT REQUESTED"); - } - } + final String packageName = context.getPackageName(); + final String packageInstaller = context.getPackageManager().getInstallerPackageName(packageName); + // Package installer is set + if (TextUtils.isEmpty(packageInstaller)) { + setUp(listener); + } else { + setUpForPackage(listener, packageInstaller); + } + } - //todo get rid of this, DO NOT save anything to fields! - for (Appstore store : stores2check) { - if (store instanceof SamsungApps) { - samsungInSetup = (SamsungApps) store; - break; - } - } - IabResult result = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing isn't supported"); + private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener, final String packageInstaller) { + final PackageManager packageManager = context.getPackageManager(); + boolean packageInstalled; + try { + packageManager.getPackageInfo(packageInstaller, PackageManager.GET_ACTIVITIES); + packageInstalled = true; + } catch (NameNotFoundException exception) { + packageInstalled = false; + } - if (options.checkInventory) { + if (!packageInstalled) { + setUp(listener); + return; + } - final List equippedStores = checkInventory(stores2check); + // Package installer exist + Appstore appstore = null; + for (final Wrapper wrapper : Wrapper.values()) { + if (TextUtils.equals(packageInstaller, wrapper.wrappedPackage)){ + appstore = wrapper.instantiate(context); + break; + } + } - if (!equippedStores.isEmpty()) { - mAppstore = selectBillingService(equippedStores); - } + final IabResult iabResult; + if (appstore != null) { + // We've got a wrapper for package installer + if (!appstore.isBillingAvailable(packageInstaller)) { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); + } else { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + } + listener.onIabSetupFinished(iabResult); + return; + } - Logger.dWithTimeFromUp("select equipped"); - if (mAppstore != null) { - final String message = "Successfully initialized with existing inventory: " + mAppstore.getAppstoreName(); - result = new IabResult(BILLING_RESPONSE_RESULT_OK, message); - Logger.d(message); - } else { - // found no equipped stores. Select store based on store parameters - mAppstore = selectBillingService(stores2check); - Logger.dWithTimeFromUp("select non-equipped"); - if (mAppstore != null) { - final String message = "Successfully initialized with non-equipped store: " + mAppstore.getAppstoreName(); - result = new IabResult(BILLING_RESPONSE_RESULT_OK, message); - Logger.d(message); + final Intent intentAppstoreServices = new Intent(BIND_INTENT); + Intent bindServiceIntent = null; + for (final ServiceInfo serviceInfo : queryOpenStoreServices()) { + final String servicePackage = serviceInfo.packageName; + if (TextUtils.equals(packageInstaller, servicePackage)) { + bindServiceIntent = new Intent(intentAppstoreServices); + bindServiceIntent.setClassName(servicePackage, serviceInfo.name); + } + } + if (bindServiceIntent != null) { + // We've got an open store as package installer + if (!context.bindService(bindServiceIntent, new ServiceConnection() { + // Open store is available + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); + IabResult iabResult; + try { + if (openAppstoreService.isBillingAvailable(packageInstaller)) { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + } else { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); } + } catch (RemoteException e) { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Error occurred during billing setup"); } - if (mAppstore != null) { - mAppstoreBillingService = mAppstore.getInAppBillingService(); - } - fireSetupFinished(listener, result); - } else { // no inventory check. Select store based on store parameters - mAppstore = selectBillingService(stores2check); - if (mAppstore != null) { - mAppstoreBillingService = mAppstore.getInAppBillingService(); - mAppstoreBillingService.startSetup(new OnIabSetupFinishedListener() { - public void onIabSetupFinished(IabResult result) { - fireSetupFinished(listener, result); - } - }); - } else { - fireSetupFinished(listener, result); - } + listener.onIabSetupFinished(iabResult); } - for (Appstore store : stores2check) { - if (store != mAppstore && store.getInAppBillingService() != null) { - store.getInAppBillingService().dispose(); - Logger.dWithTimeFromUp("startSetup() disposing ", store.getAppstoreName()); - } + + @Override + public void onServiceDisconnected(final ComponentName name) {} + }, Context.BIND_AUTO_CREATE)) { + listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Can't bind to service")); + } + } else { + setUp(listener); + } + } + + private void setUp(final IabHelper.OnIabSetupFinishedListener listener) { + // Look for appropriate open store + for (final ServiceInfo serviceInfo : queryOpenStoreServices()) { + final Intent bindServiceIntent = new Intent(BIND_INTENT); + if(!context.bindService(bindServiceIntent,new ServiceConnection() { + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + + } + + @Override + public void onServiceDisconnected(final ComponentName name) { + } + },Context.BIND_AUTO_CREATE)){ + } - }, "openiab-setup").start(); + } + } + + private List queryOpenStoreServices() { + final Intent intentAppstoreServices = new Intent(BIND_INTENT); + final PackageManager packageManager = context.getPackageManager(); + final List resolveInfos = packageManager.queryIntentServices(intentAppstoreServices, 0); + final List serviceInfos = new ArrayList(); + for (final ResolveInfo resolveInfo : resolveInfos) { + serviceInfos.add(resolveInfo.serviceInfo); + } + return (List) Collections.unmodifiableCollection(serviceInfos); } +// new Thread(new Runnable() { +// public void run() { +// List stores2check = new ArrayList(); +// if (options.availableStores != null) { +// stores2check.addAll(options.availableStores); +// } else { // if appstores are not specified by user - lookup for all available stores +// final List openStores = discoverOpenStores(context, null, options); +// Logger.dWithTimeFromUp("startSetup() discovered openstores: ", openStores.toString()); +// stores2check.addAll(openStores); +// if (options.getVerifyMode() == Options.VERIFY_EVERYTHING && !options.hasStoreKey(NAME_GOOGLE)) { +// // don't work with GooglePlay if verifyMode is strict and no publicKey provided +// } else { +// final String publicKey = options.verifyMode == Options.VERIFY_SKIP ? null +// : options.storeKeys.get(OpenIabHelper.NAME_GOOGLE); +// stores2check.add(new GooglePlay(context, publicKey)); +// } +// +// // try AmazonApps if in-app-purchasing.jar with Amazon SDK is compiled with app +// try { +// OpenIabHelper.class.getClassLoader().loadClass("com.amazon.inapp.purchasing.PurchasingManager"); +// stores2check.add(new AmazonAppstore(context)); +// } catch (ClassNotFoundException ignored) { +// } +// +// if (!CollectionUtils.isEmpty(SkuManager.getInstance().getAllStoreSkus(NAME_SAMSUNG))) { +// // SamsungApps shows lot of UI stuff during init +// // try it only if samsung SKUs are specified +// stores2check.add(new SamsungApps(activity, options)); +// } +// //Nokia TODO change logic +// stores2check.add(new NokiaStore(context)); +// if (!Utils.hasRequestedPermission(context, NokiaStore.NOKIA_BILLING_PERMISSION)) { +// Logger.w("Required permission \"" + +// NokiaStore.NOKIA_BILLING_PERMISSION + "\" NOT REQUESTED"); +// } +// } +// +// //todo get rid of this, DO NOT save anything to fields! +// for (Appstore store : stores2check) { +// if (store instanceof SamsungApps) { +// samsungInSetup = (SamsungApps) store; +// break; +// } +// } +// +// IabResult result = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing isn't supported"); +// +// if (options.checkInventory) { +// +// final List equippedStores = checkInventory(stores2check); +// +// if (!equippedStores.isEmpty()) { +// mAppstore = selectBillingService(equippedStores); +// } +// +// Logger.dWithTimeFromUp("select equipped"); +// if (mAppstore != null) { +// final String message = "Successfully initialized with existing inventory: " + mAppstore.getAppstoreName(); +// result = new IabResult(BILLING_RESPONSE_RESULT_OK, message); +// Logger.d(message); +// } else { +// // found no equipped stores. Select store based on store parameters +// mAppstore = selectBillingService(stores2check); +// Logger.dWithTimeFromUp("select non-equipped"); +// if (mAppstore != null) { +// final String message = "Successfully initialized with non-equipped store: " + mAppstore.getAppstoreName(); +// result = new IabResult(BILLING_RESPONSE_RESULT_OK, message); +// Logger.d(message); +// } +// } +// if (mAppstore != null) { +// mAppstoreBillingService = mAppstore.getInAppBillingService(); +// } +// fireSetupFinished(listener, result); +// } else { // no inventory check. Select store based on store parameters +// mAppstore = selectBillingService(stores2check); +// if (mAppstore != null) { +// mAppstoreBillingService = mAppstore.getInAppBillingService(); +// mAppstoreBillingService.startSetup(new OnIabSetupFinishedListener() { +// public void onIabSetupFinished(IabResult result) { +// fireSetupFinished(listener, result); +// } +// }); +// } else { +// fireSetupFinished(listener, result); +// } +// } +// for (Appstore store : stores2check) { +// if (store != mAppstore && store.getInAppBillingService() != null) { +// store.getInAppBillingService().dispose(); +// Logger.dWithTimeFromUp("startSetup() disposing ", store.getAppstoreName()); +// } +// } +// } +// }, "openiab-setup").start(); +// } + @MagicConstant(intValues = {SETUP_DISPOSED, SETUP_IN_PROGRESS, SETUP_RESULT_FAILED, SETUP_RESULT_NOT_STARTED, SETUP_RESULT_SUCCESSFUL}) From 555fc164e05cdfeb3e0b4433f8a7dcf9f6c4456b Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Sun, 7 Sep 2014 23:25:33 +0400 Subject: [PATCH 16/72] Further store picking algorithm implementation --- .../java/org/onepf/oms/OpenIabHelper.java | 157 +++++++++++------- .../main/java/org/onepf/oms/util/Utils.java | 5 + 2 files changed, 104 insertions(+), 58 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 47406ddc..0cdc78e2 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -151,26 +151,26 @@ public class OpenIabHelper { private static enum Wrapper { Google("com.google.play") { @Override - Appstore instantiate(final Context context) { - return super.instantiate(context); + Appstore createInstance(final Context context) { + return super.createInstance(context); } }, Amazon("com.amazon.apps") { @Override - Appstore instantiate(final Context context) { - return super.instantiate(context); + Appstore createInstance(final Context context) { + return super.createInstance(context); } }, Samsung("com.samsung.apps") { @Override - Appstore instantiate(final Context context) { - return super.instantiate(context); + Appstore createInstance(final Context context) { + return super.createInstance(context); } }, Nokia("com.nokia.nstore") { @Override - Appstore instantiate(final Context context) { - return super.instantiate(context); + Appstore createInstance(final Context context) { + return super.createInstance(context); } }; @@ -180,19 +180,11 @@ Appstore instantiate(final Context context) { private final String wrappedPackage; - Appstore instantiate(final Context context) { + Appstore createInstance(final Context context) { return null; } } - private static final List WRAPPED_PACKAGES = Arrays.asList( - NAME_GOOGLE, - NAME_AMAZON, - NAME_SAMSUNG, - NAME_NOKIA - ); - - /** * @param sku - application inner SKU * @param storeSku - shouldn't duplicate already mapped values @@ -344,7 +336,6 @@ public OpenIabHelper(Context context, Options options) { /** * Discover all available stores and select the best billing service. - * If the flag {@link Options#checkInventory} is set to true, stores with existing inventory are checked first. *

* Should be called from UI thread * @@ -363,8 +354,8 @@ public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { final String packageName = context.getPackageName(); final String packageInstaller = context.getPackageManager().getInstallerPackageName(packageName); - // Package installer is set if (TextUtils.isEmpty(packageInstaller)) { + // Package installer is not set setUp(listener); } else { setUpForPackage(listener, packageInstaller); @@ -383,6 +374,7 @@ private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener } if (!packageInstalled) { + // Package installer no longer exist, fallback to default algorithm setUp(listener); return; } @@ -391,7 +383,7 @@ private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener Appstore appstore = null; for (final Wrapper wrapper : Wrapper.values()) { if (TextUtils.equals(packageInstaller, wrapper.wrappedPackage)){ - appstore = wrapper.instantiate(context); + appstore = wrapper.createInstance(context); break; } } @@ -404,12 +396,13 @@ private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener } else { iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); } - listener.onIabSetupFinished(iabResult); + finishSetUp(listener, iabResult, appstore); return; } final Intent intentAppstoreServices = new Intent(BIND_INTENT); Intent bindServiceIntent = null; + // Look for package installer among available open stores for (final ServiceInfo serviceInfo : queryOpenStoreServices()) { final String servicePackage = serviceInfo.packageName; if (TextUtils.equals(packageInstaller, servicePackage)) { @@ -417,33 +410,41 @@ private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener bindServiceIntent.setClassName(servicePackage, serviceInfo.name); } } - if (bindServiceIntent != null) { - // We've got an open store as package installer - if (!context.bindService(bindServiceIntent, new ServiceConnection() { - // Open store is available - @Override - public void onServiceConnected(final ComponentName name, final IBinder service) { - final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); - IabResult iabResult; - try { - if (openAppstoreService.isBillingAvailable(packageInstaller)) { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); - } else { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); - } - } catch (RemoteException e) { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Error occurred during billing setup"); + + if (bindServiceIntent == null) { + // No intent for open store service, fallback to default algorithm + setUp(listener); + return; + } + + // We've got an open store as package installer + final ServiceConnection serviceConnection = new ServiceConnection() { + // Open store is available + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); + IabResult iabResult; + Appstore appstore = null; + try { + if (openAppstoreService.isBillingAvailable(packageInstaller)) { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + } else { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); } - listener.onIabSetupFinished(iabResult); + appstore = getOpenAppstore(name, this, openAppstoreService); + } catch (RemoteException e) { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Error occurred during billing setup"); } - @Override - public void onServiceDisconnected(final ComponentName name) {} - }, Context.BIND_AUTO_CREATE)) { - listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Can't bind to service")); + finishSetUp(listener, iabResult, appstore); } - } else { - setUp(listener); + + @Override + public void onServiceDisconnected(final ComponentName name) {} + }; + if (!context.bindService(bindServiceIntent, serviceConnection , Context.BIND_AUTO_CREATE)) { + // Unable to connect to open store service + finishSetUp(listener, new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Can't bind to service"), null); } } @@ -451,19 +452,6 @@ private void setUp(final IabHelper.OnIabSetupFinishedListener listener) { // Look for appropriate open store for (final ServiceInfo serviceInfo : queryOpenStoreServices()) { final Intent bindServiceIntent = new Intent(BIND_INTENT); - if(!context.bindService(bindServiceIntent,new ServiceConnection() { - @Override - public void onServiceConnected(final ComponentName name, final IBinder service) { - - } - - @Override - public void onServiceDisconnected(final ComponentName name) { - - } - },Context.BIND_AUTO_CREATE)){ - - } } } @@ -478,6 +466,59 @@ private List queryOpenStoreServices() { return (List) Collections.unmodifiableCollection(serviceInfos); } + private OpenAppstore getOpenAppstore(final ComponentName name, + final ServiceConnection serviceConnection, + final IOpenAppstore openAppstoreService) + throws RemoteException { + final String appstoreName = openAppstoreService.getAppstoreName(); + final Intent billingIntent = openAppstoreService.getBillingServiceIntent(); + + if (appstoreName == null) { // no name - no service + Logger.e("discoverOpenStores() Appstore doesn't have name. Skipped. ComponentName: ", name); + return null; + } + + if (billingIntent == null) { + Logger.d("discoverOpenStores(): billing is not supported by store: ", name); + return null; + } + + // TODO Do something about verifyMode +// if ((options.verifyMode == Options.VERIFY_EVERYTHING) && !options.hasStoreKey(appstoreName)) { +// don't connect to OpenStore if no key provided and verification is strict +// Logger.e("discoverOpenStores() verification is required but publicKey is not provided: ", name); +// } +// String publicKey = options.getStoreKey(appstoreName); +// if (options.verifyMode == Options.VERIFY_SKIP) publicKey = null; + + final OpenAppstore openAppstore = new OpenAppstore(context, appstoreName, openAppstoreService, billingIntent, null, serviceConnection); + openAppstore.componentName = name; + return openAppstore; + } + + private void finishSetUp(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + @NotNull final IabResult iabResult, + @Nullable final Appstore appstore) { + if (!Utils.uiThread()) { + throw new IllegalStateException("Must be called from UI thread."); + } + if (setupState != SETUP_IN_PROGRESS) { + throw new IllegalStateException("Setup is not started or already finished."); + } + + final boolean setUpSuccessful = iabResult.isSuccess(); + setupState = setUpSuccessful ? SETUP_RESULT_SUCCESSFUL : SETUP_RESULT_FAILED; + if (setUpSuccessful) { + if (appstore == null) { + throw new IllegalStateException("Appstore can't be null if setup is successful"); + } + mAppstore = appstore; + mAppstoreBillingService = appstore.getInAppBillingService(); + } + Logger.dWithTimeFromUp("finishSetUp() === SETUP DONE === result: ", iabResult, " Appstore: ", appstore); + listener.onIabSetupFinished(iabResult); + } + // new Thread(new Runnable() { // public void run() { // List stores2check = new ArrayList(); @@ -1455,7 +1496,7 @@ public List getAvailableStores() { /** * storeKeys is map of [ appstore name -> publicKeyBase64 ] - * Put keys for all stores you support in this Map and pass it to instantiate {@link OpenIabHelper} + * Put keys for all stores you support in this Map and pass it to createInstance {@link OpenIabHelper} *

* publicKey key is used to verify receipt is created by genuine Appstore using * provided signature. It can be found in Developer Console of particular store diff --git a/library/src/main/java/org/onepf/oms/util/Utils.java b/library/src/main/java/org/onepf/oms/util/Utils.java index 1a1568e7..e3ffd540 100644 --- a/library/src/main/java/org/onepf/oms/util/Utils.java +++ b/library/src/main/java/org/onepf/oms/util/Utils.java @@ -3,6 +3,7 @@ import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.os.Looper; import android.text.TextUtils; import org.jetbrains.annotations.NotNull; @@ -41,4 +42,8 @@ public static boolean hasRequestedPermission(@NotNull Context context, final Str } return false; } + + public static boolean uiThread() { + return Thread.currentThread() == Looper.getMainLooper().getThread(); + } } From dcb507006a33d56ea9d97bd072de69c1db1b4fbe Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 8 Sep 2014 16:24:59 +0400 Subject: [PATCH 17/72] Base store picking algorithm implementation --- .../java/org/onepf/oms/OpenIabHelper.java | 105 ++++++++++++++++-- 1 file changed, 93 insertions(+), 12 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 0cdc78e2..f77ca29f 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -17,13 +17,14 @@ package org.onepf.oms; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Queue; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -351,7 +352,6 @@ public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { Logger.init(); setupState = SETUP_IN_PROGRESS; - final String packageName = context.getPackageName(); final String packageInstaller = context.getPackageManager().getInstallerPackageName(packageName); if (TextUtils.isEmpty(packageInstaller)) { @@ -362,7 +362,6 @@ public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { } } - private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener, final String packageInstaller) { final PackageManager packageManager = context.getPackageManager(); boolean packageInstalled; @@ -400,14 +399,12 @@ private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener return; } - final Intent intentAppstoreServices = new Intent(BIND_INTENT); Intent bindServiceIntent = null; // Look for package installer among available open stores for (final ServiceInfo serviceInfo : queryOpenStoreServices()) { final String servicePackage = serviceInfo.packageName; if (TextUtils.equals(packageInstaller, servicePackage)) { - bindServiceIntent = new Intent(intentAppstoreServices); - bindServiceIntent.setClassName(servicePackage, serviceInfo.name); + bindServiceIntent = getBindServiceIntent(serviceInfo); } } @@ -422,20 +419,18 @@ private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener // Open store is available @Override public void onServiceConnected(final ComponentName name, final IBinder service) { - final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); IabResult iabResult; Appstore appstore = null; try { - if (openAppstoreService.isBillingAvailable(packageInstaller)) { + appstore = checkOpenStoreService(name, service, this); + if (appstore != null) { iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); } else { iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); } - appstore = getOpenAppstore(name, this, openAppstoreService); } catch (RemoteException e) { iabResult = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Error occurred during billing setup"); } - finishSetUp(listener, iabResult, appstore); } @@ -450,9 +445,88 @@ public void onServiceDisconnected(final ComponentName name) {} private void setUp(final IabHelper.OnIabSetupFinishedListener listener) { // Look for appropriate open store - for (final ServiceInfo serviceInfo : queryOpenStoreServices()) { - final Intent bindServiceIntent = new Intent(BIND_INTENT); + final List serviceInfos = queryOpenStoreServices(); + if (serviceInfos.isEmpty()) { + // No open stores available, fall back to wrappers + checkWrappers(listener); + return; } + final Queue bindServiceIntents = new LinkedList(); + for (final ServiceInfo serviceInfo : serviceInfos) { + bindServiceIntents.add(getBindServiceIntent(serviceInfo)); + } + + checkOpenStores(listener, bindServiceIntents); + } + + private void checkOpenStores(final IabHelper.OnIabSetupFinishedListener listener, + final Queue bindServiceIntents) { + final ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + Appstore appstore = null; + try { + appstore = checkOpenStoreService(name, service, this); + } catch (RemoteException ignore) {} + if (appstore != null && versionOk(appstore)) { + // Found open store + final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + finishSetUp(listener, iabResult, appstore); + return; + } + // Check another store + checkOpenStores(listener, bindServiceIntents); + } + + @Override + public void onServiceDisconnected(final ComponentName name) {} + }; + + while (!bindServiceIntents.isEmpty()) { + if (context.bindService(bindServiceIntents.poll(), serviceConnection, Context.BIND_AUTO_CREATE)) { + return; + } + } + + // No suitable open store found, fall back to wrappers + checkWrappers(listener); + } + + private Appstore checkOpenStoreService(final ComponentName name, + final IBinder service, + final ServiceConnection serviceConnection) + throws RemoteException { + final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); + if (!openAppstoreService.isBillingAvailable(context.getPackageName())) { + return null; + } + return getOpenAppstore(name, serviceConnection, openAppstoreService); + } + + private void checkWrappers(final IabHelper.OnIabSetupFinishedListener listener) { + for (final Wrapper wrapper : Wrapper.values()) { + final Appstore appstore = wrapper.createInstance(context); + final String packageName = context.getPackageName(); + if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { + // Found suitable wrapper + final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + finishSetUp(listener, iabResult, appstore); + return; + } + } + + // No suitable store found + final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); + finishSetUp(listener, iabResult, null); + } + + private boolean versionOk(@NotNull final Appstore appstore) { + final String packageName = context.getPackageName(); + int versionCode = Appstore.PACKAGE_VERSION_UNDEFINED; + try { + versionCode = context.getPackageManager().getPackageInfo(packageName, 0).versionCode; + } catch (NameNotFoundException ignore) {} + return appstore.getPackageVersion(packageName) >= versionCode; } private List queryOpenStoreServices() { @@ -466,6 +540,7 @@ private List queryOpenStoreServices() { return (List) Collections.unmodifiableCollection(serviceInfos); } + @Nullable private OpenAppstore getOpenAppstore(final ComponentName name, final ServiceConnection serviceConnection, final IOpenAppstore openAppstoreService) @@ -496,6 +571,12 @@ private OpenAppstore getOpenAppstore(final ComponentName name, return openAppstore; } + private Intent getBindServiceIntent(final ServiceInfo serviceInfo) { + final Intent bindServiceIntent = new Intent(BIND_INTENT); + bindServiceIntent.setClassName(serviceInfo.packageName, serviceInfo.name); + return bindServiceIntent; + } + private void finishSetUp(@NotNull final IabHelper.OnIabSetupFinishedListener listener, @NotNull final IabResult iabResult, @Nullable final Appstore appstore) { From 32c8732bc2f5ed368e1c91c1af19f80fc2c487b5 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Wed, 10 Sep 2014 03:29:54 +0400 Subject: [PATCH 18/72] Major OpenIabHelper refactoring; New store picking algorithm implemented --- .../java/org/onepf/oms/OpenIabHelper.java | 855 +++++++----------- .../org/onepf/oms/appstore/GooglePlay.java | 21 +- .../org/onepf/oms/appstore/SamsungApps.java | 8 +- .../main/java/org/onepf/oms/util/Utils.java | 9 + 4 files changed, 379 insertions(+), 514 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index f77ca29f..14d65fe7 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -17,21 +17,26 @@ package org.onepf.oms; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; -import java.util.Random; +import java.util.Set; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.onepf.oms.appstore.AmazonAppstore; +import org.onepf.oms.appstore.GooglePlay; import org.onepf.oms.appstore.NokiaStore; import org.onepf.oms.appstore.OpenAppstore; import org.onepf.oms.appstore.SamsungApps; @@ -86,13 +91,14 @@ public class OpenIabHelper { * Used for all communication with Android services */ private final Context context; + private final PackageManager packageManager; /** * Necessary to initialize SamsungApps. For other stuff {@link #context} is used */ private Activity activity; - private static final Handler notifyHandler = new Handler(Looper.getMainLooper()); + private final Handler handler = new Handler(Looper.getMainLooper()); /** * selected appstore @@ -139,53 +145,66 @@ public class OpenIabHelper { public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3; public static final int BILLING_RESPONSE_RESULT_ERROR = 6; + // Known wrappers public static final String NAME_GOOGLE = "com.google.play"; public static final String NAME_AMAZON = "com.amazon.apps"; public static final String NAME_SAMSUNG = "com.samsung.apps"; public static final String NAME_NOKIA = "com.nokia.nstore"; + // Knows open stores public static final String NAME_YANDEX = "com.yandex.store"; public static final String NAME_APPLAND = "Appland"; public static final String NAME_SLIDEME = "SlideME"; public static final String NAME_APTOIDE = "cm.aptoide.pt"; - private static enum Wrapper { - Google("com.google.play") { + private static interface AppstoreFactory{ + Appstore get(); + } + + private final Map appstorePackageMap = new HashMap(); + private final Map appstoreFactoryMap = new HashMap(); + + { + // Known packages for open stores + appstorePackageMap.put("com.yandex.store", NAME_YANDEX); + appstorePackageMap.put("cm.aptoide.pt", NAME_APTOIDE); + + + appstorePackageMap.put("com.google.play", NAME_GOOGLE); + appstoreFactoryMap.put(NAME_GOOGLE, new AppstoreFactory() { @Override - Appstore createInstance(final Context context) { - return super.createInstance(context); + public Appstore get() { + final String googleKey = options.getStoreKey(NAME_GOOGLE); + return new GooglePlay(context, googleKey); } - }, - Amazon("com.amazon.apps") { + }); + + appstorePackageMap.put("com.amazon.apps", NAME_AMAZON); + appstoreFactoryMap.put(NAME_AMAZON, new AppstoreFactory() { @Override - Appstore createInstance(final Context context) { - return super.createInstance(context); + public Appstore get() { + return new AmazonAppstore(context); } - }, - Samsung("com.samsung.apps") { + }); + + appstorePackageMap.put("com.samsung.apps", NAME_SAMSUNG); + appstoreFactoryMap.put(NAME_SAMSUNG, new AppstoreFactory() { @Override - Appstore createInstance(final Context context) { - return super.createInstance(context); + public Appstore get() { + return new SamsungApps(activity, options); } - }, - Nokia("com.nokia.nstore") { + }); + + appstorePackageMap.put("com.nokia.nstore", NAME_NOKIA); + appstoreFactoryMap.put(NAME_NOKIA, new AppstoreFactory() { @Override - Appstore createInstance(final Context context) { - return super.createInstance(context); + public Appstore get() { + return new NokiaStore(context); } - }; - - Wrapper(final String wrappedPackage) { - this.wrappedPackage = wrappedPackage; - } - - private final String wrappedPackage; - - Appstore createInstance(final Context context) { - return null; - } + }); } + /** * @param sku - application inner SKU * @param storeSku - shouldn't duplicate already mapped values @@ -326,15 +345,18 @@ public OpenIabHelper(Context context, Map storeKeys, String[] pr */ public OpenIabHelper(Context context, Options options) { this.context = context.getApplicationContext(); + packageManager = context.getPackageManager(); this.options = options; if (context instanceof Activity) { this.activity = (Activity) context; } - checkSettings(options, context); + checkOptions(); Logger.init(); } + private Executor setupThreadPoolExecutor; + /** * Discover all available stores and select the best billing service. *

@@ -351,131 +373,132 @@ public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { } Logger.init(); setupState = SETUP_IN_PROGRESS; + setupThreadPoolExecutor = Executors.newSingleThreadExecutor(); final String packageName = context.getPackageName(); - final String packageInstaller = context.getPackageManager().getInstallerPackageName(packageName); + final String packageInstaller = packageManager.getInstallerPackageName(packageName); if (TextUtils.isEmpty(packageInstaller)) { // Package installer is not set - setUp(listener); + setup(listener); } else { - setUpForPackage(listener, packageInstaller); + setupForPackage(listener, packageInstaller); } } - private void setUpForPackage(final IabHelper.OnIabSetupFinishedListener listener, final String packageInstaller) { - final PackageManager packageManager = context.getPackageManager(); - boolean packageInstalled; - try { - packageManager.getPackageInfo(packageInstaller, PackageManager.GET_ACTIVITIES); - packageInstalled = true; - } catch (NameNotFoundException exception) { - packageInstalled = false; - } - - if (!packageInstalled) { + private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener, final String packageInstaller) { + if (!Utils.packageInstalled(context, packageInstaller)) { // Package installer no longer exist, fallback to default algorithm - setUp(listener); + setup(listener); return; } - // Package installer exist - Appstore appstore = null; - for (final Wrapper wrapper : Wrapper.values()) { - if (TextUtils.equals(packageInstaller, wrapper.wrappedPackage)){ - appstore = wrapper.createInstance(context); - break; + final String appstoreName = appstorePackageMap.get(packageInstaller); + if (!TextUtils.isEmpty(appstoreName)) { + final Appstore appstore; + // Package installer is a known appstore + if (!CollectionUtils.isEmpty(options.getAvailableStores())) { + // Check available stores + appstore = options.getAvailableStoreWithName(appstoreName); + } else { + // Check all available wrappers + appstore = appstoreFactoryMap.get(appstoreName).get(); } - } - - final IabResult iabResult; - if (appstore != null) { - // We've got a wrapper for package installer - if (!appstore.isBillingAvailable(packageInstaller)) { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); + if (appstore != null) { + checkBillingAndFinish(listener, appstore); } else { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + finishSetupNoStore(listener); } - finishSetUp(listener, iabResult, appstore); + // Either we found appstore or not, we know it was a wrapper + // Our work here is done return; } - Intent bindServiceIntent = null; + // Package installer is unknown // Look for package installer among available open stores + Intent bindServiceIntent = null; for (final ServiceInfo serviceInfo : queryOpenStoreServices()) { final String servicePackage = serviceInfo.packageName; if (TextUtils.equals(packageInstaller, servicePackage)) { bindServiceIntent = getBindServiceIntent(serviceInfo); + break; } } - - if (bindServiceIntent == null) { - // No intent for open store service, fallback to default algorithm - setUp(listener); - return; - } - - // We've got an open store as package installer - final ServiceConnection serviceConnection = new ServiceConnection() { - // Open store is available - @Override - public void onServiceConnected(final ComponentName name, final IBinder service) { - IabResult iabResult; - Appstore appstore = null; - try { - appstore = checkOpenStoreService(name, service, this); - if (appstore != null) { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); - } else { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); - } - } catch (RemoteException e) { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Error occurred during billing setup"); + if (bindServiceIntent != null) { + // We've got an open store as package installer + final ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + checkBillingAndFinish(listener, name, service, this); } - finishSetUp(listener, iabResult, appstore); - } - @Override - public void onServiceDisconnected(final ComponentName name) {} - }; - if (!context.bindService(bindServiceIntent, serviceConnection , Context.BIND_AUTO_CREATE)) { - // Unable to connect to open store service - finishSetUp(listener, new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Can't bind to service"), null); + @Override + public void onServiceDisconnected(final ComponentName name) {} + }; + if (context.bindService(bindServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE)) { + // We fount suitable open store, wait for service to connect + return; + } } + + // Appstore is unknown to this library + finishSetupNoStore(listener); } - private void setUp(final IabHelper.OnIabSetupFinishedListener listener) { + private void setup(final IabHelper.OnIabSetupFinishedListener listener) { // Look for appropriate open store final List serviceInfos = queryOpenStoreServices(); if (serviceInfos.isEmpty()) { // No open stores available, fall back to wrappers - checkWrappers(listener); + checkWrappersAndFinish(listener); return; } + // TODO check preferred stores + // TODO handle check inventory final Queue bindServiceIntents = new LinkedList(); for (final ServiceInfo serviceInfo : serviceInfos) { bindServiceIntents.add(getBindServiceIntent(serviceInfo)); } - checkOpenStores(listener, bindServiceIntents); + checkOpenStoresThenWrappers(listener, bindServiceIntents); } - private void checkOpenStores(final IabHelper.OnIabSetupFinishedListener listener, - final Queue bindServiceIntents) { + private void checkOpenStoresThenWrappers(final IabHelper.OnIabSetupFinishedListener listener, + final Queue bindServiceIntents) { final ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(final ComponentName name, final IBinder service) { - Appstore appstore = null; - try { - appstore = checkOpenStoreService(name, service, this); - } catch (RemoteException ignore) {} - if (appstore != null && versionOk(appstore)) { - // Found open store - final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); - finishSetUp(listener, iabResult, appstore); - return; - } - // Check another store - checkOpenStores(listener, bindServiceIntents); + final ServiceConnection serviceConnection = this; + setupThreadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + Runnable finishRunnable = null; + Appstore openStore = null; + try { + openStore = checkOpenStoreBilling(name, service, serviceConnection); + } catch (RemoteException ignore) {} + if (openStore != null) { + // Found open store + final Appstore appstore = openStore; + final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + finishRunnable = new Runnable() { + @Override + public void run() { + finishSetup(listener, iabResult, appstore); + } + }; + } + if (finishRunnable == null) { + finishRunnable = new Runnable() { + @Override + public void run() { + // Check another store + checkOpenStoresThenWrappers(listener, bindServiceIntents); + } + }; + } + handler.post(finishRunnable); + } + }); } @Override @@ -489,44 +512,40 @@ public void onServiceDisconnected(final ComponentName name) {} } // No suitable open store found, fall back to wrappers - checkWrappers(listener); + checkWrappersAndFinish(listener); } - private Appstore checkOpenStoreService(final ComponentName name, - final IBinder service, - final ServiceConnection serviceConnection) - throws RemoteException { - final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); - if (!openAppstoreService.isBillingAvailable(context.getPackageName())) { - return null; + private void checkWrappersAndFinish(final IabHelper.OnIabSetupFinishedListener listener) { + // List of wrappers to check + final Set appstores = new LinkedHashSet(); + for (final String name : options.getPreferredStoreNames()) { + // Add available stored according to preferred stores priority + final Appstore appstore = options.getAvailableStoreWithName(name); + if (appstore != null) { + appstores.add(appstore); + } } - return getOpenAppstore(name, serviceConnection, openAppstoreService); - } - - private void checkWrappers(final IabHelper.OnIabSetupFinishedListener listener) { - for (final Wrapper wrapper : Wrapper.values()) { - final Appstore appstore = wrapper.createInstance(context); - final String packageName = context.getPackageName(); - if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { - // Found suitable wrapper - final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); - finishSetUp(listener, iabResult, appstore); - return; + final List availableStores; + if (!CollectionUtils.isEmpty(availableStores = options.getAvailableStores())) { + // Use only stores specified explicitly + appstores.addAll(availableStores); + } else { + // Add all available wrappers + for (final String name : appstoreFactoryMap.keySet()) { + // No need to instantiate appstore if it's already added + boolean alreadyAdded = false; + for (final Appstore appstore : appstores) { + if (TextUtils.equals(appstore.getAppstoreName(), name)) { + alreadyAdded = true; + break; + } + } + if (!alreadyAdded) { + appstores.add(appstoreFactoryMap.get(name).get()); + } } } - - // No suitable store found - final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing unavailable"); - finishSetUp(listener, iabResult, null); - } - - private boolean versionOk(@NotNull final Appstore appstore) { - final String packageName = context.getPackageName(); - int versionCode = Appstore.PACKAGE_VERSION_UNDEFINED; - try { - versionCode = context.getPackageManager().getPackageInfo(packageName, 0).versionCode; - } catch (NameNotFoundException ignore) {} - return appstore.getPackageVersion(packageName) >= versionCode; + checkBillingAndFinish(listener, appstores); } private List queryOpenStoreServices() { @@ -546,13 +565,12 @@ private OpenAppstore getOpenAppstore(final ComponentName name, final IOpenAppstore openAppstoreService) throws RemoteException { final String appstoreName = openAppstoreService.getAppstoreName(); - final Intent billingIntent = openAppstoreService.getBillingServiceIntent(); - - if (appstoreName == null) { // no name - no service + if (TextUtils.isEmpty(appstoreName)) { // no name - no service Logger.e("discoverOpenStores() Appstore doesn't have name. Skipped. ComponentName: ", name); return null; } + final Intent billingIntent = openAppstoreService.getBillingServiceIntent(); if (billingIntent == null) { Logger.d("discoverOpenStores(): billing is not supported by store: ", name); return null; @@ -577,7 +595,118 @@ private Intent getBindServiceIntent(final ServiceInfo serviceInfo) { return bindServiceIntent; } - private void finishSetUp(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + private Appstore checkOpenStoreBilling(final ComponentName name, + final IBinder service, + final ServiceConnection serviceConnection) + throws RemoteException { + if (Utils.uiThread()) { + throw new IllegalStateException("Must not be called from UI thread"); + } + final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); + // Check billing and create Appstore object for open store + Appstore openAppstore = null; + if (openAppstoreService.isBillingAvailable(context.getPackageName())) { + openAppstore = getOpenAppstore(name, serviceConnection, openAppstoreService); + } + if (openAppstore != null && versionOk(openAppstore)) { + return openAppstore; + } + // Billing is unavailable for this open store + return null; + } + + private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + final ComponentName name, + final IBinder service, + final ServiceConnection serviceConnection){ + setupThreadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + Appstore openAppstore = null; + int iabResultResponse; + String iabResultMessage; + try { + openAppstore = checkOpenStoreBilling(name, service, serviceConnection); + if (openAppstore != null) { + iabResultResponse = BILLING_RESPONSE_RESULT_OK; + iabResultMessage = "Setup ok"; + } else { + iabResultResponse = BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE; + iabResultMessage = "Billing unavailable for open store"; + } + } catch (RemoteException e) { + iabResultResponse = BILLING_RESPONSE_RESULT_ERROR; + iabResultMessage = "Error occurred during billing setup"; + } + final IabResult iabResult = new IabResult(iabResultResponse, iabResultMessage); + final Appstore appstore = openAppstore; + handler.post(new Runnable() { + @Override + public void run() { + finishSetup(listener, iabResult, appstore); + } + }); + } + }); + } + + private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + @NotNull final Appstore appstore) { + checkBillingAndFinish(listener, Arrays.asList(appstore)); + } + + private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + @NotNull final Collection appstores) { + final String packageName = context.getPackageName(); + if (appstores.isEmpty()) { + finishSetupNoStore(listener); + return; + } + setupThreadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + Runnable finishRunnable = null; + for (final Appstore appstore : appstores) { + final IabResult iabResult; + if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { + iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + finishRunnable = new Runnable() { + @Override + public void run() { + finishSetup(listener, iabResult, appstore); + } + }; + break; + } + } + if (finishRunnable == null) { + finishRunnable = new Runnable() { + @Override + public void run() { + finishSetupNoStore(listener); + } + }; + } + handler.post(finishRunnable); + } + }); + } + + private boolean versionOk(@NotNull final Appstore appstore) { + final String packageName = context.getPackageName(); + int versionCode = Appstore.PACKAGE_VERSION_UNDEFINED; + try { + versionCode = context.getPackageManager().getPackageInfo(packageName, 0).versionCode; + } catch (NameNotFoundException ignore) {} + return appstore.getPackageVersion(packageName) >= versionCode; + } + + private void finishSetupNoStore(@NotNull final IabHelper.OnIabSetupFinishedListener listener) { + final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "No suitable appstore was found"); + finishSetup(listener, iabResult, null); + } + + private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener, @NotNull final IabResult iabResult, @Nullable final Appstore appstore) { if (!Utils.uiThread()) { @@ -589,6 +718,7 @@ private void finishSetUp(@NotNull final IabHelper.OnIabSetupFinishedListener lis final boolean setUpSuccessful = iabResult.isSuccess(); setupState = setUpSuccessful ? SETUP_RESULT_SUCCESSFUL : SETUP_RESULT_FAILED; + setupThreadPoolExecutor = null; if (setUpSuccessful) { if (appstore == null) { throw new IllegalStateException("Appstore can't be null if setup is successful"); @@ -596,108 +726,10 @@ private void finishSetUp(@NotNull final IabHelper.OnIabSetupFinishedListener lis mAppstore = appstore; mAppstoreBillingService = appstore.getInAppBillingService(); } - Logger.dWithTimeFromUp("finishSetUp() === SETUP DONE === result: ", iabResult, " Appstore: ", appstore); + Logger.dWithTimeFromUp("finishSetup() === SETUP DONE === result: ", iabResult, " Appstore: ", appstore); listener.onIabSetupFinished(iabResult); } -// new Thread(new Runnable() { -// public void run() { -// List stores2check = new ArrayList(); -// if (options.availableStores != null) { -// stores2check.addAll(options.availableStores); -// } else { // if appstores are not specified by user - lookup for all available stores -// final List openStores = discoverOpenStores(context, null, options); -// Logger.dWithTimeFromUp("startSetup() discovered openstores: ", openStores.toString()); -// stores2check.addAll(openStores); -// if (options.getVerifyMode() == Options.VERIFY_EVERYTHING && !options.hasStoreKey(NAME_GOOGLE)) { -// // don't work with GooglePlay if verifyMode is strict and no publicKey provided -// } else { -// final String publicKey = options.verifyMode == Options.VERIFY_SKIP ? null -// : options.storeKeys.get(OpenIabHelper.NAME_GOOGLE); -// stores2check.add(new GooglePlay(context, publicKey)); -// } -// -// // try AmazonApps if in-app-purchasing.jar with Amazon SDK is compiled with app -// try { -// OpenIabHelper.class.getClassLoader().loadClass("com.amazon.inapp.purchasing.PurchasingManager"); -// stores2check.add(new AmazonAppstore(context)); -// } catch (ClassNotFoundException ignored) { -// } -// -// if (!CollectionUtils.isEmpty(SkuManager.getInstance().getAllStoreSkus(NAME_SAMSUNG))) { -// // SamsungApps shows lot of UI stuff during init -// // try it only if samsung SKUs are specified -// stores2check.add(new SamsungApps(activity, options)); -// } -// //Nokia TODO change logic -// stores2check.add(new NokiaStore(context)); -// if (!Utils.hasRequestedPermission(context, NokiaStore.NOKIA_BILLING_PERMISSION)) { -// Logger.w("Required permission \"" + -// NokiaStore.NOKIA_BILLING_PERMISSION + "\" NOT REQUESTED"); -// } -// } -// -// //todo get rid of this, DO NOT save anything to fields! -// for (Appstore store : stores2check) { -// if (store instanceof SamsungApps) { -// samsungInSetup = (SamsungApps) store; -// break; -// } -// } -// -// IabResult result = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "Billing isn't supported"); -// -// if (options.checkInventory) { -// -// final List equippedStores = checkInventory(stores2check); -// -// if (!equippedStores.isEmpty()) { -// mAppstore = selectBillingService(equippedStores); -// } -// -// Logger.dWithTimeFromUp("select equipped"); -// if (mAppstore != null) { -// final String message = "Successfully initialized with existing inventory: " + mAppstore.getAppstoreName(); -// result = new IabResult(BILLING_RESPONSE_RESULT_OK, message); -// Logger.d(message); -// } else { -// // found no equipped stores. Select store based on store parameters -// mAppstore = selectBillingService(stores2check); -// Logger.dWithTimeFromUp("select non-equipped"); -// if (mAppstore != null) { -// final String message = "Successfully initialized with non-equipped store: " + mAppstore.getAppstoreName(); -// result = new IabResult(BILLING_RESPONSE_RESULT_OK, message); -// Logger.d(message); -// } -// } -// if (mAppstore != null) { -// mAppstoreBillingService = mAppstore.getInAppBillingService(); -// } -// fireSetupFinished(listener, result); -// } else { // no inventory check. Select store based on store parameters -// mAppstore = selectBillingService(stores2check); -// if (mAppstore != null) { -// mAppstoreBillingService = mAppstore.getInAppBillingService(); -// mAppstoreBillingService.startSetup(new OnIabSetupFinishedListener() { -// public void onIabSetupFinished(IabResult result) { -// fireSetupFinished(listener, result); -// } -// }); -// } else { -// fireSetupFinished(listener, result); -// } -// } -// for (Appstore store : stores2check) { -// if (store != mAppstore && store.getInAppBillingService() != null) { -// store.getInAppBillingService().dispose(); -// Logger.dWithTimeFromUp("startSetup() disposing ", store.getAppstoreName()); -// } -// } -// } -// }, "openiab-setup").start(); -// } - - @MagicConstant(intValues = {SETUP_DISPOSED, SETUP_IN_PROGRESS, SETUP_RESULT_FAILED, SETUP_RESULT_NOT_STARTED, SETUP_RESULT_SUCCESSFUL}) public int getSetupState() { @@ -717,8 +749,12 @@ public synchronized String getConnectedAppstoreName() { /** * Check options are valid */ - public static void checkOptions(Options options) { - if (options.verifyMode != Options.VERIFY_SKIP && options.storeKeys != null) { // check publicKeys. Must be not null and valid + public void checkOptions() { + checkSamsung(); + checkNokia(); + // TODO handle verify + // check publicKeys. Must be not null and valid + if (options.verifyMode != Options.VERIFY_SKIP && options.storeKeys != null) { for (Entry entry : options.storeKeys.entrySet()) { if (entry.getValue() == null) { throw new IllegalArgumentException("Null publicKey for store: " + entry.getKey() + ", key: " + entry.getValue()); @@ -734,30 +770,41 @@ public static void checkOptions(Options options) { } } - private static void checkSettings(Options options, Context context) { - checkOptions(options); - checkSamsung(context); - checkNokia(options, context); - } - - private static void checkNokia(Options options, Context context) { - if (options.hasAvailableStoreWithName(NAME_NOKIA) - && !Utils.hasRequestedPermission(context, NokiaStore.NOKIA_BILLING_PERMISSION)) { + private void checkNokia() { + final boolean hasPermission = Utils.hasRequestedPermission(context, NokiaStore.NOKIA_BILLING_PERMISSION); + Logger.d("checkNokia() has permission : ", hasPermission); + if (hasPermission) { + return; + } + if (options.getAvailableStoreWithName(NAME_NOKIA) != null + || options.hasPreferredStoreName(NAME_NOKIA)) { throw new IllegalStateException("Nokia permission \"" + NokiaStore.NOKIA_BILLING_PERMISSION + "\" NOT REQUESTED"); } + Logger.d("checkNokia() ignoring Nokia wrapper"); + appstoreFactoryMap.remove(NAME_NOKIA); } - //todo move to Utils - private static void checkPermission(Context context, String paramString, StringBuilder builder) { - if (context.checkCallingOrSelfPermission(paramString) != PackageManager.PERMISSION_GRANTED) { - if (builder.length() > 0) { - builder.append('\n'); - } - builder.append(String.format(" - Required permission \"%s\" is NOT granted.", paramString)); + private void checkSamsung() { + Logger.d("checkSamsung() activity is : ", activity); + if (activity != null) { + return; } + if (options.getAvailableStoreWithName(NAME_SAMSUNG) != null + || options.hasPreferredStoreName(NAME_SAMSUNG)) { + // Unfortunately, SamsungApps requires to launch their own "Certification Activity" + // in order to connect to billing service. So it's also needed for OpenIAB. + // + // Intance of Activity needs to be passed to OpenIAB constructor to launch + // Samsung Cerfitication Activity. + // Activity also need to pass activityResult to OpenIABHelper.handleActivityResult() + throw new IllegalArgumentException("You must supply Activity object as context in order to use " + NAME_SAMSUNG + " store"); + } + Logger.d("checkSamsung() ignoring Samsung wrapper"); + appstoreFactoryMap.remove(NAME_SAMSUNG); } + //todo move to Utils private static void formatComponentStatus(String message, StringBuilder messageBuilder) { if (messageBuilder.length() > 0) { @@ -766,142 +813,6 @@ private static void formatComponentStatus(String message, StringBuilder messageB messageBuilder.append(message); } - - private static void checkSamsung(Context context) { - Collection allStoreSkus = SkuManager.getInstance().getAllStoreSkus(OpenIabHelper.NAME_SAMSUNG); - if (!CollectionUtils.isEmpty(allStoreSkus)) { // it means that Samsung is among the candidates - - if (!(context instanceof Activity)) { - // - // Unfortunately, SamsungApps requires to launch their own "Certification Activity" - // in order to connect to billing service. So it's also needed for OpenIAB. - // - // Because of SKU for SamsungApps are specified, - // intance of Activity needs to be passed to OpenIAB constructor to launch - // Samsung Cerfitication Activity. - // Activity also need to pass activityResult to OpenIABHelper.handleActivityResult() - // - // - throw new IllegalArgumentException( - "\n " - + "\nContext is not instance of Activity." - + "\nUnfortunately, SamsungApps requires to launch their own Certification Activity " - + "\nin order to connect to billing service. So it's also needed for OpenIAB." - + "\n " - + "\nBecause of SKU for SamsungApps are specified, instance of Activity needs to be passed " - + "\nto OpenIAB constructor to launch Samsung Cerfitication Activity." - + "\nActivity should call OpenIabHelper#handleActivityResult()." - + "\n "); - } - } - } - - protected void fireSetupFinished(final IabHelper.OnIabSetupFinishedListener listener, final IabResult result) { - if (setupState == SETUP_DISPOSED) { - return; - } - - if (mAppstore != null) { - Logger.dWithTimeFromUp("fireSetupFinished() === SETUP DONE === result: ", result, ", appstore: ", - mAppstore.getAppstoreName()); - } else { - Logger.dWithTimeFromUp("fireSetupFinished() === SETUP DONE === result: ", result); - } - - samsungInSetup = null; - setupState = result.isSuccess() ? SETUP_RESULT_SUCCESSFUL : SETUP_RESULT_FAILED; - notifyHandler.post(new Runnable() { - public void run() { - listener.onIabSetupFinished(result); - } - }); - } - - /** - * Discover all OpenStore services, checks them and build {@link org.onepf.oms.OpenIabHelper.Options#getAvailableStores()} list
- *

- * Lock current thread for {@link org.onepf.oms.OpenIabHelper.Options#getDiscoveryTimeout()}
- * Must not be called from main thread to avoid service connection blocking - * - * @param dest - discovered OpenStores will be added here. If null new List() will be created - * @param options - settings for Appstore discovery like verifyMode and timeouts - * @return dest or new List with discovered Appstores - */ - public static List discoverOpenStores(final Context context, final List dest, final Options options) { - if (Thread.currentThread().equals(Looper.getMainLooper().getThread())) { - throw new IllegalStateException("Must not be called from main thread. " - + "Service interaction will be blocked"); - } - PackageManager packageManager = context.getPackageManager(); - final Intent intentAppstoreServices = new Intent(BIND_INTENT); - List infoList = packageManager.queryIntentServices(intentAppstoreServices, 0); - final List result = dest != null ? dest : new ArrayList(infoList != null ? infoList.size() : 0); - if (infoList == null || infoList.isEmpty()) { - return result; - } - final CountDownLatch storesToCheck = new CountDownLatch(infoList.size()); - for (ResolveInfo info : infoList) { - String packageName = info.serviceInfo.packageName; - String name = info.serviceInfo.name; - Intent intentAppstore = new Intent(intentAppstoreServices); - intentAppstore.setClassName(packageName, name); - try { - boolean isBound = context.bindService(intentAppstore, new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - Logger.d("discoverOpenStores() appstoresService connected for component: ", name.flattenToShortString()); - IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); - - try { - String appstoreName = openAppstoreService.getAppstoreName(); - Intent billingIntent = openAppstoreService.getBillingServiceIntent(); - if (appstoreName == null) { // no name - no service - Logger.e("discoverOpenStores() Appstore doesn't have name. Skipped. ComponentName: ", name); - } else if (billingIntent == null) { // don't handle stores without billing support - Logger.d("discoverOpenStores(): billing is not supported by store: ", name); - } else if ((options.verifyMode == Options.VERIFY_EVERYTHING) && !options.hasStoreKey(appstoreName)) { - // don't connect to OpenStore if no key provided and verification is strict - Logger.e("discoverOpenStores() verification is required but publicKey is not provided: ", name); - } else { - String publicKey = options.getStoreKey(appstoreName); - if (options.verifyMode == Options.VERIFY_SKIP) publicKey = null; - final OpenAppstore openAppstore = new OpenAppstore(context, appstoreName, openAppstoreService, billingIntent, publicKey, this); - openAppstore.componentName = name; - Logger.e("discoverOpenStores() add new OpenStore: ", openAppstore); - synchronized (result) { - if (!result.contains(openAppstore)) { - result.add(openAppstore); - } - } - } - } catch (RemoteException e) { - Logger.e(e, "discoverOpenStores() ComponentName: ", name); - } - storesToCheck.countDown(); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - Logger.d("onServiceDisconnected() appstoresService disconnected for component: ", name.flattenToShortString()); - //Nothing to do here - } - }, Context.BIND_AUTO_CREATE); - if (!isBound) { - storesToCheck.countDown(); - } - } catch (SecurityException e) { - Logger.e(e, "bindService() failed for ", packageName); - storesToCheck.countDown(); - } - } - try { - storesToCheck.await(options.discoveryTimeoutMs, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Logger.e(e, "Interrupted: discovering OpenStores."); - } - return result; - } - /** * Connects to Billing Service of each store. Request list of user purchases (inventory) * @@ -961,82 +872,14 @@ public void run() { return equippedStores; } - /** - * Lookup for requested service in store based on isPackageInstaller() & isBillingAvailable() - *

- * Scenario: - *

  • - * - look for installer: if exists and supports billing service - we done
  • - * - rest of stores who support billing considered as candidates

  • - *

    - * - find candidate according to [prefferedStoreNames]. if found - we done

  • - *

    - * - select candidate randomly from 3 groups based on published package version

  • - * - published version == app.versionCode
  • - * - published version > app.versionCode
  • - * - published version < app.versionCode - */ - protected Appstore selectBillingService(final List availableStores) { - String packageName = context.getPackageName(); - // candidates: - Map candidates = new HashMap(); - // - for (Appstore appstore : availableStores) { - if (appstore.isBillingAvailable(packageName)) { - candidates.put(appstore.getAppstoreName(), appstore); - } else { - continue; // for billing we cannot select store without billing - } - if (appstore.isPackageInstaller(packageName)) { - return appstore; - } - } - if (candidates.isEmpty()) { - return null; - } - - // lookup for developer preffered stores - if (options.prefferedStoreNames != null) { - for (int i = 0; i < options.prefferedStoreNames.length; i++) { - Appstore candidate = candidates.get(options.prefferedStoreNames[i]); - if (candidate != null) { - return candidate; - } - } - } - // nothing found. select something that matches package version - int versionCode = Appstore.PACKAGE_VERSION_UNDEFINED; - try { - versionCode = context.getPackageManager().getPackageInfo(packageName, 0).versionCode; - } catch (NameNotFoundException e) { - Logger.e(e, "Are we installed?"); - } - List sameVersion = new ArrayList(); - List higherVersion = new ArrayList(); - for (Appstore candidate : candidates.values()) { - final int storeVersion = candidate.getPackageVersion(packageName); - if (storeVersion == versionCode) { - sameVersion.add(candidate); - } else if (storeVersion > versionCode) { - higherVersion.add(candidate); - } - } - // use random if found stores with same version of package - if (!sameVersion.isEmpty()) { - return sameVersion.get(new Random().nextInt(sameVersion.size())); - } else if (!higherVersion.isEmpty()) { // or one of higher version - return higherVersion.get(new Random().nextInt(higherVersion.size())); - } else { // ok, return no matter what - return new ArrayList(candidates.values()) - .get(new Random().nextInt(candidates.size())); - } - } - public void dispose() { Logger.d("Disposing."); if (mAppstoreBillingService != null) { mAppstoreBillingService.dispose(); } + mAppstore = null; + mAppstoreBillingService = null; + activity = null; setupState = SETUP_DISPOSED; } @@ -1166,7 +1009,7 @@ public void run() { final IabResult result_f = result; final Inventory inv_f = inv; if (setupState != SETUP_DISPOSED) { - notifyHandler.post(new Runnable() { + handler.post(new Runnable() { public void run() { listener.onQueryInventoryFinished(result_f, inv_f); } @@ -1253,14 +1096,14 @@ public void run() { flagEndAsync(); if (setupState != SETUP_DISPOSED && singleListener != null) { - notifyHandler.post(new Runnable() { + handler.post(new Runnable() { public void run() { singleListener.onConsumeFinished(purchases.get(0), results.get(0)); } }); } if (setupState != SETUP_DISPOSED && multiListener != null) { - notifyHandler.post(new Runnable() { + handler.post(new Runnable() { public void run() { multiListener.onConsumeMultiFinished(purchases, results); } @@ -1393,9 +1236,19 @@ public static class Options { *

    * If you put only your instance of Appstore in this list OpenIAB will use it *

    - * TODO: consider to use AppstoreFactory.get(storeName) -> Appstore instance */ - public List availableStores; + // TODO discover open stores + public List availableStores = Collections.emptyList(); + + /** + * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getPreferredStoreNames()} + * Will be private since 1.0. + *

    + *

    + * Used as priority list if store that installed app is not found and there are + * multiple stores installed on device that supports billing. + */ + public List preferredStoreNames = Collections.emptyList(); /** * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getDiscoveryTimeout()} @@ -1404,6 +1257,7 @@ public static class Options { *

    * Wait specified amount of ms to find all OpenStores on device */ + // TODO remove? public int discoveryTimeoutMs = DEFAULT_DISCOVER_TIMEOUT; /** @@ -1460,16 +1314,6 @@ public static class Options { */ public Map storeKeys = new HashMap(); - /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getPreferredStoreNames()} - * Will be private since 1.0. - *

    - *

    - * Used as priority list if store that installed app is not found and there are - * multiple stores installed on device that supports billing. - */ - public String[] prefferedStoreNames = new String[]{}; - /** * @deprecated Usr {@link org.onepf.oms.OpenIabHelper.Options#getSamsungCertificationRequestCode()} * Will be private since 1.0. @@ -1492,14 +1336,14 @@ private Options(List availableStores, int checkInventoryTimeout, int discoveryTimeout, @MagicConstant(intValues = {VERIFY_EVERYTHING, VERIFY_ONLY_KNOWN, VERIFY_SKIP}) int verifyMode, - String[] preferredStoreNames, + List preferredStoreNames, int samsungCertificationRequestCode) { this.checkInventory = checkInventory; this.checkInventoryTimeoutMs = checkInventoryTimeout; this.availableStores = availableStores; this.discoveryTimeoutMs = discoveryTimeout; this.storeKeys = storeKeys; - this.prefferedStoreNames = preferredStoreNames; + this.preferredStoreNames = preferredStoreNames; this.verifyMode = verifyMode; this.samsungCertificationRequestCode = samsungCertificationRequestCode; } @@ -1513,15 +1357,6 @@ public int getSamsungCertificationRequestCode() { return samsungCertificationRequestCode; } - /** - * Used as priority list if store that installed app is not found and there are - * multiple stores installed on device that supports billing. - */ - @Nullable - public String[] getPreferredStoreNames() { - return prefferedStoreNames; - } - /** * OpenIAB could skip receipt verification by publicKey for GooglePlay and OpenStores *

    @@ -1568,13 +1403,19 @@ public long getDiscoveryTimeout() { *

    * If you put only your instance of Appstore in this list OpenIAB will use it *

    - * TODO: consider to use AppstoreFactory.get(storeName) -> Appstore instance */ - @Nullable public List getAvailableStores() { return availableStores; } + /** + * Used as priority list if store that installed app is not found and there are + * multiple stores installed on device that supports billing. + */ + public List getPreferredStoreNames() { + return preferredStoreNames; + } + /** * storeKeys is map of [ appstore name -> publicKeyBase64 ] * Put keys for all stores you support in this Map and pass it to createInstance {@link OpenIabHelper} @@ -1594,15 +1435,19 @@ public Map getStoreKeys() { return storeKeys; } - public boolean hasAvailableStoreWithName(@NotNull String name) { + public Appstore getAvailableStoreWithName(@NotNull String name) { if (!CollectionUtils.isEmpty(availableStores)) { for (Appstore s : availableStores) { if (name.equals(s.getAppstoreName())) { - return true; + return s; } } } - return false; + return null; + } + + public boolean hasPreferredStoreName(@NotNull final String name) { + return getPreferredStoreNames().contains(name); } public boolean hasStoreKey(String storeName) { @@ -1854,12 +1699,12 @@ public Builder setSamsungCertificationRequestCode(int code) { * @return Create new instance of {@link org.onepf.oms.OpenIabHelper.Options}. */ public Options build() { - List availableStores = CollectionUtils.isEmpty(this.availableStores) ? null : + List availableStores = CollectionUtils.isEmpty(this.availableStores) ? Collections.emptyList() : Collections.unmodifiableList(this.availableStores); Map storeKeys = CollectionUtils.isEmpty(this.storeKeys) ? null : Collections.unmodifiableMap(this.storeKeys); - String[] preferredStoreNames = CollectionUtils.isEmpty(this.preferredStoreNames) ? null : - this.preferredStoreNames.toArray(new String[this.preferredStoreNames.size()]); + List preferredStoreNames = CollectionUtils.isEmpty(this.preferredStoreNames) ? Collections.emptyList() : + new ArrayList(this.preferredStoreNames); return new Options( availableStores, storeKeys, diff --git a/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java b/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java index 2354e4c2..ad8da2fe 100644 --- a/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java +++ b/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java @@ -27,7 +27,9 @@ import org.onepf.oms.DefaultAppstore; import org.onepf.oms.OpenIabHelper; import org.onepf.oms.appstore.googleUtils.IabHelper; +import org.onepf.oms.util.CollectionUtils; import org.onepf.oms.util.Logger; +import org.onepf.oms.util.Utils; import android.content.ComponentName; import android.content.Context; @@ -90,14 +92,18 @@ public boolean isBillingAvailable(final String packageName) { return billingAvailable; // return previosly checked result } + if (Utils.uiThread()) { + throw new IllegalStateException("Must no be called from UI thread."); + } + billingAvailable = false; if (packageExists(context, ANDROID_INSTALLER) || packageExists(context, GOOGLE_INSTALLER)) { final Intent intent = new Intent(GooglePlay.VENDING_ACTION); intent.setPackage(GooglePlay.ANDROID_INSTALLER); final List infoList = context.getPackageManager().queryIntentServices(intent, 0); - if (infoList != null && !infoList.isEmpty()) { + if (!CollectionUtils.isEmpty(infoList)) { final CountDownLatch latch = new CountDownLatch(1); - context.bindService(intent, new ServiceConnection() { + final ServiceConnection serviceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { IInAppBillingService mService = IInAppBillingService.Stub.asInterface(service); int response; @@ -117,12 +123,13 @@ public void onServiceConnected(ComponentName name, IBinder service) { } public void onServiceDisconnected(ComponentName name) {/*do nothing*/} - }, Context.BIND_AUTO_CREATE); - try { - latch.await(TIMEOUT_BILLING_SUPPORTED, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Logger.e("isBillingAvailable() billing is not supported. Initialization error. ", e); + }; + if (context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) { + try { + latch.await(TIMEOUT_BILLING_SUPPORTED, TimeUnit.MILLISECONDS); + } catch (InterruptedException ignore) {} } + Logger.e("isBillingAvailable() billing is not supported. Initialization error."); } } return billingAvailable; diff --git a/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java b/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java index eff3e1a0..e510e9f8 100644 --- a/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java +++ b/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java @@ -28,13 +28,13 @@ import android.text.TextUtils; import org.onepf.oms.SkuManager; -import org.onepf.oms.SkuMappingException; import org.onepf.oms.appstore.googleUtils.IabException; import org.onepf.oms.appstore.googleUtils.IabHelper; import org.onepf.oms.appstore.googleUtils.IabResult; import org.onepf.oms.appstore.googleUtils.Inventory; import org.onepf.oms.util.CollectionUtils; import org.onepf.oms.util.Logger; +import org.onepf.oms.util.Utils; import java.util.concurrent.CountDownLatch; @@ -96,7 +96,11 @@ public boolean isBillingAvailable(String packageName) { if (isBillingAvailable != null) { return isBillingAvailable; } - + + if (Utils.uiThread()) { + throw new IllegalStateException("Must no be called from UI thread."); + } + if (isSamsungTestMode) { Logger.d("isBillingAvailable() billing is supported in test mode."); isBillingAvailable = true; diff --git a/library/src/main/java/org/onepf/oms/util/Utils.java b/library/src/main/java/org/onepf/oms/util/Utils.java index e3ffd540..128d7a6b 100644 --- a/library/src/main/java/org/onepf/oms/util/Utils.java +++ b/library/src/main/java/org/onepf/oms/util/Utils.java @@ -43,6 +43,15 @@ public static boolean hasRequestedPermission(@NotNull Context context, final Str return false; } + public static boolean packageInstalled(@NotNull Context context, final String packageName) { + final PackageManager packageManager = context.getPackageManager(); + try { + packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); + return true; + } catch (PackageManager.NameNotFoundException ignore) {} + return false; + } + public static boolean uiThread() { return Thread.currentThread() == Looper.getMainLooper().getThread(); } From 7b2aa63c1827ae65d21fd4c8891fd7d51320d9da Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Wed, 10 Sep 2014 23:11:30 +0400 Subject: [PATCH 19/72] Change open stores processing --- .../java/org/onepf/oms/OpenIabHelper.java | 448 +++++++++--------- 1 file changed, 216 insertions(+), 232 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 14d65fe7..bbfca86d 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -170,7 +170,7 @@ private static interface AppstoreFactory{ appstorePackageMap.put("cm.aptoide.pt", NAME_APTOIDE); - appstorePackageMap.put("com.google.play", NAME_GOOGLE); + appstorePackageMap.put("com.android.vending", NAME_GOOGLE); appstoreFactoryMap.put(NAME_GOOGLE, new AppstoreFactory() { @Override public Appstore get() { @@ -179,7 +179,7 @@ public Appstore get() { } }); - appstorePackageMap.put("com.amazon.apps", NAME_AMAZON); + appstorePackageMap.put("com.amazon.venezia", NAME_AMAZON); appstoreFactoryMap.put(NAME_AMAZON, new AppstoreFactory() { @Override public Appstore get() { @@ -187,7 +187,7 @@ public Appstore get() { } }); - appstorePackageMap.put("com.samsung.apps", NAME_SAMSUNG); + appstorePackageMap.put("com.sec.android.app.samsungapps", NAME_SAMSUNG); appstoreFactoryMap.put(NAME_SAMSUNG, new AppstoreFactory() { @Override public Appstore get() { @@ -195,6 +195,7 @@ public Appstore get() { } }); + // TODO check package appstorePackageMap.put("com.nokia.nstore", NAME_NOKIA); appstoreFactoryMap.put(NAME_NOKIA, new AppstoreFactory() { @Override @@ -392,191 +393,129 @@ private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener return; } - final String appstoreName = appstorePackageMap.get(packageInstaller); - if (!TextUtils.isEmpty(appstoreName)) { - final Appstore appstore; + Appstore appstore = null; + + if (appstorePackageMap.containsKey(packageInstaller)) { // Package installer is a known appstore - if (!CollectionUtils.isEmpty(options.getAvailableStores())) { - // Check available stores + final String appstoreName = appstorePackageMap.get(packageInstaller); + if (!options.getAvailableStores().isEmpty()) { + // Developer explicitly specified available stores appstore = options.getAvailableStoreWithName(appstoreName); - } else { - // Check all available wrappers + } else if (appstoreFactoryMap.containsKey(appstoreName)) { appstore = appstoreFactoryMap.get(appstoreName).get(); } - if (appstore != null) { - checkBillingAndFinish(listener, appstore); - } else { - finishSetupNoStore(listener); - } - // Either we found appstore or not, we know it was a wrapper - // Our work here is done + } + + if (appstore != null) { + // Package installer found + checkBillingAndFinish(listener, appstore); return; } - // Package installer is unknown - // Look for package installer among available open stores + // Look among open stores Intent bindServiceIntent = null; for (final ServiceInfo serviceInfo : queryOpenStoreServices()) { - final String servicePackage = serviceInfo.packageName; - if (TextUtils.equals(packageInstaller, servicePackage)) { + if (TextUtils.equals(serviceInfo.packageName, packageInstaller)) { bindServiceIntent = getBindServiceIntent(serviceInfo); break; } } - if (bindServiceIntent != null) { - // We've got an open store as package installer - final ServiceConnection serviceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(final ComponentName name, final IBinder service) { - checkBillingAndFinish(listener, name, service, this); - } - - @Override - public void onServiceDisconnected(final ComponentName name) {} - }; - if (context.bindService(bindServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE)) { - // We fount suitable open store, wait for service to connect - return; - } - } - - // Appstore is unknown to this library - finishSetupNoStore(listener); - } - - private void setup(final IabHelper.OnIabSetupFinishedListener listener) { - // Look for appropriate open store - final List serviceInfos = queryOpenStoreServices(); - if (serviceInfos.isEmpty()) { - // No open stores available, fall back to wrappers - checkWrappersAndFinish(listener); + if (bindServiceIntent == null) { + // Package installer not found + finishSetup(listener); return; } - // TODO check preferred stores - // TODO handle check inventory - final Queue bindServiceIntents = new LinkedList(); - for (final ServiceInfo serviceInfo : serviceInfos) { - bindServiceIntents.add(getBindServiceIntent(serviceInfo)); - } - - checkOpenStoresThenWrappers(listener, bindServiceIntents); - } - private void checkOpenStoresThenWrappers(final IabHelper.OnIabSetupFinishedListener listener, - final Queue bindServiceIntents) { - final ServiceConnection serviceConnection = new ServiceConnection() { + if (!context.bindService(bindServiceIntent, new ServiceConnection() { @Override public void onServiceConnected(final ComponentName name, final IBinder service) { - final ServiceConnection serviceConnection = this; - setupThreadPoolExecutor.execute(new Runnable() { - @Override - public void run() { - Runnable finishRunnable = null; - Appstore openStore = null; - try { - openStore = checkOpenStoreBilling(name, service, serviceConnection); - } catch (RemoteException ignore) {} - if (openStore != null) { - // Found open store - final Appstore appstore = openStore; - final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); - finishRunnable = new Runnable() { - @Override - public void run() { - finishSetup(listener, iabResult, appstore); - } - }; - } - if (finishRunnable == null) { - finishRunnable = new Runnable() { - @Override - public void run() { - // Check another store - checkOpenStoresThenWrappers(listener, bindServiceIntents); - } - }; + Appstore appstore = null; + try { + final Appstore openAppstore = getOpenAppstore(name, service, this); + if (openAppstore != null) { + // Found open store + final String openStoreName = openAppstore.getAppstoreName(); + if (!options.getAvailableStores().isEmpty()) { + // Developer explicitly specified available stores + appstore = options.getAvailableStoreWithName(openStoreName); + } else { + appstore = openAppstore; } - handler.post(finishRunnable); } - }); + } catch (RemoteException ignore) {} + checkBillingAndFinish(listener, appstore); } @Override public void onServiceDisconnected(final ComponentName name) {} - }; - - while (!bindServiceIntents.isEmpty()) { - if (context.bindService(bindServiceIntents.poll(), serviceConnection, Context.BIND_AUTO_CREATE)) { - return; - } + }, Context.BIND_AUTO_CREATE)) { + // Can't bind to open store service + finishSetupWithError(listener); } - - // No suitable open store found, fall back to wrappers - checkWrappersAndFinish(listener); } - private void checkWrappersAndFinish(final IabHelper.OnIabSetupFinishedListener listener) { + private void setup(final IabHelper.OnIabSetupFinishedListener listener) { // List of wrappers to check - final Set appstores = new LinkedHashSet(); - for (final String name : options.getPreferredStoreNames()) { - // Add available stored according to preferred stores priority - final Appstore appstore = options.getAvailableStoreWithName(name); - if (appstore != null) { - appstores.add(appstore); - } - } + final Set appstoresToCheck = new LinkedHashSet(); + final List availableStores; - if (!CollectionUtils.isEmpty(availableStores = options.getAvailableStores())) { + if (!(availableStores = options.getAvailableStores()).isEmpty()) { // Use only stores specified explicitly - appstores.addAll(availableStores); + for (final String name : options.getPreferredStoreNames()) { + // Add available stored according to preferred stores priority + final Appstore appstore = options.getAvailableStoreWithName(name); + if (appstore != null) { + appstoresToCheck.add(appstore); + } + } + appstoresToCheck.addAll(availableStores); + checkBillingAndFinish(listener, appstoresToCheck); } else { - // Add all available wrappers - for (final String name : appstoreFactoryMap.keySet()) { - // No need to instantiate appstore if it's already added - boolean alreadyAdded = false; - for (final Appstore appstore : appstores) { - if (TextUtils.equals(appstore.getAppstoreName(), name)) { - alreadyAdded = true; - break; + discoverOpenStores(new OpenStoresDiscoveredListener() { + @Override + public void openStoresDiscovered(@NotNull final List appstores) { + final List allAvailableAppsotres = new ArrayList(appstores); + // Add all available wrappers + for (final String appstorePackage : appstorePackageMap.keySet()) { + final String name = appstorePackageMap.get(appstorePackage); + if (!TextUtils.isEmpty(name) + && appstoreFactoryMap.containsKey(name) + && Utils.packageInstalled(context, appstorePackage)) { + allAvailableAppsotres.add(appstoreFactoryMap.get(name).get()); + } } + // Add available stored according to preferred stores priority + for (final String name : options.getPreferredStoreNames()) { + for (final Appstore appstore : allAvailableAppsotres) { + if (TextUtils.equals(appstore.getAppstoreName(), name)) { + appstoresToCheck.add(appstore); + break; + } + } + } + // Add everything else + appstoresToCheck.addAll(allAvailableAppsotres); + checkBillingAndFinish(listener, allAvailableAppsotres); } - if (!alreadyAdded) { - appstores.add(appstoreFactoryMap.get(name).get()); - } - } - } - checkBillingAndFinish(listener, appstores); - } - - private List queryOpenStoreServices() { - final Intent intentAppstoreServices = new Intent(BIND_INTENT); - final PackageManager packageManager = context.getPackageManager(); - final List resolveInfos = packageManager.queryIntentServices(intentAppstoreServices, 0); - final List serviceInfos = new ArrayList(); - for (final ResolveInfo resolveInfo : resolveInfos) { - serviceInfos.add(resolveInfo.serviceInfo); + }); } - return (List) Collections.unmodifiableCollection(serviceInfos); } @Nullable private OpenAppstore getOpenAppstore(final ComponentName name, - final ServiceConnection serviceConnection, - final IOpenAppstore openAppstoreService) + final IBinder service, final ServiceConnection serviceConnection) throws RemoteException { + final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); final String appstoreName = openAppstoreService.getAppstoreName(); - if (TextUtils.isEmpty(appstoreName)) { // no name - no service - Logger.e("discoverOpenStores() Appstore doesn't have name. Skipped. ComponentName: ", name); - return null; - } - final Intent billingIntent = openAppstoreService.getBillingServiceIntent(); - if (billingIntent == null) { + if (TextUtils.isEmpty(appstoreName)) { // no name - no service + Logger.d("discoverOpenStores() Appstore doesn't have name. Skipped. ComponentName: ", name); + } else if (billingIntent == null) { Logger.d("discoverOpenStores(): billing is not supported by store: ", name); - return null; + } else { + // TODO Do something about verifyMode + return new OpenAppstore(context, appstoreName, openAppstoreService, billingIntent, null, serviceConnection); } - - // TODO Do something about verifyMode // if ((options.verifyMode == Options.VERIFY_EVERYTHING) && !options.hasStoreKey(appstoreName)) { // don't connect to OpenStore if no key provided and verification is strict // Logger.e("discoverOpenStores() verification is required but publicKey is not provided: ", name); @@ -584,9 +523,7 @@ private OpenAppstore getOpenAppstore(final ComponentName name, // String publicKey = options.getStoreKey(appstoreName); // if (options.verifyMode == Options.VERIFY_SKIP) publicKey = null; - final OpenAppstore openAppstore = new OpenAppstore(context, appstoreName, openAppstoreService, billingIntent, null, serviceConnection); - openAppstore.componentName = name; - return openAppstore; + return null; } private Intent getBindServiceIntent(final ServiceInfo serviceInfo) { @@ -595,99 +532,39 @@ private Intent getBindServiceIntent(final ServiceInfo serviceInfo) { return bindServiceIntent; } - private Appstore checkOpenStoreBilling(final ComponentName name, - final IBinder service, - final ServiceConnection serviceConnection) - throws RemoteException { - if (Utils.uiThread()) { - throw new IllegalStateException("Must not be called from UI thread"); - } - final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); - // Check billing and create Appstore object for open store - Appstore openAppstore = null; - if (openAppstoreService.isBillingAvailable(context.getPackageName())) { - openAppstore = getOpenAppstore(name, serviceConnection, openAppstoreService); - } - if (openAppstore != null && versionOk(openAppstore)) { - return openAppstore; - } - // Billing is unavailable for this open store - return null; - } - - private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedListener listener, - final ComponentName name, - final IBinder service, - final ServiceConnection serviceConnection){ - setupThreadPoolExecutor.execute(new Runnable() { - @Override - public void run() { - Appstore openAppstore = null; - int iabResultResponse; - String iabResultMessage; - try { - openAppstore = checkOpenStoreBilling(name, service, serviceConnection); - if (openAppstore != null) { - iabResultResponse = BILLING_RESPONSE_RESULT_OK; - iabResultMessage = "Setup ok"; - } else { - iabResultResponse = BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE; - iabResultMessage = "Billing unavailable for open store"; - } - } catch (RemoteException e) { - iabResultResponse = BILLING_RESPONSE_RESULT_ERROR; - iabResultMessage = "Error occurred during billing setup"; - } - final IabResult iabResult = new IabResult(iabResultResponse, iabResultMessage); - final Appstore appstore = openAppstore; - handler.post(new Runnable() { - @Override - public void run() { - finishSetup(listener, iabResult, appstore); - } - }); - } - }); - } - private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedListener listener, - @NotNull final Appstore appstore) { - checkBillingAndFinish(listener, Arrays.asList(appstore)); + @Nullable final Appstore appstore) { + if (appstore == null) { + finishSetup(listener); + } else { + checkBillingAndFinish(listener, Arrays.asList(appstore)); + } } private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedListener listener, @NotNull final Collection appstores) { final String packageName = context.getPackageName(); if (appstores.isEmpty()) { - finishSetupNoStore(listener); + finishSetup(listener); return; } setupThreadPoolExecutor.execute(new Runnable() { @Override public void run() { - Runnable finishRunnable = null; + Appstore checkedAppstore = null; for (final Appstore appstore : appstores) { - final IabResult iabResult; if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { - iabResult = new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); - finishRunnable = new Runnable() { - @Override - public void run() { - finishSetup(listener, iabResult, appstore); - } - }; + checkedAppstore = appstore; break; } } - if (finishRunnable == null) { - finishRunnable = new Runnable() { - @Override - public void run() { - finishSetupNoStore(listener); - } - }; - } - handler.post(finishRunnable); + final Appstore appstore = checkedAppstore; + handler.post(new Runnable() { + @Override + public void run() { + finishSetup(listener, appstore); + } + }); } }); } @@ -698,12 +575,31 @@ private boolean versionOk(@NotNull final Appstore appstore) { try { versionCode = context.getPackageManager().getPackageInfo(packageName, 0).versionCode; } catch (NameNotFoundException ignore) {} - return appstore.getPackageVersion(packageName) >= versionCode; + // TODO investigate getPackageVersion() behaviour +// return appstore.getPackageVersion(packageName) >= versionCode; + return true; + } + + private void finishSetupWithError(@NotNull final IabHelper.OnIabSetupFinishedListener listener) { + finishSetupWithError(listener, null); + } + + private void finishSetupWithError(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + @Nullable final Exception exception) { + Logger.e("finishSetupWithError() error occurred during setup: ", exception == null ? "" : exception); + finishSetup(listener, new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Error occured, setup failed"), null); + } + + private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener) { + finishSetup(listener, null); } - private void finishSetupNoStore(@NotNull final IabHelper.OnIabSetupFinishedListener listener) { - final IabResult iabResult = new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "No suitable appstore was found"); - finishSetup(listener, iabResult, null); + private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + @Nullable final Appstore appstore) { + final IabResult iabResult = appstore == null + ? new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "No suitable appstore was found") + : new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup ok"); + finishSetup(listener, iabResult, appstore); } private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener, @@ -718,6 +614,7 @@ private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener lis final boolean setUpSuccessful = iabResult.isSuccess(); setupState = setUpSuccessful ? SETUP_RESULT_SUCCESSFUL : SETUP_RESULT_FAILED; + activity = null; setupThreadPoolExecutor = null; if (setUpSuccessful) { if (appstore == null) { @@ -727,7 +624,11 @@ private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener lis mAppstoreBillingService = appstore.getInAppBillingService(); } Logger.dWithTimeFromUp("finishSetup() === SETUP DONE === result: ", iabResult, " Appstore: ", appstore); - listener.onIabSetupFinished(iabResult); + if (mAppstoreBillingService == null) { + listener.onIabSetupFinished(iabResult); + } else { + mAppstoreBillingService.startSetup(listener); + } } @MagicConstant(intValues = {SETUP_DISPOSED, SETUP_IN_PROGRESS, @@ -736,6 +637,86 @@ public int getSetupState() { return setupState; } + public @Nullable List discoverOpenStores() { + if (Utils.uiThread()) { + throw new IllegalStateException("Must not be called from UI thread"); + } + + final List openAppstores = new ArrayList(); + final CountDownLatch countDownLatch = new CountDownLatch(1); + discoverOpenStores(new OpenStoresDiscoveredListener() { + @Override + public void openStoresDiscovered(@NotNull final List appstores) { + openAppstores.addAll(appstores); + countDownLatch.notify(); + } + }); + try { + countDownLatch.await(); + }catch (InterruptedException e) { + return null; + } + return openAppstores; + } + + public void discoverOpenStores(@NotNull final OpenStoresDiscoveredListener listener) { + final List serviceInfos = queryOpenStoreServices(); + final Queue bindServiceIntents = new LinkedList(); + for (final ServiceInfo serviceInfo : serviceInfos) { + bindServiceIntents.add(getBindServiceIntent(serviceInfo)); + } + + discoverOpenStores(listener, bindServiceIntents, new ArrayList()); + } + + private void discoverOpenStores(@NotNull final OpenStoresDiscoveredListener listener, + @NotNull final Queue bindServiceIntents, + @NotNull final List appstores) { + final ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + Appstore openAppstore = null; + try { + openAppstore = getOpenAppstore(name, service, this); + } catch (RemoteException ignore) {} + if (openAppstore != null) { + appstores.add(openAppstore); + } + discoverOpenStores(listener, bindServiceIntents, appstores); + } + + @Override + public void onServiceDisconnected(final ComponentName name) {} + }; + + while (!bindServiceIntents.isEmpty()) { + if (context.bindService(bindServiceIntents.poll(), serviceConnection, Context.BIND_AUTO_CREATE)) { + return; + } + } + + listener.openStoresDiscovered(Collections.unmodifiableList(appstores)); + } + + @Deprecated + /** + * Use {@link OpenIabHelper#discoverOpenStores(OpenStoresDiscoveredListener)} or {@link OpenIabHelper#discoverOpenStores()} instead. + */ + public static List discoverOpenStores(final Context context, final List dest, final Options options) { + throw new UnsupportedOperationException("This action is no longer supported."); + } + + private List queryOpenStoreServices() { + final Intent intentAppstoreServices = new Intent(BIND_INTENT); + final PackageManager packageManager = context.getPackageManager(); + final List resolveInfos = packageManager.queryIntentServices(intentAppstoreServices, 0); + final List serviceInfos = new ArrayList(); + for (final ResolveInfo resolveInfo : resolveInfos) { + serviceInfos.add(resolveInfo.serviceInfo); + } + return Collections.unmodifiableList(serviceInfos); + } + /** * Must be called after setup is finished. See {@link org.onepf.oms.OpenIabHelper#startSetup(org.onepf.oms.appstore.googleUtils.IabHelper.OnIabSetupFinishedListener)} * @@ -1195,6 +1176,10 @@ public interface OnOpenIabHelperInitFinished { void onOpenIabHelperInitFinished(); } + public interface OpenStoresDiscoveredListener{ + void openStoresDiscovered(@NotNull List appstores); + } + /** * All options of OpenIAB can be found here. * Create instance of this class via {@link org.onepf.oms.OpenIabHelper.Options.Builder}. @@ -1232,7 +1217,7 @@ public static class Options { *

    * To specify your own list, you need to instantiate Appstore object manually. * GooglePlay, Amazon and SamsungApps could be instantiated directly. OpenStore can be discovered - * using {@link OpenIabHelper#discoverOpenStores(Context, List, Options)} + * using {@link OpenIabHelper#discoverOpenStores()} *

    * If you put only your instance of Appstore in this list OpenIAB will use it *

    @@ -1257,7 +1242,6 @@ public static class Options { *

    * Wait specified amount of ms to find all OpenStores on device */ - // TODO remove? public int discoveryTimeoutMs = DEFAULT_DISCOVER_TIMEOUT; /** @@ -1399,7 +1383,7 @@ public long getDiscoveryTimeout() { *

    * To specify your own list, you need to instantiate Appstore object manually. * GooglePlay, Amazon and SamsungApps could be instantiated directly. OpenStore can be discovered - * using {@link OpenIabHelper#discoverOpenStores(android.content.Context, java.util.List, org.onepf.oms.OpenIabHelper.Options)} + * using {@link OpenIabHelper#discoverOpenStores()} *

    * If you put only your instance of Appstore in this list OpenIAB will use it *

    From 9e5cd1b7ab7b5bcf6c48a29bdf188a548c63d6e0 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Thu, 11 Sep 2014 15:40:14 +0400 Subject: [PATCH 20/72] Discovery timeout is now deprecated. --- .../java/org/onepf/oms/OpenIabHelper.java | 45 +++---------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index bbfca86d..8655a4b8 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -80,10 +80,6 @@ public class OpenIabHelper { * TODO: Optimize: ~1sec is consumed for check account certification via account activity + ~3sec for actual setup */ private static final int CHECK_INVENTORY_TIMEOUT = 10 * 1000; - /** - * Default timeout (in milliseconds) for discover all OpenStores on device. - */ - private static final int DEFAULT_DISCOVER_TIMEOUT = 5 * 1000; private static final String BIND_INTENT = "org.onepf.oms.openappstore.BIND"; @@ -508,6 +504,7 @@ private OpenAppstore getOpenAppstore(final ComponentName name, final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); final String appstoreName = openAppstoreService.getAppstoreName(); final Intent billingIntent = openAppstoreService.getBillingServiceIntent(); + if (TextUtils.isEmpty(appstoreName)) { // no name - no service Logger.d("discoverOpenStores() Appstore doesn't have name. Skipped. ComponentName: ", name); } else if (billingIntent == null) { @@ -785,15 +782,6 @@ private void checkSamsung() { appstoreFactoryMap.remove(NAME_SAMSUNG); } - - //todo move to Utils - private static void formatComponentStatus(String message, StringBuilder messageBuilder) { - if (messageBuilder.length() > 0) { - messageBuilder.append('\n'); - } - messageBuilder.append(message); - } - /** * Connects to Billing Service of each store. Request list of user purchases (inventory) * @@ -1237,12 +1225,9 @@ public static class Options { /** * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getDiscoveryTimeout()} - * Will be private since 1.0. - *

    - *

    - * Wait specified amount of ms to find all OpenStores on device + * No longer used. */ - public int discoveryTimeoutMs = DEFAULT_DISCOVER_TIMEOUT; + public int discoveryTimeoutMs = 0; /** * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#isCheckInventory()} @@ -1318,14 +1303,12 @@ private Options(List availableStores, Map storeKeys, boolean checkInventory, int checkInventoryTimeout, - int discoveryTimeout, @MagicConstant(intValues = {VERIFY_EVERYTHING, VERIFY_ONLY_KNOWN, VERIFY_SKIP}) int verifyMode, List preferredStoreNames, int samsungCertificationRequestCode) { this.checkInventory = checkInventory; this.checkInventoryTimeoutMs = checkInventoryTimeout; this.availableStores = availableStores; - this.discoveryTimeoutMs = discoveryTimeout; this.storeKeys = storeKeys; this.preferredStoreNames = preferredStoreNames; this.verifyMode = verifyMode; @@ -1370,11 +1353,9 @@ public long getCheckInventoryTimeout() { return checkInventoryTimeoutMs; } - /** - * Wait specified amount of ms to find all OpenStores on device - */ + @Deprecated public long getDiscoveryTimeout() { - return discoveryTimeoutMs; + return 0; } /** @@ -1450,7 +1431,6 @@ public static final class Builder { private List preferredStoreNames; private Map storeKeys; private List availableStores; - private int discoveryTimeout = DEFAULT_DISCOVER_TIMEOUT; private int checkInventoryTimeout = CHECK_INVENTORY_TIMEOUT; private boolean checkInventory; private int samsungCertificationRequestCode @@ -1501,18 +1481,8 @@ public Builder setCheckInventory(boolean checkInventory) { return this; } - /** - * Set discovery timeout. By default 5 sec. - * - * @throws java.lang.IllegalArgumentException if timeout is negative value. - * @see org.onepf.oms.OpenIabHelper.Options#getDiscoveryTimeout() - */ + @Deprecated public Builder setDiscoveryTimeout(int discoveryTimeout) { - if (discoveryTimeout < 0) { - throw new IllegalArgumentException("Discovery timeout can't be" + - " a negative value."); - } - this.discoveryTimeout = discoveryTimeout; return this; } @@ -1526,7 +1496,7 @@ public Builder setDiscoveryTimeout(int discoveryTimeout) { * @see org.onepf.oms.OpenIabHelper.Options.Builder#setCheckInventory(boolean) */ public Builder setCheckInventoryTimeout(int checkInventoryTimeout) { - if (discoveryTimeout < 0) { + if (checkInventoryTimeout < 0) { throw new IllegalArgumentException("Check inventory timeout can't be" + " a negative value."); } @@ -1694,7 +1664,6 @@ public Options build() { storeKeys, checkInventory, checkInventoryTimeout, - discoveryTimeout, verifyMode, preferredStoreNames, samsungCertificationRequestCode); From feb5d4afff78910ed57b668bed033cf2b64cfd72 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Sat, 13 Sep 2014 21:11:34 +0400 Subject: [PATCH 21/72] Refactor OpenIabHelper.options; Introduce storeSearchAlgorithm option --- .../java/org/onepf/oms/OpenIabHelper.java | 478 ++++++++---------- 1 file changed, 204 insertions(+), 274 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 8655a4b8..ad92909d 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -16,11 +16,13 @@ package org.onepf.oms; +import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -67,6 +69,9 @@ import android.os.RemoteException; import android.text.TextUtils; +import static org.onepf.oms.OpenIabHelper.Options.SEARCH_STRATEGY_INSTALLER; +import static org.onepf.oms.OpenIabHelper.Options.SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT; + /** * @author Boris Minaev, Oleg Orlov, Kirill Rozov * @since 16.04.13 @@ -170,7 +175,7 @@ private static interface AppstoreFactory{ appstoreFactoryMap.put(NAME_GOOGLE, new AppstoreFactory() { @Override public Appstore get() { - final String googleKey = options.getStoreKey(NAME_GOOGLE); + final String googleKey = options.getStoreKeys().get(NAME_GOOGLE); return new GooglePlay(context, googleKey); } }); @@ -373,22 +378,23 @@ public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { setupThreadPoolExecutor = Executors.newSingleThreadExecutor(); final String packageName = context.getPackageName(); + final int storeSearchStrategy = options.getStoreSearchStrategy(); final String packageInstaller = packageManager.getInstallerPackageName(packageName); - if (TextUtils.isEmpty(packageInstaller)) { + final boolean packageInstallerAvailable = !TextUtils.isEmpty(packageInstaller) && Utils.packageInstalled(context, packageInstaller); + if ((storeSearchStrategy == SEARCH_STRATEGY_INSTALLER || storeSearchStrategy == SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT) + && packageInstallerAvailable) { // Package installer is not set - setup(listener); + setupForPackage(listener, packageInstaller, storeSearchStrategy == SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT); } else { - setupForPackage(listener, packageInstaller); - } - } - - private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener, final String packageInstaller) { - if (!Utils.packageInstalled(context, packageInstaller)) { - // Package installer no longer exist, fallback to default algorithm setup(listener); - return; } + } + // TODO simplify conditions + // TODO decompose to a separate methods? + private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener, + final String packageInstaller, + final boolean withFallback) { Appstore appstore = null; if (appstorePackageMap.containsKey(packageInstaller)) { @@ -397,6 +403,10 @@ private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener if (!options.getAvailableStores().isEmpty()) { // Developer explicitly specified available stores appstore = options.getAvailableStoreWithName(appstoreName); + if (appstore == null) { + finishSetup(listener); + return; + } } else if (appstoreFactoryMap.containsKey(appstoreName)) { appstore = appstoreFactoryMap.get(appstoreName).get(); } @@ -418,7 +428,12 @@ private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener } if (bindServiceIntent == null) { // Package installer not found - finishSetup(listener); + if (withFallback) { + // Fallback to default algorithm + setup(listener); + }else { + finishSetup(listener); + } return; } @@ -454,7 +469,7 @@ private void setup(final IabHelper.OnIabSetupFinishedListener listener) { // List of wrappers to check final Set appstoresToCheck = new LinkedHashSet(); - final List availableStores; + final Set availableStores; if (!(availableStores = options.getAvailableStores()).isEmpty()) { // Use only stores specified explicitly for (final String name : options.getPreferredStoreNames()) { @@ -510,7 +525,7 @@ private OpenAppstore getOpenAppstore(final ComponentName name, } else if (billingIntent == null) { Logger.d("discoverOpenStores(): billing is not supported by store: ", name); } else { - // TODO Do something about verifyMode + // TODO Rethink store verification return new OpenAppstore(context, appstoreName, openAppstoreService, billingIntent, null, serviceConnection); } // if ((options.verifyMode == Options.VERIFY_EVERYTHING) && !options.hasStoreKey(appstoreName)) { @@ -730,22 +745,22 @@ public synchronized String getConnectedAppstoreName() { public void checkOptions() { checkSamsung(); checkNokia(); - // TODO handle verify + // TODO Rethink store verification strategy // check publicKeys. Must be not null and valid - if (options.verifyMode != Options.VERIFY_SKIP && options.storeKeys != null) { - for (Entry entry : options.storeKeys.entrySet()) { - if (entry.getValue() == null) { - throw new IllegalArgumentException("Null publicKey for store: " + entry.getKey() + ", key: " + entry.getValue()); - } - - try { - Security.generatePublicKey(entry.getValue()); - } catch (Exception e) { - throw new IllegalArgumentException("Invalid publicKey for store: " - + entry.getKey() + ", key: " + entry.getValue(), e); - } - } - } +// if (options.verifyMode != Options.VERIFY_SKIP && options.storeKeys != null) { +// for (Entry entry : options.storeKeys.entrySet()) { +// if (entry.getValue() == null) { +// throw new IllegalArgumentException("Null publicKey for store: " + entry.getKey() + ", key: " + entry.getValue()); +// } +// +// try { +// Security.generatePublicKey(entry.getValue()); +// } catch (Exception e) { +// throw new IllegalArgumentException("Invalid publicKey for store: " +// + entry.getKey() + ", key: " + entry.getValue(), e); +// } +// } +// } } private void checkNokia() { @@ -755,7 +770,7 @@ private void checkNokia() { return; } if (options.getAvailableStoreWithName(NAME_NOKIA) != null - || options.hasPreferredStoreName(NAME_NOKIA)) { + || options.getPreferredStoreNames().contains(NAME_NOKIA)) { throw new IllegalStateException("Nokia permission \"" + NokiaStore.NOKIA_BILLING_PERMISSION + "\" NOT REQUESTED"); } @@ -769,7 +784,7 @@ private void checkSamsung() { return; } if (options.getAvailableStoreWithName(NAME_SAMSUNG) != null - || options.hasPreferredStoreName(NAME_SAMSUNG)) { + || options.getPreferredStoreNames().contains(NAME_SAMSUNG)) { // Unfortunately, SamsungApps requires to launch their own "Certification Activity" // in order to connect to billing service. So it's also needed for OpenIAB. // @@ -1170,9 +1185,7 @@ public interface OpenStoresDiscoveredListener{ /** * All options of OpenIAB can be found here. - * Create instance of this class via {@link org.onepf.oms.OpenIabHelper.Options.Builder}. - *

    - * TODO: consider to use cloned instance of Options in OpenIABHelper + * Create instance of this class via {@link Builder}. */ public static class Options { @@ -1196,129 +1209,97 @@ public static class Options { */ public static final int VERIFY_ONLY_KNOWN = 2; + + public static final int SEARCH_STRATEGY_INSTALLER = 0; + public static final int SEARCH_STRATEGY_BEST_FIT = 1; + public static final int SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT = 2; + + /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getAvailableStores()} + * @deprecated Use {@link #getAvailableStores()} * Will be private since 1.0. - *

    - * List of stores to be used for store elections. By default GooglePlay, Amazon, SamsungApps and - * all installed OpenStores are used. - *

    - * To specify your own list, you need to instantiate Appstore object manually. - * GooglePlay, Amazon and SamsungApps could be instantiated directly. OpenStore can be discovered - * using {@link OpenIabHelper#discoverOpenStores()} - *

    - * If you put only your instance of Appstore in this list OpenIAB will use it - *

    */ - // TODO discover open stores - public List availableStores = Collections.emptyList(); + public final Set availableStores; /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getPreferredStoreNames()} + * @deprecated Use {@link #getPreferredStoreNames()} * Will be private since 1.0. - *

    - *

    - * Used as priority list if store that installed app is not found and there are - * multiple stores installed on device that supports billing. */ - public List preferredStoreNames = Collections.emptyList(); + public final Set preferredStoreNames; /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getDiscoveryTimeout()} + * @deprecated * No longer used. */ - public int discoveryTimeoutMs = 0; + public final int discoveryTimeoutMs = 0; /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#isCheckInventory()} + * @deprecated Use {@link #isCheckInventory()} * Will be private since 1.0. - *

    - *

    - * Check user inventory in every store to select proper store - *

    - * Will try to connect to each billingService and extract user's purchases. - * If purchases have been found in the only store that store will be used for further purchases. - * If purchases have been found in multiple stores only such stores will be used for further elections */ - public boolean checkInventory; + public final boolean checkInventory; /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getCheckInventoryTimeout()} - * Will be private since 1.0. - *

    - * Wait specified amount of ms to check inventory in all stores + * @deprecated + * No longer used. */ - public int checkInventoryTimeoutMs = CHECK_INVENTORY_TIMEOUT; + public final int checkInventoryTimeoutMs = 0; /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getVerifyMode()} + * @deprecated Use {@link #getVerifyMode()} * Will be private since 1.0. - *

    - *

    - * OpenIAB could skip receipt verification by publicKey for GooglePlay and OpenStores - *

    - * Receipt could be verified in {@link org.onepf.oms.appstore.googleUtils.IabHelper.OnIabPurchaseFinishedListener#onIabPurchaseFinished(org.onepf.oms.appstore.googleUtils.IabResult, org.onepf.oms.appstore.googleUtils.Purchase)} - * using {@link Purchase#getOriginalJson()} and {@link Purchase#getSignature()} */ @MagicConstant(intValues = {VERIFY_EVERYTHING, VERIFY_ONLY_KNOWN, VERIFY_SKIP}) - public int verifyMode = VERIFY_EVERYTHING; + public final int verifyMode; + + @MagicConstant(intValues = {SEARCH_STRATEGY_INSTALLER, SEARCH_STRATEGY_BEST_FIT, SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT}) + private final int storeSearchStrategy; /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options#getStoreKeys()} + * @deprecated Use {@link #getStoreKeys()} * Will be private since 1.0. - *

    - *

    - * storeKeys is map of [ appstore name -> publicKeyBase64 ] - * Put keys for all stores you support in this Map and pass it to instantiate {@link OpenIabHelper} - *

    - * publicKey key is used to verify receipt is created by genuine Appstore using - * provided signature. It can be found in Developer Console of particular store - *

    - * name of particular store can be provided by local_store tool if you run it on device. - * For Google Play OpenIAB uses {@link OpenIabHelper#NAME_GOOGLE}. - *

    - *

    Note: - * AmazonApps and SamsungApps doesn't use RSA keys for receipt verification, so you don't need - * to specify it */ - public Map storeKeys = new HashMap(); + private final Map storeKeys; /** - * @deprecated Usr {@link org.onepf.oms.OpenIabHelper.Options#getSamsungCertificationRequestCode()} + * @deprecated Usr {@link #getSamsungCertificationRequestCode()} * Will be private since 1.0. - *

    - *

    - * Used for SamsungApps setup. Specify your own value if default one interfere your code. - *

    default value is {@link SamsungAppsBillingService#REQUEST_CODE_IS_ACCOUNT_CERTIFICATION} */ - public int samsungCertificationRequestCode = SamsungAppsBillingService.REQUEST_CODE_IS_ACCOUNT_CERTIFICATION; + public final int samsungCertificationRequestCode; /** - * @deprecated Use {@link org.onepf.oms.OpenIabHelper.Options.Builder} instead. + * @deprecated Use {@link Builder} instead. */ public Options() { + this.checkInventory = false; + this.availableStores = Collections.emptySet(); + this.storeKeys = Collections.emptyMap(); + this.preferredStoreNames = Collections.emptySet(); + this.verifyMode = VERIFY_SKIP; + this.samsungCertificationRequestCode = SamsungAppsBillingService.REQUEST_CODE_IS_ACCOUNT_CERTIFICATION; + this.storeSearchStrategy = SEARCH_STRATEGY_INSTALLER; } - private Options(List availableStores, - Map storeKeys, - boolean checkInventory, - int checkInventoryTimeout, - @MagicConstant(intValues = {VERIFY_EVERYTHING, VERIFY_ONLY_KNOWN, VERIFY_SKIP}) int verifyMode, - List preferredStoreNames, - int samsungCertificationRequestCode) { + private Options(final Set availableStores, + final Map storeKeys, + final boolean checkInventory, + final @MagicConstant(intValues = {VERIFY_EVERYTHING, VERIFY_ONLY_KNOWN, VERIFY_SKIP}) int verifyMode, + final Set preferredStoreNames, + final int samsungCertificationRequestCode, + final int storeSearchStrategy) { this.checkInventory = checkInventory; - this.checkInventoryTimeoutMs = checkInventoryTimeout; this.availableStores = availableStores; this.storeKeys = storeKeys; this.preferredStoreNames = preferredStoreNames; this.verifyMode = verifyMode; this.samsungCertificationRequestCode = samsungCertificationRequestCode; + this.storeSearchStrategy = storeSearchStrategy; } /** - * Used for SamsungApps setup. Specify your own value if default one interfere your code. + * Used for SamsungApps setup. Specify your own value using {@link Builder#setSamsungCertificationRequestCode(int)} if default one interfere your with code. *

    - * default value is {@link org.onepf.oms.appstore.SamsungAppsBillingService#REQUEST_CODE_IS_ACCOUNT_CERTIFICATION} + * default value is {@link SamsungAppsBillingService#REQUEST_CODE_IS_ACCOUNT_CERTIFICATION} */ public int getSamsungCertificationRequestCode() { return samsungCertificationRequestCode; @@ -1327,8 +1308,9 @@ public int getSamsungCertificationRequestCode() { /** * OpenIAB could skip receipt verification by publicKey for GooglePlay and OpenStores *

    - * Receipt could be verified in {@link org.onepf.oms.appstore.googleUtils.IabHelper.OnIabPurchaseFinishedListener#onIabPurchaseFinished(org.onepf.oms.appstore.googleUtils.IabResult, org.onepf.oms.appstore.googleUtils.Purchase)} - * using {@link org.onepf.oms.appstore.googleUtils.Purchase#getOriginalJson()} and {@link org.onepf.oms.appstore.googleUtils.Purchase#getSignature()} + * Receipt could be verified in {@link IabHelper.OnIabPurchaseFinishedListener#onIabPurchaseFinished(IabResult, Purchase)} + * using {@link Purchase#getOriginalJson()} and {@link Purchase#getSignature()} + * @see Builder#setVerifyMode(int) */ @MagicConstant(intValues = {VERIFY_EVERYTHING, VERIFY_ONLY_KNOWN, VERIFY_SKIP}) public int getVerifyMode() { @@ -1336,7 +1318,17 @@ public int getVerifyMode() { } /** - * Check user inventory in every store to select proper store + * Set strategy to help OpenIAB pick correct store + *

    + * @see Builder#setStoreSearchStrategy(int) + */ + @MagicConstant(intValues = {SEARCH_STRATEGY_INSTALLER, SEARCH_STRATEGY_BEST_FIT, SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT}) + public int getStoreSearchStrategy() { + return storeSearchStrategy; + } + + /** + * Check user inventory in every store to help with selecting proper store *

    * Will try to connect to each billingService and extract user's purchases. * If purchases have been found in the only store that store will be used for further purchases. @@ -1347,12 +1339,16 @@ public boolean isCheckInventory() { } /** - * Wait specified amount of ms to check inventory in all stores + * @deprecated */ + @Deprecated public long getCheckInventoryTimeout() { - return checkInventoryTimeoutMs; + return 0; } + /** + * @deprecated + */ @Deprecated public long getDiscoveryTimeout() { return 0; @@ -1366,10 +1362,9 @@ public long getDiscoveryTimeout() { * GooglePlay, Amazon and SamsungApps could be instantiated directly. OpenStore can be discovered * using {@link OpenIabHelper#discoverOpenStores()} *

    - * If you put only your instance of Appstore in this list OpenIAB will use it - *

    + * If not empty, only this instances of {@link Appstore} will be considered by OpenIAB */ - public List getAvailableStores() { + public @NotNull Set getAvailableStores() { return availableStores; } @@ -1377,13 +1372,12 @@ public List getAvailableStores() { * Used as priority list if store that installed app is not found and there are * multiple stores installed on device that supports billing. */ - public List getPreferredStoreNames() { + public @NotNull Set getPreferredStoreNames() { return preferredStoreNames; } /** * storeKeys is map of [ appstore name -> publicKeyBase64 ] - * Put keys for all stores you support in this Map and pass it to createInstance {@link OpenIabHelper} *

    * publicKey key is used to verify receipt is created by genuine Appstore using * provided signature. It can be found in Developer Console of particular store @@ -1400,58 +1394,48 @@ public Map getStoreKeys() { return storeKeys; } - public Appstore getAvailableStoreWithName(@NotNull String name) { - if (!CollectionUtils.isEmpty(availableStores)) { - for (Appstore s : availableStores) { - if (name.equals(s.getAppstoreName())) { - return s; - } + /** + * Look for store with name among available stores + * @return {@link Appstore} with name if one found, null otherwise. + * @see #getAvailableStores() + * @see Builder#addAvailableStores(java.util.Collection) + */ + public @Nullable Appstore getAvailableStoreWithName(@NotNull final String name) { + for (Appstore s : availableStores) { + if (name.equals(s.getAppstoreName())) { + return s; } } return null; } - public boolean hasPreferredStoreName(@NotNull final String name) { - return getPreferredStoreNames().contains(name); - } - - public boolean hasStoreKey(String storeName) { - return storeKeys != null && storeKeys.containsKey(storeName); - } - - public String getStoreKey(String storeName) { - return storeKeys != null ? storeKeys.get(storeName) : null; - } - /** - * Utility class for create instance of {@link org.onepf.oms.OpenIabHelper.Options} + * Builder class for {@link Options} */ public static final class Builder { - private List preferredStoreNames; - private Map storeKeys; - private List availableStores; - private int checkInventoryTimeout = CHECK_INVENTORY_TIMEOUT; - private boolean checkInventory; + private final Set preferredStoreNames = new LinkedHashSet(); + private final Set availableStores = new HashSet(); + private final Map storeKeys = new HashMap(); + private boolean checkInventory = false; private int samsungCertificationRequestCode = SamsungAppsBillingService.REQUEST_CODE_IS_ACCOUNT_CERTIFICATION; @MagicConstant(intValues = {VERIFY_EVERYTHING, VERIFY_ONLY_KNOWN, VERIFY_SKIP}) private int verifyMode = VERIFY_EVERYTHING; + @MagicConstant(intValues = {SEARCH_STRATEGY_INSTALLER, SEARCH_STRATEGY_BEST_FIT, SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT}) + private int storeSearchStrategy = SEARCH_STRATEGY_INSTALLER; + /** * Add available store to options. * * @param stores Stores to add. - * @see org.onepf.oms.OpenIabHelper.Options#getAvailableStores() + * @see #addAvailableStores(Collection) + * @see Options#getAvailableStores() */ - public Builder addAvailableStores(Appstore... stores) { - if (!CollectionUtils.isEmpty(stores)) { - if (this.availableStores == null) { - this.availableStores = new ArrayList(stores.length); - } - Collections.addAll(this.availableStores, stores); - } + public Builder addAvailableStores(@NotNull final Appstore... stores) { + addAvailableStores(Arrays.asList(stores)); return this; } @@ -1459,69 +1443,37 @@ public Builder addAvailableStores(Appstore... stores) { * Add available store to options. * * @param stores Stores to add. - * @see org.onepf.oms.OpenIabHelper.Options#getAvailableStores(). + * @see Options#getAvailableStores(). */ - public Builder addAvailableStores(List stores) { - if (!CollectionUtils.isEmpty(stores)) { - if (this.availableStores == null) { - this.availableStores = new ArrayList(stores.size()); - } - this.availableStores.addAll(stores); - } + public Builder addAvailableStores(@NotNull final Collection stores) { + this.availableStores.addAll(stores); return this; } /** - * Set check inventory. By default is true. + * Set check inventory, false. * - * @see org.onepf.oms.OpenIabHelper.Options#isCheckInventory() + * @see Options#isCheckInventory() */ - public Builder setCheckInventory(boolean checkInventory) { + public Builder setCheckInventory(final boolean checkInventory) { this.checkInventory = checkInventory; return this; } - @Deprecated - public Builder setDiscoveryTimeout(int discoveryTimeout) { - return this; - } - /** - * Set inventory check timeout. By default 10 sec. - * This value has no effect if {@link org.onepf.oms.OpenIabHelper.Options.Builder#setCheckInventory(boolean)} - * set to false. - * - * @throws java.lang.IllegalArgumentException if timeout is negative value. - * @see org.onepf.oms.OpenIabHelper.Options#getCheckInventoryTimeout() - * @see org.onepf.oms.OpenIabHelper.Options.Builder#setCheckInventory(boolean) + * No longer used. */ - public Builder setCheckInventoryTimeout(int checkInventoryTimeout) { - if (checkInventoryTimeout < 0) { - throw new IllegalArgumentException("Check inventory timeout can't be" + - " a negative value."); - } - this.checkInventoryTimeout = checkInventoryTimeout; + @Deprecated + public Builder setDiscoveryTimeout(final int discoveryTimeout) { return this; } /** - * Get list of added available stores. - * - * @return List of available store of null if nothing was add. - */ - @Nullable - public List getAvailableStores() { - return availableStores; - } - - /** - * Get map "store name -> public key" of added store keys. - * - * @return Map of added store keys or null if nothing was add. + * No longer used. */ - @Nullable - public Map getStoreKeys() { - return storeKeys; + @Deprecated + public Builder setCheckInventoryTimeout(final int checkInventoryTimeout) { + return this; } /** @@ -1529,32 +1481,10 @@ public Map getStoreKeys() { * * @param storeName Name of store. * @param publicKey Key of store. - * @throws java.lang.IllegalArgumentException If value pair (storeName, publicKey) can't be add. - * It can be when store name empty or null, - * or store public key is not in base64 decode format. - * @see org.onepf.oms.OpenIabHelper.Options#getStoreKeys() + * @throws java.lang.IllegalArgumentException When store public key is not in valid base64 format. + * @see Options#getStoreKeys() */ - public Builder addStoreKey(String storeName, String publicKey) { - checkStoreKeyParam(storeName, publicKey); - - if (this.storeKeys == null) { - this.storeKeys = new HashMap(); - } - this.storeKeys.put(storeName, publicKey); - return this; - } - - private static void checkStoreKeyParam(String storeName, String publicKey) { - if (TextUtils.isEmpty(storeName)) { - throw new IllegalArgumentException( - "Store name can't be null or empty value."); - } - - if (TextUtils.isEmpty(publicKey)) { - throw new IllegalArgumentException( - "Store public key can't be null or empty value."); - } - + public Builder addStoreKey(@NotNull final String storeName, @NotNull final String publicKey) { try { Security.generatePublicKey(publicKey); } catch (Exception e) { @@ -1563,72 +1493,78 @@ private static void checkStoreKeyParam(String storeName, String publicKey) { storeName, publicKey), e); } + this.storeKeys.put(storeName, publicKey); + return this; } /** - * Set verify mode for store. By default set to {@link org.onepf.oms.OpenIabHelper.Options#VERIFY_EVERYTHING}. + * Add store keys to options. * - * @param verifyMode Verify doe for store. Must be on of {@link org.onepf.oms.OpenIabHelper.Options#VERIFY_EVERYTHING}, - * {@link org.onepf.oms.OpenIabHelper.Options#VERIFY_SKIP}, - * {@link org.onepf.oms.OpenIabHelper.Options#VERIFY_ONLY_KNOWN}. - * @see org.onepf.oms.OpenIabHelper.Options#getVerifyMode() + * @param storeKeys Map storeName - store public key. + * @throws java.lang.IllegalArgumentException If some key in map is not in valid base64 format. + * @see Options.Builder#addStoreKeys(java.util.Map) + * @see Options#getStoreKeys() + */ + public Builder addStoreKeys(@NotNull final Map storeKeys) { + for (final String key : storeKeys.keySet()) { + final String value; + if (!TextUtils.isEmpty(value = storeKeys.get(key))) { + addStoreKey(key, value); + } + } + return this; + } + + /** + * Set verify mode for store. By default set to {@link Options#VERIFY_EVERYTHING}. + * + * @param verifyMode Verify mode for store. Must be one of {@link Options#VERIFY_EVERYTHING}, + * {@link Options#VERIFY_SKIP}, + * {@link Options#VERIFY_ONLY_KNOWN}. + * @see Options#getVerifyMode() */ public Builder setVerifyMode( - @MagicConstant(intValues = {VERIFY_EVERYTHING, VERIFY_ONLY_KNOWN, VERIFY_SKIP}) int verifyMode) { + final @MagicConstant(intValues = { + VERIFY_EVERYTHING, + VERIFY_ONLY_KNOWN, + VERIFY_SKIP}) int verifyMode) { this.verifyMode = verifyMode; return this; } /** - * Add store keys to options. * - * @param storeKeys Map storeName - store public key. - * @throws java.lang.IllegalArgumentException If one of item in map can't be add. - * @see org.onepf.oms.OpenIabHelper.Options.Builder#addStoreKeys(java.util.Map) - * @see org.onepf.oms.OpenIabHelper.Options#getStoreKeys() + * @param storeSearchStrategy Store search strategy for OpenIAB. + * Must be one of {@link #SEARCH_STRATEGY_INSTALLER}, {@link #SEARCH_STRATEGY_BEST_FIT} or {@link #SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT} + * @see Options#getStoreSearchStrategy() */ - public Builder addStoreKeys(Map storeKeys) { - if (!CollectionUtils.isEmpty(storeKeys)) { - for (Entry entry : storeKeys.entrySet()) { - checkStoreKeyParam(entry.getKey(), entry.getValue()); - } - - if (this.storeKeys == null) { - this.storeKeys = new HashMap(); - } - - this.storeKeys.putAll(storeKeys); - } + public Builder setStoreSearchStrategy( + final @MagicConstant(intValues = { + SEARCH_STRATEGY_INSTALLER, + SEARCH_STRATEGY_BEST_FIT, + SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT}) int storeSearchStrategy) { + this.storeSearchStrategy = storeSearchStrategy; return this; } /** * Add preferred stores to options. Priority of selection is order in what stores add. * - * @see org.onepf.oms.OpenIabHelper.Options#getPreferredStoreNames() + * @see #addPreferredStoreName(java.util.Collection) + * @see Options#getPreferredStoreNames() */ - public Builder addPreferredStoreName(String... storeNames) { - if (!CollectionUtils.isEmpty(storeNames)) { - if (this.preferredStoreNames == null) { - this.preferredStoreNames = new ArrayList(storeNames.length); - } - Collections.addAll(this.preferredStoreNames, storeNames); - } + public Builder addPreferredStoreName(@NotNull final String... storeNames) { + addPreferredStoreName(Arrays.asList(storeNames)); return this; } /** * Add preferred stores to options. Priority of selection is order in what stores add. * - * @see org.onepf.oms.OpenIabHelper.Options#getPreferredStoreNames() + * @see Options#getPreferredStoreNames() */ - public Builder addPreferredStoreName(List storeNames) { - if (!CollectionUtils.isEmpty(storeNames)) { - if (this.preferredStoreNames == null) { - this.preferredStoreNames = new ArrayList(storeNames.size()); - } - this.preferredStoreNames.addAll(storeNames); - } + public Builder addPreferredStoreName(@NotNull final Collection storeNames) { + this.preferredStoreNames.addAll(storeNames); return this; } @@ -1637,7 +1573,7 @@ public Builder addPreferredStoreName(List storeNames) { * * @param code Request code. Must be positive value. * @throws java.lang.IllegalArgumentException if code negative or zero value. - * @see org.onepf.oms.OpenIabHelper.Options#getSamsungCertificationRequestCode() + * @see Options#getSamsungCertificationRequestCode() */ public Builder setSamsungCertificationRequestCode(int code) { if (code < 0) { @@ -1650,23 +1586,17 @@ public Builder setSamsungCertificationRequestCode(int code) { } /** - * @return Create new instance of {@link org.onepf.oms.OpenIabHelper.Options}. + * @return Create new instance of {@link Options}. */ public Options build() { - List availableStores = CollectionUtils.isEmpty(this.availableStores) ? Collections.emptyList() : - Collections.unmodifiableList(this.availableStores); - Map storeKeys = CollectionUtils.isEmpty(this.storeKeys) ? null : - Collections.unmodifiableMap(this.storeKeys); - List preferredStoreNames = CollectionUtils.isEmpty(this.preferredStoreNames) ? Collections.emptyList() : - new ArrayList(this.preferredStoreNames); return new Options( - availableStores, - storeKeys, + Collections.unmodifiableSet(availableStores), + Collections.unmodifiableMap(storeKeys), checkInventory, - checkInventoryTimeout, verifyMode, - preferredStoreNames, - samsungCertificationRequestCode); + Collections.unmodifiableSet(preferredStoreNames), + samsungCertificationRequestCode, + storeSearchStrategy); } } } From 3178c12b71f3d1713fb1df237c5a76c29e3fb727 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Sat, 13 Sep 2014 21:12:20 +0400 Subject: [PATCH 22/72] Fix sample, to work with latest OpenIab.Options fixes. --- .../src/main/java/org/onepf/lifegame/GameActivity.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/life_openiab/src/main/java/org/onepf/lifegame/GameActivity.java b/samples/life_openiab/src/main/java/org/onepf/lifegame/GameActivity.java index 976f5e5a..7d676135 100644 --- a/samples/life_openiab/src/main/java/org/onepf/lifegame/GameActivity.java +++ b/samples/life_openiab/src/main/java/org/onepf/lifegame/GameActivity.java @@ -301,17 +301,17 @@ public void startSetup() { OpenIabHelper.Options.Builder builder = new OpenIabHelper.Options.Builder(); builder.addStoreKeys(Config.STORE_KEYS_MAP); - if (verifyStorePublicKeys(builder)) { + final OpenIabHelper.Options options = builder.build(); + if (verifyStorePublicKeys(options)) { showErrorMessage(R.string.error_no_store_public_keys, true); } else { - OpenIabHelper.Options options = builder.build(); openIabHelper = new OpenIabHelper(GameActivity.this, options); openIabHelper.startSetup(new LifeGameOnIabSetupFinishedListener()); } } - private boolean verifyStorePublicKeys(OpenIabHelper.Options.Builder builder) { - final Map storeKeys = builder.getStoreKeys(); + private boolean verifyStorePublicKeys(OpenIabHelper.Options options) { + final Map storeKeys = options.getStoreKeys(); if (CollectionUtils.isEmpty(storeKeys)) { return false; } else { From 80412428be365d30e5eaeb614bbf8f3f15593b65 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 15 Sep 2014 21:24:39 +0400 Subject: [PATCH 23/72] Refactor inventory handling. --- .../java/org/onepf/oms/OpenIabHelper.java | 302 +++++++++--------- .../onepf/oms/appstore/AmazonAppstore.java | 5 +- .../org/onepf/oms/appstore/GooglePlay.java | 2 +- .../org/onepf/oms/appstore/SamsungApps.java | 4 +- .../main/java/org/onepf/oms/util/Utils.java | 8 +- 5 files changed, 171 insertions(+), 150 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index ad92909d..3f8e2d31 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -16,7 +16,6 @@ package org.onepf.oms; -import java.security.PublicKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -27,12 +26,14 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadFactory; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; @@ -50,7 +51,6 @@ import org.onepf.oms.appstore.googleUtils.Inventory; import org.onepf.oms.appstore.googleUtils.Purchase; import org.onepf.oms.appstore.googleUtils.Security; -import org.onepf.oms.util.CollectionUtils; import org.onepf.oms.util.Logger; import org.onepf.oms.util.Utils; @@ -131,11 +131,11 @@ public class OpenIabHelper { // Is an asynchronous operation in progress? // (only one at a time can be in progress) - private boolean mAsyncInProgress = false; + private volatile boolean mAsyncInProgress = false; // (for logging/debugging) // if mAsyncInProgress == true, what asynchronous operation is in progress? - private String mAsyncOperation = ""; + private volatile String mAsyncOperation = ""; // Item types public static final String ITEM_TYPE_INAPP = "inapp"; @@ -171,7 +171,7 @@ private static interface AppstoreFactory{ appstorePackageMap.put("cm.aptoide.pt", NAME_APTOIDE); - appstorePackageMap.put("com.android.vending", NAME_GOOGLE); + appstorePackageMap.put(GooglePlay.ANDROID_INSTALLER, NAME_GOOGLE); appstoreFactoryMap.put(NAME_GOOGLE, new AppstoreFactory() { @Override public Appstore get() { @@ -180,7 +180,7 @@ public Appstore get() { } }); - appstorePackageMap.put("com.amazon.venezia", NAME_AMAZON); + appstorePackageMap.put(AmazonAppstore.AMAZON_INSTALLER, NAME_AMAZON); appstoreFactoryMap.put(NAME_AMAZON, new AppstoreFactory() { @Override public Appstore get() { @@ -188,7 +188,7 @@ public Appstore get() { } }); - appstorePackageMap.put("com.sec.android.app.samsungapps", NAME_SAMSUNG); + appstorePackageMap.put(SamsungApps.SAMSUNG_INSTALLER, NAME_SAMSUNG); appstoreFactoryMap.put(NAME_SAMSUNG, new AppstoreFactory() { @Override public Appstore get() { @@ -197,7 +197,7 @@ public Appstore get() { }); // TODO check package - appstorePackageMap.put("com.nokia.nstore", NAME_NOKIA); + appstorePackageMap.put(NokiaStore.NOKIA_INSTALLER, NAME_NOKIA); appstoreFactoryMap.put(NAME_NOKIA, new AppstoreFactory() { @Override public Appstore get() { @@ -684,26 +684,32 @@ public void discoverOpenStores(@NotNull final OpenStoresDiscoveredListener liste private void discoverOpenStores(@NotNull final OpenStoresDiscoveredListener listener, @NotNull final Queue bindServiceIntents, @NotNull final List appstores) { - final ServiceConnection serviceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(final ComponentName name, final IBinder service) { - Appstore openAppstore = null; - try { - openAppstore = getOpenAppstore(name, service, this); - } catch (RemoteException ignore) {} - if (openAppstore != null) { - appstores.add(openAppstore); + while (!bindServiceIntents.isEmpty()) { + final Intent intent = bindServiceIntents.poll(); + final ServiceConnection serviceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + Appstore openAppstore = null; + try { + openAppstore = getOpenAppstore(name, service, this); + } catch (RemoteException exception) { + Logger.w("onServiceConnected() Error creating appsotre: ", exception); + } + if (openAppstore != null) { + appstores.add(openAppstore); + } + discoverOpenStores(listener, bindServiceIntents, appstores); } - discoverOpenStores(listener, bindServiceIntents, appstores); - } - @Override - public void onServiceDisconnected(final ComponentName name) {} - }; + @Override + public void onServiceDisconnected(final ComponentName name) {} + }; - while (!bindServiceIntents.isEmpty()) { - if (context.bindService(bindServiceIntents.poll(), serviceConnection, Context.BIND_AUTO_CREATE)) { + if (context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) { + // Wait for open store service return; + } else { + Logger.e("discoverOpenStores() Couldn't connect to open store: " + intent); } } @@ -712,13 +718,13 @@ public void onServiceDisconnected(final ComponentName name) {} @Deprecated /** - * Use {@link OpenIabHelper#discoverOpenStores(OpenStoresDiscoveredListener)} or {@link OpenIabHelper#discoverOpenStores()} instead. + * Use {@link discoverOpenStores(OpenStoresDiscoveredListener)} or {@link discoverOpenStores()} instead. */ public static List discoverOpenStores(final Context context, final List dest, final Options options) { throw new UnsupportedOperationException("This action is no longer supported."); } - private List queryOpenStoreServices() { + private @NotNull List queryOpenStoreServices() { final Intent intentAppstoreServices = new Intent(BIND_INTENT); final PackageManager packageManager = context.getPackageManager(); final List resolveInfos = packageManager.queryIntentServices(intentAppstoreServices, 0); @@ -730,11 +736,11 @@ private List queryOpenStoreServices() { } /** - * Must be called after setup is finished. See {@link org.onepf.oms.OpenIabHelper#startSetup(org.onepf.oms.appstore.googleUtils.IabHelper.OnIabSetupFinishedListener)} + * Must be called after setup is finished. See {@link #startSetup(OnIabSetupFinishedListener)} * * @return null if no appstore connected, otherwise name of Appstore OpenIAB has connected to. */ - public synchronized String getConnectedAppstoreName() { + public @Nullable String getConnectedAppstoreName() { if (mAppstore == null) return null; return mAppstore.getAppstoreName(); } @@ -788,8 +794,8 @@ private void checkSamsung() { // Unfortunately, SamsungApps requires to launch their own "Certification Activity" // in order to connect to billing service. So it's also needed for OpenIAB. // - // Intance of Activity needs to be passed to OpenIAB constructor to launch - // Samsung Cerfitication Activity. + // Instance of Activity needs to be passed to OpenIAB constructor to launch + // Samsung Certification Activity. // Activity also need to pass activityResult to OpenIABHelper.handleActivityResult() throw new IllegalArgumentException("You must supply Activity object as context in order to use " + NAME_SAMSUNG + " store"); } @@ -798,62 +804,71 @@ private void checkSamsung() { } /** - * Connects to Billing Service of each store. Request list of user purchases (inventory) + * Connects to Billing Service of each store and request list of user purchases (inventory). + * Can be used as a factor when looking for best fitting store. * * @param availableStores - list of stores to check - * @return list of stores with non-empty inventory - * @see org.onepf.oms.OpenIabHelper#CHECK_INVENTORY_TIMEOUT + * @return first found store with not empty inventory, null otherwise. */ - protected List checkInventory(final List availableStores) { - String packageName = context.getPackageName(); - // candidates: - Map candidates = new HashMap(); - for (Appstore appstore : availableStores) { - if (appstore.isBillingAvailable(packageName)) { - candidates.put(appstore.getAppstoreName(), appstore); - } + private @Nullable Appstore checkInventory(@NotNull final Set availableStores) { + if (Utils.uiThread()) { + throw new IllegalStateException("Must not be called from UI thread"); } - Logger.dWithTimeFromUp(candidates.size(), " inventory candidates"); - final List equippedStores = Collections.synchronizedList(new ArrayList()); - final CountDownLatch storeRemains = new CountDownLatch(candidates.size()); - // for every appstore: connect to billing service and check inventory - for (Map.Entry entry : candidates.entrySet()) { - final Appstore appstore = entry.getValue(); - final AppstoreInAppBillingService billingService = entry.getValue().getInAppBillingService(); - billingService.startSetup(new OnIabSetupFinishedListener() { - public void onIabSetupFinished(IabResult result) { - Logger.dWithTimeFromUp("billing set ", appstore.getAppstoreName()); - if (result.isFailure()) { - storeRemains.countDown(); + + final Semaphore inventorySemaphore = new Semaphore(0); + final ExecutorService inventoryExecutor = Executors.newSingleThreadExecutor(); + final Appstore[] inventoryAppstore = new Appstore[1]; + + for (final Appstore appstore : availableStores) { + final AppstoreInAppBillingService billingService = appstore.getInAppBillingService(); + final OnIabSetupFinishedListener listener = new OnIabSetupFinishedListener() { + @Override + public void onIabSetupFinished(final IabResult result) { + if (!result.isSuccess()) { + inventorySemaphore.release(); return; } - new Thread(new Runnable() { + // queryInventory() is a blocking call and must be call from background + final Runnable checkInventoryRunnable = new Runnable() { + @Override public void run() { try { - Inventory inventory = billingService.queryInventory(false, null, null); - if (!inventory.getAllPurchases().isEmpty()) { - equippedStores.add(appstore); + final Inventory inventory = billingService.queryInventory(false, null, null); + if (inventory != null && !inventory.getAllPurchases().isEmpty()) { + inventoryAppstore[0] = appstore; + Logger.dWithTimeFromUp("inventoryCheck() in ", + appstore.getAppstoreName(), " found: ", + inventory.getAllPurchases().size(), " purchases"); } - Logger.dWithTimeFromUp("inventoryCheck() in ", - appstore.getAppstoreName(), " found: ", - inventory.getAllPurchases().size(), " purchases"); - } catch (IabException e) { - Logger.e("inventoryCheck() failed for ", appstore.getAppstoreName()); + } catch (IabException exception) { + Logger.e("inventoryCheck() failed for ", appstore.getAppstoreName() + " : ", exception); } - storeRemains.countDown(); + inventorySemaphore.release(); } - }, "inv-check[" + appstore.getAppstoreName() + ']').start(); + }; + inventoryExecutor.execute(checkInventoryRunnable); + } + }; + // startSetup() must be called form UI thread + handler.post(new Runnable() { + @Override + public void run() { + billingService.startSetup(listener); } }); + + try { + inventorySemaphore.acquire(); + } catch (InterruptedException exception) { + Logger.e("checkInventory() Error during inventory check: ",exception); + return null; + } + if (inventoryAppstore[0] != null) { + return inventoryAppstore[0]; + } } - try { - storeRemains.await(); - Logger.dWithTimeFromUp("inventory check done"); - } catch (InterruptedException e) { - Logger.e(e, "selectBillingService() inventory check is failed. candidates: ", candidates.size() - , ", inventory remains: ", storeRemains.getCount()); - } - return equippedStores; + + return null; } public void dispose() { @@ -917,7 +932,9 @@ public boolean handleActivityResult(int requestCode, int resultCode, Intent data /** * See {@link #queryInventory(boolean, List, List)} for details */ - public Inventory queryInventory(boolean querySkuDetails, List moreSkus) throws IabException { + public @Nullable Inventory queryInventory(final boolean querySkuDetails, + @Nullable final List moreSkus) + throws IabException { return queryInventory(querySkuDetails, moreSkus, null); } @@ -934,10 +951,16 @@ public Inventory queryInventory(boolean querySkuDetails, List moreSkus) * Ignored if null or if querySkuDetails is false. * @throws IabException if a problem occurs while refreshing the inventory. */ - public Inventory queryInventory(boolean querySkuDetails, List moreItemSkus, List moreSubsSkus) throws IabException { + public @Nullable Inventory queryInventory(final boolean querySkuDetails, + @Nullable final List moreItemSkus, + @Nullable final List moreSubsSkus) + throws IabException { + if (Utils.uiThread()) { + throw new IllegalStateException("Must not be called from UI thread"); + } checkSetupDone("queryInventory"); - List moreItemStoreSkus; + final List moreItemStoreSkus; final SkuManager skuManager = SkuManager.getInstance(); if (moreItemSkus != null) { moreItemStoreSkus = new ArrayList(moreItemSkus.size()); @@ -948,7 +971,7 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk moreItemStoreSkus = null; } - List moreSubsStoreSkus; + final List moreSubsStoreSkus; if (moreSubsSkus != null) { moreSubsStoreSkus = new ArrayList(moreSubsSkus.size()); for (String sku : moreSubsSkus) { @@ -960,6 +983,30 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk return mAppstoreBillingService.queryInventory(querySkuDetails, moreItemStoreSkus, moreSubsStoreSkus); } + /** + * @see #queryInventoryAsync(boolean, List, List, IabHelper.QueryInventoryFinishedListener) + */ + public void queryInventoryAsync(@NotNull final IabHelper.QueryInventoryFinishedListener listener) { + queryInventoryAsync(true, listener); + } + + /** + * @see #queryInventoryAsync(boolean, List, List, IabHelper.QueryInventoryFinishedListener) + */ + public void queryInventoryAsync(final boolean querySkuDetails, + @NotNull IabHelper.QueryInventoryFinishedListener listener) { + queryInventoryAsync(querySkuDetails, null, listener); + } + + /** + * @see #queryInventoryAsync(boolean, List, List, IabHelper.QueryInventoryFinishedListener) + */ + public void queryInventoryAsync(final boolean querySkuDetails, + @Nullable final List moreSkus, + @NotNull final IabHelper.QueryInventoryFinishedListener listener) { + queryInventoryAsync(querySkuDetails, moreSkus, null, listener); + } + /** * Queries the inventory. This will query all owned items from the server, as well as * information on additional skus, if specified. This method may block or take long to execute. @@ -970,22 +1017,27 @@ public Inventory queryInventory(boolean querySkuDetails, List moreItemSk * Ignored if null or if querySkuDetails is false. * @param moreSubsSkus additional SUBSCRIPTIONS skus to query information on, regardless of ownership. * Ignored if null or if querySkuDetails is false. - * @throws IabException if a problem occurs while refreshing the inventory. */ - public void queryInventoryAsync(final boolean querySkuDetails, final List moreItemSkus, final List moreSubsSkus, final IabHelper.QueryInventoryFinishedListener listener) { + public void queryInventoryAsync(final boolean querySkuDetails, + @Nullable final List moreItemSkus, + @Nullable final List moreSubsSkus, + @NotNull final IabHelper.QueryInventoryFinishedListener listener) { checkSetupDone("queryInventory"); + //noinspection ConstantConditions if (listener == null) { throw new IllegalArgumentException("Inventory listener must be not null"); } flagStartAsync("refresh inventory"); - (new Thread(new Runnable() { + new Thread(new Runnable() { public void run() { - IabResult result = new IabResult(BILLING_RESPONSE_RESULT_OK, "Inventory refresh successful."); + IabResult result; Inventory inv = null; try { inv = queryInventory(querySkuDetails, moreItemSkus, moreSubsSkus); - } catch (IabException ex) { - result = ex.getResult(); + result = new IabResult(BILLING_RESPONSE_RESULT_OK, "Inventory refresh successful."); + } catch (IabException exception) { + result = exception.getResult(); + Logger.e("queryInventoryAsync() Error : ",exception); } flagEndAsync(); @@ -1000,81 +1052,48 @@ public void run() { }); } } - })).start(); + }).start(); } - /** - * For details see {@link org.onepf.oms.OpenIabHelper#queryInventoryAsync(boolean, java.util.List, java.util.List, org.onepf.oms.appstore.googleUtils.IabHelper.QueryInventoryFinishedListener)} - */ - public void queryInventoryAsync(final boolean querySkuDetails, final List moreSkus, final IabHelper.QueryInventoryFinishedListener listener) { - checkSetupDone("queryInventoryAsync"); - if (listener == null) { - throw new IllegalArgumentException("Inventory listener must be not null!"); - } - queryInventoryAsync(querySkuDetails, moreSkus, null, listener); - } - - /** - * For details see {@link org.onepf.oms.OpenIabHelper#queryInventoryAsync(boolean, java.util.List, java.util.List, org.onepf.oms.appstore.googleUtils.IabHelper.QueryInventoryFinishedListener)} - */ - public void queryInventoryAsync(IabHelper.QueryInventoryFinishedListener listener) { - checkSetupDone("queryInventoryAsync"); - if (listener == null) { - throw new IllegalArgumentException("Inventory listener must be not null!"); - } - queryInventoryAsync(true, null, listener); - } - - /** - * For details see {@link org.onepf.oms.OpenIabHelper#queryInventoryAsync(boolean, java.util.List, java.util.List, org.onepf.oms.appstore.googleUtils.IabHelper.QueryInventoryFinishedListener)} - */ - public void queryInventoryAsync(boolean querySkuDetails, IabHelper.QueryInventoryFinishedListener listener) { - checkSetupDone("queryInventoryAsync"); - if (listener == null) { - throw new IllegalArgumentException("Inventory listener must be not null!"); - } - queryInventoryAsync(querySkuDetails, null, listener); - } - - public void consume(Purchase itemInfo) throws IabException { + public void consume(Purchase purchase) throws IabException { checkSetupDone("consume"); - Purchase purchaseStoreSku = (Purchase) itemInfo.clone(); // TODO: use Purchase.getStoreSku() - purchaseStoreSku.setSku(SkuManager.getInstance().getStoreSku(mAppstore.getAppstoreName(), itemInfo.getSku())); + Purchase purchaseStoreSku = (Purchase) purchase.clone(); // TODO: use Purchase.getStoreSku() + purchaseStoreSku.setSku(SkuManager.getInstance().getStoreSku(mAppstore.getAppstoreName(), purchase.getSku())); mAppstoreBillingService.consume(purchaseStoreSku); } - public void consumeAsync(Purchase purchase, IabHelper.OnConsumeFinishedListener listener) { - checkSetupDone("consumeAsync"); - if (listener == null) { - throw new IllegalArgumentException("Consume listener must be not null!"); - } - List purchases = new ArrayList(); - purchases.add(purchase); - consumeAsyncInternal(purchases, listener, null); + public void consumeAsync(@NotNull final Purchase purchase, + @NotNull final IabHelper.OnConsumeFinishedListener listener) { + consumeAsyncInternal(Arrays.asList(purchase), listener, null); } - public void consumeAsync(List purchases, IabHelper.OnConsumeMultiFinishedListener listener) { - checkSetupDone("consumeAsync"); + public void consumeAsync(@NotNull final List purchases, + @NotNull final IabHelper.OnConsumeMultiFinishedListener listener) { + //noinspection ConstantConditions if (listener == null) { throw new IllegalArgumentException("Consume listener must be not null!"); } consumeAsyncInternal(purchases, null, listener); } - void consumeAsyncInternal(final List purchases, - final IabHelper.OnConsumeFinishedListener singleListener, - final IabHelper.OnConsumeMultiFinishedListener multiListener) { + void consumeAsyncInternal(@NotNull final List purchases, + @Nullable final IabHelper.OnConsumeFinishedListener singleListener, + @Nullable final IabHelper.OnConsumeMultiFinishedListener multiListener) { checkSetupDone("consume"); + if (purchases.isEmpty()) { + throw new IllegalArgumentException("Nothing to consume."); + } flagStartAsync("consume"); - (new Thread(new Runnable() { + new Thread(new Runnable() { public void run() { final List results = new ArrayList(); - for (Purchase purchase : purchases) { + for (final Purchase purchase : purchases) { try { consume(purchase); results.add(new IabResult(BILLING_RESPONSE_RESULT_OK, "Successful consume of sku " + purchase.getSku())); - } catch (IabException ex) { - results.add(ex.getResult()); + } catch (IabException exception) { + results.add(exception.getResult()); + Logger.e("consumeAsyncInternal() Error : ", exception); } } @@ -1094,13 +1113,13 @@ public void run() { }); } } - })).start(); + }).start(); } // Checks that setup was done; if not, throws an exception. void checkSetupDone(String operation) { - String stateToString = setupStateToString(setupState); if (setupState != SETUP_RESULT_SUCCESSFUL) { + String stateToString = setupStateToString(setupState); Logger.e("Illegal state for operation (", operation, "): ", stateToString); throw new IllegalStateException(stateToString + " Can't perform operation: " + operation); } @@ -1166,11 +1185,6 @@ public static void enableDebuglLogging(boolean enabled, String tag) { Logger.setLoggable(enabled); } - public static boolean isPackageInstaller(Context appContext, String installer) { - String installerPackageName = appContext.getPackageManager().getInstallerPackageName(appContext.getPackageName()); - return installerPackageName != null && installerPackageName.equals(installer); - } - public interface OnInitListener { void onInitFinished(); } diff --git a/library/src/main/java/org/onepf/oms/appstore/AmazonAppstore.java b/library/src/main/java/org/onepf/oms/appstore/AmazonAppstore.java index 527d665b..ad69e59a 100644 --- a/library/src/main/java/org/onepf/oms/appstore/AmazonAppstore.java +++ b/library/src/main/java/org/onepf/oms/appstore/AmazonAppstore.java @@ -21,6 +21,7 @@ import org.onepf.oms.DefaultAppstore; import org.onepf.oms.OpenIabHelper; import org.onepf.oms.util.Logger; +import org.onepf.oms.util.Utils; import android.content.Context; @@ -35,7 +36,7 @@ * @since 16.04.13 */ public class AmazonAppstore extends DefaultAppstore { - private static final String AMAZON_INSTALLER = "com.amazon.venezia"; + public static final String AMAZON_INSTALLER = "com.amazon.venezia"; private final Context context; @@ -47,7 +48,7 @@ public AmazonAppstore(Context context) { @Override public boolean isPackageInstaller(String packageName) { - final boolean amazonIsInstaller = OpenIabHelper.isPackageInstaller(context, AMAZON_INSTALLER); + final boolean amazonIsInstaller = Utils.isPackageInstaller(context, AMAZON_INSTALLER); final boolean result = amazonIsInstaller || hasAmazonClasses(); Logger.d("isPackageInstaller() sandBox: ", PurchasingService.IS_SANDBOX_MODE); return PurchasingService.IS_SANDBOX_MODE || result; diff --git a/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java b/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java index ad8da2fe..a4d22ac7 100644 --- a/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java +++ b/library/src/main/java/org/onepf/oms/appstore/GooglePlay.java @@ -72,7 +72,7 @@ public boolean isPackageInstaller(String packageName) { if (isDebugMode) { return true; } - return OpenIabHelper.isPackageInstaller(context, ANDROID_INSTALLER); + return Utils.isPackageInstaller(context, ANDROID_INSTALLER); } /** diff --git a/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java b/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java index e510e9f8..9e28b726 100644 --- a/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java +++ b/library/src/main/java/org/onepf/oms/appstore/SamsungApps.java @@ -63,7 +63,7 @@ * @since 10.10.2013 */ public class SamsungApps extends DefaultAppstore { - private static final String SAMSUNG_INSTALLER = "com.sec.android.app.samsungapps"; + public static final String SAMSUNG_INSTALLER = "com.sec.android.app.samsungapps"; private static final int IAP_SIGNATURE_HASHCODE = 0x7a7eaf4b; public static final String IAP_PACKAGE_NAME = "com.sec.android.iap"; @@ -85,7 +85,7 @@ public SamsungApps(Activity activity, OpenIabHelper.Options options) { @Override public boolean isPackageInstaller(String packageName) { - return OpenIabHelper.isPackageInstaller(activity, SAMSUNG_INSTALLER) || isSamsungTestMode; + return Utils.isPackageInstaller(activity, SAMSUNG_INSTALLER) || isSamsungTestMode; } /** diff --git a/library/src/main/java/org/onepf/oms/util/Utils.java b/library/src/main/java/org/onepf/oms/util/Utils.java index 128d7a6b..e9ba7c73 100644 --- a/library/src/main/java/org/onepf/oms/util/Utils.java +++ b/library/src/main/java/org/onepf/oms/util/Utils.java @@ -43,7 +43,7 @@ public static boolean hasRequestedPermission(@NotNull Context context, final Str return false; } - public static boolean packageInstalled(@NotNull Context context, final String packageName) { + public static boolean packageInstalled(@NotNull final Context context, final String packageName) { final PackageManager packageManager = context.getPackageManager(); try { packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); @@ -52,6 +52,12 @@ public static boolean packageInstalled(@NotNull Context context, final String pa return false; } + public static boolean isPackageInstaller(@NotNull final Context context, final String packageName) { + final PackageManager packageManager = context.getPackageManager(); + final String installerPackageName = packageManager.getInstallerPackageName(context.getPackageName()); + return TextUtils.equals(installerPackageName, packageName); + } + public static boolean uiThread() { return Thread.currentThread() == Looper.getMainLooper().getThread(); } From d1f01e18b8cf63aef1a6fc53c2298347f6b22d84 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Wed, 17 Sep 2014 16:17:58 +0400 Subject: [PATCH 24/72] Minor store picking algorithm refactoring; Change 'withInventory' option handling --- .../java/org/onepf/oms/OpenIabHelper.java | 161 ++++++++++++------ .../main/java/org/onepf/oms/util/Utils.java | 2 +- 2 files changed, 112 insertions(+), 51 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 3f8e2d31..9cb58cdb 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -28,12 +28,11 @@ import java.util.Map; import java.util.Queue; import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; -import java.util.concurrent.ThreadFactory; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; @@ -77,14 +76,6 @@ * @since 16.04.13 */ public class OpenIabHelper { - /** - * Default timeout (in milliseconds) for check inventory in all stores. - * For generic stores it takes 1.5 - 3sec. - *

    - * SamsungApps initialization is very time consuming (from 4 to 12 seconds). - * TODO: Optimize: ~1sec is consumed for check account certification via account activity + ~3sec for actual setup - */ - private static final int CHECK_INVENTORY_TIMEOUT = 10 * 1000; private static final String BIND_INTENT = "org.onepf.oms.openappstore.BIND"; @@ -357,7 +348,7 @@ public OpenIabHelper(Context context, Options options) { Logger.init(); } - private Executor setupThreadPoolExecutor; + private ExecutorService setupExecutorService; /** * Discover all available stores and select the best billing service. @@ -366,7 +357,8 @@ public OpenIabHelper(Context context, Options options) { * * @param listener - called when setup is completed */ - public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { + public void startSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener) { + //noinspection ConstantConditions if (listener == null) { throw new IllegalArgumentException("Setup listener must be not null!"); } @@ -375,40 +367,66 @@ public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { } Logger.init(); setupState = SETUP_IN_PROGRESS; - setupThreadPoolExecutor = Executors.newSingleThreadExecutor(); + setupExecutorService = Executors.newSingleThreadExecutor(); - final String packageName = context.getPackageName(); final int storeSearchStrategy = options.getStoreSearchStrategy(); + final String packageName = context.getPackageName(); final String packageInstaller = packageManager.getInstallerPackageName(packageName); - final boolean packageInstallerAvailable = !TextUtils.isEmpty(packageInstaller) && Utils.packageInstalled(context, packageInstaller); - if ((storeSearchStrategy == SEARCH_STRATEGY_INSTALLER || storeSearchStrategy == SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT) - && packageInstallerAvailable) { - // Package installer is not set - setupForPackage(listener, packageInstaller, storeSearchStrategy == SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT); + final boolean packageInstallerSet = !TextUtils.isEmpty(packageInstaller); + + if (storeSearchStrategy == SEARCH_STRATEGY_INSTALLER) { + // Check only package installer + if (packageInstallerSet) { + // Check without fallback + setupForPackage(listener, packageInstaller, false); + } else { + // Package installer isn't available + finishSetup(listener); + } + } else if (storeSearchStrategy == SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT) { + // Check package installer then all others + if (packageInstallerSet) { + // Check with fallback + setupForPackage(listener, packageInstaller, true); + } else { + // Check other stores + setup(listener); + } } else { setup(listener); } } - // TODO simplify conditions - // TODO decompose to a separate methods? private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener, final String packageInstaller, final boolean withFallback) { - Appstore appstore = null; + if (!Utils.packageInstalled(context, packageInstaller)) { + // Package installer is no longer available + if (withFallback) { + // Check other stores + setup(listener); + } else { + finishSetup(listener); + } + return; + } + Appstore appstore = null; if (appstorePackageMap.containsKey(packageInstaller)) { // Package installer is a known appstore final String appstoreName = appstorePackageMap.get(packageInstaller); - if (!options.getAvailableStores().isEmpty()) { + if (options.getAvailableStores().isEmpty()) { + if (appstoreFactoryMap.containsKey(appstoreName)) { + appstore = appstoreFactoryMap.get(appstoreName).get(); + } + } else { // Developer explicitly specified available stores appstore = options.getAvailableStoreWithName(appstoreName); if (appstore == null) { + // Store is known but isn't available finishSetup(listener); return; } - } else if (appstoreFactoryMap.containsKey(appstoreName)) { - appstore = appstoreFactoryMap.get(appstoreName).get(); } } @@ -429,9 +447,9 @@ private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener if (bindServiceIntent == null) { // Package installer not found if (withFallback) { - // Fallback to default algorithm + // Check other stores setup(listener); - }else { + } else { finishSetup(listener); } return; @@ -446,22 +464,33 @@ public void onServiceConnected(final ComponentName name, final IBinder service) if (openAppstore != null) { // Found open store final String openStoreName = openAppstore.getAppstoreName(); - if (!options.getAvailableStores().isEmpty()) { + if (options.getAvailableStores().isEmpty()) { + appstore = openAppstore; + } else { // Developer explicitly specified available stores appstore = options.getAvailableStoreWithName(openStoreName); - } else { - appstore = openAppstore; } } - } catch (RemoteException ignore) {} - checkBillingAndFinish(listener, appstore); + } catch (RemoteException exception) { + Logger.e("setupForPackage() Error binding to open store service : ", exception); + } + if (appstore == null && withFallback) { + setup(listener); + } else { + checkBillingAndFinish(listener, appstore); + } } @Override public void onServiceDisconnected(final ComponentName name) {} }, Context.BIND_AUTO_CREATE)) { // Can't bind to open store service - finishSetupWithError(listener); + Logger.e("setupForPackage() Error binding to open store service"); + if (withFallback) { + setup(listener); + } else { + finishSetupWithError(listener); + } } } @@ -560,23 +589,54 @@ private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedLi finishSetup(listener); return; } - setupThreadPoolExecutor.execute(new Runnable() { - @Override - public void run() { - Appstore checkedAppstore = null; - for (final Appstore appstore : appstores) { - if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { - checkedAppstore = appstore; - break; + + final Callable callable; + if (options.isCheckInventory()) { + callable = new Callable() { + @Override + public Appstore call() { + final List availableAppstores = new ArrayList(); + for (final Appstore appstore : appstores) { + if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { + availableAppstores.add(appstore); + } } + Appstore checkedAppstore = checkInventory(new HashSet(appstores)); + if (checkedAppstore == null) { + checkedAppstore = availableAppstores.isEmpty() ? null : availableAppstores.get(0); + } + return checkedAppstore; } - final Appstore appstore = checkedAppstore; - handler.post(new Runnable() { - @Override - public void run() { - finishSetup(listener, appstore); + }; + } else { + callable = new Callable() { + @Override + public Appstore call() { + for (final Appstore appstore : appstores) { + if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { + return appstore; + } } - }); + return null; + } + }; + } + + setupExecutorService.execute(new Runnable() { + @Override + public void run() { + final Appstore appstore; + try { + appstore = callable.call(); + handler.post(new Runnable() { + @Override + public void run() { + finishSetup(listener, appstore); + } + }); + return; + } catch (Exception ignore) {} + finishSetup(listener); } }); } @@ -598,7 +658,7 @@ private void finishSetupWithError(@NotNull final IabHelper.OnIabSetupFinishedLis private void finishSetupWithError(@NotNull final IabHelper.OnIabSetupFinishedListener listener, @Nullable final Exception exception) { - Logger.e("finishSetupWithError() error occurred during setup: ", exception == null ? "" : exception); + Logger.e("finishSetupWithError() error occurred during setup", exception == null ? "" : " : " + exception); finishSetup(listener, new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Error occured, setup failed"), null); } @@ -627,7 +687,8 @@ private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener lis final boolean setUpSuccessful = iabResult.isSuccess(); setupState = setUpSuccessful ? SETUP_RESULT_SUCCESSFUL : SETUP_RESULT_FAILED; activity = null; - setupThreadPoolExecutor = null; + setupExecutorService.shutdownNow(); + setupExecutorService = null; if (setUpSuccessful) { if (appstore == null) { throw new IllegalStateException("Appstore can't be null if setup is successful"); @@ -718,7 +779,7 @@ public void onServiceDisconnected(final ComponentName name) {} @Deprecated /** - * Use {@link discoverOpenStores(OpenStoresDiscoveredListener)} or {@link discoverOpenStores()} instead. + * Use {@link #discoverOpenStores(OpenStoresDiscoveredListener)} or {@link #discoverOpenStores()} instead. */ public static List discoverOpenStores(final Context context, final List dest, final Options options) { throw new UnsupportedOperationException("This action is no longer supported."); diff --git a/library/src/main/java/org/onepf/oms/util/Utils.java b/library/src/main/java/org/onepf/oms/util/Utils.java index e9ba7c73..4158843f 100644 --- a/library/src/main/java/org/onepf/oms/util/Utils.java +++ b/library/src/main/java/org/onepf/oms/util/Utils.java @@ -43,7 +43,7 @@ public static boolean hasRequestedPermission(@NotNull Context context, final Str return false; } - public static boolean packageInstalled(@NotNull final Context context, final String packageName) { + public static boolean packageInstalled(@NotNull final Context context,@NotNull final String packageName) { final PackageManager packageManager = context.getPackageManager(); try { packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); From 9f24782d3e6a372906d17f6d0226579306f240ff Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Wed, 17 Sep 2014 20:37:42 +0400 Subject: [PATCH 25/72] Minor bug fixes. --- .../java/org/onepf/oms/OpenIabHelper.java | 74 ++++++++++--------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 9cb58cdb..46305598 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -70,6 +70,7 @@ import static org.onepf.oms.OpenIabHelper.Options.SEARCH_STRATEGY_INSTALLER; import static org.onepf.oms.OpenIabHelper.Options.SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT; +import static org.onepf.oms.OpenIabHelper.Options.VERIFY_EVERYTHING; /** * @author Boris Minaev, Oleg Orlov, Kirill Rozov @@ -187,7 +188,6 @@ public Appstore get() { } }); - // TODO check package appstorePackageMap.put(NokiaStore.NOKIA_INSTALLER, NAME_NOKIA); appstoreFactoryMap.put(NAME_NOKIA, new AppstoreFactory() { @Override @@ -535,7 +535,7 @@ public void openStoresDiscovered(@NotNull final List appstores) { } // Add everything else appstoresToCheck.addAll(allAvailableAppsotres); - checkBillingAndFinish(listener, allAvailableAppsotres); + checkBillingAndFinish(listener, appstoresToCheck); } }); } @@ -543,26 +543,30 @@ public void openStoresDiscovered(@NotNull final List appstores) { @Nullable private OpenAppstore getOpenAppstore(final ComponentName name, - final IBinder service, final ServiceConnection serviceConnection) + final IBinder service, + final ServiceConnection serviceConnection) throws RemoteException { final IOpenAppstore openAppstoreService = IOpenAppstore.Stub.asInterface(service); final String appstoreName = openAppstoreService.getAppstoreName(); final Intent billingIntent = openAppstoreService.getBillingServiceIntent(); + final int verifyMode = options.getVerifyMode(); + final String publicKey = verifyMode == Options.VERIFY_SKIP + ? null + : options.getStoreKeys().get(appstoreName); if (TextUtils.isEmpty(appstoreName)) { // no name - no service - Logger.d("discoverOpenStores() Appstore doesn't have name. Skipped. ComponentName: ", name); + Logger.d("getOpenAppstore() Appstore doesn't have name. Skipped. ComponentName: ", name); } else if (billingIntent == null) { - Logger.d("discoverOpenStores(): billing is not supported by store: ", name); + Logger.d("getOpenAppstore(): billing is not supported by store: ", name); + } else if (verifyMode == Options.VERIFY_EVERYTHING && TextUtils.isEmpty(publicKey)) { + // don't connect to OpenStore if no key provided and verification is strict + Logger.e("getOpenAppstore() verification is required but publicKey is not provided: ", name); } else { - // TODO Rethink store verification - return new OpenAppstore(context, appstoreName, openAppstoreService, billingIntent, null, serviceConnection); + final OpenAppstore openAppstore = + new OpenAppstore(context, appstoreName, openAppstoreService, billingIntent, publicKey, serviceConnection); + openAppstore.componentName = name; + return openAppstore; } -// if ((options.verifyMode == Options.VERIFY_EVERYTHING) && !options.hasStoreKey(appstoreName)) { -// don't connect to OpenStore if no key provided and verification is strict -// Logger.e("discoverOpenStores() verification is required but publicKey is not provided: ", name); -// } -// String publicKey = options.getStoreKey(appstoreName); -// if (options.verifyMode == Options.VERIFY_SKIP) publicKey = null; return null; } @@ -628,14 +632,16 @@ public void run() { final Appstore appstore; try { appstore = callable.call(); - handler.post(new Runnable() { + handler.postDelayed(new Runnable() { @Override public void run() { finishSetup(listener, appstore); } - }); + }, 5000); return; - } catch (Exception ignore) {} + } catch (Exception exception) { + Logger.e("checkBillingAndFinish() unknown error : ", exception); + } finishSetup(listener); } }); @@ -810,24 +816,9 @@ public static List discoverOpenStores(final Context context, final Lis * Check options are valid */ public void checkOptions() { + checkGoogle(); checkSamsung(); checkNokia(); - // TODO Rethink store verification strategy - // check publicKeys. Must be not null and valid -// if (options.verifyMode != Options.VERIFY_SKIP && options.storeKeys != null) { -// for (Entry entry : options.storeKeys.entrySet()) { -// if (entry.getValue() == null) { -// throw new IllegalArgumentException("Null publicKey for store: " + entry.getKey() + ", key: " + entry.getValue()); -// } -// -// try { -// Security.generatePublicKey(entry.getValue()); -// } catch (Exception e) { -// throw new IllegalArgumentException("Invalid publicKey for store: " -// + entry.getKey() + ", key: " + entry.getValue(), e); -// } -// } -// } } private void checkNokia() { @@ -864,6 +855,22 @@ private void checkSamsung() { appstoreFactoryMap.remove(NAME_SAMSUNG); } + private void checkGoogle() { + final boolean googleKeyProvided = options.getStoreKeys().containsKey(NAME_GOOGLE); + Logger.d("checkGoogle() google key available : ", googleKeyProvided); + if (googleKeyProvided) { + return; + } + + final boolean googleRequired = options.getAvailableStoreWithName(NAME_GOOGLE) != null + || options.getPreferredStoreNames().contains(NAME_GOOGLE); + if (googleRequired && options.getVerifyMode() == VERIFY_EVERYTHING) { + throw new IllegalStateException("You must supply Google verification key"); + } + Logger.d("checkGoogle() ignoring GooglePlay wrapper", googleKeyProvided); + appstoreFactoryMap.remove(NAME_GOOGLE); + } + /** * Connects to Billing Service of each store and request list of user purchases (inventory). * Can be used as a factor when looking for best fitting store. @@ -1464,8 +1471,7 @@ public long getDiscoveryTimeout() { * AmazonApps and SamsungApps doesn't use RSA keys for receipt verification, so you don't need * to specify it */ - @Nullable - public Map getStoreKeys() { + @NotNull public Map getStoreKeys() { return storeKeys; } From 3932af1c455e864f4cf23c412db2432f12852292 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Thu, 18 Sep 2014 15:03:36 +0400 Subject: [PATCH 26/72] Remove redundant synchronization conditions. --- .../java/org/onepf/oms/OpenIabHelper.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 46305598..77edcdb5 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -1095,7 +1095,6 @@ public void queryInventoryAsync(final boolean querySkuDetails, if (listener == null) { throw new IllegalArgumentException("Inventory listener must be not null"); } - flagStartAsync("refresh inventory"); new Thread(new Runnable() { public void run() { IabResult result; @@ -1108,8 +1107,6 @@ public void run() { Logger.e("queryInventoryAsync() Error : ",exception); } - flagEndAsync(); - final IabResult result_f = result; final Inventory inv_f = inv; if (setupState != SETUP_DISPOSED) { @@ -1151,7 +1148,6 @@ void consumeAsyncInternal(@NotNull final List purchases, if (purchases.isEmpty()) { throw new IllegalArgumentException("Nothing to consume."); } - flagStartAsync("consume"); new Thread(new Runnable() { public void run() { final List results = new ArrayList(); @@ -1165,7 +1161,6 @@ public void run() { } } - flagEndAsync(); if (setupState != SETUP_DISPOSED && singleListener != null) { handler.post(new Runnable() { public void run() { @@ -1193,23 +1188,6 @@ void checkSetupDone(String operation) { } } - void flagStartAsync(String operation) { - // TODO: why can't be called consume and queryInventory at the same time? -// if (mAsyncInProgress) { -// throw new IllegalStateException("Can't start async operation (" + -// operation + ") because another async operation(" + mAsyncOperation + ") is in progress."); -// } - mAsyncOperation = operation; - mAsyncInProgress = true; - Logger.d("Starting async operation: ", operation); - } - - void flagEndAsync() { - Logger.d("Ending async operation: ", mAsyncOperation); - mAsyncOperation = ""; - mAsyncInProgress = false; - } - private static String setupStateToString(int setupState) { String state; if (setupState == SETUP_RESULT_NOT_STARTED) { From 8355a19bd40e3ecbbe108bcf644914261139f4ae Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Thu, 18 Sep 2014 19:29:05 +0400 Subject: [PATCH 27/72] Cleanup debug changes. --- library/src/main/java/org/onepf/oms/OpenIabHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 77edcdb5..76fb5860 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -632,12 +632,12 @@ public void run() { final Appstore appstore; try { appstore = callable.call(); - handler.postDelayed(new Runnable() { + handler.post(new Runnable() { @Override public void run() { finishSetup(listener, appstore); } - }, 5000); + }); return; } catch (Exception exception) { Logger.e("checkBillingAndFinish() unknown error : ", exception); From b08a3c17461978cc45cca7414d08744378c62b4c Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Thu, 18 Sep 2014 20:09:40 +0400 Subject: [PATCH 28/72] Fix checkInventory bug --- library/src/main/java/org/onepf/oms/OpenIabHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 76fb5860..998230fe 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -605,7 +605,7 @@ public Appstore call() { availableAppstores.add(appstore); } } - Appstore checkedAppstore = checkInventory(new HashSet(appstores)); + Appstore checkedAppstore = checkInventory(new HashSet(availableAppstores)); if (checkedAppstore == null) { checkedAppstore = availableAppstores.isEmpty() ? null : availableAppstores.get(0); } @@ -911,6 +911,7 @@ public void run() { } catch (IabException exception) { Logger.e("inventoryCheck() failed for ", appstore.getAppstoreName() + " : ", exception); } + billingService.dispose(); inventorySemaphore.release(); } }; From 8ef3bbee818eab1caf8febe1e0aac1b99878d3d8 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 22 Sep 2014 19:35:50 +0400 Subject: [PATCH 29/72] Update Yandex Store skus. --- .../src/main/java/org/onepf/trivialdrivegame/Config.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java b/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java index b80fce78..5b32c240 100644 --- a/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java +++ b/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java @@ -70,9 +70,9 @@ public final class Config { public static final String SKU_INFINITE_GAS_AMAZON = "amazon.sku_infinite_gas"; public static final String SKU_PREMIUM_AMAZON = "amazon.sku_premium"; - public static final String SKU_GAS_YANDEX = "yandex.sku_gas"; - public static final String SKU_INFINITE_GAS_YANDEX = "yandex.sku_infinite_gas"; - public static final String SKU_PREMIUM_YANDEX = "yandex.sku_premium"; + public static final String SKU_GAS_YANDEX = "org.onepf.trivialdrivegame.sku_gas"; + public static final String SKU_INFINITE_GAS_YANDEX = "org.onepf.trivialdrivegame.sku_infinite_gas"; + public static final String SKU_PREMIUM_YANDEX = "org.onepf.trivialdrivegame.sku_premium"; public static final String SKU_GAS_APPLAND = "appland.sku_gas"; public static final String SKU_INFINITE_GAS_APPLAND = "appland.sku_infinite_gas"; From 8bec30cd62a3e33965cd6fb0fa9829cfab33c3e8 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 22 Sep 2014 19:36:18 +0400 Subject: [PATCH 30/72] Add store picking algorithm diagram. --- specification/store_picking_algorithm.puml | 68 ++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 specification/store_picking_algorithm.puml diff --git a/specification/store_picking_algorithm.puml b/specification/store_picking_algorithm.puml new file mode 100644 index 00000000..8b591b6b --- /dev/null +++ b/specification/store_picking_algorithm.puml @@ -0,0 +1,68 @@ +@startuml +partition "Pick Appstore" { + start + :onStartSetup(); + :Check Options.storeSearchStrategy; + if (SEARCH_STRATEGY_INSTALLER) then + :Check package installer| + elseif (SEARCH_STRATEGY_BEST_FIT) then + :Check all available appstores| + elseif (SEARCH_STRATEGY_INSTALLER_THEN_BEST_FIT) then + :Check package installer| + if (Appstore found?) then + else (no) + :Check all available appstores| + endif + endif + if (Appstore found?) then (yes) + :Appstore.startSetup(); + endif + :onSetupFinished(); + stop +} +@enduml + +@startuml +partition "Package Installer" { + start + if (Package installer available?) then (yes) + if (Options.availableStores is set?) then (yes) + note right + Check in order specified + by Options.preferedStores + end note + :Check available stores; + else (no) + :Check all available wrappers; + :Check all open stores; + endif + if (Appstore found?) then (yes) + :Check Appstore.isBillingAvailable(); + endif + endif + stop +} +@enduml + +@startuml +partition "All available Appstores" { + start + if (Options.availableStores is set?) then (yes) + else (no) + :Discover all open stores; + :Check all available wrappers; + endif + :Filter not installed; + :Check Appstore.isBillingAvailable(); + note + Check in order specified + by Options.preferedStores + end note + if (Options.checkInventory is set?) then (yes) + repeat + :checkInventory(); + repeat while (Inventory is empty and there's more Appstores to check) + endif + stop +} +@enduml From e2d8b534c7dac00884f52b404ea3f9e34bb3d93a Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 22 Sep 2014 19:36:59 +0400 Subject: [PATCH 31/72] Fix open store setup bug. --- .../java/org/onepf/oms/OpenIabHelper.java | 114 +++++++++++------- 1 file changed, 72 insertions(+), 42 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 998230fe..05303280 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -357,7 +357,7 @@ public OpenIabHelper(Context context, Options options) { * * @param listener - called when setup is completed */ - public void startSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener) { + public void startSetup(@NotNull final OnIabSetupFinishedListener listener) { //noinspection ConstantConditions if (listener == null) { throw new IllegalArgumentException("Setup listener must be not null!"); @@ -397,7 +397,7 @@ public void startSetup(@NotNull final IabHelper.OnIabSetupFinishedListener liste } } - private void setupForPackage(final IabHelper.OnIabSetupFinishedListener listener, + private void setupForPackage(final OnIabSetupFinishedListener listener, final String packageInstaller, final boolean withFallback) { if (!Utils.packageInstalled(context, packageInstaller)) { @@ -494,7 +494,7 @@ public void onServiceDisconnected(final ComponentName name) {} } } - private void setup(final IabHelper.OnIabSetupFinishedListener listener) { + private void setup(final OnIabSetupFinishedListener listener) { // List of wrappers to check final Set appstoresToCheck = new LinkedHashSet(); @@ -577,7 +577,7 @@ private Intent getBindServiceIntent(final ServiceInfo serviceInfo) { return bindServiceIntent; } - private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + private void checkBillingAndFinish(@NotNull final OnIabSetupFinishedListener listener, @Nullable final Appstore appstore) { if (appstore == null) { finishSetup(listener); @@ -586,7 +586,7 @@ private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedLi } } - private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + private void checkBillingAndFinish(@NotNull final OnIabSetupFinishedListener listener, @NotNull final Collection appstores) { final String packageName = context.getPackageName(); if (appstores.isEmpty()) { @@ -594,11 +594,11 @@ private void checkBillingAndFinish(@NotNull final IabHelper.OnIabSetupFinishedLi return; } - final Callable callable; + final Runnable checkStoresRunnable; if (options.isCheckInventory()) { - callable = new Callable() { + checkStoresRunnable = new Runnable() { @Override - public Appstore call() { + public void run() { final List availableAppstores = new ArrayList(); for (final Appstore appstore : appstores) { if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { @@ -606,45 +606,78 @@ public Appstore call() { } } Appstore checkedAppstore = checkInventory(new HashSet(availableAppstores)); + final Appstore foundAppstore; if (checkedAppstore == null) { - checkedAppstore = availableAppstores.isEmpty() ? null : availableAppstores.get(0); + foundAppstore = availableAppstores.isEmpty() ? null : availableAppstores.get(0); + } else { + foundAppstore = checkedAppstore; } - return checkedAppstore; + final OnIabSetupFinishedListener listenerWrapper = new OnIabSetupFinishedListener() { + @Override + public void onIabSetupFinished(final IabResult result) { + // Dispose of all initialized open appstores + final Collection appstoresToDispose = new ArrayList(); + if (foundAppstore != null) { + appstoresToDispose.remove(foundAppstore); + } + dispose(appstoresToDispose); + listener.onIabSetupFinished(result); + } + }; + handler.post(new Runnable() { + @Override + public void run() { + finishSetup(listenerWrapper, foundAppstore); + } + }); } }; } else { - callable = new Callable() { + checkStoresRunnable = new Runnable() { @Override - public Appstore call() { + public void run() { + Appstore checkedAppstore = null; for (final Appstore appstore : appstores) { if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { - return appstore; + checkedAppstore = appstore; + break; } } - return null; - } - }; - } - - setupExecutorService.execute(new Runnable() { - @Override - public void run() { - final Appstore appstore; - try { - appstore = callable.call(); + final Appstore foundAppstore = checkedAppstore; + final OnIabSetupFinishedListener listenerWrapper = new OnIabSetupFinishedListener() { + @Override + public void onIabSetupFinished(final IabResult result) { + // Dispose of all initialized open appstores + final Collection appstoresToDispose = new ArrayList(); + if (foundAppstore != null) { + appstoresToDispose.remove(foundAppstore); + } + dispose(appstoresToDispose); + if (foundAppstore != null) { + foundAppstore.getInAppBillingService().startSetup(listener); + } else { + listener.onIabSetupFinished(result); + } + } + }; handler.post(new Runnable() { @Override public void run() { - finishSetup(listener, appstore); + finishSetup(listenerWrapper, foundAppstore); } }); - return; - } catch (Exception exception) { - Logger.e("checkBillingAndFinish() unknown error : ", exception); } - finishSetup(listener); - } - }); + }; + } + + setupExecutorService.execute(checkStoresRunnable); + } + + private void dispose(@NotNull final Collection appstores) { + for (final Appstore appstore : appstores) { + final AppstoreInAppBillingService billingService = appstore.getInAppBillingService(); + billingService.dispose(); + } } private boolean versionOk(@NotNull final Appstore appstore) { @@ -658,21 +691,21 @@ private boolean versionOk(@NotNull final Appstore appstore) { return true; } - private void finishSetupWithError(@NotNull final IabHelper.OnIabSetupFinishedListener listener) { + private void finishSetupWithError(@NotNull final OnIabSetupFinishedListener listener) { finishSetupWithError(listener, null); } - private void finishSetupWithError(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + private void finishSetupWithError(@NotNull final OnIabSetupFinishedListener listener, @Nullable final Exception exception) { Logger.e("finishSetupWithError() error occurred during setup", exception == null ? "" : " : " + exception); finishSetup(listener, new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Error occured, setup failed"), null); } - private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener) { + private void finishSetup(@NotNull final OnIabSetupFinishedListener listener) { finishSetup(listener, null); } - private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + private void finishSetup(@NotNull final OnIabSetupFinishedListener listener, @Nullable final Appstore appstore) { final IabResult iabResult = appstore == null ? new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE, "No suitable appstore was found") @@ -680,7 +713,7 @@ private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener lis finishSetup(listener, iabResult, appstore); } - private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener listener, + private void finishSetup(@NotNull final OnIabSetupFinishedListener listener, @NotNull final IabResult iabResult, @Nullable final Appstore appstore) { if (!Utils.uiThread()) { @@ -703,11 +736,7 @@ private void finishSetup(@NotNull final IabHelper.OnIabSetupFinishedListener lis mAppstoreBillingService = appstore.getInAppBillingService(); } Logger.dWithTimeFromUp("finishSetup() === SETUP DONE === result: ", iabResult, " Appstore: ", appstore); - if (mAppstoreBillingService == null) { - listener.onIabSetupFinished(iabResult); - } else { - mAppstoreBillingService.startSetup(listener); - } + listener.onIabSetupFinished(iabResult); } @MagicConstant(intValues = {SETUP_DISPOSED, SETUP_IN_PROGRESS, @@ -911,7 +940,6 @@ public void run() { } catch (IabException exception) { Logger.e("inventoryCheck() failed for ", appstore.getAppstoreName() + " : ", exception); } - billingService.dispose(); inventorySemaphore.release(); } }; @@ -933,10 +961,12 @@ public void run() { return null; } if (inventoryAppstore[0] != null) { + inventoryExecutor.shutdownNow(); return inventoryAppstore[0]; } } + inventoryExecutor.shutdownNow(); return null; } From 6b11dababc6199de2ef486e2bb12b6c1d94f8698 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Mon, 22 Sep 2014 20:33:12 +0400 Subject: [PATCH 32/72] Update Amazon Store skus. --- .../src/main/java/org/onepf/trivialdrivegame/Config.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java b/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java index 5b32c240..40f94650 100644 --- a/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java +++ b/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java @@ -66,9 +66,9 @@ public final class Config { public static final String SKU_INFINITE_GAS_NOKIA_STORE = "1290302"; public static final String SKU_PREMIUM_NOKIA_STORE = "1290315"; - public static final String SKU_GAS_AMAZON = "amazon.sku_gas"; - public static final String SKU_INFINITE_GAS_AMAZON = "amazon.sku_infinite_gas"; - public static final String SKU_PREMIUM_AMAZON = "amazon.sku_premium"; + public static final String SKU_GAS_AMAZON = "org.onepf.trivialdrivegame.sku_gas"; + public static final String SKU_INFINITE_GAS_AMAZON = "org.onepf.trivialdrivegame.subscrption.sku_infinite_gas"; + public static final String SKU_PREMIUM_AMAZON = "org.onepf.trivialdrivegame.sku_premium"; public static final String SKU_GAS_YANDEX = "org.onepf.trivialdrivegame.sku_gas"; public static final String SKU_INFINITE_GAS_YANDEX = "org.onepf.trivialdrivegame.sku_infinite_gas"; From fb31c4e279757a4915a5d38cefdbe93abee044ae Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Wed, 24 Sep 2014 22:36:39 +0400 Subject: [PATCH 33/72] Allow retry setup for OpenIabHelper --- .../main/java/org/onepf/oms/OpenIabHelper.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 05303280..fba0b20e 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -362,7 +362,7 @@ public void startSetup(@NotNull final OnIabSetupFinishedListener listener) { if (listener == null) { throw new IllegalArgumentException("Setup listener must be not null!"); } - if (setupState != SETUP_RESULT_NOT_STARTED) { + if (setupState != SETUP_RESULT_NOT_STARTED && setupState != SETUP_RESULT_FAILED) { throw new IllegalStateException("Couldn't be set up. Current state: " + setupStateToString(setupState)); } Logger.init(); @@ -588,6 +588,10 @@ private void checkBillingAndFinish(@NotNull final OnIabSetupFinishedListener lis private void checkBillingAndFinish(@NotNull final OnIabSetupFinishedListener listener, @NotNull final Collection appstores) { + if (setupState != SETUP_IN_PROGRESS) { + throw new IllegalStateException("Can't check billing. Current state: " + setupStateToString(setupState)); + } + final String packageName = context.getPackageName(); if (appstores.isEmpty()) { finishSetup(listener); @@ -794,7 +798,7 @@ public void onServiceConnected(final ComponentName name, final IBinder service) if (openAppstore != null) { appstores.add(openAppstore); } - discoverOpenStores(listener, bindServiceIntents, appstores); + discoverOpenStores(listener, bindServiceIntents, appstores); } @Override @@ -1212,7 +1216,7 @@ public void run() { // Checks that setup was done; if not, throws an exception. void checkSetupDone(String operation) { - if (setupState != SETUP_RESULT_SUCCESSFUL) { + if (!setupSuccessful()) { String stateToString = setupStateToString(setupState); Logger.e("Illegal state for operation (", operation, "): ", stateToString); throw new IllegalStateException(stateToString + " Can't perform operation: " + operation); @@ -1235,6 +1239,14 @@ private static String setupStateToString(int setupState) { return state; } + + /** + * @return True if this OpenIabHelper instance is set up successfully. + */ + public boolean setupSuccessful() { + return setupState == SETUP_RESULT_SUCCESSFUL; + } + /** * @deprecated Use {@link org.onepf.oms.util.Logger#isLoggable()} *

    From 76d879cd396bc579e48d82e6eff5e16ae7965774 Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Thu, 25 Sep 2014 17:02:14 +0400 Subject: [PATCH 34/72] Possible workaround for discoverOpenStores() bug --- library/src/main/java/org/onepf/oms/OpenIabHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index fba0b20e..7811480d 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -798,7 +798,7 @@ public void onServiceConnected(final ComponentName name, final IBinder service) if (openAppstore != null) { appstores.add(openAppstore); } - discoverOpenStores(listener, bindServiceIntents, appstores); + discoverOpenStores(listener, bindServiceIntents, appstores); } @Override @@ -809,6 +809,8 @@ public void onServiceDisconnected(final ComponentName name) {} // Wait for open store service return; } else { + // TODO It seems serviceConnection still might be called in this point hopefully this will help + context.unbindService(serviceConnection); Logger.e("discoverOpenStores() Couldn't connect to open store: " + intent); } } From 35253d865aa87d21d860ce0a160341ae96dc8daa Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Thu, 25 Sep 2014 18:02:26 +0400 Subject: [PATCH 35/72] Fix orderId missing from Purchase object with Amazon billing. --- .../org/onepf/oms/appstore/AmazonAppstoreBillingService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/src/main/java/org/onepf/oms/appstore/AmazonAppstoreBillingService.java b/library/src/main/java/org/onepf/oms/appstore/AmazonAppstoreBillingService.java index 33c454ce..e4e9a83a 100644 --- a/library/src/main/java/org/onepf/oms/appstore/AmazonAppstoreBillingService.java +++ b/library/src/main/java/org/onepf/oms/appstore/AmazonAppstoreBillingService.java @@ -350,6 +350,8 @@ public void onPurchaseResponse(final PurchaseResponse purchaseResponse) { purchase.setOriginalJson(generateOriginalJson(purchaseResponse)); + purchase.setOrderId(requestId.toString()); + final Receipt receipt = purchaseResponse.getReceipt(); final ProductType productType = receipt.getProductType(); From e3e0eeef79912d04326dc61e38b94449e108c1ec Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Fri, 26 Sep 2014 19:04:33 +0400 Subject: [PATCH 36/72] Add some missing getters to Inventory class. --- .../org/onepf/oms/appstore/googleUtils/Inventory.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/src/main/java/org/onepf/oms/appstore/googleUtils/Inventory.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/Inventory.java index b6dd6d13..10109e76 100644 --- a/library/src/main/java/org/onepf/oms/appstore/googleUtils/Inventory.java +++ b/library/src/main/java/org/onepf/oms/appstore/googleUtils/Inventory.java @@ -17,6 +17,7 @@ package org.onepf.oms.appstore.googleUtils; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -104,4 +105,12 @@ public void addSkuDetails(SkuDetails d) { public void addPurchase(Purchase p) { mPurchaseMap.put(p.getSku(), p); } + + public Map getSkuMap() { + return Collections.unmodifiableMap(mSkuMap); + } + + public Map getPurchaseMap() { + return Collections.unmodifiableMap(mPurchaseMap); + } } From 1e5e645dd03f95256c628af90d14a35f7a2b6d68 Mon Sep 17 00:00:00 2001 From: Shane Isbell Date: Tue, 30 Sep 2014 21:17:42 -0700 Subject: [PATCH 37/72] Added skubit open iab support --- .../android/billing/IBillingService.aidl | 131 ++++++++++++++ .../java/org/onepf/oms/OpenIabHelper.java | 23 ++- .../onepf/oms/appstore/SkubitAppstore.java | 170 ++++++++++++++++++ .../oms/appstore/SkubitTestAppstore.java | 66 +++++++ .../oms/appstore/googleUtils/SkuDetails.java | 2 +- .../skubitUtils/BillingResponseCodes.java | 58 ++++++ .../appstore/skubitUtils/SkubitIabHelper.java | 1 + .../skubitUtils/SkubitTestIabHelper.java | 1 + 8 files changed, 450 insertions(+), 2 deletions(-) create mode 100644 library/src/main/aidl/com/skubit/android/billing/IBillingService.aidl create mode 100644 library/src/main/java/org/onepf/oms/appstore/SkubitAppstore.java create mode 100644 library/src/main/java/org/onepf/oms/appstore/SkubitTestAppstore.java create mode 100644 library/src/main/java/org/onepf/oms/appstore/skubitUtils/BillingResponseCodes.java create mode 100644 library/src/main/java/org/onepf/oms/appstore/skubitUtils/SkubitIabHelper.java create mode 100644 library/src/main/java/org/onepf/oms/appstore/skubitUtils/SkubitTestIabHelper.java diff --git a/library/src/main/aidl/com/skubit/android/billing/IBillingService.aidl b/library/src/main/aidl/com/skubit/android/billing/IBillingService.aidl new file mode 100644 index 00000000..1964f955 --- /dev/null +++ b/library/src/main/aidl/com/skubit/android/billing/IBillingService.aidl @@ -0,0 +1,131 @@ +package com.skubit.android.billing; + +import android.os.Bundle; + +/** + * InAppBillingService is the service that provides in-app billing version 3 and beyond. + * This service provides the following features: + * 1. Provides a new API to get details of in-app items published for the app including + * price, type, title and description. + * 2. The purchase flow is synchronous and purchase information is available immediately + * after it completes. + * 3. Purchase information of in-app purchases is maintained within the Google Play system + * till the purchase is consumed. + * 4. An API to consume a purchase of an inapp item. All purchases of one-time + * in-app items are consumable and thereafter can be purchased again. + * 5. An API to get current purchases of the user immediately. This will not contain any + * consumed purchases. + * + * All calls will give a response code with the following possible values + * RESULT_OK = 0 - success + * RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog + * RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested + * RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase + * RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API + * RESULT_ERROR = 6 - Fatal error during the API action + * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned + * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned + */ +interface IBillingService { + + boolean isAuthenticated(boolean startDialog); + + /** + * Checks support for the requested billing API version, package and in-app type. + * Minimum API version supported by this interface is 3. + * @param apiVersion the billing version which the app is using + * @param packageName the package name of the calling app + * @param type type of the in-app item being purchased "inapp" for one-time purchases + * and "subs" for subscription. + * @return RESULT_OK(0) on success, corresponding result code on failures + */ + int isBillingSupported(int apiVersion, String packageName, String type); + + /** + * Provides details of a list of SKUs + * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle + * with a list JSON strings containing the productId, price, title and description. + * This API can be called with a maximum of 20 SKUs. + * @param apiVersion billing API version that the Third-party is using + * @param packageName the package name of the calling app + * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST" + * @return Bundle containing the following key-value pairs + * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on + * failure as listed above. + * "DETAILS_LIST" with a StringArrayList containing purchase information + * in JSON format similar to: + * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00", + * "title : "Example Title", "description" : "This is an example description" }' + */ + Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle); + + /** + * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU, + * the type, a unique purchase token and an optional developer payload. + * @param apiVersion billing API version that the app is using + * @param packageName package name of the calling app + * @param sku the SKU of the in-app item as published in the developer console + * @param type the type of the in-app item ("inapp" for one-time purchases + * and "subs" for subscription). + * @param developerPayload optional argument to be sent back with the purchase information + * @return Bundle containing the following key-value pairs + * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on + * failure as listed above. + * "BUY_INTENT" - PendingIntent to start the purchase flow + * + * The Pending intent should be launched with startIntentSenderForResult. When purchase flow + * has completed, the onActivityResult() will give a resultCode of OK or CANCELED. + * If the purchase is successful, the result data will contain the following key-value pairs + * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on + * failure as listed above. + * "INAPP_PURCHASE_DATA" - String in JSON format similar to + * '{"orderId":"12999763169054705758.1371079406387615", + * "packageName":"com.example.app", + * "productId":"exampleSku", + * "purchaseTime":1345678900000, + * "purchaseToken" : "122333444455555", + * "developerPayload":"example developer payload" }' + * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that + * was signed with the private key of the developer + * TODO: change this to app-specific keys. + */ + Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type, + String developerPayload); + + /** + * Returns the current SKUs owned by the user of the type and package name specified along with + * purchase information and a signature of the data to be validated. + * This will return all SKUs that have been purchased in V3 and managed items purchased using + * V1 and V2 that have not been consumed. + * @param apiVersion billing API version that the app is using + * @param packageName package name of the calling app + * @param type the type of the in-app items being requested + * ("inapp" for one-time purchases and "subs" for subscription). + * @param continuationToken to be set as null for the first call, if the number of owned + * skus are too many, a continuationToken is returned in the response bundle. + * This method can be called again with the continuation token to get the next set of + * owned skus. + * @return Bundle containing the following key-value pairs + * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on + * failure as listed above. + * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs + * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information + * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures + * of the purchase information + * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the + * next set of in-app purchases. Only set if the + * user has more owned skus than the current list. + */ + Bundle getPurchases(int apiVersion, String packageName, String type, String paginationToken); + + /** + * Consume the last purchase of the given SKU. This will result in this item being removed + * from all subsequent responses to getPurchases() and allow re-purchase of this item. + * @param apiVersion billing API version that the app is using + * @param packageName package name of the calling app + * @param purchaseToken token in the purchase information JSON that identifies the purchase + * to be consumed + * @return 0 if consumption succeeded. Appropriate error values for failures. + */ + int consumePurchase(int apiVersion, String packageName, String purchaseToken); +} diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 7811480d..9367c7b5 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -28,7 +28,6 @@ import java.util.Map; import java.util.Queue; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -43,6 +42,8 @@ import org.onepf.oms.appstore.OpenAppstore; import org.onepf.oms.appstore.SamsungApps; import org.onepf.oms.appstore.SamsungAppsBillingService; +import org.onepf.oms.appstore.SkubitAppstore; +import org.onepf.oms.appstore.SkubitTestAppstore; import org.onepf.oms.appstore.googleUtils.IabException; import org.onepf.oms.appstore.googleUtils.IabHelper; import org.onepf.oms.appstore.googleUtils.IabHelper.OnIabSetupFinishedListener; @@ -143,6 +144,8 @@ public class OpenIabHelper { public static final String NAME_AMAZON = "com.amazon.apps"; public static final String NAME_SAMSUNG = "com.samsung.apps"; public static final String NAME_NOKIA = "com.nokia.nstore"; + public static final String NAME_SKUBIT = "com.skubit.android"; + public static final String NAME_SKUBIT_TEST = "net.skubit.android"; // Knows open stores public static final String NAME_YANDEX = "com.yandex.store"; @@ -195,6 +198,22 @@ public Appstore get() { return new NokiaStore(context); } }); + + appstorePackageMap.put(SkubitAppstore.SKUBIT_INSTALLER, NAME_SKUBIT); + appstoreFactoryMap.put(NAME_SKUBIT, new AppstoreFactory() { + @Override + public Appstore get() { + return new SkubitAppstore(context); + } + }); + + appstorePackageMap.put(SkubitTestAppstore.SKUBIT_INSTALLER, NAME_SKUBIT_TEST); + appstoreFactoryMap.put(NAME_SKUBIT_TEST, new AppstoreFactory() { + @Override + public Appstore get() { + return new SkubitTestAppstore(context); + } + }); } @@ -642,6 +661,8 @@ public void run() { public void run() { Appstore checkedAppstore = null; for (final Appstore appstore : appstores) { + System.out.println("AS: " + appstore.getAppstoreName() + ":" + + appstore.isBillingAvailable(packageName) + ":" + versionOk(appstore)); if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { checkedAppstore = appstore; break; diff --git a/library/src/main/java/org/onepf/oms/appstore/SkubitAppstore.java b/library/src/main/java/org/onepf/oms/appstore/SkubitAppstore.java new file mode 100644 index 00000000..a940e017 --- /dev/null +++ b/library/src/main/java/org/onepf/oms/appstore/SkubitAppstore.java @@ -0,0 +1,170 @@ +/* + * Copyright 2012-2014 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.oms.appstore; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.IBinder; +import android.os.RemoteException; +import android.text.TextUtils; + +import com.skubit.android.billing.IBillingService; + +import org.onepf.oms.Appstore; +import org.onepf.oms.AppstoreInAppBillingService; +import org.onepf.oms.DefaultAppstore; +import org.onepf.oms.OpenIabHelper; +import org.onepf.oms.appstore.googleUtils.IabHelper; +import org.onepf.oms.appstore.skubitUtils.SkubitIabHelper; +import org.onepf.oms.util.CollectionUtils; +import org.onepf.oms.util.Logger; +import org.onepf.oms.util.Utils; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class SkubitAppstore extends DefaultAppstore { + + public static final String SKUBIT_INSTALLER = "com.skubit.android"; + + public static final String VENDING_ACTION = "com.skubit.android.billing.IBillingService.BIND"; + + public static final int TIMEOUT_BILLING_SUPPORTED = 2000; + + protected final Context context; + + protected AppstoreInAppBillingService mBillingService; + + private volatile Boolean billingAvailable = null; + + protected final boolean isDebugMode = false; + + public SkubitAppstore(Context context) { + if(context == null) { + throw new IllegalArgumentException("context is null"); + } + this.context = context; + } + + public String getInstaller() { + return SKUBIT_INSTALLER; + } + + public String getAction() { + return VENDING_ACTION; + } + + @Override + public boolean isPackageInstaller(String packageName) { + if (isDebugMode) { + return true; + } + return Utils.isPackageInstaller(context, SKUBIT_INSTALLER); + } + + /** + * @return true if Skubit is installed in the system + */ + @Override + public boolean isBillingAvailable(final String packageName) { + Logger.d("isBillingAvailable() packageName: ", packageName); + if (billingAvailable != null) { + return billingAvailable; + } + + if (Utils.uiThread()) { + throw new IllegalStateException("Must no be called from UI thread."); + } + + if(TextUtils.isEmpty(packageName)) { + throw new IllegalArgumentException("packageName is null"); + } + + billingAvailable = false; + if (packageExists(context, SKUBIT_INSTALLER)) { + final Intent intent = new Intent(getAction()); + intent.setPackage(getInstaller()); + final List infoList = context.getPackageManager().queryIntentServices(intent, 0); + if (!CollectionUtils.isEmpty(infoList)) { + final CountDownLatch latch = new CountDownLatch(1); + final ServiceConnection serviceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + IBillingService mService = IBillingService.Stub.asInterface(service); + int response; + try { + response = mService.isBillingSupported(1, packageName, IabHelper.ITEM_TYPE_INAPP); + if (response == IabHelper.BILLING_RESPONSE_RESULT_OK) { + billingAvailable = true; + } else { + Logger.d("isBillingAvailable() Google Play billing unavaiable"); + } + } catch (RemoteException e) { + Logger.e("isBillingAvailable() RemoteException while setting up in-app billing", e); + } finally { + latch.countDown(); + context.unbindService(this); + } + } + + public void onServiceDisconnected(ComponentName name) {/*do nothing*/} + }; + if (context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) { + try { + latch.await(TIMEOUT_BILLING_SUPPORTED, TimeUnit.MILLISECONDS); + } catch (InterruptedException ignore) { + } + } + Logger.e("isBillingAvailable() billing is not supported. Initialization error."); + } + } + return billingAvailable; + } + + @Override + public int getPackageVersion(String packageName) { + return Appstore.PACKAGE_VERSION_UNDEFINED; + } + + @Override + public synchronized AppstoreInAppBillingService getInAppBillingService() { + if (mBillingService == null) { + mBillingService = new SkubitIabHelper(context, null, this); + } + return mBillingService; + } + + @Override + public String getAppstoreName() { + return OpenIabHelper.NAME_SKUBIT; + } + + private boolean packageExists(Context context, String packageName) { + try { + context.getPackageManager().getPackageInfo(packageName, 0); + return true; + } catch (PackageManager.NameNotFoundException ignored) { + Logger.d(packageName, " package was not found."); + return false; + } + } + +} diff --git a/library/src/main/java/org/onepf/oms/appstore/SkubitTestAppstore.java b/library/src/main/java/org/onepf/oms/appstore/SkubitTestAppstore.java new file mode 100644 index 00000000..4c45031f --- /dev/null +++ b/library/src/main/java/org/onepf/oms/appstore/SkubitTestAppstore.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2014 One Platform Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onepf.oms.appstore; + +import android.content.Context; + +import org.onepf.oms.AppstoreInAppBillingService; +import org.onepf.oms.OpenIabHelper; +import org.onepf.oms.appstore.skubitUtils.SkubitTestIabHelper; +import org.onepf.oms.util.Utils; + +public class SkubitTestAppstore extends SkubitAppstore { + + public static final String SKUBIT_INSTALLER = "net.skubit.android"; + + public static final String VENDING_ACTION = "net.skubit.android.billing.IBillingService.BIND"; + + public SkubitTestAppstore(Context context) { + super(context); + } + + @Override + public boolean isPackageInstaller(String packageName) { + if (isDebugMode) { + return true; + } + return Utils.isPackageInstaller(context, SKUBIT_INSTALLER); + } + + @Override + public synchronized AppstoreInAppBillingService getInAppBillingService() { + if (mBillingService == null) { + mBillingService = new SkubitTestIabHelper(context, null, this); + } + return mBillingService; + } + + public String getInstaller() { + return SKUBIT_INSTALLER; + } + + public String getAction() { + return VENDING_ACTION; + } + + @Override + public String getAppstoreName() { + return OpenIabHelper.NAME_SKUBIT_TEST; + } + + +} diff --git a/library/src/main/java/org/onepf/oms/appstore/googleUtils/SkuDetails.java b/library/src/main/java/org/onepf/oms/appstore/googleUtils/SkuDetails.java index 5a41c0d5..173d58b0 100644 --- a/library/src/main/java/org/onepf/oms/appstore/googleUtils/SkuDetails.java +++ b/library/src/main/java/org/onepf/oms/appstore/googleUtils/SkuDetails.java @@ -89,7 +89,7 @@ public String getJson() { return mJson; } - void setSku(String sku) { + public void setSku(String sku) { this.mSku = sku; } diff --git a/library/src/main/java/org/onepf/oms/appstore/skubitUtils/BillingResponseCodes.java b/library/src/main/java/org/onepf/oms/appstore/skubitUtils/BillingResponseCodes.java new file mode 100644 index 00000000..f5ea0691 --- /dev/null +++ b/library/src/main/java/org/onepf/oms/appstore/skubitUtils/BillingResponseCodes.java @@ -0,0 +1,58 @@ +/** + * Copyright 2014 Skubit + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.onepf.oms.appstore.skubitUtils; + +public class BillingResponseCodes { + + public static final int RESULT_BILLING_UNAVAILABLE = 3;// - this billing API + // version is not + // supported for the + // type requested + + public static final int RESULT_DEVELOPER_ERROR = 5;// - invalid arguments + // provided to the API + + public static final int RESULT_ERROR = 6;// - Fatal error during the API + // action + + public static final int RESULT_ITEM_ALREADY_OWNED = 7;// - Failure to + // purchase since + // item is + // already owned + + public static final int RESULT_ITEM_NOT_OWNED = 8;// - Failure to consume + // since item is not + // owned + + public static final int RESULT_ITEM_UNAVAILABLE = 4;// - requested SKU is + // not available for + // purchase + + public static final int RESULT_NOT_PROMO_ELIGIBLE = 10; + + public static final int RESULT_OK = 0;// - success + + public static final int RESULT_PROMO_ELIGIBLE = 9; + + public static final int RESULT_SERVICE_UNAVAILABLE = 2; + + public static final int RESULT_USER_CANCELED = 1;// - user pressed back or + // canceled a dialog + + public static final int RESULT_INSUFFICIENT_FUNDS = 100; + +} diff --git a/library/src/main/java/org/onepf/oms/appstore/skubitUtils/SkubitIabHelper.java b/library/src/main/java/org/onepf/oms/appstore/skubitUtils/SkubitIabHelper.java new file mode 100644 index 00000000..877289c0 --- /dev/null +++ b/library/src/main/java/org/onepf/oms/appstore/skubitUtils/SkubitIabHelper.java @@ -0,0 +1 @@ +/* * Copyright 2012-2014 One Platform Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onepf.oms.appstore.skubitUtils; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; import android.content.pm.ResolveInfo; import org.json.JSONException; import org.onepf.oms.Appstore; import org.onepf.oms.AppstoreInAppBillingService; import org.onepf.oms.OpenIabHelper; import org.onepf.oms.SkuManager; import org.onepf.oms.appstore.SkubitAppstore; import org.onepf.oms.appstore.googleUtils.IabException; import org.onepf.oms.appstore.googleUtils.IabHelper; import static org.onepf.oms.appstore.googleUtils.IabHelper.*; import static org.onepf.oms.appstore.skubitUtils.BillingResponseCodes.*; import org.onepf.oms.appstore.googleUtils.IabResult; import org.onepf.oms.appstore.googleUtils.Inventory; import org.onepf.oms.appstore.googleUtils.Purchase; import org.onepf.oms.appstore.googleUtils.Security; import org.onepf.oms.appstore.googleUtils.SkuDetails; import org.onepf.oms.util.Logger; import android.app.Activity; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.text.TextUtils; import com.skubit.android.billing.IBillingService; /** * Provides convenience methods for in-app billing. You can create one instance of this * class for your application and use it to process in-app billing operations. * It provides synchronous (blocking) and asynchronous (non-blocking) methods for * many common in-app billing operations, as well as automatic signature * verification. *

    * After instantiating, you must perform setup in order to start using the object. * To perform setup, call the {@link #startSetup} method and provide a listener; * that listener will be notified when setup is complete, after which (and not before) * you may call other methods. *

    * After setup is complete, you will typically want to request an inventory of owned * items and subscriptions. See {@link #queryInventory}, {@link #queryInventoryAsync} * and related methods. *

    * When you are done with this object, don't forget to call {@link #dispose} * to ensure proper cleanup. This object holds a binding to the in-app billing * service, which will leak unless you dispose of it correctly. If you created * the object on an Activity's onCreate method, then the recommended * place to dispose of it is the Activity's onDestroy method. *

    * A note about threading: When using this object from a background thread, you may * call the blocking versions of methods; when using from a UI thread, call * only the asynchronous versions and handle the results via callbacks. * Also, notice that you can only call one asynchronous operation at a time; * attempting to start a second asynchronous operation while the first one * has not yet completed will result in an exception being thrown. * * @author Bruno Oliveira (Google) * @author sisbell (Modified original class for Skubit support) */ public class SkubitIabHelper implements AppstoreInAppBillingService { /** * TODO: move to Options? * Skubit doesn't support getSkuDetails for more than 30 SKUs at once */ public static final int QUERY_SKU_DETAILS_BATCH_SIZE = 30; // Is setup done? protected boolean mSetupDone = false; // Are subscriptions supported? protected boolean mSubscriptionsSupported = false; // Is an asynchronous operation in progress? // (only one at a time can be in progress) protected boolean mAsyncInProgress = false; // (for logging/debugging) // if mAsyncInProgress == true, what asynchronous operation is in progress? protected String mAsyncOperation = ""; // Context we were passed during initialization protected Context mContext; // Connection to the service protected IBillingService mService; protected ServiceConnection mServiceConn; /** * for debug purposes */ protected ComponentName mComponentName; // The request code used to launch purchase flow protected int mRequestCode; // The item type of the current purchase flow protected String mPurchasingItemType; // Public key for verifying signature, in base64 encoding protected String mSignatureBase64 = null; /** * TODO: IabHelper for Skubit and OpenStore must not be same */ private Appstore mAppstore; /** * Creates an instance. After creation, it will not yet be ready to use. You must perform * setup by calling {@link #startSetup} and wait for setup to complete. This constructor does not * block and is safe to call from a UI thread. * * @param context Your application or Activity context. Needed to bind to the in-app billing service. * @param base64PublicKey Your application's public key, encoded in base64. * This is used for verification of purchase signatures. You can find your app's base64-encoded * public key in your application's page on Google Play Developer Console. Note that this * is NOT your "developer public key". * @param appstore TODO */ public SkubitIabHelper(Context context, String base64PublicKey, Appstore appstore) { if(context == null) { throw new IllegalArgumentException("context is null"); } mContext = context.getApplicationContext(); mSignatureBase64 = base64PublicKey; mAppstore = appstore; Logger.d("Skubit IAB helper created."); } /** * Starts the setup process. This will start up the setup process asynchronously. * You will be notified through the listener when the setup process is complete. * This method is safe to call from a UI thread. * * @param listener The listener to notify when the setup process is complete. */ public void startSetup(final IabHelper.OnIabSetupFinishedListener listener) { // If already set up, can't do it again. if (mSetupDone) throw new IllegalStateException("IAB helper is already set up."); // Connection to IAB service Logger.d("Starting in-app billing setup."); mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Logger.d("Billing service disconnected."); mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Logger.d("Billing service connected."); mService = getServiceFromBinder(service); mComponentName = name; String packageName = mContext.getPackageName(); try { Logger.d("Checking for in-app billing 1 support."); // check for in-app billing v1 support int response = mService.isBillingSupported(1, packageName, ITEM_TYPE_INAPP); if (response != RESULT_OK) { if (listener != null) listener.onIabSetupFinished(new IabResult(response, "Error checking for billing v1 support.")); // if in-app purchases aren't supported, neither are subscriptions. mSubscriptionsSupported = false; return; } Logger.d("In-app billing version 1 supported for ", packageName); // check for v3 subscriptions support response = mService.isBillingSupported(1, packageName, ITEM_TYPE_SUBS); if (response == RESULT_OK) { Logger.d("Subscriptions AVAILABLE."); mSubscriptionsSupported = true; } else { Logger.d("Subscriptions NOT AVAILABLE. Response: ", response); } mSetupDone = true; } catch (RemoteException e) { if (listener != null) { listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION, "RemoteException while setting up in-app billing.")); } Logger.e("RemoteException while setting up in-app billing", e); return; } if (listener != null) { listener.onIabSetupFinished(new IabResult(RESULT_OK, "Setup successful.")); } } }; Intent serviceIntent = getServiceIntent(); final List infoList = mContext.getPackageManager().queryIntentServices(serviceIntent, 0); if (infoList != null && !infoList.isEmpty()) { // service available to handle that Intent mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); } else { // no service available to handle that Intent if (listener != null) { listener.onIabSetupFinished(new IabResult(RESULT_BILLING_UNAVAILABLE, "Billing service unavailable on device.")); } } } /** * IabHelper code is shared between OpenStore and Skubit, but services has different names */ protected Intent getServiceIntent() { final Intent intent = new Intent(SkubitAppstore.VENDING_ACTION); intent.setPackage(SkubitAppstore.SKUBIT_INSTALLER); return intent; } /** * Override to return needed service interface */ protected IBillingService getServiceFromBinder(IBinder service) { return IBillingService.Stub.asInterface(service); } /** * Dispose of object, releasing resources. It's very important to call this * method when you are done with this object. It will release any resources * used by it such as service connections. Naturally, once the object is * disposed of, it can't be used again. */ public void dispose() { Logger.d("Disposing."); mSetupDone = false; if (mServiceConn != null) { Logger.d("Unbinding from service."); if (mContext != null) mContext.unbindService(mServiceConn); mServiceConn = null; mService = null; mPurchaseListener = null; } } /** * Returns whether subscriptions are supported. */ public boolean subscriptionsSupported() { return mSubscriptionsSupported; } public void setSubscriptionsSupported(boolean subscriptionsSupported) { mSubscriptionsSupported = subscriptionsSupported; } public void setSetupDone(boolean setupDone) { mSetupDone = setupDone; } // The listener registered on launchPurchaseFlow, which we have to call back when // the purchase finishes IabHelper.OnIabPurchaseFinishedListener mPurchaseListener; public void launchPurchaseFlow(Activity act, String sku, int requestCode, IabHelper.OnIabPurchaseFinishedListener listener) { launchPurchaseFlow(act, sku, requestCode, listener, ""); } public void launchPurchaseFlow(Activity act, String sku, int requestCode, IabHelper.OnIabPurchaseFinishedListener listener, String extraData) { launchPurchaseFlow(act, sku, ITEM_TYPE_INAPP, requestCode, listener, extraData); } public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, IabHelper.OnIabPurchaseFinishedListener listener) { launchSubscriptionPurchaseFlow(act, sku, requestCode, listener, ""); } public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode, IabHelper.OnIabPurchaseFinishedListener listener, String extraData) { launchPurchaseFlow(act, sku, ITEM_TYPE_SUBS, requestCode, listener, extraData); } /** * Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase, * which will involve bringing up the Google Play screen. The calling activity will be paused while * the user interacts with Google Play, and the result will be delivered via the activity's * {@link android.app.Activity#onActivityResult} method, at which point you must call * this object's {@link #handleActivityResult} method to continue the purchase flow. This method * MUST be called from the UI thread of the Activity. * * @param act The calling activity. * @param sku The sku of the item to purchase. * @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or ITEM_TYPE_SUBS) * @param requestCode A request code (to differentiate from other responses -- * as in {@link android.app.Activity#startActivityForResult}). * @param listener The listener to notify when the purchase process finishes * @param extraData Extra data (developer payload), which will be returned with the purchase data * when the purchase completes. This extra data will be permanently bound to that purchase * and will always be returned when the purchase is queried. */ public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode, IabHelper.OnIabPurchaseFinishedListener listener, String extraData) { checkSetupDone("launchPurchaseFlow"); flagStartAsync("launchPurchaseFlow"); IabResult result; if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) { IabResult r = new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE, "Subscriptions are not available."); if (listener != null) listener.onIabPurchaseFinished(r, null); flagEndAsync(); return; } try { Logger.d("Constructing buy intent for ", sku, ", item type: ", itemType); if (mService == null) { Logger.e("In-app billing error: Unable to buy item, Error response: service is not connected."); result = new IabResult(RESULT_ERROR, "Unable to buy item"); if (listener != null) listener.onIabPurchaseFinished(result, null); flagEndAsync(); return; } Bundle buyIntentBundle = mService.getBuyIntent(1, getPackageName(), sku, itemType, extraData); int response = getResponseCodeFromBundle(buyIntentBundle); if (response != RESULT_OK) { Logger.e("In-app billing error: Unable to buy item, Error response: " + getResponseDesc(response)); result = new IabResult(response, "Unable to buy item"); if (listener != null) listener.onIabPurchaseFinished(result, null); flagEndAsync(); return; } PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT); Logger.d("Launching buy intent for ", sku, ". Request code: ", requestCode); mRequestCode = requestCode; mPurchaseListener = listener; mPurchasingItemType = itemType; act.startIntentSenderForResult(pendingIntent.getIntentSender(), requestCode, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); } catch (SendIntentException e) { Logger.e("In-app billing error: SendIntentException while launching purchase flow for sku " + sku); e.printStackTrace(); result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent."); if (listener != null) listener.onIabPurchaseFinished(result, null); } catch (RemoteException e) { Logger.e("In-app billing error: RemoteException while launching purchase flow for sku " + sku); e.printStackTrace(); result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow"); if (listener != null) listener.onIabPurchaseFinished(result, null); } flagEndAsync(); } /** * Handles an activity result that's part of the purchase flow in in-app billing. If you * are calling {@link #launchPurchaseFlow}, then you must call this method from your * Activity's {@link android.app.Activity#onActivityResult} method. This method * MUST be called from the UI thread of the Activity. * * @param requestCode The requestCode as you received it. * @param resultCode The resultCode as you received it. * @param data The data (Intent) as you received it. * @return Returns true if the result was related to a purchase flow and was handled; * false if the result was not related to a purchase, in which case you should * handle it normally. */ public boolean handleActivityResult(int requestCode, int resultCode, Intent data) { IabResult result; if (requestCode != mRequestCode) return false; checkSetupDone("handleActivityResult"); // end of async purchase operation flagEndAsync(); if (data == null) { Logger.e("In-app billing error: Null data in IAB activity result."); result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result"); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return true; } int responseCode = getResponseCodeFromIntent(data); String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA); String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE); if (resultCode == Activity.RESULT_OK && responseCode == RESULT_OK) { processPurchaseSuccess(data, purchaseData, dataSignature); } else if (resultCode == Activity.RESULT_OK) { // result code was OK, but in-app billing response was not OK. processPurchaseFail(responseCode); } else if (resultCode == Activity.RESULT_CANCELED) { Logger.d("Purchase canceled - Response: ", getResponseDesc(responseCode)); result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); } else { Logger.e("In-app billing error: Purchase failed. Result code: " + Integer.toString(resultCode) + ". Response: " + getResponseDesc(responseCode)); result = new IabResult(IABHELPER_UNKNOWN_PURCHASE_RESPONSE, "Unknown purchase response."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); } return true; } public void processPurchaseFail(int responseCode) { IabResult result; Logger.d("Result code was OK but in-app billing response was not OK: ", getResponseDesc(responseCode)); if (mPurchaseListener != null) { result = new IabResult(responseCode, "Problem purchashing item."); mPurchaseListener.onIabPurchaseFinished(result, null); } } public void processPurchaseSuccess(Intent data, String purchaseData, String dataSignature) { IabResult result; Logger.d("Successful resultcode from purchase activity."); Logger.d("Purchase data: ", purchaseData); Logger.d("Data signature: ", dataSignature); Logger.d("Extras: ", data.getExtras()); Logger.d("Expected item type: ", mPurchasingItemType); if (purchaseData == null || dataSignature == null) { Logger.e("In-app billing error: BUG: either purchaseData or dataSignature is null."); Logger.d("Extras: ", data.getExtras()); result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature"); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return; } Purchase purchase; try { purchase = new Purchase(mPurchasingItemType, purchaseData, dataSignature, mAppstore.getAppstoreName()); String sku = purchase.getSku(); purchase.setSku(SkuManager.getInstance().getSku(mAppstore.getAppstoreName(), sku)); if (!isValidDataSignature(mSignatureBase64, purchaseData, dataSignature)) { Logger.e("In-app billing error: Purchase signature verification FAILED for sku " + sku); result = new IabResult(IABHELPER_VERIFICATION_FAILED, "Signature verification failed for sku " + sku); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, purchase); return; } Logger.d("Purchase signature successfully verified."); } catch (JSONException e) { Logger.e("In-app billing error: Failed to parse purchase data."); e.printStackTrace(); result = new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data."); if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null); return; } if (mPurchaseListener != null) { mPurchaseListener.onIabPurchaseFinished(new IabResult(RESULT_OK, "Success"), purchase); } } public Inventory queryInventory(boolean querySkuDetails, List moreSkus) throws IabException { return queryInventory(querySkuDetails, moreSkus, null); } /** * Queries the inventory. This will query all owned items from the server, as well as * information on additional skus, if specified. This method may block or take long to execute. * Do not call from a UI thread. For that, use the non-blocking version {@link #queryInventoryAsync}. * * @param querySkuDetails if true, SKU details (price, description, etc) will be queried as well * as purchase information. * @param moreItemSkus additional PRODUCT skus to query information on, regardless of ownership. * Ignored if null or if querySkuDetails is false. * @param moreSubsSkus additional SUBSCRIPTIONS skus to query information on, regardless of ownership. * Ignored if null or if querySkuDetails is false. * @throws IabException if a problem occurs while refreshing the inventory. */ public Inventory queryInventory(boolean querySkuDetails, List moreItemSkus, List moreSubsSkus) throws IabException { checkSetupDone("queryInventory"); try { Inventory inv = new Inventory(); int r = queryPurchases(inv, ITEM_TYPE_INAPP); if (r != RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying owned items)."); } if (querySkuDetails) { r = querySkuDetails(ITEM_TYPE_INAPP, inv, moreItemSkus); if (r != RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying prices of items)."); } } // if subscriptions are supported, then also query for subscriptions if (mSubscriptionsSupported) { r = queryPurchases(inv, ITEM_TYPE_SUBS); if (r != RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying owned subscriptions)."); } if (querySkuDetails) { r = querySkuDetails(ITEM_TYPE_SUBS, inv, moreSubsSkus); if (r != RESULT_OK) { throw new IabException(r, "Error refreshing inventory (querying prices of subscriptions)."); } } } return inv; } catch (RemoteException e) { throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while refreshing inventory.", e); } catch (JSONException e) { throw new IabException(IABHELPER_BAD_RESPONSE, "Error parsing JSON response while refreshing inventory.", e); } } /** * Listener that notifies when an inventory query operation completes. */ public interface QueryInventoryFinishedListener { /** * Called to notify that an inventory query operation completed. * * @param result The result of the operation. * @param inv The inventory. */ public void onQueryInventoryFinished(IabResult result, Inventory inv); } /** * Asynchronous wrapper for inventory query. This will perform an inventory * query as described in {@link #queryInventory}, but will do so asynchronously * and call back the specified listener upon completion. This method is safe to * call from a UI thread. * * @param querySkuDetails as in {@link #queryInventory} * @param moreSkus as in {@link #queryInventory} * @param listener The listener to notify when the refresh operation completes. */ public void queryInventoryAsync(final boolean querySkuDetails, final List moreSkus, final QueryInventoryFinishedListener listener) { final Handler handler = new Handler(); checkSetupDone("queryInventory"); flagStartAsync("refresh inventory"); (new Thread(new Runnable() { public void run() { IabResult result = new IabResult(RESULT_OK, "Inventory refresh successful."); Inventory inv = null; try { inv = queryInventory(querySkuDetails, moreSkus); } catch (IabException ex) { result = ex.getResult(); } flagEndAsync(); final IabResult result_f = result; final Inventory inv_f = inv; handler.post(new Runnable() { public void run() { listener.onQueryInventoryFinished(result_f, inv_f); } }); } })).start(); } public void queryInventoryAsync(QueryInventoryFinishedListener listener) { queryInventoryAsync(true, null, listener); } public void queryInventoryAsync(boolean querySkuDetails, QueryInventoryFinishedListener listener) { queryInventoryAsync(querySkuDetails, null, listener); } /** * Consumes a given in-app product. Consuming can only be done on an item * that's owned, and as a reslibs res src unity_src wp8_dll_srcult of consumption, the user will no longer own it. * This method may block or take long to return. Do not call from the UI thread. * For that, see {@link #consumeAsync}. * * @param itemInfo The PurchaseInfo that represents the item to consume. * @throws IabException if there is a problem during consumption. */ public void consume(Purchase itemInfo) throws IabException { checkSetupDone("consume"); if (!itemInfo.getItemType().equals(ITEM_TYPE_INAPP)) { throw new IabException(IABHELPER_INVALID_CONSUMPTION, "Items of type '" + itemInfo.getItemType() + "' can't be consumed."); } try { String token = itemInfo.getToken(); String sku = itemInfo.getSku(); if (token == null || token.equals("")) { Logger.e("In-app billing error: Can't consume ", sku, ". No token."); throw new IabException(IABHELPER_MISSING_TOKEN, "PurchaseInfo is missing token for sku: " + sku + " " + itemInfo); } Logger.d("Consuming sku: ", sku, ", token: ", token); if (mService == null) { Logger.d("Error consuming consuming sku ", sku, ". Service is not connected."); throw new IabException(RESULT_ERROR, "Error consuming sku " + sku); } int response = mService.consumePurchase(1, getPackageName(), token); if (response == RESULT_OK) { Logger.d("Successfully consumed sku: ", sku); } else { Logger.d("Error consuming consuming sku ", sku, ". ", getResponseDesc(response)); throw new IabException(response, "Error consuming sku " + sku); } } catch (RemoteException e) { throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while consuming. PurchaseInfo: " + itemInfo, e); } } public String getPackageName() { return mContext.getPackageName(); } /** * Callback that notifies when a consumption operation finishes. */ public interface OnConsumeFinishedListener { /** * Called to notify that a consumption has finished. * * @param purchase The purchase that was (or was to be) consumed. * @param result The result of the consumption operation. */ public void onConsumeFinished(Purchase purchase, IabResult result); } /** * Callback that notifies when a multi-item consumption operation finishes. */ public interface OnConsumeMultiFinishedListener { /** * Called to notify that a consumption of multiple items has finished. * * @param purchases The purchases that were (or were to be) consumed. * @param results The results of each consumption operation, corresponding to each * sku. */ public void onConsumeMultiFinished(List purchases, List results); } /** * Asynchronous wrapper to item consumption. Works like {@link #consume}, but * performs the consumption in the background and notifies completion through * the provided listener. This method is safe to call from a UI thread. * * @param purchase The purchase to be consumed. * @param listener The listener to notify when the consumption operation finishes. */ public void consumeAsync(Purchase purchase, OnConsumeFinishedListener listener) { checkSetupDone("consume"); List purchases = new ArrayList(); purchases.add(purchase); consumeAsyncInternal(purchases, listener, null); } /** * Same as {@link org.onepf.oms.appstore.googleUtils.IabHelper#consumeAsync(Purchase, org.onepf.oms.appstore.googleUtils.IabHelper.OnConsumeFinishedListener)}, * but for multiple items at once. * * @param purchases The list of PurchaseInfo objects representing the purchases to consume. * @param listener The listener to notify when the consumption operation finishes. */ public void consumeAsync(List purchases, OnConsumeMultiFinishedListener listener) { checkSetupDone("consume"); consumeAsyncInternal(purchases, null, listener); } /** * Checks that setup was done; if not, throws an exception. *

    *

    OpenIAB specific: NOT USED

    *

    * setupDone state is tracked by {@link OpenIabHelper}, so check here duplicates * already existed logic. At the same time we discovered race condition problem based on end-user * crash reports *

    Time to time when common onSetupSuccessfulListener calls queryInventory() IabHelper.setupDone is false * We tried to solve it with volatile modifier and synchronized blocks. Both approaches failed. * Reasons are still unclear. The same flow in wrapper works perfect (OpenIabHelper) *

         *  java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: queryInventory
         * at IabHelper.checkSetupDone(IabHelper.java:806)
         * at IabHelper.queryInventory(IabHelper.java:566)
         * at OpenIabHelper.queryInventory(OpenIabHelper.java:930)
         * at OpenIabHelper$5.run(OpenIabHelper.java:957)
         * at java.lang.Thread.run(Thread.java:864)
         * 

    * * @see https://github.com/onepf/OpenIAB/issues/199 */ void checkSetupDone(String operation) { } // Workaround to bug where sometimes response codes come as Long instead of Integer int getResponseCodeFromBundle(Bundle b) { Object o = b.get(RESPONSE_CODE); if (o == null) { Logger.d("Bundle with null response code, assuming OK (known issue)"); return RESULT_OK; } else if (o instanceof Integer) return ((Integer) o).intValue(); else if (o instanceof Long) return (int) ((Long) o).longValue(); else { Logger.e("In-app billing error: ", "Unexpected type for bundle response code."); Logger.e("In-app billing error: ", o.getClass().getName()); throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName()); } } // Workaround to bug where sometimes response codes come as Long instead of Integer int getResponseCodeFromIntent(Intent i) { Object o = i.getExtras().get(RESPONSE_CODE); if (o == null) { Logger.e("In-app billing error: Intent with no response code, assuming OK (known issue)"); return RESULT_OK; } else if (o instanceof Integer) return ((Integer) o).intValue(); else if (o instanceof Long) return (int) ((Long) o).longValue(); else { Logger.e("In-app billing error: Unexpected type for intent response code."); Logger.e("In-app billing error: ", o.getClass().getName()); throw new RuntimeException("Unexpected type for intent response code: " + o.getClass().getName()); } } void flagStartAsync(String operation) { if (mAsyncInProgress) throw new IllegalStateException("Can't start async operation (" + operation + ") because another async operation(" + mAsyncOperation + ") is in progress."); mAsyncOperation = operation; mAsyncInProgress = true; Logger.d("Starting async operation: ", operation); } void flagEndAsync() { Logger.d("Ending async operation: ", mAsyncOperation); mAsyncOperation = ""; mAsyncInProgress = false; } int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException { // Query purchases Logger.d("Querying owned items, item type: ", itemType); Logger.d("Package name: ", getPackageName()); boolean verificationFailed = false; String continueToken = null; do { Logger.d("Calling getPurchases with continuation token: ", continueToken); if (mService == null) { Logger.d("getPurchases() failed: service is not connected."); return RESULT_ERROR; } Bundle ownedItems = mService.getPurchases(1, getPackageName(), itemType, continueToken); int response = getResponseCodeFromBundle(ownedItems); Logger.d("Owned items response: ", response); if (response != RESULT_OK) { Logger.d("getPurchases() failed: ", getResponseDesc(response)); return response; } if (!ownedItems.containsKey(RESPONSE_INAPP_ITEM_LIST) || !ownedItems.containsKey(RESPONSE_INAPP_PURCHASE_DATA_LIST) || !ownedItems.containsKey(RESPONSE_INAPP_SIGNATURE_LIST)) { Logger.e("In-app billing error: Bundle returned from getPurchases() doesn't contain required fields."); return IABHELPER_BAD_RESPONSE; } ArrayList ownedSkus = ownedItems.getStringArrayList(RESPONSE_INAPP_ITEM_LIST); ArrayList purchaseDataList = ownedItems.getStringArrayList(RESPONSE_INAPP_PURCHASE_DATA_LIST); ArrayList signatureList = ownedItems.getStringArrayList(RESPONSE_INAPP_SIGNATURE_LIST); for (int i = 0; i < purchaseDataList.size(); ++i) { String purchaseData = purchaseDataList.get(i); String signature = signatureList.get(i); String sku = ownedSkus.get(i); if (isValidDataSignature(mSignatureBase64, purchaseData, signature)) { Logger.d("Sku is owned: ", sku); Purchase purchase = new Purchase(itemType, purchaseData, signature, mAppstore.getAppstoreName()); String storeSku = purchase.getSku(); purchase.setSku(SkuManager.getInstance().getSku(mAppstore.getAppstoreName(), storeSku)); if (TextUtils.isEmpty(purchase.getToken())) { Logger.w("In-app billing warning: BUG: empty/null token!"); Logger.d("Purchase data: ", purchaseData); } // Record ownership and token inv.addPurchase(purchase); } else { Logger.w("In-app billing warning: Purchase signature verification **FAILED**. Not adding item."); Logger.d(" Purchase data: ", purchaseData); Logger.d(" Signature: ", signature); verificationFailed = true; } } continueToken = ownedItems.getString(INAPP_CONTINUATION_TOKEN); Logger.d("Continuation token: ", continueToken); } while (!TextUtils.isEmpty(continueToken)); return verificationFailed ? IABHELPER_VERIFICATION_FAILED : RESULT_OK; } /** * @param inv - Inventory with application SKUs * @param moreSkus - storeSKUs (processed in {@link OpenIabHelper#queryInventory(boolean, List, List)} */ int querySkuDetails(String itemType, Inventory inv, List moreSkus) throws RemoteException, JSONException { Logger.d("querySkuDetails() Querying SKU details."); final SkuManager skuManager = SkuManager.getInstance(); final String appstoreName = mAppstore.getAppstoreName(); Set storeSkus = new TreeSet(); for (String sku : inv.getAllOwnedSkus(itemType)) { storeSkus.add(skuManager.getStoreSku(appstoreName, sku)); } if (moreSkus != null) { for (String sku : moreSkus) { storeSkus.add(skuManager.getStoreSku(appstoreName, sku)); } } if (storeSkus.isEmpty()) { Logger.d("querySkuDetails(): nothing to do because there are no SKUs."); return RESULT_OK; } // Split the sku list in blocks of no more than QUERY_SKU_DETAILS_BATCH_SIZE elements. ArrayList> batches = new ArrayList>(); ArrayList tmpBatch = new ArrayList(QUERY_SKU_DETAILS_BATCH_SIZE); int iSku = 0; for (String sku : storeSkus) { tmpBatch.add(sku); iSku++; if (tmpBatch.size() == QUERY_SKU_DETAILS_BATCH_SIZE || iSku == storeSkus.size()) { batches.add(tmpBatch); tmpBatch = new ArrayList(QUERY_SKU_DETAILS_BATCH_SIZE); } } Logger.d("querySkuDetails() batches: ", batches.size(), ", ", batches); for (ArrayList batch : batches) { Bundle querySkus = new Bundle(); querySkus.putStringArrayList(GET_SKU_DETAILS_ITEM_LIST, batch); if (mService == null) { Logger.e("In-app billing error: unable to get sku details: service is not connected."); return IABHELPER_BAD_RESPONSE; } Bundle skuDetails = mService.getSkuDetails(1, mContext.getPackageName(), itemType, querySkus); if (!skuDetails.containsKey(RESPONSE_GET_SKU_DETAILS_LIST)) { int response = getResponseCodeFromBundle(skuDetails); if (response != RESULT_OK) { Logger.d("getSkuDetails() failed: ", getResponseDesc(response)); return response; } else { Logger.e("In-app billing error: getSkuDetails() returned a bundle with neither an error nor a detail list."); return IABHELPER_BAD_RESPONSE; } } ArrayList responseList = skuDetails.getStringArrayList(RESPONSE_GET_SKU_DETAILS_LIST); for (String thisResponse : responseList) { SkuDetails d = new SkuDetails(itemType, thisResponse); d.setSku(SkuManager.getInstance().getSku(appstoreName, d.getSku())); Logger.d("querySkuDetails() Got sku details: ", d); inv.addSkuDetails(d); } } return RESULT_OK; } void consumeAsyncInternal(final List purchases, final OnConsumeFinishedListener singleListener, final OnConsumeMultiFinishedListener multiListener) { final Handler handler = new Handler(); flagStartAsync("consume"); (new Thread(new Runnable() { public void run() { final List results = new ArrayList(); for (Purchase purchase : purchases) { try { consume(purchase); results.add(new IabResult(RESULT_OK, "Successful consume of sku " + purchase.getSku())); } catch (IabException ex) { results.add(ex.getResult()); } } flagEndAsync(); if (singleListener != null) { handler.post(new Runnable() { public void run() { singleListener.onConsumeFinished(purchases.get(0), results.get(0)); } }); } if (multiListener != null) { handler.post(new Runnable() { public void run() { multiListener.onConsumeMultiFinished(purchases, results); } }); } } })).start(); } boolean isValidDataSignature(String base64PublicKey, String purchaseData, String signature) { if (base64PublicKey == null) return true; boolean isValid = Security.verifyPurchase(base64PublicKey, purchaseData, signature); if (!isValid) { Logger.w("In-app billing warning: Purchase signature verification **FAILED**."); } return isValid; } } \ No newline at end of file diff --git a/library/src/main/java/org/onepf/oms/appstore/skubitUtils/SkubitTestIabHelper.java b/library/src/main/java/org/onepf/oms/appstore/skubitUtils/SkubitTestIabHelper.java new file mode 100644 index 00000000..8c57e70d --- /dev/null +++ b/library/src/main/java/org/onepf/oms/appstore/skubitUtils/SkubitTestIabHelper.java @@ -0,0 +1 @@ +/* * Copyright 2012-2014 One Platform Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onepf.oms.appstore.skubitUtils; import android.content.Context; import android.content.Intent; import org.onepf.oms.Appstore; import org.onepf.oms.appstore.SkubitTestAppstore; public class SkubitTestIabHelper extends SkubitIabHelper { public SkubitTestIabHelper(Context ctx, String base64PublicKey, Appstore appstore) { super(ctx, base64PublicKey, appstore); } protected Intent getServiceIntent() { final Intent intent = new Intent(SkubitTestAppstore.VENDING_ACTION); intent.setPackage(SkubitTestAppstore.SKUBIT_INSTALLER); return intent; } } \ No newline at end of file From 87772e082a32201e3449c8acefc457166eba9e6a Mon Sep 17 00:00:00 2001 From: Shane Isbell Date: Tue, 30 Sep 2014 21:25:59 -0700 Subject: [PATCH 38/72] Removed logging statement --- library/src/main/java/org/onepf/oms/OpenIabHelper.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 9367c7b5..76b29037 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -661,8 +661,6 @@ public void run() { public void run() { Appstore checkedAppstore = null; for (final Appstore appstore : appstores) { - System.out.println("AS: " + appstore.getAppstoreName() + ":" - + appstore.isBillingAvailable(packageName) + ":" + versionOk(appstore)); if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { checkedAppstore = appstore; break; From 2a2d2a6ce0937601fa37d1499547714bcf77d2dd Mon Sep 17 00:00:00 2001 From: Roman Zhilich Date: Wed, 1 Oct 2014 16:46:07 +0400 Subject: [PATCH 39/72] Fix Samsung billing being stuck during OpenIabHelper.setup() --- .../main/java/org/onepf/oms/OpenIabHelper.java | 15 +++++++++------ .../java/org/onepf/trivialdrivegame/Config.java | 9 ++++++++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/library/src/main/java/org/onepf/oms/OpenIabHelper.java b/library/src/main/java/org/onepf/oms/OpenIabHelper.java index 7811480d..09ab6f5f 100644 --- a/library/src/main/java/org/onepf/oms/OpenIabHelper.java +++ b/library/src/main/java/org/onepf/oms/OpenIabHelper.java @@ -116,10 +116,10 @@ public class OpenIabHelper { private int setupState = SETUP_RESULT_NOT_STARTED; /** - * SamsungApps requires {@link #handleActivityResult(int, int, Intent)} but it doesn't + * Some appstores requires {@link #handleActivityResult(int, int, Intent)} but it doesn't * work until setup is completed. */ - private volatile SamsungApps samsungInSetup; + private volatile Appstore appstoreInSetup; // Is an asynchronous operation in progress? // (only one at a time can be in progress) @@ -605,6 +605,7 @@ private void checkBillingAndFinish(@NotNull final OnIabSetupFinishedListener lis public void run() { final List availableAppstores = new ArrayList(); for (final Appstore appstore : appstores) { + appstoreInSetup = appstore; if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { availableAppstores.add(appstore); } @@ -620,7 +621,7 @@ public void run() { @Override public void onIabSetupFinished(final IabResult result) { // Dispose of all initialized open appstores - final Collection appstoresToDispose = new ArrayList(); + final Collection appstoresToDispose = new ArrayList(availableAppstores); if (foundAppstore != null) { appstoresToDispose.remove(foundAppstore); } @@ -642,6 +643,7 @@ public void run() { public void run() { Appstore checkedAppstore = null; for (final Appstore appstore : appstores) { + appstoreInSetup = appstore; if (appstore.isBillingAvailable(packageName) && versionOk(appstore)) { checkedAppstore = appstore; break; @@ -652,7 +654,7 @@ public void run() { @Override public void onIabSetupFinished(final IabResult result) { // Dispose of all initialized open appstores - final Collection appstoresToDispose = new ArrayList(); + final Collection appstoresToDispose = new ArrayList(appstores); if (foundAppstore != null) { appstoresToDispose.remove(foundAppstore); } @@ -730,6 +732,7 @@ private void finishSetup(@NotNull final OnIabSetupFinishedListener listener, final boolean setUpSuccessful = iabResult.isSuccess(); setupState = setUpSuccessful ? SETUP_RESULT_SUCCESSFUL : SETUP_RESULT_FAILED; activity = null; + appstoreInSetup = null; setupExecutorService.shutdownNow(); setupExecutorService = null; if (setUpSuccessful) { @@ -1024,8 +1027,8 @@ public void launchPurchaseFlow(Activity act, String sku, String itemType, int re public boolean handleActivityResult(int requestCode, int resultCode, Intent data) { Logger.dWithTimeFromUp("handleActivityResult() requestCode: ", requestCode, " resultCode: ", resultCode, " data: ", data); - if (requestCode == options.samsungCertificationRequestCode && samsungInSetup != null) { - return samsungInSetup.getInAppBillingService().handleActivityResult(requestCode, resultCode, data); + if (requestCode == options.samsungCertificationRequestCode && appstoreInSetup!= null) { + return appstoreInSetup.getInAppBillingService().handleActivityResult(requestCode, resultCode, data); } if (setupState != SETUP_RESULT_SUCCESSFUL) { Logger.d("handleActivityResult() setup is not done. requestCode: ", requestCode, " resultCode: ", resultCode, " data: ", data); diff --git a/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java b/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java index 40f94650..e5b6811d 100644 --- a/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java +++ b/samples/trivialdrive/src/main/java/org/onepf/trivialdrivegame/Config.java @@ -70,6 +70,10 @@ public final class Config { public static final String SKU_INFINITE_GAS_AMAZON = "org.onepf.trivialdrivegame.subscrption.sku_infinite_gas"; public static final String SKU_PREMIUM_AMAZON = "org.onepf.trivialdrivegame.sku_premium"; + public static final String SKU_GAS_SAMSUNG = "000001003746/org.onepf.trivialdrivegame.sku_gas"; + public static final String SKU_INFINITE_GAS_SAMSUNG = "000001003744/org.onepf.trivialdrivegame.subscrption.sku_infinite_gas"; + public static final String SKU_PREMIUM_SAMSUNG = "000001003747/org.onepf.trivialdrivegame.sku_premium"; + public static final String SKU_GAS_YANDEX = "org.onepf.trivialdrivegame.sku_gas"; public static final String SKU_INFINITE_GAS_YANDEX = "org.onepf.trivialdrivegame.sku_infinite_gas"; public static final String SKU_PREMIUM_YANDEX = "org.onepf.trivialdrivegame.sku_premium"; @@ -103,18 +107,21 @@ public final class Config { .mapSku(SKU_INFINITE_GAS, OpenIabHelper.NAME_AMAZON, SKU_INFINITE_GAS_AMAZON) .mapSku(SKU_INFINITE_GAS, OpenIabHelper.NAME_APPLAND, SKU_INFINITE_GAS_APPLAND) .mapSku(SKU_INFINITE_GAS, OpenIabHelper.NAME_SLIDEME, SKU_INFINITE_GAS_SLIDEME) + .mapSku(SKU_INFINITE_GAS, OpenIabHelper.NAME_SAMSUNG, SKU_INFINITE_GAS_SAMSUNG) .mapSku(SKU_GAS, OpenIabHelper.NAME_NOKIA, SKU_GAS_NOKIA_STORE) .mapSku(SKU_GAS, OpenIabHelper.NAME_YANDEX, SKU_GAS_YANDEX) .mapSku(SKU_GAS, OpenIabHelper.NAME_AMAZON, SKU_GAS_AMAZON) .mapSku(SKU_GAS, OpenIabHelper.NAME_APPLAND, SKU_GAS_APPLAND) .mapSku(SKU_GAS, OpenIabHelper.NAME_SLIDEME, SKU_GAS_SLIDEME) + .mapSku(SKU_GAS, OpenIabHelper.NAME_SAMSUNG, SKU_GAS_SAMSUNG) .mapSku(SKU_PREMIUM, OpenIabHelper.NAME_NOKIA, SKU_PREMIUM_NOKIA_STORE) .mapSku(SKU_PREMIUM, OpenIabHelper.NAME_YANDEX, SKU_PREMIUM_YANDEX) .mapSku(SKU_PREMIUM, OpenIabHelper.NAME_AMAZON, SKU_PREMIUM_AMAZON) .mapSku(SKU_PREMIUM, OpenIabHelper.NAME_APPLAND, SKU_PREMIUM_APPLAND) - .mapSku(SKU_PREMIUM, OpenIabHelper.NAME_SLIDEME, SKU_PREMIUM_SLIDEME); + .mapSku(SKU_PREMIUM, OpenIabHelper.NAME_SLIDEME, SKU_PREMIUM_SLIDEME) + .mapSku(SKU_PREMIUM, OpenIabHelper.NAME_SAMSUNG, SKU_PREMIUM_SAMSUNG); } private Config() { From ba235df61157032fa0c4e2f344e9228dc9ea5905 Mon Sep 17 00:00:00 2001 From: Ruslan Sayfutdinov Date: Wed, 1 Oct 2014 21:54:53 +0400 Subject: [PATCH 40/72] Move Local Store to separate repo --- local_store/build.gradle | 23 -- .../src/androidTest/AndroidManifest.xml | 22 -- .../onepf/oms/AppstoreServiceJsonTest.java | 115 -------- .../org/onepf/oms/AppstoreServiceXmlTest.java | 138 ---------- .../org/onepf/oms/BillingServiceCsvTest.java | 24 -- .../org/onepf/oms/BillingServiceJsonTest.java | 20 -- .../org/onepf/oms/BillingServiceTestBase.java | 191 ------------- .../org/onepf/oms/BillingServiceXmlTest.java | 21 -- .../onepf/oms/MockBillingApplicationBase.java | 110 -------- .../onepf/oms/MockBillingCsvApplication.java | 23 -- .../onepf/oms/MockBillingJsonApplication.java | 23 -- .../onepf/oms/MockBillingXmlApplication.java | 22 -- local_store/src/main/AndroidManifest.xml | 35 --- .../src/main/assets/amazon.sdktester.json | 53 ---- local_store/src/main/assets/google-play.csv | 5 - local_store/src/main/assets/onepf.xml | 14 - .../java/org/onepf/oms/IOpenAppstore.aidl | 62 ----- .../onepf/oms/IOpenInAppBillingService.aidl | 143 ---------- .../java/org/onepf/store/AppstoreBinder.java | 66 ----- .../java/org/onepf/store/AppstoreService.java | 21 -- .../java/org/onepf/store/BillingBinder.java | 258 ------------------ .../java/org/onepf/store/BillingService.java | 21 -- .../org/onepf/store/PurchaseActivity.java | 47 ---- .../java/org/onepf/store/StoreActivity.java | 30 -- .../org/onepf/store/StoreApplication.java | 136 --------- .../main/java/org/onepf/store/XmlHelper.java | 30 -- .../java/org/onepf/store/data/Database.java | 235 ---------------- .../java/org/onepf/store/data/Purchase.java | 116 -------- .../java/org/onepf/store/data/SkuDetails.java | 86 ------ .../main/res/drawable-hdpi/ic_launcher.png | Bin 1072 -> 0 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 736 -> 0 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 1384 -> 0 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 2205 -> 0 bytes local_store/src/main/res/layout/main.xml | 16 -- local_store/src/main/res/layout/purchase.xml | 32 --- local_store/src/main/res/values/strings.xml | 4 - settings.gradle | 2 - 37 files changed, 2144 deletions(-) delete mode 100644 local_store/build.gradle delete mode 100644 local_store/src/androidTest/AndroidManifest.xml delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/AppstoreServiceJsonTest.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/AppstoreServiceXmlTest.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceCsvTest.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceJsonTest.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceTestBase.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceXmlTest.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/MockBillingApplicationBase.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/MockBillingCsvApplication.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/MockBillingJsonApplication.java delete mode 100644 local_store/src/androidTest/java/src/org/onepf/oms/MockBillingXmlApplication.java delete mode 100644 local_store/src/main/AndroidManifest.xml delete mode 100644 local_store/src/main/assets/amazon.sdktester.json delete mode 100644 local_store/src/main/assets/google-play.csv delete mode 100644 local_store/src/main/assets/onepf.xml delete mode 100644 local_store/src/main/java/org/onepf/oms/IOpenAppstore.aidl delete mode 100644 local_store/src/main/java/org/onepf/oms/IOpenInAppBillingService.aidl delete mode 100644 local_store/src/main/java/org/onepf/store/AppstoreBinder.java delete mode 100644 local_store/src/main/java/org/onepf/store/AppstoreService.java delete mode 100644 local_store/src/main/java/org/onepf/store/BillingBinder.java delete mode 100644 local_store/src/main/java/org/onepf/store/BillingService.java delete mode 100644 local_store/src/main/java/org/onepf/store/PurchaseActivity.java delete mode 100644 local_store/src/main/java/org/onepf/store/StoreActivity.java delete mode 100644 local_store/src/main/java/org/onepf/store/StoreApplication.java delete mode 100644 local_store/src/main/java/org/onepf/store/XmlHelper.java delete mode 100644 local_store/src/main/java/org/onepf/store/data/Database.java delete mode 100644 local_store/src/main/java/org/onepf/store/data/Purchase.java delete mode 100644 local_store/src/main/java/org/onepf/store/data/SkuDetails.java delete mode 100644 local_store/src/main/res/drawable-hdpi/ic_launcher.png delete mode 100644 local_store/src/main/res/drawable-mdpi/ic_launcher.png delete mode 100644 local_store/src/main/res/drawable-xhdpi/ic_launcher.png delete mode 100644 local_store/src/main/res/drawable-xxhdpi/ic_launcher.png delete mode 100644 local_store/src/main/res/layout/main.xml delete mode 100644 local_store/src/main/res/layout/purchase.xml delete mode 100644 local_store/src/main/res/values/strings.xml diff --git a/local_store/build.gradle b/local_store/build.gradle deleted file mode 100644 index b0b1cb45..00000000 --- a/local_store/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion - - defaultConfig { - minSdkVersion rootProject.minSdkVersion - targetSdkVersion rootProject.targetSdkVersion - versionCode 1 - versionName '1.0' - - applicationId 'org.onepf.store' - } - - lintOptions { - abortOnError false - } -} - -dependencies { - compile project(':OpenIab Library') -} \ No newline at end of file diff --git a/local_store/src/androidTest/AndroidManifest.xml b/local_store/src/androidTest/AndroidManifest.xml deleted file mode 100644 index f49de8a4..00000000 --- a/local_store/src/androidTest/AndroidManifest.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/AppstoreServiceJsonTest.java b/local_store/src/androidTest/java/src/org/onepf/oms/AppstoreServiceJsonTest.java deleted file mode 100644 index 305f235b..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/AppstoreServiceJsonTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.onepf.oms; - -import android.content.Intent; -import android.os.RemoteException; -import android.test.ServiceTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -public class AppstoreServiceJsonTest extends ServiceTestCase { - - AppstoreBinder _binder; - MockBillingJsonApplication _app; - - final String _jsonConfig = "{\"applications\":[{\"packageName\":\"org.onepf.trivialdrive\",\"version\":\"1\",\"installed\":\"true\",\"billingActive\":\"true\",\"products\":[{\"productId\":\"sku_gas\",\"type\":\"inapp\",\"price\":\"50\",\"title\":\"GAS\",\"description\":\"car fuel\"},{\"productId\":\"sku_premium\",\"type\":\"inapp\"},{\"productId\":\"sku_infinite_gas\",\"type\":\"subs\"}],\"inventory\":[\"sku_premium\",\"sku_infinite_gas\"]}]}"; - - public AppstoreServiceJsonTest() { - super(AppstoreService.class); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - _binder = null; - _app = null; - } - - private void start(String json) { - _app = new MockBillingJsonApplication(json); - _app.onCreate(); - setApplication(_app); - _binder = (AppstoreBinder) bindService(new Intent()); - } - - @SmallTest - public void testNullBind() { - start(null); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testEmptyBind() { - start(""); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testEmptyJsonBind() { - start("{}"); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testInvalidJsonBind() { - start("{ error }"); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testProperJsonBind() { - start(_jsonConfig); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testIsPackageInstaller() throws RemoteException { - start("{\"applications\":[{\"packageName\":\"org.some.app\",\"installed\":\"true\"}]}"); - assertTrue(_binder.isPackageInstaller("org.some.app")); - } - -// @SmallTest -// public void testIsNotPackageInstaller() throws RemoteException { -// start("{\"applications\":[{\"packageName\":\"org.some.app\",\"installed\":\"false\"}]}"); -// assertFalse(_binder.isPackageInstaller("org.some.app")); -// } - - @SmallTest - public void testIsBillingAvailable() throws RemoteException { - start("{\"applications\":[{\"packageName\":\"org.some.app\",\"billingActive\":\"true\"}]}"); - assertTrue(_binder.isBillingAvailable("org.some.app")); - } - -// @SmallTest -// public void testIsNotBillingAvailable() throws RemoteException { -// start("{\"applications\":[{\"packageName\":\"org.some.app\",\"billingActive\":\"false\"}]}"); -// assertFalse(_binder.isBillingAvailable("org.some.app")); -// } - -// @SmallTest -// public void testVersion() throws RemoteException { -// start("{\"applications\":[{\"packageName\":\"org.some.app\",\"version\":\"666\"}]}"); -// assertTrue(_binder.getPackageVersion("org.some.app") == 666); -// } - - @SmallTest - public void testBillingServiceIntent() throws RemoteException { - start(""); - Intent intent = _binder.getBillingServiceIntent(); - assertTrue(intent != null); - assertEquals(intent.getAction(), "org.onepf.oms.billing.BIND"); - } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/AppstoreServiceXmlTest.java b/local_store/src/androidTest/java/src/org/onepf/oms/AppstoreServiceXmlTest.java deleted file mode 100644 index dc4b03cd..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/AppstoreServiceXmlTest.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.onepf.oms; - -import android.content.Intent; -import android.test.ServiceTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -public class AppstoreServiceXmlTest extends ServiceTestCase { - - AppstoreBinder _binder; - MockBillingXmlApplication _app; - - final String _xmlConfig = "50000000sku_premiumsku_infinite_gas "; - - public AppstoreServiceXmlTest() { - super(AppstoreService.class); - } - - @Override - public void setUp() throws Exception { - super.setUp(); - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - _binder = null; - _app = null; - } - - private void start(String xml) throws Exception { - _app = new MockBillingXmlApplication(xml); - _app.onCreate(); - setApplication(_app); - _binder = (AppstoreBinder) bindService(new Intent()); - } - - @SmallTest - public void testNullBind() throws Exception { - start(null); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testEmptyBind() throws Exception { - start(""); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testEmptyXmlBind() throws Exception { - start(""); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testInvalidXmlBind() throws Exception { - start(""); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testProperXmlBind() throws Exception { - start(_xmlConfig); - assertTrue(_binder != null); - assertTrue(_app != null); - assertTrue(_app.getDatabase() != null); - } - - @SmallTest - public void testIsPackageInstaller() throws Exception { - start(" " + - " " + - " " + - " "); - assertTrue(_binder.isPackageInstaller("org.some.app")); - } - -// @SmallTest -// public void testIsNotPackageInstaller() throws Exception { -// start(" " + -// " " + -// " " + -// " "); -// assertFalse(_binder.isPackageInstaller("org.some.app")); -// } - - @SmallTest - public void testIsBillingAvailable() throws Exception { - start(" " + - " " + - " " + - " "); - assertTrue(_binder.isBillingAvailable("org.some.app")); - } - -// @SmallTest -// public void testIsNotBillingAvailable() throws Exception { -// start(" " + -// " " + -// " " + -// " "); -// assertFalse(_binder.isBillingAvailable("org.some.app")); -// } - -// @SmallTest -// public void testVersion() throws Exception { -// start(" " + -// " " + -// " " + -// " "); -// assertTrue(_binder.getPackageVersion("org.some.app") == 666); -// } - - @SmallTest - public void testBillingServiceIntent() throws Exception { - start(""); - Intent intent = _binder.getBillingServiceIntent(); - assertTrue(intent != null); - assertEquals(intent.getAction(), "org.onepf.oms.billing.BIND"); - } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceCsvTest.java b/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceCsvTest.java deleted file mode 100644 index 872cca25..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceCsvTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.onepf.oms; - -import android.test.mock.MockApplication; - -public class BillingServiceCsvTest extends BillingServiceTestBase { - - public BillingServiceCsvTest() { - super(BillingService.class); - } - - @Override - protected MockApplication getApp(String config) throws Exception { - return new MockBillingCsvApplication(config); - } - - - @Override - protected String getConfig() { - return "Product ID,Published State,Purchase Type,Auto Translate,Locale; Title; Description,Auto Fill Prices,Price\n" + - "sku_infinite_gas,published,managed_by_android,false,en_US; Infinite Gas Subscription; For 1 month,true,100000000,subs\n" + - "sku_gas,published,managed_by_android,false,en_US; Some Gas; Fulfil tank for 1/4,true,50000000,inapp\n" + - "sku_premium,published,managed_by_android,false,en_US; Premium Upgrade: Red Color; Red color for your car,false,RU; 30000000; AU; 990000; AT; 680000; BE; 680000; BR; 2070000; CA; 990000; CZ; 19500000; DK; 6000000; EE; 680000; FI; 680000; FR; 680000; DE; 680000; GR; 680000; HK; 7000000; HU; 225000000; IN; 58890000; IE; 680000; IL; 3270000; IT; 680000; JP; 99000000; LU; 680000; MX; 11900000; NL; 680000; NZ; 1130000; NO; 6000000; PL; 2990000; PT; 680000; SG; 1150000; SK; 680000; SI; 680000; KR; 999000000; ES; 680000; SE; 7000000; CH; 990000; TW; 30000000; GB; 580000; US; 990000\n"; - } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceJsonTest.java b/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceJsonTest.java deleted file mode 100644 index c273274b..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceJsonTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.onepf.oms; - -import android.test.mock.MockApplication; - -public class BillingServiceJsonTest extends BillingServiceTestBase { - - public BillingServiceJsonTest() { - super(BillingService.class); - } - - @Override - protected MockApplication getApp(String config) throws Exception { - return new MockBillingJsonApplication(config); - } - - @Override - protected String getConfig() { - return "{\"sku_premium\":{\"description\":\"Red color for your car\",\"title\":\"Premium Upgrade: Red Color\",\"itemType\":\"ENTITLED\",\"price\":0.0,\"smallIconUrl\":\"https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FM21KGKW42WYYNL%2Fimages%2F_9cbc6f46-9fd6-4aeb-8c80-2d1a00b272f8_137ceaf73d63da7aa18c079d8367ffab\"},\"sku_gas\":{\"description\":\"Fulfil tank for 1/4\",\"title\":\"Some Gas\",\"itemType\":\"CONSUMABLE\",\"price\":50000000,\"smallIconUrl\":\"https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FM15KG0C1BK49B9%2Fimages%2F_59c1b075-c3f8-4c3a-ac91-5a9307ac7172_52a8cf3601aab22b2e2760c8eaf21587\"},\"sku_infinite_gas\":{\"description\":\"For 1 month\",\"title\":\"Infinite Gas Subscription\",\"itemType\":\"SUBSCRIPTION\",\"price\":0.0,\"smallIconUrl\":\"https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FM2NYN8TJDTDG86%2Fimages%2F_e58b3162-0c8a-43da-a766-5625d39b592f_434269651dead989e2eb402cbe2c511f\",\"subscriptionParent\":\"org.onepf.trivialdrive.amazon.infinite_gas\"}}"; - } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceTestBase.java b/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceTestBase.java deleted file mode 100644 index 1ba2b58b..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceTestBase.java +++ /dev/null @@ -1,191 +0,0 @@ -package org.onepf.oms; - -import android.content.Intent; -import android.os.Bundle; -import android.test.ServiceTestCase; -import android.test.mock.MockApplication; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public abstract class BillingServiceTestBase extends ServiceTestCase { - - protected BillingBinder _binder; - protected MockApplication _app; - - public BillingServiceTestBase(Class serviceClass) { - super(serviceClass); - } - - protected abstract String getConfig(); - protected abstract MockApplication getApp(String config) throws Exception; - - @Override - public void setUp() throws Exception { - super.setUp(); - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - _binder = null; - _app = null; - } - - void start(String config) throws Exception { - _app = getApp(config); - _app.onCreate(); - setApplication(_app); - _binder = (BillingBinder) bindService(new Intent()); - } - - Bundle createSkuBundle(List skuList) { - Bundle skuBundle = new Bundle(); - skuBundle.putStringArrayList(BillingBinder.ITEM_ID_LIST, new ArrayList(skuList)); - return skuBundle; - } - - @MediumTest - public void testIsBillingSupported() throws Exception { - start(getConfig()); - assertEquals(_binder.isBillingSupported(3, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP), BillingBinder.RESULT_OK); - assertEquals(_binder.isBillingSupported(3, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_SUBS), BillingBinder.RESULT_OK); - assertEquals(_binder.isBillingSupported(100500, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP), BillingBinder.RESULT_OK); - assertEquals(_binder.isBillingSupported(100500, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_SUBS), BillingBinder.RESULT_OK); - assertEquals(_binder.isBillingSupported(0, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP), BillingBinder.RESULT_BILLING_UNAVAILABLE); - assertEquals(_binder.isBillingSupported(0, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_SUBS), BillingBinder.RESULT_BILLING_UNAVAILABLE); - //assertEquals(_binder.isBillingSupported(3, "wrong.app.package", BillingBinder.ITEM_TYPE_INAPP), BillingBinder.RESULT_BILLING_UNAVAILABLE); - //assertEquals(_binder.isBillingSupported(3, "wrong.app.package", BillingBinder.ITEM_TYPE_INAPP), BillingBinder.RESULT_BILLING_UNAVAILABLE); - assertEquals(_binder.isBillingSupported(0, "org.onepf.trivialdrive", "UNKNOWN_TYPE"), BillingBinder.RESULT_BILLING_UNAVAILABLE); - } - - @MediumTest - public void testGetSkuDetails_wrong() throws Exception { - start(getConfig()); - - //Bundle result = _binder.getSkuDetails(3, "org.wrong.package", BillingBinder.ITEM_TYPE_INAPP, createSkuBundle(Arrays.asList("sku_gas"))); - //assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_ITEM_UNAVAILABLE); - - Bundle result = _binder.getSkuDetails(1, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP, createSkuBundle(Arrays.asList("sku_gas"))); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_DEVELOPER_ERROR); - - result = _binder.getSkuDetails(3, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP, createSkuBundle(Collections.nCopies(21, "sku"))); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_DEVELOPER_ERROR); - } - - @MediumTest - public void testGetSkuDetails_proper() throws Exception { - start(getConfig()); - Bundle result = _binder.getSkuDetails(3, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP, createSkuBundle(Arrays.asList("sku_gas"))); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); - ArrayList detailsList = result.getStringArrayList(BillingBinder.DETAILS_LIST); - assertEquals(detailsList.size(), 1); - JSONObject o = new JSONObject(detailsList.get(0)); - assertEquals(o.getString("productId"), "sku_gas"); - assertEquals(o.getString("type"), "inapp"); - assertEquals(o.getString("title"), "Some Gas"); - assertEquals(o.getString("description"), "Fulfil tank for 1/4"); - assertEquals(o.getInt("price"), 50000000); - - result = _binder.getSkuDetails(3, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_SUBS, createSkuBundle(Arrays.asList("sku_gas"))); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); - - result = _binder.getSkuDetails(3, "org.onepf.trivialdrive", null, createSkuBundle(Arrays.asList("sku_gas"))); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); - - result = _binder.getSkuDetails(3, "org.onepf.trivialdrive", null, createSkuBundle(Arrays.asList("sku_premium"))); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); - - result = _binder.getSkuDetails(3, "org.onepf.trivialdrive", null, createSkuBundle(Arrays.asList("sku_infinite_gas"))); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); - } - - @MediumTest - public void testGetBuyIntent_wrong() throws Exception { - start(getConfig()); - - Bundle result = _binder.getBuyIntent(1, "org.onepf.trivialdrive", "sku_gas", BillingBinder.ITEM_TYPE_INAPP, "payload"); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_DEVELOPER_ERROR); - assertTrue(result.getParcelable(BillingBinder.BUY_INTENT) != null); - -// result = _binder.getBuyIntent(3, "org.wrong.package", "sku_gas", BillingBinder.ITEM_TYPE_INAPP, "payload"); -// assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_ITEM_UNAVAILABLE); -// assertTrue(result.getParcelable(BillingBinder.BUY_INTENT) != null); - - result = _binder.getBuyIntent(3, "org.onepf.trivialdrive", "sku_unknown", BillingBinder.ITEM_TYPE_INAPP, "payload"); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_ITEM_UNAVAILABLE); - assertTrue(result.getParcelable(BillingBinder.BUY_INTENT) != null); - -// result = _binder.getBuyIntent(3, "org.onepf.trivialdrive", "sku_gas", BillingBinder.ITEM_TYPE_SUBS, "payload"); -// assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_DEVELOPER_ERROR); -// assertTrue(result.getParcelable(BillingBinder.BUY_INTENT) != null); - } - - - @SmallTest - public void testGetBuyIntent_proper() throws Exception { - start(getConfig()); - - Bundle result = _binder.getBuyIntent(3, "org.onepf.trivialdrive", "sku_gas", BillingBinder.ITEM_TYPE_INAPP, "payload"); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); - - result = _binder.getBuyIntent(3, "org.onepf.trivialdrive", "sku_infinite_gas", BillingBinder.ITEM_TYPE_SUBS, "payload"); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); - } - - @SmallTest - public void testGetPurchases_wrong() throws Exception { - start(getConfig()); - - Bundle result = _binder.getPurchases(2, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP, ""); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_DEVELOPER_ERROR); - - result = _binder.getPurchases(3, "org.onepf.trivialdrive", "wrong_type", ""); - assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_DEVELOPER_ERROR); - } - -// @MediumTest -// public void testGetPurchases() throws Exception { -// start(getConfig()); -// -// // No token -// Bundle result = _binder.getPurchases(3, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP, null); -// assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); -// -// // INAPP -// result = _binder.getPurchases(3, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_INAPP, ""); -// assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); -// -// ArrayList inappPurchaseItemList = result.getStringArrayList(BillingBinder.INAPP_PURCHASE_ITEM_LIST); -// ArrayList inappPurchaseDataList = result.getStringArrayList(BillingBinder.INAPP_PURCHASE_DATA_LIST); -// ArrayList inappDataSignatureList = result.getStringArrayList(BillingBinder.INAPP_DATA_SIGNATURE_LIST); -// -// assertTrue(inappPurchaseItemList.size() == inappPurchaseDataList.size() && inappPurchaseItemList.size() == inappDataSignatureList.size() && inappPurchaseItemList.size() == 1); -// assertFalse(result.containsKey(BillingBinder.INAPP_CONTINUATION_TOKEN)); -// assertEquals(inappPurchaseItemList.get(0), "sku_premium"); -// -// JSONObject o = new JSONObject(inappPurchaseDataList.get(0)); -// assertEquals(o.getString("productId"), "sku_premium"); -// -// -// // SUBS -// result = _binder.getPurchases(3, "org.onepf.trivialdrive", BillingBinder.ITEM_TYPE_SUBS, ""); -// assertEquals(result.getInt(BillingBinder.RESPONSE_CODE), BillingBinder.RESULT_OK); -// -// inappPurchaseItemList = result.getStringArrayList(BillingBinder.INAPP_PURCHASE_ITEM_LIST); -// inappPurchaseDataList = result.getStringArrayList(BillingBinder.INAPP_PURCHASE_DATA_LIST); -// inappDataSignatureList = result.getStringArrayList(BillingBinder.INAPP_DATA_SIGNATURE_LIST); -// -// assertTrue(inappPurchaseItemList.size() == inappPurchaseDataList.size() && inappPurchaseItemList.size() == inappDataSignatureList.size() && inappPurchaseItemList.size() == 1); -// assertFalse(result.containsKey(BillingBinder.INAPP_CONTINUATION_TOKEN)); -// assertEquals(inappPurchaseItemList.get(0), "sku_infinite_gas"); -// -// o = new JSONObject(inappPurchaseDataList.get(0)); -// assertEquals(o.getString("productId"), "sku_infinite_gas"); -// } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceXmlTest.java b/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceXmlTest.java deleted file mode 100644 index 27cd84dc..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/BillingServiceXmlTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.onepf.oms; - -import android.test.mock.MockApplication; - -public class BillingServiceXmlTest extends BillingServiceTestBase { - - public BillingServiceXmlTest() { - super(BillingService.class); - } - - @Override - protected MockApplication getApp(String config) throws Exception { - return new MockBillingXmlApplication(config); - } - - - @Override - protected String getConfig() { - return ""; - } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingApplicationBase.java b/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingApplicationBase.java deleted file mode 100644 index 5d4fd9af..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingApplicationBase.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.onepf.oms; - -import android.content.SharedPreferences; -import android.test.mock.MockApplication; -import org.onepf.oms.data.Database; - -import java.util.Map; - -public class MockBillingApplicationBase extends MockApplication implements IBillingApplication { - - protected Database _database; - - @Override - public Database getDatabase() { - return _database; - } - - @Override - public SharedPreferences getSharedPreferences(String name, int mode) { - return new SharedPreferences() { - @Override - public Map getAll() { - return null; - } - - @Override - public String getString(String s, String s2) { - return null; - } - - @Override - public int getInt(String s, int i) { - return 0; - } - - @Override - public long getLong(String s, long l) { - return 0; - } - - @Override - public float getFloat(String s, float v) { - return 0; - } - - @Override - public boolean getBoolean(String s, boolean b) { - return false; - } - - @Override - public boolean contains(String s) { - return false; - } - - @Override - public Editor edit() { - return new Editor() { - @Override - public Editor putString(String s, String s2) { - return null; - } - - @Override - public Editor putInt(String s, int i) { - return null; - } - - @Override - public Editor putLong(String s, long l) { - return null; - } - - @Override - public Editor putFloat(String s, float v) { - return null; - } - - @Override - public Editor putBoolean(String s, boolean b) { - return null; - } - - @Override - public Editor remove(String s) { - return null; - } - - @Override - public Editor clear() { - return null; - } - - @Override - public boolean commit() { - return false; - } - }; - } - - @Override - public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) { - } - - @Override - public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) { - } - }; - } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingCsvApplication.java b/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingCsvApplication.java deleted file mode 100644 index 88e77162..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingCsvApplication.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.onepf.oms; - -import org.onepf.oms.data.CSVException; -import org.onepf.oms.data.Database; - -public class MockBillingCsvApplication extends MockBillingApplicationBase { - - String _csv; - - public MockBillingCsvApplication(String csv) { - _csv = csv; - } - - @Override - public void onCreate() { - try { - _database = new Database(this); - _database.deserializeFromGoogleCSV(_csv); - } catch (CSVException e) { - _database = new Database(this); - } - } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingJsonApplication.java b/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingJsonApplication.java deleted file mode 100644 index 1d6d46da..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingJsonApplication.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.onepf.oms; - -import org.json.JSONException; -import org.onepf.oms.data.Database; - -public class MockBillingJsonApplication extends MockBillingApplicationBase { - - String _json; - - public MockBillingJsonApplication(String json) { - _json = json; - } - - @Override - public void onCreate() { - try { - _database = new Database(this); - _database.deserializeFromAmazonJson(_json); - } catch (JSONException e) { - _database = new Database(this); - } - } -} diff --git a/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingXmlApplication.java b/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingXmlApplication.java deleted file mode 100644 index 8b5c7ea5..00000000 --- a/local_store/src/androidTest/java/src/org/onepf/oms/MockBillingXmlApplication.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.onepf.oms; - -import org.onepf.oms.data.Database; - -public class MockBillingXmlApplication extends MockBillingApplicationBase { - - String _xml; - - public MockBillingXmlApplication(String xml) { - _xml = xml; - } - - @Override - public void onCreate() { - try { - _database = new Database(this); - _database.deserializeFromOnePFXML(_xml); - } catch (Exception e) { - _database = new Database(this); - } - } -} diff --git a/local_store/src/main/AndroidManifest.xml b/local_store/src/main/AndroidManifest.xml deleted file mode 100644 index b5309906..00000000 --- a/local_store/src/main/AndroidManifest.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/local_store/src/main/assets/amazon.sdktester.json b/local_store/src/main/assets/amazon.sdktester.json deleted file mode 100644 index 18ce2606..00000000 --- a/local_store/src/main/assets/amazon.sdktester.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "sku_cowboy_hat": { - "description": "Cool hat for the character. Adds +10 to show-off.", - "title": "Cowboy Hat", - "itemType": "ENTITLED", - "price": 0.0, - "smallIconUrl": "https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FM39RYFRXX5YSUC%2Fimages%2F_f951a8de-79b1-4318-afa8-000f8d04062e_ece9f14b2c987416ab4838dd63729b2d" - }, - "sku_medkit": { - "description": "Expendable for restoring player HP", - "title": "MedKit", - "itemType": "CONSUMABLE", - "price": 0.0, - "smallIconUrl": "https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FM276AF3BMWA9C8%2Fimages%2F_d61c2cc0-7393-4672-b98a-8d28c47578b8_a7cb492847be7ebfc4a981ba57aec40c" - }, - "sku_ammo": { - "description": "Weapon ammo. Kill 'em all!", - "title": "Ammo", - "itemType": "CONSUMABLE", - "price": 0.0, - "smallIconUrl": "https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FMTRRINXECUKEB%2Fimages%2F_936d53ae-9539-4b63-ac99-040bb2302ac7_5ab89aade6606f41dda5cabbcc39d9a4" - }, - "sku_infinite_ammo_weekly": { - "description": "Subscription for infinite ammo cheat", - "title": "Infinite Ammo", - "itemType": "SUBSCRIPTION", - "price": 0.0, - "smallIconUrl": "https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FMQDQ9ZPJKEGWT%2Fimages%2F_9cc92035-3a68-4a5e-abb6-e6fb55ab7cb0_5230126951b9894ca8265e9ce2e7ed3a", - "subscriptionParent": "sku_infinite_ammo" - }, - "sku_premium": { - "description": "Red color for your car", - "title": "Premium Upgrade: Red Color", - "itemType": "ENTITLED", - "price": 0.0, - "smallIconUrl": "https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FM21KGKW42WYYNL%2Fimages%2F_9cbc6f46-9fd6-4aeb-8c80-2d1a00b272f8_137ceaf73d63da7aa18c079d8367ffab" - }, - "sku_gas": { - "description": "Fulfil tank for 1/4", - "title": "Some Gas", - "itemType": "CONSUMABLE", - "price": 0.0, - "smallIconUrl": "https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FM15KG0C1BK49B9%2Fimages%2F_59c1b075-c3f8-4c3a-ac91-5a9307ac7172_52a8cf3601aab22b2e2760c8eaf21587" - }, - "sku_infinite_gas": { - "description": "For 1 month", - "title": "Infinite Gas Subscription", - "itemType": "SUBSCRIPTION", - "price": 0.0, - "smallIconUrl": "https://s3-external-1.amazonaws.com/com-amazon-mas-catalog/M3Q90L0VNS056B%2FM2NYN8TJDTDG86%2Fimages%2F_e58b3162-0c8a-43da-a766-5625d39b592f_434269651dead989e2eb402cbe2c511f", - "subscriptionParent": "org.onepf.trivialdrive.amazon.infinite_gas" - } -} \ No newline at end of file diff --git a/local_store/src/main/assets/google-play.csv b/local_store/src/main/assets/google-play.csv deleted file mode 100644 index f834a6c3..00000000 --- a/local_store/src/main/assets/google-play.csv +++ /dev/null @@ -1,5 +0,0 @@ -Product ID,Published State,Purchase Type,Auto Translate,Locale; Title; Description,Auto Fill Prices,Price -onepf.sku_infinite_ammo,published,managed_by_android,false,en_US; Ammo; Weapon ammo. Kill 'em all!,true,100000000,subs -onepf.sku_ammo,published,managed_by_android,false,en_US; Ammo; Weapon ammo. Kill 'em all!,true,30000000,inapp -onepf.sku_cowboy_hat,published,managed_by_android,false,en_US; Cowboy Hat; Cool hat for the character. Adds +10 to show-off.,true,50000000 -onepf.sku_medkit,published,managed_by_android,false,en_US; MedKit; Expendable for restoring player HP,false,RU; 30000000; AU; 990000; AT; 680000; BE; 680000; BR; 2070000; CA; 990000; CZ; 19500000; DK; 6000000; EE; 680000; FI; 680000; FR; 680000; DE; 680000; GR; 680000; HK; 7000000; HU; 225000000; IN; 58890000; IE; 680000; IL; 3270000; IT; 680000; JP; 99000000; LU; 680000; MX; 11900000; NL; 680000; NZ; 1130000; NO; 6000000; PL; 2990000; PT; 680000; SG; 1150000; SK; 680000; SI; 680000; KR; 999000000; ES; 680000; SE; 7000000; CH; 990000; TW; 30000000; GB; 580000; US; 990000 diff --git a/local_store/src/main/assets/onepf.xml b/local_store/src/main/assets/onepf.xml deleted file mode 100644 index fb4b29cb..00000000 --- a/local_store/src/main/assets/onepf.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/local_store/src/main/java/org/onepf/oms/IOpenAppstore.aidl b/local_store/src/main/java/org/onepf/oms/IOpenAppstore.aidl deleted file mode 100644 index 0adaeb25..00000000 --- a/local_store/src/main/java/org/onepf/oms/IOpenAppstore.aidl +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright 2013 One Platform Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package org.onepf.oms; - -import android.content.Intent; - -/** - * Service interface to implement by OpenStore implementation - * - * @author Boris Minaev, Oleg Orlov - * @since 29.04.2013 - */ -interface IOpenAppstore { - - /** - * Every OpenStore implementation must provide their name. It's required for core OpenIAB functions - */ - String getAppstoreName(); - - /** - * OpenStores must provide information about packages it installed. If OpenStore is installer - * and supports In-App billing it will be used for purchases - */ - boolean isPackageInstaller(String packageName); - - /** - * If true OpenIAB assumes In-App items (SKU) for app are published and ready to use - */ - boolean isBillingAvailable(String packageName); - - /** - * Provides android:versionCode of .apk published in OpenStore - * @return -1 if UNDEFINED - */ - int getPackageVersion(String packageName); - - /** - * Should provide Intent to be used for binding IOpenInAppBillingService - */ - Intent getBillingServiceIntent(); - - Intent getProductPageIntent(String packageName); - - Intent getRateItPageIntent(String packageName); - - Intent getSameDeveloperPageIntent(String packageName); - - boolean areOutsideLinksAllowed(); -} diff --git a/local_store/src/main/java/org/onepf/oms/IOpenInAppBillingService.aidl b/local_store/src/main/java/org/onepf/oms/IOpenInAppBillingService.aidl deleted file mode 100644 index 1b8787c4..00000000 --- a/local_store/src/main/java/org/onepf/oms/IOpenInAppBillingService.aidl +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * Copyright 2013 One Platform Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -package org.onepf.oms; - -/** - * IOpenInAppBillingService is the service that provides in-app billing. - * It's based on com.android.vending.billing.IInAppBillingService provided by Google Play In-App API v3 - * This service provides the following features: - * 1. Provides a new API to get details of in-app items published for the app including - * price, type, title and description. - * 2. The purchase flow is synchronous and purchase information is available immediately - * after it completes. - * 3. Purchase information of in-app purchases is maintained within the Google Play system - * till the purchase is consumed. - * 4. An API to consume a purchase of an inapp item. All purchases of one-time - * in-app items are consumable and thereafter can be purchased again. - * 5. An API to get current purchases of the user immediately. This will not contain any - * consumed purchases. - * - * All calls will give a response code with the following possible values - * RESULT_OK = 0 - success - * RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog - * RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested - * RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase - * RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API - * RESULT_ERROR = 6 - Fatal error during the API action - * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned - * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned - */ -interface IOpenInAppBillingService { - /** - * Checks support for the requested billing API version, package and in-app type. - * Minimum API version supported by this interface is 3. - * @param apiVersion the billing version which the app is using - * @param packageName the package name of the calling app - * @param type type of the in-app item being purchased "inapp" for one-time purchases - * and "subs" for subscription. - * @return RESULT_OK(0) on success, corresponding result code on failures - */ - int isBillingSupported(int apiVersion, String packageName, String type); - - /** - * Provides details of a list of SKUs - * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle - * with a list JSON strings containing the productId, price, title and description. - * This API can be called with a maximum of 20 SKUs. - * @param apiVersion billing API version that the Third-party is using - * @param packageName the package name of the calling app - * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST" - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "DETAILS_LIST" with a StringArrayList containing purchase information - * in JSON format similar to: - * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00", - * "title : "Example Title", "description" : "This is an example description" }' - */ - Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle); - - /** - * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU, - * the type, a unique purchase token and an optional developer payload. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param sku the SKU of the in-app item as published in the developer console - * @param type the type of the in-app item ("inapp" for one-time purchases - * and "subs" for subscription). - * @param developerPayload optional argument to be sent back with the purchase information - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "BUY_INTENT" - PendingIntent to start the purchase flow - * - * The Pending intent should be launched with startIntentSenderForResult. When purchase flow - * has completed, the onActivityResult() will give a resultCode of OK or CANCELED. - * If the purchase is successful, the result data will contain the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "INAPP_PURCHASE_DATA" - String in JSON format similar to - * '{"orderId":"12999763169054705758.1371079406387615", - * "packageName":"com.example.app", - * "productId":"exampleSku", - * "purchaseTime":1345678900000, - * "purchaseToken" : "122333444455555", - * "developerPayload":"example developer payload" }' - * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that - * was signed with the private key of the developer - * TODO: change this to app-specific keys. - */ - Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type, String developerPayload); - - - /** - * Returns the current SKUs owned by the user of the type and package name specified along with - * purchase information and a signature of the data to be validated. - * This will return all SKUs that have been purchased in V3 and managed items purchased using - * V1 and V2 that have not been consumed. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param type the type of the in-app items being requested - * ("inapp" for one-time purchases and "subs" for subscription). - * @param continuationToken to be set as null for the first call, if the number of owned - * skus are too many, a continuationToken is returned in the response bundle. - * This method can be called again with the continuation token to get the next set of - * owned skus. - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs - * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information - * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures - * of the purchase information - * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the - * next set of in-app purchases. Only set if the - * user has more owned skus than the current list. - */ - Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken); - - /** - * Consume the last purchase of the given SKU. This will result in this item being removed - * from all subsequent responses to getPurchases() and allow re-purchase of this item. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param purchaseToken token in the purchase information JSON that identifies the purchase - * to be consumed - * @return 0 if consumption succeeded. Appropriate error values for failures. - */ - int consumePurchase(int apiVersion, String packageName, String purchaseToken); -} \ No newline at end of file diff --git a/local_store/src/main/java/org/onepf/store/AppstoreBinder.java b/local_store/src/main/java/org/onepf/store/AppstoreBinder.java deleted file mode 100644 index fd8752b7..00000000 --- a/local_store/src/main/java/org/onepf/store/AppstoreBinder.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.onepf.store; - -import android.content.Context; -import android.content.Intent; -import android.os.RemoteException; - -import org.onepf.oms.IOpenAppstore; -import org.onepf.store.data.Database; - -public class AppstoreBinder extends IOpenAppstore.Stub { - - private static final String BILLING_BIND_INTENT = "org.onepf.oms.billing.BIND"; - - final Database _db; - final Context _context; - - public AppstoreBinder(AppstoreService context, Database database) { - _db = database; - _context = context; - } - - @Override - public String getAppstoreName() throws RemoteException { - return "org.onepf.store"; - } - - @Override - public boolean isPackageInstaller(String packageName) throws RemoteException { - return true; - } - - @Override - public boolean isBillingAvailable(String packageName) throws RemoteException { - return true; - } - - @Override - public int getPackageVersion(String packageName) throws RemoteException { - return 0; - } - - @Override - public Intent getBillingServiceIntent() throws RemoteException { - return new Intent(BILLING_BIND_INTENT); - } - - @Override - public Intent getProductPageIntent(String packageName) throws RemoteException { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public Intent getRateItPageIntent(String packageName) throws RemoteException { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public Intent getSameDeveloperPageIntent(String packageName) throws RemoteException { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public boolean areOutsideLinksAllowed() throws RemoteException { - throw new UnsupportedOperationException("Not implemented"); - } -} diff --git a/local_store/src/main/java/org/onepf/store/AppstoreService.java b/local_store/src/main/java/org/onepf/store/AppstoreService.java deleted file mode 100644 index 189aa297..00000000 --- a/local_store/src/main/java/org/onepf/store/AppstoreService.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.onepf.store; - -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; - -public class AppstoreService extends Service { - - AppstoreBinder _binder; - - @Override - public void onCreate() { - super.onCreate(); - _binder = new AppstoreBinder(this, ((StoreApplication)getApplication()).getDatabase()); - } - - @Override - public IBinder onBind(Intent intent) { - return _binder; - } -} diff --git a/local_store/src/main/java/org/onepf/store/BillingBinder.java b/local_store/src/main/java/org/onepf/store/BillingBinder.java deleted file mode 100644 index 5b955943..00000000 --- a/local_store/src/main/java/org/onepf/store/BillingBinder.java +++ /dev/null @@ -1,258 +0,0 @@ -package org.onepf.store; - -import java.util.ArrayList; -import java.util.Collections; - -import org.onepf.oms.IOpenInAppBillingService; -import org.onepf.store.data.Database; -import org.onepf.store.data.Purchase; -import org.onepf.store.data.SkuDetails; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.RemoteException; - -public class BillingBinder extends IOpenInAppBillingService.Stub { - - public static final String PURCHASE_COMPLETE_INTENT = "org.onepf.oms.PURCHASE_COMPLETE"; - - // Response result codes - public static final int RESULT_OK = 0; - public static final int RESULT_USER_CANCELED = 1; - public static final int RESULT_BILLING_UNAVAILABLE = 3; - public static final int RESULT_ITEM_UNAVAILABLE = 4; - public static final int RESULT_DEVELOPER_ERROR = 5; - public static final int RESULT_ERROR = 6; - public static final int RESULT_ITEM_ALREADY_OWNED = 7; - public static final int RESULT_ITEM_NOT_OWNED = 8; - - // Keys for the responses - public static final String RESPONSE_CODE = "RESPONSE_CODE"; - public static final String DETAILS_LIST = "DETAILS_LIST"; - public static final String BUY_INTENT = "BUY_INTENT"; - public static final String INAPP_PURCHASE_DATA = "INAPP_PURCHASE_DATA"; - public static final String INAPP_DATA_SIGNATURE = "INAPP_DATA_SIGNATURE"; - public static final String INAPP_PURCHASE_ITEM_LIST = "INAPP_PURCHASE_ITEM_LIST"; - public static final String INAPP_PURCHASE_DATA_LIST = "INAPP_PURCHASE_DATA_LIST"; - public static final String INAPP_DATA_SIGNATURE_LIST = "INAPP_DATA_SIGNATURE_LIST"; - public static final String INAPP_CONTINUATION_TOKEN = "INAPP_CONTINUATION_TOKEN"; - - // Param keys - public static final String ITEM_ID_LIST = "ITEM_ID_LIST"; - public static final String ITEM_TYPE_LIST = "ITEM_TYPE_LIST"; - - // Item types - public static final String ITEM_TYPE_INAPP = "inapp"; - public static final String ITEM_TYPE_SUBS = "subs"; - - // Purchase states - public static final int PURCHASE_STATE_PURCHASED = 0; - public static final int PURCHASE_STATE_CANCELED = 1; - public static final int PURCHASE_STATE_REFUNDED = 2; - - final Database _db; - final Context _context; - - public BillingBinder(Context context, Database database) { - _db = database; - _context = context; - } - - /** - * Checks support for the requested billing API version, package and in-app type. - * Minimum API version supported by this interface is 3. - * @param apiVersion the billing version which the app is using - * @param packageName the package name of the calling app - * @param type type of the in-app item being purchased "inapp" for one-time purchases - * and "subs" for subscription. - * @return RESULT_OK(0) on success, corresponding result code on failures - */ - @Override - public int isBillingSupported(int apiVersion, String packageName, String type) throws RemoteException { - if (apiVersion >= 3 && - (type.equals(BillingBinder.ITEM_TYPE_INAPP) || type.equals(BillingBinder.ITEM_TYPE_SUBS))) { - return RESULT_OK; - } else { - return RESULT_BILLING_UNAVAILABLE; - } - } - - /** - * Provides details of a list of SKUs - * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle - * with a list JSON strings containing the productId, price, title and description. - * This API can be called with a maximum of 20 SKUs. - * - * @param apiVersion billing API version that the Third-party is using - * @param packageName the package name of the calling app - * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST" - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "DETAILS_LIST" with a StringArrayList containing purchase information - * in JSON format similar to: - * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00", - * "title : "Example Title", "description" : "This is an example description" }' - */ - @Override - public Bundle getSkuDetails(int apiVersion, String packageName, String type, Bundle skusBundle) throws RemoteException { - Bundle result = new Bundle(); - - if (!skusBundle.containsKey(ITEM_ID_LIST) || apiVersion < 3) { - result.putInt(RESPONSE_CODE, RESULT_DEVELOPER_ERROR); - return result; - } - - ArrayList itemIdList = skusBundle.getStringArrayList(ITEM_ID_LIST); - - if (itemIdList == null || itemIdList.size() <= 0 || itemIdList.size() >= 20) { - result.putInt(RESPONSE_CODE, RESULT_DEVELOPER_ERROR); - return result; - } - - ArrayList detailsList = new ArrayList(); - for (String itemId : itemIdList) { - SkuDetails skuDetails = _db.getSkuDetails(itemId); - if (skuDetails != null) { - detailsList.add(skuDetails.toJson()); - } - } - - if (detailsList.size() <= 0) { - result.putInt(RESPONSE_CODE, RESULT_ITEM_UNAVAILABLE); - } else { - result.putInt(RESPONSE_CODE, RESULT_OK); - result.putStringArrayList(DETAILS_LIST, detailsList); - } - - return result; - } - - /** - * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU, - * the type, a unique purchase token and an optional developer payload. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param sku the SKU of the in-app item as published in the developer console - * @param type the type of the in-app item ("inapp" for one-time purchases - * and "subs" for subscription). - * @param developerPayload optional argument to be sent back with the purchase information - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "BUY_INTENT" - PendingIntent to start the purchase flow - * - * The Pending intent should be launched with startIntentSenderForResult. When purchase flow - * has completed, the onActivityResult() will give a resultCode of OK or CANCELED. - * If the purchase is successful, the result data will contain the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "INAPP_PURCHASE_DATA" - String in JSON format similar to - * '{"orderId":"12999763169054705758.1371079406387615", - * "packageName":"com.example.app", - * "productId":"exampleSku", - * "purchaseTime":1345678900000, - * "purchaseToken" : "122333444455555", - * "developerPayload":"example developer payload" }' - * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that - * was signed with the private key of the developer - * TODO: change this to app-specific keys. - */ - @Override - public Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type, String developerPayload) throws RemoteException { - Bundle result = new Bundle(); - - PendingIntent pendingIntent; - Intent purchaseIntent = new Intent(_context, PurchaseActivity.class); - - if (apiVersion < 3 || !(type.equals(ITEM_TYPE_INAPP) || type.equals(ITEM_TYPE_SUBS))) { - result.putInt(RESPONSE_CODE, RESULT_DEVELOPER_ERROR); - } else { - SkuDetails skuDetails = _db.getSkuDetails(sku); - if (skuDetails == null) { - result.putInt(RESPONSE_CODE, RESULT_ITEM_UNAVAILABLE); - } else if (!skuDetails.getType().equals(type)) { - result.putInt(RESPONSE_CODE, RESULT_DEVELOPER_ERROR); - } else { - purchaseIntent.putExtra("packageName", packageName); - purchaseIntent.putExtra("sku", sku); - purchaseIntent.putExtra("developerPayload", developerPayload); - result.putInt(RESPONSE_CODE, RESULT_OK); - } - } - - pendingIntent = PendingIntent.getActivity(_context, 0, purchaseIntent, PendingIntent.FLAG_UPDATE_CURRENT); - result.putParcelable(BUY_INTENT, pendingIntent); - return result; - } - - /** - * Returns the current SKUs owned by the user of the type and package name specified along with - * purchase information and a signature of the data to be validated. - * This will return all SKUs that have been purchased in V3 and managed items purchased using - * V1 and V2 that have not been consumed. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param type the type of the in-app items being requested - * ("inapp" for one-time purchases and "subs" for subscription). - * @param continuationToken to be set as null for the first call, if the number of owned - * skus are too many, a continuationToken is returned in the response bundle. - * This method can be called again with the continuation token to get the next set of - * owned skus. - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs - * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information - * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures - * of the purchase information - * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the - * next set of in-app purchases. Only set if the - * user has more owned skus than the current list. - */ - @Override - public Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken) throws RemoteException { - Bundle result = new Bundle(); - - if (apiVersion < 3 || !(type.equals(ITEM_TYPE_INAPP) || type.equals(ITEM_TYPE_SUBS))) { - result.putInt(RESPONSE_CODE, RESULT_DEVELOPER_ERROR); - return result; - } - - result.putInt(RESPONSE_CODE, RESULT_OK); - - ArrayList purchaseHistory = _db.getInventory(packageName, type); - int size = purchaseHistory.size(); - - ArrayList purchaseItemList = new ArrayList(size); - ArrayList purchaseDataList = new ArrayList(size); - ArrayList purchaseSignatureList = new ArrayList(Collections.nCopies(size, "")); - - for (Purchase aPurchaseHistory : purchaseHistory) { - purchaseItemList.add(aPurchaseHistory.getSku()); - purchaseDataList.add(aPurchaseHistory.toJson()); - } - - result.putStringArrayList(INAPP_PURCHASE_ITEM_LIST, purchaseItemList); - result.putStringArrayList(INAPP_PURCHASE_DATA_LIST, purchaseDataList); - result.putStringArrayList(INAPP_DATA_SIGNATURE_LIST, purchaseSignatureList); - - return result; - } - - /** - * Consume the last purchase of the given SKU. This will result in this item being removed - * from all subsequent responses to getPurchases() and allow re-purchase of this item. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param purchaseToken token in the purchase information JSON that identifies the purchase - * to be consumed - * @return 0 if consumption succeeded. Appropriate error values for failures. - */ - @Override - public int consumePurchase(int apiVersion, String packageName, String purchaseToken) throws RemoteException { - return apiVersion < 3 ? RESULT_DEVELOPER_ERROR : _db.consume(purchaseToken); - } -} diff --git a/local_store/src/main/java/org/onepf/store/BillingService.java b/local_store/src/main/java/org/onepf/store/BillingService.java deleted file mode 100644 index 8efd7751..00000000 --- a/local_store/src/main/java/org/onepf/store/BillingService.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.onepf.store; - -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; - -public class BillingService extends Service { - - BillingBinder _binder; - - @Override - public void onCreate() { - super.onCreate(); - _binder = new BillingBinder(this, ((StoreApplication)getApplication()).getDatabase()); - } - - @Override - public IBinder onBind(Intent intent) { - return _binder; - } -} diff --git a/local_store/src/main/java/org/onepf/store/PurchaseActivity.java b/local_store/src/main/java/org/onepf/store/PurchaseActivity.java deleted file mode 100644 index 93d45694..00000000 --- a/local_store/src/main/java/org/onepf/store/PurchaseActivity.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.onepf.store; - -import org.onepf.store.R; -import org.onepf.store.data.Database; -import org.onepf.store.data.Purchase; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; - -public class PurchaseActivity extends Activity { - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.purchase); - } - - public void onCancelClick(View view) { - Intent intent = getIntent(); - intent.putExtra(BillingBinder.RESPONSE_CODE, BillingBinder.RESULT_USER_CANCELED); - setResult(RESULT_OK, intent); - finish(); - } - - public void onOkClick(View view) { - Intent intent = getIntent(); - Database db = ((StoreApplication) getApplication()).getDatabase(); - - final String packageName = intent.getStringExtra("packageName"); - final String sku = intent.getStringExtra("sku"); - final String developerPayload = intent.getStringExtra("developerPayload"); - - Purchase purchase = db.createPurchase(packageName, sku, developerPayload); - if (purchase == null) { - intent.putExtra(BillingBinder.RESPONSE_CODE, BillingBinder.RESULT_ERROR); - } else { - db.storePurchase(purchase); - intent.putExtra(BillingBinder.RESPONSE_CODE, BillingBinder.RESULT_OK); - intent.putExtra(BillingBinder.INAPP_PURCHASE_DATA, purchase.toJson()); - // TODO: create signature properly! - intent.putExtra(BillingBinder.INAPP_DATA_SIGNATURE, ""); - } - - setResult(RESULT_OK, intent); - finish(); - } -} \ No newline at end of file diff --git a/local_store/src/main/java/org/onepf/store/StoreActivity.java b/local_store/src/main/java/org/onepf/store/StoreActivity.java deleted file mode 100644 index be2e51e5..00000000 --- a/local_store/src/main/java/org/onepf/store/StoreActivity.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.onepf.store; - -import java.util.List; - -import org.onepf.store.R; - -import android.app.Activity; -import android.content.pm.PackageInfo; -import android.os.Bundle; -import android.util.Log; -import android.widget.TextView; - -public class StoreActivity extends Activity { - private static final String TAG = StoreActivity.class.getSimpleName(); - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - TextView textInformation = (TextView) findViewById(R.id.textInformation); - List apps = getPackageManager().getInstalledPackages(0); - Log.d(TAG, "onCreate() *********** PACKAGES INFO ***********"); - for (PackageInfo app : apps) { - String installer = getPackageManager().getInstallerPackageName(app.packageName); - Log.d(TAG, "package: " + app.packageName + " ; installer: " + installer); - } - - } -} diff --git a/local_store/src/main/java/org/onepf/store/StoreApplication.java b/local_store/src/main/java/org/onepf/store/StoreApplication.java deleted file mode 100644 index d845bf15..00000000 --- a/local_store/src/main/java/org/onepf/store/StoreApplication.java +++ /dev/null @@ -1,136 +0,0 @@ -package org.onepf.store; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import org.onepf.store.data.Database; - -import android.app.Application; -import android.os.Environment; -import android.os.FileObserver; -import android.util.Log; - -public class StoreApplication extends Application { - - public static final String TAG = "OnePF-store"; - static final String GOOGLE_CONFIG_FILE = "google-play.csv"; - static final String AMAZON_CONFIG_FILE = "amazon.sdktester.json"; - static final String ONEPF_CONFIG_FILE = "onepf.xml"; - - Database _database; - FileObserver _configObserver; - - public Database getDatabase() { - return _database; - } - - @Override - public void onCreate() { - super.onCreate(); - - copyConfigFromAssets(GOOGLE_CONFIG_FILE); - copyConfigFromAssets(AMAZON_CONFIG_FILE); - copyConfigFromAssets(ONEPF_CONFIG_FILE); - - if (createDbFromConfig()) { - _configObserver = new FileObserver(getConfigDir()) { - @Override - public void onEvent(int event, String file) { - switch (event) { - case FileObserver.CLOSE_WRITE: - createDbFromConfig(); - break; - } - } - }; - _configObserver.startWatching(); - } - } - - private void copyConfigFromAssets(String configFile) { - File configDir = new File(getConfigDir()); - if (!configDir.exists()) { - if (!configDir.mkdirs()) { - Log.e(TAG, "Problem creating config folder"); - return; - } - } - - File outFile = new File(getConfigDir(), configFile); - if (outFile.exists()) { - return; - } - - InputStream in; - OutputStream out; - try { - in = getAssets().open(configFile); - out = new FileOutputStream(outFile); - copyFile(in, out); - in.close(); - out.flush(); - out.close(); - } catch(IOException e) { - Log.e(TAG, "Failed to copy asset file: " + configFile, e); - } - } - - private String getConfigDir() { - return Environment.getExternalStorageDirectory() + File.separator + "OnePF-store"; - } - - private void copyFile(InputStream in, OutputStream out) throws IOException { - byte[] buffer = new byte[1024]; - int read; - while((read = in.read(buffer)) != -1){ - out.write(buffer, 0, read); - } - } - - private boolean createDbFromConfig() { - try { - _database = new Database(this); - _database.deserializeFromGoogleCSV(readConfigFromSdCard(GOOGLE_CONFIG_FILE)); - _database.deserializeFromAmazonJson(readConfigFromSdCard(AMAZON_CONFIG_FILE)); - _database.deserializeFromOnePFXML(readConfigFromSdCard(ONEPF_CONFIG_FILE)); - } catch (Exception e) { - Log.e(TAG, "Couldn't parse provided 'config' file", e); - _database = new Database(this); - return false; - } - return true; - } - - private String readConfigFromSdCard(String configFile) { - File file = new File(getConfigDir(), configFile); - if (!file.exists()) { - Log.i(TAG, "'config' file not found"); - return ""; - } - StringBuilder sb = new StringBuilder(); - BufferedReader br = null; - try { - br = new BufferedReader(new FileReader(file)); - String temp; - while ((temp = br.readLine()) != null) { - sb.append(temp).append("\n"); - } - } catch (IOException e) { - Log.e(TAG, "Couldn't read 'config'", e); - } finally { - try { - if (br != null) { - br.close(); - } - } catch (IOException e) { - Log.e(TAG, "Couldn't close stream while reading 'config'", e); - } - } - return sb.toString(); - } -} diff --git a/local_store/src/main/java/org/onepf/store/XmlHelper.java b/local_store/src/main/java/org/onepf/store/XmlHelper.java deleted file mode 100644 index d209748b..00000000 --- a/local_store/src/main/java/org/onepf/store/XmlHelper.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.onepf.store; - -import java.io.ByteArrayInputStream; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.w3c.dom.Document; -import org.xml.sax.SAXException; - -import android.util.Log; - -public class XmlHelper { - public static Document loadXMLFromString(String xml) throws Exception { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc; - try { - doc = builder.parse(new ByteArrayInputStream(xml.getBytes())); - } catch (SAXException e) { - Log.i("XmlHelper", "Parse error.", e); - doc = null; - } catch (NullPointerException e) { - Log.i("XmlHelper", "Parse error. String must not be null.", e); - doc = null; - } - return doc; - } -} diff --git a/local_store/src/main/java/org/onepf/store/data/Database.java b/local_store/src/main/java/org/onepf/store/data/Database.java deleted file mode 100644 index 835f2e61..00000000 --- a/local_store/src/main/java/org/onepf/store/data/Database.java +++ /dev/null @@ -1,235 +0,0 @@ -package org.onepf.store.data; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.UUID; - -import org.json.JSONException; -import org.json.JSONObject; -import org.onepf.store.BillingBinder; -import org.onepf.store.StoreApplication; -import org.onepf.store.XmlHelper; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - -public class Database { - - static final String INVENTORY_KEY = "inventory"; - - long _orderid = 0; - - ArrayList _productList = new ArrayList(); - ArrayList _purchaseHistory = new ArrayList(); - - final Context _context; - - - public Database(Context context) { - _context = context; - loadInventory(); - } - - private void saveInventory() { - StringBuilder result = new StringBuilder(); - for (Purchase p : _purchaseHistory) { - result.append(p.toJson()).append("|"); - } - SharedPreferences.Editor prefsEditor = _context.getSharedPreferences(INVENTORY_KEY, 0).edit(); - prefsEditor.putString(INVENTORY_KEY, result.toString()); - prefsEditor.commit(); - } - - private void loadInventory() { - SharedPreferences prefs = _context.getSharedPreferences(INVENTORY_KEY, 0); - String data = prefs.getString(INVENTORY_KEY, null); - - if (data == null || data.equals("")) return; - - String[] purchases = data.split("\\|"); - for (String json : purchases) { - try { - _purchaseHistory.add(new Purchase(json)); - } catch (JSONException e) { - Log.e(StoreApplication.TAG, "Failed to deserialize purchase.", e); - } - } - } - - public void deserializeFromAmazonJson(String json) throws JSONException { - if (json == null || json.equals("")) return; - - JSONObject o = new JSONObject(json); - Iterator keys = o.keys(); - while (keys.hasNext()) { - String key = (String) keys.next(); - if (o.get(key) instanceof JSONObject) { - JSONObject product = (JSONObject) o.get(key); - String sku; - String type; - String price; - String title; - String description; - - sku = key; - String configItemType = product.getString("itemType"); - if (configItemType.equals("ENTITLED") || configItemType.equals("CONSUMABLE")) { - type = BillingBinder.ITEM_TYPE_INAPP; - } else if (configItemType.equals("SUBSCRIPTION")) { - type = BillingBinder.ITEM_TYPE_SUBS; - } else { - throw new JSONException("Invalid itemType: " + configItemType); - } - price = product.getString("price"); - title = product.getString("title"); - description = product.getString("description"); - - _productList.add(new SkuDetails(type, sku, title, price, description)); - } - } - } - - public static final int PRIMARY_COLUMNS_NUMBER = 7; - - public void deserializeFromGoogleCSV(String csv) throws IOException { - if (csv == null || csv.equals("")) return; - - String[] lines = csv.split("[\\r\\n]+"); - - final String JUNK = "Product ID,Published State,Purchase Type,Auto Translate,Locale; Title; Description,Auto Fill Prices,Price"; - for (int i = lines[0].equals(JUNK) ? 1 : 0; i < lines.length; ++i) { - String line = lines[i]; - String[] primaryColumns = line.split("\\s*,\\s*"); - if (primaryColumns.length < PRIMARY_COLUMNS_NUMBER) { - throw new IOException(csv + ":" + line + ": Invalid primary columns number: " + primaryColumns.length); - } - String sku; - String type; - String price; - String title; - String description; - - sku = primaryColumns[0]; - - String[] localeColumns = primaryColumns[4].split("\\s*;\\s*"); - if (localeColumns.length % 3 != 0) { - throw new IOException(csv + ":" + line + ": Invalid locale columns number: " + localeColumns.length + ". Should be multiple of 3"); - } - title = localeColumns[1]; - description = localeColumns[2]; - - boolean fillPrices = Boolean.parseBoolean(primaryColumns[5]); - if (fillPrices) { - price = primaryColumns[6]; - } else { - String[] priceColumns = primaryColumns[6].split("\\s*;\\s*"); - if (priceColumns.length % 2 != 0) { - throw new IOException(csv + ":" + line + ": Invalid price columns number: " + priceColumns.length + ". Should be even"); - } - price = priceColumns[1]; - } - - // TODO: this is added in order to support subscriptions in the config - if (primaryColumns.length > PRIMARY_COLUMNS_NUMBER) { - type = primaryColumns[PRIMARY_COLUMNS_NUMBER]; - if (!type.equals(BillingBinder.ITEM_TYPE_INAPP) && !type.equals(BillingBinder.ITEM_TYPE_SUBS)) { - throw new IOException(csv + ":" + line + ": Invalid product type: " + type); - } - } else { - type = BillingBinder.ITEM_TYPE_INAPP; - } - - _productList.add(new SkuDetails(type, sku, title, price, description)); - } - } - - public void deserializeFromOnePFXML(String xml) throws Exception { - if (xml == null || xml.equals("")) return; - - Element store = XmlHelper.loadXMLFromString(xml).getDocumentElement(); - NodeList productList = store.getElementsByTagName("product"); - for (int i = 0; i < productList.getLength(); ++i) { - String sku; - String type; - String price; - String title; - String description; - - Element product = (Element) productList.item(i); - NamedNodeMap attributes = product.getAttributes(); - - sku = attributes.getNamedItem("productId").getNodeValue(); - Node typeNode = attributes.getNamedItem("type"); - type = typeNode == null ? BillingBinder.ITEM_TYPE_INAPP : typeNode.getNodeValue(); - Node priceNode = attributes.getNamedItem("price"); - price = priceNode == null ? "0" : priceNode.getNodeValue(); - Node titleNode = attributes.getNamedItem("title"); - title = titleNode == null ? "" : titleNode.getNodeValue(); - Node descriptionNode = attributes.getNamedItem("description"); - description = descriptionNode == null ? "" : descriptionNode.getNodeValue(); - - _productList.add(new SkuDetails(type, sku, title, price, description)); - } - } - - String nextOrderId() { - return Long.toString(_orderid++); - } - - String generateToken(String packageName, String sku) { - return packageName + "." + sku + "." + UUID.randomUUID(); - } - - public SkuDetails getSkuDetails(String sku) { - for (SkuDetails product : _productList) { - if (product.getSku().equals(sku)) { - return product; - } - } - return null; - } - - // returns null if failed - public Purchase createPurchase(String packageName, String sku, String developerPayload) { - SkuDetails skuDetails = getSkuDetails(sku); - if (skuDetails == null) { - return null; - } - return new Purchase(nextOrderId(), packageName, sku, System.currentTimeMillis(), - BillingBinder.PURCHASE_STATE_PURCHASED, - developerPayload, generateToken(packageName, sku)); - } - - public void storePurchase(Purchase purchase) { - _purchaseHistory.add(purchase); - saveInventory(); - - } - - public int consume(String purchaseToken) { - for (int i = _purchaseHistory.size() - 1; i >= 0; --i) { - if (_purchaseHistory.get(i).getToken().equals(purchaseToken)) { - _purchaseHistory.remove(i); - saveInventory(); - return BillingBinder.RESULT_OK; - } - } - return BillingBinder.RESULT_ITEM_NOT_OWNED; - } - - public ArrayList getInventory(String packageName, String type) { - ArrayList inventory = new ArrayList(); - for (Purchase p : _purchaseHistory) { - if (getSkuDetails(p.getSku()).getType().equals(type) && p.getPackageName().equals(packageName)) { - inventory.add(p); - } - } - return inventory; - } -} diff --git a/local_store/src/main/java/org/onepf/store/data/Purchase.java b/local_store/src/main/java/org/onepf/store/data/Purchase.java deleted file mode 100644 index c22b073f..00000000 --- a/local_store/src/main/java/org/onepf/store/data/Purchase.java +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (c) 2012 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onepf.store.data; - -import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; -import org.onepf.store.AppstoreBinder; -import org.onepf.store.StoreApplication; - -/** - * Represents an in-app billing purchase. - */ -public class Purchase implements Cloneable { - - String _orderId; - String _packageName; - String _sku; - long _purchaseTime; - int _purchaseState; - String _developerPayload; - String _token; - - public Purchase(String orderId, String packageName, String sku, long purchaseTime, int purchaseState, String developerPayload, String token) { - _orderId = orderId; - _packageName = packageName; - _sku = sku; - _purchaseTime = purchaseTime; - _purchaseState = purchaseState; - _developerPayload = developerPayload; - _token = token; - } - - public Purchase(String json) throws JSONException { - JSONObject o = new JSONObject(json); - _orderId = o.getString("orderId"); - _packageName = o.getString("packageName"); - _sku = o.getString("productId"); - _purchaseTime = o.getLong("purchaseTime"); - _purchaseState = o.getInt("purchaseState"); - _developerPayload = o.getString("developerPayload"); - _token = o.getString("purchaseToken"); - } - - public String toJson() { - JSONObject o = new JSONObject(); - try { - o.put("orderId", _orderId); - o.put("packageName", _packageName); - o.put("productId", _sku); - o.put("purchaseTime", _purchaseTime); - o.put("purchaseState", _purchaseState); - o.put("developerPayload", _developerPayload); - o.put("purchaseToken", _token); - } catch (Exception e) { - Log.e(StoreApplication.TAG, "Couldn't serialize " + getClass().getSimpleName()); - return ""; - } - return o.toString(); - } - - public Object clone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new IllegalStateException("Somebody forgot to add Cloneable to class", e); - } - } - - public String getOrderId() { - return _orderId; - } - - public String getPackageName() { - return _packageName; - } - - public String getSku() { - return _sku; - } - - public long getPurchaseTime() { - return _purchaseTime; - } - - public int getPurchaseState() { - return _purchaseState; - } - - public String getDeveloperPayload() { - return _developerPayload; - } - - public String getToken() { - return _token; - } - - @Override - public String toString() { - return "PurchaseInfo: " + toJson(); - } -} diff --git a/local_store/src/main/java/org/onepf/store/data/SkuDetails.java b/local_store/src/main/java/org/onepf/store/data/SkuDetails.java deleted file mode 100644 index c78da597..00000000 --- a/local_store/src/main/java/org/onepf/store/data/SkuDetails.java +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (c) 2012 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onepf.store.data; - -import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; -import org.onepf.store.StoreApplication; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -/** - * Represents an in-app product's listing details. - */ -public class SkuDetails { - - String _sku; - String _type; - String _price; - String _title; - String _description; - - public SkuDetails(String itemType, String sku, String title, String price, String description) { - _type = itemType; - _sku = sku; - _title = title; - _price = price; - _description = description; - } - - public String toJson() { - JSONObject o = new JSONObject(); - try { - o.put("productId", _sku); - o.put("type", _type); - o.put("price", _price); - o.put("title", _title); - o.put("description", _description); - } catch (JSONException e) { - Log.e(StoreApplication.TAG, "Couldn't serialize " + getClass().getSimpleName()); - return ""; - } - return o.toString(); - } - - public String getSku() { - return _sku; - } - - public String getType() { - return _type; - } - - public String getPrice() { - return _price; - } - - public String getTitle() { - return _title; - } - - public String getDescription() { - return _description; - } - - @Override - public String toString() { - return String.format("SkuDetails: type = %s, SKU = %s, title = %s, price = %s, description = %s", _type, _sku, _title, _price, _description); - } -} diff --git a/local_store/src/main/res/drawable-hdpi/ic_launcher.png b/local_store/src/main/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 994253e1913f7ff4aae77db4dd4f0dcaf65124a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1072 zcmV-01kd}4P)O{$*%V{;0;2TMdQ3PoT z(hdBi_S?A48an6z>o`dT1pg(+D1!7freBek#9B_kle0u+?T!BvV?;q(`WZnkj74#& z6@64pA>D8KBb8%BL3(O4Jk4p*dX9ciP8uC-?P$apU62;PjhFJqy`(sjM+fR3iyWg1 z(sO;Tprnp^NeLvCE~)Ed6=NnrU=!qzOog;Q;^Uho>w|9calCR&BSxFEBYq7AU$42lvWLU!Sv*;q66|qON^NXg^EkMfuC66YMKJB15*MlIT1*jxnN;@0F4wEW(4*xtcM zEJx4bjtkISvv=YP3hg;uRMvio4y-TP1U1eRn-@p?q25gvkEZu!W-wvEN{Yw(AIpPq}egQ*-_z~9pK0vGYRTt1nJL|hCz({#3nL0r89_O&^|dSsM(e8uIs0)cLp6$^){#1+iRO1_wl$H0 z2Iz7sGs4Bqu(L_K(8-drKYRP}64XxRY_gbyALV2dt-{;mBFM(e)$=`XXIe}Kc$_n7 q6huLT^yJxw)VcTU(c2sqRy@D-Me@3eBpb2{CpnvK0eRSW+NdX zAt538pW{ICIQ1;jX7=9&?rNLPGvj41L?L+*cl{)i8MEM%K9ylZU_e8mLPO;JdhI_6z!Dtp(xy-gbD zn!>QDeee|($X0cTQdg;KYyN9p7*ah1+%$o7b^L8ioSl^#i{ACw{>CKFPGCU&2zb*3 zGNlE1`IIPk7Cd2irW1dt%LTyA7uaor5~NX^2kH;(y2n$G&-GCZl_(OvDgxPtPWV^p z>>9uQeKdv%{R4Q_1=8K3L}f_xF+&(+s|5J!3Y>Yd0==tq@Ibu}@!$=pDFNrZ5V+%h zxb@SU>D~B4c>nf}^#x8p{|c9-S2^(geH8J)4{^Y{0$0?RdEv92R3;#4RRQ{`1Q)_@ z;O6F&1K@+3k74e^7$p3x^}m60^B-X#Cg$MBqLZM$9;~fXs67~gi!1Y~z^8kuZQ(l9 z+%bw9a|EPI_`h$uv04+DIgXUp+%*C*t@y4#QwstcG*aC8FLJCKLv2`7K#&*OgJ0~0 z*89b=b_}*)C4n6ZNy9dPx}x6$V5`%7>@Wu0sjh&igdKtJ9VJg6ynP+`vKOv~fKDUH zbKbkuQ2v|vc0-b5J$YVd6%kM=^v!AXR05w;&O_eEh6<$cZ++T`NyL#83Z9+bGodg> zfJSnZ5|kdZCUd&a)uVGv)DQuOD392Asm1i!DBs_LPpT0zohBh6At52La{d51B7I;L SNGu`%00008m4-ivl>8g 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 - - - - - - diff --git a/local_store/src/main/res/layout/purchase.xml b/local_store/src/main/res/layout/purchase.xml deleted file mode 100644 index e41a0c78..00000000 --- a/local_store/src/main/res/layout/purchase.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - -
  • 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