From 9e97602060ac3f82081222fca1abc2a4f8b28ade Mon Sep 17 00:00:00 2001 From: sebalopez Date: Tue, 9 Jun 2020 09:20:59 -0300 Subject: [PATCH 01/66] test --- a | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 a diff --git a/a b/a new file mode 100644 index 0000000..e69de29 From 2c17a8177b5e97b0e43474f1d5756729249cbd45 Mon Sep 17 00:00:00 2001 From: sebalopez Date: Tue, 9 Jun 2020 09:32:07 -0300 Subject: [PATCH 02/66] encrypted google api keys --- .gitignore | 2 ++ .gitsecret/keys/pubring.kbx | Bin 0 -> 1460 bytes .gitsecret/keys/pubring.kbx~ | Bin 0 -> 32 bytes .gitsecret/keys/trustdb.gpg | Bin 0 -> 1200 bytes .gitsecret/paths/mapping.cfg | 2 ++ a | 0 fastlane/Appfile | 2 +- gradle.properties | 8 ++++---- secure/google-api.json.secret | Bin 0 -> 2009 bytes secure/key.keystore.secret | Bin 0 -> 2422 bytes 10 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 .gitsecret/keys/pubring.kbx create mode 100644 .gitsecret/keys/pubring.kbx~ create mode 100644 .gitsecret/keys/trustdb.gpg create mode 100644 .gitsecret/paths/mapping.cfg delete mode 100644 a create mode 100644 secure/google-api.json.secret create mode 100644 secure/key.keystore.secret diff --git a/.gitignore b/.gitignore index 5707080..d3456a7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ /build /captures .externalNativeBuild +.gitsecret/keys/random_seed +!*.secret diff --git a/.gitsecret/keys/pubring.kbx b/.gitsecret/keys/pubring.kbx new file mode 100644 index 0000000000000000000000000000000000000000..49632e7cb4189e85e8a5740832ef591607731612 GIT binary patch literal 1460 zcmZQzU{GLWWMJ}kib!Jsg1Gy2??D*Eh5*(nOpIVw9RmX^69W^2jBU#|=R7;-x9*MG zXNhi^a-s70LXchskSf6wg&xnlR|l{A@x|%qBdd}m2e&0aVH=Pf5HK?E0Eu}(mK>PR z#J~ck(Fm9eW-{`!#O<+~&B(!UdqEzX%$#%s>!%r(^OFLym(P8~sw=>|1`pZq~iI%w`Mwjwzj+=)=VK<{sB4^)>$^-pExwxf#dyhvo97 zk3yyEI&{laZ8TT5_8Bz%7<#i>mE}5%9VQ%(F2A}bdAE+W zTi>c%9JlA6@)B6{IH&lF@_HSE<8x&12&DGaupEiw$vHA9>k!XMrNc@b=Q~d=IlsG6 zJaqSxy=!|z+gJRMY}$A%@0Vn+*=;%RN~WhF2OmFCeC2q2=9Tm9wo+3VbD|^)|DAem zn0{u>oV;3{=~{yoF8+KTgk#)W0J8kbbHB>Xzl8~*9_ZD>8dEARq;u|?7bHU?&3lrZc> zB-MR?-NdGp?To#m#SqGLeADEW@;@)|$|oL5w>J)o)&6#9PQ{~{_rFNK)!y1w(7mgv zL`_s)$;?mUPKWp=7pV4yW^H&ze|7T!r zykc%wTUB-atg+lKhKo10&(Atk6_dfc(Wx|_F*)ve`CdaY=bDFB;mHfcI+U0kE=$w| zOzDtkRh>BDy1pnlsdh5jut)%tn;L=S#sf)i$jNH@Qx1khGyM);I>4}~dR6P9*tTHgS(Q6I; z9oaK&hpz1VF}GdtR@(vf!sACS@9OSWz2?~BKgP3{QHgnl6nOd04z z8M8D=xa}PZJO%=*Mk(NIC|+znPl}Y1e;LuB3$;PNSgSGNBLrI&TH^=mEdB|RyrSj@ zcL_=dVbg*zJ-Ykxjk*o6xLT~lGEA?VsA!ZbSqO)aG&aSiLnz@{rRyE~WYVMd!zLPu zMNA;jt&p8Xnu1NCioi)Q3MNi^1dP2@-#(6LQ(p}qEWkV{mP#m!%S8zRyCC@~X+r$x zpo3mf_CTgoCRhSo>h4gtH6C6^;bgO8X=Q6?6}26rW}}K4j<;nM)k|xoP`W0NuF{4m zHMW->T1K(z$9*0G3zs-XMZVJN0sj}Y1FR4x7l+n!VPgVT!H~CzMuOdRQ~~|u!MOVg zE+FM$13q-dmG&)8&DJ)v#U0|>_+rD-%1pWzz}6?Piml=EoLqw>{dJ}~qZJn`_Fen$ zYx8kmO-}+dDauGemLVgy*iq1~c&MWB*I82t)@{4VY3^ zN5dIx&Gw=lgBF*D-q}ziu1aB4un`LUM<^$57*%Ry2w&_1ZZbKWhVIQ%F9%Z!z--d~ zravr`qQxVtVkk9&^PKzmON5B}T!DbgZeKQ%?E}B9bzgO>mTAO*ivdzTYZFa_w29Bj zy`MQ24DS)uh(ZE+HYSKmgbmcLnAqodD>f$4d9-*2js}dy)B6A-M74;041*b~-`?Dg za&G+*^VkR{Ofq>%jAVKCaC4f*n)4nIAUj27EUg^)NjC}=YO%NNS&-U2>q5;r7h=1m zsIa>5jRkRR&WnWL5!K}mI(OGC09HIO_c8Qc#Tesq1Tb?#NQ`O*>eaCfVEYP|HOla; zx(|#<>}@-(AEn zc#EKX#AvmOM77)r@<=C!s23AY!8!0|`k|Ge>G>mYT9D?hT=Osn7;%p$C~=;Vpnhj^ zWT1S4t+1G<)Lhg~i{Gr(+)j2q`hNlTmen`XU^!wv>oNcaxndFT%mb3bzP4?`ZLXhV!^wPO^e@ZD%I#iBcbfZ{0ZjhJ%YaN`h9~H$yjP2N z`oYKi_0V0MK{Pc;9h4o6J+96Fjs_)rtC@61__*1FB-<;<@0$Mqp2pCWwe#E>ZNw@% z0?>Gp#?xVNY)1I{qwWLC!uucIqw{B~iLZJDd3Y0Q%WC#m57dt-dCbzS<-17J+H;`~ za7BPq0`2y=yd&?B?UKdzY7d(P(&_?c^bC6u8(l8)trq6l_@WG+SzFfaJo zQ}c6>N|j;hzGUst$KH7?R5+&&j$e?jHY1~_?TDA!xlu}K=T(99JP{7J3CoYDB_4YNC&K2v}_CEtRX9E0N*Bit#NcS!~ zf_!H9$~D>K)=^V|_7ihKzSuAuR;h)QqmNX@e>0929h1PsB4fU%IWPr=*7q<#9oYi& zQez0Vf45dCw=(8&|ZSnbZ}^tlTW(YmAMm z(qyU2aq1P{KJKvOzMDHCr(4k|0ER5xC~eC(&B{NH5EWgy|(Y?3@~bnY63ELiJ>*68~FMk)xRC8B3SBp#^ z=avUds(jhrsB^^sQlybSrDlL|mHC2hak`wUVL(Pc(U>PqND|f|r rzO&_zFk@^T0CG{m>Z`GP^X`q$`?`6+SUiw}xLNMr4m@?^^u@gdPu=hA literal 0 HcmV?d00001 diff --git a/secure/key.keystore.secret b/secure/key.keystore.secret new file mode 100644 index 0000000000000000000000000000000000000000..692aea3a42fe337c9566802e757fd5082ed347f8 GIT binary patch literal 2422 zcmV-+35oWF0Sp8A;yQFb`dY68sNCLG)m&O~dq@UmuPVxQJL9tRm^h#|LxzVaw_`ZmycoGzk!4y*%6 zFgK2DlKnt0q$uN&?o6wP8eZbPFneb=LC9y&`*{;T9 zmV{ZF%)XUT^?}m#4ao!*tUH6DA0W_?$NpZ`dP4jNkZuz{}k^L0l9gYzVr1@*GBN-TYVYO1m^?;w-IBII=^7sK>B{Y z(`?Hf;NKG!QT`7~$+8}p=660F9@>7y#+ ziL6DNL8*`}Ku#!X3U%b%#D_tj)>_KRCd2eeJ-)at_?DjG#w9NkHO@6UHBoQ&*b1|S zsxVgg;;VMboBNR*Pv`d$Pk^98ZjE#3g3yFCm?z~`&dueSEpA+lZN-Q^Lz^<5^y2p@ zhI>p56g&zI&rl=(It=Bp*DisW4vNv75{`u9FYAJjD_Q!-0OX7vDpz0(@BG`4O~>VE z_8E=oD=X}^Se_n36V>e!1n!YNcyVbKD`5hSaM^q5G~8wIh|u!Vi}c5R zHAx?(S{zhrnrjG`#PPUV@Mvgc90M9)1}dV@Fz#$wb;SRn{Vu_G48Nc zhF1(c2aK-%ohFS>L&x5BQc}3!KZ8eH9Wt}p0Y3w@;P-#V?8j%KpY?~L6HKV)%SaN^ z$8_SUqaXultUZ%O#-oTu2Npy_X1i?y(6DTh|0q|`x{?=CBv(ceqvoz>MrC)II^9n6 zussHlUv_9X=;}W32cH%*zC44hBI*Ia;9rD{Zv5Itrldk=m)qT*uF#1<}cvaA&rnr^J%+jd^}H++I%K8lCS(FIpoO@*ef;M%T9!j zDQ< zg4^&Pq#?qb1ttoI;V%fK))Q&fZD)5kBHeNjvARS$M%&0;#YX#wEz@5x9-sRUMme_O zd>syVAPU{VhKo=gr}V(|F%*Xj;4h(iDAh+g@#5(8OsnKu5V{31hnIMOk%G-{Rqo9s zvEc;0*OoyoH<~7%8phE5cHd->`y90^EhS}9O^oe7eZfLmx)!VoEkmC>)vcMbClx~f zP9jeuaA(PV$GN*No4zev>A{gNf!#UnunpLI0zsFLkRZkRg`~mzi~t2b7;mxis0*0k zsVK@ zFIqU|+l{CGAOlL6%<3Oqm1XiX2YZYyZSp7leT~(0%-%tO?Ig_ADMw6}g#ni^-^3k_ zJbZJP;z8U!L!L)IBB#b`7hhxQGHRX%`2uTdM;?SOSo%Wicrw4d;S{DwIYrruqJL}7 zOR9!)S!_B2BToGW!8A-Fa4d__-Za(h?(J7eN6)*nhZVrvXRFXs_(p1Q)`Xp14Pe_)KSu_nF!dTm_4A&}IHL*n0Va1wO)V z)1Oj?7%Z^R3sNDbsM-Fjd+QMV^HGnp%Dok+lLF0rHlXE5B}gvIy3!`aim`6hZw&i& z1ZX7Ax7+g&Q914J9nL(=c?t^=bwGjeVHgOCk|YCrpal?UQ4)bEG?6CyL^9zJnYi8- zFq(gASq9r2gz@sy`C~(qm)G^wyKUk6d4WmNmG>Ry+rF~45lg+`C&Ou`4aaO1AO^@^ zv$D%#wsk(3@01;%8F0_qS};;J9xv6745{T+gXc;=3;av=LjaFyB|$2pG$C$21!beB zoCTWQmu~h?OvM49n~16fc@ixr?8E5dJe&ZjnnL_oq22@YS|}ZVq_i2= z-pXZk|EOrFHm`oZ2K?+T!SnLnPV|gZG)_Ww`&Q<`1#B~ zJBYuwmv8ot94qK;q^Uo+q5xYyJjszv+t*jX^K>6NsQ&bR%sOv+VRSd`yg@tQ9bqYH zg2~|z&}j1RcO}7+zV)hIU;;#l;86t|*6V*iQCUr|E~x0Ym2+ib7!L4%)8U@eRsELs z?NP2HkSA2#sN=7%$(G+#!_r$(s*~DZ_R9L@0iTm6bibuCO-PDxPIYJF)G){QPDyY+ zL8D{vL^-9Fa2y!92?rH{YQCGo7*{aZjzmnJ2=*hhKZv9Jxyru>3bMG2c33~6P&e6h zpwaiJ_tgCNAC6nIX}jSItpMFz3|Vcc)BJy;RZS&7`q zGU_&m1nhl0g>REGx~sB+mNxn70wV zNdGc=jM!_izMO;fd6?b5T|I2y!rGA04%jS+d-j&jmqEjypgW#mGd`gc2>j#{4-euN zlI8Dt{;`12*Y0uujp?AsSrOVyD)b#z>3xg68=WR z3%(>pSt@aRc5Jzm5{zypHSa@!$QK1|%_(*0TD*>U)bGml6LA>FWF*F~$y-uZoXBOl zRVf0Oldk;&wVoc Date: Tue, 9 Jun 2020 14:56:32 -0300 Subject: [PATCH 03/66] Added details to Readme and included gradle.properties as secret file --- .gitignore | 1 + .gitsecret/paths/mapping.cfg | 1 + README.md | 25 ++++++++++++++++++++ fastlane/report.xml | 43 ---------------------------------- gradle.properties | 8 +++---- gradle.properties.secret | Bin 0 -> 1103 bytes secure/google-api.json.secret | Bin 2009 -> 2008 bytes secure/key.keystore.secret | Bin 2422 -> 2420 bytes 8 files changed, 31 insertions(+), 47 deletions(-) delete mode 100644 fastlane/report.xml create mode 100644 gradle.properties.secret diff --git a/.gitignore b/.gitignore index d3456a7..0d56f93 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ /captures .externalNativeBuild .gitsecret/keys/random_seed +fastlane/report.xml !*.secret diff --git a/.gitsecret/paths/mapping.cfg b/.gitsecret/paths/mapping.cfg index 5e34e83..77a60b4 100644 --- a/.gitsecret/paths/mapping.cfg +++ b/.gitsecret/paths/mapping.cfg @@ -1,2 +1,3 @@ secure/google-api.json:e494b2376306f39ea643ebe13156e4b885cddb64755a3a3fe4dd5f2807cb9a70 secure/key.keystore:6c70718b97803ac753020362b214a40762dc4ed95c4665648f86758c20ddbae9 +gradle.properties:7bb5c0348e4ae886eb140fb65b455624107816ed0ae813d2a176ee57edb095d5 diff --git a/README.md b/README.md index a053190..1bb1e78 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,31 @@ to handle the server side authentication, in case you need to modify them: ## Usage - You can use this open source project as a template of your new Android projects. +## Key File encryption + +Build signing requires a developer-owned keystore. Location and credentials for it are specified in `gradle.properties`. Likewise submission to Google Play requires a Developer API key in .json format (`google-api.json`). +It is recommended that these files remains outside the source repo + +We suggest using [git secret](https://git-secret.io/) as a simple and secure solution for keeping these sensitive files in the repo. See [Config](./secure/Readme.md) for detailed instructions. + + +## Build and Release with Fastlane + +We provide configuration files for automating build, test and submission of the application using [Fastlane](https://docs.fastlane.tools/) + + +Lanes for each deployment target example are provided with some basic behavior: +- Each target has two options: `debug_x` and `deploy_x`. +- Each option will: + - Increment the build number. + - Run `gradlew clean` + - Run `gradlew androidDependencies` + - Build the app (`gradle assemble`) for the target flavor. +- The `deploy` lanes will additionaly submit the APK to the corresponding track in the Play Store. + +Check `fastlane/Appfile` and `fastlane/Fastfile` for more information. + + ## Analytics - Add analytics manager: 1. Firebase diff --git a/fastlane/report.xml b/fastlane/report.xml deleted file mode 100644 index 9043bc9..0000000 --- a/fastlane/report.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gradle.properties b/gradle.properties index 6ba6165..51fbaac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,7 +24,7 @@ kotlin.code.style=official kapt.incremental.apt=false ##Signing configurations -projectKeyAlias = Rootstrap2020 -projectKeyPassword = Rootstrap2020 -projectStoreFile = ../secure/key.keystore -projectStorePassword = Rootstrap2020 \ No newline at end of file +projectKeyAlias = PAlias +projectKeyPassword = KeyPassword +projectStoreFile = KeyStorePathFile +projectStorePassword = KeyStorePassword \ No newline at end of file diff --git a/gradle.properties.secret b/gradle.properties.secret new file mode 100644 index 0000000000000000000000000000000000000000..d9a06af8631db2e39d63a85351249949c9897f33 GIT binary patch literal 1103 zcmV-V1hD&s0Sp8A;yQFeNdZrWX!cgRLF?K;Tbra~Bd~&rpxb9n zz-%e+7}L-LU^Ctn_gYXVB>qJDAjxA&Q!Sjln!l)~RaGA7vCw*tS!km%k`2)0{HfD{ zOi8VJ(m=79tf1PKSv1G3dugd-uTlbJ9v5CphwE;oF^q!l$pF9zO5#F8wq@KI{&_h>>*U}@ExK7lsLV#KG!q7zxZ%x^TQ#tJl znDT6RM-zf|_*O3xE9QrXYqwDR&ZEyW^G`2-4mfzYH~5CN#d)1}88E`ij9B3{;AgCq zTWWv&{(^io+2-UGB@!F@q7FGRhCAghmTx`JtR`2a@ zZRY^?w#L!Q%IHWIKp=Y&UAR)3OWkWMVr7pE0EWcVIh#caa$UO|>}ZyASxJ`0PVrVb zaFnRt1NzEJqv~h27$K`iOiumqfA!-wOe73~U-U=!pC)~~ ztl^g0W-zD%a~ri(Z%idzgeJ1~-A-Osu$z0|=qg!`djawh1IP7XB0sw9ga-(R{u85amWOW>IJ+20aUvPtMh!7<|pHVR=Akk8=V3VT@ zBK(Av;;XB-`d04nc$ir^=cSqUklQccphy-wFd^7qf&+V+-c+GSh#0?YnKRFj5hsvf*5rkVGNDA`5Sx-~#Rs?b z-k#3}N=4-`5ehg&H(QB>!o4k0JG1PO7yhIrrud{*LG!vzb%1uowyCLI-jv;?34_l> z3w{@beH+P0iGhOaKR(?lB1)%=Jqj{Cn(h|ff@GhyLnqr*~2D9NgXGAZIXf86TN~%8=E6V3 zg)(aZgf4-rE{_*^C9GEDqsg(S%I|IH1!DKBfPLd_W24|hF`Kc4YT4l66@}K*+5A{+ z$wdv{)e!M$fqC00Ls3^ly6@QxIbB361i@Ch=&&hhM~RxbPrYO`pF|`_GJLbjS*^=Kp;MBF%52yC6ZMXw!+z%d3_KXt6Sh-Z^OD#`n{f{8Sya%4Ykwo z?tH(m_tw5ad9c!)Sd2}wiI-u*jD9zArk}xm=I>yPM@0*33yf*n?_<1uj7T$6pu7cG z@KbMrD?+@dd%=bIT2snG@DRKZ1%SYs+U{n9_R5g-@P*gs;E%DY@K>ZW#Xiz{)t5jI z1$is-qK2k#Q}iaha?Dt}n|vs)4Vuj2`5h6(IIvCX&I;XyDa3F`(sT+{6P}t5L!2n^ zY2`#N#hug@fpTM{#v7%wr-ixt41bJRcTv~Bi79)h-#RuhuZNE`6<7#12|c>vdE&ow z;RXHmaw3lS`1O^+Js~4y*Xml$fD^bf%ErfYA(QxUD{Lrr(pO^PIT#nPbN+r8?j6NT zc^aR+vTvA3UOeXeS;Lu`7Iq0KvMryMxH0M}MV#Ip?@+qxb9(LIMQ>uaUSeRbK7e1e zkVf4oK*~RVC{dxoi-biXD<)P~O!i4+XERp=qO*n0yYNOv!zx5S=R&^b8yg@v^w z`YCnSwU%pMEZcEZb|x~5fS=L5dIQYR&9)-UgEjxp9+3*hLvb95<9v#Cm+(s)?d*z; z$D?g7MK`a`P&NTl1$WTvv@ZiXHglx>uBcka2apn2Y~X2~*F-Mf#c}FrF^yZuVl{9j z4Irhf!g}fWJN%`*jMM%!e4E=o+t{kA0M{O91N2@%r<3*zcr`e`GY$K%Zq{5f(v}4| zctzhR>HqdoRjMVb;$ga&n!9pNsTY5AqV#H<#Qa$-vRZ=$-o{}HM}YNqCXNfd2&N=4 zYtH|LTaI{_Wlnw7wE?~=bRt$1b8V(f0C(`7>W|D|+Ss+kiylly6t%4fPiL)q_?lpq zl}ppLGLiFp*d`Ha&LW$}S2-y@IP&?g#|P*%z$9n=8Kx5v>Z_DVjl(=RZRR0Db#-pi z#sQ!uikWx`evhT@pyB*+!-{U22 zv15I~MB%8D(u>St7#!x)f0Qj6rRZnTB5E5S@2`c)C1~BKbMOCV9_cb0sO{DgQd)TrL~IfQY;CU0kHa_^ zY~+?c3UR7CAJ)1RWDnf~e}8lqiKD6$qrm+ zl^S`NOn!14{ks{3cB2cEPGys;qM*(i%6;eLt5&q${C`YyUV^QGA+}ObBa&3iN?J!A q`EeUM19Yhh2jp-uvN*LcFm!bjxxpv(6Q+lIP5cjOvEAY3YCq!rHPin9 literal 2009 zcmV;~2PXK10Sp8A;yQFSWz2?~BKgP3{QHgnl6nOd04z z8M8D=xa}PZJO%=*Mk(NIC|+znPl}Y1e;LuB3$;PNSgSGNBLrI&TH^=mEdB|RyrSj@ zcL_=dVbg*zJ-Ykxjk*o6xLT~lGEA?VsA!ZbSqO)aG&aSiLnz@{rRyE~WYVMd!zLPu zMNA;jt&p8Xnu1NCioi)Q3MNi^1dP2@-#(6LQ(p}qEWkV{mP#m!%S8zRyCC@~X+r$x zpo3mf_CTgoCRhSo>h4gtH6C6^;bgO8X=Q6?6}26rW}}K4j<;nM)k|xoP`W0NuF{4m zHMW->T1K(z$9*0G3zs-XMZVJN0sj}Y1FR4x7l+n!VPgVT!H~CzMuOdRQ~~|u!MOVg zE+FM$13q-dmG&)8&DJ)v#U0|>_+rD-%1pWzz}6?Piml=EoLqw>{dJ}~qZJn`_Fen$ zYx8kmO-}+dDauGemLVgy*iq1~c&MWB*I82t)@{4VY3^ zN5dIx&Gw=lgBF*D-q}ziu1aB4un`LUM<^$57*%Ry2w&_1ZZbKWhVIQ%F9%Z!z--d~ zravr`qQxVtVkk9&^PKzmON5B}T!DbgZeKQ%?E}B9bzgO>mTAO*ivdzTYZFa_w29Bj zy`MQ24DS)uh(ZE+HYSKmgbmcLnAqodD>f$4d9-*2js}dy)B6A-M74;041*b~-`?Dg za&G+*^VkR{Ofq>%jAVKCaC4f*n)4nIAUj27EUg^)NjC}=YO%NNS&-U2>q5;r7h=1m zsIa>5jRkRR&WnWL5!K}mI(OGC09HIO_c8Qc#Tesq1Tb?#NQ`O*>eaCfVEYP|HOla; zx(|#<>}@-(AEn zc#EKX#AvmOM77)r@<=C!s23AY!8!0|`k|Ge>G>mYT9D?hT=Osn7;%p$C~=;Vpnhj^ zWT1S4t+1G<)Lhg~i{Gr(+)j2q`hNlTmen`XU^!wv>oNcaxndFT%mb3bzP4?`ZLXhV!^wPO^e@ZD%I#iBcbfZ{0ZjhJ%YaN`h9~H$yjP2N z`oYKi_0V0MK{Pc;9h4o6J+96Fjs_)rtC@61__*1FB-<;<@0$Mqp2pCWwe#E>ZNw@% z0?>Gp#?xVNY)1I{qwWLC!uucIqw{B~iLZJDd3Y0Q%WC#m57dt-dCbzS<-17J+H;`~ za7BPq0`2y=yd&?B?UKdzY7d(P(&_?c^bC6u8(l8)trq6l_@WG+SzFfaJo zQ}c6>N|j;hzGUst$KH7?R5+&&j$e?jHY1~_?TDA!xlu}K=T(99JP{7J3CoYDB_4YNC&K2v}_CEtRX9E0N*Bit#NcS!~ zf_!H9$~D>K)=^V|_7ihKzSuAuR;h)QqmNX@e>0929h1PsB4fU%IWPr=*7q<#9oYi& zQez0Vf45dCw=(8&|ZSnbZ}^tlTW(YmAMm z(qyU2aq1P{KJKvOzMDHCr(4k|0ER5xC~eC(&B{NH5EWgy|(Y?3@~bnY63ELiJ>*68~FMk)xRC8B3SBp#^ z=avUds(jhrsB^^sQlybSrDlL|mHC2hak`wUVL(Pc(U>PqND|f|r rzO&_zFk@^T0CG{m>Z`GP^X`q$`?`6+SUiw}xLNMr4m@?^^u@gdPu=hA diff --git a/secure/key.keystore.secret b/secure/key.keystore.secret index 692aea3a42fe337c9566802e757fd5082ed347f8..5823e35e82980b2551783d91bf705e0e14aeaef3 100644 GIT binary patch literal 2420 zcmV-)35)iH0Sp8A;yQFNFU# z6LdYbqeWdu{@ahv5?fQAdTs4dN?Q=1(&MJz zxX@9WUu6 zZEt3ZR2Tv0K`8}5Y)L>(ZB6sRt9%#LEy_Z%V{EH5x8jR~&uDtM(Dq9kOE_$5TP)69 zgPPQ?1tscT&t#G;cgagA0FM~GjQW8VfV_cUR)c~qXAoaG4yrWq+w z-!1$>KH@wytnQ>-+XVIOqp|Sotpy?eFR1W5)^gTT?QVzmemV;QK-s^Xja~^@N=TLA z3f_%1f|R7g54Zp@R;9}|a2ZzwPY>Nw&Z02A3stE9fHA2!l?@j|Gfxw94r!(joKQYR zcoq(4i6dFfK-B>@$GqNj9LFZ1hoBB$N&93owAVdzg`jr{k2;AUC5W=NB}=QZ5FScU zQ`}M%sKI^Zf?2~kmHJ2!_DNmloYG!ha~IBg5Ct;?mCTLFs4;dZFKPD!OgRyw#{q0+ z{Lb{Qb@}Vqh(Z}KkN!~wW`PXYwsO6z6Q%5nBW9Mr~Z++&} zJor>lKI&BWO>=1R?eI%Mv(fWa3t%b09x=%tsfB%#6cC{w zaPDZzb~LD(sE>ksax8lvf!mwEl9DG_|1QlTcKu`&-kU;IpR=cS@9++2grCZ?+LH*p zlcyEJW_6ZV;%8n=Yu*n~$;yT}!;aBUPIhxN1%k>c)@FVV#VYfDD;L$F5+Oy_9KFJ0 zhVU23$rmGh2siNERpF{LrlR2qxm1>YzUiK!i*_8jJkitmxqL}+w`#=Bvlj*UqzNA3>o|4g(i3d-~=4@{yv1U>z@7ALQ7D)%zp9uc%O$f|9 z{)W4X_k+rRa=GX*N3dKOH3q-ps?0ima)#xf%BDFQ9gz{DvSo*ChDcoltm!-q%H;t0UQ0U&s4YCsP3Q*FR*G%vr!I9XGLSw6YQieFn`HrfN>~sToe+ z7Mlk;WAR$A!A~e|)K5l9zwPJS(>HL&6*|Va3;&>Gm=ATpZA)q;9ZMs!)t4I*%|c#i z0KTr&0BalbWTjTU~qivV+><#r?~ z-(f@H6laHj*g;h=~7& zDHG3o0(`N>>D41C%~=F6e4k;+Pfq8LpsX1A#HZ6r0OQHG|}m&by%MIu-AoO z7R-tNs4WKgN4?bf=6O+KHp_<)Wrn5$7NwkV;nhRQ@;_Gw`a$1yGGAO*FJZ*t>n8C_`Aw<%+BKAl<4!819&f(~G;wm<)&5|TQMIRV*|8c4BBm9CJ z5w_ms_*S~|7Usrrb1k=CzlQ3yM*qCIFYr_Ob+pCLwBzr(DEhc*sWjJB+MXBrO>PwX zEE_NO!TI7%7yncuLHR`??0L(B!BrdxB9#9;Gs`l)stEbM67f7SXj1Y_(sc8{tmHH` z&LA40{pf-E4TE?G@6p>5KhZDubIdtjn9t*UIS?HHtEy18#{coSV#89Kx_7sf5$3vc zBrDeNR}t&SQ%8(_uIMEM^Zc?AM0ijT`f@^6+tgf237uG~U*)9TID&4tQZ#iqv-LU% z+GWw2jvQXx9IYn^d*fWHb9nc-k&S8aGm`R$Qjl`GYH<2n2U{5{(yGR}w6r(K%2unx@ zZ++G9jr2)B6UrVW`n9J_qaG-DrJv@HE|4mQ%wx4kR1-95Ds{!*TJ157{ePODX8_K> zw311&N<)2}&VdT_Q(>9+t`K0m5pS{08#9e!fdhuI-mxe}%flXP;aO4)zj}AMW@EpF z&I}|&+9OLYN8OCYxuxC8c`bk5ws!jL34P%rajnbVzgBEWLy|1jVjIZb#d5WJ2ZtIo zn&hjj?$i?Q2E0s)^imkOes*2^E`9lK&f!Y?SgL{f2TAqGIP0l1pKoUbL-d-q_q9{D mkYI2}w#~{Xv%ezPXqLkPO)_vV_h-@0>ES%ThSur%pX;Ub`dY68sNCLG)m&O~dq@UmuPVxQJL9tRm^h#|LxzVaw_`ZmycoGzk!4y*%6 zFgK2DlKnt0q$uN&?o6wP8eZbPFneb=LC9y&`*{;T9 zmV{ZF%)XUT^?}m#4ao!*tUH6DA0W_?$NpZ`dP4jNkZuz{}k^L0l9gYzVr1@*GBN-TYVYO1m^?;w-IBII=^7sK>B{Y z(`?Hf;NKG!QT`7~$+8}p=660F9@>7y#+ ziL6DNL8*`}Ku#!X3U%b%#D_tj)>_KRCd2eeJ-)at_?DjG#w9NkHO@6UHBoQ&*b1|S zsxVgg;;VMboBNR*Pv`d$Pk^98ZjE#3g3yFCm?z~`&dueSEpA+lZN-Q^Lz^<5^y2p@ zhI>p56g&zI&rl=(It=Bp*DisW4vNv75{`u9FYAJjD_Q!-0OX7vDpz0(@BG`4O~>VE z_8E=oD=X}^Se_n36V>e!1n!YNcyVbKD`5hSaM^q5G~8wIh|u!Vi}c5R zHAx?(S{zhrnrjG`#PPUV@Mvgc90M9)1}dV@Fz#$wb;SRn{Vu_G48Nc zhF1(c2aK-%ohFS>L&x5BQc}3!KZ8eH9Wt}p0Y3w@;P-#V?8j%KpY?~L6HKV)%SaN^ z$8_SUqaXultUZ%O#-oTu2Npy_X1i?y(6DTh|0q|`x{?=CBv(ceqvoz>MrC)II^9n6 zussHlUv_9X=;}W32cH%*zC44hBI*Ia;9rD{Zv5Itrldk=m)qT*uF#1<}cvaA&rnr^J%+jd^}H++I%K8lCS(FIpoO@*ef;M%T9!j zDQ< zg4^&Pq#?qb1ttoI;V%fK))Q&fZD)5kBHeNjvARS$M%&0;#YX#wEz@5x9-sRUMme_O zd>syVAPU{VhKo=gr}V(|F%*Xj;4h(iDAh+g@#5(8OsnKu5V{31hnIMOk%G-{Rqo9s zvEc;0*OoyoH<~7%8phE5cHd->`y90^EhS}9O^oe7eZfLmx)!VoEkmC>)vcMbClx~f zP9jeuaA(PV$GN*No4zev>A{gNf!#UnunpLI0zsFLkRZkRg`~mzi~t2b7;mxis0*0k zsVK@ zFIqU|+l{CGAOlL6%<3Oqm1XiX2YZYyZSp7leT~(0%-%tO?Ig_ADMw6}g#ni^-^3k_ zJbZJP;z8U!L!L)IBB#b`7hhxQGHRX%`2uTdM;?SOSo%Wicrw4d;S{DwIYrruqJL}7 zOR9!)S!_B2BToGW!8A-Fa4d__-Za(h?(J7eN6)*nhZVrvXRFXs_(p1Q)`Xp14Pe_)KSu_nF!dTm_4A&}IHL*n0Va1wO)V z)1Oj?7%Z^R3sNDbsM-Fjd+QMV^HGnp%Dok+lLF0rHlXE5B}gvIy3!`aim`6hZw&i& z1ZX7Ax7+g&Q914J9nL(=c?t^=bwGjeVHgOCk|YCrpal?UQ4)bEG?6CyL^9zJnYi8- zFq(gASq9r2gz@sy`C~(qm)G^wyKUk6d4WmNmG>Ry+rF~45lg+`C&Ou`4aaO1AO^@^ zv$D%#wsk(3@01;%8F0_qS};;J9xv6745{T+gXc;=3;av=LjaFyB|$2pG$C$21!beB zoCTWQmu~h?OvM49n~16fc@ixr?8E5dJe&ZjnnL_oq22@YS|}ZVq_i2= z-pXZk|EOrFHm`oZ2K?+T!SnLnPV|gZG)_Ww`&Q<`1#B~ zJBYuwmv8ot94qK;q^Uo+q5xYyJjszv+t*jX^K>6NsQ&bR%sOv+VRSd`yg@tQ9bqYH zg2~|z&}j1RcO}7+zV)hIU;;#l;86t|*6V*iQCUr|E~x0Ym2+ib7!L4%)8U@eRsELs z?NP2HkSA2#sN=7%$(G+#!_r$(s*~DZ_R9L@0iTm6bibuCO-PDxPIYJF)G){QPDyY+ zL8D{vL^-9Fa2y!92?rH{YQCGo7*{aZjzmnJ2=*hhKZv9Jxyru>3bMG2c33~6P&e6h zpwaiJ_tgCNAC6nIX}jSItpMFz3|Vcc)BJy;RZS&7`q zGU_&m1nhl0g>REGx~sB+mNxn70wV zNdGc=jM!_izMO;fd6?b5T|I2y!rGA04%jS+d-j&jmqEjypgW#mGd`gc2>j#{4-euN zlI8Dt{;`12*Y0uujp?AsSrOVyD)b#z>3xg68=WR z3%(>pSt@aRc5Jzm5{zypHSa@!$QK1|%_(*0TD*>U)bGml6LA>FWF*F~$y-uZoXBOl zRVf0Oldk;&wVoc Date: Tue, 9 Jun 2020 15:51:09 -0300 Subject: [PATCH 04/66] added secrets Readme and updated .gitignore --- .gitignore | 3 +- .gitsecret/paths/mapping.cfg | 2 +- gradle.properties.secret | Bin 1103 -> 1093 bytes secure/Readme.md | 83 ++++++++++++++++++++++++++++++++++ secure/google-api.json.secret | Bin 2008 -> 2007 bytes secure/key.keystore.secret | Bin 2420 -> 2420 bytes 6 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 secure/Readme.md diff --git a/.gitignore b/.gitignore index 0d56f93..83d8a4c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ /.idea/assetWizardSettings.xml /.idea/gradle.xml /.idea/* -/secure/* /app/prod/release/* /fastlane/report.xml .DS_Store @@ -20,3 +19,5 @@ .gitsecret/keys/random_seed fastlane/report.xml !*.secret +/secure/key.keystore +/secure/google-api.json \ No newline at end of file diff --git a/.gitsecret/paths/mapping.cfg b/.gitsecret/paths/mapping.cfg index 77a60b4..08f0ea8 100644 --- a/.gitsecret/paths/mapping.cfg +++ b/.gitsecret/paths/mapping.cfg @@ -1,3 +1,3 @@ secure/google-api.json:e494b2376306f39ea643ebe13156e4b885cddb64755a3a3fe4dd5f2807cb9a70 secure/key.keystore:6c70718b97803ac753020362b214a40762dc4ed95c4665648f86758c20ddbae9 -gradle.properties:7bb5c0348e4ae886eb140fb65b455624107816ed0ae813d2a176ee57edb095d5 +gradle.properties:2a428ac440037c87d1001976c703d0ea412dee852aae78d9827f1307c4604545 diff --git a/gradle.properties.secret b/gradle.properties.secret index d9a06af8631db2e39d63a85351249949c9897f33..5b531c3cc9cc7018bea56090e835be96e44291de 100644 GIT binary patch literal 1093 zcmV-L1iJf$0Sp8A;yQFcK*@cy5t~5`Nii&WSYqtLY5Ua~+HgS0+ z&z}J!JB+BN&zv%m>zSlE$@3F#S)F@*nUnO$>d*V&9mHuN}Z}H2W%q73+>`&EZ8W@{L<_@rY|&z-S$w&2d*FAjaZJTXshBV$mM#+cOJ zsViS75)qNV~_d- z_XgdrCHyt#*SU27V_<#^q3TrxZR$1uyLkGI)voet z&=?6K=YUcSRM4~?!+?Ku;N^S!qIkibVU+SeagC&9!d41?IQ%XS#46}BMqyBe19_sy z@P5rrIe-@F3lgD!aBC>VUZPeYNd}~RO1LnJpUM^A7Vus$X*Y0z>9fz7*az?^c2346 ziN0-D>6k-NlaEP`J~6Z4*%{Q?CshIOR`R4ec_w%!{z~|7pM;WWaNyUC2wqPC^)p&L z2%>@<>I=-kJEZ{ zXnaf&kyH46jlK$SJ{6qD%0MiSe8t)o#-ZTzy7 z1(Cny5iCgkZ;|vq2-sRQZo|-vHiOilx#0_c#f_^vtw-kCZ7#dyEK}e%n>Q51X%>;x zoHsX~X$CT;DE&~MwqB@UlMpjfg}EmxusU~T&#C#7eBBRhYKDK_MtsjW2et6X&Oo3^ zezZ+y5;2m@$phT#-ieNdZrWX!cgRLF?K;Tbra~Bd~&rpxb9n zz-%e+7}L-LU^Ctn_gYXVB>qJDAjxA&Q!Sjln!l)~RaGA7vCw*tS!km%k`2)0{HfD{ zOi8VJ(m=79tf1PKSv1G3dugd-uTlbJ9v5CphwE;oF^q!l$pF9zO5#F8wq@KI{&_h>>*U}@ExK7lsLV#KG!q7zxZ%x^TQ#tJl znDT6RM-zf|_*O3xE9QrXYqwDR&ZEyW^G`2-4mfzYH~5CN#d)1}88E`ij9B3{;AgCq zTWWv&{(^io+2-UGB@!F@q7FGRhCAghmTx`JtR`2a@ zZRY^?w#L!Q%IHWIKp=Y&UAR)3OWkWMVr7pE0EWcVIh#caa$UO|>}ZyASxJ`0PVrVb zaFnRt1NzEJqv~h27$K`iOiumqfA!-wOe73~U-U=!pC)~~ ztl^g0W-zD%a~ri(Z%idzgeJ1~-A-Osu$z0|=qg! ./private_key.gpg +# Import private key +gpg --import ./private_key.gpg +# Reveal secrets +git secret reveal + +# Continue normal build pipeline +``` \ No newline at end of file diff --git a/secure/google-api.json.secret b/secure/google-api.json.secret index f67f27bd828e35d642c00614d82a934961ae680f..89b10188f434581fe1a68ef993bded1af79ad891 100644 GIT binary patch literal 2007 zcmV;|2PpW30Sg29;yQFjJFq=aRnG(~>d=y4zg9Qs`)uidCiNG7n0<5b^BS`{O(<4gX=&Y3UF+od{(n zImee8P#EzKYZldxz6Dm|$p89-Csr$_mK$0Ul?=1HmS_r}LtJez)1fV=KY(rMHP{N3GQNdLIwncdwW4hXv#=Za| zV;l{0>aqBi2v!ni0Ddvk_luIcERHcFN{V zsF9g)i=`7o_e%sLWD)BLHPY$<_=eX;)l+C+oB&2-;-7|UNr_NnDQSlG(TNu%g==>q`y8q`3fD#hH2x}q}{XjtB~KD$;;Fc0=A|>$J<~d zWeN|S7CXr#5Vzcc3A%J2pG#0y|E#umRF&(_1=8(;6)&!%v!kTZL4B0Lm>0lOhs>~! zjm66VUO;aySO$4lLbmxJydCk5(Cj=QytAyw+N^Cf>3umC#7i-imve1l!W2W8;Q$r* zgJgrwCD~1NGOHZb{<-$GouBuzFlqVrPK14D&PL*G*Mh+512d}BrU zGy3c%!tV`sqgdMD&LvFUmT4ya9Y>~0koj>Kc*aJlyX~DB(Ym?;{>VWB52!*C+oDW} zSt!v}rUwjp9^6_A;b9?k>L){OUlL)q6~#R{PiDH)FD4StVN*#=>w9bNO`x~9fRA_` z?S^_989Y4ak`H}(67xQ`ZaFufMZ_Er?p=QQNIdf#gjec7-s@6m(JYuY7;Wz0YpWE2 z0M~0eN}r9-X~^e~bJJvkNih^0lIWv}94{iS9fd*$lR1+`$2w_CN%y%g!s@STPcc8+ zzj z!QIYdk?J0YmWqEc z+R_A`=+?`L;uu$?V~7!Ayf@ga%FQdadJkqA5&F|^RNOzf+sQIsNXJXR!r;NW<5HY0 zx+BBs6loq(HE|`ph{{jTUFZKB;C_M-S*mL>H`dqo*QD-IVrK~C zIUd&H(--#EQh?-r4&q6$=zDM?ispACFt!)U4;y5LdE>L;(&5i0?E#7z%!|!8$HP-- zEx?hEw6C$gSWxV@`IovB#ZZqYuHS{*XeoU&o1z<=W})6H;b!wyRr6GDPE^ySA%K}O z4Q2e=s&|b^e1k)Lg{HvZ<7O6=W7#Um+NG0iBRICQMT%i&!;x}~hIqm5l}A_aFr8y{ z<^JCkTpWS2jwc|V_thZn3OAZ|@XQu-_#MvGE+sx-c#0LR)?t1)iS%4TBnTq^vEnG6 zMN;AHWnz2dKfx=@D6u^7SvYV2)T*6lL;@=N<*1wvZsXBUe3?1x^V~E8Qwx?y7xkBS z<>sSAlj6^9TILOH;zW^5a8xzZ3X28kj6A7;;|Kin<*-zy!fX zFMCBhQ1l;o00%-Nj6j#XY%)xPq<`|s-ve3RjN-|o95^Mbg`QKO*uXMB0)`MNiLyZL z!y63Py_RY7*wd~rA-trQe;483u(UlyN@dT87^)v=Femq~6E;Gg0mLgYXweL|N4!@; z>44JrNPD-LH=;U}@N~EJ=%&0h$Nt*99p#o|Kz_cjCy#r4&ay2-PW?p-h+gktt literal 2008 zcmV;}2PgQ20Sp8A;yQF`djawh1IP7XB0sw9ga-(R{u85amWOW>IJ+20aUvPtMh!7<|pHVR=Akk8=V3VT@ zBK(Av;;XB-`d04nc$ir^=cSqUklQccphy-wFd^7qf&+V+-c+GSh#0?YnKRFj5hsvf*5rkVGNDA`5Sx-~#Rs?b z-k#3}N=4-`5ehg&H(QB>!o4k0JG1PO7yhIrrud{*LG!vzb%1uowyCLI-jv;?34_l> z3w{@beH+P0iGhOaKR(?lB1)%=Jqj{Cn(h|ff@GhyLnqr*~2D9NgXGAZIXf86TN~%8=E6V3 zg)(aZgf4-rE{_*^C9GEDqsg(S%I|IH1!DKBfPLd_W24|hF`Kc4YT4l66@}K*+5A{+ z$wdv{)e!M$fqC00Ls3^ly6@QxIbB361i@Ch=&&hhM~RxbPrYO`pF|`_GJLbjS*^=Kp;MBF%52yC6ZMXw!+z%d3_KXt6Sh-Z^OD#`n{f{8Sya%4Ykwo z?tH(m_tw5ad9c!)Sd2}wiI-u*jD9zArk}xm=I>yPM@0*33yf*n?_<1uj7T$6pu7cG z@KbMrD?+@dd%=bIT2snG@DRKZ1%SYs+U{n9_R5g-@P*gs;E%DY@K>ZW#Xiz{)t5jI z1$is-qK2k#Q}iaha?Dt}n|vs)4Vuj2`5h6(IIvCX&I;XyDa3F`(sT+{6P}t5L!2n^ zY2`#N#hug@fpTM{#v7%wr-ixt41bJRcTv~Bi79)h-#RuhuZNE`6<7#12|c>vdE&ow z;RXHmaw3lS`1O^+Js~4y*Xml$fD^bf%ErfYA(QxUD{Lrr(pO^PIT#nPbN+r8?j6NT zc^aR+vTvA3UOeXeS;Lu`7Iq0KvMryMxH0M}MV#Ip?@+qxb9(LIMQ>uaUSeRbK7e1e zkVf4oK*~RVC{dxoi-biXD<)P~O!i4+XERp=qO*n0yYNOv!zx5S=R&^b8yg@v^w z`YCnSwU%pMEZcEZb|x~5fS=L5dIQYR&9)-UgEjxp9+3*hLvb95<9v#Cm+(s)?d*z; z$D?g7MK`a`P&NTl1$WTvv@ZiXHglx>uBcka2apn2Y~X2~*F-Mf#c}FrF^yZuVl{9j z4Irhf!g}fWJN%`*jMM%!e4E=o+t{kA0M{O91N2@%r<3*zcr`e`GY$K%Zq{5f(v}4| zctzhR>HqdoRjMVb;$ga&n!9pNsTY5AqV#H<#Qa$-vRZ=$-o{}HM}YNqCXNfd2&N=4 zYtH|LTaI{_Wlnw7wE?~=bRt$1b8V(f0C(`7>W|D|+Ss+kiylly6t%4fPiL)q_?lpq zl}ppLGLiFp*d`Ha&LW$}S2-y@IP&?g#|P*%z$9n=8Kx5v>Z_DVjl(=RZRR0Db#-pi z#sQ!uikWx`evhT@pyB*+!-{U22 zv15I~MB%8D(u>St7#!x)f0Qj6rRZnTB5E5S@2`c)C1~BKbMOCV9_cb0sO{DgQd)TrL~IfQY;CU0kHa_^ zY~+?c3UR7CAJ)1RWDnf~e}8lqiKD6$qrm+ zl^S`NOn!14{ks{3cB2cEPGys;qM*(i%6;eLt5&q${C`YyUV^QGA+}ObBa&3iN?J!A q`EeUM19Yhh2jp-uvN*LcFm!bjxxpv(6Q+lIP5cjOvEAY3YCq!rHPin9 diff --git a/secure/key.keystore.secret b/secure/key.keystore.secret index 5823e35e82980b2551783d91bf705e0e14aeaef3..01928da2bb8dca1131c8eaa81d23dc0b84b1f60d 100644 GIT binary patch literal 2420 zcmV-)35)iH0Sp8A;yQFax9mSG;0SyjOxGFjCp?*#G?)&V(JzO29BQ5GRJ-9@(2EdBh&~P zny`4&Wn;wE3Ht>B-s#kCgwEbL$Jl#%?(2|Vr(ZGoz z4@c){bJ;8AF@8Y1bx-55h?MXe6KENB2`w$T~g-dij)=MQ{XV+&*)K-GPOVVh z-DWpHfyE_oVA^*_`x*VYOVg%Ercob&2M z)IDbQ8h79*N#tD8h;sSuDxYfn;R$>IXta-h1PT?}cWyn@=$CGK%fdl}Mzqwh^vNKR zke%T3FGj$^&r(a1R3)E_d;Pv%D@Ek;NP3y#jLBhIQXoWB{95uh zKSDd+;V?ty6@mzGFo;YWPCt$NAfi5h_vZUF*qOTSO*QkCc1HagqNbY(6vMv3Q56q$bXZ6$;YmnaQ%$XDxKN(o+`CIXb(jKApdx zm80ySc;%1N(03gC;<=f^7SwL~9FYS{?eL!6S=l*9x8zgc9hY||&ZMIX4ivvL}ve&pddBBDm5(3$QrUCZ!hMVULB`mQb>u&-q^U!PVqO8d-l-|{j;7jZ3Rn( zyljNrc6RUYR@QF-0J!REe(@*pdP0>(dVnjE%En4WZY)Y882t9SbOj@h7`2)wyxByP zefNTMl~OZ`XBwm{`N9Xut+ZfSNhc?Np=@s1mQ9tga&`Q#<5*z-NV_7EkZP$a=Q<@(N4JznD?c(X;s7L)aC zi`>1ynUt-2O+w+=DZip=Q=7;DE1HmMA+eccN@nH3?4*C2R5%U1k>CTQzJas`q;6`? zh3BXLm#rYA9ujGy;UK3(j72_8W9l2Sp6nGO_8KH!unQqUHL@$YtQzID(lSIZf@cmc zSP7$UDPLu{rn+!Pq2db*IEbRqEKo}cfbw`pd!Y$reQ=$Z=Xb~OpY5{M`qdlNbE9b8 z2$YKyqxGmZ=!`J)W@4QH(0~`vgm!!!8O)L!V^U>TPq;{;n?8z-G@)-7K;cr}=5ou< zv?=dhOoHN7x1yKO&i+UoaX z>=k)%u*K2!z2c}wQc+`K1}}<<*#Kq6Lygv8#T6bfUzd@yw-h8BOe1U0@v-26W`3U$ z*+RBdjj0S3C-Ud1!8}9sl~;||D`cS0tP%(ua6gS8UofRL0593xj*m<-inFo4_Zg`D`U|1_to`* z=mL-hII`082>JYKW5mL^A>5Lfdj*~y2x2LY5xL%>v#zVx(>lc?ReHmsRH3vUd0#jb>@P%O)Sm3oUNjk-Ngo`u_TOYWP0cv zqnS^UEHUnyD7LC=JVFj%ds#Rr9;|Be-J%86^;vK{3-w48P*O=BNkyBOJV}*%0?r30 zXx6)t{wQ=^JR%Xm_s(#K@>*mkFwf;SZd08fl)z#Vme4CHfvpq>gGAAeFr=lUwagJb z7n9%qO7bH3eW*_m-h8I#?a0}Q#S4r4rC?;_J+nu57XahLzdUQNSM$?E?N&a|UUD4rG zAXiA~47$^iEkE@02Hz)~uUbRmoaGHz(m>zbHP;t|q>E1RVvaXi0i;FSq*I8y zd^qFeSevQJqS~*+{IWboYP!@$E3|JUPv<)Hj4qFMMZGpMQ6D1D!INP##~-kCKg9HM m!TM0k-bvT2gEa69`n!OvaBwKcIe#?qTMllVaaJk8to5B(wZcIF literal 2420 zcmV-)35)iH0Sp8A;yQFNFU# z6LdYbqeWdu{@ahv5?fQAdTs4dN?Q=1(&MJz zxX@9WUu6 zZEt3ZR2Tv0K`8}5Y)L>(ZB6sRt9%#LEy_Z%V{EH5x8jR~&uDtM(Dq9kOE_$5TP)69 zgPPQ?1tscT&t#G;cgagA0FM~GjQW8VfV_cUR)c~qXAoaG4yrWq+w z-!1$>KH@wytnQ>-+XVIOqp|Sotpy?eFR1W5)^gTT?QVzmemV;QK-s^Xja~^@N=TLA z3f_%1f|R7g54Zp@R;9}|a2ZzwPY>Nw&Z02A3stE9fHA2!l?@j|Gfxw94r!(joKQYR zcoq(4i6dFfK-B>@$GqNj9LFZ1hoBB$N&93owAVdzg`jr{k2;AUC5W=NB}=QZ5FScU zQ`}M%sKI^Zf?2~kmHJ2!_DNmloYG!ha~IBg5Ct;?mCTLFs4;dZFKPD!OgRyw#{q0+ z{Lb{Qb@}Vqh(Z}KkN!~wW`PXYwsO6z6Q%5nBW9Mr~Z++&} zJor>lKI&BWO>=1R?eI%Mv(fWa3t%b09x=%tsfB%#6cC{w zaPDZzb~LD(sE>ksax8lvf!mwEl9DG_|1QlTcKu`&-kU;IpR=cS@9++2grCZ?+LH*p zlcyEJW_6ZV;%8n=Yu*n~$;yT}!;aBUPIhxN1%k>c)@FVV#VYfDD;L$F5+Oy_9KFJ0 zhVU23$rmGh2siNERpF{LrlR2qxm1>YzUiK!i*_8jJkitmxqL}+w`#=Bvlj*UqzNA3>o|4g(i3d-~=4@{yv1U>z@7ALQ7D)%zp9uc%O$f|9 z{)W4X_k+rRa=GX*N3dKOH3q-ps?0ima)#xf%BDFQ9gz{DvSo*ChDcoltm!-q%H;t0UQ0U&s4YCsP3Q*FR*G%vr!I9XGLSw6YQieFn`HrfN>~sToe+ z7Mlk;WAR$A!A~e|)K5l9zwPJS(>HL&6*|Va3;&>Gm=ATpZA)q;9ZMs!)t4I*%|c#i z0KTr&0BalbWTjTU~qivV+><#r?~ z-(f@H6laHj*g;h=~7& zDHG3o0(`N>>D41C%~=F6e4k;+Pfq8LpsX1A#HZ6r0OQHG|}m&by%MIu-AoO z7R-tNs4WKgN4?bf=6O+KHp_<)Wrn5$7NwkV;nhRQ@;_Gw`a$1yGGAO*FJZ*t>n8C_`Aw<%+BKAl<4!819&f(~G;wm<)&5|TQMIRV*|8c4BBm9CJ z5w_ms_*S~|7Usrrb1k=CzlQ3yM*qCIFYr_Ob+pCLwBzr(DEhc*sWjJB+MXBrO>PwX zEE_NO!TI7%7yncuLHR`??0L(B!BrdxB9#9;Gs`l)stEbM67f7SXj1Y_(sc8{tmHH` z&LA40{pf-E4TE?G@6p>5KhZDubIdtjn9t*UIS?HHtEy18#{coSV#89Kx_7sf5$3vc zBrDeNR}t&SQ%8(_uIMEM^Zc?AM0ijT`f@^6+tgf237uG~U*)9TID&4tQZ#iqv-LU% z+GWw2jvQXx9IYn^d*fWHb9nc-k&S8aGm`R$Qjl`GYH<2n2U{5{(yGR}w6r(K%2unx@ zZ++G9jr2)B6UrVW`n9J_qaG-DrJv@HE|4mQ%wx4kR1-95Ds{!*TJ157{ePODX8_K> zw311&N<)2}&VdT_Q(>9+t`K0m5pS{08#9e!fdhuI-mxe}%flXP;aO4)zj}AMW@EpF z&I}|&+9OLYN8OCYxuxC8c`bk5ws!jL34P%rajnbVzgBEWLy|1jVjIZb#d5WJ2ZtIo zn&hjj?$i?Q2E0s)^imkOes*2^E`9lK&f!Y?SgL{f2TAqGIP0l1pKoUbL-d-q_q9{D mkYI2}w#~{Xv%ezPXqLkPO)_vV_h-@0>ES%ThSur%pX;U Date: Fri, 3 May 2019 16:12:43 -0300 Subject: [PATCH 05/66] Initial commit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a053190..dcfab20 100644 --- a/README.md +++ b/README.md @@ -78,3 +78,4 @@ NOTE: Remove the free LICENSE file for private projects or replace it with the c **Android Base** is maintained by [Rootstrap](http://www.rootstrap.com) with the help of our [contributors](https://github.com/rootstrap/android-base/contributors). [](http://www.rootstrap.com) + From a4df0a270d02b92efb1f1d85a7f64ae1019ff3d2 Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Wed, 15 Jul 2020 16:13:34 -0300 Subject: [PATCH 06/66] Viewmodel comunication with views --- .../ui/activity/main/ProfileActivity.kt | 34 +++++++++++----- .../activity/main/ProfileActivityViewModel.kt | 38 ++++++++++++------ .../ui/activity/main/SignInActivity.kt | 34 +++++++++++----- .../activity/main/SignInActivityViewModel.kt | 37 ++++++++++++------ .../ui/activity/main/SignUpActivity.kt | 34 +++++++++++----- .../activity/main/SignUpActivityViewModel.kt | 39 +++++++++++++------ .../android/ui/base/BaseViewModel.kt | 25 ++++++++---- .../android/util/ViewModelsHelper.kt | 12 ++++++ app/src/main/res/values/strings.xml | 1 + 9 files changed, 183 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt index 30a0865..446c79c 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt @@ -9,6 +9,8 @@ import com.rootstrap.android.metrics.VISIT_PROFILE import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.ProfileView +import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.ViewModelDelegate import kotlinx.android.synthetic.main.activity_profile.* class ProfileActivity : BaseActivity(), ProfileView { @@ -19,7 +21,7 @@ class ProfileActivity : BaseActivity(), ProfileView { super.onCreate(savedInstanceState) setContentView(R.layout.activity_profile) - val factory = ProfileActivityViewModelFactory(this) + val factory = ProfileActivityViewModelFactory(viewModelDelegate) viewModel = ViewModelProviders.of(this, factory) .get(ProfileActivityViewModel::class.java) @@ -27,19 +29,31 @@ class ProfileActivity : BaseActivity(), ProfileView { welcome_text_view.text = getString(R.string.welcome_message, SessionManager.user?.firstName) sign_out_button.setOnClickListener { viewModel.signOut() } - } - - override fun onResume() { - super.onResume() - viewModel.register() - } - override fun onPause() { - super.onPause() - viewModel.unregister() + lifecycle.addObserver(viewModel) } override fun goToFirstScreen() { startActivityClearTask(SignUpActivity()) } + + // delegate + private val viewModelDelegate = object : ViewModelDelegate { + override fun updateState() { + when (viewModel.state) { + ProfileState.signOutFailure -> showError(viewModel.error) + ProfileState.signedOutSuccessfully -> goToFirstScreen() + else -> { + } + } + } + + override fun updateNetworkState() { + when (viewModel.networkState) { + NetworkState.loading -> showProgress() + NetworkState.idle -> hideProgress() + else -> showError(viewModel.error ?: getString(R.string.default_error)) + } + } + } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index 6ca6722..40e90b8 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -4,41 +4,57 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.ui.base.BaseViewModel -import com.rootstrap.android.ui.view.ProfileView +import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.ViewModelDelegate import com.rootstrap.android.util.extensions.ErrorEvent import com.rootstrap.android.util.extensions.FailureEvent import com.squareup.otto.Subscribe -open class ProfileActivityViewModel(var view: ProfileView) : BaseViewModel(view) { +open class ProfileActivityViewModel(delegate: ViewModelDelegate?) : BaseViewModel(delegate) { private val manager = UserManager fun signOut() { - view.showProgress() + networkState = NetworkState.loading manager.signOut() } + var state: ProfileState = ProfileState.none + set(value) { + field = value + delegate?.updateState() + } + @Subscribe fun signedOutSuccessfully(event: UserManager.SignedOutSuccessfullyEvent) { - view.hideProgress() - view.goToFirstScreen() + networkState = NetworkState.idle + state = ProfileState.signedOutSuccessfully } @Subscribe fun signOutError(event: ErrorEvent) { - view.hideProgress() - view.showError(event.error) + error = event.error + networkState = NetworkState.idle + networkState = NetworkState.error } @Subscribe fun signOutFailure(event: FailureEvent) { - view.hideProgress() - view.showError(null) + error = null + networkState = NetworkState.idle + state = ProfileState.signOutFailure } } -class ProfileActivityViewModelFactory(var view: ProfileView) : ViewModelProvider.Factory { +enum class ProfileState { + signOutFailure, + signedOutSuccessfully, + none, +} + +class ProfileActivityViewModelFactory(var delegate: ViewModelDelegate?) : + ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return ProfileActivityViewModel(view) as T + return ProfileActivityViewModel(delegate) as T } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index f502642..928856e 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -9,6 +9,8 @@ import com.rootstrap.android.metrics.VISIT_SIGN_IN import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.AuthView +import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.ViewModelDelegate import com.rootstrap.android.util.extensions.value import kotlinx.android.synthetic.main.activity_sign_in.* @@ -21,21 +23,13 @@ class SignInActivity : BaseActivity(), AuthView { setContentView(R.layout.activity_sign_in) Analytics.track(PageEvents.visit(VISIT_SIGN_IN)) - val factory = SignInActivityViewModelFactory(this) + val factory = SignInActivityViewModelFactory(viewModelDelegate) viewModel = ViewModelProviders.of(this, factory) .get(SignInActivityViewModel::class.java) sign_in_button.setOnClickListener { signIn() } - } - - override fun onResume() { - super.onResume() - viewModel.register() - } - override fun onPause() { - super.onPause() - viewModel.unregister() + lifecycle.addObserver(viewModel) } override fun showProfile() { @@ -49,4 +43,24 @@ class SignInActivity : BaseActivity(), AuthView { ) viewModel.signIn(user) } + + // delegate + private val viewModelDelegate = object : ViewModelDelegate { + override fun updateState() { + when (viewModel.state) { + SignInState.signedInFailure -> showError(viewModel.error) + SignInState.signedInSuccess -> showProfile() + else -> { + } + } + } + + override fun updateNetworkState() { + when (viewModel.networkState) { + NetworkState.loading -> showProgress() + NetworkState.idle -> hideProgress() + else -> showError(viewModel.error ?: getString(R.string.default_error)) + } + } + } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index ca525f7..0b4fbc1 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -5,41 +5,56 @@ import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel -import com.rootstrap.android.ui.view.AuthView +import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.ViewModelDelegate import com.rootstrap.android.util.extensions.ErrorEvent import com.rootstrap.android.util.extensions.FailureEvent import com.squareup.otto.Subscribe -open class SignInActivityViewModel(var view: AuthView) : BaseViewModel(view) { +open class SignInActivityViewModel(delegate: ViewModelDelegate?) : BaseViewModel(delegate) { private val manager = UserManager + var state: SignInState = SignInState.none + set(value) { + field = value + delegate?.updateState() + } + fun signIn(user: User) { - view.showProgress() + networkState = NetworkState.loading manager.signIn(user) } @Subscribe fun signedInSuccessfully(event: UserManager.SignInSuccessfullyEvent) { - view.hideProgress() - view.showProfile() + networkState = NetworkState.idle + state = SignInState.signedInSuccess } @Subscribe fun signedInError(event: ErrorEvent) { - view.hideProgress() - view.showError(event.error) + error = event.error + networkState = NetworkState.idle + networkState = NetworkState.error } @Subscribe fun signedInFailure(event: FailureEvent) { - view.hideProgress() - view.showError(null) + error = null + networkState = NetworkState.idle + state = SignInState.signedInFailure } } -class SignInActivityViewModelFactory(var view: AuthView) : ViewModelProvider.Factory { +enum class SignInState { + signedInFailure, + signedInSuccess, + none, +} + +class SignInActivityViewModelFactory(var delegate: ViewModelDelegate?) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return SignInActivityViewModel(view) as T + return SignInActivityViewModel(delegate) as T } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index 1e38f4a..4484edf 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -10,6 +10,8 @@ import com.rootstrap.android.metrics.VISIT_SIGN_UP import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.AuthView +import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.ViewModelDelegate import com.rootstrap.android.util.extensions.value import kotlinx.android.synthetic.main.activity_sign_up.* @@ -22,22 +24,14 @@ class SignUpActivity : BaseActivity(), AuthView { setContentView(R.layout.activity_sign_up) Analytics.track(PageEvents.visit(VISIT_SIGN_UP)) - val factory = SignUpActivityViewModelFactory(this) + val factory = SignUpActivityViewModelFactory(viewModelDelegate) viewModel = ViewModelProviders.of(this, factory) .get(SignUpActivityViewModel::class.java) sign_up_button.setOnClickListener { signUp() } sign_in_text_view.setOnClickListener { startActivity(Intent(this, SignInActivity::class.java)) } - } - - override fun onResume() { - super.onResume() - viewModel.register() - } - override fun onPause() { - super.onPause() - viewModel.unregister() + lifecycle.addObserver(viewModel) } override fun showProfile() { @@ -53,4 +47,24 @@ class SignUpActivity : BaseActivity(), AuthView { ) viewModel.signUp(user) } + + // delegate + private val viewModelDelegate = object : ViewModelDelegate { + override fun updateState() { + when (viewModel.state) { + SignUpState.signedUpFailure -> showError(viewModel.error) + SignUpState.signedUpSuccess -> showProfile() + else -> { + } + } + } + + override fun updateNetworkState() { + when (viewModel.networkState) { + NetworkState.loading -> showProgress() + NetworkState.idle -> hideProgress() + else -> showError(viewModel.error ?: getString(R.string.default_error)) + } + } + } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index dc4b01d..e5576fb 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -5,41 +5,58 @@ import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel -import com.rootstrap.android.ui.view.AuthView +import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.ViewModelDelegate import com.rootstrap.android.util.extensions.ErrorEvent import com.rootstrap.android.util.extensions.FailureEvent import com.squareup.otto.Subscribe -open class SignUpActivityViewModel(var view: AuthView) : BaseViewModel(view) { +open class SignUpActivityViewModel( + delegate: ViewModelDelegate? +) : BaseViewModel(delegate) { private val manager = UserManager + var state: SignUpState = SignUpState.none + set(value) { + field = value + delegate?.updateState() + } + fun signUp(user: User) { - view.showProgress() + networkState = NetworkState.loading manager.signUp(user) } @Subscribe fun signedUpSuccessfully(event: UserManager.UserCreatedSuccessfullyEvent) { - view.hideProgress() - view.showProfile() + networkState = NetworkState.idle + state = SignUpState.signedUpSuccess } @Subscribe fun signedUpError(event: ErrorEvent) { - view.hideProgress() - view.showError(event.error) + error = event.error + networkState = NetworkState.idle + networkState = NetworkState.error } @Subscribe fun signedUpFailure(event: FailureEvent) { - view.hideProgress() - view.showError(null) + error = null + networkState = NetworkState.idle + state = SignUpState.signedUpFailure } } -class SignUpActivityViewModelFactory(var view: AuthView) : ViewModelProvider.Factory { +enum class SignUpState { + signedUpFailure, + signedUpSuccess, + none, +} + +class SignUpActivityViewModelFactory(var delegate: ViewModelDelegate?) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return SignUpActivityViewModel(view) as T + return SignUpActivityViewModel(delegate) as T } } diff --git a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt index a28872d..0f3571a 100644 --- a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt @@ -1,20 +1,29 @@ package com.rootstrap.android.ui.base +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ViewModel import com.rootstrap.android.bus +import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.ViewModelDelegate /** * A [ViewModel] base class * implement app general LiveData as Session or User * **/ -open class BaseViewModel(open var v: BaseView?) : ViewModel() { +open class BaseViewModel(var delegate: ViewModelDelegate?) : ViewModel(), LifecycleObserver { + var error: String? = null - fun register() { - bus.register(this) - } + var networkState: NetworkState = NetworkState.idle + set(value) { + field = value + delegate?.updateNetworkState() + } - fun unregister() { - v = null - bus.unregister(this) - } + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun register() = bus.register(this) + + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + fun unregister() = bus.unregister(this) } diff --git a/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt b/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt new file mode 100644 index 0000000..3fdc9d2 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt @@ -0,0 +1,12 @@ +package com.rootstrap.android.util + +interface ViewModelDelegate { + fun updateState() + fun updateNetworkState() +} + +enum class NetworkState { + loading, + idle, + error +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2b5fd99..d3de60d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,4 +25,5 @@ Hi %1$s! Sign Out + Opps!!!, Network error!!! From 596736d88ca626da268100c689797e75c80c840d Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Wed, 15 Jul 2020 16:28:58 -0300 Subject: [PATCH 07/66] Fix comment --- .../android/ui/activity/main/SignUpActivityViewModel.kt | 4 +--- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index e5576fb..14e1152 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -11,9 +11,7 @@ import com.rootstrap.android.util.extensions.ErrorEvent import com.rootstrap.android.util.extensions.FailureEvent import com.squareup.otto.Subscribe -open class SignUpActivityViewModel( - delegate: ViewModelDelegate? -) : BaseViewModel(delegate) { +open class SignUpActivityViewModel(delegate: ViewModelDelegate?) : BaseViewModel(delegate) { private val manager = UserManager diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3de60d..d4e82f2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,5 +25,5 @@ Hi %1$s! Sign Out - Opps!!!, Network error!!! + Oops! Network error From 0d7448996574b73c87384157ee091048dc3627ec Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Wed, 15 Jul 2020 16:42:03 -0300 Subject: [PATCH 08/66] Change delegate name to listener --- .../android/ui/activity/main/ProfileActivity.kt | 8 ++++---- .../ui/activity/main/ProfileActivityViewModel.kt | 10 +++++----- .../android/ui/activity/main/SignInActivity.kt | 8 ++++---- .../ui/activity/main/SignInActivityViewModel.kt | 10 +++++----- .../android/ui/activity/main/SignUpActivity.kt | 8 ++++---- .../ui/activity/main/SignUpActivityViewModel.kt | 10 +++++----- .../com/rootstrap/android/ui/base/BaseViewModel.kt | 6 +++--- .../com/rootstrap/android/util/ViewModelsHelper.kt | 2 +- 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt index 446c79c..0f8403f 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt @@ -10,7 +10,7 @@ import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.ProfileView import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelDelegate +import com.rootstrap.android.util.ViewModelListener import kotlinx.android.synthetic.main.activity_profile.* class ProfileActivity : BaseActivity(), ProfileView { @@ -21,7 +21,7 @@ class ProfileActivity : BaseActivity(), ProfileView { super.onCreate(savedInstanceState) setContentView(R.layout.activity_profile) - val factory = ProfileActivityViewModelFactory(viewModelDelegate) + val factory = ProfileActivityViewModelFactory(viewModelListener) viewModel = ViewModelProviders.of(this, factory) .get(ProfileActivityViewModel::class.java) @@ -37,8 +37,8 @@ class ProfileActivity : BaseActivity(), ProfileView { startActivityClearTask(SignUpActivity()) } - // delegate - private val viewModelDelegate = object : ViewModelDelegate { + // ViewModelListener + private val viewModelListener = object : ViewModelListener { override fun updateState() { when (viewModel.state) { ProfileState.signOutFailure -> showError(viewModel.error) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index 40e90b8..d2a8995 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -5,12 +5,12 @@ import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelDelegate +import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.ErrorEvent import com.rootstrap.android.util.extensions.FailureEvent import com.squareup.otto.Subscribe -open class ProfileActivityViewModel(delegate: ViewModelDelegate?) : BaseViewModel(delegate) { +open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { private val manager = UserManager @@ -22,7 +22,7 @@ open class ProfileActivityViewModel(delegate: ViewModelDelegate?) : BaseViewMode var state: ProfileState = ProfileState.none set(value) { field = value - delegate?.updateState() + listener?.updateState() } @Subscribe @@ -52,9 +52,9 @@ enum class ProfileState { none, } -class ProfileActivityViewModelFactory(var delegate: ViewModelDelegate?) : +class ProfileActivityViewModelFactory(var listener: ViewModelListener?) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return ProfileActivityViewModel(delegate) as T + return ProfileActivityViewModel(listener) as T } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index 928856e..7dad0d8 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -10,7 +10,7 @@ import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelDelegate +import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.value import kotlinx.android.synthetic.main.activity_sign_in.* @@ -23,7 +23,7 @@ class SignInActivity : BaseActivity(), AuthView { setContentView(R.layout.activity_sign_in) Analytics.track(PageEvents.visit(VISIT_SIGN_IN)) - val factory = SignInActivityViewModelFactory(viewModelDelegate) + val factory = SignInActivityViewModelFactory(viewModelListener) viewModel = ViewModelProviders.of(this, factory) .get(SignInActivityViewModel::class.java) @@ -44,8 +44,8 @@ class SignInActivity : BaseActivity(), AuthView { viewModel.signIn(user) } - // delegate - private val viewModelDelegate = object : ViewModelDelegate { + // ViewModelListener + private val viewModelListener = object : ViewModelListener { override fun updateState() { when (viewModel.state) { SignInState.signedInFailure -> showError(viewModel.error) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index 0b4fbc1..3ea6f64 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -6,19 +6,19 @@ import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelDelegate +import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.ErrorEvent import com.rootstrap.android.util.extensions.FailureEvent import com.squareup.otto.Subscribe -open class SignInActivityViewModel(delegate: ViewModelDelegate?) : BaseViewModel(delegate) { +open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { private val manager = UserManager var state: SignInState = SignInState.none set(value) { field = value - delegate?.updateState() + listener?.updateState() } fun signIn(user: User) { @@ -53,8 +53,8 @@ enum class SignInState { none, } -class SignInActivityViewModelFactory(var delegate: ViewModelDelegate?) : ViewModelProvider.Factory { +class SignInActivityViewModelFactory(var listener: ViewModelListener?) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return SignInActivityViewModel(delegate) as T + return SignInActivityViewModel(listener) as T } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index 4484edf..627bec9 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -11,7 +11,7 @@ import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelDelegate +import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.value import kotlinx.android.synthetic.main.activity_sign_up.* @@ -24,7 +24,7 @@ class SignUpActivity : BaseActivity(), AuthView { setContentView(R.layout.activity_sign_up) Analytics.track(PageEvents.visit(VISIT_SIGN_UP)) - val factory = SignUpActivityViewModelFactory(viewModelDelegate) + val factory = SignUpActivityViewModelFactory(viewModelListener) viewModel = ViewModelProviders.of(this, factory) .get(SignUpActivityViewModel::class.java) @@ -48,8 +48,8 @@ class SignUpActivity : BaseActivity(), AuthView { viewModel.signUp(user) } - // delegate - private val viewModelDelegate = object : ViewModelDelegate { + // ViewModelListener + private val viewModelListener = object : ViewModelListener { override fun updateState() { when (viewModel.state) { SignUpState.signedUpFailure -> showError(viewModel.error) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index 14e1152..9545e63 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -6,19 +6,19 @@ import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelDelegate +import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.ErrorEvent import com.rootstrap.android.util.extensions.FailureEvent import com.squareup.otto.Subscribe -open class SignUpActivityViewModel(delegate: ViewModelDelegate?) : BaseViewModel(delegate) { +open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { private val manager = UserManager var state: SignUpState = SignUpState.none set(value) { field = value - delegate?.updateState() + listener?.updateState() } fun signUp(user: User) { @@ -53,8 +53,8 @@ enum class SignUpState { none, } -class SignUpActivityViewModelFactory(var delegate: ViewModelDelegate?) : ViewModelProvider.Factory { +class SignUpActivityViewModelFactory(var listener: ViewModelListener?) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return SignUpActivityViewModel(delegate) as T + return SignUpActivityViewModel(listener) as T } } diff --git a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt index 0f3571a..205b51a 100644 --- a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt @@ -6,19 +6,19 @@ import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ViewModel import com.rootstrap.android.bus import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelDelegate +import com.rootstrap.android.util.ViewModelListener /** * A [ViewModel] base class * implement app general LiveData as Session or User * **/ -open class BaseViewModel(var delegate: ViewModelDelegate?) : ViewModel(), LifecycleObserver { +open class BaseViewModel(var listener: ViewModelListener?) : ViewModel(), LifecycleObserver { var error: String? = null var networkState: NetworkState = NetworkState.idle set(value) { field = value - delegate?.updateNetworkState() + listener?.updateNetworkState() } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) diff --git a/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt b/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt index 3fdc9d2..eed1eb1 100644 --- a/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt +++ b/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt @@ -1,6 +1,6 @@ package com.rootstrap.android.util -interface ViewModelDelegate { +interface ViewModelListener { fun updateState() fun updateNetworkState() } From f2ff08344bb38264b825e3df26cf213cb956a8d6 Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Tue, 11 Aug 2020 15:00:12 -0300 Subject: [PATCH 09/66] Adding permissions manager --- app/src/main/AndroidManifest.xml | 1 + .../ui/activity/main/SignInActivity.kt | 24 ++++++- .../ui/fragment/sample/SampleFragment.kt | 42 ----------- .../ui/fragment/sample/SampleViewModel.kt | 15 ---- .../util/permissions/PermissionActivity.kt | 70 +++++++++++++++++++ .../util/permissions/PermissionFragment.kt | 70 +++++++++++++++++++ .../util/permissions/PermissionManager.kt | 37 ++++++++++ 7 files changed, 200 insertions(+), 59 deletions(-) delete mode 100644 app/src/main/java/com/rootstrap/android/ui/fragment/sample/SampleFragment.kt delete mode 100644 app/src/main/java/com/rootstrap/android/ui/fragment/sample/SampleViewModel.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/permissions/PermissionActivity.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/permissions/PermissionFragment.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9c22035..b16d794 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.rootstrap.android"> + create(modelClass: Class): T { - return Sample1ViewModel(view) as T - } -} diff --git a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionActivity.kt b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionActivity.kt new file mode 100644 index 0000000..9039896 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionActivity.kt @@ -0,0 +1,70 @@ +package com.rootstrap.android.util.permissions + +import android.content.pm.PackageManager +import androidx.core.app.ActivityCompat +import com.rootstrap.android.ui.base.BaseActivity + +open class PermissionActivity : BaseActivity() { + + private var permissionListener: PermissionResponse? = null + + private fun requestPermission(permissions: Array) { + val notGrantedPermissions = this.checkNoGrantedPermissions(permissions) + + when { + notGrantedPermissions.isEmpty() -> permissionListener?.granted() + else -> ActivityCompat.requestPermissions( + this, + notGrantedPermissions.toTypedArray(), + REQUEST_PERMISSION_REQUEST_CODE + ) + } + } + + fun checkPermission(permission: String, listener: PermissionResponse) { + permissionListener = listener + requestPermission(arrayOf(permission)) + } + + fun checkPermissions(permissions: Array, listener: PermissionResponse) { + permissionListener = listener + requestPermission(permissions) + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + permissionListener?.let { listener -> + if (requestCode != REQUEST_PERMISSION_REQUEST_CODE) { + return + } + + val permissionDenied = mutableListOf() + + for (i in grantResults.indices) { + if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { + permissionDenied.add(permissions[i]) + } + } + + val granted = permissionDenied.size == 0 + + when { + granted -> listener.granted() + else -> { + for (s in permissionDenied) { + if (!ActivityCompat.shouldShowRequestPermissionRationale(this, s)) { + listener.foreverDenied() + return + } + } + listener.denied() + } + } + } + + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } +} diff --git a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionFragment.kt b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionFragment.kt new file mode 100644 index 0000000..f37014b --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionFragment.kt @@ -0,0 +1,70 @@ +package com.rootstrap.android.util.permissions + +import android.content.pm.PackageManager +import com.rootstrap.android.ui.base.BaseFragment + +open class PermissionFragment : BaseFragment() { + + private var permissionListener: PermissionResponse? = null + + private fun requestPermission(permissions: Array) { + activity?.let { activityContext -> + val notGrantedPermissions = activityContext.checkNoGrantedPermissions(permissions) + + when { + notGrantedPermissions.isEmpty() -> permissionListener?.granted() + else -> requestPermissions( + notGrantedPermissions.toTypedArray(), + REQUEST_PERMISSION_REQUEST_CODE + ) + } + } + } + + fun checkPermission(permission: String, listener: PermissionResponse) { + permissionListener = listener + requestPermission(arrayOf(permission)) + } + + fun checkPermissions(permissions: Array, listener: PermissionResponse) { + permissionListener = listener + requestPermission(permissions) + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + permissionListener?.let { listener -> + if (requestCode != REQUEST_PERMISSION_REQUEST_CODE) { + return + } + + val permissionDenied = mutableListOf() + + for (i in grantResults.indices) { + if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { + permissionDenied.add(permissions[i]) + } + } + + val granted = permissionDenied.size == 0 + + when { + granted -> listener.granted() + else -> { + for (s in permissionDenied) { + if (!shouldShowRequestPermissionRationale(s)) { + listener.foreverDenied() + return + } + } + listener.denied() + } + } + } + + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } +} diff --git a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt new file mode 100644 index 0000000..550c5af --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt @@ -0,0 +1,37 @@ +package com.rootstrap.android.util.permissions + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.provider.Settings +import androidx.core.content.ContextCompat.checkSelfPermission + +interface PermissionResponse { + fun granted() + fun denied() + fun foreverDenied() +} + +val REQUEST_PERMISSION_REQUEST_CODE = 999 + +fun Context.checkPermission(permission: String): Boolean = + Build.VERSION.SDK_INT < Build.VERSION_CODES.M || checkSelfPermission( + this, + permission + ) == PackageManager.PERMISSION_GRANTED + +fun Context.checkNoGrantedPermissions(permission: Array): List = + permission.filter { !checkPermission(it) } + +/** + * Use this extension to open the app details to grant permission manually + * in case that the user denied the permission all the time + * **/ +fun Context.openAppSettings() = + startActivity( + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).also { + it.data = Uri.parse("package:" + this.packageName) + } + ) From 2989c578b2b4a87a7860ac7ea8506e90c8538445 Mon Sep 17 00:00:00 2001 From: Amaury Date: Thu, 13 Aug 2020 10:35:27 -0300 Subject: [PATCH 10/66] Update app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mathías Cabano --- .../com/rootstrap/android/util/permissions/PermissionManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt index 550c5af..2fdb4c7 100644 --- a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt +++ b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt @@ -22,7 +22,7 @@ fun Context.checkPermission(permission: String): Boolean = permission ) == PackageManager.PERMISSION_GRANTED -fun Context.checkNoGrantedPermissions(permission: Array): List = +fun Context.checkNotGrantedPermissions(permissions: Array): List = permission.filter { !checkPermission(it) } /** From c2071d47526bc153a364c8ed1aa433dd5480503a Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Thu, 13 Aug 2020 12:56:40 -0300 Subject: [PATCH 11/66] Fix comments --- .../ui/activity/main/SignInActivity.kt | 2 +- .../util/permissions/PermissionActivity.kt | 27 ++++++------------- .../util/permissions/PermissionFragment.kt | 27 ++++++------------- .../util/permissions/PermissionManager.kt | 2 +- 4 files changed, 18 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index 829035d..be29db0 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -69,7 +69,7 @@ class SignInActivity : PermissionActivity(), AuthView { } fun sampleAskForPermission() { - checkPermission(Manifest.permission.CAMERA, object : PermissionResponse { + requestPermission(arrayOf(Manifest.permission.CAMERA), object : PermissionResponse { override fun granted() { // TODO.. } diff --git a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionActivity.kt b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionActivity.kt index 9039896..9ae9aa1 100644 --- a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionActivity.kt +++ b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionActivity.kt @@ -8,8 +8,9 @@ open class PermissionActivity : BaseActivity() { private var permissionListener: PermissionResponse? = null - private fun requestPermission(permissions: Array) { - val notGrantedPermissions = this.checkNoGrantedPermissions(permissions) + fun requestPermission(permissions: Array, listener: PermissionResponse) { + permissionListener = listener + val notGrantedPermissions = this.checkNotGrantedPermissions(permissions) when { notGrantedPermissions.isEmpty() -> permissionListener?.granted() @@ -21,16 +22,6 @@ open class PermissionActivity : BaseActivity() { } } - fun checkPermission(permission: String, listener: PermissionResponse) { - permissionListener = listener - requestPermission(arrayOf(permission)) - } - - fun checkPermissions(permissions: Array, listener: PermissionResponse) { - permissionListener = listener - requestPermission(permissions) - } - override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, @@ -41,21 +32,19 @@ open class PermissionActivity : BaseActivity() { return } - val permissionDenied = mutableListOf() + val deniedPermissions = mutableListOf() for (i in grantResults.indices) { if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { - permissionDenied.add(permissions[i]) + deniedPermissions.add(permissions[i]) } } - val granted = permissionDenied.size == 0 - when { - granted -> listener.granted() + deniedPermissions.isEmpty() -> listener.granted() else -> { - for (s in permissionDenied) { - if (!ActivityCompat.shouldShowRequestPermissionRationale(this, s)) { + for (deniedPermission in deniedPermissions) { + if (!ActivityCompat.shouldShowRequestPermissionRationale(this, deniedPermission)) { listener.foreverDenied() return } diff --git a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionFragment.kt b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionFragment.kt index f37014b..baa5671 100644 --- a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionFragment.kt +++ b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionFragment.kt @@ -7,9 +7,10 @@ open class PermissionFragment : BaseFragment() { private var permissionListener: PermissionResponse? = null - private fun requestPermission(permissions: Array) { + private fun requestPermission(permissions: Array, listener: PermissionResponse) { + permissionListener = listener activity?.let { activityContext -> - val notGrantedPermissions = activityContext.checkNoGrantedPermissions(permissions) + val notGrantedPermissions = activityContext.checkNotGrantedPermissions(permissions) when { notGrantedPermissions.isEmpty() -> permissionListener?.granted() @@ -21,16 +22,6 @@ open class PermissionFragment : BaseFragment() { } } - fun checkPermission(permission: String, listener: PermissionResponse) { - permissionListener = listener - requestPermission(arrayOf(permission)) - } - - fun checkPermissions(permissions: Array, listener: PermissionResponse) { - permissionListener = listener - requestPermission(permissions) - } - override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, @@ -41,21 +32,19 @@ open class PermissionFragment : BaseFragment() { return } - val permissionDenied = mutableListOf() + val deniedPermissions = mutableListOf() for (i in grantResults.indices) { if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { - permissionDenied.add(permissions[i]) + deniedPermissions.add(permissions[i]) } } - val granted = permissionDenied.size == 0 - when { - granted -> listener.granted() + deniedPermissions.isEmpty() -> listener.granted() else -> { - for (s in permissionDenied) { - if (!shouldShowRequestPermissionRationale(s)) { + for (deniedPermission in deniedPermissions) { + if (!shouldShowRequestPermissionRationale(deniedPermission)) { listener.foreverDenied() return } diff --git a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt index 2fdb4c7..88800e8 100644 --- a/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt +++ b/app/src/main/java/com/rootstrap/android/util/permissions/PermissionManager.kt @@ -23,7 +23,7 @@ fun Context.checkPermission(permission: String): Boolean = ) == PackageManager.PERMISSION_GRANTED fun Context.checkNotGrantedPermissions(permissions: Array): List = - permission.filter { !checkPermission(it) } + permissions.filter { !checkPermission(it) } /** * Use this extension to open the app details to grant permission manually From b64b5e90d067eafe796dbbdc732d69ff5a31906d Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Wed, 16 Dec 2020 15:23:18 -0300 Subject: [PATCH 12/66] update apicall with coroutines --- .idea/gradle.xml | 1 + .idea/misc.xml | 2 +- app/build.gradle | 37 ++++++---- .../android/network/managers/UserManager.kt | 58 ++------------- .../network/providers/ServiceProvider.kt | 5 ++ .../android/network/services/ApiService.kt | 6 +- .../activity/main/SignUpActivityViewModel.kt | 37 ++++------ .../android/util/extensions/ActionCallback.kt | 71 +++++++++++-------- 8 files changed, 94 insertions(+), 123 deletions(-) diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 674414f..41871c2 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -15,6 +15,7 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index 703e5d4..3378229 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,7 +5,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index df2d3b4..b03ae34 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -83,6 +83,10 @@ android { ktlint } + kotlinOptions { + freeCompilerArgs = ["-Xallow-result-return-type"] + } + task ktlint(type: JavaExec, group: "verification") { description = "Check Kotlin code style." classpath = configurations.ktlint @@ -117,21 +121,21 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.core:core-ktx:1.2.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation "androidx.preference:preference-ktx:1.1.0" - implementation 'com.google.android.material:material:1.1.0' + implementation "androidx.preference:preference-ktx:1.1.1" + implementation 'com.google.android.material:material:1.2.1' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.28.2' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test:runner:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test:rules:1.3.0' androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.3.1' //---- ANDROID ARCH ROOM ---- implementation 'android.arch.persistence.room:runtime:1.1.1' @@ -139,16 +143,19 @@ dependencies { //---- ANDROID ARCH LIFECYCLE ---- implementation 'android.arch.lifecycle:common-java8:1.1.1' kapt "android.arch.lifecycle:compiler:1.1.1" - implementation 'android.arch.lifecycle:runtime:1.1.1' - implementation 'android.arch.lifecycle:extensions:1.1.1' + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + //---- GOOGLE JSON SERIALIZER/DESERIALIZER ---- implementation 'com.google.code.gson:gson:2.8.5' //---- MixPanel ---- implementation 'com.mixpanel.android:mixpanel-android:5.6.1' //---- Firebase ---- - implementation 'com.google.firebase:firebase-core:17.2.2' - implementation 'com.google.firebase:firebase-analytics:17.2.1' - implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' + implementation 'com.google.firebase:firebase-core:18.0.0' + implementation 'com.google.firebase:firebase-analytics:18.0.0' + implementation 'com.crashlytics.sdk.android:crashlytics:17.2.2' implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.61' //---- Image ---- implementation group: 'com.github.bumptech.glide', name: 'glide', version: '4.10.0' diff --git a/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt b/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt index f324299..ff4f427 100644 --- a/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt +++ b/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt @@ -1,13 +1,11 @@ package com.rootstrap.android.network.managers import androidx.annotation.RestrictTo -import com.rootstrap.android.bus import com.rootstrap.android.network.models.User import com.rootstrap.android.network.models.UserSerializer import com.rootstrap.android.network.providers.ServiceProvider import com.rootstrap.android.network.services.ApiService import com.rootstrap.android.util.extensions.ActionCallback -import retrofit2.Response /** * Singleton Object @@ -16,60 +14,16 @@ object UserManager { private var service = ServiceProvider.create(ApiService::class.java) - fun signUp(user: User) { - val userSerializer = UserSerializer(user) - val signUp = service.signUp(userSerializer) - signUp.enqueue(UserCallback()) - } + suspend fun signUp(user: User): Result = + ActionCallback.call(service.signUp(UserSerializer(user))) - fun signIn(user: User) { - val userSerializer = UserSerializer(user) - val signIn = service.signIn(userSerializer) - signIn.enqueue(LogInCallback()) - } + suspend fun signIn(user: User): Result = + ActionCallback.call(service.signIn(UserSerializer(user))) - fun signOut() { - val signOut = service.signOut() - signOut.enqueue(LogOutCallback()) - } - - private class UserCallback : ActionCallback() { - override fun responseAction(response: Response) { - super.responseAction(response) - response.body()?.let { - SessionManager.user = it.user - } - bus.post(UserCreatedSuccessfullyEvent()) - } - } - - private class LogInCallback : ActionCallback() { - - override fun responseAction(response: Response) { - super.responseAction(response) - response.body()?.let { - SessionManager.signIn(it.user) - } - bus.post(SignInSuccessfullyEvent()) - } - } - - private class LogOutCallback : ActionCallback() { - - override fun responseAction(response: Response) { - super.responseAction(response) - - SessionManager.signOut() - bus.post(SignedOutSuccessfullyEvent()) - } - } + suspend fun signOut() = ActionCallback.call(service.signOut()) @RestrictTo(RestrictTo.Scope.TESTS) - open fun reloadService(url: String) { + fun reloadService(url: String) { service = ServiceProvider.create(ApiService::class.java, url) } - - class UserCreatedSuccessfullyEvent - class SignInSuccessfullyEvent - class SignedOutSuccessfullyEvent } diff --git a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt index 16f9890..5d84c02 100644 --- a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt +++ b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt @@ -1,11 +1,16 @@ package com.rootstrap.android.network.providers +import com.google.gson.Gson import com.rootstrap.android.BuildConfig +import com.rootstrap.android.network.managers.ApiErrorType +import com.rootstrap.android.network.managers.ApiException +import com.rootstrap.android.network.models.ErrorModel import com.rootstrap.android.network.services.AuthenticationInterceptor import com.rootstrap.android.network.services.HeadersInterceptor import com.rootstrap.android.network.services.ResponseInterceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Response import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory diff --git a/app/src/main/java/com/rootstrap/android/network/services/ApiService.kt b/app/src/main/java/com/rootstrap/android/network/services/ApiService.kt index 2a832fd..7545164 100644 --- a/app/src/main/java/com/rootstrap/android/network/services/ApiService.kt +++ b/app/src/main/java/com/rootstrap/android/network/services/ApiService.kt @@ -9,11 +9,11 @@ import retrofit2.http.POST interface ApiService { @POST("users/") - fun signUp(@Body user: UserSerializer): Call + suspend fun signUp(@Body user: UserSerializer): Call @POST("users/sign_in") - fun signIn(@Body user: UserSerializer): Call + suspend fun signIn(@Body user: UserSerializer): Call @DELETE("users/sign_out") - fun signOut(): Call + suspend fun signOut(): Call } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index 9545e63..d234634 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -2,14 +2,13 @@ package com.rootstrap.android.ui.activity.main import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.ViewModelListener -import com.rootstrap.android.util.extensions.ErrorEvent -import com.rootstrap.android.util.extensions.FailureEvent -import com.squareup.otto.Subscribe +import kotlinx.coroutines.launch open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { @@ -23,27 +22,17 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel fun signUp(user: User) { networkState = NetworkState.loading - manager.signUp(user) - } - - @Subscribe - fun signedUpSuccessfully(event: UserManager.UserCreatedSuccessfullyEvent) { - networkState = NetworkState.idle - state = SignUpState.signedUpSuccess - } - - @Subscribe - fun signedUpError(event: ErrorEvent) { - error = event.error - networkState = NetworkState.idle - networkState = NetworkState.error - } - - @Subscribe - fun signedUpFailure(event: FailureEvent) { - error = null - networkState = NetworkState.idle - state = SignUpState.signedUpFailure + viewModelScope.launch { + val result = kotlin.runCatching { manager.signUp(user = user) } + result.onSuccess { + networkState = NetworkState.idle + state = SignUpState.signedUpSuccess + }.onFailure { + error = it.message + networkState = NetworkState.idle + networkState = NetworkState.error + } + } } } diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt b/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt index 175ee6a..16c2d76 100644 --- a/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt +++ b/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt @@ -1,44 +1,59 @@ package com.rootstrap.android.util.extensions import com.google.gson.Gson -import com.rootstrap.android.bus import com.rootstrap.android.network.models.ErrorModel -import com.rootstrap.android.util.ErrorUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.withContext import retrofit2.Call -import retrofit2.Callback import retrofit2.Response -abstract class ActionCallback : Callback { +class ActionCallback { - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful) { - responseAction(response) - } else { - errorAction(response) - } - } + companion object { - override fun onFailure(call: Call, t: Throwable) { - failureAction(t) - } - - open fun responseAction(response: Response) {} + suspend fun call(apiCall: Call): Result = withContext(Dispatchers.IO) { + val call = async { apiCall.execute() } + val response = call.await() + manageResponse(response) + } - open fun errorAction(response: Response) { - try { - response.errorBody()?.let { - val error = Gson().fromJson(it.charStream(), ErrorModel::class.java) - bus.post(ErrorEvent(ErrorUtil.handleCustomError(error))) + private fun manageResponse(response: Response): Result { + when { + response.isSuccessful -> { + response.body()?.let { + return Result.success(it) + } + } + else -> { + try { + response.errorBody()?.let { + val apiError = Gson().fromJson(it.charStream(), ErrorModel::class.java) + return Result.failure( + ApiException( + errorMessage = apiError.error + ) + ) + } + } catch (ignore: Exception) { + } + } } - } catch (e: Exception) { - bus.post(FailureEvent()) + + return Result.failure(ApiException(errorType = ApiErrorType.uknownError)) } } +} - open fun failureAction(throwable: Throwable?) { - bus.post(FailureEvent()) - } +class ApiException( + private val errorMessage: String?, + val errorType: ApiErrorType = ApiErrorType.apiError +) : java.lang.Exception() { + override val message: String? + get() = errorMessage } -class ErrorEvent(val error: String) -class FailureEvent +enum class ApiErrorType { + apiError, + uknownError +} From 5279a4f5b58ed8e591f30a459b412408fbb292fa Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Wed, 16 Dec 2020 17:57:44 -0300 Subject: [PATCH 13/66] Update network layer with coroutine --- app/build.gradle | 2 +- .../android/network/managers/IUserManager.kt | 11 ++++ .../android/network/managers/UserManager.kt | 10 ++-- .../rootstrap/android/network/models/User.kt | 2 +- .../network/providers/ServiceProvider.kt | 5 -- .../android/network/services/ApiService.kt | 6 +-- .../activity/main/ProfileActivityViewModel.kt | 50 +++++++++---------- .../activity/main/SignInActivityViewModel.kt | 43 +++++++++------- .../activity/main/SignUpActivityViewModel.kt | 31 +++++++++--- .../android/util/extensions/ActionCallback.kt | 46 ++++++++--------- 10 files changed, 117 insertions(+), 89 deletions(-) create mode 100644 app/src/main/java/com/rootstrap/android/network/managers/IUserManager.kt diff --git a/app/build.gradle b/app/build.gradle index b03ae34..88a2a22 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -155,7 +155,7 @@ dependencies { //---- Firebase ---- implementation 'com.google.firebase:firebase-core:18.0.0' implementation 'com.google.firebase:firebase-analytics:18.0.0' - implementation 'com.crashlytics.sdk.android:crashlytics:17.2.2' + implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.61' //---- Image ---- implementation group: 'com.github.bumptech.glide', name: 'glide', version: '4.10.0' diff --git a/app/src/main/java/com/rootstrap/android/network/managers/IUserManager.kt b/app/src/main/java/com/rootstrap/android/network/managers/IUserManager.kt new file mode 100644 index 0000000..d30f5a6 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/network/managers/IUserManager.kt @@ -0,0 +1,11 @@ +package com.rootstrap.android.network.managers + +import com.rootstrap.android.network.models.User +import com.rootstrap.android.network.models.UserSerializer +import com.rootstrap.android.util.extensions.Data + +interface IUserManager { + suspend fun signUp(user: User): Result> + suspend fun signIn(user: User): Result> + suspend fun signOut(): Result> +} diff --git a/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt b/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt index ff4f427..bb9475d 100644 --- a/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt +++ b/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt @@ -6,21 +6,23 @@ import com.rootstrap.android.network.models.UserSerializer import com.rootstrap.android.network.providers.ServiceProvider import com.rootstrap.android.network.services.ApiService import com.rootstrap.android.util.extensions.ActionCallback +import com.rootstrap.android.util.extensions.Data /** * Singleton Object * */ -object UserManager { +class UserManager : IUserManager { private var service = ServiceProvider.create(ApiService::class.java) - suspend fun signUp(user: User): Result = + override suspend fun signUp(user: User): Result> = ActionCallback.call(service.signUp(UserSerializer(user))) - suspend fun signIn(user: User): Result = + override suspend fun signIn(user: User): Result> = ActionCallback.call(service.signIn(UserSerializer(user))) - suspend fun signOut() = ActionCallback.call(service.signOut()) + override suspend fun signOut(): Result> = + ActionCallback.call(service.signOut()) @RestrictTo(RestrictTo.Scope.TESTS) fun reloadService(url: String) { diff --git a/app/src/main/java/com/rootstrap/android/network/models/User.kt b/app/src/main/java/com/rootstrap/android/network/models/User.kt index 2f490d3..99185e8 100644 --- a/app/src/main/java/com/rootstrap/android/network/models/User.kt +++ b/app/src/main/java/com/rootstrap/android/network/models/User.kt @@ -12,4 +12,4 @@ data class User( @Json(name = "username") val username: String = "" ) -data class UserSerializer(val user: User) +data class UserSerializer(@Json(name = "user") val user: User) diff --git a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt index 5d84c02..16f9890 100644 --- a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt +++ b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt @@ -1,16 +1,11 @@ package com.rootstrap.android.network.providers -import com.google.gson.Gson import com.rootstrap.android.BuildConfig -import com.rootstrap.android.network.managers.ApiErrorType -import com.rootstrap.android.network.managers.ApiException -import com.rootstrap.android.network.models.ErrorModel import com.rootstrap.android.network.services.AuthenticationInterceptor import com.rootstrap.android.network.services.HeadersInterceptor import com.rootstrap.android.network.services.ResponseInterceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Response import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory diff --git a/app/src/main/java/com/rootstrap/android/network/services/ApiService.kt b/app/src/main/java/com/rootstrap/android/network/services/ApiService.kt index 7545164..2a832fd 100644 --- a/app/src/main/java/com/rootstrap/android/network/services/ApiService.kt +++ b/app/src/main/java/com/rootstrap/android/network/services/ApiService.kt @@ -9,11 +9,11 @@ import retrofit2.http.POST interface ApiService { @POST("users/") - suspend fun signUp(@Body user: UserSerializer): Call + fun signUp(@Body user: UserSerializer): Call @POST("users/sign_in") - suspend fun signIn(@Body user: UserSerializer): Call + fun signIn(@Body user: UserSerializer): Call @DELETE("users/sign_out") - suspend fun signOut(): Call + fun signOut(): Call } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index d2a8995..92e0bec 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -2,48 +2,48 @@ package com.rootstrap.android.ui.activity.main import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.rootstrap.android.network.managers.IUserManager import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.ViewModelListener -import com.rootstrap.android.util.extensions.ErrorEvent -import com.rootstrap.android.util.extensions.FailureEvent -import com.squareup.otto.Subscribe +import com.rootstrap.android.util.extensions.ApiErrorType +import com.rootstrap.android.util.extensions.ApiException +import kotlinx.coroutines.launch open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { - private val manager = UserManager + private val manager: IUserManager = UserManager() fun signOut() { networkState = NetworkState.loading - manager.signOut() - } - - var state: ProfileState = ProfileState.none - set(value) { - field = value - listener?.updateState() + viewModelScope.launch { + val result = manager.signOut() + if (result.isSuccess) { + networkState = NetworkState.idle + state = ProfileState.signedOutSuccessfully + } else { + mangeError(result.exceptionOrNull()) + } } - - @Subscribe - fun signedOutSuccessfully(event: UserManager.SignedOutSuccessfullyEvent) { - networkState = NetworkState.idle - state = ProfileState.signedOutSuccessfully } - @Subscribe - fun signOutError(event: ErrorEvent) { - error = event.error - networkState = NetworkState.idle - networkState = NetworkState.error - } + private fun mangeError(exception: Throwable?) { + error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { + exception.message + } else null - @Subscribe - fun signOutFailure(event: FailureEvent) { - error = null networkState = NetworkState.idle + networkState = NetworkState.error state = ProfileState.signOutFailure } + + var state: ProfileState = ProfileState.none + set(value) { + field = value + listener?.updateState() + } } enum class ProfileState { diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index 3ea6f64..11fd95f 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -2,18 +2,21 @@ package com.rootstrap.android.ui.activity.main import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.rootstrap.android.network.managers.IUserManager +import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.ViewModelListener -import com.rootstrap.android.util.extensions.ErrorEvent -import com.rootstrap.android.util.extensions.FailureEvent -import com.squareup.otto.Subscribe +import com.rootstrap.android.util.extensions.ApiErrorType +import com.rootstrap.android.util.extensions.ApiException +import kotlinx.coroutines.launch open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { - private val manager = UserManager + private val manager: IUserManager = UserManager() var state: SignInState = SignInState.none set(value) { @@ -23,26 +26,28 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel fun signIn(user: User) { networkState = NetworkState.loading - manager.signIn(user) - } + viewModelScope.launch { + val result = manager.signIn(user = user) + if (result.isSuccess) { + result.getOrNull()?.value?.user?.let { user -> + SessionManager.signIn(user) + } - @Subscribe - fun signedInSuccessfully(event: UserManager.SignInSuccessfullyEvent) { - networkState = NetworkState.idle - state = SignInState.signedInSuccess + networkState = NetworkState.idle + state = SignInState.signedInSuccess + } else { + mangeError(result.exceptionOrNull()) + } + } } - @Subscribe - fun signedInError(event: ErrorEvent) { - error = event.error - networkState = NetworkState.idle - networkState = NetworkState.error - } + private fun mangeError(exception: Throwable?) { + error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { + exception.message + } else null - @Subscribe - fun signedInFailure(event: FailureEvent) { - error = null networkState = NetworkState.idle + networkState = NetworkState.error state = SignInState.signedInFailure } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index d234634..e33597e 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -3,16 +3,20 @@ package com.rootstrap.android.ui.activity.main import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import com.rootstrap.android.network.managers.IUserManager +import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.ViewModelListener +import com.rootstrap.android.util.extensions.ApiErrorType +import com.rootstrap.android.util.extensions.ApiException import kotlinx.coroutines.launch open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { - private val manager = UserManager + private val manager: IUserManager = UserManager() var state: SignUpState = SignUpState.none set(value) { @@ -23,17 +27,30 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel fun signUp(user: User) { networkState = NetworkState.loading viewModelScope.launch { - val result = kotlin.runCatching { manager.signUp(user = user) } - result.onSuccess { + val result = manager.signUp(user = user) + + if (result.isSuccess) { + result.getOrNull()?.value?.user?.let { user -> + SessionManager.signIn(user) + } + networkState = NetworkState.idle state = SignUpState.signedUpSuccess - }.onFailure { - error = it.message - networkState = NetworkState.idle - networkState = NetworkState.error + } else { + mangeError(result.exceptionOrNull()) } } } + + private fun mangeError(exception: Throwable?) { + error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { + exception.message + } else null + + networkState = NetworkState.idle + networkState = NetworkState.error + state = SignUpState.signedUpFailure + } } enum class SignUpState { diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt b/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt index 16c2d76..63b4072 100644 --- a/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt +++ b/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt @@ -3,7 +3,6 @@ package com.rootstrap.android.util.extensions import com.google.gson.Gson import com.rootstrap.android.network.models.ErrorModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async import kotlinx.coroutines.withContext import retrofit2.Call import retrofit2.Response @@ -12,31 +11,28 @@ class ActionCallback { companion object { - suspend fun call(apiCall: Call): Result = withContext(Dispatchers.IO) { - val call = async { apiCall.execute() } - val response = call.await() - manageResponse(response) - } + suspend fun call(apiCall: Call): Result> = + withContext(Dispatchers.IO) { + val response = apiCall.execute() + manageResponse(response) + } - private fun manageResponse(response: Response): Result { - when { - response.isSuccessful -> { - response.body()?.let { - return Result.success(it) - } - } - else -> { - try { - response.errorBody()?.let { - val apiError = Gson().fromJson(it.charStream(), ErrorModel::class.java) - return Result.failure( - ApiException( - errorMessage = apiError.error - ) + private fun manageResponse(response: Response): Result> { + if (response.isSuccessful) { + return Result.success( + Data(response.body()) + ) + } else { + try { + response.errorBody()?.let { + val apiError = Gson().fromJson(it.charStream(), ErrorModel::class.java) + return Result.failure( + ApiException( + errorMessage = apiError.error ) - } - } catch (ignore: Exception) { + ) } + } catch (ignore: Exception) { } } @@ -45,8 +41,10 @@ class ActionCallback { } } +class Data(val value: T?) + class ApiException( - private val errorMessage: String?, + private val errorMessage: String? = null, val errorType: ApiErrorType = ApiErrorType.apiError ) : java.lang.Exception() { override val message: String? From aee5c384d318389427afe60a296bd6629046d4cc Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Thu, 17 Dec 2020 13:54:28 -0300 Subject: [PATCH 14/66] fix comments --- .../android/ui/activity/main/ProfileActivityViewModel.kt | 4 ++-- .../android/ui/activity/main/SignInActivityViewModel.kt | 4 ++-- .../android/ui/activity/main/SignUpActivityViewModel.kt | 4 ++-- .../com/rootstrap/android/util/extensions/ActionCallback.kt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index 92e0bec..e0c3a9b 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -24,12 +24,12 @@ open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewMode networkState = NetworkState.idle state = ProfileState.signedOutSuccessfully } else { - mangeError(result.exceptionOrNull()) + manageError(result.exceptionOrNull()) } } } - private fun mangeError(exception: Throwable?) { + private fun manageError(exception: Throwable?) { error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { exception.message } else null diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index 11fd95f..cb776e3 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -36,12 +36,12 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel networkState = NetworkState.idle state = SignInState.signedInSuccess } else { - mangeError(result.exceptionOrNull()) + manageError(result.exceptionOrNull()) } } } - private fun mangeError(exception: Throwable?) { + private fun manageError(exception: Throwable?) { error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { exception.message } else null diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index e33597e..1adc57f 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -37,12 +37,12 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel networkState = NetworkState.idle state = SignUpState.signedUpSuccess } else { - mangeError(result.exceptionOrNull()) + manageError(result.exceptionOrNull()) } } } - private fun mangeError(exception: Throwable?) { + private fun manageError(exception: Throwable?) { error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { exception.message } else null diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt b/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt index 63b4072..c81c67b 100644 --- a/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt +++ b/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt @@ -36,7 +36,7 @@ class ActionCallback { } } - return Result.failure(ApiException(errorType = ApiErrorType.uknownError)) + return Result.failure(ApiException(errorType = ApiErrorType.unknownError)) } } } @@ -53,5 +53,5 @@ class ApiException( enum class ApiErrorType { apiError, - uknownError + unknownError } From 8de6f63b077d709eb3489b61e0975ff8781c00b7 Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Fri, 18 Dec 2020 12:06:56 -0300 Subject: [PATCH 15/66] fix comments --- .../rootstrap/android/ui/activity/main/ProfileActivity.kt | 2 +- .../android/ui/activity/main/ProfileActivityViewModel.kt | 4 ++-- .../rootstrap/android/ui/activity/main/SignInActivity.kt | 4 ++-- .../android/ui/activity/main/SignInActivityViewModel.kt | 8 ++++---- .../rootstrap/android/ui/activity/main/SignUpActivity.kt | 4 ++-- .../android/ui/activity/main/SignUpActivityViewModel.kt | 8 ++++---- .../rootstrap/android/util/extensions/ActionCallback.kt | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt index 0f8403f..7b24c22 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt @@ -42,7 +42,7 @@ class ProfileActivity : BaseActivity(), ProfileView { override fun updateState() { when (viewModel.state) { ProfileState.signOutFailure -> showError(viewModel.error) - ProfileState.signedOutSuccessfully -> goToFirstScreen() + ProfileState.signOutSuccessfully -> goToFirstScreen() else -> { } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index e0c3a9b..8e38de0 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -22,7 +22,7 @@ open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewMode val result = manager.signOut() if (result.isSuccess) { networkState = NetworkState.idle - state = ProfileState.signedOutSuccessfully + state = ProfileState.signOutSuccessfully } else { manageError(result.exceptionOrNull()) } @@ -48,7 +48,7 @@ open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewMode enum class ProfileState { signOutFailure, - signedOutSuccessfully, + signOutSuccessfully, none, } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index be29db0..2ea67eb 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -52,8 +52,8 @@ class SignInActivity : PermissionActivity(), AuthView { private val viewModelListener = object : ViewModelListener { override fun updateState() { when (viewModel.state) { - SignInState.signedInFailure -> showError(viewModel.error) - SignInState.signedInSuccess -> showProfile() + SignInState.signInFailure -> showError(viewModel.error) + SignInState.signInSuccessfully -> showProfile() else -> { } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index cb776e3..df138aa 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -34,7 +34,7 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel } networkState = NetworkState.idle - state = SignInState.signedInSuccess + state = SignInState.signInSuccessfully } else { manageError(result.exceptionOrNull()) } @@ -48,13 +48,13 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel networkState = NetworkState.idle networkState = NetworkState.error - state = SignInState.signedInFailure + state = SignInState.signInFailure } } enum class SignInState { - signedInFailure, - signedInSuccess, + signInFailure, + signInSuccessfully, none, } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index 627bec9..ad1dabc 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -52,8 +52,8 @@ class SignUpActivity : BaseActivity(), AuthView { private val viewModelListener = object : ViewModelListener { override fun updateState() { when (viewModel.state) { - SignUpState.signedUpFailure -> showError(viewModel.error) - SignUpState.signedUpSuccess -> showProfile() + SignUpState.signUpFailure -> showError(viewModel.error) + SignUpState.signUpSuccessfully -> showProfile() else -> { } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index 1adc57f..ebe198e 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -35,7 +35,7 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel } networkState = NetworkState.idle - state = SignUpState.signedUpSuccess + state = SignUpState.signUpSuccessfully } else { manageError(result.exceptionOrNull()) } @@ -49,13 +49,13 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel networkState = NetworkState.idle networkState = NetworkState.error - state = SignUpState.signedUpFailure + state = SignUpState.signUpFailure } } enum class SignUpState { - signedUpFailure, - signedUpSuccess, + signUpFailure, + signUpSuccessfully, none, } diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt b/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt index c81c67b..ef22a27 100644 --- a/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt +++ b/app/src/main/java/com/rootstrap/android/util/extensions/ActionCallback.kt @@ -14,10 +14,10 @@ class ActionCallback { suspend fun call(apiCall: Call): Result> = withContext(Dispatchers.IO) { val response = apiCall.execute() - manageResponse(response) + handleResponse(response) } - private fun manageResponse(response: Response): Result> { + private fun handleResponse(response: Response): Result> { if (response.isSuccessful) { return Result.success( Data(response.body()) From cb1cbb9b5a27ae0af4eb1fa03dc19fb018ef7159 Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Fri, 18 Dec 2020 14:03:17 -0300 Subject: [PATCH 16/66] refactor view models --- .../java/com/rootstrap/android/utils/BaseTests.kt | 1 - .../rootstrap/android/network/managers/UserManager.kt | 2 +- .../android/ui/activity/main/ProfileActivity.kt | 2 +- .../ui/activity/main/ProfileActivityViewModel.kt | 10 +++++----- .../android/ui/activity/main/SignInActivity.kt | 2 +- .../ui/activity/main/SignInActivityViewModel.kt | 10 +++++----- .../android/ui/activity/main/SignUpActivity.kt | 2 +- .../ui/activity/main/SignUpActivityViewModel.kt | 10 +++++----- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt b/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt index c0a2676..3fa93b6 100644 --- a/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt +++ b/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt @@ -12,7 +12,6 @@ import androidx.test.runner.lifecycle.Stage import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User -import okhttp3.Dispatcher import okhttp3.mockwebserver.Dispatcher import org.junit.runner.RunWith diff --git a/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt b/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt index bb9475d..89632af 100644 --- a/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt +++ b/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt @@ -11,7 +11,7 @@ import com.rootstrap.android.util.extensions.Data /** * Singleton Object * */ -class UserManager : IUserManager { +object UserManager : IUserManager { private var service = ServiceProvider.create(ApiService::class.java) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt index 7b24c22..696b34a 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt @@ -42,7 +42,7 @@ class ProfileActivity : BaseActivity(), ProfileView { override fun updateState() { when (viewModel.state) { ProfileState.signOutFailure -> showError(viewModel.error) - ProfileState.signOutSuccessfully -> goToFirstScreen() + ProfileState.signOutSuccess -> goToFirstScreen() else -> { } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index 8e38de0..626c949 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.launch open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { - private val manager: IUserManager = UserManager() + private val manager: IUserManager = UserManager fun signOut() { networkState = NetworkState.loading @@ -22,14 +22,14 @@ open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewMode val result = manager.signOut() if (result.isSuccess) { networkState = NetworkState.idle - state = ProfileState.signOutSuccessfully + state = ProfileState.signOutSuccess } else { - manageError(result.exceptionOrNull()) + handleError(result.exceptionOrNull()) } } } - private fun manageError(exception: Throwable?) { + private fun handleError(exception: Throwable?) { error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { exception.message } else null @@ -48,7 +48,7 @@ open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewMode enum class ProfileState { signOutFailure, - signOutSuccessfully, + signOutSuccess, none, } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index 2ea67eb..fa0c9b6 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -53,7 +53,7 @@ class SignInActivity : PermissionActivity(), AuthView { override fun updateState() { when (viewModel.state) { SignInState.signInFailure -> showError(viewModel.error) - SignInState.signInSuccessfully -> showProfile() + SignInState.signInSuccess -> showProfile() else -> { } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index df138aa..c882c2a 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.launch open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { - private val manager: IUserManager = UserManager() + private val manager: IUserManager = UserManager var state: SignInState = SignInState.none set(value) { @@ -34,14 +34,14 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel } networkState = NetworkState.idle - state = SignInState.signInSuccessfully + state = SignInState.signInSuccess } else { - manageError(result.exceptionOrNull()) + handleError(result.exceptionOrNull()) } } } - private fun manageError(exception: Throwable?) { + private fun handleError(exception: Throwable?) { error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { exception.message } else null @@ -54,7 +54,7 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel enum class SignInState { signInFailure, - signInSuccessfully, + signInSuccess, none, } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index ad1dabc..9a131be 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -53,7 +53,7 @@ class SignUpActivity : BaseActivity(), AuthView { override fun updateState() { when (viewModel.state) { SignUpState.signUpFailure -> showError(viewModel.error) - SignUpState.signUpSuccessfully -> showProfile() + SignUpState.signUpSuccess -> showProfile() else -> { } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index ebe198e..ec4348a 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.launch open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { - private val manager: IUserManager = UserManager() + private val manager: IUserManager = UserManager var state: SignUpState = SignUpState.none set(value) { @@ -35,14 +35,14 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel } networkState = NetworkState.idle - state = SignUpState.signUpSuccessfully + state = SignUpState.signUpSuccess } else { - manageError(result.exceptionOrNull()) + handleError(result.exceptionOrNull()) } } } - private fun manageError(exception: Throwable?) { + private fun handleError(exception: Throwable?) { error = if (exception is ApiException && exception.errorType == ApiErrorType.apiError) { exception.message } else null @@ -55,7 +55,7 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel enum class SignUpState { signUpFailure, - signUpSuccessfully, + signUpSuccess, none, } From e84ea9b26ba571b88b9d4e6669bd360a2ef30e78 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Mon, 18 Jan 2021 09:47:52 -0300 Subject: [PATCH 17/66] updated kotlin plug in version updated crashlytics updated build graddle added view binding --- app/build.gradle | 19 +++++++++------ .../ui/activity/main/SignInActivity.kt | 15 +++++++----- .../ui/activity/main/SignUpActivity.kt | 23 +++++++++++-------- build.gradle | 8 +++---- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 88a2a22..59226f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,9 @@ apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' -apply plugin: 'io.fabric' +apply plugin: 'com.google.gms.google-services' + +apply plugin: 'com.google.firebase.crashlytics' android { compileSdkVersion 29 @@ -20,6 +22,10 @@ android { testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } + viewBinding { + enabled = true + } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 @@ -124,7 +130,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation "androidx.preference:preference-ktx:1.1.1" implementation 'com.google.android.material:material:1.2.1' @@ -153,10 +159,11 @@ dependencies { //---- MixPanel ---- implementation 'com.mixpanel.android:mixpanel-android:5.6.1' //---- Firebase ---- + implementation platform('com.google.firebase:firebase-bom:26.1.0') implementation 'com.google.firebase:firebase-core:18.0.0' - implementation 'com.google.firebase:firebase-analytics:18.0.0' - implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' - implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.61' + implementation 'com.google.firebase:firebase-analytics-ktx' + implementation 'com.google.firebase:firebase-crashlytics-ktx' + implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.72' //---- Image ---- implementation group: 'com.github.bumptech.glide', name: 'glide', version: '4.10.0' //---- Network ---- @@ -169,5 +176,3 @@ dependencies { //---- Linters ---- ktlint "com.pinterest:ktlint:0.35.0" } - -apply plugin: 'com.google.gms.google-services' diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index fa0c9b6..25b2d8c 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -2,8 +2,9 @@ package com.rootstrap.android.ui.activity.main import android.Manifest import android.os.Bundle -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R +import com.rootstrap.android.databinding.ActivitySignInBinding import com.rootstrap.android.metrics.Analytics import com.rootstrap.android.metrics.PageEvents import com.rootstrap.android.metrics.VISIT_SIGN_IN @@ -14,22 +15,24 @@ import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.value import com.rootstrap.android.util.permissions.PermissionActivity import com.rootstrap.android.util.permissions.PermissionResponse -import kotlinx.android.synthetic.main.activity_sign_in.* class SignInActivity : PermissionActivity(), AuthView { private lateinit var viewModel: SignInActivityViewModel + private lateinit var binding: ActivitySignInBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + binding = ActivitySignInBinding.inflate(layoutInflater) + setContentView(R.layout.activity_sign_in) Analytics.track(PageEvents.visit(VISIT_SIGN_IN)) val factory = SignInActivityViewModelFactory(viewModelListener) - viewModel = ViewModelProviders.of(this, factory) + viewModel = ViewModelProvider(this, factory) .get(SignInActivityViewModel::class.java) - sign_in_button.setOnClickListener { signIn() } + binding.signInButton.setOnClickListener { signIn() } lifecycle.addObserver(viewModel) @@ -42,8 +45,8 @@ class SignInActivity : PermissionActivity(), AuthView { private fun signIn() { val user = User( - email = email_edit_text.value(), - password = password_edit_text.value() + email = binding.emailEditText.value(), + password = binding.passwordEditText.value() ) viewModel.signIn(user) } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index 9a131be..c551476 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -2,8 +2,9 @@ package com.rootstrap.android.ui.activity.main import android.content.Intent import android.os.Bundle -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R +import com.rootstrap.android.databinding.ActivitySignUpBinding import com.rootstrap.android.metrics.Analytics import com.rootstrap.android.metrics.PageEvents import com.rootstrap.android.metrics.VISIT_SIGN_UP @@ -13,23 +14,25 @@ import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.value -import kotlinx.android.synthetic.main.activity_sign_up.* class SignUpActivity : BaseActivity(), AuthView { private lateinit var viewModel: SignUpActivityViewModel + private lateinit var binding: ActivitySignUpBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_sign_up) + binding = ActivitySignUpBinding.inflate(layoutInflater) + + setContentView(binding.root) Analytics.track(PageEvents.visit(VISIT_SIGN_UP)) val factory = SignUpActivityViewModelFactory(viewModelListener) - viewModel = ViewModelProviders.of(this, factory) + viewModel = ViewModelProvider(this, factory) .get(SignUpActivityViewModel::class.java) - sign_up_button.setOnClickListener { signUp() } - sign_in_text_view.setOnClickListener { startActivity(Intent(this, SignInActivity::class.java)) } + binding.signUpButton.setOnClickListener { signUp() } + binding.signInTextView.setOnClickListener { startActivity(Intent(this, SignInActivity::class.java)) } lifecycle.addObserver(viewModel) } @@ -40,10 +43,10 @@ class SignUpActivity : BaseActivity(), AuthView { private fun signUp() { val user = User( - email = email_edit_text.value(), - firstName = first_name_edit_text.value(), - lastName = last_name_edit_text.value(), - password = password_edit_text.value() + email = binding.emailEditText.value(), + firstName = binding.firstNameEditText.value(), + lastName = binding.lastNameEditText.value(), + password = binding.passwordEditText.value() ) viewModel.signUp(user) } diff --git a/build.gradle b/build.gradle index eba192c..991aa38 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.61' + ext.kotlin_version = '1.3.72' repositories { google() jcenter() @@ -11,10 +11,10 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.3' - classpath 'io.fabric.tools:gradle:1.31.2' // Crashlytics plugin + classpath 'com.google.gms:google-services:4.3.4' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 62f7a0c..e7b189d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 22 13:26:09 UYT 2020 +#Mon Jan 18 09:20:52 ART 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip From 294c370ca5515f4219e0d9f5d626e372c8567b7b Mon Sep 17 00:00:00 2001 From: aguskoll Date: Thu, 21 Jan 2021 09:28:02 -0300 Subject: [PATCH 18/66] update libraries removed deprecated code --- app/build.gradle | 4 ++-- .../com/rootstrap/android/ui/activity/main/ProfileActivity.kt | 4 ++-- .../android/ui/activity/main/ProfileActivityViewModel.kt | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 59226f3..730165c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -134,7 +134,7 @@ dependencies { implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation "androidx.preference:preference-ktx:1.1.1" implementation 'com.google.android.material:material:1.2.1' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.1' testImplementation 'org.mockito:mockito-core:2.28.2' androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' @@ -160,7 +160,7 @@ dependencies { implementation 'com.mixpanel.android:mixpanel-android:5.6.1' //---- Firebase ---- implementation platform('com.google.firebase:firebase-bom:26.1.0') - implementation 'com.google.firebase:firebase-core:18.0.0' + implementation 'com.google.firebase:firebase-core:18.0.1' implementation 'com.google.firebase:firebase-analytics-ktx' implementation 'com.google.firebase:firebase-crashlytics-ktx' implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.72' diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt index 696b34a..c8e85c2 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt @@ -1,7 +1,7 @@ package com.rootstrap.android.ui.activity.main import android.os.Bundle -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R import com.rootstrap.android.metrics.Analytics import com.rootstrap.android.metrics.PageEvents @@ -22,7 +22,7 @@ class ProfileActivity : BaseActivity(), ProfileView { setContentView(R.layout.activity_profile) val factory = ProfileActivityViewModelFactory(viewModelListener) - viewModel = ViewModelProviders.of(this, factory) + viewModel = ViewModelProvider(this, factory) .get(ProfileActivityViewModel::class.java) Analytics.track(PageEvents.visit(VISIT_PROFILE)) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index 626c949..18d680c 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.rootstrap.android.network.managers.IUserManager +import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState @@ -23,6 +24,7 @@ open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewMode if (result.isSuccess) { networkState = NetworkState.idle state = ProfileState.signOutSuccess + SessionManager.signOut() } else { handleError(result.exceptionOrNull()) } From 59d51c3085bbb893aa71c7b48043887c145482da Mon Sep 17 00:00:00 2001 From: sebalopez Date: Mon, 25 Jan 2021 11:12:29 -0300 Subject: [PATCH 19/66] added GitHub workflows for CI and automated release for Dev and Staging --- .circleci/config.yml | 13 ------ .github/workflows/cicd.yml | 83 ++++++++++++++++++++++++++++++++++++++ Gemfile.lock | 2 +- README.md | 22 ++++++++++ app/build.gradle | 2 +- build.gradle | 2 +- fastlane/Fastfile | 4 ++ fastlane/README.md | 60 +++++++++------------------ 8 files changed, 130 insertions(+), 58 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/cicd.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 01c267c..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference -version: 2.1 -# Use a package of configuration called an orb. -orbs: - # Declare a dependency on the welcome-orb - welcome: circleci/welcome-orb@0.4.1 -# Orchestrate or schedule a set of jobs -workflows: - # Name the workflow "welcome" - welcome: - # Run the welcome/run job in its own container - jobs: - - welcome/run \ No newline at end of file diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml new file mode 100644 index 0000000..17eca57 --- /dev/null +++ b/.github/workflows/cicd.yml @@ -0,0 +1,83 @@ +name: Continuous Integration + +on: + push: + pull_request: + +env: + LANG: en_US.UTF-8 + # Notifications + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_URL }} + SLACK_CHANNEL: '#dev-builds' + +jobs: + + ci: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Install Fastlane and required plugins + run: | + sudo gem install bundler -v "$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1)" + bundle install --path vendor/bundle + # Runs build with Gradle + - name: Build with Fastlane + run: bundle exec fastlane debug_dev + - name: Send notification of build result + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + text: '${{github.repository}} Dev build status is ${{ job.status }}' + fields: repo,message,commit,author,action,eventName,ref,workflow,job,took + if: always() + + + release: + if: ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master' }} + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + # S3 + FOLDER: android-base + AWS_REGION: us-east-1 + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + KEYS_BUCKET: ${{ secrets.AWS_S3_KEYS_BUCKET }} + # Android Release + JSON_KEYFILE: google-api.json + RELEASE_STORE_FILE: key.keystore + RELEASE_STORE_PASSWORD: ${{ secrets.ANDROID_RELEASE_STORE_PASSWORD }} + RELEASE_KEY_ALIAS: debug + RELEASE_KEY_PASSWORD: ${{ secrets.ANDROID_RELEASE_KEY_PASSWORD }} + # Notifications + SLACK_URL: ${{ secrets.SLACK_URL }} + SLACK_CHANNEL: '#dev-builds' + steps: + - name: Checkout + uses: actions/checkout@v2 + # Downloads certificate, private key and Firebase file + - name: Download code signing items + run: | + aws s3 cp s3://$KEYS_BUCKET/$FOLDER/ . --recursive + mv ./android/$RELEASE_STORE_FILE ./app/$RELEASE_STORE_FILE + - name: Install Fastlane and required plugins + run: | + sudo gem install bundler + bundle install --path vendor/bundle + # Build with Gradle and submit to Play Store + - name: Submit Development build with Fastlane + if: ${{ github.ref == 'refs/heads/develop' }} + run: bundle exec fastlane deploy_dev + - name: Submit Staging build with Fastlane + if: ${{ github.ref == 'refs/heads/master' }} + run: bundle exec fastlane deploy_staging + - name: Send notification of build result + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + text: '${{github.repository}} ${{github.ref}} build submission status is ${{ job.status }}' + fields: repo,message,commit,author,action,eventName,ref,workflow,job,took + if: always() + diff --git a/Gemfile.lock b/Gemfile.lock index 2fc0111..79227d5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -178,4 +178,4 @@ DEPENDENCIES fastlane-plugin-increment_version_code BUNDLED WITH - 1.17.2 + 2.1.2 diff --git a/README.md b/README.md index 1bb1e78..0d936a7 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,17 @@ We suggest using [git secret](https://git-secret.io/) as a simple and secure sol We provide configuration files for automating build, test and submission of the application using [Fastlane](https://docs.fastlane.tools/) +### Requirements +* Ensure JDK 1.8 is installed +* Ensure proper version of Android SDK command line tools is installed +* Install _fastlane_ using +``` +[sudo] gem install fastlane -NV +``` +or alternatively using `brew cask install fastlane` + +### Usage Lanes for each deployment target example are provided with some basic behavior: - Each target has two options: `debug_x` and `deploy_x`. - Each option will: @@ -64,6 +74,18 @@ Lanes for each deployment target example are provided with some basic behavior: Check `fastlane/Appfile` and `fastlane/Fastfile` for more information. +## Continuous Integration with GitHub Actions + +We provide an example workflow [cicd.yml](.github/workflows/cicd.yml) including two jobs for running under [GitHub Actions](https://docs.github.com/en/actions), which can be modified according to the specifics of each project: + +* `ci` + * runs upon every push and PR + * installs Fastlane and runs `debug_dev` lane +* `release` + * runs upon every push to `develop` or `master` + * downloads keystore and Google api key from S3 (credentials need to be present in repo Secrets) + * installs Fastlane and runs `deploy_*` lane depending on branch (`Dev` if in `develop`, `Stsaging` if in `master`) - This could be easily modified to release `Prod` instead + ## Analytics - Add analytics manager: 1. Firebase diff --git a/app/build.gradle b/app/build.gradle index df2d3b4..aacbae1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,7 +15,7 @@ android { applicationId "com.rootstrap.android" minSdkVersion 21 targetSdkVersion 29 - versionCode 35 + versionCode 42 versionName "1.0" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } diff --git a/build.gradle b/build.gradle index eba192c..2aa169a 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:4.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.3' classpath 'io.fabric.tools:gradle:1.31.2' // Crashlytics plugin diff --git a/fastlane/Fastfile b/fastlane/Fastfile index adee47a..c4dd60a 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,6 +1,10 @@ +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane default_platform(:android) +skip_docs + platform :android do lane :deploy_production do release(flavor: 'Prod', track: 'internal') diff --git a/fastlane/README.md b/fastlane/README.md index ba11be3..a0baa2f 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -1,64 +1,40 @@ -fastlane documentation -================ -# Installation +# Android Fastlane configuration +============================ -Make sure you have the latest version of the Xcode command line tools installed: +## Installation and requirements -``` -xcode-select --install -``` +* Ensure JDK 1.8 is installed + +* Ensure proper version of Android SDK command line tools is installed -Install _fastlane_ using +* Install _fastlane_ using ``` [sudo] gem install fastlane -NV ``` or alternatively using `brew cask install fastlane` -# Available Actions -## Android -### android deploy_production -``` -fastlane android deploy_production -``` -### android deploy_dev -``` -fastlane android deploy_dev -``` +## General workflow -### android deploy_staging -``` -fastlane android deploy_staging -``` +* Fastlane for Android basically executes Gradle commands for cleaning, installing Android dependencies and assembling the project into a .apk +* Application file is published to Google Play Store - keystore file needs to be present under `./app` and json API key file present in the root folder. -### android debug_production -``` -fastlane android debug_production -``` -### android debug_dev -``` -fastlane android debug_dev -``` +## Actions breakdown -### android debug_staging -``` -fastlane android debug_staging -``` +Modify the Fastfile as appropiate for your project. -### android release +Execute with ``` -fastlane android release +fastlane lane_name ``` -### android debug -``` -fastlane android debug -``` +### debug_* +Builds and archive corresponding flavor for local use +### deploy_* +Builds corresponding flavor and pushes to Play Store ---- - -This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). From a009a1574c8efe9b281e21937a6b3bbbd3d0df53 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Thu, 4 Feb 2021 09:12:38 -0300 Subject: [PATCH 20/66] dialog refactor and base fragment implementation --- .../rootstrap/android/ui/base/BaseActivity.kt | 32 ++------------- .../rootstrap/android/ui/base/BaseFragment.kt | 7 ++-- .../android/util/LoadingDialogUtil.kt | 41 +++++++++++++++++++ 3 files changed, 49 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt diff --git a/app/src/main/java/com/rootstrap/android/ui/base/BaseActivity.kt b/app/src/main/java/com/rootstrap/android/ui/base/BaseActivity.kt index b0cf488..4b3765a 100644 --- a/app/src/main/java/com/rootstrap/android/ui/base/BaseActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/base/BaseActivity.kt @@ -2,47 +2,23 @@ package com.rootstrap.android.ui.base import android.annotation.SuppressLint import android.app.Activity -import android.app.AlertDialog import android.content.Intent import androidx.appcompat.app.AppCompatActivity -import com.rootstrap.android.R -import com.rootstrap.android.ui.custom.LoadingDialog +import com.rootstrap.android.util.LoadingDialogUtil @SuppressLint("Registered") open class BaseActivity : AppCompatActivity(), BaseView { - private var loadingDialog: LoadingDialog? = null - override fun showProgress() { - if (loadingDialog == null) { - loadingDialog = LoadingDialog(this, null) - } - - loadingDialog!!.show() + LoadingDialogUtil.showProgress(this) } override fun hideProgress() { - if (loadingDialog != null) { - loadingDialog!!.dismiss() - } + LoadingDialogUtil.hideProgress() } override fun showError(message: String?) { - val builder = AlertDialog.Builder(this) - builder.setTitle(getString(R.string.error)) - - when (message) { - "" -> builder.setMessage(getString(R.string.generic_error)) - null -> builder.setMessage(getString(R.string.generic_error)) - else -> builder.setMessage(message) - } - - builder.setPositiveButton(getString(R.string.ok)) { dialog, _ -> - dialog.cancel() - } - - val dialog: AlertDialog = builder.create() - dialog.show() + LoadingDialogUtil.showError(message, this) } protected fun startActivityClearTask(activity: Activity) { diff --git a/app/src/main/java/com/rootstrap/android/ui/base/BaseFragment.kt b/app/src/main/java/com/rootstrap/android/ui/base/BaseFragment.kt index aa6c60d..863b49e 100644 --- a/app/src/main/java/com/rootstrap/android/ui/base/BaseFragment.kt +++ b/app/src/main/java/com/rootstrap/android/ui/base/BaseFragment.kt @@ -1,18 +1,19 @@ package com.rootstrap.android.ui.base import androidx.fragment.app.Fragment +import com.rootstrap.android.util.LoadingDialogUtil open class BaseFragment : Fragment(), BaseView { override fun showProgress() { - TODO("not implemented") // To change body of created functions use File | Settings | File Templates. + LoadingDialogUtil.showProgress(requireContext()) } override fun hideProgress() { - TODO("not implemented") // To change body of created functions use File | Settings | File Templates. + LoadingDialogUtil.hideProgress() } override fun showError(message: String?) { - TODO("not implemented") // To change body of created functions use File | Settings | File Templates. + LoadingDialogUtil.showError(message, requireContext()) } } diff --git a/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt b/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt new file mode 100644 index 0000000..7ce4de3 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt @@ -0,0 +1,41 @@ +package com.rootstrap.android.util + +import android.app.AlertDialog +import android.content.Context +import com.rootstrap.android.R +import com.rootstrap.android.ui.custom.LoadingDialog + +object LoadingDialogUtil { + + private var loadingDialog: LoadingDialog? = null + + fun showProgress(context: Context) { + if (loadingDialog == null) { + loadingDialog = LoadingDialog(context, null) + } + + loadingDialog?.show() + } + + fun hideProgress() { + loadingDialog?.run { dismiss() } + } + + fun showError(message: String?, context: Context) { + val builder = AlertDialog.Builder(context) + builder.setTitle(context.getString(R.string.error)) + + when (message) { + "" -> builder.setMessage(context.getString(R.string.generic_error)) + null -> builder.setMessage(context.getString(R.string.generic_error)) + else -> builder.setMessage(message) + } + + builder.setPositiveButton(context.getString(R.string.ok)) { dialog, _ -> + dialog.cancel() + } + + val dialog: AlertDialog = builder.create() + dialog.show() + } +} From 68ba8f9f25200872e49cb08376ed16219d03c9f9 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Thu, 4 Feb 2021 09:53:27 -0300 Subject: [PATCH 21/66] libraries updates --- app/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 730165c..f6e470e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -127,7 +127,7 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' @@ -155,15 +155,15 @@ dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" //---- GOOGLE JSON SERIALIZER/DESERIALIZER ---- - implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.code.gson:gson:2.8.6' //---- MixPanel ---- implementation 'com.mixpanel.android:mixpanel-android:5.6.1' //---- Firebase ---- implementation platform('com.google.firebase:firebase-bom:26.1.0') - implementation 'com.google.firebase:firebase-core:18.0.1' + implementation 'com.google.firebase:firebase-core:18.0.2' implementation 'com.google.firebase:firebase-analytics-ktx' implementation 'com.google.firebase:firebase-crashlytics-ktx' - implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.72' + implementation 'org.jetbrains.kotlin:kotlin-reflect:1.4.10' //---- Image ---- implementation group: 'com.github.bumptech.glide', name: 'glide', version: '4.10.0' //---- Network ---- From 97e2ffd2671e3e0067cfbe4b560cc003db001a51 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Thu, 4 Feb 2021 10:45:15 -0300 Subject: [PATCH 22/66] code clean up --- .../ui/activity/main/SignInActivity.kt | 2 +- .../ui/activity/main/SignUpActivity.kt | 27 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index 25b2d8c..d8171ca 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -71,7 +71,7 @@ class SignInActivity : PermissionActivity(), AuthView { } } - fun sampleAskForPermission() { + private fun sampleAskForPermission() { requestPermission(arrayOf(Manifest.permission.CAMERA), object : PermissionResponse { override fun granted() { // TODO.. diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index c551476..9f9f01f 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -31,9 +31,10 @@ class SignUpActivity : BaseActivity(), AuthView { viewModel = ViewModelProvider(this, factory) .get(SignUpActivityViewModel::class.java) - binding.signUpButton.setOnClickListener { signUp() } - binding.signInTextView.setOnClickListener { startActivity(Intent(this, SignInActivity::class.java)) } - + with(binding) { + signUpButton.setOnClickListener { signUp() } + signInTextView.setOnClickListener { signIn() } + } lifecycle.addObserver(viewModel) } @@ -41,14 +42,20 @@ class SignUpActivity : BaseActivity(), AuthView { startActivityClearTask(ProfileActivity()) } + private fun signIn() { + startActivity(Intent(this, SignInActivity::class.java)) + } + private fun signUp() { - val user = User( - email = binding.emailEditText.value(), - firstName = binding.firstNameEditText.value(), - lastName = binding.lastNameEditText.value(), - password = binding.passwordEditText.value() - ) - viewModel.signUp(user) + with(binding) { + val user = User( + email = emailEditText.value(), + firstName = firstNameEditText.value(), + lastName = lastNameEditText.value(), + password = passwordEditText.value() + ) + viewModel.signUp(user) + } } // ViewModelListener From 30683b0409b57eb864509e41dba6a349260a0876 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Thu, 4 Feb 2021 10:46:59 -0300 Subject: [PATCH 23/66] kotlin scope functions --- .../android/ui/activity/main/SignInActivity.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index d8171ca..cb7f323 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -44,11 +44,13 @@ class SignInActivity : PermissionActivity(), AuthView { } private fun signIn() { - val user = User( - email = binding.emailEditText.value(), - password = binding.passwordEditText.value() - ) - viewModel.signIn(user) + with(binding) { + val user = User( + email = emailEditText.value(), + password = passwordEditText.value() + ) + viewModel.signIn(user) + } } // ViewModelListener From 072bbf6b0fd4d5272e0bc51fb20f160ce6ec59c2 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Thu, 4 Feb 2021 12:00:56 -0300 Subject: [PATCH 24/66] refactor when by if statement --- .../java/com/rootstrap/android/util/LoadingDialogUtil.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt b/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt index 7ce4de3..9dffa28 100644 --- a/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt +++ b/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt @@ -25,11 +25,9 @@ object LoadingDialogUtil { val builder = AlertDialog.Builder(context) builder.setTitle(context.getString(R.string.error)) - when (message) { - "" -> builder.setMessage(context.getString(R.string.generic_error)) - null -> builder.setMessage(context.getString(R.string.generic_error)) - else -> builder.setMessage(message) - } + if (message.isNullOrEmpty()) + builder.setMessage(context.getString(R.string.generic_error)) + else builder.setMessage(message) builder.setPositiveButton(context.getString(R.string.ok)) { dialog, _ -> dialog.cancel() From 0f097a1c6b78dcab61da053f30b193444dcc0a26 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Thu, 4 Feb 2021 12:03:09 -0300 Subject: [PATCH 25/66] kotlin scope function --- .../android/util/LoadingDialogUtil.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt b/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt index 9dffa28..810851f 100644 --- a/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt +++ b/app/src/main/java/com/rootstrap/android/util/LoadingDialogUtil.kt @@ -23,17 +23,20 @@ object LoadingDialogUtil { fun showError(message: String?, context: Context) { val builder = AlertDialog.Builder(context) - builder.setTitle(context.getString(R.string.error)) + with(builder) { + setTitle(context.getString(R.string.error)) - if (message.isNullOrEmpty()) - builder.setMessage(context.getString(R.string.generic_error)) - else builder.setMessage(message) + val showMessage = if (message.isNullOrEmpty()) + context.getString(R.string.generic_error) + else message - builder.setPositiveButton(context.getString(R.string.ok)) { dialog, _ -> - dialog.cancel() - } + setMessage(showMessage) - val dialog: AlertDialog = builder.create() - dialog.show() + setPositiveButton(context.getString(R.string.ok)) { dialog, _ -> + dialog.cancel() + } + val dialog: AlertDialog = create() + dialog.show() + } } } From 406e2230c04caf7c085a8ed08f684b2937de9dc1 Mon Sep 17 00:00:00 2001 From: Lucas Miotti Date: Thu, 25 Feb 2021 11:03:47 -0300 Subject: [PATCH 26/66] Replace ViewModel's listener with LiveData and Observers (#41) Co-authored-by: Lucas Miotti --- .../ui/activity/main/ProfileActivity.kt | 23 +++++------ .../activity/main/ProfileActivityViewModel.kt | 39 +++++++------------ .../ui/activity/main/SignInActivity.kt | 23 +++++------ .../activity/main/SignInActivityViewModel.kt | 36 +++++++---------- .../ui/activity/main/SignUpActivity.kt | 23 +++++------ .../activity/main/SignUpActivityViewModel.kt | 36 +++++++---------- .../android/ui/base/BaseViewModel.kt | 13 +++---- .../{ViewModelsHelper.kt => NetworkState.kt} | 5 --- 8 files changed, 76 insertions(+), 122 deletions(-) rename app/src/main/java/com/rootstrap/android/util/{ViewModelsHelper.kt => NetworkState.kt} (53%) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt index c8e85c2..cda11ed 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt @@ -1,6 +1,7 @@ package com.rootstrap.android.ui.activity.main import android.os.Bundle +import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R import com.rootstrap.android.metrics.Analytics @@ -10,7 +11,6 @@ import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.ProfileView import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelListener import kotlinx.android.synthetic.main.activity_profile.* class ProfileActivity : BaseActivity(), ProfileView { @@ -21,8 +21,7 @@ class ProfileActivity : BaseActivity(), ProfileView { super.onCreate(savedInstanceState) setContentView(R.layout.activity_profile) - val factory = ProfileActivityViewModelFactory(viewModelListener) - viewModel = ViewModelProvider(this, factory) + viewModel = ViewModelProvider(this) .get(ProfileActivityViewModel::class.java) Analytics.track(PageEvents.visit(VISIT_PROFILE)) @@ -31,29 +30,27 @@ class ProfileActivity : BaseActivity(), ProfileView { sign_out_button.setOnClickListener { viewModel.signOut() } lifecycle.addObserver(viewModel) + setObservers() } override fun goToFirstScreen() { startActivityClearTask(SignUpActivity()) } - // ViewModelListener - private val viewModelListener = object : ViewModelListener { - override fun updateState() { - when (viewModel.state) { + private fun setObservers() { + viewModel.state.observe(this, Observer { + when (it) { ProfileState.signOutFailure -> showError(viewModel.error) ProfileState.signOutSuccess -> goToFirstScreen() - else -> { - } } - } + }) - override fun updateNetworkState() { - when (viewModel.networkState) { + viewModel.networkState.observe(this, Observer { + when (it) { NetworkState.loading -> showProgress() NetworkState.idle -> hideProgress() else -> showError(viewModel.error ?: getString(R.string.default_error)) } - } + }) } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index 18d680c..6542d02 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -1,29 +1,32 @@ package com.rootstrap.android.ui.activity.main -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.rootstrap.android.network.managers.IUserManager import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException import kotlinx.coroutines.launch -open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { +open class ProfileActivityViewModel : BaseViewModel() { private val manager: IUserManager = UserManager + private val _state = MutableLiveData() + val state: LiveData + get() = _state + fun signOut() { - networkState = NetworkState.loading + _networkState.value = NetworkState.loading viewModelScope.launch { val result = manager.signOut() if (result.isSuccess) { - networkState = NetworkState.idle - state = ProfileState.signOutSuccess + _networkState.value = NetworkState.idle + _state.value = ProfileState.signOutSuccess SessionManager.signOut() } else { handleError(result.exceptionOrNull()) @@ -36,27 +39,13 @@ open class ProfileActivityViewModel(listener: ViewModelListener?) : BaseViewMode exception.message } else null - networkState = NetworkState.idle - networkState = NetworkState.error - state = ProfileState.signOutFailure + _networkState.value = NetworkState.idle + _networkState.value = NetworkState.error + _state.value = ProfileState.signOutFailure } - - var state: ProfileState = ProfileState.none - set(value) { - field = value - listener?.updateState() - } } enum class ProfileState { signOutFailure, - signOutSuccess, - none, -} - -class ProfileActivityViewModelFactory(var listener: ViewModelListener?) : - ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return ProfileActivityViewModel(listener) as T - } + signOutSuccess } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index cb7f323..fcbbc79 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -2,6 +2,7 @@ package com.rootstrap.android.ui.activity.main import android.Manifest import android.os.Bundle +import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignInBinding @@ -11,7 +12,6 @@ import com.rootstrap.android.metrics.VISIT_SIGN_IN import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.value import com.rootstrap.android.util.permissions.PermissionActivity import com.rootstrap.android.util.permissions.PermissionResponse @@ -28,8 +28,7 @@ class SignInActivity : PermissionActivity(), AuthView { setContentView(R.layout.activity_sign_in) Analytics.track(PageEvents.visit(VISIT_SIGN_IN)) - val factory = SignInActivityViewModelFactory(viewModelListener) - viewModel = ViewModelProvider(this, factory) + viewModel = ViewModelProvider(this) .get(SignInActivityViewModel::class.java) binding.signInButton.setOnClickListener { signIn() } @@ -37,6 +36,7 @@ class SignInActivity : PermissionActivity(), AuthView { lifecycle.addObserver(viewModel) sampleAskForPermission() + setObservers() } override fun showProfile() { @@ -53,24 +53,21 @@ class SignInActivity : PermissionActivity(), AuthView { } } - // ViewModelListener - private val viewModelListener = object : ViewModelListener { - override fun updateState() { - when (viewModel.state) { + private fun setObservers() { + viewModel.state.observe(this, Observer { + when (it) { SignInState.signInFailure -> showError(viewModel.error) SignInState.signInSuccess -> showProfile() - else -> { - } } - } + }) - override fun updateNetworkState() { - when (viewModel.networkState) { + viewModel.networkState.observe(this, Observer { + when (it) { NetworkState.loading -> showProgress() NetworkState.idle -> hideProgress() else -> showError(viewModel.error ?: getString(R.string.default_error)) } - } + }) } private fun sampleAskForPermission() { diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index c882c2a..e8dfa19 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -1,7 +1,7 @@ package com.rootstrap.android.ui.activity.main -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.rootstrap.android.network.managers.IUserManager import com.rootstrap.android.network.managers.SessionManager @@ -9,23 +9,20 @@ import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException import kotlinx.coroutines.launch -open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { +open class SignInActivityViewModel : BaseViewModel() { private val manager: IUserManager = UserManager - var state: SignInState = SignInState.none - set(value) { - field = value - listener?.updateState() - } + private val _state = MutableLiveData() + val state: LiveData + get() = _state fun signIn(user: User) { - networkState = NetworkState.loading + _networkState.value = NetworkState.loading viewModelScope.launch { val result = manager.signIn(user = user) if (result.isSuccess) { @@ -33,8 +30,8 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel SessionManager.signIn(user) } - networkState = NetworkState.idle - state = SignInState.signInSuccess + _networkState.value = NetworkState.idle + _state.value = SignInState.signInSuccess } else { handleError(result.exceptionOrNull()) } @@ -46,20 +43,13 @@ open class SignInActivityViewModel(listener: ViewModelListener?) : BaseViewModel exception.message } else null - networkState = NetworkState.idle - networkState = NetworkState.error - state = SignInState.signInFailure + _networkState.value = NetworkState.idle + _networkState.value = NetworkState.error + _state.value = SignInState.signInFailure } } enum class SignInState { signInFailure, - signInSuccess, - none, -} - -class SignInActivityViewModelFactory(var listener: ViewModelListener?) : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return SignInActivityViewModel(listener) as T - } + signInSuccess } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index 9f9f01f..d0d9e9d 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -2,6 +2,7 @@ package com.rootstrap.android.ui.activity.main import android.content.Intent import android.os.Bundle +import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignUpBinding @@ -12,7 +13,6 @@ import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.value class SignUpActivity : BaseActivity(), AuthView { @@ -27,8 +27,7 @@ class SignUpActivity : BaseActivity(), AuthView { setContentView(binding.root) Analytics.track(PageEvents.visit(VISIT_SIGN_UP)) - val factory = SignUpActivityViewModelFactory(viewModelListener) - viewModel = ViewModelProvider(this, factory) + viewModel = ViewModelProvider(this) .get(SignUpActivityViewModel::class.java) with(binding) { @@ -36,6 +35,7 @@ class SignUpActivity : BaseActivity(), AuthView { signInTextView.setOnClickListener { signIn() } } lifecycle.addObserver(viewModel) + setObservers() } override fun showProfile() { @@ -58,23 +58,20 @@ class SignUpActivity : BaseActivity(), AuthView { } } - // ViewModelListener - private val viewModelListener = object : ViewModelListener { - override fun updateState() { - when (viewModel.state) { + private fun setObservers() { + viewModel.state.observe(this, Observer { + when (it) { SignUpState.signUpFailure -> showError(viewModel.error) SignUpState.signUpSuccess -> showProfile() - else -> { - } } - } + }) - override fun updateNetworkState() { - when (viewModel.networkState) { + viewModel.networkState.observe(this, Observer { + when (it) { NetworkState.loading -> showProgress() NetworkState.idle -> hideProgress() else -> showError(viewModel.error ?: getString(R.string.default_error)) } - } + }) } } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index ec4348a..51bf07a 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -1,7 +1,7 @@ package com.rootstrap.android.ui.activity.main -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.rootstrap.android.network.managers.IUserManager import com.rootstrap.android.network.managers.SessionManager @@ -9,23 +9,20 @@ import com.rootstrap.android.network.managers.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelListener import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException import kotlinx.coroutines.launch -open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel(listener) { +open class SignUpActivityViewModel : BaseViewModel() { private val manager: IUserManager = UserManager - var state: SignUpState = SignUpState.none - set(value) { - field = value - listener?.updateState() - } + private val _state = MutableLiveData() + val state: LiveData + get() = _state fun signUp(user: User) { - networkState = NetworkState.loading + _networkState.value = NetworkState.loading viewModelScope.launch { val result = manager.signUp(user = user) @@ -34,8 +31,8 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel SessionManager.signIn(user) } - networkState = NetworkState.idle - state = SignUpState.signUpSuccess + _networkState.value = NetworkState.idle + _state.value = SignUpState.signUpSuccess } else { handleError(result.exceptionOrNull()) } @@ -47,20 +44,13 @@ open class SignUpActivityViewModel(listener: ViewModelListener?) : BaseViewModel exception.message } else null - networkState = NetworkState.idle - networkState = NetworkState.error - state = SignUpState.signUpFailure + _networkState.value = NetworkState.idle + _networkState.value = NetworkState.error + _state.value = SignUpState.signUpFailure } } enum class SignUpState { signUpFailure, - signUpSuccess, - none, -} - -class SignUpActivityViewModelFactory(var listener: ViewModelListener?) : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return SignUpActivityViewModel(listener) as T - } + signUpSuccess } diff --git a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt index 205b51a..6a4e921 100644 --- a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt @@ -2,24 +2,23 @@ package com.rootstrap.android.ui.base import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ViewModel import com.rootstrap.android.bus import com.rootstrap.android.util.NetworkState -import com.rootstrap.android.util.ViewModelListener /** * A [ViewModel] base class * implement app general LiveData as Session or User * **/ -open class BaseViewModel(var listener: ViewModelListener?) : ViewModel(), LifecycleObserver { +open class BaseViewModel : ViewModel(), LifecycleObserver { var error: String? = null - var networkState: NetworkState = NetworkState.idle - set(value) { - field = value - listener?.updateNetworkState() - } + protected val _networkState = MutableLiveData() + val networkState: LiveData + get() = _networkState @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun register() = bus.register(this) diff --git a/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt b/app/src/main/java/com/rootstrap/android/util/NetworkState.kt similarity index 53% rename from app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt rename to app/src/main/java/com/rootstrap/android/util/NetworkState.kt index eed1eb1..fa41682 100644 --- a/app/src/main/java/com/rootstrap/android/util/ViewModelsHelper.kt +++ b/app/src/main/java/com/rootstrap/android/util/NetworkState.kt @@ -1,10 +1,5 @@ package com.rootstrap.android.util -interface ViewModelListener { - fun updateState() - fun updateNetworkState() -} - enum class NetworkState { loading, idle, From 88e8da823350fed346a4f55d7836e1c4b40ff3db Mon Sep 17 00:00:00 2001 From: Lucas Miotti Date: Mon, 1 Mar 2021 09:32:44 -0300 Subject: [PATCH 27/66] Hilt (#42) * Hilt dependencies added, ViewModels are now injected in Activities * Utils and managers are now injectable * Fix tests. Injectable classes are now singleton * Fix for ktlint --- app/build.gradle | 15 +++++- .../com/rootstrap/android/CustomTestRunner.kt | 13 +++++ .../android/tests/ProfileActivityTest.kt | 9 ++-- .../android/tests/SignInActivityTest.kt | 6 ++- .../android/tests/SignUpActivityTest.kt | 6 ++- .../com/rootstrap/android/utils/BaseTests.kt | 25 ++++++--- .../main/java/com/rootstrap/android/App.kt | 20 +------ .../android/network/managers/ManagerModule.kt | 24 +++++++++ .../network/managers/SessionManager.kt | 34 ------------ .../managers/session/SessionManager.kt | 11 ++++ .../managers/session/SessionManagerImpl.kt | 35 +++++++++++++ .../{IUserManager.kt => user/UserManager.kt} | 4 +- .../UserManagerImpl.kt} | 16 ++---- .../network/providers/ServiceProvider.kt | 35 ------------- .../providers/ServiceProviderModule.kt | 52 +++++++++++++++++++ .../android/network/services/ApiModule.kt | 17 ++++++ .../services/AuthenticationInterceptor.kt | 5 +- .../network/services/ResponseInterceptor.kt | 12 +++-- .../ui/activity/main/ProfileActivity.kt | 16 +++--- .../activity/main/ProfileActivityViewModel.kt | 17 +++--- .../ui/activity/main/SignInActivity.kt | 13 +++-- .../activity/main/SignInActivityViewModel.kt | 17 +++--- .../ui/activity/main/SignUpActivity.kt | 9 ++-- .../activity/main/SignUpActivityViewModel.kt | 17 +++--- .../android/ui/base/BaseViewModel.kt | 6 +-- .../java/com/rootstrap/android/util/Prefs.kt | 7 +-- .../com/rootstrap/android/util/UtilModule.kt | 27 ++++++++++ build.gradle | 1 + 28 files changed, 294 insertions(+), 175 deletions(-) create mode 100644 app/src/androidTest/java/com/rootstrap/android/CustomTestRunner.kt create mode 100644 app/src/main/java/com/rootstrap/android/network/managers/ManagerModule.kt delete mode 100644 app/src/main/java/com/rootstrap/android/network/managers/SessionManager.kt create mode 100644 app/src/main/java/com/rootstrap/android/network/managers/session/SessionManager.kt create mode 100644 app/src/main/java/com/rootstrap/android/network/managers/session/SessionManagerImpl.kt rename app/src/main/java/com/rootstrap/android/network/managers/{IUserManager.kt => user/UserManager.kt} (81%) rename app/src/main/java/com/rootstrap/android/network/managers/{UserManager.kt => user/UserManagerImpl.kt} (61%) delete mode 100644 app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt create mode 100644 app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt create mode 100644 app/src/main/java/com/rootstrap/android/network/services/ApiModule.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/UtilModule.kt diff --git a/app/build.gradle b/app/build.gradle index f6e470e..9e2f006 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,6 +6,8 @@ apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' +apply plugin: 'dagger.hilt.android.plugin' + apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' @@ -19,7 +21,7 @@ android { targetSdkVersion 29 versionCode 35 versionName "1.0" - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + testInstrumentationRunner 'com.rootstrap.android.CustomTestRunner' } viewBinding { @@ -31,6 +33,10 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = '1.8' + } + signingConfigs { releaseConfig { keyAlias projectKeyAlias @@ -175,4 +181,11 @@ dependencies { implementation 'com.squareup:otto:1.3.8' //---- Linters ---- ktlint "com.pinterest:ktlint:0.35.0" + //---- Hilt ---- + implementation "com.google.dagger:hilt-android:2.28-alpha" + implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02' + kapt "com.google.dagger:hilt-android-compiler:2.28-alpha" + kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02' + androidTestImplementation 'com.google.dagger:hilt-android-testing:2.28-alpha' + kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.28-alpha' } diff --git a/app/src/androidTest/java/com/rootstrap/android/CustomTestRunner.kt b/app/src/androidTest/java/com/rootstrap/android/CustomTestRunner.kt new file mode 100644 index 0000000..7dfbf3d --- /dev/null +++ b/app/src/androidTest/java/com/rootstrap/android/CustomTestRunner.kt @@ -0,0 +1,13 @@ +package com.rootstrap.android + +import android.app.Application +import android.content.Context +import androidx.test.runner.AndroidJUnitRunner +import dagger.hilt.android.testing.HiltTestApplication + +class CustomTestRunner : AndroidJUnitRunner() { + + override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { + return super.newApplication(cl, HiltTestApplication::class.java.name, context) + } +} diff --git a/app/src/androidTest/java/com/rootstrap/android/tests/ProfileActivityTest.kt b/app/src/androidTest/java/com/rootstrap/android/tests/ProfileActivityTest.kt index 25089fe..54c03d4 100644 --- a/app/src/androidTest/java/com/rootstrap/android/tests/ProfileActivityTest.kt +++ b/app/src/androidTest/java/com/rootstrap/android/tests/ProfileActivityTest.kt @@ -5,10 +5,10 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.matcher.ViewMatchers.withId import com.rootstrap.android.R -import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.ui.activity.main.ProfileActivity import com.rootstrap.android.ui.activity.main.SignUpActivity import com.rootstrap.android.utils.BaseTests +import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest @@ -17,6 +17,7 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test +@HiltAndroidTest class ProfileActivityTest : BaseTests() { private lateinit var activity: ProfileActivity @@ -26,7 +27,7 @@ class ProfileActivityTest : BaseTests() { override fun before() { super.before() setServerDispatch(logoutDispatcher()) - setupSession() + sessionManager.user = testUser() scenario = ActivityScenario.launch(ProfileActivity::class.java) scenario.onActivity { activity -> this.activity = activity } } @@ -35,10 +36,10 @@ class ProfileActivityTest : BaseTests() { fun profileUiTest() { stringMatches( R.id.welcome_text_view, - activity.getString(R.string.welcome_message, SessionManager.user?.firstName) + activity.getString(R.string.welcome_message, sessionManager.user?.firstName) ) onView(withId(R.id.sign_out_button)).perform(click()) - assertEquals(null, SessionManager.user) + assertEquals(null, sessionManager.user) // Check if this activity was successful launched activity.runOnUiThread { diff --git a/app/src/androidTest/java/com/rootstrap/android/tests/SignInActivityTest.kt b/app/src/androidTest/java/com/rootstrap/android/tests/SignInActivityTest.kt index 2598c70..b36cfab 100644 --- a/app/src/androidTest/java/com/rootstrap/android/tests/SignInActivityTest.kt +++ b/app/src/androidTest/java/com/rootstrap/android/tests/SignInActivityTest.kt @@ -3,11 +3,11 @@ package com.rootstrap.android.tests import androidx.test.core.app.ActivityScenario import com.google.gson.Gson import com.rootstrap.android.R -import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.network.models.UserSerializer import com.rootstrap.android.ui.activity.main.ProfileActivity import com.rootstrap.android.ui.activity.main.SignInActivity import com.rootstrap.android.utils.BaseTests +import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest @@ -16,6 +16,7 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test +@HiltAndroidTest class SignInActivityTest : BaseTests() { private lateinit var activity: SignInActivity @@ -36,8 +37,9 @@ class SignInActivityTest : BaseTests() { typeText(R.id.email_edit_text, testUser.email) typeText(R.id.password_edit_text, testUser.password) performClick(R.id.sign_in_button) - val user = SessionManager.user + val user = sessionManager.user assertEquals(user, testUser) + activity.runOnUiThread { val current = currentActivity() assertEquals(ProfileActivity::class.java.name, current::class.java.name) diff --git a/app/src/androidTest/java/com/rootstrap/android/tests/SignUpActivityTest.kt b/app/src/androidTest/java/com/rootstrap/android/tests/SignUpActivityTest.kt index 09f8eff..8409a87 100644 --- a/app/src/androidTest/java/com/rootstrap/android/tests/SignUpActivityTest.kt +++ b/app/src/androidTest/java/com/rootstrap/android/tests/SignUpActivityTest.kt @@ -3,12 +3,12 @@ package com.rootstrap.android.tests import androidx.test.core.app.ActivityScenario import com.google.gson.Gson import com.rootstrap.android.R -import com.rootstrap.android.network.managers.SessionManager import com.rootstrap.android.network.models.UserSerializer import com.rootstrap.android.ui.activity.main.ProfileActivity import com.rootstrap.android.ui.activity.main.SignInActivity import com.rootstrap.android.ui.activity.main.SignUpActivity import com.rootstrap.android.utils.BaseTests +import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest @@ -17,6 +17,7 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test +@HiltAndroidTest class SignUpActivityTest : BaseTests() { private lateinit var activity: SignUpActivity @@ -39,8 +40,9 @@ class SignUpActivityTest : BaseTests() { scrollAndTypeText(R.id.email_edit_text, testUser.email) scrollAndTypeText(R.id.password_edit_text, testUser.password) scrollAndPerformClick(R.id.sign_up_button) - val user = SessionManager.user + val user = sessionManager.user assertEquals(user, testUser) + activity.runOnUiThread { val current = currentActivity() assertEquals(ProfileActivity::class.java.name, current::class.java.name) diff --git a/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt b/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt index 3fa93b6..2be338b 100644 --- a/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt +++ b/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt @@ -2,41 +2,50 @@ package com.rootstrap.android.utils import android.app.Activity import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.* // ktlint-disable no-wildcard-imports +import androidx.test.espresso.action.ViewActions.typeText +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.clearText +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry import androidx.test.runner.lifecycle.Stage -import com.rootstrap.android.network.managers.SessionManager -import com.rootstrap.android.network.managers.UserManager +import com.rootstrap.android.network.managers.session.SessionManager import com.rootstrap.android.network.models.User +import com.rootstrap.android.network.providers.ServiceProviderModule +import dagger.hilt.android.testing.HiltAndroidRule import okhttp3.mockwebserver.Dispatcher +import org.junit.Rule import org.junit.runner.RunWith +import javax.inject.Inject @RunWith(AndroidJUnit4ClassRunner::class) open class BaseTests { + @Inject lateinit var sessionManager: SessionManager + var mockServer: MockServer = MockServer + @get:Rule + var hiltRule = HiltAndroidRule(this) + open fun setServerDispatch(dispatcher: Dispatcher) { mockServer.server().dispatcher = dispatcher } open fun before() { mockServer.startServer() - UserManager.reloadService(mockServer.server().url("/").toString()) + ServiceProviderModule.URL_API = mockServer.server().url("/").toString() + hiltRule.inject() } open fun after() { mockServer.stopServer() } - open fun setupSession() { - SessionManager.user = testUser() - } - open fun testUser() = User( "9032", "user123@mail.com", diff --git a/app/src/main/java/com/rootstrap/android/App.kt b/app/src/main/java/com/rootstrap/android/App.kt index 82d646c..7ca19b3 100644 --- a/app/src/main/java/com/rootstrap/android/App.kt +++ b/app/src/main/java/com/rootstrap/android/App.kt @@ -3,30 +3,14 @@ package com.rootstrap.android import android.app.Application import com.rootstrap.android.metrics.Analytics import com.rootstrap.android.metrics.GoogleAnalytics -import com.rootstrap.android.util.Prefs -import com.squareup.otto.Bus - -val prefs: Prefs by lazy { - App.prefs!! -} - -val bus: Bus by lazy { - App.bus!! -} +import dagger.hilt.android.HiltAndroidApp +@HiltAndroidApp class App : Application() { - companion object { - var prefs: Prefs? = null - var bus: Bus? = null - } - override fun onCreate() { super.onCreate() - prefs = Prefs(applicationContext) - bus = Bus() - Analytics.addProvider(GoogleAnalytics(this)) // You need the api key in order to use MixPanel // Analytics.addProvider(MixPanelAnalytics(this)) diff --git a/app/src/main/java/com/rootstrap/android/network/managers/ManagerModule.kt b/app/src/main/java/com/rootstrap/android/network/managers/ManagerModule.kt new file mode 100644 index 0000000..6447fec --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/network/managers/ManagerModule.kt @@ -0,0 +1,24 @@ +package com.rootstrap.android.network.managers + +import com.rootstrap.android.network.managers.session.SessionManager +import com.rootstrap.android.network.managers.session.SessionManagerImpl +import com.rootstrap.android.network.managers.user.UserManager +import com.rootstrap.android.network.managers.user.UserManagerImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent +import javax.inject.Singleton + +@Module +@InstallIn(ApplicationComponent::class) +abstract class ManagerModule { + + @Binds + @Singleton + abstract fun bindSessionManager(sessionManagerImpl: SessionManagerImpl): SessionManager + + @Binds + @Singleton + abstract fun bindUserManager(userManagerImplImpl: UserManagerImpl): UserManager +} diff --git a/app/src/main/java/com/rootstrap/android/network/managers/SessionManager.kt b/app/src/main/java/com/rootstrap/android/network/managers/SessionManager.kt deleted file mode 100644 index b97e59f..0000000 --- a/app/src/main/java/com/rootstrap/android/network/managers/SessionManager.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.rootstrap.android.network.managers - -import com.rootstrap.android.network.models.User -import com.rootstrap.android.prefs - -object SessionManager { - - var user: User? = prefs.user - set(value) { - field = value - prefs.user = value - } - - fun addAuthenticationHeaders(accessToken: String, client: String, uid: String) { - prefs.accessToken = accessToken - prefs.client = client - prefs.uid = uid - } - - fun signOut() { - user = null - prefs.clear() - } - - fun signIn(user: User) { - this.user = user - prefs.user = user - prefs.signedIn = true - } - - fun isUserSignedIn(): Boolean { - return (user != null && prefs.signedIn) - } -} diff --git a/app/src/main/java/com/rootstrap/android/network/managers/session/SessionManager.kt b/app/src/main/java/com/rootstrap/android/network/managers/session/SessionManager.kt new file mode 100644 index 0000000..be8ed7a --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/network/managers/session/SessionManager.kt @@ -0,0 +1,11 @@ +package com.rootstrap.android.network.managers.session + +import com.rootstrap.android.network.models.User + +interface SessionManager { + var user: User? + fun addAuthenticationHeaders(accessToken: String, client: String, uid: String) + fun signOut() + fun signIn(user: User) + fun isUserSignedIn(): Boolean +} diff --git a/app/src/main/java/com/rootstrap/android/network/managers/session/SessionManagerImpl.kt b/app/src/main/java/com/rootstrap/android/network/managers/session/SessionManagerImpl.kt new file mode 100644 index 0000000..adb674c --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/network/managers/session/SessionManagerImpl.kt @@ -0,0 +1,35 @@ +package com.rootstrap.android.network.managers.session + +import com.rootstrap.android.network.models.User +import com.rootstrap.android.util.Prefs +import javax.inject.Inject + +class SessionManagerImpl @Inject constructor(private val prefs: Prefs) : SessionManager { + + override var user: User? = prefs.user + set(value) { + field = value + prefs.user = value + } + + override fun addAuthenticationHeaders(accessToken: String, client: String, uid: String) { + prefs.accessToken = accessToken + prefs.client = client + prefs.uid = uid + } + + override fun signOut() { + user = null + prefs.clear() + } + + override fun signIn(user: User) { + this.user = user + prefs.user = user + prefs.signedIn = true + } + + override fun isUserSignedIn(): Boolean { + return (user != null && prefs.signedIn) + } +} diff --git a/app/src/main/java/com/rootstrap/android/network/managers/IUserManager.kt b/app/src/main/java/com/rootstrap/android/network/managers/user/UserManager.kt similarity index 81% rename from app/src/main/java/com/rootstrap/android/network/managers/IUserManager.kt rename to app/src/main/java/com/rootstrap/android/network/managers/user/UserManager.kt index d30f5a6..a8e487a 100644 --- a/app/src/main/java/com/rootstrap/android/network/managers/IUserManager.kt +++ b/app/src/main/java/com/rootstrap/android/network/managers/user/UserManager.kt @@ -1,10 +1,10 @@ -package com.rootstrap.android.network.managers +package com.rootstrap.android.network.managers.user import com.rootstrap.android.network.models.User import com.rootstrap.android.network.models.UserSerializer import com.rootstrap.android.util.extensions.Data -interface IUserManager { +interface UserManager { suspend fun signUp(user: User): Result> suspend fun signIn(user: User): Result> suspend fun signOut(): Result> diff --git a/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt b/app/src/main/java/com/rootstrap/android/network/managers/user/UserManagerImpl.kt similarity index 61% rename from app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt rename to app/src/main/java/com/rootstrap/android/network/managers/user/UserManagerImpl.kt index 89632af..6c048d2 100644 --- a/app/src/main/java/com/rootstrap/android/network/managers/UserManager.kt +++ b/app/src/main/java/com/rootstrap/android/network/managers/user/UserManagerImpl.kt @@ -1,19 +1,16 @@ -package com.rootstrap.android.network.managers +package com.rootstrap.android.network.managers.user -import androidx.annotation.RestrictTo import com.rootstrap.android.network.models.User import com.rootstrap.android.network.models.UserSerializer -import com.rootstrap.android.network.providers.ServiceProvider import com.rootstrap.android.network.services.ApiService import com.rootstrap.android.util.extensions.ActionCallback import com.rootstrap.android.util.extensions.Data +import javax.inject.Inject /** - * Singleton Object + * Singleton class * */ -object UserManager : IUserManager { - - private var service = ServiceProvider.create(ApiService::class.java) +class UserManagerImpl @Inject constructor(private val service: ApiService) : UserManager { override suspend fun signUp(user: User): Result> = ActionCallback.call(service.signUp(UserSerializer(user))) @@ -23,9 +20,4 @@ object UserManager : IUserManager { override suspend fun signOut(): Result> = ActionCallback.call(service.signOut()) - - @RestrictTo(RestrictTo.Scope.TESTS) - fun reloadService(url: String) { - service = ServiceProvider.create(ApiService::class.java, url) - } } diff --git a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt deleted file mode 100644 index 16f9890..0000000 --- a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProvider.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.rootstrap.android.network.providers - -import com.rootstrap.android.BuildConfig -import com.rootstrap.android.network.services.AuthenticationInterceptor -import com.rootstrap.android.network.services.HeadersInterceptor -import com.rootstrap.android.network.services.ResponseInterceptor -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory - -object ServiceProvider { - - private var URL_API: String? = null - - private fun build(): Retrofit { - val client = OkHttpClient.Builder() - .addInterceptor(HeadersInterceptor()) - .addInterceptor(AuthenticationInterceptor()) - .addInterceptor(ResponseInterceptor()) - .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) - .build() - - return Retrofit.Builder() - .baseUrl(URL_API) - .addConverterFactory(MoshiConverterFactory.create().withNullSerialization()) - .client(client) - .build() - } - - fun create(klass: Class, url: String? = BuildConfig.API_URL): T { - URL_API = url - return build().create(klass) - } -} diff --git a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt new file mode 100644 index 0000000..63de6ea --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt @@ -0,0 +1,52 @@ +package com.rootstrap.android.network.providers + +import com.rootstrap.android.BuildConfig +import com.rootstrap.android.network.services.AuthenticationInterceptor +import com.rootstrap.android.network.services.HeadersInterceptor +import com.rootstrap.android.network.services.ResponseInterceptor +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory +import javax.inject.Singleton + +@Module +@InstallIn(ApplicationComponent::class) +class ServiceProviderModule { + + @Provides + @Singleton + fun provideOkHttpClient( + authenticationInterceptor: AuthenticationInterceptor, + responseInterceptor: ResponseInterceptor + ): OkHttpClient { + val httpInterceptorLevel = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY + else HttpLoggingInterceptor.Level.BASIC + + return OkHttpClient.Builder() + .addInterceptor(HeadersInterceptor()) + .addInterceptor(authenticationInterceptor) + .addInterceptor(responseInterceptor) + .addInterceptor(HttpLoggingInterceptor().setLevel(httpInterceptorLevel)) + .build() + } + + @Provides + @Singleton + fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { + val url = URL_API ?: BuildConfig.API_URL + return Retrofit.Builder() + .baseUrl(url) + .addConverterFactory(MoshiConverterFactory.create().withNullSerialization()) + .client(okHttpClient) + .build() + } + + companion object { + var URL_API: String? = null + } +} diff --git a/app/src/main/java/com/rootstrap/android/network/services/ApiModule.kt b/app/src/main/java/com/rootstrap/android/network/services/ApiModule.kt new file mode 100644 index 0000000..53c844a --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/network/services/ApiModule.kt @@ -0,0 +1,17 @@ +package com.rootstrap.android.network.services + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent +import retrofit2.Retrofit + +@Module +@InstallIn(ApplicationComponent::class) +class ApiModule { + + @Provides + fun provideApiService(retrofit: Retrofit): ApiService { + return retrofit.create(ApiService::class.java) + } +} diff --git a/app/src/main/java/com/rootstrap/android/network/services/AuthenticationInterceptor.kt b/app/src/main/java/com/rootstrap/android/network/services/AuthenticationInterceptor.kt index 6aa6bc8..af7666b 100644 --- a/app/src/main/java/com/rootstrap/android/network/services/AuthenticationInterceptor.kt +++ b/app/src/main/java/com/rootstrap/android/network/services/AuthenticationInterceptor.kt @@ -1,11 +1,12 @@ package com.rootstrap.android.network.services -import com.rootstrap.android.prefs +import com.rootstrap.android.util.Prefs import okhttp3.Interceptor import okhttp3.Response import java.io.IOException +import javax.inject.Inject -class AuthenticationInterceptor : Interceptor { +class AuthenticationInterceptor @Inject constructor(private val prefs: Prefs) : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { diff --git a/app/src/main/java/com/rootstrap/android/network/services/ResponseInterceptor.kt b/app/src/main/java/com/rootstrap/android/network/services/ResponseInterceptor.kt index e573244..9d7e252 100644 --- a/app/src/main/java/com/rootstrap/android/network/services/ResponseInterceptor.kt +++ b/app/src/main/java/com/rootstrap/android/network/services/ResponseInterceptor.kt @@ -1,12 +1,16 @@ package com.rootstrap.android.network.services -import com.rootstrap.android.network.managers.SessionManager -import com.rootstrap.android.prefs +import com.rootstrap.android.network.managers.session.SessionManager +import com.rootstrap.android.util.Prefs import okhttp3.Interceptor import okhttp3.Response import java.io.IOException +import javax.inject.Inject -class ResponseInterceptor : Interceptor { +class ResponseInterceptor @Inject constructor( + private val prefs: Prefs, + private val sessionManager: SessionManager +) : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { @@ -20,7 +24,7 @@ class ResponseInterceptor : Interceptor { val client = response.header(prefs.CLIENT) val uid = response.header(prefs.UID) if (preferValid(accessToken, client, uid)) - SessionManager.addAuthenticationHeaders(accessToken!!, client!!, uid!!) + sessionManager.addAuthenticationHeaders(accessToken!!, client!!, uid!!) } private fun preferValid(accessToken: String?, client: String?, uid: String?): Boolean { diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt index cda11ed..7c92979 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt @@ -1,32 +1,34 @@ package com.rootstrap.android.ui.activity.main import android.os.Bundle +import androidx.activity.viewModels import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R import com.rootstrap.android.metrics.Analytics import com.rootstrap.android.metrics.PageEvents import com.rootstrap.android.metrics.VISIT_PROFILE -import com.rootstrap.android.network.managers.SessionManager +import com.rootstrap.android.network.managers.session.SessionManager import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.ProfileView import com.rootstrap.android.util.NetworkState +import dagger.hilt.android.AndroidEntryPoint import kotlinx.android.synthetic.main.activity_profile.* +import javax.inject.Inject +@AndroidEntryPoint class ProfileActivity : BaseActivity(), ProfileView { - private lateinit var viewModel: ProfileActivityViewModel + @Inject lateinit var sessionManager: SessionManager + + private val viewModel: ProfileActivityViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_profile) - viewModel = ViewModelProvider(this) - .get(ProfileActivityViewModel::class.java) - Analytics.track(PageEvents.visit(VISIT_PROFILE)) - welcome_text_view.text = getString(R.string.welcome_message, SessionManager.user?.firstName) + welcome_text_view.text = getString(R.string.welcome_message, sessionManager.user?.firstName) sign_out_button.setOnClickListener { viewModel.signOut() } lifecycle.addObserver(viewModel) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index 6542d02..7fdf3ec 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -1,20 +1,21 @@ package com.rootstrap.android.ui.activity.main +import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope -import com.rootstrap.android.network.managers.IUserManager -import com.rootstrap.android.network.managers.SessionManager -import com.rootstrap.android.network.managers.UserManager +import com.rootstrap.android.network.managers.session.SessionManager +import com.rootstrap.android.network.managers.user.UserManager import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException import kotlinx.coroutines.launch -open class ProfileActivityViewModel : BaseViewModel() { - - private val manager: IUserManager = UserManager +open class ProfileActivityViewModel @ViewModelInject constructor( + private val sessionManager: SessionManager, + private val userManager: UserManager +) : BaseViewModel() { private val _state = MutableLiveData() val state: LiveData @@ -23,11 +24,11 @@ open class ProfileActivityViewModel : BaseViewModel() { fun signOut() { _networkState.value = NetworkState.loading viewModelScope.launch { - val result = manager.signOut() + val result = userManager.signOut() if (result.isSuccess) { _networkState.value = NetworkState.idle _state.value = ProfileState.signOutSuccess - SessionManager.signOut() + sessionManager.signOut() } else { handleError(result.exceptionOrNull()) } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index fcbbc79..fdf09a0 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -2,8 +2,8 @@ package com.rootstrap.android.ui.activity.main import android.Manifest import android.os.Bundle +import androidx.activity.viewModels import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignInBinding import com.rootstrap.android.metrics.Analytics @@ -15,28 +15,27 @@ import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.value import com.rootstrap.android.util.permissions.PermissionActivity import com.rootstrap.android.util.permissions.PermissionResponse +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class SignInActivity : PermissionActivity(), AuthView { - private lateinit var viewModel: SignInActivityViewModel + private val viewModel: SignInActivityViewModel by viewModels() private lateinit var binding: ActivitySignInBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivitySignInBinding.inflate(layoutInflater) - setContentView(R.layout.activity_sign_in) + setContentView(binding.root) Analytics.track(PageEvents.visit(VISIT_SIGN_IN)) - viewModel = ViewModelProvider(this) - .get(SignInActivityViewModel::class.java) - binding.signInButton.setOnClickListener { signIn() } lifecycle.addObserver(viewModel) - sampleAskForPermission() setObservers() + sampleAskForPermission() } override fun showProfile() { diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index e8dfa19..c3783ac 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -1,11 +1,11 @@ package com.rootstrap.android.ui.activity.main +import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope -import com.rootstrap.android.network.managers.IUserManager -import com.rootstrap.android.network.managers.SessionManager -import com.rootstrap.android.network.managers.UserManager +import com.rootstrap.android.network.managers.session.SessionManager +import com.rootstrap.android.network.managers.user.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState @@ -13,9 +13,10 @@ import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException import kotlinx.coroutines.launch -open class SignInActivityViewModel : BaseViewModel() { - - private val manager: IUserManager = UserManager +open class SignInActivityViewModel @ViewModelInject constructor( + private val sessionManager: SessionManager, + private val userManager: UserManager +) : BaseViewModel() { private val _state = MutableLiveData() val state: LiveData @@ -24,10 +25,10 @@ open class SignInActivityViewModel : BaseViewModel() { fun signIn(user: User) { _networkState.value = NetworkState.loading viewModelScope.launch { - val result = manager.signIn(user = user) + val result = userManager.signIn(user = user) if (result.isSuccess) { result.getOrNull()?.value?.user?.let { user -> - SessionManager.signIn(user) + sessionManager.signIn(user) } _networkState.value = NetworkState.idle diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index d0d9e9d..83ef2f4 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -2,8 +2,8 @@ package com.rootstrap.android.ui.activity.main import android.content.Intent import android.os.Bundle +import androidx.activity.viewModels import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignUpBinding import com.rootstrap.android.metrics.Analytics @@ -14,10 +14,12 @@ import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.value +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class SignUpActivity : BaseActivity(), AuthView { - private lateinit var viewModel: SignUpActivityViewModel + private val viewModel: SignUpActivityViewModel by viewModels() private lateinit var binding: ActivitySignUpBinding override fun onCreate(savedInstanceState: Bundle?) { @@ -27,9 +29,6 @@ class SignUpActivity : BaseActivity(), AuthView { setContentView(binding.root) Analytics.track(PageEvents.visit(VISIT_SIGN_UP)) - viewModel = ViewModelProvider(this) - .get(SignUpActivityViewModel::class.java) - with(binding) { signUpButton.setOnClickListener { signUp() } signInTextView.setOnClickListener { signIn() } diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index 51bf07a..47ae75f 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -1,11 +1,11 @@ package com.rootstrap.android.ui.activity.main +import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope -import com.rootstrap.android.network.managers.IUserManager -import com.rootstrap.android.network.managers.SessionManager -import com.rootstrap.android.network.managers.UserManager +import com.rootstrap.android.network.managers.session.SessionManager +import com.rootstrap.android.network.managers.user.UserManager import com.rootstrap.android.network.models.User import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState @@ -13,9 +13,10 @@ import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException import kotlinx.coroutines.launch -open class SignUpActivityViewModel : BaseViewModel() { - - private val manager: IUserManager = UserManager +open class SignUpActivityViewModel @ViewModelInject constructor( + private val sessionManager: SessionManager, + private val userManager: UserManager +) : BaseViewModel() { private val _state = MutableLiveData() val state: LiveData @@ -24,11 +25,11 @@ open class SignUpActivityViewModel : BaseViewModel() { fun signUp(user: User) { _networkState.value = NetworkState.loading viewModelScope.launch { - val result = manager.signUp(user = user) + val result = userManager.signUp(user = user) if (result.isSuccess) { result.getOrNull()?.value?.user?.let { user -> - SessionManager.signIn(user) + sessionManager.signIn(user) } _networkState.value = NetworkState.idle diff --git a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt index 6a4e921..5cda3e2 100644 --- a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt @@ -6,8 +6,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ViewModel -import com.rootstrap.android.bus import com.rootstrap.android.util.NetworkState +import com.squareup.otto.Bus /** * A [ViewModel] base class @@ -21,8 +21,8 @@ open class BaseViewModel : ViewModel(), LifecycleObserver { get() = _networkState @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun register() = bus.register(this) + fun register() = Bus().register(this) @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) - fun unregister() = bus.unregister(this) + fun unregister() = Bus().unregister(this) } diff --git a/app/src/main/java/com/rootstrap/android/util/Prefs.kt b/app/src/main/java/com/rootstrap/android/util/Prefs.kt index be6f349..7230177 100644 --- a/app/src/main/java/com/rootstrap/android/util/Prefs.kt +++ b/app/src/main/java/com/rootstrap/android/util/Prefs.kt @@ -1,13 +1,12 @@ package com.rootstrap.android.util -import android.content.Context import android.content.SharedPreferences -import androidx.preference.PreferenceManager import com.google.gson.Gson import com.rootstrap.android.network.models.User import com.rootstrap.android.util.extensions.fromJson +import javax.inject.Inject -class Prefs(context: Context) { +class Prefs @Inject constructor(private val prefs: SharedPreferences) { val ACCESS_TOKEN = "access-token" val CLIENT = "Client" @@ -15,8 +14,6 @@ class Prefs(context: Context) { val USER = "user" val SIGNED_IN = "signed_in" - val prefs: SharedPreferences = PreferenceManager - .getDefaultSharedPreferences(context) private val gson: Gson = Gson() var accessToken: String diff --git a/app/src/main/java/com/rootstrap/android/util/UtilModule.kt b/app/src/main/java/com/rootstrap/android/util/UtilModule.kt new file mode 100644 index 0000000..0a8a516 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/UtilModule.kt @@ -0,0 +1,27 @@ +package com.rootstrap.android.util + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceManager +import com.squareup.otto.Bus +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Singleton + +@Module +@InstallIn(ApplicationComponent::class) +class UtilModule { + + @Provides + @Singleton + fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences { + return PreferenceManager.getDefaultSharedPreferences(context) + } + + @Provides + @Singleton + fun provideBus(): Bus = Bus() +} diff --git a/build.gradle b/build.gradle index 991aa38..8f768b0 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.4' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1' + classpath "com.google.dagger:hilt-android-gradle-plugin:2.28-alpha" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } From 3463dbfe9eb976c8529956b2cffb914e96a2ad1d Mon Sep 17 00:00:00 2001 From: Marcos Salto Date: Tue, 2 Mar 2021 20:14:03 -0300 Subject: [PATCH 28/66] Add KotlinJsonAdapterFactory to Moshi --- app/build.gradle | 2 +- .../android/network/providers/ServiceProviderModule.kt | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9e2f006..a52e719 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -174,7 +174,7 @@ dependencies { implementation group: 'com.github.bumptech.glide', name: 'glide', version: '4.10.0' //---- Network ---- implementation 'com.squareup.retrofit2:retrofit:2.6.2' - implementation 'com.squareup.moshi:moshi:1.8.0' + implementation 'com.squareup.moshi:moshi-kotlin:1.9.2' implementation 'com.squareup.retrofit2:converter-moshi:2.5.0' implementation 'com.squareup.okhttp3:logging-interceptor:4.3.1' //---- Events ---- diff --git a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt index 63de6ea..8665732 100644 --- a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt +++ b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt @@ -4,10 +4,12 @@ import com.rootstrap.android.BuildConfig import com.rootstrap.android.network.services.AuthenticationInterceptor import com.rootstrap.android.network.services.HeadersInterceptor import com.rootstrap.android.network.services.ResponseInterceptor +import com.squareup.moshi.Moshi import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ApplicationComponent +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit @@ -39,9 +41,12 @@ class ServiceProviderModule { @Singleton fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { val url = URL_API ?: BuildConfig.API_URL + val moshi = Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .build() return Retrofit.Builder() .baseUrl(url) - .addConverterFactory(MoshiConverterFactory.create().withNullSerialization()) + .addConverterFactory(MoshiConverterFactory.create(moshi).withNullSerialization()) .client(okHttpClient) .build() } From 3d4da68092066ece60f004586b506a6e192c473b Mon Sep 17 00:00:00 2001 From: Amaury Date: Wed, 3 Mar 2021 11:51:58 -0300 Subject: [PATCH 29/66] Pull request template. --- pull_request_template.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 pull_request_template.md diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 0000000..a3f38f6 --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,27 @@ +#### ISSUE[#] +* Issue title + +--- + +#### Description +* + +--- + +#### Tasks +* + +--- + +#### Risk +* + +--- + +#### Notes +* + +--- + +#### Preview +* Screen shots From d62a5b36b1113885b4f30987efe1d877c65029f9 Mon Sep 17 00:00:00 2001 From: Amaury Ricardo Date: Wed, 10 Mar 2021 18:22:46 -0300 Subject: [PATCH 30/66] secure preferences --- .idea/misc.xml | 2 +- .idea/runConfigurations.xml | 1 + app/build.gradle | 11 +++++++- .../com/rootstrap/android/util/UtilModule.kt | 28 +++++++++++++++++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 3378229..7c7f635 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,7 +5,7 @@ - + diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml index 7f68460..e497da9 100644 --- a/.idea/runConfigurations.xml +++ b/.idea/runConfigurations.xml @@ -3,6 +3,7 @@ diff --git a/app/build.gradle b/app/build.gradle index 4074a58..7e98361 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,8 +4,6 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' - apply plugin: 'dagger.hilt.android.plugin' apply plugin: 'com.google.gms.google-services' @@ -13,26 +11,26 @@ apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' android { - compileSdkVersion 29 - dataBinding.enabled = true + compileSdkVersion 31 defaultConfig { applicationId "com.rootstrap.android" minSdkVersion 23 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 42 versionName "1.0" testInstrumentationRunner 'com.rootstrap.android.CustomTestRunner' } - viewBinding { - enabled = true - } - compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + buildFeatures { + viewBinding true + dataBinding true + } + kotlinOptions { jvmTarget = '1.8' } @@ -58,6 +56,10 @@ android { } } + kapt { + correctErrorTypes true + } + flavorDimensions "server" productFlavors { @@ -138,22 +140,21 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1' - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation "androidx.preference:preference-ktx:1.1.1" - implementation 'com.google.android.material:material:1.2.1' + implementation 'com.google.android.material:material:1.4.0' testImplementation 'junit:junit:4.13.1' testImplementation 'org.mockito:mockito-core:2.28.2' - androidTestImplementation 'androidx.test:runner:1.3.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' - androidTestImplementation 'androidx.test:rules:1.3.0' + androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.3.1' //---- ANDROID ARCH ROOM ---- implementation 'android.arch.persistence.room:runtime:1.1.1' @@ -161,21 +162,21 @@ dependencies { //---- ANDROID ARCH LIFECYCLE ---- implementation 'android.arch.lifecycle:common-java8:1.1.1' kapt "android.arch.lifecycle:compiler:1.1.1" - implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1" //---- GOOGLE JSON SERIALIZER/DESERIALIZER ---- implementation 'com.google.code.gson:gson:2.8.6' //---- MixPanel ---- implementation 'com.mixpanel.android:mixpanel-android:5.6.1' //---- Firebase ---- - implementation platform('com.google.firebase:firebase-bom:26.1.0') - implementation 'com.google.firebase:firebase-core:18.0.2' + implementation platform('com.google.firebase:firebase-bom:28.4.2') + implementation 'com.google.firebase:firebase-core:19.0.2' implementation 'com.google.firebase:firebase-analytics-ktx' implementation 'com.google.firebase:firebase-crashlytics-ktx' - implementation 'org.jetbrains.kotlin:kotlin-reflect:1.4.10' + implementation 'org.jetbrains.kotlin:kotlin-reflect:1.5.31' //---- Image ---- implementation group: 'com.github.bumptech.glide', name: 'glide', version: '4.10.0' //---- Network ---- @@ -188,12 +189,12 @@ dependencies { //---- Linters ---- ktlint "com.pinterest:ktlint:0.35.0" //---- Hilt ---- - implementation "com.google.dagger:hilt-android:2.28-alpha" - implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02' - kapt "com.google.dagger:hilt-android-compiler:2.28-alpha" - kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02' - androidTestImplementation 'com.google.dagger:hilt-android-testing:2.28-alpha' - kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.28-alpha' + implementation "com.google.dagger:hilt-android:$hilt_version" + implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03' + kapt "com.google.dagger:hilt-android-compiler:$hilt_version" + kapt 'androidx.hilt:hilt-compiler:1.0.0' + androidTestImplementation "com.google.dagger:hilt-android-testing:$hilt_version" + kaptAndroidTest "com.google.dagger:hilt-android-compiler:$hilt_version" //security crypto implementation "androidx.security:security-crypto:1.1.0-alpha03" diff --git a/app/src/main/java/com/rootstrap/android/network/managers/ManagerModule.kt b/app/src/main/java/com/rootstrap/android/network/managers/ManagerModule.kt index 6447fec..2da2571 100644 --- a/app/src/main/java/com/rootstrap/android/network/managers/ManagerModule.kt +++ b/app/src/main/java/com/rootstrap/android/network/managers/ManagerModule.kt @@ -7,11 +7,11 @@ import com.rootstrap.android.network.managers.user.UserManagerImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn -import dagger.hilt.android.components.ApplicationComponent +import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module -@InstallIn(ApplicationComponent::class) +@InstallIn(SingletonComponent::class) abstract class ManagerModule { @Binds diff --git a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt index 8665732..c7bc33c 100644 --- a/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt +++ b/app/src/main/java/com/rootstrap/android/network/providers/ServiceProviderModule.kt @@ -8,8 +8,8 @@ import com.squareup.moshi.Moshi import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ApplicationComponent import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import dagger.hilt.components.SingletonComponent import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit @@ -17,7 +17,7 @@ import retrofit2.converter.moshi.MoshiConverterFactory import javax.inject.Singleton @Module -@InstallIn(ApplicationComponent::class) +@InstallIn(SingletonComponent::class) class ServiceProviderModule { @Provides diff --git a/app/src/main/java/com/rootstrap/android/network/services/ApiModule.kt b/app/src/main/java/com/rootstrap/android/network/services/ApiModule.kt index 53c844a..66b0593 100644 --- a/app/src/main/java/com/rootstrap/android/network/services/ApiModule.kt +++ b/app/src/main/java/com/rootstrap/android/network/services/ApiModule.kt @@ -3,11 +3,11 @@ package com.rootstrap.android.network.services import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ApplicationComponent +import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit @Module -@InstallIn(ApplicationComponent::class) +@InstallIn(SingletonComponent::class) class ApiModule { @Provides diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt index 7c92979..3999b3b 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import androidx.activity.viewModels import androidx.lifecycle.Observer import com.rootstrap.android.R +import com.rootstrap.android.databinding.ActivityProfileBinding import com.rootstrap.android.metrics.Analytics import com.rootstrap.android.metrics.PageEvents import com.rootstrap.android.metrics.VISIT_PROFILE @@ -12,7 +13,6 @@ import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.ProfileView import com.rootstrap.android.util.NetworkState import dagger.hilt.android.AndroidEntryPoint -import kotlinx.android.synthetic.main.activity_profile.* import javax.inject.Inject @AndroidEntryPoint @@ -21,15 +21,18 @@ class ProfileActivity : BaseActivity(), ProfileView { @Inject lateinit var sessionManager: SessionManager private val viewModel: ProfileActivityViewModel by viewModels() + private val binding by lazy { ActivityProfileBinding.inflate(layoutInflater) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_profile) Analytics.track(PageEvents.visit(VISIT_PROFILE)) - welcome_text_view.text = getString(R.string.welcome_message, sessionManager.user?.firstName) - sign_out_button.setOnClickListener { viewModel.signOut() } + with(binding) { + setContentView(root) + welcomeTextView.text = getString(R.string.welcome_message, sessionManager.user?.firstName) + signOutButton.setOnClickListener { viewModel.signOut() } + } lifecycle.addObserver(viewModel) setObservers() diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt index 7fdf3ec..e8a7a04 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt @@ -1,6 +1,5 @@ package com.rootstrap.android.ui.activity.main -import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -10,9 +9,12 @@ import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import javax.inject.Inject -open class ProfileActivityViewModel @ViewModelInject constructor( +@HiltViewModel +open class ProfileActivityViewModel @Inject constructor( private val sessionManager: SessionManager, private val userManager: UserManager ) : BaseViewModel() { diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt index fdf09a0..964581c 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt @@ -21,11 +21,12 @@ import dagger.hilt.android.AndroidEntryPoint class SignInActivity : PermissionActivity(), AuthView { private val viewModel: SignInActivityViewModel by viewModels() - private lateinit var binding: ActivitySignInBinding + private val binding: ActivitySignInBinding by lazy { + ActivitySignInBinding.inflate(layoutInflater) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivitySignInBinding.inflate(layoutInflater) setContentView(binding.root) Analytics.track(PageEvents.visit(VISIT_SIGN_IN)) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt index c3783ac..d8b16a6 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt @@ -1,6 +1,5 @@ package com.rootstrap.android.ui.activity.main -import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -11,9 +10,12 @@ import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import javax.inject.Inject -open class SignInActivityViewModel @ViewModelInject constructor( +@HiltViewModel +open class SignInActivityViewModel @Inject constructor( private val sessionManager: SessionManager, private val userManager: UserManager ) : BaseViewModel() { diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt index 83ef2f4..8f75729 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt @@ -20,11 +20,12 @@ import dagger.hilt.android.AndroidEntryPoint class SignUpActivity : BaseActivity(), AuthView { private val viewModel: SignUpActivityViewModel by viewModels() - private lateinit var binding: ActivitySignUpBinding + private val binding: ActivitySignUpBinding by lazy { + ActivitySignUpBinding.inflate(layoutInflater) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivitySignUpBinding.inflate(layoutInflater) setContentView(binding.root) Analytics.track(PageEvents.visit(VISIT_SIGN_UP)) diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt index 47ae75f..c571d58 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt @@ -1,6 +1,5 @@ package com.rootstrap.android.ui.activity.main -import androidx.hilt.lifecycle.ViewModelInject import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -11,9 +10,12 @@ import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.ApiErrorType import com.rootstrap.android.util.extensions.ApiException +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import javax.inject.Inject -open class SignUpActivityViewModel @ViewModelInject constructor( +@HiltViewModel +open class SignUpActivityViewModel @Inject constructor( private val sessionManager: SessionManager, private val userManager: UserManager ) : BaseViewModel() { diff --git a/app/src/main/java/com/rootstrap/android/util/UtilModule.kt b/app/src/main/java/com/rootstrap/android/util/UtilModule.kt index 3038e42..39a1245 100644 --- a/app/src/main/java/com/rootstrap/android/util/UtilModule.kt +++ b/app/src/main/java/com/rootstrap/android/util/UtilModule.kt @@ -11,12 +11,12 @@ import com.squareup.otto.Bus import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ApplicationComponent import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module -@InstallIn(ApplicationComponent::class) +@InstallIn(SingletonComponent::class) class UtilModule { @Provides diff --git a/build.gradle b/build.gradle index 8f768b0..924ac52 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,8 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.72' + ext.kotlin_version = '1.5.31' + ext.hilt_version = '2.39.1' repositories { google() jcenter() @@ -11,11 +12,11 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:4.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.4' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1' - classpath "com.google.dagger:hilt-android-gradle-plugin:2.28-alpha" + classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' + classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } From 9242d49fdcf4434b7604523756c739ed87c256e6 Mon Sep 17 00:00:00 2001 From: Rodrigo Soria Date: Wed, 3 Nov 2021 13:16:00 -0300 Subject: [PATCH 35/66] Update Lifecycle and Room to AndroidX. Fix manifest merger targetting api 31. Update Firebase --- app/build.gradle | 40 +++++++++++++++++++++----------- app/src/main/AndroidManifest.xml | 3 ++- build.gradle | 2 +- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7e98361..576eec3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -139,10 +139,13 @@ android { } dependencies { + def room_version = "2.3.0" + def lifecycle_version = "2.4.0" + implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0' implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation "androidx.preference:preference-ktx:1.1.1" @@ -156,41 +159,50 @@ dependencies { androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.3.1' - //---- ANDROID ARCH ROOM ---- - implementation 'android.arch.persistence.room:runtime:1.1.1' - kapt "android.arch.persistence.room:compiler:1.1.1" - //---- ANDROID ARCH LIFECYCLE ---- - implementation 'android.arch.lifecycle:common-java8:1.1.1' - kapt "android.arch.lifecycle:compiler:1.1.1" - implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" + + //---- ROOM ---- + implementation "androidx.room:room-runtime:$room_version" + kapt "androidx.room:room-compiler:$room_version" + // Kotlin Extensions and Coroutines support for Room + implementation "androidx.room:room-ktx:$room_version" + + //---- LIFECYCLE ----] + implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" //---- GOOGLE JSON SERIALIZER/DESERIALIZER ---- implementation 'com.google.code.gson:gson:2.8.6' + //---- MixPanel ---- implementation 'com.mixpanel.android:mixpanel-android:5.6.1' + //---- Firebase ---- implementation platform('com.google.firebase:firebase-bom:28.4.2') - implementation 'com.google.firebase:firebase-core:19.0.2' + implementation 'com.google.firebase:firebase-core:20.0.0' implementation 'com.google.firebase:firebase-analytics-ktx' implementation 'com.google.firebase:firebase-crashlytics-ktx' implementation 'org.jetbrains.kotlin:kotlin-reflect:1.5.31' + //---- Image ---- implementation group: 'com.github.bumptech.glide', name: 'glide', version: '4.10.0' + //---- Network ---- implementation 'com.squareup.retrofit2:retrofit:2.6.2' - implementation 'com.squareup.moshi:moshi-kotlin:1.9.2' + implementation 'com.squareup.moshi:moshi-kotlin:1.12.0' implementation 'com.squareup.retrofit2:converter-moshi:2.5.0' implementation 'com.squareup.okhttp3:logging-interceptor:4.3.1' + //---- Events ---- implementation 'com.squareup:otto:1.3.8' + //---- Linters ---- ktlint "com.pinterest:ktlint:0.35.0" + //---- Hilt ---- implementation "com.google.dagger:hilt-android:$hilt_version" - implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03' kapt "com.google.dagger:hilt-android-compiler:$hilt_version" kapt 'androidx.hilt:hilt-compiler:1.0.0' androidTestImplementation "com.google.dagger:hilt-android-testing:$hilt_version" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b16d794..9f08fae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,7 +17,8 @@ + android:screenOrientation="portrait" + android:exported="true"> diff --git a/build.gradle b/build.gradle index 924ac52..ea46473 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { classpath 'com.android.tools.build:gradle:4.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.10' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.0' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From e5642b44bc1483f0385e15ef784ea402b22ccf1f Mon Sep 17 00:00:00 2001 From: guillepijuan <86972717+guillepijuan@users.noreply.github.com> Date: Sat, 11 Dec 2021 19:58:06 -0300 Subject: [PATCH 36/66] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ca66eb4..447838a 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,13 @@ Lanes for each deployment target example are provided with some basic behavior: Check `fastlane/Appfile` and `fastlane/Fastfile` for more information. +## CI/CD configuration with Bitrise (updated on Dec 12th 2021) -## Continuous Integration with GitHub Actions +We are going to start using a tool called Bitrise to configure de CI/CD pipelines for mobiles apps. + +--> For Android apps you can find how to do it in this link: https://www.notion.so/rootstrap/Android-CI-CD-26d4abd4f2454224be8f617110147366 + +## Continuous Integration with GitHub Actions (DEPRECATED) We provide an example workflow [cicd.yml](.github/workflows/cicd.yml) including two jobs for running under [GitHub Actions](https://docs.github.com/en/actions), which can be modified according to the specifics of each project: From 4480c94eff8e6cee5b7cfa940c564f467993a047 Mon Sep 17 00:00:00 2001 From: Marcos Salto Date: Mon, 13 Dec 2021 16:25:01 -0300 Subject: [PATCH 37/66] Add clean architecture --- .idea/gradle.xml | 4 + build.gradle | 6 +- buildSrc/.gitignore | 1 + buildSrc/build.gradle.kts | 6 ++ buildSrc/java/Dependencies.kt | 148 ++++++++++++++++++++++++++++++++++ data/.gitignore | 1 + data/build.gradle | 9 +++ domain/.gitignore | 1 + domain/build.gradle | 9 +++ settings.gradle | 3 + usecases/.gitignore | 1 + usecases/build.gradle | 9 +++ 12 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 buildSrc/.gitignore create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/java/Dependencies.kt create mode 100644 data/.gitignore create mode 100644 data/build.gradle create mode 100644 domain/.gitignore create mode 100644 domain/build.gradle create mode 100644 usecases/.gitignore create mode 100644 usecases/build.gradle diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 4404972..ea63707 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -12,6 +12,10 @@ diff --git a/app/src/main/java/com/rootstrap/android/App.kt b/app/src/main/java/com/rootstrap/android/App.kt index 034c806..9603c11 100644 --- a/app/src/main/java/com/rootstrap/android/App.kt +++ b/app/src/main/java/com/rootstrap/android/App.kt @@ -2,8 +2,8 @@ package com.rootstrap.android import android.app.Application import com.rootstrap.android.di.initDI -import com.rootstrap.android.metrics.Analytics -import com.rootstrap.android.metrics.GoogleAnalytics +import com.rootstrap.data.metrics.Analytics +import com.rootstrap.data.metrics.GoogleAnalytics import com.rootstrap.data.api.interceptors.WifiService import org.koin.core.KoinExperimentalAPI diff --git a/app/src/main/java/com/rootstrap/android/di/DI.kt b/app/src/main/java/com/rootstrap/android/di/DI.kt index f94d691..1a064a1 100644 --- a/app/src/main/java/com/rootstrap/android/di/DI.kt +++ b/app/src/main/java/com/rootstrap/android/di/DI.kt @@ -4,7 +4,12 @@ import android.app.Application import android.content.Context import android.content.SharedPreferences import com.rootstrap.android.BuildConfig -import com.rootstrap.android.ui.activity.main.* +import com.rootstrap.android.ui.login.SignInActivity +import com.rootstrap.android.ui.login.SignInActivityViewModel +import com.rootstrap.android.ui.login.SignUpActivity +import com.rootstrap.android.ui.login.SignUpActivityViewModel +import com.rootstrap.android.ui.profile.ProfileActivity +import com.rootstrap.android.ui.profile.ProfileActivityViewModel import com.rootstrap.data.api.ApiProvider import com.rootstrap.data.api.ApiServiceFactory import com.rootstrap.data.api.interceptors.AuthenticationInterceptor diff --git a/app/src/main/java/com/rootstrap/android/ui/adapter/Adapter.kt b/app/src/main/java/com/rootstrap/android/ui/adapter/Adapter.kt deleted file mode 100644 index 5249c60..0000000 --- a/app/src/main/java/com/rootstrap/android/ui/adapter/Adapter.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.rootstrap.android.ui.adapter - -class Adapter diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt similarity index 91% rename from app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt rename to app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt index 4c5052f..5fddcba 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt @@ -1,13 +1,14 @@ -package com.rootstrap.android.ui.activity.main +package com.rootstrap.android.ui.login import android.Manifest import android.os.Bundle import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignInBinding -import com.rootstrap.android.metrics.Analytics -import com.rootstrap.android.metrics.PageEvents -import com.rootstrap.android.metrics.VISIT_SIGN_IN +import com.rootstrap.data.metrics.Analytics +import com.rootstrap.data.metrics.PageEvents +import com.rootstrap.data.metrics.VISIT_SIGN_IN +import com.rootstrap.android.ui.profile.ProfileActivity import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.value diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt similarity index 97% rename from app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt rename to app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt index 2253815..018e53c 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt @@ -1,4 +1,4 @@ -package com.rootstrap.android.ui.activity.main +package com.rootstrap.android.ui.login import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt similarity index 90% rename from app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt rename to app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt index 2a30036..1fa2ea9 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt @@ -1,14 +1,15 @@ -package com.rootstrap.android.ui.activity.main +package com.rootstrap.android.ui.login import android.content.Intent import android.os.Bundle import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignUpBinding -import com.rootstrap.android.metrics.Analytics -import com.rootstrap.android.metrics.PageEvents -import com.rootstrap.android.metrics.VISIT_SIGN_UP +import com.rootstrap.data.metrics.Analytics +import com.rootstrap.data.metrics.PageEvents +import com.rootstrap.data.metrics.VISIT_SIGN_UP import com.rootstrap.android.ui.base.BaseActivity +import com.rootstrap.android.ui.profile.ProfileActivity import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.value diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt similarity index 97% rename from app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt rename to app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt index bd09428..4398af2 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt @@ -1,4 +1,4 @@ -package com.rootstrap.android.ui.activity.main +package com.rootstrap.android.ui.login import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt similarity index 89% rename from app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt rename to app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt index e584a5a..af35fdc 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt @@ -1,12 +1,13 @@ -package com.rootstrap.android.ui.activity.main +package com.rootstrap.android.ui.profile import android.os.Bundle import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivityProfileBinding -import com.rootstrap.android.metrics.Analytics -import com.rootstrap.android.metrics.PageEvents -import com.rootstrap.android.metrics.VISIT_PROFILE +import com.rootstrap.data.metrics.Analytics +import com.rootstrap.data.metrics.PageEvents +import com.rootstrap.data.metrics.VISIT_PROFILE +import com.rootstrap.android.ui.login.SignUpActivity import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.view.ProfileView import com.rootstrap.android.util.NetworkState diff --git a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivityViewModel.kt similarity index 97% rename from app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt rename to app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivityViewModel.kt index 1d1b669..649b79a 100644 --- a/app/src/main/java/com/rootstrap/android/ui/activity/main/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivityViewModel.kt @@ -1,4 +1,4 @@ -package com.rootstrap.android.ui.activity.main +package com.rootstrap.android.ui.profile import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/res/layout/activity_sign_up.xml b/app/src/main/res/layout/activity_sign_up.xml index 10863ce..6528681 100644 --- a/app/src/main/res/layout/activity_sign_up.xml +++ b/app/src/main/res/layout/activity_sign_up.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/sign_up_margin_horizontal" - tools:context=".ui.activity.main.SignUpActivity"> + tools:context=".ui.login.SignUpActivity"> = ArrayList() diff --git a/app/src/main/java/com/rootstrap/android/metrics/Events.kt b/data/src/main/java/com/rootstrap/data/metrics/Events.kt similarity index 92% rename from app/src/main/java/com/rootstrap/android/metrics/Events.kt rename to data/src/main/java/com/rootstrap/data/metrics/Events.kt index c939024..5dc6504 100644 --- a/app/src/main/java/com/rootstrap/android/metrics/Events.kt +++ b/data/src/main/java/com/rootstrap/data/metrics/Events.kt @@ -1,6 +1,6 @@ -package com.rootstrap.android.metrics +package com.rootstrap.data.metrics -import com.rootstrap.android.metrics.base.TrackEvent +import com.rootstrap.data.metrics.base.TrackEvent class UserEvents { companion object { diff --git a/app/src/main/java/com/rootstrap/android/metrics/GoogleAnalytics.kt b/data/src/main/java/com/rootstrap/data/metrics/GoogleAnalytics.kt similarity index 89% rename from app/src/main/java/com/rootstrap/android/metrics/GoogleAnalytics.kt rename to data/src/main/java/com/rootstrap/data/metrics/GoogleAnalytics.kt index b76eeba..acdaf94 100644 --- a/app/src/main/java/com/rootstrap/android/metrics/GoogleAnalytics.kt +++ b/data/src/main/java/com/rootstrap/data/metrics/GoogleAnalytics.kt @@ -1,10 +1,10 @@ -package com.rootstrap.android.metrics +package com.rootstrap.data.metrics import android.content.Context import com.google.firebase.analytics.FirebaseAnalytics -import com.rootstrap.android.metrics.base.Provider -import com.rootstrap.android.metrics.base.TrackEvent -import com.rootstrap.android.metrics.base.UserProperty +import com.rootstrap.data.metrics.base.Provider +import com.rootstrap.data.metrics.base.TrackEvent +import com.rootstrap.data.metrics.base.UserProperty import org.json.JSONException /** diff --git a/app/src/main/java/com/rootstrap/android/metrics/MetricsEvents.kt b/data/src/main/java/com/rootstrap/data/metrics/MetricsEvents.kt similarity index 85% rename from app/src/main/java/com/rootstrap/android/metrics/MetricsEvents.kt rename to data/src/main/java/com/rootstrap/data/metrics/MetricsEvents.kt index c73c69e..5aad9c3 100644 --- a/app/src/main/java/com/rootstrap/android/metrics/MetricsEvents.kt +++ b/data/src/main/java/com/rootstrap/data/metrics/MetricsEvents.kt @@ -1,4 +1,4 @@ -package com.rootstrap.android.metrics +package com.rootstrap.data.metrics // sample events const val LOGIN = "logIn" diff --git a/app/src/main/java/com/rootstrap/android/metrics/MixPanelAnalytics.kt b/data/src/main/java/com/rootstrap/data/metrics/MixPanelAnalytics.kt similarity index 85% rename from app/src/main/java/com/rootstrap/android/metrics/MixPanelAnalytics.kt rename to data/src/main/java/com/rootstrap/data/metrics/MixPanelAnalytics.kt index 283f096..9177f24 100644 --- a/app/src/main/java/com/rootstrap/android/metrics/MixPanelAnalytics.kt +++ b/data/src/main/java/com/rootstrap/data/metrics/MixPanelAnalytics.kt @@ -1,11 +1,11 @@ -package com.rootstrap.android.metrics +package com.rootstrap.data.metrics import android.content.Context import com.mixpanel.android.mpmetrics.MixpanelAPI -import com.rootstrap.android.R -import com.rootstrap.android.metrics.base.Provider -import com.rootstrap.android.metrics.base.TrackEvent -import com.rootstrap.android.metrics.base.UserProperty +import com.rootstrap.data.BuildConfig +import com.rootstrap.data.metrics.base.Provider +import com.rootstrap.data.metrics.base.TrackEvent +import com.rootstrap.data.metrics.base.UserProperty import org.json.JSONException /** @@ -13,7 +13,7 @@ import org.json.JSONException * */ class MixPanelAnalytics(context: Context) : Provider { var analytic: MixpanelAPI = - MixpanelAPI.getInstance(context, context.getString(R.string.mixpanel_api_key)) + MixpanelAPI.getInstance(context, BuildConfig.MIX_PANEL_KEY) /** * Track any event in the app diff --git a/app/src/main/java/com/rootstrap/android/metrics/base/BaseAnalytics.kt b/data/src/main/java/com/rootstrap/data/metrics/base/BaseAnalytics.kt similarity index 88% rename from app/src/main/java/com/rootstrap/android/metrics/base/BaseAnalytics.kt rename to data/src/main/java/com/rootstrap/data/metrics/base/BaseAnalytics.kt index 3f9e1e9..aabd112 100644 --- a/app/src/main/java/com/rootstrap/android/metrics/base/BaseAnalytics.kt +++ b/data/src/main/java/com/rootstrap/data/metrics/base/BaseAnalytics.kt @@ -1,4 +1,4 @@ -package com.rootstrap.android.metrics.base +package com.rootstrap.data.metrics.base /** * Add custom metrics diff --git a/app/src/main/java/com/rootstrap/android/metrics/base/Provider.kt b/data/src/main/java/com/rootstrap/data/metrics/base/Provider.kt similarity index 95% rename from app/src/main/java/com/rootstrap/android/metrics/base/Provider.kt rename to data/src/main/java/com/rootstrap/data/metrics/base/Provider.kt index ab5d9b7..6238824 100644 --- a/app/src/main/java/com/rootstrap/android/metrics/base/Provider.kt +++ b/data/src/main/java/com/rootstrap/data/metrics/base/Provider.kt @@ -1,4 +1,4 @@ -package com.rootstrap.android.metrics.base +package com.rootstrap.data.metrics.base import android.os.Bundle import com.google.gson.Gson From a60e5be76b569082ff8ba1c618adeca487497879 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Tue, 11 Jan 2022 15:17:37 -0300 Subject: [PATCH 50/66] removed scope view model added test libraries renamed user dto --- .idea/vcs.xml | 7 ++ README.md | 7 +- .../android/tests/ProfileActivityTest.kt | 2 - .../com/rootstrap/android/utils/BaseTests.kt | 4 +- .../main/java/com/rootstrap/android/di/DI.kt | 4 +- .../android/ui/base/BaseViewModel.kt | 16 +-- .../com/rootstrap/android/ui/base/Scope.kt | 28 ----- .../android/ui/base/ScopedViewModel.kt | 19 ---- .../android/ui/login/SignInActivity.kt | 2 - .../ui/login/SignInActivityViewModel.kt | 19 ++-- .../android/ui/login/SignUpActivity.kt | 2 +- .../ui/login/SignUpActivityViewModel.kt | 20 ++-- .../android/ui/profile/ProfileActivity.kt | 1 - .../ui/profile/ProfileActivityViewModel.kt | 16 +-- .../com/rootstrap/android/util/UtilModule.kt | 1 + .../util/dispatcher/AppDispatcherProvider.kt | 11 ++ .../util/dispatcher/DispatcherProvider.kt | 10 ++ .../android/SignInActivityViewModelTest.kt | 99 +++++++++++++++++ .../android/SignUpActivityViewModelTest.kt | 102 ++++++++++++++++++ .../com/rootstrap/android/ValidationTests.kt | 2 +- .../android/test/TestDispatcherProvider.kt | 15 +++ .../rootstrap/android/test/UnitTestBase.kt | 19 ++++ build.gradle | 8 +- buildSrc/src/main/java/Dependencies.kt | 20 ++-- .../request/UserSignUpRequestSerializer.kt | 2 +- .../dto/response/UserResponseSerializer.kt | 16 ++- .../data/managers/session/SessionManager.kt | 2 +- .../managers/session/SessionManagerImpl.kt | 4 +- .../java/com/rootstrap/data/util/Prefs.kt | 2 +- 29 files changed, 346 insertions(+), 114 deletions(-) delete mode 100644 app/src/main/java/com/rootstrap/android/ui/base/Scope.kt delete mode 100644 app/src/main/java/com/rootstrap/android/ui/base/ScopedViewModel.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/UtilModule.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/dispatcher/AppDispatcherProvider.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/dispatcher/DispatcherProvider.kt create mode 100644 app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt create mode 100644 app/src/test/java/com/rootstrap/android/SignUpActivityViewModelTest.kt create mode 100644 app/src/test/java/com/rootstrap/android/test/TestDispatcherProvider.kt create mode 100644 app/src/test/java/com/rootstrap/android/test/UnitTestBase.kt diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..38b67fd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,5 +1,12 @@ + + + diff --git a/README.md b/README.md index ca66eb4..447838a 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,13 @@ Lanes for each deployment target example are provided with some basic behavior: Check `fastlane/Appfile` and `fastlane/Fastfile` for more information. +## CI/CD configuration with Bitrise (updated on Dec 12th 2021) -## Continuous Integration with GitHub Actions +We are going to start using a tool called Bitrise to configure de CI/CD pipelines for mobiles apps. + +--> For Android apps you can find how to do it in this link: https://www.notion.so/rootstrap/Android-CI-CD-26d4abd4f2454224be8f617110147366 + +## Continuous Integration with GitHub Actions (DEPRECATED) We provide an example workflow [cicd.yml](.github/workflows/cicd.yml) including two jobs for running under [GitHub Actions](https://docs.github.com/en/actions), which can be modified according to the specifics of each project: diff --git a/app/src/androidTest/java/com/rootstrap/android/tests/ProfileActivityTest.kt b/app/src/androidTest/java/com/rootstrap/android/tests/ProfileActivityTest.kt index 8b94290..12d8e13 100644 --- a/app/src/androidTest/java/com/rootstrap/android/tests/ProfileActivityTest.kt +++ b/app/src/androidTest/java/com/rootstrap/android/tests/ProfileActivityTest.kt @@ -8,7 +8,6 @@ import com.rootstrap.android.R import com.rootstrap.android.ui.profile.ProfileActivity import com.rootstrap.android.ui.login.SignUpActivity import com.rootstrap.android.utils.BaseTests -import dagger.hilt.android.testing.HiltAndroidTest import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest @@ -17,7 +16,6 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test -@HiltAndroidTest class ProfileActivityTest : BaseTests() { private lateinit var activity: ProfileActivity diff --git a/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt b/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt index 96dd335..bf29e1b 100644 --- a/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt +++ b/app/src/androidTest/java/com/rootstrap/android/utils/BaseTests.kt @@ -14,7 +14,7 @@ import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry import androidx.test.runner.lifecycle.Stage import com.rootstrap.data.managers.session.SessionManager -import com.rootstrap.data.dto.response.User +import com.rootstrap.data.dto.response.UserDTO import com.rootstrap.data.api.ApiServiceFactory import dagger.hilt.android.testing.HiltAndroidRule import okhttp3.mockwebserver.Dispatcher @@ -46,7 +46,7 @@ open class BaseTests { mockServer.stopServer() } - open fun testUser() = User( + open fun testUser() = UserDTO( "9032", "user123@mail.com", "Richard", diff --git a/app/src/main/java/com/rootstrap/android/di/DI.kt b/app/src/main/java/com/rootstrap/android/di/DI.kt index 1a064a1..c3115d8 100644 --- a/app/src/main/java/com/rootstrap/android/di/DI.kt +++ b/app/src/main/java/com/rootstrap/android/di/DI.kt @@ -10,6 +10,8 @@ import com.rootstrap.android.ui.login.SignUpActivity import com.rootstrap.android.ui.login.SignUpActivityViewModel import com.rootstrap.android.ui.profile.ProfileActivity import com.rootstrap.android.ui.profile.ProfileActivityViewModel +import com.rootstrap.android.util.dispatcher.AppDispatcherProvider +import com.rootstrap.android.util.dispatcher.DispatcherProvider import com.rootstrap.data.api.ApiProvider import com.rootstrap.data.api.ApiServiceFactory import com.rootstrap.data.api.interceptors.AuthenticationInterceptor @@ -69,7 +71,7 @@ val dataModule = module { Context.MODE_PRIVATE ) } - + single { AppDispatcherProvider() } factory { Prefs(get()) } factory { UserRepository(get()) } single { SessionManagerImpl(get()) } diff --git a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt index 24294c4..990fcaf 100644 --- a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt @@ -1,30 +1,22 @@ package com.rootstrap.android.ui.base -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.OnLifecycleEvent -import androidx.lifecycle.ViewModel +import androidx.lifecycle.* import com.rootstrap.android.util.NetworkState -import com.squareup.otto.Bus -import kotlinx.coroutines.CoroutineDispatcher /** * A [ViewModel] base class * implement app general LiveData as Session or User * **/ -open class BaseViewModel(uiDispatcher: CoroutineDispatcher) : ScopedViewModel(uiDispatcher), - LifecycleObserver { +open class BaseViewModel : ViewModel() { var error: String? = null protected val _networkState = MutableLiveData() val networkState: LiveData get() = _networkState - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + /* @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun register() = Bus().register(this) @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) - fun unregister() = Bus().unregister(this) + fun unregister() = Bus().unregister(this) */ } diff --git a/app/src/main/java/com/rootstrap/android/ui/base/Scope.kt b/app/src/main/java/com/rootstrap/android/ui/base/Scope.kt deleted file mode 100644 index 1ea6fa8..0000000 --- a/app/src/main/java/com/rootstrap/android/ui/base/Scope.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.rootstrap.android.ui.base - -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlin.coroutines.CoroutineContext - -interface Scope : CoroutineScope { - - class Impl(override val uiDispatcher: CoroutineDispatcher) : Scope { - override lateinit var job: Job - } - - var job: Job - val uiDispatcher: CoroutineDispatcher - - override val coroutineContext: CoroutineContext - get() = uiDispatcher + job - - fun initScope() { - job = SupervisorJob() - } - - fun destroyScope() { - job.cancel() - } -} diff --git a/app/src/main/java/com/rootstrap/android/ui/base/ScopedViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/base/ScopedViewModel.kt deleted file mode 100644 index 7e47777..0000000 --- a/app/src/main/java/com/rootstrap/android/ui/base/ScopedViewModel.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.rootstrap.android.ui.base - -import androidx.annotation.CallSuper -import androidx.lifecycle.ViewModel -import kotlinx.coroutines.CoroutineDispatcher - -abstract class ScopedViewModel(uiDispatcher: CoroutineDispatcher) : ViewModel(), - Scope by Scope.Impl(uiDispatcher) { - - init { - initScope() - } - - @CallSuper - override fun onCleared() { - destroyScope() - super.onCleared() - } -} diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt index 5fddcba..9b2ecad 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt @@ -30,8 +30,6 @@ class SignInActivity : PermissionActivity(), AuthView { binding.signInButton.setOnClickListener { signIn() } - lifecycle.addObserver(viewModel) - setObservers() sampleAskForPermission() } diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt index 018e53c..4174a0b 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt @@ -5,20 +5,21 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.dispatcher.DispatcherProvider import com.rootstrap.data.api.ApiException import com.rootstrap.data.dto.request.UserSignInRequest import com.rootstrap.data.dto.request.UserSignInRequestSerializer import com.rootstrap.data.dto.response.DataResult +import com.rootstrap.data.dto.response.toDomainUser import com.rootstrap.data.managers.session.SessionManager import com.rootstrap.usecases.SignIn -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch open class SignInActivityViewModel( private val signIn: SignIn, private val sessionManager: SessionManager, - uiDispatcher: CoroutineDispatcher -) : BaseViewModel(uiDispatcher) { + private val dispatcherProvider: DispatcherProvider +) : BaseViewModel() { private val _state = MutableLiveData() val state: LiveData @@ -26,15 +27,15 @@ open class SignInActivityViewModel( fun signIn(user: UserSignInRequest) { _networkState.value = NetworkState.LOADING - viewModelScope.launch { + viewModelScope.launch(dispatcherProvider.io) { when (val result = signIn.invoke(UserSignInRequestSerializer(user))) { is DataResult.Success -> { result.data?.user?.let { user -> - sessionManager.signIn(user) + sessionManager.signIn(user.toDomainUser()) } restoreNetworkState() - _state.value = SignInState.SIGN_IN_SUCCESS + _state.postValue(SignInState.SIGN_IN_SUCCESS) } is DataResult.Error -> { handleError(result.exception) @@ -46,12 +47,12 @@ open class SignInActivityViewModel( private fun handleError(exception: ApiException?) { error = exception?.message - _networkState.value = NetworkState.ERROR - _state.value = SignInState.SIGN_IN_FAILURE + _networkState.postValue(NetworkState.ERROR) + _state.postValue(SignInState.SIGN_IN_FAILURE) } private fun restoreNetworkState() { - _networkState.value = NetworkState.IDLE + _networkState.postValue(NetworkState.IDLE) } } diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt index 1fa2ea9..e4b6182 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt @@ -32,7 +32,7 @@ class SignUpActivity : BaseActivity(), AuthView { signUpButton.setOnClickListener { signUp() } signInTextView.setOnClickListener { signIn() } } - lifecycle.addObserver(viewModel) + setObservers() } diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt index 4398af2..e0742fd 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt @@ -5,21 +5,21 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.dispatcher.DispatcherProvider import com.rootstrap.data.api.ApiException import com.rootstrap.data.dto.request.UserSignUpRequest import com.rootstrap.data.dto.request.UserSignUpRequestSerializer import com.rootstrap.data.dto.response.DataResult +import com.rootstrap.data.dto.response.toDomainUser import com.rootstrap.data.managers.session.SessionManager - import com.rootstrap.usecases.SignUp -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch open class SignUpActivityViewModel( private val signUp: SignUp, private val sessionManager: SessionManager, - uiDispatcher: CoroutineDispatcher -) : BaseViewModel(uiDispatcher) { + private val dispatcherProvider: DispatcherProvider +) : BaseViewModel() { private val _state = MutableLiveData() val state: LiveData @@ -27,16 +27,16 @@ open class SignUpActivityViewModel( fun signUp(userSignUpRequest: UserSignUpRequest) { _networkState.value = NetworkState.LOADING - viewModelScope.launch { + viewModelScope.launch(dispatcherProvider.io) { when (val result = signUp.invoke(UserSignUpRequestSerializer(userSignUpRequest))) { is DataResult.Success -> { result.data?.user?.let { user -> - sessionManager.signIn(user) + sessionManager.signIn(user.toDomainUser()) } ?: handleError(ApiException(null)) restoreNetworkState() - _state.value = SignUpState.SIGN_UP_SUCCESS + _state.postValue(SignUpState.SIGN_UP_SUCCESS) } is DataResult.Error -> { handleError(result.exception) @@ -48,12 +48,12 @@ open class SignUpActivityViewModel( private fun handleError(exception: ApiException?) { error = exception?.message - _networkState.value = NetworkState.ERROR - _state.value = SignUpState.SIGN_UP_FAILURE + _networkState.postValue(NetworkState.ERROR) + _state.postValue(SignUpState.SIGN_UP_FAILURE) } private fun restoreNetworkState() { - _networkState.value = NetworkState.IDLE + _networkState.postValue(NetworkState.IDLE) } } diff --git a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt index af35fdc..a3bec42 100644 --- a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt @@ -33,7 +33,6 @@ class ProfileActivity : BaseActivity(), ProfileView { signOutButton.setOnClickListener { viewModel.signOut() } } - lifecycle.addObserver(viewModel) setObservers() } diff --git a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivityViewModel.kt index 649b79a..e4e7f76 100644 --- a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivityViewModel.kt @@ -5,18 +5,18 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.rootstrap.android.ui.base.BaseViewModel import com.rootstrap.android.util.NetworkState +import com.rootstrap.android.util.dispatcher.DispatcherProvider import com.rootstrap.data.api.ApiException import com.rootstrap.data.dto.response.DataResult import com.rootstrap.data.managers.session.SessionManager import com.rootstrap.usecases.SignOut -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch open class ProfileActivityViewModel( private val signOut: SignOut, private val sessionManager: SessionManager, - uiDispatcher: CoroutineDispatcher -) : BaseViewModel(uiDispatcher) { + private val dispatcherProvider: DispatcherProvider +) : BaseViewModel() { private val _state = MutableLiveData() val state: LiveData @@ -24,11 +24,11 @@ open class ProfileActivityViewModel( fun signOut() { _networkState.value = NetworkState.LOADING - viewModelScope.launch { + viewModelScope.launch(dispatcherProvider.io) { when (val result = signOut.invoke()) { is DataResult.Success -> { restoreNetworkState() - _state.value = ProfileState.SIGN_OUT_SUCCESS + _state.postValue(ProfileState.SIGN_OUT_SUCCESS) sessionManager.signOut() } is DataResult.Error -> { @@ -41,12 +41,12 @@ open class ProfileActivityViewModel( private fun handleError(exception: ApiException?) { error = exception?.message - _networkState.value = NetworkState.ERROR - _state.value = ProfileState.SIGN_OUT_FAILURE + _networkState.postValue(NetworkState.ERROR) + _state.postValue(ProfileState.SIGN_OUT_FAILURE) } private fun restoreNetworkState() { - _networkState.value = NetworkState.IDLE + _networkState.postValue(NetworkState.IDLE) } } diff --git a/app/src/main/java/com/rootstrap/android/util/UtilModule.kt b/app/src/main/java/com/rootstrap/android/util/UtilModule.kt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/UtilModule.kt @@ -0,0 +1 @@ + diff --git a/app/src/main/java/com/rootstrap/android/util/dispatcher/AppDispatcherProvider.kt b/app/src/main/java/com/rootstrap/android/util/dispatcher/AppDispatcherProvider.kt new file mode 100644 index 0000000..f53ba6c --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/dispatcher/AppDispatcherProvider.kt @@ -0,0 +1,11 @@ +package com.rootstrap.android.util.dispatcher + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +class AppDispatcherProvider : DispatcherProvider { + override val io: CoroutineDispatcher = Dispatchers.IO + override val default: CoroutineDispatcher = Dispatchers.Default + override val main: CoroutineDispatcher = Dispatchers.Main + override val unconfined: CoroutineDispatcher = Dispatchers.Unconfined +} diff --git a/app/src/main/java/com/rootstrap/android/util/dispatcher/DispatcherProvider.kt b/app/src/main/java/com/rootstrap/android/util/dispatcher/DispatcherProvider.kt new file mode 100644 index 0000000..29c0213 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/dispatcher/DispatcherProvider.kt @@ -0,0 +1,10 @@ +package com.rootstrap.android.util.dispatcher + +import kotlinx.coroutines.CoroutineDispatcher + +interface DispatcherProvider { + val io: CoroutineDispatcher + val default: CoroutineDispatcher + val main: CoroutineDispatcher + val unconfined: CoroutineDispatcher +} diff --git a/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt b/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt new file mode 100644 index 0000000..aaaadc7 --- /dev/null +++ b/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt @@ -0,0 +1,99 @@ +package com.rootstrap.android + +import com.rootstrap.android.test.TestDispatcherProvider +import com.rootstrap.android.test.UnitTestBase +import com.rootstrap.android.ui.login.SignInActivityViewModel +import com.rootstrap.android.ui.login.SignInState +import com.rootstrap.android.util.NetworkState +import com.rootstrap.data.api.ApiException +import com.rootstrap.data.dto.request.UserSignInRequest +import com.rootstrap.data.dto.request.UserSignInRequestSerializer +import com.rootstrap.data.dto.response.DataResult +import com.rootstrap.data.dto.response.UserResponseSerializer +import com.rootstrap.data.managers.session.SessionManager +import com.rootstrap.domain.User +import com.rootstrap.usecases.SignIn +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +@ExperimentalCoroutinesApi +class SignInActivityViewModelTest : UnitTestBase() { + + private lateinit var viewModel: SignInActivityViewModel + + @RelaxedMockK + lateinit var sessionManager: SessionManager + + @RelaxedMockK + lateinit var signIn: SignIn + + @MockK + lateinit var user: User + + @MockK + lateinit var userSignInRequestSerializer: UserSignInRequestSerializer + + @MockK + lateinit var userSignInResponseSerializer: UserResponseSerializer + + @MockK + lateinit var userSignInRequest: UserSignInRequest + + companion object { + const val ERROR_EXAMPLE_TEXT = "Time out example" + } + + @Before + override fun setup() { + super.setup() + every { userSignInRequestSerializer.user } returns userSignInRequest + viewModel = SignInActivityViewModel(signIn, sessionManager, TestDispatcherProvider()) // ver q hacer con los dispatchers + } + + // reading: naming standards for unit testing https://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html + @Test + fun `signIn success assert signInSuccess and network idle`() { + var state: SignInState? = null + coEvery { signIn.invoke(userSignInRequestSerializer) } returns DataResult.Success( + userSignInResponseSerializer + ) + + viewModel.signIn(userSignInRequest) + viewModel.state.observeForever { + state = it + } + + assertEquals(state, SignInState.SIGN_IN_SUCCESS) + assertEquals(viewModel.networkState.value, NetworkState.IDLE) + verify { sessionManager.signIn(user) } + coVerify { signIn.invoke(userSignInRequestSerializer) } + } + + @Test + fun `signIn fail assert signInFailure and network error`() { + var state: SignInState? = null + coEvery { signIn.invoke(userSignInRequestSerializer) } returns DataResult.Error( + ApiException( + ERROR_EXAMPLE_TEXT + ) + ) + + viewModel.signIn(userSignInRequest) + viewModel.state.observeForever { + state = it + } + + assertEquals(state, SignInState.SIGN_IN_FAILURE) + assertEquals(viewModel.networkState.value, NetworkState.ERROR) + assertEquals(viewModel.error, ERROR_EXAMPLE_TEXT) + coVerify { signIn.invoke(userSignInRequestSerializer) } + } +} diff --git a/app/src/test/java/com/rootstrap/android/SignUpActivityViewModelTest.kt b/app/src/test/java/com/rootstrap/android/SignUpActivityViewModelTest.kt new file mode 100644 index 0000000..f559ef1 --- /dev/null +++ b/app/src/test/java/com/rootstrap/android/SignUpActivityViewModelTest.kt @@ -0,0 +1,102 @@ +package com.rootstrap.android + +import com.rootstrap.android.test.TestDispatcherProvider +import com.rootstrap.android.test.UnitTestBase +import com.rootstrap.android.ui.login.SignUpActivityViewModel +import com.rootstrap.android.ui.login.SignUpState +import com.rootstrap.android.util.NetworkState +import com.rootstrap.data.api.ApiException +import com.rootstrap.data.dto.request.UserSignUpRequest +import com.rootstrap.data.dto.request.UserSignUpRequestSerializer +import com.rootstrap.data.dto.response.DataResult +import com.rootstrap.data.dto.response.UserDTO +import com.rootstrap.data.dto.response.UserResponseSerializer +import com.rootstrap.data.managers.session.SessionManager +import com.rootstrap.domain.User +import com.rootstrap.usecases.SignUp +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.RelaxedMockK +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +@ExperimentalCoroutinesApi // This annotation is required to use TestDispatcherProvider is still an experiment +class SignUpActivityViewModelTest : UnitTestBase() { + + private lateinit var viewModel: SignUpActivityViewModel + + @RelaxedMockK + lateinit var sessionManager: SessionManager + + @RelaxedMockK + lateinit var signUp: SignUp + + @MockK + lateinit var userDTO: UserDTO + + @MockK + lateinit var user: User + + @MockK + lateinit var userSignUpRequestSerializer: UserSignUpRequestSerializer + + @MockK + lateinit var userResponseSerializer: UserResponseSerializer + + @MockK + lateinit var userSignUpRequest: UserSignUpRequest + + companion object { + const val ERROR_EXAMPLE_TEXT = "Time out example" + } + + @Before + override fun setup() { + super.setup() + every { userResponseSerializer.user } returns userDTO + // every { userDTO.toDomainUser() } returns user + viewModel = SignUpActivityViewModel(signUp, sessionManager, TestDispatcherProvider()) + } + + @Test + fun `signUp success assert signUpSuccess and network idle`() { + var state: SignUpState? = null + coEvery { signUp.invoke(userSignUpRequestSerializer) } returns DataResult.Success( + userResponseSerializer + ) + + viewModel.signUp(userSignUpRequest) + viewModel.state.observeForever { + state = it + } + + assertEquals(state, SignUpState.SIGN_UP_SUCCESS) +// assertEquals(viewModel.networkState.value, NetworkState.IDLE) + // verify { sessionManager.signIn(userDTO.toDomainUser()) } + coVerify { signUp.invoke(userSignUpRequestSerializer) } + } + + @Test + fun `signUp fail assert signUpFailure and network error`() { + var state: SignUpState? = null + coEvery { signUp.invoke(userSignUpRequestSerializer) } returns DataResult.Error( + ApiException( + ERROR_EXAMPLE_TEXT + ) + ) + + viewModel.signUp(userSignUpRequest) + viewModel.state.observeForever { + state = it + } + +// assertEquals(state, SignUpState.SIGN_UP_FAILURE) + assertEquals(viewModel.networkState.value, NetworkState.ERROR) + assertEquals(viewModel.error, ERROR_EXAMPLE_TEXT) + coVerify { signUp.invoke(userSignUpRequestSerializer) } + } +} diff --git a/app/src/test/java/com/rootstrap/android/ValidationTests.kt b/app/src/test/java/com/rootstrap/android/ValidationTests.kt index 4d0cebf..515e6ba 100644 --- a/app/src/test/java/com/rootstrap/android/ValidationTests.kt +++ b/app/src/test/java/com/rootstrap/android/ValidationTests.kt @@ -4,7 +4,7 @@ import com.rootstrap.android.util.extensions.isEmail import org.junit.Assert.assertEquals import org.junit.Test -public class ValidationTests { +class ValidationTests { @Test fun checkEmailTest() { assertEquals(true, "email@mkdi.com".isEmail()) diff --git a/app/src/test/java/com/rootstrap/android/test/TestDispatcherProvider.kt b/app/src/test/java/com/rootstrap/android/test/TestDispatcherProvider.kt new file mode 100644 index 0000000..1037e26 --- /dev/null +++ b/app/src/test/java/com/rootstrap/android/test/TestDispatcherProvider.kt @@ -0,0 +1,15 @@ +package com.rootstrap.android.test + +import com.rootstrap.android.util.dispatcher.DispatcherProvider +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher + +@ExperimentalCoroutinesApi +class TestDispatcherProvider(testCoroutineDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()) : + DispatcherProvider { + override val default: CoroutineDispatcher = testCoroutineDispatcher + override val main: CoroutineDispatcher = testCoroutineDispatcher + override val io: CoroutineDispatcher = testCoroutineDispatcher + override val unconfined: CoroutineDispatcher = testCoroutineDispatcher +} diff --git a/app/src/test/java/com/rootstrap/android/test/UnitTestBase.kt b/app/src/test/java/com/rootstrap/android/test/UnitTestBase.kt new file mode 100644 index 0000000..f288824 --- /dev/null +++ b/app/src/test/java/com/rootstrap/android/test/UnitTestBase.kt @@ -0,0 +1,19 @@ +package com.rootstrap.android.test + +import androidx.annotation.CallSuper +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import io.mockk.MockKAnnotations +import org.junit.Before +import org.junit.Rule + +abstract class UnitTestBase { + + @get:Rule + val instantExecutorRule = InstantTaskExecutorRule() + + @CallSuper + @Before + open fun setup() { + MockKAnnotations.init(this) + } +} diff --git a/build.gradle b/build.gradle index d479ccd..966e6ee 100644 --- a/build.gradle +++ b/build.gradle @@ -95,9 +95,11 @@ allprojects { ] testLibs = [ - junit : Libs.junit, - mockitoCore: Libs.mockitoCore, - mockk : Libs.mockk + junit : Libs.junit, + mockitoCore : Libs.mockitoCore, + mockk : Libs.mockk, + coroutinesTest: Libs.coroutinesTest, + coreTest : Libs.coreTesting ] androidTestLibs = [ diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 387ee47..9346502 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -2,7 +2,7 @@ object Versions { const val appCompat = "1.3.1" const val constraintLayout = "2.1.1" const val core = "1.6.0" - const val coroutines = "1.5.2-native-mt" + const val coroutines = "1.5.1" const val legacySupportV4 = "1.0.0" const val preference = "1.1.1" const val recyclerview = "1.1.0" @@ -35,17 +35,20 @@ object Versions { const val mockwebserver = "4.9.0" const val testRunner = "1.4.0" const val mixPanel = "5.6.1" + const val coreTesting = "1.1.1" } object Libs { // Kotlin const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}" - const val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutines}" + const val coroutinesAndroid = + "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutines}" const val reflect = "org.jetbrains.kotlin:kotlin-reflect:${Versions.kotlin}" const val appCompat = "androidx.appcompat:appcompat:${Versions.appCompat}" const val core = "androidx.core:core-ktx:${Versions.core}" - const val constraintlayout = "androidx.constraintlayout:constraintlayout:${Versions.constraintLayout}" + const val constraintlayout = + "androidx.constraintlayout:constraintlayout:${Versions.constraintLayout}" const val legacySupportV4 = "androidx.legacy:legacy-support-v4:${Versions.legacySupportV4}" const val preference = "androidx.preference:preference-ktx:${Versions.preference}" const val recyclerview = "androidx.recyclerview:recyclerview:${Versions.recyclerview}" @@ -62,11 +65,14 @@ object Libs { const val lifecycleKapt = "android.arch.lifecycle:compiler:${Versions.lifecycleCommon}" const val lifecycleRuntime = "androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}" const val lifecycleLivedata = "androidx.lifecycle:lifecycle-livedata-ktx:${Versions.lifecycle}" - const val lifecycleExtensions = "androidx.lifecycle:lifecycle-extensions:${Versions.lifecycleExtension}" - const val lifecycleViewModel = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.lifecycle}" + const val lifecycleExtensions = + "androidx.lifecycle:lifecycle-extensions:${Versions.lifecycleExtension}" + const val lifecycleViewModel = + "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.lifecycle}" // Navigation - const val navigationFragment = "androidx.navigation:navigation-fragment-ktx:${Versions.navigation}" + const val navigationFragment = + "androidx.navigation:navigation-fragment-ktx:${Versions.navigation}" const val navigationUi = "androidx.navigation:navigation-ui-ktx:${Versions.navigation}" // Google JSON serializer/deserializer @@ -124,4 +130,6 @@ object Libs { const val uiautomator = "androidx.test.uiautomator:uiautomator:${Versions.uiautomator}" const val rules = "androidx.test:rules:${Versions.rules}" const val mockwebserver = "com.squareup.okhttp3:mockwebserver:${Versions.mockwebserver}" + const val coroutinesTest = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.coroutines}" + const val coreTesting = "android.arch.core:core-testing:${Versions.coreTesting}" } diff --git a/data/src/main/java/com/rootstrap/data/dto/request/UserSignUpRequestSerializer.kt b/data/src/main/java/com/rootstrap/data/dto/request/UserSignUpRequestSerializer.kt index 2ea06c0..550c0e2 100644 --- a/data/src/main/java/com/rootstrap/data/dto/request/UserSignUpRequestSerializer.kt +++ b/data/src/main/java/com/rootstrap/data/dto/request/UserSignUpRequestSerializer.kt @@ -10,4 +10,4 @@ data class UserSignUpRequest( val password: String = "" ) -data class UserSignUpRequestSerializer(val UserSignUpRequest: UserSignUpRequest) +data class UserSignUpRequestSerializer(val user: UserSignUpRequest) diff --git a/data/src/main/java/com/rootstrap/data/dto/response/UserResponseSerializer.kt b/data/src/main/java/com/rootstrap/data/dto/response/UserResponseSerializer.kt index 05b780f..e941a5b 100644 --- a/data/src/main/java/com/rootstrap/data/dto/response/UserResponseSerializer.kt +++ b/data/src/main/java/com/rootstrap/data/dto/response/UserResponseSerializer.kt @@ -1,9 +1,9 @@ package com.rootstrap.data.dto.response import com.google.gson.annotations.SerializedName +import com.rootstrap.domain.User -// TODO Discuss class name on Android Roundtable (UserDTO - DTO = Data Transfer Object as suffix) -data class User( +data class UserDTO( val id: String = "", var email: String = "", @SerializedName("first_name") var firstName: String = "", @@ -14,4 +14,14 @@ data class User( val uid: String? ) -data class UserResponseSerializer(val user: User) +fun UserDTO.toDomainUser() = User( + id = id, + email = email, + firstName = firstName, + lastName = lastName, + phoneNumber = phoneNumber, + password = password, + username = username +) + +data class UserResponseSerializer(val user: UserDTO) diff --git a/data/src/main/java/com/rootstrap/data/managers/session/SessionManager.kt b/data/src/main/java/com/rootstrap/data/managers/session/SessionManager.kt index 7fbf84a..5e5f6ab 100644 --- a/data/src/main/java/com/rootstrap/data/managers/session/SessionManager.kt +++ b/data/src/main/java/com/rootstrap/data/managers/session/SessionManager.kt @@ -1,6 +1,6 @@ package com.rootstrap.data.managers.session -import com.rootstrap.data.dto.response.User +import com.rootstrap.domain.User interface SessionManager { var user: User? diff --git a/data/src/main/java/com/rootstrap/data/managers/session/SessionManagerImpl.kt b/data/src/main/java/com/rootstrap/data/managers/session/SessionManagerImpl.kt index ae7f6a7..0efabcf 100644 --- a/data/src/main/java/com/rootstrap/data/managers/session/SessionManagerImpl.kt +++ b/data/src/main/java/com/rootstrap/data/managers/session/SessionManagerImpl.kt @@ -1,9 +1,9 @@ package com.rootstrap.data.managers.session -import com.rootstrap.data.dto.response.User import com.rootstrap.data.util.Prefs +import com.rootstrap.domain.User -class SessionManagerImpl (private val prefs: Prefs) : SessionManager { +class SessionManagerImpl(private val prefs: Prefs) : SessionManager { override var user: User? = prefs.user set(value) { diff --git a/data/src/main/java/com/rootstrap/data/util/Prefs.kt b/data/src/main/java/com/rootstrap/data/util/Prefs.kt index 02ea6b4..3216a22 100644 --- a/data/src/main/java/com/rootstrap/data/util/Prefs.kt +++ b/data/src/main/java/com/rootstrap/data/util/Prefs.kt @@ -3,7 +3,7 @@ package com.rootstrap.data.util import android.content.SharedPreferences import com.google.gson.Gson import com.rootstrap.data.util.extensions.fromJson -import com.rootstrap.data.dto.response.User +import com.rootstrap.domain.User class Prefs(private val prefs: SharedPreferences) { From d733a6fff78b615f3cf70fc7242335018601f7a3 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Tue, 11 Jan 2022 16:20:54 -0300 Subject: [PATCH 51/66] unit test fixed --- .../android/ui/login/SignInActivity.kt | 11 ++++--- .../ui/login/SignInActivityViewModel.kt | 1 - .../android/ui/login/SignUpActivity.kt | 11 ++++--- .../ui/login/SignUpActivityViewModel.kt | 5 ++-- .../android/SignInActivityViewModelTest.kt | 11 +++---- .../android/SignUpActivityViewModelTest.kt | 30 ++++++------------- .../rootstrap/android/test/UnitTestBase.kt | 12 ++++++++ 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt index 9b2ecad..24aa832 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt @@ -5,9 +5,6 @@ import android.os.Bundle import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignInBinding -import com.rootstrap.data.metrics.Analytics -import com.rootstrap.data.metrics.PageEvents -import com.rootstrap.data.metrics.VISIT_SIGN_IN import com.rootstrap.android.ui.profile.ProfileActivity import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState @@ -15,6 +12,9 @@ import com.rootstrap.android.util.extensions.value import com.rootstrap.android.util.permissions.PermissionActivity import com.rootstrap.android.util.permissions.PermissionResponse import com.rootstrap.data.dto.request.UserSignInRequest +import com.rootstrap.data.metrics.Analytics +import com.rootstrap.data.metrics.PageEvents +import com.rootstrap.data.metrics.VISIT_SIGN_IN import org.koin.androidx.viewmodel.ext.android.viewModel class SignInActivity : PermissionActivity(), AuthView { @@ -60,7 +60,10 @@ class SignInActivity : PermissionActivity(), AuthView { when (it) { NetworkState.LOADING -> showProgress() NetworkState.IDLE -> hideProgress() - else -> showError(viewModel.error ?: getString(R.string.default_error)) + else -> { + hideProgress() + showError(viewModel.error ?: getString(R.string.default_error)) + } } }) } diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt index 4174a0b..8a88a00 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivityViewModel.kt @@ -39,7 +39,6 @@ open class SignInActivityViewModel( } is DataResult.Error -> { handleError(result.exception) - restoreNetworkState() } } } diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt index e4b6182..751f563 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt @@ -5,15 +5,15 @@ import android.os.Bundle import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignUpBinding -import com.rootstrap.data.metrics.Analytics -import com.rootstrap.data.metrics.PageEvents -import com.rootstrap.data.metrics.VISIT_SIGN_UP import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.profile.ProfileActivity import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.value import com.rootstrap.data.dto.request.UserSignUpRequest +import com.rootstrap.data.metrics.Analytics +import com.rootstrap.data.metrics.PageEvents +import com.rootstrap.data.metrics.VISIT_SIGN_UP import org.koin.androidx.viewmodel.ext.android.viewModel class SignUpActivity : BaseActivity(), AuthView { @@ -68,7 +68,10 @@ class SignUpActivity : BaseActivity(), AuthView { when (it) { NetworkState.LOADING -> showProgress() NetworkState.IDLE -> hideProgress() - else -> showError(viewModel.error ?: getString(R.string.default_error)) + else -> { + hideProgress() + showError(viewModel.error ?: getString(R.string.default_error)) + } } }) } diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt index e0742fd..ae60d36 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivityViewModel.kt @@ -28,8 +28,8 @@ open class SignUpActivityViewModel( fun signUp(userSignUpRequest: UserSignUpRequest) { _networkState.value = NetworkState.LOADING viewModelScope.launch(dispatcherProvider.io) { - - when (val result = signUp.invoke(UserSignUpRequestSerializer(userSignUpRequest))) { + val result = signUp.invoke(UserSignUpRequestSerializer(userSignUpRequest)) + when (result) { is DataResult.Success -> { result.data?.user?.let { user -> sessionManager.signIn(user.toDomainUser()) @@ -40,7 +40,6 @@ open class SignUpActivityViewModel( } is DataResult.Error -> { handleError(result.exception) - restoreNetworkState() } } } diff --git a/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt b/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt index aaaadc7..ab0599c 100644 --- a/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt +++ b/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt @@ -55,6 +55,7 @@ class SignInActivityViewModelTest : UnitTestBase() { override fun setup() { super.setup() every { userSignInRequestSerializer.user } returns userSignInRequest + every { userSignInResponseSerializer.user } returns userDTO viewModel = SignInActivityViewModel(signIn, sessionManager, TestDispatcherProvider()) // ver q hacer con los dispatchers } @@ -62,7 +63,7 @@ class SignInActivityViewModelTest : UnitTestBase() { @Test fun `signIn success assert signInSuccess and network idle`() { var state: SignInState? = null - coEvery { signIn.invoke(userSignInRequestSerializer) } returns DataResult.Success( + coEvery { signIn.invoke(any()) } returns DataResult.Success( userSignInResponseSerializer ) @@ -73,14 +74,14 @@ class SignInActivityViewModelTest : UnitTestBase() { assertEquals(state, SignInState.SIGN_IN_SUCCESS) assertEquals(viewModel.networkState.value, NetworkState.IDLE) - verify { sessionManager.signIn(user) } - coVerify { signIn.invoke(userSignInRequestSerializer) } + verify { sessionManager.signIn(any()) } + coVerify { signIn.invoke(any()) } } @Test fun `signIn fail assert signInFailure and network error`() { var state: SignInState? = null - coEvery { signIn.invoke(userSignInRequestSerializer) } returns DataResult.Error( + coEvery { signIn.invoke(any()) } returns DataResult.Error( ApiException( ERROR_EXAMPLE_TEXT ) @@ -94,6 +95,6 @@ class SignInActivityViewModelTest : UnitTestBase() { assertEquals(state, SignInState.SIGN_IN_FAILURE) assertEquals(viewModel.networkState.value, NetworkState.ERROR) assertEquals(viewModel.error, ERROR_EXAMPLE_TEXT) - coVerify { signIn.invoke(userSignInRequestSerializer) } + coVerify { signIn.invoke(any()) } } } diff --git a/app/src/test/java/com/rootstrap/android/SignUpActivityViewModelTest.kt b/app/src/test/java/com/rootstrap/android/SignUpActivityViewModelTest.kt index f559ef1..d4ff921 100644 --- a/app/src/test/java/com/rootstrap/android/SignUpActivityViewModelTest.kt +++ b/app/src/test/java/com/rootstrap/android/SignUpActivityViewModelTest.kt @@ -7,18 +7,16 @@ import com.rootstrap.android.ui.login.SignUpState import com.rootstrap.android.util.NetworkState import com.rootstrap.data.api.ApiException import com.rootstrap.data.dto.request.UserSignUpRequest -import com.rootstrap.data.dto.request.UserSignUpRequestSerializer import com.rootstrap.data.dto.response.DataResult -import com.rootstrap.data.dto.response.UserDTO import com.rootstrap.data.dto.response.UserResponseSerializer import com.rootstrap.data.managers.session.SessionManager -import com.rootstrap.domain.User import com.rootstrap.usecases.SignUp import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Assert.assertEquals import org.junit.Before @@ -35,15 +33,6 @@ class SignUpActivityViewModelTest : UnitTestBase() { @RelaxedMockK lateinit var signUp: SignUp - @MockK - lateinit var userDTO: UserDTO - - @MockK - lateinit var user: User - - @MockK - lateinit var userSignUpRequestSerializer: UserSignUpRequestSerializer - @MockK lateinit var userResponseSerializer: UserResponseSerializer @@ -58,14 +47,13 @@ class SignUpActivityViewModelTest : UnitTestBase() { override fun setup() { super.setup() every { userResponseSerializer.user } returns userDTO - // every { userDTO.toDomainUser() } returns user viewModel = SignUpActivityViewModel(signUp, sessionManager, TestDispatcherProvider()) } @Test fun `signUp success assert signUpSuccess and network idle`() { var state: SignUpState? = null - coEvery { signUp.invoke(userSignUpRequestSerializer) } returns DataResult.Success( + coEvery { signUp.invoke(any()) } returns DataResult.Success( userResponseSerializer ) @@ -74,16 +62,16 @@ class SignUpActivityViewModelTest : UnitTestBase() { state = it } - assertEquals(state, SignUpState.SIGN_UP_SUCCESS) -// assertEquals(viewModel.networkState.value, NetworkState.IDLE) - // verify { sessionManager.signIn(userDTO.toDomainUser()) } - coVerify { signUp.invoke(userSignUpRequestSerializer) } + assertEquals(state, SignUpState.SIGN_UP_SUCCESS) + assertEquals(viewModel.networkState.value, NetworkState.IDLE) + verify { sessionManager.signIn(any()) } + coVerify { signUp.invoke(any()) } } @Test fun `signUp fail assert signUpFailure and network error`() { var state: SignUpState? = null - coEvery { signUp.invoke(userSignUpRequestSerializer) } returns DataResult.Error( + coEvery { signUp.invoke(any()) } returns DataResult.Error( ApiException( ERROR_EXAMPLE_TEXT ) @@ -94,9 +82,9 @@ class SignUpActivityViewModelTest : UnitTestBase() { state = it } -// assertEquals(state, SignUpState.SIGN_UP_FAILURE) + assertEquals(state, SignUpState.SIGN_UP_FAILURE) assertEquals(viewModel.networkState.value, NetworkState.ERROR) assertEquals(viewModel.error, ERROR_EXAMPLE_TEXT) - coVerify { signUp.invoke(userSignUpRequestSerializer) } + coVerify { signUp.invoke(any()) } } } diff --git a/app/src/test/java/com/rootstrap/android/test/UnitTestBase.kt b/app/src/test/java/com/rootstrap/android/test/UnitTestBase.kt index f288824..c7ba50e 100644 --- a/app/src/test/java/com/rootstrap/android/test/UnitTestBase.kt +++ b/app/src/test/java/com/rootstrap/android/test/UnitTestBase.kt @@ -2,6 +2,7 @@ package com.rootstrap.android.test import androidx.annotation.CallSuper import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.rootstrap.data.dto.response.UserDTO import io.mockk.MockKAnnotations import org.junit.Before import org.junit.Rule @@ -11,6 +12,17 @@ abstract class UnitTestBase { @get:Rule val instantExecutorRule = InstantTaskExecutorRule() + val userDTO: UserDTO = UserDTO( + "1", + "as@rs.com", + "joseph", + "GG", + "1234", + "12345", + "as", + "" + ) + @CallSuper @Before open fun setup() { From 7030a850da514e608993ecb907ac6389fc01c989 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Tue, 11 Jan 2022 16:23:06 -0300 Subject: [PATCH 52/66] removed unused var --- .../rootstrap/android/SignInActivityViewModelTest.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt b/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt index ab0599c..8089862 100644 --- a/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt +++ b/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt @@ -11,7 +11,6 @@ import com.rootstrap.data.dto.request.UserSignInRequestSerializer import com.rootstrap.data.dto.response.DataResult import com.rootstrap.data.dto.response.UserResponseSerializer import com.rootstrap.data.managers.session.SessionManager -import com.rootstrap.domain.User import com.rootstrap.usecases.SignIn import io.mockk.coEvery import io.mockk.coVerify @@ -35,9 +34,6 @@ class SignInActivityViewModelTest : UnitTestBase() { @RelaxedMockK lateinit var signIn: SignIn - @MockK - lateinit var user: User - @MockK lateinit var userSignInRequestSerializer: UserSignInRequestSerializer @@ -56,7 +52,11 @@ class SignInActivityViewModelTest : UnitTestBase() { super.setup() every { userSignInRequestSerializer.user } returns userSignInRequest every { userSignInResponseSerializer.user } returns userDTO - viewModel = SignInActivityViewModel(signIn, sessionManager, TestDispatcherProvider()) // ver q hacer con los dispatchers + viewModel = SignInActivityViewModel( + signIn, + sessionManager, + TestDispatcherProvider() + ) // ver q hacer con los dispatchers } // reading: naming standards for unit testing https://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html From 35d88d1d17c3cf427e8be09d1c41f7ee8dac3596 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Wed, 12 Jan 2022 09:15:41 -0300 Subject: [PATCH 53/66] removed view package --- .../rootstrap/android/ui/login/SignInActivity.kt | 5 ++--- .../rootstrap/android/ui/login/SignUpActivity.kt | 5 ++--- .../android/ui/profile/ProfileActivity.kt | 16 ++++++++-------- .../com/rootstrap/android/ui/view/AuthView.kt | 8 -------- .../java/com/rootstrap/android/ui/view/IView.kt | 3 --- .../com/rootstrap/android/ui/view/ProfileView.kt | 8 -------- 6 files changed, 12 insertions(+), 33 deletions(-) delete mode 100644 app/src/main/java/com/rootstrap/android/ui/view/AuthView.kt delete mode 100644 app/src/main/java/com/rootstrap/android/ui/view/IView.kt delete mode 100644 app/src/main/java/com/rootstrap/android/ui/view/ProfileView.kt diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt index 24aa832..73ea7a7 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt @@ -6,7 +6,6 @@ import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignInBinding import com.rootstrap.android.ui.profile.ProfileActivity -import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.value import com.rootstrap.android.util.permissions.PermissionActivity @@ -17,7 +16,7 @@ import com.rootstrap.data.metrics.PageEvents import com.rootstrap.data.metrics.VISIT_SIGN_IN import org.koin.androidx.viewmodel.ext.android.viewModel -class SignInActivity : PermissionActivity(), AuthView { +class SignInActivity : PermissionActivity() { private val viewModel: SignInActivityViewModel by viewModel() private lateinit var binding: ActivitySignInBinding @@ -34,7 +33,7 @@ class SignInActivity : PermissionActivity(), AuthView { sampleAskForPermission() } - override fun showProfile() { + private fun showProfile() { startActivityClearTask(ProfileActivity()) } diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt index 751f563..7a1be4b 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt @@ -7,7 +7,6 @@ import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignUpBinding import com.rootstrap.android.ui.base.BaseActivity import com.rootstrap.android.ui.profile.ProfileActivity -import com.rootstrap.android.ui.view.AuthView import com.rootstrap.android.util.NetworkState import com.rootstrap.android.util.extensions.value import com.rootstrap.data.dto.request.UserSignUpRequest @@ -16,7 +15,7 @@ import com.rootstrap.data.metrics.PageEvents import com.rootstrap.data.metrics.VISIT_SIGN_UP import org.koin.androidx.viewmodel.ext.android.viewModel -class SignUpActivity : BaseActivity(), AuthView { +class SignUpActivity : BaseActivity() { private val viewModel: SignUpActivityViewModel by viewModel() private val binding: ActivitySignUpBinding by lazy { @@ -36,7 +35,7 @@ class SignUpActivity : BaseActivity(), AuthView { setObservers() } - override fun showProfile() { + private fun showProfile() { startActivityClearTask(ProfileActivity()) } diff --git a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt index a3bec42..b5c2d55 100644 --- a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt @@ -4,18 +4,17 @@ import android.os.Bundle import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivityProfileBinding -import com.rootstrap.data.metrics.Analytics -import com.rootstrap.data.metrics.PageEvents -import com.rootstrap.data.metrics.VISIT_PROFILE -import com.rootstrap.android.ui.login.SignUpActivity import com.rootstrap.android.ui.base.BaseActivity -import com.rootstrap.android.ui.view.ProfileView +import com.rootstrap.android.ui.login.SignUpActivity import com.rootstrap.android.util.NetworkState import com.rootstrap.data.managers.session.SessionManager +import com.rootstrap.data.metrics.Analytics +import com.rootstrap.data.metrics.PageEvents +import com.rootstrap.data.metrics.VISIT_PROFILE import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel -class ProfileActivity : BaseActivity(), ProfileView { +class ProfileActivity : BaseActivity() { private val sessionManager: SessionManager by inject() @@ -29,14 +28,15 @@ class ProfileActivity : BaseActivity(), ProfileView { with(binding) { setContentView(root) - welcomeTextView.text = getString(R.string.welcome_message, sessionManager.user?.firstName) + welcomeTextView.text = + getString(R.string.welcome_message, sessionManager.user?.firstName) signOutButton.setOnClickListener { viewModel.signOut() } } setObservers() } - override fun goToFirstScreen() { + private fun goToFirstScreen() { startActivityClearTask(SignUpActivity()) } diff --git a/app/src/main/java/com/rootstrap/android/ui/view/AuthView.kt b/app/src/main/java/com/rootstrap/android/ui/view/AuthView.kt deleted file mode 100644 index 84e2276..0000000 --- a/app/src/main/java/com/rootstrap/android/ui/view/AuthView.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.rootstrap.android.ui.view - -import com.rootstrap.android.ui.base.BaseView - -interface AuthView : BaseView { - - fun showProfile() -} diff --git a/app/src/main/java/com/rootstrap/android/ui/view/IView.kt b/app/src/main/java/com/rootstrap/android/ui/view/IView.kt deleted file mode 100644 index 259b1a6..0000000 --- a/app/src/main/java/com/rootstrap/android/ui/view/IView.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.rootstrap.android.ui.view - -class IView diff --git a/app/src/main/java/com/rootstrap/android/ui/view/ProfileView.kt b/app/src/main/java/com/rootstrap/android/ui/view/ProfileView.kt deleted file mode 100644 index 7fa9062..0000000 --- a/app/src/main/java/com/rootstrap/android/ui/view/ProfileView.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.rootstrap.android.ui.view - -import com.rootstrap.android.ui.base.BaseView - -interface ProfileView : BaseView { - - fun goToFirstScreen() -} From fdc2e4da05551ba80ae9a30d8434dcbc45040568 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Wed, 2 Feb 2022 14:04:52 -0300 Subject: [PATCH 54/66] removed comment --- .../java/com/rootstrap/android/SignInActivityViewModelTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt b/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt index 8089862..90755b7 100644 --- a/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt +++ b/app/src/test/java/com/rootstrap/android/SignInActivityViewModelTest.kt @@ -56,7 +56,7 @@ class SignInActivityViewModelTest : UnitTestBase() { signIn, sessionManager, TestDispatcherProvider() - ) // ver q hacer con los dispatchers + ) } // reading: naming standards for unit testing https://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html From 925539ed56c613fffd4e161ccf2c62d6a280f26d Mon Sep 17 00:00:00 2001 From: aguskoll Date: Wed, 2 Feb 2022 14:17:34 -0300 Subject: [PATCH 55/66] removed comment --- .../com/rootstrap/android/ui/base/BaseViewModel.kt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt index 990fcaf..34d417b 100644 --- a/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt +++ b/app/src/main/java/com/rootstrap/android/ui/base/BaseViewModel.kt @@ -1,6 +1,8 @@ package com.rootstrap.android.ui.base -import androidx.lifecycle.* +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import com.rootstrap.android.util.NetworkState /** @@ -13,10 +15,4 @@ open class BaseViewModel : ViewModel() { protected val _networkState = MutableLiveData() val networkState: LiveData get() = _networkState - - /* @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun register() = Bus().register(this) - - @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) - fun unregister() = Bus().unregister(this) */ } From b89a416a10ce48307ab531c25387fd566e3cad0f Mon Sep 17 00:00:00 2001 From: aguskoll Date: Wed, 2 Feb 2022 15:07:42 -0300 Subject: [PATCH 56/66] removed fastlane dev flavor --- .../rootstrap/android/database/AppDataBase.kt | 5 ---- .../rootstrap/data/database/AppDataBase.kt | 5 ++++ fastlane/Appfile | 8 ------- fastlane/Fastfile | 24 ------------------- 4 files changed, 5 insertions(+), 37 deletions(-) delete mode 100644 app/src/main/java/com/rootstrap/android/database/AppDataBase.kt create mode 100644 data/src/main/java/com/rootstrap/data/database/AppDataBase.kt diff --git a/app/src/main/java/com/rootstrap/android/database/AppDataBase.kt b/app/src/main/java/com/rootstrap/android/database/AppDataBase.kt deleted file mode 100644 index bcf5c65..0000000 --- a/app/src/main/java/com/rootstrap/android/database/AppDataBase.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.rootstrap.android.database - -class AppDataBase { - // TODO -} diff --git a/data/src/main/java/com/rootstrap/data/database/AppDataBase.kt b/data/src/main/java/com/rootstrap/data/database/AppDataBase.kt new file mode 100644 index 0000000..edd7d66 --- /dev/null +++ b/data/src/main/java/com/rootstrap/data/database/AppDataBase.kt @@ -0,0 +1,5 @@ +package com.rootstrap.data.database + +class AppDataBase { + // TODO +} diff --git a/fastlane/Appfile b/fastlane/Appfile index a6b0b8b..34b53b4 100644 --- a/fastlane/Appfile +++ b/fastlane/Appfile @@ -10,14 +10,6 @@ for_platform :android do package_name("com.rootstrap.android") end - for_lane :deploy_dev do - package_name("com.rootstrap.android.dev") - end - - for_lane :debug_dev do - package_name("com.rootstrap.android.dev") - end - for_lane :deploy_staging do package_name("com.rootstrap.android.staging") end diff --git a/fastlane/Fastfile b/fastlane/Fastfile index c4dd60a..2171b35 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -6,30 +6,6 @@ default_platform(:android) skip_docs platform :android do - lane :deploy_production do - release(flavor: 'Prod', track: 'internal') - end - - lane :deploy_dev do - release(flavor: 'Dev', track: 'internal') - end - - lane :deploy_staging do - release(flavor: 'Staging', track: 'internal') - end - - lane :debug_production do - debug(flavor: 'Prod') - end - - lane :debug_dev do - debug(flavor: 'Dev') - end - - lane :debug_staging do - debug(flavor: 'Staging') - end - lane :release do |options| increment_version_code( gradle_file_path: “app/build.gradle” From 3bd8c71b9eae36d843c4b70ac507ebbcfd7626a9 Mon Sep 17 00:00:00 2001 From: aguskoll Date: Wed, 2 Feb 2022 15:11:38 -0300 Subject: [PATCH 57/66] removed fastlane dev flavor --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 17eca57..87c89b5 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -24,7 +24,7 @@ jobs: bundle install --path vendor/bundle # Runs build with Gradle - name: Build with Fastlane - run: bundle exec fastlane debug_dev + run: bundle exec fastlane debug - name: Send notification of build result uses: 8398a7/action-slack@v3 with: From f647fab6cf0d87774640f4c21643ad2fc318a20c Mon Sep 17 00:00:00 2001 From: Marcos Salto Date: Wed, 2 Mar 2022 17:27:26 -0300 Subject: [PATCH 58/66] Migrated Groovy (*.gradle) to Kotlin (*.gradle.kts) Updated Koin library to 3.1.3 Updated KtLint library to 0.44.0 Optimized buildSrc module for Gradle Dependencies Management Updated App name Optimized app for Android 12 Added pre-push GitHook --- .editorconfig | 22 ++ app/build.gradle | 142 ----------- app/build.gradle.kts | 225 ++++++++++++++++++ app/src/main/AndroidManifest.xml | 13 +- .../main/java/com/rootstrap/android/App.kt | 4 +- .../main/java/com/rootstrap/android/di/DI.kt | 2 +- .../android/ui/login/SignInActivity.kt | 44 ++-- .../android/ui/login/SignUpActivity.kt | 9 +- .../android/ui/profile/ProfileActivity.kt | 28 ++- .../android/util/extensions/Validations.kt | 12 +- app/src/main/res/values/strings.xml | 2 +- build.gradle | 129 ---------- build.gradle.kts | 53 +++++ buildSrc/build.gradle.kts | 10 +- buildSrc/src/main/java/Dep.kt | 177 ++++++++++++++ buildSrc/src/main/java/Dependencies.kt | 135 ----------- data/build.gradle | 55 ----- data/build.gradle.kts | 87 +++++++ domain/build.gradle | 9 - domain/build.gradle.kts | 9 + gradle.properties | 3 +- gradle/wrapper/gradle-wrapper.properties | 4 +- pre-commit | 6 +- pre-push | 53 +++++ settings.gradle | 4 - settings.gradle.kts | 2 + usecases/build.gradle | 46 ---- usecases/build.gradle.kts | 59 +++++ 28 files changed, 767 insertions(+), 577 deletions(-) delete mode 100644 app/build.gradle create mode 100644 app/build.gradle.kts delete mode 100644 build.gradle create mode 100644 build.gradle.kts create mode 100644 buildSrc/src/main/java/Dep.kt delete mode 100644 buildSrc/src/main/java/Dependencies.kt delete mode 100644 data/build.gradle create mode 100644 data/build.gradle.kts delete mode 100644 domain/build.gradle create mode 100644 domain/build.gradle.kts create mode 100644 pre-push delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts delete mode 100644 usecases/build.gradle create mode 100644 usecases/build.gradle.kts diff --git a/.editorconfig b/.editorconfig index 448817d..f5578ef 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,3 +3,25 @@ # Note that rules in any ruleset other than the standard ruleset will need to be prefixed # by the ruleset identifier. disabled_rules=no-wildcard-imports,import-ordering,experimental:annotation +ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL + +ij_kotlin_line_comment_at_first_column = false +ij_kotlin_line_comment_add_space = true + +# These options can keep to use single name import +ij_kotlin_name_count_to_use_star_import = 2147483647 +ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 + +ij_kotlin_keep_blank_lines_in_declarations = 1 +ij_kotlin_keep_blank_lines_in_code = 1 +ij_kotlin_keep_blank_lines_before_right_brace = 0 + +# optional but recommended +ij_kotlin_align_multiline_parameters = false + +# optional but recommended +ij_continuation_indent_size = 4 + +# Android specific rules +ij_kotlin_import_nested_classes = false +ij_kotlin_imports_layout = *,^ diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 88b125b..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,142 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "kotlin-kapt" - id "com.google.gms.google-services" - id "com.google.firebase.crashlytics" - -} - -android { - compileSdkVersion 31 - defaultConfig { - applicationId "com.rootstrap.android" - minSdkVersion 23 - targetSdkVersion 31 - versionCode 42 - versionName "1.0" - testInstrumentationRunner "com.rootstrap.android.CustomTestRunner" - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - buildFeatures { - viewBinding true - dataBinding true - } - - kotlinOptions { - jvmTarget = "1.8" - } - - signingConfigs { - releaseConfig { - keyAlias projectKeyAlias - keyPassword projectKeyPassword - storeFile file(projectStoreFile) - storePassword projectStorePassword - } - } - - buildTypes { - getByName("debug") { - applicationIdSuffix = ".debug" - versionNameSuffix "-debug" - minifyEnabled false - buildConfigField("String", "API_URL", "\"https://rails5-api-base.herokuapp.com/api/v1/\"") - buildConfigField("String", "SECURE_KEY_ALIAS", "\"$projectKeyAlias\"") - buildConfigField("String", "SECURE_FILE_NAME", "\"appPreferencesDev\"") - } - - create("staging") { - initWith(getByName("debug")) - applicationIdSuffix = ".staging" - initWith(getByName("debug")) - buildConfigField("String", "API_URL", "\"https://proj-staging.herokuapp.com/api/\"") - buildConfigField("String", "SECURE_FILE_NAME", "\"appPreferencesStaging\"") - } - - getByName("release") { - signingConfig signingConfigs.releaseConfig - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - buildConfigField("String", "API_URL", "\"https://proj-production.herokuapp.com/api/\"") - buildConfigField("String", "SECURE_KEY_ALIAS", "\"$projectKeyAlias\"") - buildConfigField("String", "SECURE_FILE_NAME", "\"appPreferences\"") - } - } - - kapt { - correctErrorTypes true - } - - applicationVariants.all { variant -> - variant.outputs.all { output -> - def apk = output.outputFile - def newName = apk.name.replace(".apk", "-v" + variant.versionName + ".apk") - newName = newName.replace("-" + variant.buildType.name, "") - - outputFileName = new File("./apks/" + newName) - } - } - - configurations { - ktlint - } - - kotlinOptions { - freeCompilerArgs = ["-Xallow-result-return-type"] - } - - task ktlint(type: JavaExec, group: "verification") { - description = "Check Kotlin code style." - classpath = configurations.ktlint - main = "com.pinterest.ktlint.Main" - args "src/**/*.kt" - // to generate report in checkstyle format prepend following args: - // "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml" - // see https://github.com/pinterest/ktlint#usage for more - } - - check.dependsOn ktlint - - task ktlintFormat(type: JavaExec, group: "formatting") { - description = "Fix Kotlin code style deviations." - classpath = configurations.ktlint - main = "com.pinterest.ktlint.Main" - args "-F", "src/**/*.kt" - } - - useLibrary 'android.test.runner' - useLibrary 'android.test.base' - useLibrary 'android.test.mock' - - testOptions { - unitTests { - includeAndroidResources = true - animationsDisabled = true - } - } -} - -dependencies { - - implementation project(":data") - implementation project(":domain") - implementation project(":usecases") - - implementation fileTree(include: ['*.jar'], dir: 'libs') - - implementation androidLibs.values() - implementation androidDataLibs.values() - implementation kotlinLibs.values() - implementation platform(Libs.firebaseBom) - implementation libs.values() - kapt androidKaptLibs.values() - ktlint ktlintLibs.values() - testImplementation testLibs.values() - androidTestImplementation androidTestLibs.values() -} diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..a3a602f --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,225 @@ +plugins { + with(Dependencies.Plugins) { + id(ANDROID_APP) + kotlin(ANDROID) + kotlin(KAPT) + id(GOOGLE_SERVICES) + id(CRASHLYTICS) + } +} + +android { + val projectKeyAlias: String by project + val projectKeyPassword: String by project + val projectStoreFile: String by project + val projectStorePassword: String by project + + with(Dependencies.ConfigData) { + compileSdk = COMPILE_SDK_VERSION + buildToolsVersion = BUILD_TOOLS_VERSION + + defaultConfig { + applicationId = "com.rootstrap.android" + minSdk = MIN_SDK_VERSION + targetSdk = TARGET_SDK_VERSION + versionCode = VERSION_CODE + versionName = VERSION_NAME + testInstrumentationRunner = "com.rootstrap.android.CustomTestRunner" + } + } + + signingConfigs { + create("release") { + keyAlias = projectKeyAlias + keyPassword = projectKeyPassword + storeFile = file(projectStoreFile) + storePassword = projectStorePassword + } + } + + buildFeatures { + viewBinding = true + dataBinding = true + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = listOf("-Xallow-result-return-type") + } + + kapt { + correctErrorTypes = true + } + + useLibrary("android.test.runner") + useLibrary("android.test.base") + useLibrary("android.test.mock") + + testOptions { + unitTests { + isIncludeAndroidResources = true + animationsDisabled = true + } + } + + buildTypes { + getByName("debug") { + applicationIdSuffix = ".debug" + versionNameSuffix = "-DEBUG" + isMinifyEnabled = false + buildConfigField("String", "API_URL", "\"https://rails5-api-base.herokuapp.com/api/v1/\"") + buildConfigField("String", "SECURE_KEY_ALIAS", "\"$projectKeyAlias\"") + buildConfigField("String", "SECURE_FILE_NAME", "\"appPreferencesDev\"") + } + + create("staging") { + applicationIdSuffix = ".staging" + versionNameSuffix = ".STAGING" + isMinifyEnabled = false + buildConfigField("String", "API_URL", "\"https://proj-staging.herokuapp.com/api/\"") + buildConfigField("String", "SECURE_KEY_ALIAS", "\"$projectKeyAlias\"") + buildConfigField("String", "SECURE_FILE_NAME", "\"appPreferencesStaging\"") + } + + getByName("release") { + signingConfig = signingConfigs.getByName("release") + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") + buildConfigField("String", "API_URL", "\"https://proj-production.herokuapp.com/api/\"") + buildConfigField("String", "SECURE_KEY_ALIAS", "\"$projectKeyAlias\"") + buildConfigField("String", "SECURE_FILE_NAME", "\"appPreferences\"") + } + } +} +val ktlint: Configuration by configurations.creating + +dependencies { + implementation(project(":data")) + implementation(project(":domain")) + implementation(project(":usecases")) + + with(Dependencies.Android) { + implementation(APP_COMPAT) + implementation(CORE) + implementation(CONSTRAINT_LAYOUT) + implementation(LEGACY_SUPPORT_V4) + implementation(LIFECYCLE_COMMON) + implementation(LIFECYCLE_RUNTIME) + implementation(LIFECYCLE_LIVEDATA) + implementation(LIFECYCLE_EXTENSIONS) + implementation(LIFECYCLE_VIEW_MODEL) + implementation(PREFERENCE) + implementation(ROOM_RUNTIME) + implementation(NAVIGATION_FRAGMENT) + implementation(NAVIGATION_UI) + implementation(RECYCLERVIEW) + implementation(WORK_RUNTIME) + } + + with(Dependencies.Common) { + implementation(GLIDE) + implementation(MIX_PANEL) + implementation(OTTO) + implementation(SECURITY_CRYPTO) + } + + implementation(platform(Dependencies.Google.FIREBASE_BOM)) + + with(Dependencies.Google) { + implementation(MATERIAL) + implementation(FIREBASE_CORE) + implementation(FIREBASE_ANALYTICS) + implementation(FIREBASE_CRASHLYTICS) + implementation(GSON) + } + + with(Dependencies.Koin) { + implementation(ANDROID) + implementation(ANDROID_COMPAT) + implementation(ANDROID_NAVIGATION) + implementation(COMPOSE) + implementation(CORE) + implementation(WORK_MANAGER) + } + + with(Dependencies.Kotlin) { + implementation(COROUTINES_ANDROID) + implementation(KOTLIN_STDLIB) + implementation(REFLECT) + } + + with(Dependencies.Network) { + implementation(CONVERTER_MOSHI) + implementation(LOGGING_INTERCEPTOR) + implementation(MOSHI) + implementation(OKHTTP) + implementation(RETROFIT) + implementation(RETROFIT_GSON_CONVERTER) + } + + with(Dependencies.Kapt) { + kapt(GLIDE_KAPT) + kapt(LIFECYCLE_KAPT) + kapt(ROOM_KAPT) + } + + ktlint(Dependencies.KT_LINT) { + attributes { + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + } + } + + with(Dependencies.Test) { + testImplementation(CORE_TESTING) + testImplementation(COROUTINES_TEST) + testImplementation(JUNIT) + testImplementation(MOCKITO_CORE) + testImplementation(MOCKK) + } + + with(Dependencies.AndroidTest) { + androidTestImplementation(ESPRESSO_CORE) + androidTestImplementation(ESPRESSO_INTENTS) + androidTestImplementation(EXT_JUNIT) + androidTestImplementation(KOIN_TEST) + androidTestImplementation(MOCK_WEBSERVER) + androidTestImplementation(MOCKK_ANDROID) + androidTestImplementation(RULES) + androidTestImplementation(TEST_RUNNER) + androidTestImplementation(UI_AUTOMATOR) + } +} + +val outputDir = "${project.buildDir}/reports/ktlint/" +val inputFiles = project.fileTree(mapOf("dir" to "src", "include" to "**/*.kt")) + +val ktlintCheck by tasks.creating(JavaExec::class) { + inputs.files(inputFiles) + outputs.dir(outputDir) + group = "verification" + + description = "Check Kotlin code style." + classpath = ktlint + mainClass.set("com.pinterest.ktlint.Main") + args = listOf("src/**/*.kt") + // See https://github.com/pinterest/ktlint#usage for more +} + +val ktlintFormat by tasks.creating(JavaExec::class) { + inputs.files(inputFiles) + outputs.dir(outputDir) + group = "formatting" + + description = "Fix Kotlin code style deviations." + classpath = ktlint + mainClass.set("com.pinterest.ktlint.Main") + args = listOf("-F", "src/**/*.kt") +} + +tasks.getByPath("check").dependsOn(ktlint) + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2d525cb..25d68be 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -13,12 +14,14 @@ android:roundIcon="@mipmap/ic_launcher_round" android:networkSecurityConfig="@xml/network_security_config" android:supportsRtl="true" - android:theme="@style/AppTheme"> + android:theme="@style/AppTheme" + tools:targetApi="n"> + android:exported="true" + tools:ignore="LockedOrientationActivity"> @@ -27,11 +30,13 @@ + android:screenOrientation="portrait" + tools:ignore="LockedOrientationActivity" /> + android:screenOrientation="portrait" + tools:ignore="LockedOrientationActivity" /> diff --git a/app/src/main/java/com/rootstrap/android/App.kt b/app/src/main/java/com/rootstrap/android/App.kt index 9603c11..0a5e1d3 100644 --- a/app/src/main/java/com/rootstrap/android/App.kt +++ b/app/src/main/java/com/rootstrap/android/App.kt @@ -2,10 +2,10 @@ package com.rootstrap.android import android.app.Application import com.rootstrap.android.di.initDI +import com.rootstrap.data.api.interceptors.WifiService import com.rootstrap.data.metrics.Analytics import com.rootstrap.data.metrics.GoogleAnalytics -import com.rootstrap.data.api.interceptors.WifiService -import org.koin.core.KoinExperimentalAPI +import org.koin.core.annotation.KoinExperimentalAPI @KoinExperimentalAPI class App : Application() { diff --git a/app/src/main/java/com/rootstrap/android/di/DI.kt b/app/src/main/java/com/rootstrap/android/di/DI.kt index c3115d8..6572bb8 100644 --- a/app/src/main/java/com/rootstrap/android/di/DI.kt +++ b/app/src/main/java/com/rootstrap/android/di/DI.kt @@ -32,7 +32,7 @@ import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.androidx.fragment.koin.fragmentFactory import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.core.KoinExperimentalAPI +import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.startKoin import org.koin.core.logger.Level import org.koin.dsl.module diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt index 73ea7a7..1f9d0f9 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt @@ -2,7 +2,6 @@ package com.rootstrap.android.ui.login import android.Manifest import android.os.Bundle -import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignInBinding import com.rootstrap.android.ui.profile.ProfileActivity @@ -48,14 +47,20 @@ class SignInActivity : PermissionActivity() { } private fun setObservers() { - viewModel.state.observe(this, Observer { - when (it) { - SignInState.SIGN_IN_FAILURE -> showError(viewModel.error) - SignInState.SIGN_IN_SUCCESS -> showProfile() + viewModel.state.observe( + this + ) { + it?.run { + when (this) { + SignInState.SIGN_IN_FAILURE -> showError(viewModel.error) + SignInState.SIGN_IN_SUCCESS -> showProfile() + } } - }) + } - viewModel.networkState.observe(this, Observer { + viewModel.networkState.observe( + this + ) { when (it) { NetworkState.LOADING -> showProgress() NetworkState.IDLE -> hideProgress() @@ -64,22 +69,25 @@ class SignInActivity : PermissionActivity() { showError(viewModel.error ?: getString(R.string.default_error)) } } - }) + } } private fun sampleAskForPermission() { - requestPermission(arrayOf(Manifest.permission.CAMERA), object : PermissionResponse { - override fun granted() { - // TODO.. - } + requestPermission( + arrayOf(Manifest.permission.CAMERA), + object : PermissionResponse { + override fun granted() { + // TODO.. + } - override fun denied() { - // TODO.. - } + override fun denied() { + // TODO.. + } - override fun foreverDenied() { - // TODO.. + override fun foreverDenied() { + // TODO.. + } } - }) + ) } } diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt index 7a1be4b..605468b 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignUpActivity.kt @@ -2,7 +2,6 @@ package com.rootstrap.android.ui.login import android.content.Intent import android.os.Bundle -import androidx.lifecycle.Observer import com.rootstrap.android.R import com.rootstrap.android.databinding.ActivitySignUpBinding import com.rootstrap.android.ui.base.BaseActivity @@ -56,14 +55,14 @@ class SignUpActivity : BaseActivity() { } private fun setObservers() { - viewModel.state.observe(this, Observer { + viewModel.state.observe(this) { when (it) { SignUpState.SIGN_UP_FAILURE -> showError(viewModel.error) SignUpState.SIGN_UP_SUCCESS -> showProfile() } - }) + } - viewModel.networkState.observe(this, Observer { + viewModel.networkState.observe(this) { when (it) { NetworkState.LOADING -> showProgress() NetworkState.IDLE -> hideProgress() @@ -72,6 +71,6 @@ class SignUpActivity : BaseActivity() { showError(viewModel.error ?: getString(R.string.default_error)) } } - }) + } } } diff --git a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt index b5c2d55..86d85fb 100644 --- a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt @@ -41,19 +41,25 @@ class ProfileActivity : BaseActivity() { } private fun setObservers() { - viewModel.state.observe(this, Observer { - when (it) { - ProfileState.SIGN_OUT_FAILURE -> showError(viewModel.error) - ProfileState.SIGN_OUT_SUCCESS -> goToFirstScreen() + viewModel.state.observe( + this, + Observer { + when (it) { + ProfileState.SIGN_OUT_FAILURE -> showError(viewModel.error) + ProfileState.SIGN_OUT_SUCCESS -> goToFirstScreen() + } } - }) + ) - viewModel.networkState.observe(this, Observer { - when (it) { - NetworkState.LOADING -> showProgress() - NetworkState.IDLE -> hideProgress() - else -> showError(viewModel.error ?: getString(R.string.default_error)) + viewModel.networkState.observe( + this, + Observer { + when (it) { + NetworkState.LOADING -> showProgress() + NetworkState.IDLE -> hideProgress() + else -> showError(viewModel.error ?: getString(R.string.default_error)) + } } - }) + ) } } diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/Validations.kt b/app/src/main/java/com/rootstrap/android/util/extensions/Validations.kt index 7d76862..0c0027c 100644 --- a/app/src/main/java/com/rootstrap/android/util/extensions/Validations.kt +++ b/app/src/main/java/com/rootstrap/android/util/extensions/Validations.kt @@ -7,11 +7,11 @@ fun String.validate(pattern: String): Boolean { fun String.isEmail(): Boolean { return validate( "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" + - "\\@" + - "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + - "(" + - "\\." + - "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + - ")+" + "\\@" + + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + + "(" + + "\\." + + "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + + ")+" ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d4e82f2..000539d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ - RootstrapAndroidBase + Rootstrap Android Base OK diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 966e6ee..0000000 --- a/build.gradle +++ /dev/null @@ -1,129 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - google() - mavenCentral() - maven { - url 'https://maven.fabric.io/public' - } - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.0.4' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$Versions.kotlin" - classpath "com.google.gms:google-services:4.3.10" - classpath "com.google.firebase:firebase-crashlytics-gradle:2.8.1" - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -plugins { - id("io.gitlab.arturbosch.detekt").version("1.3.1") -} - -detekt { - config = files("$projectDir/default-detekt-config.yml") -} - -allprojects { - repositories { - google() - mavenCentral() - maven { url 'https://jitpack.io' } - } - - ext { - androidLibs = [ - appcompat : Libs.appCompat, - legacySupportV4 : Libs.legacySupportV4, - recyclerview : Libs.recyclerview, - constraintlayout : Libs.constraintlayout, - androidxCore : Libs.core, - lifecycleCommon : Libs.lifecycleCommon, - lifecycleRuntime : Libs.lifecycleRuntime, - lifecycleLivedata : Libs.lifecycleLivedata, - lifecycleExtensions: Libs.lifecycleExtensions, - lifecycleViewModel : Libs.lifecycleViewModel, - material : Libs.material, - roomRuntime : Libs.roomRuntime, - glide : Libs.glide, - navigationFragment : Libs.navigationFragment, - navigationUi : Libs.navigationUi - ] - - androidDataLibs = [ - preference: Libs.preference - ] - - androidKaptLibs = [ - roomCompiler : Libs.roomKapt, - lifecycleKapt: Libs.lifecycleKapt, - glideCompiler: Libs.glideKapt - ] - - ktlintLibs = [ - ktlint: Libs.ktlint - ] - - kotlinLibs = [ - kotlinStdLib: Libs.kotlinStdLib, - reflect : Libs.reflect, - coroutines : Libs.coroutinesAndroid - ] - - libs = [ - okhttpLoginInterceptor: Libs.loggingInterceptor, - retrofit : Libs.retrofit, - retrofitGsonConverter : Libs.retrofitGsonConverter, - koinCore : Libs.koinCore, - koinAndroid : Libs.koinAndroid, - koinAndroidCompat : Libs.koinAndroidCompat, - koinWorkManager : Libs.koinWorkManager, - koinCompose : Libs.koinCompose, - otto : Libs.otto, - gson : Libs.gson, - okhttp : Libs.okhttp, - moshi : Libs.moshi, - converterMoshi : Libs.converterMoshi, - securityCrypto : Libs.securityCrypto, - firebaseCore : Libs.firebaseCore, - firebaseAnalytics : Libs.firebaseAnalytics, - firebaseCrashlytics : Libs.firebaseCrashlytics, - mixPanel : Libs.mixPanel - ] - - testLibs = [ - junit : Libs.junit, - mockitoCore : Libs.mockitoCore, - mockk : Libs.mockk, - coroutinesTest: Libs.coroutinesTest, - coreTest : Libs.coreTesting - ] - - androidTestLibs = [ - testRunner : Libs.testRunner, - espressoCore : Libs.espressoCore, - espressoIntents: Libs.espressoIntents, - rules : Libs.rules, - extJunit : Libs.extJunit, - koinTest : Libs.koinTest, - mockWebServer : Libs.mockwebserver, - mockkAndroid : Libs.mockkAndroid, - uiautomator : Libs.uiautomator - ] - } -} - -task installGitHook(type: Copy) { - from new File(rootProject.rootDir, 'pre-commit') - into { new File(rootProject.rootDir, '.git/hooks') } - fileMode 0777 -} - -tasks.getByPath(':app:preBuild').dependsOn installGitHook - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..5be1936 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,53 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } + + dependencies { + with(Dependencies.ClassPaths) { + classpath(ANDROID_GRADLE_PLUGIN) + classpath(KOTLIN_GRADLE_PLUGIN) + classpath(GMS) + classpath(FIREBASE_CRASHLYTICS_GRADLE) + } + } +} + +allprojects { + repositories { + google() + mavenCentral() + maven("https://jitpack.io") + } +} + +val deletePreviousGitHook by tasks.registering(Delete::class) { + group = "utils" + description = "Deleting previous githook" + + val preCommit = "${rootProject.rootDir}/.git/hooks/pre-commit" + val prePush = "${rootProject.rootDir}/.git/hooks/pre-push" + if (file(preCommit).exists() && file(prePush).exists()) { + delete(preCommit, prePush) + } +} + +val installGitHook by tasks.registering(Copy::class) { + group = "utils" + description = "Adding githook to local working copy, this must be run manually" + + dependsOn(deletePreviousGitHook) + from("${rootProject.rootDir}/pre-commit", "${rootProject.rootDir}/pre-push") + into("${rootProject.rootDir}/.git/hooks") + fileMode = 777 +} + +tasks.getByPath(":app:build").dependsOn(":project:installGitHook") + +tasks.register("clean", Delete::class) { + delete(rootProject.buildDir) +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index ca013c3..16aa467 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,6 +1,10 @@ -repositories { - jcenter() -} +//import org.gradle.kotlin.dsl.`kotlin-dsl` + plugins { `kotlin-dsl` } + +repositories { + google() + mavenCentral() +} diff --git a/buildSrc/src/main/java/Dep.kt b/buildSrc/src/main/java/Dep.kt new file mode 100644 index 0000000..e625f93 --- /dev/null +++ b/buildSrc/src/main/java/Dep.kt @@ -0,0 +1,177 @@ +object Dependencies { + object Android { + const val APP_COMPAT = "androidx.appcompat:appcompat:${Versions.APP_COMPAT}" + const val CORE = "androidx.core:core-ktx:${Versions.CORE}" + const val CONSTRAINT_LAYOUT = + "androidx.constraintlayout:constraintlayout:${Versions.CONSTRAINT_LAYOUT}" + const val LEGACY_SUPPORT_V4 = "androidx.legacy:legacy-support-v4:${Versions.LEGACY_SUPPORT_V4}" + const val PREFERENCE = "androidx.preference:preference-ktx:${Versions.PREFERENCE}" + const val RECYCLERVIEW = "androidx.recyclerview:recyclerview:${Versions.RECYCLERVIEW}" + const val WORK_RUNTIME = "androidx.work:work-runtime-ktx:${Versions.WORK_RUNTIME}" + + // Lifecycle + const val LIFECYCLE_COMMON = "android.arch.lifecycle:common-java8:${Versions.LIFECYCLE_COMMON}" + const val LIFECYCLE_RUNTIME = "androidx.lifecycle:lifecycle-runtime-ktx:${Versions.LIFECYCLE}" + const val LIFECYCLE_LIVEDATA = "androidx.lifecycle:lifecycle-livedata-ktx:${Versions.LIFECYCLE}" + const val LIFECYCLE_EXTENSIONS = + "androidx.lifecycle:lifecycle-extensions:${Versions.LIFECYCLE_EXTENSION}" + const val LIFECYCLE_VIEW_MODEL = + "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.LIFECYCLE}" + + // Navigation + const val NAVIGATION_FRAGMENT = + "androidx.navigation:navigation-fragment-ktx:${Versions.NAVIGATION}" + const val NAVIGATION_UI = "androidx.navigation:navigation-ui-ktx:${Versions.NAVIGATION}" + + // Room + const val ROOM_RUNTIME = "androidx.room:room-runtime:${Versions.ROOM}" + + } + + object Common { + // Image + const val GLIDE = "com.github.bumptech.glide:glide:${Versions.GLIDE}" + // MixPanel + const val MIX_PANEL = "com.mixpanel.android:mixpanel-android:${Versions.MIX_PANEL}" + // Events + const val OTTO = "com.squareup:otto:${Versions.OTTO}" + // Security crypto + const val SECURITY_CRYPTO = "androidx.security:security-crypto:${Versions.SECURITY_CRYPTO}" + } + + object Google { + const val MATERIAL = "com.google.android.material:material:${Versions.MATERIAL_DESIGN}" + const val GSON = "com.google.code.gson:gson:${Versions.GSON}" + const val FIREBASE_BOM = "com.google.firebase:firebase-bom:${Versions.FIREBASE_BOM}" + const val FIREBASE_CORE = "com.google.firebase:firebase-core" + const val FIREBASE_ANALYTICS = "com.google.firebase:firebase-analytics-ktx" + const val FIREBASE_CRASHLYTICS = "com.google.firebase:firebase-crashlytics-ktx" + } + + object Kapt { + const val GLIDE_KAPT = "com.github.bumptech.glide:compiler:${Versions.GLIDE}" + const val LIFECYCLE_KAPT = "android.arch.lifecycle:compiler:${Versions.LIFECYCLE_COMMON}" + const val ROOM_KAPT = "androidx.room:room-compiler:${Versions.ROOM}" + } + + object Koin { + const val CORE = "io.insert-koin:koin-core:${Versions.KOIN}" + const val ANDROID = "io.insert-koin:koin-android:${Versions.KOIN}" + const val ANDROID_NAVIGATION = "io.insert-koin:koin-androidx-navigation:${Versions.KOIN}" + const val ANDROID_COMPAT = "io.insert-koin:koin-android-compat:${Versions.KOIN}" + const val WORK_MANAGER = "io.insert-koin:koin-androidx-workmanager:${Versions.KOIN}" + const val COMPOSE = "io.insert-koin:koin-androidx-compose:${Versions.KOIN}" + } + + object Kotlin { + const val KOTLIN_STDLIB = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.KOTLIN}" + const val COROUTINES_ANDROID = + "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.COROUTINES}" + const val REFLECT = "org.jetbrains.kotlin:kotlin-reflect:${Versions.KOTLIN}" + } + + object Network { + const val RETROFIT = "com.squareup.retrofit2:retrofit:${Versions.RETROFIT}" + const val RETROFIT_GSON_CONVERTER = + "com.squareup.retrofit2:converter-gson:${Versions.CONVERTER_GSON}" + const val MOSHI = "com.squareup.moshi:moshi-kotlin:${Versions.MOSHI}" + const val CONVERTER_MOSHI = "com.squareup.retrofit2:converter-moshi:${Versions.CONVERTER_MOSHI}" + const val LOGGING_INTERCEPTOR = "com.squareup.okhttp3:logging-interceptor:${Versions.OKHTTP}" + const val OKHTTP = "com.squareup.okhttp3:okhttp:${Versions.OKHTTP}" + } + + // Linters + const val KT_LINT = "com.pinterest:ktlint:${Versions.KT_LINT}" + + object Test { + const val JUNIT = "junit:junit:${Versions.JUNIT}" + const val MOCKITO_CORE = "org.mockito:mockito-core:${Versions.MOCKITO_CORE}" + const val MOCKK = "io.mockk:mockk:${Versions.MOCKK}" + const val COROUTINES_TEST = + "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.COROUTINES}" + const val CORE_TESTING = "android.arch.core:core-testing:${Versions.CORE_TESTING}" + } + + object AndroidTest { + const val MOCKK_ANDROID = "io.mockk:mockk-android:${Versions.MOCKK}" + const val TEST_RUNNER = "androidx.test:runner:${Versions.TEST_RUNNER}" + const val ESPRESSO_CORE = "androidx.test.espresso:espresso-core:${Versions.ESPRESSO}" + const val ESPRESSO_INTENTS = "androidx.test.espresso:espresso-intents:${Versions.ESPRESSO}" + const val EXT_JUNIT = "androidx.test.ext:junit:${Versions.EXT_JUNIT}" + const val KOIN_TEST = "io.insert-koin:koin-test:${Versions.KOIN}" + const val UI_AUTOMATOR = "androidx.test.uiautomator:uiautomator:${Versions.UI_AUTOMATOR}" + const val RULES = "androidx.test:rules:${Versions.RULES}" + const val MOCK_WEBSERVER = "com.squareup.okhttp3:mockwebserver:${Versions.MOCK_WEBSERVER}" + } + + object ClassPaths { + const val ANDROID_GRADLE_PLUGIN = "com.android.tools.build:gradle:${Versions.ANDROID_GRADLE_PLUGIN}" + const val KOTLIN_GRADLE_PLUGIN = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.KOTLIN}" + const val GMS = "com.google.gms:google-services:${Versions.GOOGLE_SERVICES}" + const val FIREBASE_CRASHLYTICS_GRADLE = "com.google.firebase:firebase-crashlytics-gradle:${Versions.FIREBASE_CRASHLYTICS_GRADLE}" + } + + object Versions { + const val ANDROID_GRADLE_PLUGIN = "7.1.2" + const val APP_COMPAT = "1.3.1" + const val CONSTRAINT_LAYOUT = "2.1.1" + const val CONVERTER_GSON = "2.9.0" + const val CONVERTER_MOSHI = "2.5.0" + const val CORE = "1.6.0" + const val COROUTINES = "1.5.1" + const val CORE_TESTING = "1.1.1" + const val ESPRESSO = "3.4.0" + const val EXT_JUNIT = "1.1.3" + const val FIREBASE_BOM = "26.1.0" + const val FIREBASE_CRASHLYTICS_GRADLE = "2.8.1" + const val GLIDE = "4.12.0" + const val GOOGLE_SERVICES = "4.3.10" + const val GSON = "2.8.7" + const val JUNIT = "4.13.2" + const val KOIN = "3.1.3" + const val KOTLIN = "1.6.10" + const val KT_LINT = "0.44.0" + const val LEGACY_SUPPORT_V4 = "1.0.0" + const val LIFECYCLE = "2.4.0" + const val LIFECYCLE_COMMON = "1.1.1" + const val LIFECYCLE_EXTENSION = "2.2.0" + const val MATERIAL_DESIGN = "1.4.0" + const val MIX_PANEL = "5.6.1" + const val MOCKK = "1.12.0" + const val MOCK_WEBSERVER = "4.9.0" + const val MOCKITO_CORE = "2.28.2" + const val MOSHI = "1.12.0" + const val NAVIGATION = "2.3.5" + const val OKHTTP = "4.9.0" + const val OTTO = "1.3.8" + const val PREFERENCE = "1.1.1" + const val RECYCLERVIEW = "1.1.0" + const val RETROFIT = "2.9.0" + const val ROOM = "2.4.1" + const val RULES = "1.4.0" + const val SECURITY_CRYPTO = "1.1.0-alpha03" + const val TEST_RUNNER = "1.4.0" + const val UI_AUTOMATOR = "2.2.0" + const val WORK_RUNTIME = "2.7.0" + } + + object ConfigData { + const val COMPILE_SDK_VERSION = 32 + const val BUILD_TOOLS_VERSION = "31.0.0" + const val MIN_SDK_VERSION = 23 + const val TARGET_SDK_VERSION = 32 + const val VERSION_CODE = 43 + const val VERSION_NAME = "1.0" + } + + object Plugins { + const val ANDROID = "android" + const val ANDROID_APP = "com.android.application" + const val ANDROID_LIB = "com.android.library" + const val CRASHLYTICS = "com.google.firebase.crashlytics" + const val GOOGLE_SERVICES = "com.google.gms.google-services" + const val KAPT = "kapt" + } +} + + diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt deleted file mode 100644 index 9346502..0000000 --- a/buildSrc/src/main/java/Dependencies.kt +++ /dev/null @@ -1,135 +0,0 @@ -object Versions { - const val appCompat = "1.3.1" - const val constraintLayout = "2.1.1" - const val core = "1.6.0" - const val coroutines = "1.5.1" - const val legacySupportV4 = "1.0.0" - const val preference = "1.1.1" - const val recyclerview = "1.1.0" - const val gson = "2.8.7" - const val firebaseBom = "26.1.0" - const val navigation = "2.3.5" - const val glide = "4.12.0" - const val kotlin = "1.6.0" - const val koin = "3.1.2" - const val lifecycle = "2.4.0" - const val lifecycleCommon = "1.1.1" - const val lifecycleExtension = "2.2.0" - const val materialDesign = "1.4.0" - const val moshi = "1.12.0" - const val converterMoshi = "2.5.0" - const val converterGson = "2.9.0" - const val okhttp = "4.9.0" - const val otto = "1.3.8" - const val ktlint = "0.35.0" - const val securityCrypto = "1.1.0-alpha03" - const val retrofit = "2.9.0" - const val room = "1.1.1" - const val junit = "4.13.2" - const val mockitoCore = "2.28.2" - const val mockk = "1.12.0" - const val espresso = "3.4.0" - const val extJunit = "1.1.3" - const val uiautomator = "2.2.0" - const val rules = "1.4.0" - const val mockwebserver = "4.9.0" - const val testRunner = "1.4.0" - const val mixPanel = "5.6.1" - const val coreTesting = "1.1.1" -} - -object Libs { - // Kotlin - const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}" - const val coroutinesAndroid = - "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutines}" - const val reflect = "org.jetbrains.kotlin:kotlin-reflect:${Versions.kotlin}" - - const val appCompat = "androidx.appcompat:appcompat:${Versions.appCompat}" - const val core = "androidx.core:core-ktx:${Versions.core}" - const val constraintlayout = - "androidx.constraintlayout:constraintlayout:${Versions.constraintLayout}" - const val legacySupportV4 = "androidx.legacy:legacy-support-v4:${Versions.legacySupportV4}" - const val preference = "androidx.preference:preference-ktx:${Versions.preference}" - const val recyclerview = "androidx.recyclerview:recyclerview:${Versions.recyclerview}" - - // Material - const val material = "com.google.android.material:material:${Versions.materialDesign}" - - // Room - const val roomRuntime = "android.arch.persistence.room:runtime:${Versions.room}" - const val roomKapt = "android.arch.persistence.room:compiler:${Versions.room}" - - // Lifecycle - const val lifecycleCommon = "android.arch.lifecycle:common-java8:${Versions.lifecycleCommon}" - const val lifecycleKapt = "android.arch.lifecycle:compiler:${Versions.lifecycleCommon}" - const val lifecycleRuntime = "androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}" - const val lifecycleLivedata = "androidx.lifecycle:lifecycle-livedata-ktx:${Versions.lifecycle}" - const val lifecycleExtensions = - "androidx.lifecycle:lifecycle-extensions:${Versions.lifecycleExtension}" - const val lifecycleViewModel = - "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.lifecycle}" - - // Navigation - const val navigationFragment = - "androidx.navigation:navigation-fragment-ktx:${Versions.navigation}" - const val navigationUi = "androidx.navigation:navigation-ui-ktx:${Versions.navigation}" - - // Google JSON serializer/deserializer - const val gson = "com.google.code.gson:gson:${Versions.gson}" - - // Firebase - const val firebaseBom = "com.google.firebase:firebase-bom:${Versions.firebaseBom}" - const val firebaseCore = "com.google.firebase:firebase-core" - const val firebaseAnalytics = "com.google.firebase:firebase-analytics-ktx" - const val firebaseCrashlytics = "com.google.firebase:firebase-crashlytics-ktx" - - //---- MixPanel ---- - const val mixPanel = "com.mixpanel.android:mixpanel-android:${Versions.mixPanel}" - - // Image - const val glide = "com.github.bumptech.glide:glide:${Versions.glide}" - const val glideKapt = "com.github.bumptech.glide:compiler:${Versions.glide}" - - // Network - const val retrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}" - const val retrofitGsonConverter = - "com.squareup.retrofit2:converter-gson:${Versions.converterGson}" - const val moshi = "com.squareup.moshi:moshi-kotlin:${Versions.moshi}" - const val converterMoshi = "com.squareup.retrofit2:converter-moshi:${Versions.converterMoshi}" - const val loggingInterceptor = "com.squareup.okhttp3:logging-interceptor:${Versions.okhttp}" - const val okhttp = "com.squareup.okhttp3:okhttp:${Versions.okhttp}" - - // Events - const val otto = "com.squareup:otto:${Versions.otto}" - - // Linters - const val ktlint = "com.pinterest:ktlint:${Versions.ktlint}" - - // Koin - const val koinCore = "io.insert-koin:koin-core:${Versions.koin}" - const val koinAndroid = "io.insert-koin:koin-android:${Versions.koin}" - const val koinAndroidNavigation = "io.insert-koin:koin-androidx-navigation:${Versions.koin}" - const val koinAndroidCompat = "io.insert-koin:koin-android-compat:${Versions.koin}" - const val koinWorkManager = "io.insert-koin:koin-androidx-workmanager:${Versions.koin}" - const val koinCompose = "io.insert-koin:koin-androidx-compose:${Versions.koin}" - - // Security crypto - const val securityCrypto = "androidx.security:security-crypto:${Versions.securityCrypto}" - - // Testing - const val junit = "junit:junit:${Versions.junit}" - const val mockitoCore = "org.mockito:mockito-core:${Versions.mockitoCore}" - const val mockk = "io.mockk:mockk:${Versions.mockk}" - const val mockkAndroid = "io.mockk:mockk-android:${Versions.mockk}" - const val testRunner = "androidx.test:runner:${Versions.testRunner}" - const val espressoCore = "androidx.test.espresso:espresso-core:${Versions.espresso}" - const val espressoIntents = "androidx.test.espresso:espresso-intents:${Versions.espresso}" - const val extJunit = "androidx.test.ext:junit:${Versions.extJunit}" - const val koinTest = "io.insert-koin:koin-test:${Versions.koin}" - const val uiautomator = "androidx.test.uiautomator:uiautomator:${Versions.uiautomator}" - const val rules = "androidx.test:rules:${Versions.rules}" - const val mockwebserver = "com.squareup.okhttp3:mockwebserver:${Versions.mockwebserver}" - const val coroutinesTest = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.coroutines}" - const val coreTesting = "android.arch.core:core-testing:${Versions.coreTesting}" -} diff --git a/data/build.gradle b/data/build.gradle deleted file mode 100644 index 2ed4125..0000000 --- a/data/build.gradle +++ /dev/null @@ -1,55 +0,0 @@ -plugins { - id "com.android.library" - id "kotlin-android" - id "kotlin-parcelize" -} - -android { - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 23 - targetSdkVersion 31 - - testInstrumentationRunner "com.rootstrap.android.CustomTestRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - getByName("debug") { - minifyEnabled false - buildConfigField("String", "API_URL", "\"https://rails5-api-base.herokuapp.com/api/v1/\"") - buildConfigField("String", "MIX_PANEL_KEY", "\"mixpanel_api_key\"") - } - - create("staging") { - initWith(getByName("debug")) - buildConfigField("String", "API_URL", "\"https://proj-staging.herokuapp.com/api/\"") - buildConfigField("String", "MIX_PANEL_KEY", "\"mixpanel_api_key\"") - } - - getByName("release") { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - buildConfigField("String", "API_URL", "\"https://proj-production.herokuapp.com/api/\"") - buildConfigField("String", "MIX_PANEL_KEY", "\"mixpanel_api_key\"") - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = "1.8" - } -} - -dependencies { - implementation project(":domain") - implementation androidDataLibs.values() - implementation kotlinLibs.values() - implementation platform(Libs.firebaseBom) - implementation libs.values() - testImplementation testLibs.values() -} diff --git a/data/build.gradle.kts b/data/build.gradle.kts new file mode 100644 index 0000000..ae0d088 --- /dev/null +++ b/data/build.gradle.kts @@ -0,0 +1,87 @@ +plugins { + with(Dependencies.Plugins) { + id(ANDROID_LIB) + kotlin(ANDROID) + id("kotlin-parcelize") + } +} + +android { + with(Dependencies.ConfigData) { + compileSdk = COMPILE_SDK_VERSION + + defaultConfig { + minSdk = MIN_SDK_VERSION + targetSdk = TARGET_SDK_VERSION + testInstrumentationRunner = "com.rootstrap.android.CustomTestRunner" + } + } + + buildTypes { + getByName("debug") { + isMinifyEnabled = false + buildConfigField("String", "API_URL", "\"https://rails5-api-base.herokuapp.com/api/v1/\"") + buildConfigField("String", "MIX_PANEL_KEY", "\"mixpanel_api_key\"") + } + + create("staging") { + isMinifyEnabled = false + buildConfigField("String", "API_URL", "\"https://proj-staging.herokuapp.com/api/\"") + buildConfigField("String", "MIX_PANEL_KEY", "\"mixpanel_api_key\"") + } + + getByName("release") { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") + buildConfigField("String", "API_URL", "\"https://proj-production.herokuapp.com/api/\"") + buildConfigField("String", "MIX_PANEL_KEY", "\"mixpanel_api_key\"") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(project(":domain")) + + with(Dependencies.Kotlin) { + implementation(COROUTINES_ANDROID) + implementation(KOTLIN_STDLIB) + implementation(REFLECT) + } + + implementation(Dependencies.Android.PREFERENCE) + implementation(Dependencies.Common.MIX_PANEL) + implementation(platform(Dependencies.Google.FIREBASE_BOM)) + + with(Dependencies.Google) { + implementation(MATERIAL) + implementation(FIREBASE_CORE) + implementation(FIREBASE_ANALYTICS) + implementation(FIREBASE_CRASHLYTICS) + implementation(GSON) + } + + with(Dependencies.Network) { + implementation(CONVERTER_MOSHI) + implementation(LOGGING_INTERCEPTOR) + implementation(MOSHI) + implementation(OKHTTP) + implementation(RETROFIT) + implementation(RETROFIT_GSON_CONVERTER) + } + + with(Dependencies.Test) { + testImplementation(CORE_TESTING) + testImplementation(COROUTINES_TEST) + testImplementation(JUNIT) + testImplementation(MOCKITO_CORE) + testImplementation(MOCKK) + } +} diff --git a/domain/build.gradle b/domain/build.gradle deleted file mode 100644 index c5e3111..0000000 --- a/domain/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - id 'java-library' - id 'kotlin' -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_7 - targetCompatibility = JavaVersion.VERSION_1_7 -} diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts new file mode 100644 index 0000000..c576962 --- /dev/null +++ b/domain/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("java-library") + id("kotlin") +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} diff --git a/gradle.properties b/gradle.properties index 51fbaac..4c2dee2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,7 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m +org.gradle.caching=true # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects @@ -27,4 +28,4 @@ kapt.incremental.apt=false projectKeyAlias = PAlias projectKeyPassword = KeyPassword projectStoreFile = KeyStorePathFile -projectStorePassword = KeyStorePassword \ No newline at end of file +projectStorePassword = KeyStorePassword diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0aeb7fd..086ce19 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Dec 13 17:15:09 ART 2021 +#Thu Feb 24 15:16:38 ART 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/pre-commit b/pre-commit index 533d118..86edc64 100644 --- a/pre-commit +++ b/pre-commit @@ -7,12 +7,12 @@ git add . git stash -q --keep-index -./gradlew ktlint +./gradlew ktlintCheck RESULT=$? git stash pop -q # return 1 exit code if running checks fails -[ $RESULT -ne 0 ] && echo "Please fix the remaining issues before commiting" && exit 1 -echo "😎" && exit 0 \ No newline at end of file +[ $RESULT -ne 0 ] && echo "Please fix the remaining issues before committing" && exit 1 +echo "😎" && exit 0 diff --git a/pre-push b/pre-push new file mode 100644 index 0000000..15ab6d8 --- /dev/null +++ b/pre-push @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +z40=0000000000000000000000000000000000000000 + +IFS=' ' +while read local_ref local_sha remote_ref remote_sha +do + if [ "$local_sha" = $z40 ] + then + # Handle delete + else + if [ "$remote_sha" = $z40 ] + then + # New branch, examine all commits + range="$local_sha" + else + # Update to existing branch, examine new commits + range="$remote_sha..$local_sha" + fi + + # Check for WIP commit + commit=`git rev-list -n 1 --grep '^WIP' "$range"` + if [ -n "$commit" ] + then + echo "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index dd1098d..0000000 --- a/settings.gradle +++ /dev/null @@ -1,4 +0,0 @@ -include ':app' -include ':domain' -include ':data' -include ':usecases' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..2971fa2 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,2 @@ +rootProject.name = "Android Base" +include("app", "domain", "data", "usecases") diff --git a/usecases/build.gradle b/usecases/build.gradle deleted file mode 100644 index 63d1a9f..0000000 --- a/usecases/build.gradle +++ /dev/null @@ -1,46 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 23 - targetSdkVersion 31 - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles "consumer-rules.pro" - } - - buildTypes { - getByName("debug") { - minifyEnabled false - } - - create("staging") { - initWith(getByName("debug")) - } - - getByName("release") { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - implementation project(":data") - implementation project(":domain") - - implementation kotlinLibs.values() - testImplementation testLibs.values() -} diff --git a/usecases/build.gradle.kts b/usecases/build.gradle.kts new file mode 100644 index 0000000..5585b08 --- /dev/null +++ b/usecases/build.gradle.kts @@ -0,0 +1,59 @@ +plugins { + with(Dependencies.Plugins) { + id(ANDROID_LIB) + kotlin(ANDROID) + } +} + +android { + with (Dependencies.ConfigData) { + compileSdk = COMPILE_SDK_VERSION + + defaultConfig { + minSdk = MIN_SDK_VERSION + targetSdk = TARGET_SDK_VERSION + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + } + + buildTypes { + getByName("debug") { + isMinifyEnabled = false + } + + create("staging") { + initWith(getByName("debug")) + } + + getByName("release") { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(project(":data")) + implementation(project(":domain")) + + with(Dependencies.Kotlin) { + implementation(COROUTINES_ANDROID) + implementation(KOTLIN_STDLIB) + implementation(REFLECT) + } + + with(Dependencies.Test) { + testImplementation(CORE_TESTING) + testImplementation(COROUTINES_TEST) + testImplementation(JUNIT) + testImplementation(MOCKITO_CORE) + testImplementation(MOCKK) + } +} From 5249975f9e637e5aebf38818d10b518597a49c30 Mon Sep 17 00:00:00 2001 From: Marcos Salto Date: Wed, 2 Mar 2022 17:48:14 -0300 Subject: [PATCH 59/66] Removed increment_version_code from fastlane --- fastlane/Fastfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 2171b35..4826f8b 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -7,10 +7,6 @@ skip_docs platform :android do lane :release do |options| - increment_version_code( - gradle_file_path: “app/build.gradle” - ) - gradle( task: "clean" ) From 971bb71686d65db7d2f3a37e624dcab8b2c8acc0 Mon Sep 17 00:00:00 2001 From: Marcos Salto Date: Wed, 2 Mar 2022 17:52:45 -0300 Subject: [PATCH 60/66] Renamed gradle file path --- fastlane/Fastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 4826f8b..e0538fe 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -30,7 +30,7 @@ platform :android do lane :debug do |options| increment_version_code( - gradle_file_path: “app/build.gradle” + gradle_file_path: “app/build.gradle.kts” ) gradle( From e0d2f79c122245ceebca8c328fcb3e90fc7e479e Mon Sep 17 00:00:00 2001 From: Marcos Salto Date: Wed, 2 Mar 2022 17:57:37 -0300 Subject: [PATCH 61/66] Replaced constant VERSION_CODE by a number due to fastlane incompatibility --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a3a602f..82c92e7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { applicationId = "com.rootstrap.android" minSdk = MIN_SDK_VERSION targetSdk = TARGET_SDK_VERSION - versionCode = VERSION_CODE + versionCode = 43 versionName = VERSION_NAME testInstrumentationRunner = "com.rootstrap.android.CustomTestRunner" } From 6d08b7a6dc725f54e097d8eefb096d234a3c1978 Mon Sep 17 00:00:00 2001 From: Marcos Salto Date: Mon, 14 Mar 2022 17:20:02 -0300 Subject: [PATCH 62/66] Fixed minor changes from code review --- .../android/ui/login/SignInActivity.kt | 4 ++-- .../android/ui/profile/ProfileActivity.kt | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt index 1f9d0f9..b999a17 100644 --- a/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/login/SignInActivity.kt @@ -60,8 +60,8 @@ class SignInActivity : PermissionActivity() { viewModel.networkState.observe( this - ) { - when (it) { + ) { networkState -> + when (networkState) { NetworkState.LOADING -> showProgress() NetworkState.IDLE -> hideProgress() else -> { diff --git a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt index 86d85fb..f716c17 100644 --- a/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt +++ b/app/src/main/java/com/rootstrap/android/ui/profile/ProfileActivity.kt @@ -42,24 +42,24 @@ class ProfileActivity : BaseActivity() { private fun setObservers() { viewModel.state.observe( - this, - Observer { - when (it) { + this + ) { state -> + state?.run { + when (this) { ProfileState.SIGN_OUT_FAILURE -> showError(viewModel.error) ProfileState.SIGN_OUT_SUCCESS -> goToFirstScreen() } } - ) + } viewModel.networkState.observe( - this, - Observer { - when (it) { - NetworkState.LOADING -> showProgress() - NetworkState.IDLE -> hideProgress() - else -> showError(viewModel.error ?: getString(R.string.default_error)) - } + this + ) { networkState -> + when (networkState) { + NetworkState.LOADING -> showProgress() + NetworkState.IDLE -> hideProgress() + else -> showError(viewModel.error ?: getString(R.string.default_error)) } - ) + } } } From cb0f2bd7e721f12414ad1b0831482e1a3ad45a5a Mon Sep 17 00:00:00 2001 From: Rodrigo Soria Date: Fri, 25 Feb 2022 13:57:15 -0300 Subject: [PATCH 63/66] Added utility extensions useful for every project --- .../android/ui/custom/CustomTypefaceSpan.kt | 39 +++++++++++ .../android/util/extensions/Fragment.kt | 42 ++++++++++++ .../android/util/extensions/ProgressBar.kt | 30 +++++++++ .../android/util/extensions/Textview.kt | 65 +++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 app/src/main/java/com/rootstrap/android/ui/custom/CustomTypefaceSpan.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/extensions/Fragment.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/extensions/ProgressBar.kt create mode 100644 app/src/main/java/com/rootstrap/android/util/extensions/Textview.kt diff --git a/app/src/main/java/com/rootstrap/android/ui/custom/CustomTypefaceSpan.kt b/app/src/main/java/com/rootstrap/android/ui/custom/CustomTypefaceSpan.kt new file mode 100644 index 0000000..90d7c52 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/ui/custom/CustomTypefaceSpan.kt @@ -0,0 +1,39 @@ +package com.rootstrap.android.ui.custom + +import android.graphics.Paint +import android.graphics.Typeface +import android.text.TextPaint +import android.text.style.TypefaceSpan + +/** + * Allows to apply a custom font in an spannableString + * + * Taken from https://stackoverflow.com/questions/10675070/multiple-typeface-in-single-textview + * with slightly modifications + */ +class CustomTypefaceSpan(private val newType: Typeface) : TypefaceSpan("") { + + override fun updateDrawState(ds: TextPaint) { + applyCustomTypeFace(ds, newType) + } + + override fun updateMeasureState(paint: TextPaint) { + applyCustomTypeFace(paint, newType) + } + + companion object { + private fun applyCustomTypeFace(paint: Paint, tf: Typeface) { + val oldStyle: Int + val old = paint.typeface + oldStyle = old?.style ?: 0 + val fake = oldStyle and tf.style.inv() + if (fake and Typeface.BOLD != 0) { + paint.isFakeBoldText = true + } + if (fake and Typeface.ITALIC != 0) { + paint.textSkewX = -0.25f + } + paint.typeface = tf + } + } +} diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/Fragment.kt b/app/src/main/java/com/rootstrap/android/util/extensions/Fragment.kt new file mode 100644 index 0000000..1233cf5 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/extensions/Fragment.kt @@ -0,0 +1,42 @@ +package com.rootstrap.android.util.extensions + +import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch + +/** + * Allows the collection of an unlimited amount of flows in an inline style without needing to + * add boilerplate indentations. + * + * The flows are all collected inside the same lifecycleState but it can customized. By default + * it is Lifecycle.State.STARTED. + * + * This extension can be called as many times as needed without problems, + * but generally you will only require to call it once thanks to the vararg operator. + * + * If called with a single Flow Pair the type of the flow is inferred automatically, otherwise + * you will need to cast the flow values when needed. + * + * Example of use :
+ * - Single flow collection : + * > `collectOnLifeCycle(Pair(viewModel.eventsFlow) { it.collect { event -> onEvents(event) } })` + * - Multi flow collection : + * > Just appending a trailing "," and adding a new Pair allows you to collect additional flows + */ +fun Fragment.collectOnLifeCycle( + vararg flowPairs: Pair, suspend (Flow) -> Unit>, + lifecycleState: Lifecycle.State = Lifecycle.State.STARTED +) { + lifecycleScope.launch { + repeatOnLifecycle(lifecycleState) { + flowPairs.forEach { + launch { + it.second(it.first) + } + } + } + } +} diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/ProgressBar.kt b/app/src/main/java/com/rootstrap/android/util/extensions/ProgressBar.kt new file mode 100644 index 0000000..bbddc00 --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/extensions/ProgressBar.kt @@ -0,0 +1,30 @@ +package com.rootstrap.android.util.extensions + +import android.animation.Animator +import android.animation.ObjectAnimator +import android.view.animation.DecelerateInterpolator +import android.widget.ProgressBar +import androidx.core.animation.addListener + +private const val DEFAULT_ANIMATION_TIME = 500L + +/** + * Smoothly animates the progress of the progress bar to the specified value + */ +fun ProgressBar.progressTo( + newlyProgress: Int, + timeInMillis: Long = DEFAULT_ANIMATION_TIME, + onEndListener: ((Animator) -> Unit)? = null +) { + val animation: ObjectAnimator = ObjectAnimator + .ofInt(this, "progress", progress, newlyProgress) + animation.duration = timeInMillis + animation.interpolator = DecelerateInterpolator() + animation.setAutoCancel(true) + + onEndListener?.let { + animation.addListener(onEnd = onEndListener) + } + + animation.start() +} diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/Textview.kt b/app/src/main/java/com/rootstrap/android/util/extensions/Textview.kt new file mode 100644 index 0000000..19da1ba --- /dev/null +++ b/app/src/main/java/com/rootstrap/android/util/extensions/Textview.kt @@ -0,0 +1,65 @@ +package com.rootstrap.android.util.extensions + +import android.graphics.Typeface +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.TextPaint +import android.text.method.LinkMovementMethod +import android.text.style.CharacterStyle +import android.text.style.ClickableSpan +import android.view.View +import android.widget.TextView +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat +import com.rootstrap.android.R +import com.rootstrap.android.ui.custom.CustomTypefaceSpan + +fun TextView.setClickableKeyword( + keyword: String, + onClickListener: () -> Unit, + @ColorRes keywordColor: Int = R.color.colorPrimary, + underline: Boolean = true, + typeFace: Typeface? = null +) { + val span: SpannableStringBuilder = getTextAsSpannable() + + val start = text.indexOf(keyword, 0) + + val clickableSpan = object : ClickableSpan() { + override fun onClick(widget: View) = onClickListener() + override fun updateDrawState(ds: TextPaint) { + ds.color = ContextCompat.getColor(context, keywordColor) + ds.isUnderlineText = underline + } + } + + typeFace?.let { + span.setSpan(CustomTypefaceSpan(typeFace), start, start + keyword.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + } + span.setSpan(clickableSpan, start, start + keyword.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + + if (movementMethod !is LinkMovementMethod) { + movementMethod = LinkMovementMethod.getInstance() + } + + text = span +} + +fun TextView.setColoredKeyword( + keyword: String, + @ColorRes keywordColor: Int = R.color.colorPrimary +) { + val span: SpannableStringBuilder = getTextAsSpannable() + val start = text.indexOf(keyword, 0) + val coloredSpan = object : CharacterStyle() { + override fun updateDrawState(ds: TextPaint) { + ds.color = ContextCompat.getColor(context, keywordColor) + } + } + span.setSpan(coloredSpan, start, start + keyword.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) + text = span +} + +private fun TextView.getTextAsSpannable() = + if (text is SpannableStringBuilder) text as SpannableStringBuilder + else SpannableStringBuilder(text) From ec49c7c75bde1e4b26798ff244950fde1ee9de58 Mon Sep 17 00:00:00 2001 From: Rodrigo Soria Date: Fri, 25 Feb 2022 16:56:52 -0300 Subject: [PATCH 64/66] Fix kotlin doc typo --- .../java/com/rootstrap/android/util/extensions/Fragment.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/Fragment.kt b/app/src/main/java/com/rootstrap/android/util/extensions/Fragment.kt index 1233cf5..d6ad8f7 100644 --- a/app/src/main/java/com/rootstrap/android/util/extensions/Fragment.kt +++ b/app/src/main/java/com/rootstrap/android/util/extensions/Fragment.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.launch * Allows the collection of an unlimited amount of flows in an inline style without needing to * add boilerplate indentations. * - * The flows are all collected inside the same lifecycleState but it can customized. By default + * The flows are all collected inside the same lifecycleState but it can be customized. By default * it is Lifecycle.State.STARTED. * * This extension can be called as many times as needed without problems, @@ -24,7 +24,8 @@ import kotlinx.coroutines.launch * - Single flow collection : * > `collectOnLifeCycle(Pair(viewModel.eventsFlow) { it.collect { event -> onEvents(event) } })` * - Multi flow collection : - * > Just appending a trailing "," and adding a new Pair allows you to collect additional flows + * > Just appending a trailing "," and adding a new Pair allows you to collect an additional + * flow per pair */ fun Fragment.collectOnLifeCycle( vararg flowPairs: Pair, suspend (Flow) -> Unit>, From 2f345a7fc5338e8c56d3cfd44d97cef2b411d0fe Mon Sep 17 00:00:00 2001 From: Rodrigo Soria Date: Thu, 21 Apr 2022 12:28:53 -0300 Subject: [PATCH 65/66] Mention new extension utilities in README.md --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 447838a..4d9fd1f 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Check `fastlane/Appfile` and `fastlane/Fastfile` for more information. ## CI/CD configuration with Bitrise (updated on Dec 12th 2021) -We are going to start using a tool called Bitrise to configure de CI/CD pipelines for mobiles apps. +We are going to start using a tool called Bitrise to configure the CI/CD pipelines for mobiles apps. --> For Android apps you can find how to do it in this link: https://www.notion.so/rootstrap/Android-CI-CD-26d4abd4f2454224be8f617110147366 @@ -115,6 +115,20 @@ in order to track the login event. - For MixPanel, you have to replace the API key: `mixpanel_api_key` + +## Utility extensions +We have a bunch of pre-made extensions usually used on every project to accelerate feature development. +you can access them in the util.extensions package. They include but are not limited to : + +- `Fragment.collectOnLifeCycle(...)` extension to reduce the boiler plate code and indentation when + collecting flows from a fragment. +- `ProgressBar.progressTo(...)` extension to animate progress updates in one line with ease + and with good support for older android versions. +- `TextView.setClickableKeyword(...)` extension to add clickable functions to words or prhases inside a + Textview, allowing to also change their color, font (for example, to bold the keyword), and underline +- `TextView.setColoredKeyword(...) ` extension to change the color of a word or prhase in a Textview. + + ## Code Quality Standards In order to meet the required code quality standards, this project uses [Ktlint](https://github.com/pinterest/ktlint) and [Detekt](https://github.com/arturbosch/detekt) From 9b71ae83d2f01e8ec04a0185273027f5452ea465 Mon Sep 17 00:00:00 2001 From: Rodrigo Soria Date: Thu, 21 Apr 2022 12:31:19 -0300 Subject: [PATCH 66/66] Add Kdocs and constant for italics skew factor --- .../android/ui/custom/CustomTypefaceSpan.kt | 5 +++- .../android/util/extensions/ProgressBar.kt | 19 ++++++------ .../android/util/extensions/Textview.kt | 27 ++++++++++++++++++ buildSrc/build/classes/kotlin/main/Libs.class | Bin 0 -> 6921 bytes .../main/META-INF/buildSrc.kotlin_module | Bin 0 -> 24 bytes .../build/classes/kotlin/main/Versions.class | Bin 0 -> 3313 bytes buildSrc/build/kotlin/buildSrcjar-classes.txt | 1 + .../kotlin/compileKotlin/build-history.bin | Bin 0 -> 31 bytes .../caches-jvm/inputs/source-to-output.tab | Bin 0 -> 4096 bytes .../inputs/source-to-output.tab.keystream | Bin 0 -> 4096 bytes .../inputs/source-to-output.tab.keystream.len | Bin 0 -> 8 bytes .../inputs/source-to-output.tab.len | Bin 0 -> 8 bytes .../inputs/source-to-output.tab.values.at | Bin 0 -> 458 bytes .../caches-jvm/inputs/source-to-output.tab_i | Bin 0 -> 32768 bytes .../inputs/source-to-output.tab_i.len | Bin 0 -> 8 bytes .../jvm/kotlin/class-attributes.tab | Bin 0 -> 4096 bytes .../jvm/kotlin/class-attributes.tab.keystream | Bin 0 -> 4096 bytes .../kotlin/class-attributes.tab.keystream.len | Bin 0 -> 8 bytes .../jvm/kotlin/class-attributes.tab.len | Bin 0 -> 8 bytes .../jvm/kotlin/class-attributes.tab.values.at | Bin 0 -> 55 bytes .../jvm/kotlin/class-attributes.tab_i | Bin 0 -> 32768 bytes .../jvm/kotlin/class-attributes.tab_i.len | Bin 0 -> 8 bytes .../jvm/kotlin/class-fq-name-to-source.tab | Bin 0 -> 4096 bytes .../class-fq-name-to-source.tab.keystream | Bin 0 -> 4096 bytes .../class-fq-name-to-source.tab.keystream.len | Bin 0 -> 8 bytes .../kotlin/class-fq-name-to-source.tab.len | Bin 0 -> 8 bytes .../class-fq-name-to-source.tab.values.at | Bin 0 -> 141 bytes .../jvm/kotlin/class-fq-name-to-source.tab_i | Bin 0 -> 32768 bytes .../kotlin/class-fq-name-to-source.tab_i.len | Bin 0 -> 8 bytes .../caches-jvm/jvm/kotlin/constants.tab | Bin 0 -> 4096 bytes .../jvm/kotlin/constants.tab.keystream | Bin 0 -> 4096 bytes .../jvm/kotlin/constants.tab.keystream.len | Bin 0 -> 8 bytes .../caches-jvm/jvm/kotlin/constants.tab.len | Bin 0 -> 8 bytes .../jvm/kotlin/constants.tab.values.at | Bin 0 -> 7723 bytes .../caches-jvm/jvm/kotlin/constants.tab_i | Bin 0 -> 32768 bytes .../caches-jvm/jvm/kotlin/constants.tab_i.len | Bin 0 -> 8 bytes .../jvm/kotlin/internal-name-to-source.tab | Bin 0 -> 4096 bytes .../internal-name-to-source.tab.keystream | Bin 0 -> 4096 bytes .../internal-name-to-source.tab.keystream.len | Bin 0 -> 8 bytes .../kotlin/internal-name-to-source.tab.len | Bin 0 -> 8 bytes .../internal-name-to-source.tab.values.at | Bin 0 -> 141 bytes .../jvm/kotlin/internal-name-to-source.tab_i | Bin 0 -> 32768 bytes .../kotlin/internal-name-to-source.tab_i.len | Bin 0 -> 8 bytes .../caches-jvm/jvm/kotlin/proto.tab | Bin 0 -> 4096 bytes .../caches-jvm/jvm/kotlin/proto.tab.keystream | Bin 0 -> 4096 bytes .../jvm/kotlin/proto.tab.keystream.len | Bin 0 -> 8 bytes .../caches-jvm/jvm/kotlin/proto.tab.len | Bin 0 -> 8 bytes .../caches-jvm/jvm/kotlin/proto.tab.values.at | Bin 0 -> 2944 bytes .../caches-jvm/jvm/kotlin/proto.tab_i | Bin 0 -> 32768 bytes .../caches-jvm/jvm/kotlin/proto.tab_i.len | Bin 0 -> 8 bytes .../jvm/kotlin/source-to-classes.tab | Bin 0 -> 4096 bytes .../kotlin/source-to-classes.tab.keystream | Bin 0 -> 4096 bytes .../source-to-classes.tab.keystream.len | Bin 0 -> 8 bytes .../jvm/kotlin/source-to-classes.tab.len | Bin 0 -> 8 bytes .../kotlin/source-to-classes.tab.values.at | Bin 0 -> 80 bytes .../jvm/kotlin/source-to-classes.tab_i | Bin 0 -> 32768 bytes .../jvm/kotlin/source-to-classes.tab_i.len | Bin 0 -> 8 bytes .../caches-jvm/lookups/counters.tab | 2 ++ .../caches-jvm/lookups/file-to-id.tab | Bin 0 -> 4096 bytes .../lookups/file-to-id.tab.keystream | Bin 0 -> 4096 bytes .../lookups/file-to-id.tab.keystream.len | Bin 0 -> 8 bytes .../caches-jvm/lookups/file-to-id.tab.len | Bin 0 -> 8 bytes .../lookups/file-to-id.tab.values.at | Bin 0 -> 55 bytes .../caches-jvm/lookups/file-to-id.tab_i | Bin 0 -> 32768 bytes .../caches-jvm/lookups/file-to-id.tab_i.len | Bin 0 -> 8 bytes .../caches-jvm/lookups/id-to-file.tab | Bin 0 -> 4096 bytes .../lookups/id-to-file.tab.keystream | Bin 0 -> 4096 bytes .../lookups/id-to-file.tab.keystream.len | Bin 0 -> 8 bytes .../caches-jvm/lookups/id-to-file.tab.len | Bin 0 -> 8 bytes .../lookups/id-to-file.tab.values.at | Bin 0 -> 95 bytes .../caches-jvm/lookups/lookups.tab | Bin 0 -> 4096 bytes .../caches-jvm/lookups/lookups.tab.keystream | Bin 0 -> 4096 bytes .../lookups/lookups.tab.keystream.len | Bin 0 -> 8 bytes .../caches-jvm/lookups/lookups.tab.len | Bin 0 -> 8 bytes .../caches-jvm/lookups/lookups.tab.values.at | Bin 0 -> 355 bytes .../caches-jvm/lookups/lookups.tab_i | Bin 0 -> 32768 bytes .../caches-jvm/lookups/lookups.tab_i.len | Bin 0 -> 8 bytes .../build/kotlin/compileKotlin/last-build.bin | Bin 0 -> 81 bytes buildSrc/build/libs/buildSrc.jar | Bin 0 -> 261 bytes .../plugin-under-test-metadata.properties | 1 + .../plugin-development/validation-report.txt | 0 .../source-roots/buildSrc/source-roots.txt | 8 ++++++ buildSrc/build/tmp/jar/MANIFEST.MF | 2 ++ 83 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 buildSrc/build/classes/kotlin/main/Libs.class create mode 100644 buildSrc/build/classes/kotlin/main/META-INF/buildSrc.kotlin_module create mode 100644 buildSrc/build/classes/kotlin/main/Versions.class create mode 100644 buildSrc/build/kotlin/buildSrcjar-classes.txt create mode 100644 buildSrc/build/kotlin/compileKotlin/build-history.bin create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/counters.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.len create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.values.at create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i create mode 100644 buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i.len create mode 100644 buildSrc/build/kotlin/compileKotlin/last-build.bin create mode 100644 buildSrc/build/libs/buildSrc.jar create mode 100644 buildSrc/build/pluginUnderTestMetadata/plugin-under-test-metadata.properties create mode 100644 buildSrc/build/reports/plugin-development/validation-report.txt create mode 100644 buildSrc/build/source-roots/buildSrc/source-roots.txt create mode 100644 buildSrc/build/tmp/jar/MANIFEST.MF diff --git a/app/src/main/java/com/rootstrap/android/ui/custom/CustomTypefaceSpan.kt b/app/src/main/java/com/rootstrap/android/ui/custom/CustomTypefaceSpan.kt index 90d7c52..fdda9c5 100644 --- a/app/src/main/java/com/rootstrap/android/ui/custom/CustomTypefaceSpan.kt +++ b/app/src/main/java/com/rootstrap/android/ui/custom/CustomTypefaceSpan.kt @@ -22,6 +22,9 @@ class CustomTypefaceSpan(private val newType: Typeface) : TypefaceSpan("") { } companion object { + + private const val ITALIC_SKEW_FACTOR = -0.25F + private fun applyCustomTypeFace(paint: Paint, tf: Typeface) { val oldStyle: Int val old = paint.typeface @@ -31,7 +34,7 @@ class CustomTypefaceSpan(private val newType: Typeface) : TypefaceSpan("") { paint.isFakeBoldText = true } if (fake and Typeface.ITALIC != 0) { - paint.textSkewX = -0.25f + paint.textSkewX = ITALIC_SKEW_FACTOR } paint.typeface = tf } diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/ProgressBar.kt b/app/src/main/java/com/rootstrap/android/util/extensions/ProgressBar.kt index bbddc00..f43b4ad 100644 --- a/app/src/main/java/com/rootstrap/android/util/extensions/ProgressBar.kt +++ b/app/src/main/java/com/rootstrap/android/util/extensions/ProgressBar.kt @@ -16,15 +16,12 @@ fun ProgressBar.progressTo( timeInMillis: Long = DEFAULT_ANIMATION_TIME, onEndListener: ((Animator) -> Unit)? = null ) { - val animation: ObjectAnimator = ObjectAnimator - .ofInt(this, "progress", progress, newlyProgress) - animation.duration = timeInMillis - animation.interpolator = DecelerateInterpolator() - animation.setAutoCancel(true) - - onEndListener?.let { - animation.addListener(onEnd = onEndListener) - } - - animation.start() + ObjectAnimator.ofInt(this, "progress", progress, newlyProgress).apply { + duration = timeInMillis + interpolator = DecelerateInterpolator() + setAutoCancel(true) + onEndListener?.let { + addListener(onEnd = it) + } + }.start() } diff --git a/app/src/main/java/com/rootstrap/android/util/extensions/Textview.kt b/app/src/main/java/com/rootstrap/android/util/extensions/Textview.kt index 19da1ba..7a5a206 100644 --- a/app/src/main/java/com/rootstrap/android/util/extensions/Textview.kt +++ b/app/src/main/java/com/rootstrap/android/util/extensions/Textview.kt @@ -14,6 +14,28 @@ import androidx.core.content.ContextCompat import com.rootstrap.android.R import com.rootstrap.android.ui.custom.CustomTypefaceSpan +/** + * Sets the first occurrence of the keyword string in this TextView's text as clickable and attach the + * given OnClickListener function to it. + * + * Additionally, it tints the keyword using the app's colorPrimary color and adds an underline to it + * so it looks and behaves like an Hyperlink. + * + * This behavior can be customized with the keywordColor and underline params and you can also change + * the keyword's typeFace with the typeFace param, allowing you to make it bold or apply + * other kinds of effects. + * + * You can add multiple clickable keywords by just calling this method as many times as you need in + * the same textview. + * + * The next example illustrates how to add a link to open a web browser: + *``` + * mySuggestionText.text = "For more information go to the admin web" + * mySuggestionText.setClickableKeyword("admin web") { + * // Get admin url and open the admin web via an intent + * } + *``` + */ fun TextView.setClickableKeyword( keyword: String, onClickListener: () -> Unit, @@ -45,6 +67,11 @@ fun TextView.setClickableKeyword( text = span } +/** + * changes the color of the first occurrence of the keyword string in this TextView's text. + * + * It accept a color resource as a second parameter and by default it uses the app's colorPrimary color. + */ fun TextView.setColoredKeyword( keyword: String, @ColorRes keywordColor: Int = R.color.colorPrimary diff --git a/buildSrc/build/classes/kotlin/main/Libs.class b/buildSrc/build/classes/kotlin/main/Libs.class new file mode 100644 index 0000000000000000000000000000000000000000..fa9a3c506c9693a113f461dc5ab5485a8e63e329 GIT binary patch literal 6921 zcmaKw`+pQy8OGmPLb8MeLb!%YDVIPJ*h#Vp5T;NYQm82*R01g#tz3czspWw&m%xzE33HfDa_W8c=JLmGA_ng^x z|M%wGL^MqQ(Wq&{&aoEKH0qc$=gt1I=@k3V=H{%t-$E-iYAd;Z*>20 z`JU|*@6agYdPRNC@^hYPJ4_EfjPRCWexYpVGINE}5o1t4tPiBAMWa0v@aa!{`c21i zebcvHhxJdo{$#COM&NaM*K=#W?O1HoDR{13(CF||FRMpyt7jS>88dXjTMy}jXZMIkJkIyGEl`X0dY+8jYtxeTcto9Yz*Su{7GA znDgHTzhp{&Jr2GB1D{8)eLi-6*<3(>H5w3{7Bd8pgseWu-?&+$wPmYl<`<@F)vD|H z)599=PvV5g2yYn{nq=mO(T)K=CtEdYt)kO%159mo>T$}i;0 zmN#!(mqlk$Hx~VXD}V3p8l@|yZ+W&^)~FBTsTWZB{Yj2bcHd^K7Gkv2!mU zZF?r{H8P{(I!LNTF%pa^<2=Jx&PW{KphoS9Mc9PyTe~lMcRx}laZNfOk5^q!n2|hX zqP_>ymJNq5i}jTlOxW{Q!SqdyuoQ!pwsf3wWG4nXq|y2$(8)SRobh#xy0C00E3q`P zdX`V<2=e>Fk0$F}HiAo5AJLDb z>2BoojO|%DlUXO+3RbY-5(6WC$mEdL*~8&Vx(CgS!B|jwb}j46D^Z&6MHa+fqmEf# z@a;UNPF#pFZoJlv4S6@ybOs3!8;yA;n~kF!T)qJf(=g6`ycXE?`%H(=`T_2YpH==d z&Tbmjl>5*m6QxNI=tJmj#bvX|#h|x|V;~yAH52W_D0V?_v3svcqMZqyg9%EU z4Hb1rA4wC|S96JDCovRn7oo}WBB<7eVgs-IJRIOFk-fYLxAn@V}xa3C)cxSLcG;n9F314yO#2Z z3QO#SU2vn|G?~;Ql*MJ->lvYsvMgZvxC>?@uEAn3b=2w!bjuEg*5ky(T6` z{E*29HozGVAuIJ-OfK`&CU zE&>wHE;gMJ?((@{E#*tdv5n{uw3jjDHPDk3*s<6$sYbEKmvEf&k$Tvk z9X{AwlYNJ){3rq$@q>fRR}kJ}I9xFnwYqPSxgJ9!eu(okaQ_-+HwJuJI{u=)m%ng{ z2z+tC64Mg|`3BYmcI11*vE3oPzwSrljQ;7EhHphH=saTIVg&{Lj57E;n3bAs)_fOd zI9@Q~lUiL5S|>0=e2&fq-^C1hwX%h7buDTb#t~{fjNiwRiqFzzD~HvL`h|lilCV*j z4~tEnwFu4GdK+KHBQ>gEg0sgBgSH61IqrUsfW-n@FAE3G%K zKw7Cv&8pO*N@-PErAn=;v|5$gRHQ<$Fs@i3c@s7`JWRNGvpj9$ZwLI2#MUXdhBNIc zeCT7Ri2_e6VQCPJYeMNXsEbxoGk*P**Huc>-*9h6k5=K{MXdsBlUTdJ)=I2HV4V_M zFR+ag+a$0p66+G!Hi>N)*liNqDX?7<+byuY66+S&eu?!6tWRPG1eTGQF0cWK4GL^X zV#5MEEU_a3J1Q|lV8CYYtSqsLz+8z{1?EYN z39Kfud4bg>wji*NO6*~QeOzLX2<#IQdsJYblGtMc`?SO!7uaVd_JqJbFR>>D_C<+3 zC9p3`>}i30RbtNw?CTPHR$$+h*mDB=w#1$n*b5SSQDEPb*h>O?Sz@mU?1vJ2RbW4s z*lPlNU1HehuvdtCMhd$K4*gL7jvb^QcWjWfvhO~%b(=pVT94Th+f{)uZ_Xh1(l zD8dHx%fz`G*4(cBElNFbo&I#X?;5?!gJ_F_r08GJQuG)4H}3xheG_}yTbF3XIQ?pz zemzca({JKmH^%9=bQ6HyxkMVH-_h?c(VdL0Gx`IgYm7RAhfRzMjMg)HjZrJ3R~bFS z=vhY3F`8g>iqSZuvy3hibBrpCwlmtvsGZRzMh`OTVbsm&B}R`i LdYaLTjQ;q4-beT` literal 0 HcmV?d00001 diff --git a/buildSrc/build/classes/kotlin/main/META-INF/buildSrc.kotlin_module b/buildSrc/build/classes/kotlin/main/META-INF/buildSrc.kotlin_module new file mode 100644 index 0000000000000000000000000000000000000000..c42baff38405f5d7142c103d685ef21af69b0548 GIT binary patch literal 24 acmZQzU|?ooU|KA6vxjD>@ut@tat-&cte5c>pY*XMw!On{q`wZxI!-&OE zr|p~t{RX5+AuBIn80%BQa=!rsKC3(KR<+$~g%Mw#jA2eV9VDk##MW5E0(Uo99R7#_ zeGzlFT%Sc9kG+gN^1|aNO*q1FhVZgpMFJd(Z*<9E(AX%8EMUw3)iXJ!} zuO9C#_9)s@7M!JS-S<{$!sMi#F9_T7)Nh2`_b76ABNR=7vSk-6n4sMH-rDYR;wY2C znSn&hX$nIvD590}o_KXE3 zv0<@aI@M&(F2+%B(TvF4cI5G`Qnb|)4ryABox9}tt#v0?w4hAaF=9Ll*F19VjzW?1 z#6~+1Ly^y-NZKNuN;7mD9uG_LJxLbq!i*rF#5;8x4T{5%P9IS$tGJtoPldY4@9u`9 z?9ipP;4wvOdrq5&l)o?{^Qi5MTb34cyks?`d%{RhafbY1I|#(UYI>UwoPhacKV{RU zu;4QaB<}4JnbE7D1QYLV_UTfeE~Ry;UzY}SDWgmKb!kwS4(QTBT{@&ohjrTOYJy~$cESf$T`$Ixz2X-SvF*T@O@vg5a@bRVN<&b=lp2OST+ zn$K>$|A^7^Erp`K4^-%DwAgOeShVDjjrcT%uH!E|RD@~#R!mjHcI2{Kp0F5x8+cg@ z3wc^xH$C6;FboMu(&%gPw!wmLs7=z2dZ`_Tl*IZJmX_Fn!ZH#YRM-KD9a7j~i47@i zSYpQ%c3fg76*eNV(+WEyv2zL=mDmM^jY&-1`?$nDQ2Z`R%vM-dV(Q*=5-TWvMTt!* zY+7P73cDh)s|uTy*foV+m)H%3%}MO0!saD*TVZ!3c2{BdB&M#VA~7{*3lh7p#C#yJ zhYG7oOwHMn#FiDmM-qFYu%{AJ&u>Ly>iIpBn40V75>x$NNbGZEEsn(0oKfAPw=dTK zld1y!nIQFBst8$Xso+6k;x({esAIx+^eYO6D1+Pd+qY`~YZMBopm^_`N6A{)rT+l) z$!plWH}M;MDS{aP3u40eq?zy){6OumL^r5LHCCXj41O7!We6bL8@0;t1w;e@SHM8v z&|ZNFggQb8!9_^KofN_>!WD!xf`gDjSVDM&FoH0Oa2#O)VH{x$VIJWC!bOBELJpyb n@B-lhLKWc|!ZgAegzE@%2!|1d5$+*W5LOWGAUsESitzG3eSnEDq8~2%LkOPe|RU{AdV_hQMeD jjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kgRc_9D*#r_JH literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.keystream b/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab.keystream new file mode 100644 index 0000000000000000000000000000000000000000..5055fcf3df258bf0c7b73fd4e5175c703ad1c43b GIT binary patch literal 4096 zcmeIuu?+%23yxw9kc9K(s4uvcU%Pp?hf-Wk}j1D$5uL%~{eNi8>9`hXVCn;!vX>X&+dyrROZU!%^c22wQP2D~m%|vKhPhqEImh z4T1vs{*1-7+&FK!5-N0t5&UAV7cs0RjXF c5FkK+009C72oNAZfB*pk1PBlyK;Wyu1w+dL=Kufz literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/inputs/source-to-output.tab_i.len new file mode 100644 index 0000000000000000000000000000000000000000..131e265740f37d77b7c4a3676d2a7704ca3e4a29 GIT binary patch literal 8 McmZQz0D%Su009U9fdBvi literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab new file mode 100644 index 0000000000000000000000000000000000000000..310a67f078c4b5d9356c8aa717e2b4a178a8116f GIT binary patch literal 4096 zcmbR3vzw0r2$(jE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0O=tB0E=u6bpQYW literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.keystream b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.keystream new file mode 100644 index 0000000000000000000000000000000000000000..a49790f7c8cc48d81ac696566327d7aa19722c23 GIT binary patch literal 4096 zcmeIuu?YYm3;-|(2aj|10KuV)g8d)-Ly{BH#=042XYDbwV8DO@0|pEjFkrxd0RsjM Hd^qp`;{gQl literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len new file mode 100644 index 0000000000000000000000000000000000000000..46715fa2fd463d2538b1aa407ae094d66dfb3832 GIT binary patch literal 8 LcmZQz00TY%02Tlb literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.len new file mode 100644 index 0000000000000000000000000000000000000000..01bdaa1da7d937c7e7d98e54ba912f88ab95c7f2 GIT binary patch literal 8 LcmZQz0D}nt0GI%g literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.values.at b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab.values.at new file mode 100644 index 0000000000000000000000000000000000000000..fc0e22172c5b0552e6cf19490ae85c3360166117 GIT binary patch literal 55 zcmdOA@JLNeNi9+cN=?o$N>OmjFH#6dEh^3|E=kQR@klJr@J%cTOUx-v4KB$qN=#2> IWMBXy02BHW1^@s6 literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i new file mode 100644 index 0000000000000000000000000000000000000000..61f20281ac9a944ac3ebde49bbf70cce5c1b7c18 GIT binary patch literal 32768 zcmeIuF%iHp32KfZbdv;Y7A literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-attributes.tab_i.len new file mode 100644 index 0000000000000000000000000000000000000000..131e265740f37d77b7c4a3676d2a7704ca3e4a29 GIT binary patch literal 8 McmZQz0D%Su009U9fdBvi literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab new file mode 100644 index 0000000000000000000000000000000000000000..b3cd270dc65a4e775b0bffbd9d82567e2afccc2f GIT binary patch literal 4096 zcmbR3vzw0r2$(jE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0O=tB0P@2QN&o-= literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream new file mode 100644 index 0000000000000000000000000000000000000000..a49790f7c8cc48d81ac696566327d7aa19722c23 GIT binary patch literal 4096 zcmeIuu?YYm3;-|(2aj|10KuV)g8d)-Ly{BH#=042XYDbwV8DO@0|pEjFkrxd0RsjM Hd^qp`;{gQl literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len new file mode 100644 index 0000000000000000000000000000000000000000..46715fa2fd463d2538b1aa407ae094d66dfb3832 GIT binary patch literal 8 LcmZQz00TY%02Tlb literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len new file mode 100644 index 0000000000000000000000000000000000000000..01bdaa1da7d937c7e7d98e54ba912f88ab95c7f2 GIT binary patch literal 8 LcmZQz0D}nt0GI%g literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at new file mode 100644 index 0000000000000000000000000000000000000000..8cdc05ea7a4a3fb55c3692d0960ef7c3d0f49e72 GIT binary patch literal 141 zcmdOA@JLNeNi9+cN=?o$N>OmjFH#6dEh^3|E=kQR@klJr@J%cTOUx-v4KB$qN=#4H zVbE3y2=e!Gbqf&rQtC)6YsQOVoEsElABvNzF^nOfA;SE+I`d08y?m ADgXcg literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i new file mode 100644 index 0000000000000000000000000000000000000000..61f20281ac9a944ac3ebde49bbf70cce5c1b7c18 GIT binary patch literal 32768 zcmeIuF%iHp32KfZbdv;Y7A literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len new file mode 100644 index 0000000000000000000000000000000000000000..131e265740f37d77b7c4a3676d2a7704ca3e4a29 GIT binary patch literal 8 McmZQz0D%Su009U9fdBvi literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab new file mode 100644 index 0000000000000000000000000000000000000000..cc5fab236ef71ea581697f571c33e575460ea568 GIT binary patch literal 4096 zcmbR3vzw0r2$(F(*nq3fYS?v}C zW&=cac@E2w5g%gdd)fKkmq2$?)H_(7GdsY9H7X5=$)yEZnh362=91c!%Wlysw*X1$ z9C{w7u``EY8Em?5UPB6c)KJd-t+`iOmD{M~34wZtP28}_?UsmYs$I#o2g7+p&P)5? z?a4`5c~)l=8-A9f$;Eej)i&a&Nl(G=;q|~NfR};EPk0{Vx$C2-Sc@R%mZ@?2{%Xur0o6tNontr|{-65X^@q_BPSIXs&fa-|r6h>mo!cr=Y zBFn3cnV8o}h?`+A^`gF!Ip`<%MT8*)-v+_NjOrkB(1lAb5NLbue2ASVap?4M9vS}* zU;-~h^_Z4ru{?NuqD;B@Xu&L*^^-**WzHE&1i8?n303JiS#-f4d>8w?JbpU? zmP-HLHb5UtL3Qxh zKE7(umLQfbpIVXv_wa@qXkwgFY`%@y^1KS@w2kdFRm@(!Nvf5DQDd#T9YLuwk9?1y zLp>0-T>!in19bpmOZ6Dbl{P%Y^(uI_ZpJij%xbMsp_{QL?qgpMULty37k6QFc5%i-{AQM2uWfGE z-dKwS49nn}+9zE(24n{!_MvImX)V`;dn^tsCF}Ar;55FCIhTN7P%?Ylm zNROC$W{rBv?>yaD`nKT=9h_bVe_2cDZ#GkvttgD=KQjcXCgp^;_8YVV2ZsN(Bf-#k z6rv|CRRA~y3mV{y9)$0QWP%1-{8PGRkzb!!3)p`pn&z`XWcX2HrC|KCZCjH3$>6M| z;mZ(x2r@3R)92-fKvO8TdO?wA)aD2>{s;}L>>coc8s>3nod`##f1gk=#y z8;jQvaSL;Sp6L+0B@7PJk%aw7OLW~r1^^>)c@L&L2-mRts$EOJEVBXAhpn4D32Qj- z4hu#zUtq6@5yBRnMNeqi5mE~;1=Bco|9iM!H2ltUW-82@magb`sLWRt9?WX zXEih%qbozMtBO2t0q4Z9;l;XkPi}5tQo!|hE!c*=hIjRbO>8611k5qONppZV(`{W?-ynbLZoITZ?OZ{a3m31J!P_yx((KKPus)Y>%}2ij(4XB z!zQu|0WhpH&qREdP7DyFA#rzRdv{9T3caZXDhLJgS+YuLM(~(|j>n?I1kQGXQ4l?% zso4z=Xg%Hv))6zjp+l-4BnL@1${c2=Osw=WWx~b~knA~!swi4DibD>iE|z&ZoLdpX zpJVwqA$oM<80$PFW7W~^J7NsWiqAGKH4Q9#bdR8InPh4G3sYIuRSsDnFh9FmTDv}F zi2X@*sSVkwqA1B&X|Hj7$PV`H6#f$B}u6T}LuKC%bhoQB(oynz`c zwY8mm+rrHyU>SujdmBy*my^5yuv7aR#}t9HvoMD$>;fzo3@NMoTHVYZsbmKKb9i>a zaPzZ9=V$|6W}wcMDb>^g=+%TF60=gUOTDd3;McRMW7HHNTcO0%6`;*43dm~xiO{`6 zVkIAU#bd8~lm?HGB`aq}9jhg$zH5!Aksb&Vz#kIY8mx)_yy)VuyYHstf;Et)zZ+Oprw%mdV$#D5;?$JA8aa7t3=7FsV4oY<#YH{QVgA%7cY y3*WG)m4kl=Nw=SA>i@-o8V!&rd2(S7Fy;G!_%wVYt;3p$uNA<&>Afx2(CR-$+zs~t literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i new file mode 100644 index 0000000000000000000000000000000000000000..61f20281ac9a944ac3ebde49bbf70cce5c1b7c18 GIT binary patch literal 32768 zcmeIuF%iHp32KfZbdv;Y7A literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/constants.tab_i.len new file mode 100644 index 0000000000000000000000000000000000000000..131e265740f37d77b7c4a3676d2a7704ca3e4a29 GIT binary patch literal 8 McmZQz0D%Su009U9fdBvi literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab new file mode 100644 index 0000000000000000000000000000000000000000..b3cd270dc65a4e775b0bffbd9d82567e2afccc2f GIT binary patch literal 4096 zcmbR3vzw0r2$(jE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk0O=tB0P@2QN&o-= literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream new file mode 100644 index 0000000000000000000000000000000000000000..a49790f7c8cc48d81ac696566327d7aa19722c23 GIT binary patch literal 4096 zcmeIuu?YYm3;-|(2aj|10KuV)g8d)-Ly{BH#=042XYDbwV8DO@0|pEjFkrxd0RsjM Hd^qp`;{gQl literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len new file mode 100644 index 0000000000000000000000000000000000000000..46715fa2fd463d2538b1aa407ae094d66dfb3832 GIT binary patch literal 8 LcmZQz00TY%02Tlb literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len new file mode 100644 index 0000000000000000000000000000000000000000..01bdaa1da7d937c7e7d98e54ba912f88ab95c7f2 GIT binary patch literal 8 LcmZQz0D}nt0GI%g literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at new file mode 100644 index 0000000000000000000000000000000000000000..8cdc05ea7a4a3fb55c3692d0960ef7c3d0f49e72 GIT binary patch literal 141 zcmdOA@JLNeNi9+cN=?o$N>OmjFH#6dEh^3|E=kQR@klJr@J%cTOUx-v4KB$qN=#4H zVbE3y2=e!Gbqf&rQtC)6YsQOVoEsElABvNzF^nOfA;SE+I`d08y?m ADgXcg literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i new file mode 100644 index 0000000000000000000000000000000000000000..61f20281ac9a944ac3ebde49bbf70cce5c1b7c18 GIT binary patch literal 32768 zcmeIuF%iHp32KfZbdv;Y7A literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len new file mode 100644 index 0000000000000000000000000000000000000000..131e265740f37d77b7c4a3676d2a7704ca3e4a29 GIT binary patch literal 8 McmZQz0D%Su009U9fdBvi literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab new file mode 100644 index 0000000000000000000000000000000000000000..fdc24c9ad871c97776f3f2ba4408085a483ddd1e GIT binary patch literal 4096 zcmbR3vzw0r2$(?x3Y8D&l-}z X00Rs#zyJdbFu(u<3^2d|1AiFUjQ9$o literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream.len new file mode 100644 index 0000000000000000000000000000000000000000..a930d6b3c49e3ed8f18f22ab58f3c09f10908093 GIT binary patch literal 8 LcmZQz00UV703`q( literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.len new file mode 100644 index 0000000000000000000000000000000000000000..a9f80ae0249093f1db8b14f71053acce35747e3d GIT binary patch literal 8 LcmZQz0D~C-0H6Sw literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.values.at b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/proto.tab.values.at new file mode 100644 index 0000000000000000000000000000000000000000..fee7fa0d222c0ffc025843d820c33f7fbddbbe23 GIT binary patch literal 2944 zcmeH}+j84B5QaCjEYp-MOLpA4IXdS!J;Y7ZLz=@(;+!LAGIpZ$7ADs;Dhl8Lpkws` z`XIf^GxV-EeUrXN7qlrWn{<-tlW4L!oPz>xDu=!x-%69f7F9^ zu-Git=QMc#c zQmyQD;)#pPJ(w1GzN!bga1?3B)a8tj%5}w17Y=49vV+E<#A0RzQp)T#$Pf1 zn(;S`FEd_Y{4L`vjK5=imGSqCe_;G0x^%V^XvLe`h0Iez1x+W@7LdK zPk%o^a61Qrds`(&%f!ocrNLg7p;_s0DRHwEJyQO-;$^xL=T0Tgx0T3-PR904y>3?h zxJ$jKRJMEsTYp9;j`3n;)VrLg>eoD=1rK zinBiUQk(voP;}rj(A+bS4VTbJQ3eSnEDq8~2%LkOPe|RU{AdV_hQMeD jjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kgRc_9D*#r_JH literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream new file mode 100644 index 0000000000000000000000000000000000000000..ee294e7c84d9897f407ef2b2496008f8a4f8da90 GIT binary patch literal 4096 zcmeIuu?c`M5I|9fNhf$im=xM5!CsDdMHGV=aen6z{u|-7ZjRF!`epS)bW-mmr{*HN lzAD}>E1Q*+t#-%LfB^;=V1NMz7+`<_1{h#~0S10K@BuS&4(I>? literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len new file mode 100644 index 0000000000000000000000000000000000000000..79ad34c0caaa473fa89c25f48137424132072b61 GIT binary patch literal 8 LcmZQz00SKW05kwC literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.len new file mode 100644 index 0000000000000000000000000000000000000000..2a17e6e5bd9e7704741c2a3ae485eb2d2e302b87 GIT binary patch literal 8 LcmZQz0D}$y0FVHQ literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at b/buildSrc/build/kotlin/compileKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at new file mode 100644 index 0000000000000000000000000000000000000000..a9c334538b528a2f081e5495a9a67738d92f02e7 GIT binary patch literal 80 zcmdOA@JLNeNi9+cN=?o$N>OmjFH#6dEh^3|E=kQR@klJr@J%cTOUx-v4KB$qN=#3c hW#9+{$>--4v-o5t74zw3=a=MU=EdjcreSnEDq8~2%LkOPe|RU{AdV_hQMeD jjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kgRc_9D*#r_JH literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream new file mode 100644 index 0000000000000000000000000000000000000000..ee294e7c84d9897f407ef2b2496008f8a4f8da90 GIT binary patch literal 4096 zcmeIuu?c`M5I|9fNhf$im=xM5!CsDdMHGV=aen6z{u|-7ZjRF!`epS)bW-mmr{*HN lzAD}>E1Q*+t#-%LfB^;=V1NMz7+`<_1{h#~0S10K@BuS&4(I>? literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.keystream.len new file mode 100644 index 0000000000000000000000000000000000000000..79ad34c0caaa473fa89c25f48137424132072b61 GIT binary patch literal 8 LcmZQz00SKW05kwC literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.len new file mode 100644 index 0000000000000000000000000000000000000000..2a17e6e5bd9e7704741c2a3ae485eb2d2e302b87 GIT binary patch literal 8 LcmZQz0D}$y0FVHQ literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.values.at b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab.values.at new file mode 100644 index 0000000000000000000000000000000000000000..5875372349163668e6e0a816c8855cd692143458 GIT binary patch literal 55 zcmdOA@JLNeNi9+cN=?o$N>OmjFH#6dEh^3|E=kQR@klJr@J%cTOUx-v4KB$qN=#2> HVE_RD6bTXt literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i new file mode 100644 index 0000000000000000000000000000000000000000..3c9dca6e804dde62d9267a23cdf5f92ccad81958 GIT binary patch literal 32768 zcmeIuF%1A95Cp*^7{U=&P>K#p5b>d!6|lvmrAK7~>z(#IqikqIfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly dK!5-N0t5&UAV7cs0RjXF5FkK+009DD1TH+10_p$& literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/file-to-id.tab_i.len new file mode 100644 index 0000000000000000000000000000000000000000..131e265740f37d77b7c4a3676d2a7704ca3e4a29 GIT binary patch literal 8 McmZQz0D%Su009U9fdBvi literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/id-to-file.tab new file mode 100644 index 0000000000000000000000000000000000000000..8aad32b3b84c79ee82814f17430d858dce49687b GIT binary patch literal 4096 zcmbR3vzw0r2pB;G3lgkOdJ@ z!!XE#h?thdU=Y)i7({&z9W40=$oqx6=kDI)ethT70s#T?I=XO{&Yv&bWsxX&_oS>ZOT++vNJ ztTSSR8*Fl&5!cvaoo%jimn%HrGCQoW%SE1Zfj!Q%&m!+R%K>Nj#A&`T$04Wq&Pm1* z(@ZhN43o_A1P;46!KW{Hn1bHEB8SmiBiykVU^Hh94%PZ{xqEq2)EA$?w$ cP#_ct1ww&PAQT7%LV-{q6bJ=Efqzus8%Mj(^Z)<= literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream new file mode 100644 index 0000000000000000000000000000000000000000..eb6b8e7c433d7b7ab05468babdeb41a38fa0ff44 GIT binary patch literal 4096 zcmZQjQCi0U1ic-PcEf3)nCQ{VO&h@CY_%TW zLHu3+pMcFx4Z4*C;x9_~dH|w9_%w%_)f6iqLzo|6G|#qgHXwP!Uo{#adbQ3QSrEPR zYN;`ZZn$Y?0-`-vcO--8UzZb9LG-M^yT3qjF$zXQU^E0qLtr!nMnhmU1V%$(Gz3ON SU^E0qLtr!nMniyFApiin-{e&Q literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.keystream.len new file mode 100644 index 0000000000000000000000000000000000000000..b8872cd81c887ce026c3ab1e8b35d8ffb87a7442 GIT binary patch literal 8 McmZQz00G7s005!@ng9R* literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab.len new file mode 100644 index 0000000000000000000000000000000000000000..62cf1e59e1ea5ad4e4ef4e1a82d13f51a4251077 GIT binary patch literal 8 McmZQz00AZm001Na8vpOmjFH#6dEh^3|E=kQR@klJr@J%cTOUx-v4KB$qN=#2> MVE}>L9$$I5+#N=fvF0TZq%%d|SiUTn~RRS^e{@vo}uWFEhor~CC z>4NXQXsPFr@KEdKaJ6}tFd+ENy01L0h+o<=(6qvwdfs?Ckdd{ilTozo{%oLJm)r`ql6rg}H z1){jW-#HR~W6bQB6$Q*GFoFAB(Or6C&gxi|)&l3Tf2LbLZPR)l#-{)UC_n)UP=Epy zpa2EL6Y%2og591yC|>5wSWkffCR^q7F4uETE#}&b{l2T)W7i@!V^Tn=KpXbwtwd*1 zX&jEH00k&O0SZun0!9-^BOznz(kh=60`I&wHmp|s2m)>Wo|;uFo?M(bI{1GM zt&{7=b_ysFaNzY%SI4i6k{BFoUV#CWW_uZ3Ay;7@`zYy+Ps;UUI|V8U#IS#|KUTI? zYSZG}rvHz7C%!}$ioFr-;r!Rmh1z+3zCZw8u0*r&?^n;SO1&c&e4bux;Qw{kNScD* zgJ4gt#^D@B0SZun0)hxwmPyj9sjl^M>DutTaBTAW8o7Sewznhh_}p9mokq2Ab=Ln9 z_Q~LlEWdxE&eomfQh)*!pa2DyLLiOnj*tHRj-{BJb5cN5fjkm_?mV~KDEWtwi0Aeg zzn{w}3&K1oKmiH}A`nHMh0h7AAn6(=%{Z=~KinOhD<{;4vw2F z=7LyRGuy=q_^`gMYxn$JY=8EnKt%z3u4wtQgWs!J5r-!!KmiI+Koo&dTo;XB?cJhQ zz7fP5i9I{2Ry@X`00k&O0Sf3Tki+xX>te7&&pA1lBn`mbb-D2-Ehs<%3Q)kD0wFxk d`+B6q=B$oYQ9w+ACVake&prD>%wi~Sg$Dot literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i.len b/buildSrc/build/kotlin/compileKotlin/caches-jvm/lookups/lookups.tab_i.len new file mode 100644 index 0000000000000000000000000000000000000000..131e265740f37d77b7c4a3676d2a7704ca3e4a29 GIT binary patch literal 8 McmZQz0D%Su009U9fdBvi literal 0 HcmV?d00001 diff --git a/buildSrc/build/kotlin/compileKotlin/last-build.bin b/buildSrc/build/kotlin/compileKotlin/last-build.bin new file mode 100644 index 0000000000000000000000000000000000000000..83cec77d3b31697f2fb8751bf8fece35593587c9 GIT binary patch literal 81 zcmZ4UmVvdnh(RmAC|xfrwIr!1F*C1NFFU^^Co@kkGcUO)H8(Y{Br!+NsWdYu#WOE0 lfAeSN$RAJN#WOK5dNHsUmn0UIgalUUF7xF)jD_P{N^BoE5;9=K6=UoG>nl+gc-NJ wKofyLfZ?qph=#iYSsON2fE0tkl15D+3D<_?rT}kNHjoM?Aglz^Wgrd%00f*e2><{9 literal 0 HcmV?d00001 diff --git a/buildSrc/build/pluginUnderTestMetadata/plugin-under-test-metadata.properties b/buildSrc/build/pluginUnderTestMetadata/plugin-under-test-metadata.properties new file mode 100644 index 0000000..a62bd1c --- /dev/null +++ b/buildSrc/build/pluginUnderTestMetadata/plugin-under-test-metadata.properties @@ -0,0 +1 @@ +implementation-classpath=/Users/rodrigosoria/Documents/Rootstrap/projects/Android-base/repos/android-base/buildSrc/build/classes/java/main\:/Users/rodrigosoria/Documents/Rootstrap/projects/Android-base/repos/android-base/buildSrc/build/classes/groovy/main\:/Users/rodrigosoria/Documents/Rootstrap/projects/Android-base/repos/android-base/buildSrc/build/classes/kotlin/main\:/Users/rodrigosoria/Documents/Rootstrap/projects/Android-base/repos/android-base/buildSrc/build/resources/main diff --git a/buildSrc/build/reports/plugin-development/validation-report.txt b/buildSrc/build/reports/plugin-development/validation-report.txt new file mode 100644 index 0000000..e69de29 diff --git a/buildSrc/build/source-roots/buildSrc/source-roots.txt b/buildSrc/build/source-roots/buildSrc/source-roots.txt new file mode 100644 index 0000000..2d932de --- /dev/null +++ b/buildSrc/build/source-roots/buildSrc/source-roots.txt @@ -0,0 +1,8 @@ +src/main/resources +src/main/java +src/main/groovy +src/main/kotlin +src/test/resources +src/test/java +src/test/groovy +src/test/kotlin diff --git a/buildSrc/build/tmp/jar/MANIFEST.MF b/buildSrc/build/tmp/jar/MANIFEST.MF new file mode 100644 index 0000000..58630c0 --- /dev/null +++ b/buildSrc/build/tmp/jar/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +