From 4442459b24cdbac25cb1e4de11583d22c21452b3 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 18 Feb 2022 18:34:27 +0100 Subject: [PATCH 001/179] feat(android): set default targetSDK to 31 (#5442) --- android/capacitor/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 582552154..46eb59da0 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -26,10 +26,10 @@ tasks.withType(Javadoc).all { enabled = false } apply plugin: 'com.android.library' android { - compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 30 + compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 31 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 21 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 30 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 31 versionCode 1 versionName "1.0" consumerProguardFiles 'proguard-rules.pro' From 11b648d21567c5ab8e7e195fdefec1e1254ce62a Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 21 Feb 2022 12:22:35 +0100 Subject: [PATCH 002/179] feat(cli): Upgrade Gradle to 7.4 in the template (#5446) --- android-template/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 59821 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- android-template/gradlew | 257 +++++++++++------- 4 files changed, 155 insertions(+), 106 deletions(-) diff --git a/android-template/build.gradle b/android-template/build.gradle index ad085c345..414a402df 100644 --- a/android-template/build.gradle +++ b/android-template/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.1.1' classpath 'com.google.gms:google-services:4.3.5' // NOTE: Do not place your application dependencies here; they belong diff --git a/android-template/gradle/wrapper/gradle-wrapper.jar b/android-template/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 20926 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 i_W%EDg1>@4`_NK(#)z|gdCiK zZ19IgEQKVM;e!GLTY~`u6G=uzBTBA>r3SXu@HH_0ZQF6ePkvLCrcP-MXyt&CtBl8 zI2ywicWO8wRUWX&l9}W4lH)UT<0<%j(l1233wevM!-_fz|76_{OY^OCEQ4HeWgke1 zT=Z%Lhs{aMYNDJmsQ@3uVM*Y)O^T#8jLXRke9ss&QIC4~HiDqf%shkQ-0hBOsPn=0 zZM61To*2R1#}373ZXnptZ#LlLo(7x*JKzIHgRU}7zaxVv4mMKS44eyjh3GzH1TPcH zcy2H|*oOV|1Xok`jc4kZ-H@W`x-X#kBrF?T7;D9l>eZomayDXD3;#t(mdd2qwu<%z z+ge!1by=vGTFac&-%I3qNF?;KCr-x1P2?aL(vE{6#3E#O7Kj+O9|Oj5w0slB zbuj6u#UaYwoFmw_xK!j?o;{e|^l*l0YC+yEh}A9HPkz7nH`va*zd8DxZ@rE^6={FB zo29_AS6??>E~EhsDGZl-a6uXN<+^7zDnwncQHW zb1)(1r6-UOYP{gOjS7Xupa%#>P@{LUtq|pP+e2s|7Z>hnQ{C}55dNmD6fTrgRXG^X zMk5xB=dj#ng|0fU58$`k?J0y!{X65O=!xVK^wGemq-*T6}j8e{fyp6ivF=H0-3An--i1iZCR(wQrLTZX3(3!uc(ls|1|1^41alD1Y_n zk1%twmda_ZU7|eob(Fz1w~fsXV_^&z%|2Z{MmTnH32O#rZ>%)RP0vZRnGg(N<7FKW z%{{Hshli~sFZB&Dh5{yM8d$b9RBtiS=vI@8vDe%WkKLj4xs|pre4MG$_!>p<->kt9c za2G9Dpo9uDtTUpD#M4qLmdt(yIA?l6zMl95RAPZB*OJ6817Je9vhmh_OYKEQ3pg$e#kd= zS+58w2qL+ResowRR8(d<6Ql=(*kcX(V_?Zmm4#gVE=Cn5%0fEA#86m&00Ilw7SaL{ z+!N*e+0~n7uOq~w#>tk6yt!Dck5+8&UoVZA*j)~*)Me(Usnb0DPzo0hh1_lEHG-q= z`i>qi+USBOv6$*Z7gLZ~Ma;-ax)zQ%V^&)TgdrZL#ewL47*EPmumbs89H-{!ZhWi=h3Z7o-u%0pHduII({b zG0gWv?1NYPyGQhN=A8C0#V8juG=mbBf%kcZtXMV%b?5D>h)xDn+?jH};DCYzcL8CYeu^_}io=b91O0!EWBA4zKPe`HBNz&>|3V}A= z9~Q;P<&L`^i@c`xu%mL$DRapF@3<3lzNbiR%Eph?ZgZZazDRFAO2;=VD6RG+HT*-s z`XMaZyjcGpvYyH1xa0E>2Uu!(A4+K%krgojA2s2ci#MP%9KULUo;LA^zeR75pCz>w)M+ru?^=p$*4e31>5gM(vVyDpX z*7-K|mD?lPdG$(thCB{Y)!G5WjOl3cCT(^(aW$%}(jpy7y!?SlOvA!^S>)?eUAqvi z%I*y@Dp2f%f2yM@sJ37Sq5Pf~84|}2h?5?eb(%tEglv#kZeYcNNr}&@=bXytQky&0p;2y_R+cmkfUgKtJ?w<^QsY z7+*G#G&XnFVt05f8BxMt3GnB&{QfW7M1ZqIPld%Jg3*UQ>PNlqm^qMP&1k(I-?aVG z8JlXtCWuC;pfj>{mE^!wi!Gl@qKBM+zJfmEuoO{@6{(V+h|hJE*8f#dOkvx46+ePd zDKbxnYJ#U)oq$P$!;<8|{^zWURzDi*j31j5%@i&A=P%x1=go!#Zv=Q%nZXS{TW+1$ z@A-G!7x5Dh&yRk7euU3Alo0YmoEKgYxSZJa9**XlNjcWTH%thSpOWK3N&IPcTLk8N2nF8xf1Y0#tQ6`oojv4&F#dD zhO46h>Aw*r#qa_5INPk%b2?dVqNKj*Il^O|8Mffa`|9#-vHdwzD_HTG`>my?2Wa@q zP$7yLSRRKAC{&YyHqL%3utXyGtOuyhZtCXWkos0;6pyVP*fIkTT-Y*|wtj}Hu;(RY z{u&6Q))W@Uii8l2lZ>B(p64%|hdCFCB`QjhL{^Kcv@e5T_q zTP*jOG~#*Be9NZSe2wNEBkgYk$#+k}0LYFBQDzPU?p~uQ4MmCNHPBC+gzRzjP`??8PzSe%iSN z*{C3SdApv+Ht|>Y3l&m*g5V(su0jT0Z0(#?&9YH7RbOjH&~xTqb0Vg)Ji#TF#?F!YZA zYeDMn`+_q8@~m(+Izgyi#($|nT1F)Eo#IHO%cz( zs`w)iVPzu;o72xRg6kfRz78weFPK$8IGTn~mgv=UsF}4-aLUut~Qf|fN`QB>0 z!p7zw#Sa3(kbkp1Z9g!C4EoXyIxD`DvH@?A8W zKhaT)t>k}>E)Qmz^CspyN_=EJDv4h=LLXo$ydRcbE0v+aqT7=C&ryQMeTj)}-*$1S zb%K|>v3aR$Nng3%>XW~*;Q^vxflz0CIxuw{R!4nK?v~twaw=2iKU{ge=IDN0q5%zB zHA<~DO7EAeRGUY;3Lt}6q49i(988g{z1}T*$7RtUowzTBdcP>ngozZ3Og)M0e!set z5XT&VuHM|YHBi0+StfJF^yShq1l%%_{{7yr8n&Pm!lx3!ZipHHV@lgdzNa^uQ&63_ z`a12N10{uB69h+S@3a&IC{0vg*aGhGLowAqe}#WtVQvWcQ=+vb-ID?c78cnH0ME>o z0a~bv(7%g6)lB|on64UY4*Wt+lc!_!?bJSv)&7S&7=QjY#cgOG^=f@ElwwU1f@Va5 zHbH*M8zdB$i3B}xhBRZjr632IZrx}f@*&bzk7orie>l-rie3DPi}1XzU@YDRwFKmy5##?##FD26Ru#MD}NfK z5tuAq$9=H!Tkb~_T!>jyy^be_j;rrZmM_hN;a1wVHPTGP$ZVDs3h>)NBFEWxpM9d| z0yexwY2)CpoE|{b>G1-`xh8rVb+_S`3&P{`U+n0->HU{!*s{b zh0ps#6^qc4Vdotq#sXVQ{1U!0Q6P2Jv;upQRENURxu0Xq3x|&?Z@F#yw5IFmRkG$v z)O4w|jNG(&A#isVUSfk7sqE~AWeZ^^lSj6<9gJ!^gX|sQ_}OLB9rCm|6IM_4loGz! z;VXJ1o^%@XoVxOx``v`ic^Hcc&s z?)j5`Vbp5nK=nQ-x2mktC8NCJ0!{-yTeeg|Lsb!fdCoysq)iULdCGe3C-=+#b?4VSwxn}fKF}Z$udG5?r zVczA{U!wZJ6{Pi^!d`pdVaqiz$1$^b<63%Nw(@Jk+grk3W7zuIL+LFp>YgyQmo-1D zEIYg{J)K`^1XDM?>?yyDf@%KS%?SIl(qfqjQwf)0HBhas>TkHKqM}8UpX#0(U1`(( zkvW?bMl<_nl~;V6WO-F#_extCTd=IrEf3Dc@pox~;@HL(WO8C7pX%)>vuJ6w?yl-* zVY9K|o9msu=ynP3)}Vn3S8lU;i(&urM|x4Qan@i*^KoJ6M6K+s^=Iw!a45BCME&~) zg;#IX4p7u)vC%Uu`1)pVNRpo^{wlK(@%)3||2vHrj{dgvnjpEQ5QoZl8@>Q`Tyger zW*>|tJ{uekfQzu4d0T?a4ZR~y);H||zVnAiS9Li2H66W?%`@nCkXL0?_8ImWc4BUkbgv91o3du*oNcHF-6M1; z&|5JV4d*9Q$VBI+sy)RhJcF>zG&Y=cdD4lCQ;%^B`8sVJ51o7@-zcg_24|21-nxWg z=JXW+J&nK#A|nJXS47Q9A@yw`3&G>q=9O&^BHC;WT04y1AbmU^ti~CQVqABvaVY!^ z?}5Q9KilKbIq^3(umZp5hng5{##*BUAoOASe>Psul2||iY<`&F>(#R~ACY$iiC3b2Pl(ez*Bx=D~eCf`HRyn$-~KYm5K zer-XOrJvi5E^HrNE2)j~DZkSqmf2L4kNc>{+_;(W>t7He1+HD# zT22wmE#9rL=1*#cjhhXY#_n`2xIrL{{+6U-GCkflEj4UkI6W}ks%6BjTZ9lmtw)3E ziI4m7`pF$a+{boU{LL#&S&?=EFu{Gs7jE__Oo=N{epkUUqmG49#zHP)4*C5j2qrDi zEXy!31Pty*<59nWzIgQvkCgixv6VIQ!POhyCz|&sShkU*($QFGPCb+K?*k;Lh&Rl4 zjWufiEolliWh2@}9Oy@P707bS1c5pNSSXqQfPL&t9-lQK59(OQA3LjO#18Rktw6u#SzF46}%g2(@1pM584UD!=%P}TC+>vgp19n z{qghOzYsjhUm%?Zb4aL!(&k1+zE{MN*TWxQR@^l2Hf~^m@g}30leXq*C%AR_Sb&Vk zVkg6^z2}gl3W5247Zc9|*jK^AlgtVU+ZKGp$me6P;S3A=xusy8ax#Y*Wt8Kp1j6+& z3=Lgux9$m&+pew%T6L1vPxj%RG_#)lbj92>L#KIAyj19F!CNZZOr9{tC4BrqIL z`%dX?k3$SEw1Py4A&eIdq3Jycxy+@G6E2r4RA03gR}VXNv9`H@Wh3;fzTEF7apq6%wN$6)i--FS z+IMlqv+}31_B;aXi^f`Q7vtc*B~7->Ur!}HM)BnUVxaQ)bL;a?TAj3y9#T2uee^J5ohGslCH8ejViE@UFsnirgXB&W$+j%+hjvE26+6*S zTMl$sfpw)N6M1<|b0W0SQ6c;?!G$ z@rn0bBsGYhxMECJx=($!IwxK(I>>d$@c#Q%nKhi!^%fWIm!j)>S~+aHZ-P$2{^o03 z(2eaYQLj>-8pLt=0?Qzl_9sBVhbRQ}A#;3u7t*{%M~puxpDMZ&TdFMohAWOJG&qa- zAv(x(M~BG5FENOsXu};?PW##tw!7B`;mSXCk#x*Wbh__>J)?Y_x={F=?r{(2pTjnh zolI#ARMSY3@9*?MVyFv&jJ98zrFM!XzcZZbM1Y}usOAs;BAGukn1{!T17A8ozY+Tf zCa`$xiMs{tWrYC;c$0&E9Ll_b%HUm@>m@0*^ z60ey`T-+j$OJ0gs3RKtH17i$mM(Vbrsk*OmY9Ix5SOsI(>OA=@kBZ%bMA$H9jMP(y zh%Y!ou3F_4Dw37AIp<0FkRrfNX7n)ywaO@`%19+4e0x+8M@0`^O`-)ut2n|Ys{-@C za%*GCyHLZ zab?Ca^+`6^c)}m_j>6f5tnz0)mYAqhFaF5l+KeQ4Z+V1iB4KZ=hGE z4W@qHd)fW4XW_w4Uusu1GiRdq%ZRQ;Gi3%96y4cAk_s^8)|`73GqgFR8K_;)`)NqAn&=vRs!_HE z9ZQrjY+sd(i;=F?#9MYU=X1-`V)c*iAuazFs=Xmu878=4`No zU4!wq8cv02z%=zfZeOZpJ5s4w>#k5f&pF9{DUp5N3x*X(lMk3m*Jk+DRc2TXYM=Kq zhF=oB89Luc_+F$G#MPrYK0mE!zeQk-8&J4nE3|n@abjRSe234l=auM*P&4GSI_0bO zoMW9G^C>g~;uPc1p0SV7Bsn@aj&FEK0JyJin7YzGQ@6)3tH70Vpl2)_v(Qqwp)wOCi#~RbxRWQ-9ywE z+e%G&805L5f9UJE(;fC80D7>weaPl=kLxL6ztg&H>js>0)EIf_|8i?`51~A}F6nGO-+pEgkto<8m%#+ zVVlW=-<_M<$od-d43QS+zNwqhSeoFTnDML_L-RH!?R2NcX-}U{>*BN{S~U_WiLw0| zk<77*VKj!XD_C~kPPil@7|2z;l6>RmmG{&n4F8I6UR4uK+tkiqG5GM?mul-)lscnSGV$uT1_C&R11T% zC!>?g9H#C!mT=S8qUk7|d`vZgsB7{1!U~fs>bRM4{`L#{9AjF!y7GU>$p}2J&^&e+ z2b#&Syo`W0$QQ#C^WWF6QTk-?1!Yle>ug;+SEha1kU>#V7JIZGBq2~GxmQTpBu#9W zSM-72%J#KVJ(sE8`PvetYj&dZBY%Z|_BhUK)=CLn5+*F`WIi z^W!kq3%$O(gW% z;5#w!eLtAQS6UKXa0;K;#D}^ zvZ3Ix!CO4`Of%#ZA9^B_vaCFZa~n%LC42qdcw?TSX_d1qLw-8)(W4E0(Lx@pWlGGO z-@aO&N_o>{{Z4vI(<}@Nw{h8AwTaBna5oE3lKt2>Px|2pm z&2TpT&MW3^J1iS`T-w~6O(VsDP_|i;-Pt6uSC_T^9X?mtHjVF+g4nifxy1+iqgFGf zySG7%tEJA(RJrM;BA6h20tso-aCrrkXYlwy1D)crNPZWVC2PapW1E&-V_hcpR|XA` zs4OaLF7JUhPDAi!ihwOrJgg?W>FFSZx16+& zGYPv)v|<rG(Di#UwtddEW7$_&tNxB8o;j{3T9k2vX+s zz_QqW@P2HsPxZcgzxQw8 z@&-!!7Hn?Z%N3-Qtkp!I>n}Q_w-sR-y_2+=5(&z~f6JF){ zOhao=c3S zKvsfi<5XcnF$s#qsOI4<;#GJ6|YsusW7{nIZiTM6d$T`L`+pHi$& zMSr#KbV-%6I1yESl*Znoty2UP0h*C-8p6!+PD8Bg!+YM_eJ~h7rpGH zZ$zDsM^ki$l^~JmyZU~0)%fl#rg%|e*phm>M~ZFsu3J|QI9CIBtSgIgf!iCS24RnP z(m$dJpM~j=Rd1lx;)P-@DgITC2E+r6uiZcL?=S9kR1u_m5(f4*Da1Bdc?u!$ck zfPuj$j<~@f&cp@Y=w3Da-_gB#c$g8C3V4`Nlp6f-M=(GoZQs&1cnG;>h+#={9#2LX zrW4F_DCZQbB zGrh?Rf=+j9`xLGjY9NrUUC|gL8|2ngaB5LOpk5IO28#A8WBuNlJv-O4K6&>j>@Hcz{b9%rAM7?2>~1;ic05`yG1-)WOocz*wJEFv z)+OK^y%vVlNN8~I!y_8%IjPLSq*!VzUf_VhdzfsEYNptTKM?#5<2f2Q2xt$`Gms|^ zl7CM(;d(|1Qc_iXO7ajIuNyVQgO*wFe@wIomvqPa%W>fRxLDU0(Vp(|Q|OKe`(+PI z=W1}V2#u*vB*}#cvF-@W1`?y_<=KHusRC$TKzM_AmiaDo=Kl@;WLMgQM|5Bhnm!FB z6~*UUZ8!z{Cp^qo>~|FrGEN~_UiHS*1;#(6grp95K`z|EPvx?f^#`ctO3V~t8zkw5 zqb6;{Vf%k5jEj;bQ=41CcZ|4dpM}4O|94cfhLA+=3jqd(``=D0xl~jL5M}WHQRFM9 zN2>>yg))pI6GJJ%#H?`ZpdI$B;d9KP`iso5eNMb+en^h#LuG`mNls4|kzHYSYCZRb z=Vm=~TL=I?Ae0BlAf1mav=x=9$8Lqo=y;=C^f?lQWk&IWRi0jZ=?pn-gG`!zhjv{j zZM2yPwD`;5VYZw%^VYC{-r4GAQuG=pP>=0(Gk>{ZsfKrZRKKsri{6%d8&arW%|hpG ztNx$A16FHOhU%vii1oJ6lr_jij+~)Zp(&w;c+2yxcz@N+Yp#}tFFov)yd2;1s`WYS z{%E$Jj`4R_tj@?^`fs+QE-8f}j+)*iR+Xz@>+yo<7SBY8zdf`YK1Z6?{ubBHh zFmY>E5tgnuII4UM4#bWRmTM{f8dUJr!=z#)J{Ilf5`tJ=0ZCAH2;gTzcvb}*up0z; zZeLIovm2^@?yMFIYc|aSdSkz~AzMjFC>;*cB31O+Oh_#TgcpV|{#R)utyK^l{ zb465cBpZkBjiWzlp>~S_gv2AZG@^cX4MZ=^vFOC>H5sGXLxCI|ON#Iz*NopkDA_)d z?Hatmqalapt0QkbJ-X?>;>IivQqY*(IlHu`7|~(==4h~lH*fg8o1=zsUi|MvB7q%w zKsXg+fPpbGfPwu;M&%_;j+Q_IsG7P>yyzoCnC+0Hf6$poL6|0^kmEp5&?7Eg$lWK! zOrh;|v%nfO*J8HR*6P~+7l94Vf@&+st!XzlboJ70?SIyGZDV)&ZTS0_D*QX`%^W8O zeSiJ?8v1vq>t)Mz_Fm{T&*wD!U&jp&D#QM77#pTjIkg|txC`=$WljWHK;;6)_-XTx zR2S*PbE1QMew>mYvk8rv3sZ3Sc7keIP6?;CTR#Z*no2Uuot+cPZhZ@l3Y=vE z({1#LO3w9BOS(E#y|E_rQo9)zyqpsT2;lC~4Dm{M4Jq>{OLa)5C+^&0W|3}bW2-H^ z+!J;tf0jJtfYqm-c8K`H0IN{#nvxgd@9v`7#3iJL#Cs1%9U_NeKWdL+@$!jFG_;X& zV;Ag_%4A;c(kk^JJ`~T_tDKugHX*tN`uIxBtP*VB3>KQ9&Otk+cMh?;4E5Mh=f3_* z37q$#ct#i{(*u5F_}~ty@tiiAwp&Cc*LJpBs7H!_k|@ziau-~kfdmg%>OP+%+*C`8 z1Tz9;C(^NP-*N6%ZW`KkaK-TlRn!Wp!<`@Qks4j?j{H3{KBb`gksEl`nCueJZxnyw z!%6mDe(AJ1!vW$HSYk8_A%YUFAw+|P?tU%n57gLt^9B3^nav2v%g(7*feSVVU3V3- zO!j2?LieZ3pRHUDK$nw&*h6bAV#{V5hn+*GliYMGqC9jgHhwyuh6>q^&a$0eqSvWy z8LT_(K6aZY&U^~)o}c`eby3q|bA}~5Wz9^L;-qzamWDvu{I?Pg8O(c%^w$EF-NN=~~S5pa%#NLgwE$~`97?YYaH9Kq@7C{4rgo!gL7Hf&(zV4NW zkJ3f5cBFNH^(3Jct$>B*Tm|8zUgAoMuVY)0JUZdC^J3jbZwokiXb1VU!AR0EU6vY4 z*+)f1FQb+6VfEZLcW1WEx=by<;}m^O&G^J6sitOyNv?a#Yn^nP?_gSA9!Nv=}wk$LF&n{hSA@;phy)TRM2d34U|Gfy1o$hQ;(Vu2c`4;NXm zlhZ-%s<%a-gSD=UcFU5%@8&0j2p+hqLcIHv5}PVdMmfK-0ds@j+Ru)3MF^Ww7Mob5 zDMK3P&>Gt+kR9U?$`)Hc|5}Xar*jz3qR_L{OiJk2fwh$-4W@G%zu;Z7Q0FCc=w|5P z5cD08=rGo{qTH;JZaXw{^cf0jO(y)piPz+iOu-F8x8x3EU53fg~qnR-}(=Gvc7I3+)QbLEZb3;~feu7cUEY{sXKijeF zVJ${UB*{dA4eePwD6=aya5HS)1WefN+TvX+vMOv`N2cTsSL=HF4MgF#)_(6+As4zm zcaR{RWjVp3BeCh=#Ej(4X^33FHG?%DB0xpTZc^#uy#zRlN#ZDK)wyEw2tXm@(_K0&iKy}VnnKX{*bjEciAk_C5}b1fiyNZ8|B7$9=s*(YmmRmlH&eRBB(h-dLgdf?2|8 zSW~BfI$u6O%l!)vFgO+S4WHTFbCh*0p9z1H%)KeX z&Sd0O8poeuz#M>&N`@b}hGoNzqq<_I)!d928kFotHpgmR4Jl~~-3Q(u4e?vi_mRx+ z8`h)kc{Z>Ob4oAuB52LD+ox->i}g;s-iUJJoqo?i`ob(iQ(=(yc%aFV4>Yz>1MYT}o;=?_^ z&&jc6(${hA8g)2)hXn*|rczoyhIYlsO*>WFj10D-UZ#=$*g|jY!onI|KJSYPy=EMg zGz-ISU-^O`*w%zVzwTvzJWFmNh|nw}>0}l_3JxoUfRjp6-bB0XKfY$Au+v6i$b|`H zY|;1jZXc8#GA7=Xr5Hw6WNB5#fIr1*H(9b;-ajyR=1*20R0Rws5*BlxEc7}RYcvhP zvz=mTpGOeRJ_vhJpQf36TgwhS$So}|QD8g6l`#>lcJU@z-^rmbKMcH8PH*l8c>$vx zqCm)V=*URppX7OQW+u0g>flsW1(F4PuC%u;?!#!*Abz@Zrq#Fb2o5KZ`span{@)NV z|0*AgQ4E*$ZXlN>7Z_54D=sg2yZc`HD>Z*cDO#f2R4MXTzWJD>rW5Z!^)bue?x^K= zvu^%jK;1)A5?}PlNk%j5#06TVbypNYN=HP$d@lYUB1X4CLfC3v`nOYTJfZT3hXWjj zM135o;qp6p-rr+PHXpxc>Tj!g|0MyT`$Xk}OK+2YQ2m($i=OYs< z$CNPS7Y`eKe@v3|_&M*uHLzYmP2t{zf7gu@hIe6ur062@qs?)TA*siTWv}kfcDS| zK!xGzszMroZI{%4A`Z3*hf*Nc;oKkcFWY$1*iB}c^6|jU zQdi>3<@az`aW{H69D(yCCW=LqUz%-mD%O4>wUAXLFXC8sjq0vxeArF*YTG`$>JRfi z0S~!cxa;tj-h1OLSd;JT3|BwVVev_f-5fF{+j}40xql&2;IPuOhul?!R z2q<*?(n@AiipP`;uz!PKFHpywDHvN*q7hEN2F4XRYRARdEwa&Wip!2hqSr6YKb&1` zT6Q|3CSagOD^O+XCYM?p%IA^9bKUQK05N(N+<_(BJ8^;*o25ic+sMh#$f&RqZQY@1 z_odtGgcUP!yCuRk1a-R;^ZTM4D2{t9_pHCiAvK;Ox61Ena^8?=EwLB0Kc{U-KvSU^ zC1VPin}a!7h+SE-2br!8C32kHSJP^(qOSS?R6z~(Fq_dbuGUPcXo>NnkKmm#8H}S^M1BcyM>F6z z&@SMGd0GpCPu)>t;77|6Dn21l% z)N~H{ut=4%J}_w+7@b$7658md^p#QN#Wr?M}L$7NS`QK8@8_BQJOBaq@TewO z?03~w`8teD{qv;U*gs(jp_d?E%x z42@*cqPz)^fd>PUndY!fa!|bdBYP3lJPtp9Ak@w?>M9!bSF}3-D;$5%tC`sc-~^0{ z>*?0(OT^q@%pHmz&hYmRhA)1eXS-3o!fK}{azeHG$3EMtm&_aBZBOHEi#<|K8`jS! z_5swyyLh2*+|#QSe-yHq2U0)T6T(hVyxzsXSiR;})jlq+2rtrRloPiZI!DgrJmUUm6Jq}duH5rMu}ZTv@XhSK4jKa{r-Z0rlk zUfnm8`od)#0c5Og1Rwnnlupg_YVxU#8nOPASm3E5n-p+`f~`ADgY z(9F20)1a>gm&VoRHQ!j&F|4(|1+f%0u-q%6yN-5`IJb^WFmo_F4-!i2N*p0OE9;vh z>69t7q{*{_WNYagYoRs&d_`JlE6hh;qC4mFN(LU)(p$s`1xi#)x@Fo=D%)|D3FNr@=0)wt1~Jb~*5k7iL?_cW{Kfb2riV?uj3ZQ`H~(5Sx8 z@(6oNNOz^LoFt>_EV2FpLSd1X@N)v|;K!yi zapFuxUD-7`0szua~YNc!z-yv zFzwt;DKM=6l%M2|#hV`3E5O*44SK*BHmVVndOoQ|yIr9nhc;?i2RGvr>>4YBJ^4)^t`YeDcRN1!0f(9h3hKAQa)1tlaSJ-Y z-1%L59nw)>QmF5Ps{dRC>dfqRJbCY#JKzKAIhNSO$P2FTlg08&9Mp{Ov>my91{))D ziy8byQ?nw`BsypnS$XEtwD2pDic;AFavxs6zUm zp}koQ#NGDgOl`dbol|sgidy|`9qE~v|5lRSL?1m6-4jfVcm$%o;6{A8X1wV1eezhu zR3e2p30kcy*<+_XZlN$FuV~Wgh|%m?!!L3TACuazm_sYox5G?{mOyCjA52|QU`*1O zrNVGH0~=ySZ8HVN^(6RyRW>kHN2sP`ms%(S0)6bkF{@(U5wwzRoJ92-yHqZuyrEru z;VF2DVpwEI%>PAY(Jr&pyh4*fS=aPke>4e5fusj zQII@ma!pLDA^mwD#E`ezsD$f7cf}gN1HJeU6{`!ZYdQan!^@Y|Hb%&dLB@C0D%MZn zlcQ(R02vqRadm&P5T5kMKcvd;3CwRc|H{Vkdg8eG6gBXM!xA)G2y!OBcXD_KE7KEz zl1Gja`!9RxBjHqV|F4VdfQD=9!s7}e7@ZgsW%NOmga{*QB)S;A_eAeC-i(q6qTldz z(IO!sAxe}GEj*%)-bFAvd4dS>Pv(E`{m*~yy6f(}&$sv8_pE!?I%}PMK3D{HCA84V zE~fWJ7x&+*m=;_#>~nSL4|EZsJP6?v7KYVS!)Z9IypZl~r`9_J2^yhMNXNOzJA1{Za_ z6>v8PZWDpafs`YR3~qGyZ@u(?)M6Xo9lYV4v7u1iZKc?gVUeR_f-&rU*B`);qEMDP zH+UiRc&CYqb2}gRg>l~7`HE+_Kd&gcjZ?Ng>XGI3>m{X%X=q4xb|pVVMNZC9J1i23 zTQLho*(@&ip$;5pCv)<8yaisjG6of7NsQ+lP{t_*D@x(R*AIky?|b=-Yi_G-=y0#h zk>p1H;W>@1(lKuU$TT!61mZ!cl`eLdWjm0J(}kI|hlaLGJ+b_EN6*y}cP3SA3lgHcytA6-jzbi^OxnBiY=YEPESFY`k16Q!W1B zZ}WQh!84d^ab7XXuEZFr-jOt$nyj^dG0pcx`{dq9_6MO(sSo1%X~{A!a|REvvWuxn zl9=n|Uw5*FUwDhH@)}omh&#FPnUy#c=XA-~?LZ4}Key{L7gZ9SS~3}ltp8lCcP=pY zT9z`I0P5Xj$q@|=+EpQHfCgj9YmHvc9-KZpFs~ZERq>QQ99Q?Mw1DdPJ)e2z3U}m9 zT2e3hqJ?@BJHcaX4oV56GRH_Hc2PscfRS9T#M*nQ!r7!)S8K4}Y^Rz$HdHjakw)#P z(t(1~Qty#AhWC^@Z4Te^hi8C|0<84zJ*cUAjnZ3JYMypFN2B_yt@dFtdqGZ!rh$U- zk3pW=idJL#-vvx)^V)FyFM1U#rUux%#CL@!e(JnGeduO8G%ggEGFBp+&dFn$L3?&H zAQNMbj=+V1R=i{;YWY9zhxlf$xT23&;p zkMTv|^-9_sZFD7f~qVUUOLk>bckM-SEc7)Z1#ViqwaGd9(-Aj~n9S7;{uf*STcG4d1 zh|-Hu$%xy3N!2&2azpoAuW`cSfiH38Wy=QYZ$w5IybfDizwh<#O@95n*E-qpZewrq z^N_OBenePTui;XC3Q{OUqWU%@WcOuQxsXb&+s#_zCn<#&@VVCM_x(a#USLWa?jawh z)VsY{zFF7{HZuM7j6pyDQK1zBtgm`^szFWv z7h@*$Vs$gy>oF-ic}e^9jwg4K{%r=*(gs(gD#q7Wy2~V;Gac}XZWYcoqiBQ8rd^ZA z)vY1ZS>02@W`h#Uqw;b`!9VqtOT!-|%<9X=eg zFLhk3mu+$`t6z$ef7&p}ASkOGWrsJ8U~QwHW3;SB_fTd0rrfe%iIvv;Rxmrrze9s0 zrB`6$qTk}>`=s5~^^?TKA{w%i4!sOZ$S@8DW3jrX@qbdXF$Uf4WXopWMfJ@FO`7fJ zS|K)CIiHm}fkpH`^D8ZVbKrM!qQB_m#4dLO?z;9#G|Z^6L3Oit5if><9=t_0H{j-G z5E{<0KHYlJ_1Jqt#>0+iMz5l8pFCByW}En@PjT-W%Tv6YlY$FEeNG{pQ%9}S3XNN= z(eXZ~RM*+bI{52sHoo#UupkddmEXkG;y8QWYS}c7+a7RtTAg)0{d&>E6D=CHn;is$Y~$wiQpzLV-d%8ck;ZSq>MaRF+9Ld3~Jt|3hk*Jsbp;r#yyRj zF#kbknt!cNP<}QnfOxj(+n+n-{wbK@E9y`jN3|ZTe{cKBWCNOfVmQlS0j+NF}!> zv7G^D$KZ_B`jPGl@+B{4?W!_wN}a3Rb)fk$acEKyHIUIF-ER0(*h1x_bkPV*)|teIdxCk3OTRWw?p;qE8j7z^w3cf0D)ghm{A)QdJrY30o zajOp7bxUaVPOIyKZB#sn=dHajw7~P^tGz?ccX>tb^Mik$7MgJV$YCnDDKa==&nsr% z@y)5R4+BqZ*icaOIj^k4E9ZVpzGG@#3|fT#7IXei!$E%j@AO&*44W#3)5hN0RKBrw zx$=e#vvR4Seglugurm_{K7C!+zgAhc*4W`IEwO54A`U?RgL^+npZCRKhsH zTe3Xs+vb2WRfkgKmLo=AW1>;y!EC$=j)XO4V;r3ik9nj&d8A1j&VeTyBj_Q~?bnp9 z+0au=+KQ#8Pqvrc8{b`RR27HU`5_o85Z+V^hwJyscoFJ>BR#b|k^$_CQbovY`R11> z1m{y9AJ_FSebqAlB{7GL4twf|U8Z6envXF?iI{2AI(it$7#b01X&}tS5MA`rM zowK)qw0lJHWL6bOcKu7F0Ila_fDJz|V@?;)@(0)E41rXCP-$KcX!i%hgRg)C3v}Rz zc^IG@L{Qnl{dpY#@*6mq3I`{`SbyaL#w@^qGz`(?89_^MKmz+%qS_xxO+>86&{6-L zWKlROiqOde`hJ!G1RfE^?$4?~Pb^U^OJMjl8lb@<40s<;H036FODHZ~?mK1@#e1dqL7-fvm zbFLWt@LU!YT}A>VB}7ofvNUk=f+#L7D*_uYiP3sr(-VTzfzB&1K(T^o;;P~xnuKcp zolHn2p%Vcz;l+XBb}+e15cI$!frVazhyYs#{yQM!co;x70Pf+PfQdoSVpSd#nScuZ w|E&DqkiEp6nWHb}B;da$<=?F+{O4J~(cC2_GD0yC1R_ni)(HQ!%J47kKb!F!p8x;= diff --git a/android-template/gradle/wrapper/gradle-wrapper.properties b/android-template/gradle/wrapper/gradle-wrapper.properties index 3c4101c3e..b1159fc54 100644 --- a/android-template/gradle/wrapper/gradle-wrapper.properties +++ b/android-template/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android-template/gradlew b/android-template/gradlew index 4f906e0c8..1b6c78733 100755 --- a/android-template/gradlew +++ b/android-template/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" From 8793c58f24611d7780aff80d547b367f4114f7c5 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 21 Feb 2022 12:24:28 +0100 Subject: [PATCH 003/179] feat(cli): set targetSDK to 31 in android-template (#5443) --- android-template/variables.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android-template/variables.gradle b/android-template/variables.gradle index 967549c54..bf2bbdc27 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -1,7 +1,7 @@ ext { minSdkVersion = 21 - compileSdkVersion = 30 - targetSdkVersion = 30 + compileSdkVersion = 31 + targetSdkVersion = 31 androidxActivityVersion = '1.2.0' androidxAppCompatVersion = '1.2.0' androidxCoordinatorLayoutVersion = '1.1.0' From 0a07648b4d1c5fb1fd7de3c72ac1bbcb30f48203 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 21 Feb 2022 17:09:35 +0100 Subject: [PATCH 004/179] fix(cli): Use CURRENT_PROJECT_VERSION variable on ios template (#5418) --- ios-template/App/App.xcodeproj/project.pbxproj | 4 ++++ ios-template/App/App/Info.plist | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ios-template/App/App.xcodeproj/project.pbxproj b/ios-template/App/App.xcodeproj/project.pbxproj index 28c681a57..64b192a61 100644 --- a/ios-template/App/App.xcodeproj/project.pbxproj +++ b/ios-template/App/App.xcodeproj/project.pbxproj @@ -346,9 +346,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MARKETING_VERSION = 1.0; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.App; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -364,9 +366,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.App; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; diff --git a/ios-template/App/App/Info.plist b/ios-template/App/App/Info.plist index 3dc8c0501..c32175327 100644 --- a/ios-template/App/App/Info.plist +++ b/ios-template/App/App/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS NSAppTransportSecurity From 55bf004897b3feb280ab6b6575d2a2c1a0a183e2 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Mon, 21 Feb 2022 14:53:06 -0500 Subject: [PATCH 005/179] fix(android): compatibility with cordova kotlin plugins (#5438) --- cli/src/android/update.ts | 68 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index 6978ac8cc..d3459dd32 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -293,6 +293,13 @@ export async function handleCordovaPluginsGradle( config.android.cordovaPluginsDirAbs, 'build.gradle', ); + const kotlinNeeded = await kotlinNeededCheck(config, cordovaPlugins); + const isKotlinVersionInVariablesGradle = ( + await getVariablesGradleFile(config) + ).includes('kotlin_version'); + const kotlinVersionString = + config.app.extConfig.cordova?.preferences?.GradlePluginKotlinVersion ?? + '1.4.32'; const frameworksArray: any[] = []; let prefsArray: any[] = []; const applyArray: any[] = []; @@ -328,6 +335,10 @@ export async function handleCordovaPluginsGradle( prefsArray, frameworkString, ); + if (kotlinNeeded) { + frameworkString += `\n implementation "androidx.core:core-ktx:$androidxCoreKTXVersion"`; + frameworkString += `\n implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"`; + } const applyString = applyArray.join('\n'); let buildGradle = await readFile(pluginsGradlePath, { encoding: 'utf-8' }); buildGradle = buildGradle.replace( @@ -338,6 +349,28 @@ export async function handleCordovaPluginsGradle( /(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + applyString.concat('\n') + '$2', ); + if (kotlinNeeded) { + buildGradle = buildGradle.replace( + /(buildscript\s{\n(\t|\s{4})repositories\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})}\n(\t|\s{4})dependencies\s{\n(\t{2}|\s{8}).+)\n((\t|\s{4})}\n}\n)/, + `$1\n classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:` + + (isKotlinVersionInVariablesGradle + ? '$kotlin_version' + : kotlinVersionString) + + `"\n$8`, + ); + buildGradle = buildGradle.replace( + /(ext\s{)/, + `$1\n kotlin_version = project.hasProperty('kotlin_version') ? rootProject.ext.kotlin_version : '${kotlinVersionString}'\n androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.6.0'`, + ); + buildGradle = buildGradle.replace( + /(apply\splugin:\s'com\.android\.library')/, + `$1\napply plugin: 'kotlin-android'`, + ); + buildGradle = buildGradle.replace( + /(compileOptions\s{\n((\t{2}|\s{8}).+\n)+(\t|\s{4})})\n(})/, + `$1\n sourceSets {\n main.java.srcDirs += 'src/main/kotlin'\n }\n$5`, + ); + } await writeFile(pluginsGradlePath, buildGradle); const cordovaVariables = `// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN ext { @@ -352,6 +385,26 @@ ext { ); } +async function kotlinNeededCheck(config: Config, cordovaPlugins: Plugin[]) { + if ( + config.app.extConfig.cordova?.preferences?.GradlePluginKotlinEnabled !== + 'true' + ) { + for (const plugin of cordovaPlugins) { + const androidPlatform = getPluginPlatform(plugin, platform); + const srcFiles = androidPlatform['source-file']; + for (const srcFile of srcFiles) { + if (/^.*\.kt$/.test(srcFile['$'].src)) { + return true; + } + } + } + return false; + } else { + return true; + } +} + async function copyPluginsNativeFiles( config: Config, cordovaPlugins: Plugin[], @@ -421,11 +474,7 @@ async function getPluginsTask(config: Config) { }); } -async function replaceFrameworkVariables( - config: Config, - prefsArray: any[], - frameworkString: string, -) { +async function getVariablesGradleFile(config: Config) { const variablesFile = resolve( config.android.platformDirAbs, 'variables.gradle', @@ -434,6 +483,15 @@ async function replaceFrameworkVariables( if (await pathExists(variablesFile)) { variablesGradle = await readFile(variablesFile, { encoding: 'utf-8' }); } + return variablesGradle; +} + +async function replaceFrameworkVariables( + config: Config, + prefsArray: any[], + frameworkString: string, +) { + const variablesGradle = await getVariablesGradleFile(config); prefsArray.map((preference: any) => { if (!variablesGradle.includes(preference.$.name)) { frameworkString = frameworkString.replace( From 28eaf1851fa7a912917dbb40c68fb4dd583d08ad Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 21 Feb 2022 22:37:56 +0100 Subject: [PATCH 006/179] feat(android): Upgrade gradle to 7.4 (#5445) --- android/capacitor/build.gradle | 2 +- android/gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 59821 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/gradlew | 257 +++++++++++------- .../build.gradle | 2 +- 5 files changed, 156 insertions(+), 107 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 46eb59da0..bef91924c 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -17,7 +17,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.1.1' } } diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 20926 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 i_W%EDg1>@4`_NK(#)z|gdCiK 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 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3c4101c3e..b1159fc54 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew index 4f906e0c8..1b6c78733 100755 --- a/android/gradlew +++ b/android/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index f094fa52f..3e95baf76 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -9,7 +9,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.1.1' } } From f79fb1e9c84d4c92948f06170b92b38c710f83eb Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 22 Feb 2022 16:01:10 +0100 Subject: [PATCH 007/179] chore(android): Update test dependencies (#5456) --- android/capacitor/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index bef91924c..40cc82ab9 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -4,9 +4,9 @@ ext { androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.1.0' androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.3.2' androidxFragmentVersion = project.hasProperty('androidxFragmentVersion') ? rootProject.ext.androidxFragmentVersion : '1.3.0' - junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.1' - androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.2' - androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.3.0' + junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' + androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.3' + androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.4.0' cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '7.0.0' } From 79b4a3c56ce9704bc8f5b0b8ce6d5e60f86d4d2c Mon Sep 17 00:00:00 2001 From: Seungwoo-Yu <51766845+Seungwoo-Yu@users.noreply.github.com> Date: Wed, 23 Feb 2022 00:34:47 +0900 Subject: [PATCH 008/179] feat(cli): export android-template activity for SDK 31 support (#5351) Co-authored-by: jcesarmobile --- android-template/app/src/main/AndroidManifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android-template/app/src/main/AndroidManifest.xml b/android-template/app/src/main/AndroidManifest.xml index 0f6b67b9f..79a541664 100644 --- a/android-template/app/src/main/AndroidManifest.xml +++ b/android-template/app/src/main/AndroidManifest.xml @@ -15,7 +15,8 @@ android:name="com.getcapacitor.myapp.MainActivity" android:label="@string/title_activity_main" android:theme="@style/AppTheme.NoActionBarLaunch" - android:launchMode="singleTask"> + android:launchMode="singleTask" + android:exported="true"> From e1b7f49dbd5894bca0d7a56f308b9d181d27c46f Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 22 Feb 2022 17:19:35 +0100 Subject: [PATCH 009/179] chore: Update tergetSdk to 31 on cordova plugins project (#5457) --- capacitor-cordova-android-plugins/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index 3e95baf76..c77973aec 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -16,10 +16,10 @@ buildscript { apply plugin: 'com.android.library' android { - compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 30 + compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 31 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 21 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 30 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 31 versionCode 1 versionName "1.0" } From d95a6b503c3835148c1d83949b549f668c293ffc Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 23 Feb 2022 09:57:13 +0100 Subject: [PATCH 010/179] chore(android): Update AndroidX dependencies (#5458) --- android/capacitor/build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 40cc82ab9..b4de33b57 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -1,9 +1,9 @@ ext { - androidxActivityVersion = project.hasProperty('androidxActivityVersion') ? rootProject.ext.androidxActivityVersion : '1.2.0' - androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.2.0' - androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.1.0' - androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.3.2' - androidxFragmentVersion = project.hasProperty('androidxFragmentVersion') ? rootProject.ext.androidxFragmentVersion : '1.3.0' + androidxActivityVersion = project.hasProperty('androidxActivityVersion') ? rootProject.ext.androidxActivityVersion : '1.4.0' + androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.4.1' + androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.2.0' + androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.7.0' + androidxFragmentVersion = project.hasProperty('androidxFragmentVersion') ? rootProject.ext.androidxFragmentVersion : '1.4.1' junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.3' androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.4.0' From 0c1e8cf9a54dfcee9175101d279fb9f6cdbf7f28 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 23 Feb 2022 10:18:16 +0100 Subject: [PATCH 011/179] chore(cli): Update AndroidX dependencies on cordova plugins template (#5459) --- capacitor-cordova-android-plugins/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index c77973aec..d5cb07ca1 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -1,5 +1,5 @@ ext { - androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.2.0' + androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.4.1' cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '7.0.0' } From 1eaaa5f4788aa62c5e6081190cd0ddcee05d6e0e Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 25 Feb 2022 17:37:29 +0100 Subject: [PATCH 012/179] chore(cli): Update variables.gradle in android-template (#5463) --- android-template/variables.gradle | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/android-template/variables.gradle b/android-template/variables.gradle index bf2bbdc27..c012a3414 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -2,13 +2,13 @@ ext { minSdkVersion = 21 compileSdkVersion = 31 targetSdkVersion = 31 - androidxActivityVersion = '1.2.0' - androidxAppCompatVersion = '1.2.0' - androidxCoordinatorLayoutVersion = '1.1.0' - androidxCoreVersion = '1.3.2' - androidxFragmentVersion = '1.3.0' - junitVersion = '4.13.1' - androidxJunitVersion = '1.1.2' - androidxEspressoCoreVersion = '3.3.0' + androidxActivityVersion = '1.4.0' + androidxAppCompatVersion = '1.4.1' + androidxCoordinatorLayoutVersion = '1.2.0' + androidxCoreVersion = '1.7.0' + androidxFragmentVersion = '1.4.1' + junitVersion = '4.13.2' + androidxJunitVersion = '1.1.3' + androidxEspressoCoreVersion = '3.4.0' cordovaAndroidVersion = '7.0.0' } \ No newline at end of file From 2e4a06b34d664c538ea96dde6be2abea99acb468 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 3 Mar 2022 13:33:07 +0100 Subject: [PATCH 013/179] Release 3.4.2 --- CHANGELOG.md | 11 +++++++++++ android/CHANGELOG.md | 8 ++++++++ android/package.json | 2 +- cli/CHANGELOG.md | 11 +++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 8 ++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 8 ++++++++ ios/package.json | 2 +- lerna.json | 2 +- 10 files changed, 51 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 200ad7775..78b0edcbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) + + +### Bug Fixes + +* **android:** compatibility with cordova kotlin plugins ([#5438](https://github.com/ionic-team/capacitor/issues/5438)) ([55bf004](https://github.com/ionic-team/capacitor/commit/55bf004897b3feb280ab6b6575d2a2c1a0a183e2)) + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index db575e60c..34b609944 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) + +**Note:** Version bump only for package @capacitor/android + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 1a843b633..7466ae705 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "3.4.1", + "version": "3.4.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 660e21163..89b8c5d76 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) + + +### Bug Fixes + +* **android:** compatibility with cordova kotlin plugins ([#5438](https://github.com/ionic-team/capacitor/issues/5438)) ([55bf004](https://github.com/ionic-team/capacitor/commit/55bf004897b3feb280ab6b6575d2a2c1a0a183e2)) + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) diff --git a/cli/package.json b/cli/package.json index 404e52b0d..f32f2f073 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "3.4.1", + "version": "3.4.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index e0b2733f6..febe4847e 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) + +**Note:** Version bump only for package @capacitor/core + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index e64fa69ea..4cda6a28d 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "3.4.1", + "version": "3.4.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 44e15fb2d..856e6d382 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) + +**Note:** Version bump only for package @capacitor/ios + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) diff --git a/ios/package.json b/ios/package.json index f8a43f805..5faf1c6d5 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "3.4.1", + "version": "3.4.2", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index 9973ed0a1..cede0790f 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "3.4.1" + "version": "3.4.2" } From 3c2b579c6edb1fc69d85689b268eb92067b7821b Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 3 Mar 2022 20:15:58 +0100 Subject: [PATCH 014/179] fix(cli): avoid srcFiles is not iterable on kotlinNeededCheck (#5481) --- cli/src/android/update.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index d3459dd32..964063ee2 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -392,10 +392,12 @@ async function kotlinNeededCheck(config: Config, cordovaPlugins: Plugin[]) { ) { for (const plugin of cordovaPlugins) { const androidPlatform = getPluginPlatform(plugin, platform); - const srcFiles = androidPlatform['source-file']; - for (const srcFile of srcFiles) { - if (/^.*\.kt$/.test(srcFile['$'].src)) { - return true; + const sourceFiles = androidPlatform['source-file']; + if (sourceFiles) { + for (const srcFile of sourceFiles) { + if (/^.*\.kt$/.test(srcFile['$'].src)) { + return true; + } } } } From 33e9b342790ddedfb654c589ffde18b101d4cd98 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 4 Mar 2022 10:38:14 +0100 Subject: [PATCH 015/179] Release 3.4.3 --- CHANGELOG.md | 11 +++++++++++ android/CHANGELOG.md | 8 ++++++++ android/package.json | 2 +- cli/CHANGELOG.md | 11 +++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 8 ++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 8 ++++++++ ios/package.json | 2 +- lerna.json | 2 +- 10 files changed, 51 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b0edcbf..6f5ddc535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) + + +### Bug Fixes + +* **cli:** avoid srcFiles is not iterable on kotlinNeededCheck ([#5481](https://github.com/ionic-team/capacitor/issues/5481)) ([3c2b579](https://github.com/ionic-team/capacitor/commit/3c2b579c6edb1fc69d85689b268eb92067b7821b)) + + + + + ## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 34b609944..1fb80f850 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) + +**Note:** Version bump only for package @capacitor/android + + + + + ## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 7466ae705..7558ab6d1 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "3.4.2", + "version": "3.4.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 89b8c5d76..5771e991d 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) + + +### Bug Fixes + +* **cli:** avoid srcFiles is not iterable on kotlinNeededCheck ([#5481](https://github.com/ionic-team/capacitor/issues/5481)) ([3c2b579](https://github.com/ionic-team/capacitor/commit/3c2b579c6edb1fc69d85689b268eb92067b7821b)) + + + + + ## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) diff --git a/cli/package.json b/cli/package.json index f32f2f073..d93279208 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "3.4.2", + "version": "3.4.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index febe4847e..df1041e6b 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) + +**Note:** Version bump only for package @capacitor/core + + + + + ## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index 4cda6a28d..ade19d4e0 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "3.4.2", + "version": "3.4.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 856e6d382..f2ac186fe 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) + +**Note:** Version bump only for package @capacitor/ios + + + + + ## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index 5faf1c6d5..7338f7ebc 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "3.4.2", + "version": "3.4.3", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index cede0790f..aa9e7894d 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "3.4.2" + "version": "3.4.3" } From 224a9d075629d9c9da9ddc658eb282617fc46d09 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Mon, 14 Mar 2022 11:19:51 -0400 Subject: [PATCH 016/179] feat(ios): add getConfig to CAPPlugin (#5495) * feat(ios): add getConfig to CAPPlugin * rework to use class with helpers * Update project.pbxproj * Update ios/Capacitor/Capacitor/CAPPlugin.m Co-authored-by: jcesarmobile * Update ios/Capacitor/Capacitor/CAPPlugin.h Co-authored-by: jcesarmobile * adjust getArray and fmt Co-authored-by: jcesarmobile --- .../Capacitor.xcodeproj/project.pbxproj | 4 ++ .../Capacitor/CAPInstanceConfiguration.swift | 8 +++ ios/Capacitor/Capacitor/CAPPlugin.h | 5 +- ios/Capacitor/Capacitor/CAPPlugin.m | 6 +- ios/Capacitor/Capacitor/PluginConfig.swift | 56 +++++++++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 ios/Capacitor/Capacitor/PluginConfig.swift diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index a0d4ea61d..02352358f 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0F8F33B327DA980A003F49D6 /* PluginConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */; }; 2F81F5C926FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */; }; 2F81F5CA26FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */; }; 373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */; }; @@ -136,6 +137,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PluginConfig.swift; sourceTree = ""; }; 2F81F5C726FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CAPBridgeViewController+CDVScreenOrientationDelegate.h"; sourceTree = ""; }; 2F81F5C826FB7CB400DD35BE /* CAPBridgeViewController+CDVScreenOrientationDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CAPBridgeViewController+CDVScreenOrientationDelegate.m"; sourceTree = ""; }; 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationHandlerProtocol.swift; sourceTree = ""; }; @@ -299,6 +301,7 @@ 62959AE12524DA7700A3D7F1 /* Capacitor */ = { isa = PBXGroup; children = ( + 0F8F33B127DA980A003F49D6 /* PluginConfig.swift */, 62959B0F2524DA7700A3D7F1 /* Capacitor.h */, 62959B132524DA7700A3D7F1 /* CAPPlugin.h */, 62959B012524DA7700A3D7F1 /* CAPPlugin.m */, @@ -610,6 +613,7 @@ 62959B172524DA7800A3D7F1 /* JSExport.swift in Sources */, 373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */, 625AF1ED258963C700869675 /* WebViewAssetHandler.swift in Sources */, + 0F8F33B327DA980A003F49D6 /* PluginConfig.swift in Sources */, 62959B3C2524DA7800A3D7F1 /* CAPBridgeDelegate.swift in Sources */, 62959B2F2524DA7800A3D7F1 /* DefaultPlugins.m in Sources */, 623D691E254C7462002D01D1 /* CAPInstanceConfiguration.m in Sources */, diff --git a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.swift b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.swift index 3578363b5..d436bd8a1 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.swift @@ -15,10 +15,18 @@ extension InstanceConfiguration { return serverURL } + @available(*, deprecated, message: "Use getPluginConfig") @objc public func getPluginConfigValue(_ pluginId: String, _ configKey: String) -> Any? { return (pluginConfigurations as? JSObject)?[keyPath: KeyPath("\(pluginId).\(configKey)")] } + @objc public func getPluginConfig(_ pluginId: String) -> PluginConfig { + if let cfg = (pluginConfigurations as? JSObject)?[keyPath: KeyPath("\(pluginId)")] as? JSObject { + return PluginConfig(config: cfg) + } + return PluginConfig(config: JSObject()) + } + @objc public func shouldAllowNavigation(to host: String) -> Bool { for hostname in allowedNavigationHostnames { if doesHost(host, match: hostname) { diff --git a/ios/Capacitor/Capacitor/CAPPlugin.h b/ios/Capacitor/Capacitor/CAPPlugin.h index 4f77e31f3..d09bdd157 100644 --- a/ios/Capacitor/Capacitor/CAPPlugin.h +++ b/ios/Capacitor/Capacitor/CAPPlugin.h @@ -4,6 +4,8 @@ @protocol CAPBridgeProtocol; @class CAPPluginCall; +@class PluginConfig; + @interface CAPPlugin : NSObject @property (nonatomic, weak, nullable) WKWebView *webView; @@ -44,7 +46,8 @@ -(NSString* _Nonnull)getId; -(BOOL)getBool:(CAPPluginCall* _Nonnull) call field:(NSString* _Nonnull)field defaultValue:(BOOL)defaultValue DEPRECATED_MSG_ATTRIBUTE("Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations."); -(NSString* _Nullable)getString:(CAPPluginCall* _Nonnull)call field:(NSString* _Nonnull)field defaultValue:(NSString* _Nonnull)defaultValue DEPRECATED_MSG_ATTRIBUTE("Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations."); --(id _Nullable)getConfigValue:(NSString* _Nonnull)key; +-(id _Nullable)getConfigValue:(NSString* _Nonnull)key __deprecated_msg("use getConfig() and access config values using the methods available depending on the type."); +-(PluginConfig* _Nonnull)getConfig; -(void)setCenteredPopover:(UIViewController* _Nonnull)vc; -(BOOL)supportsPopover; diff --git a/ios/Capacitor/Capacitor/CAPPlugin.m b/ios/Capacitor/Capacitor/CAPPlugin.m index 517ad4428..9ef4aabc1 100644 --- a/ios/Capacitor/Capacitor/CAPPlugin.m +++ b/ios/Capacitor/Capacitor/CAPPlugin.m @@ -29,10 +29,14 @@ - (NSString *) getString:(CAPPluginCall *)call field:(NSString *)field defaultVa return [call getString:field defaultValue:defaultValue]; } --(id)getConfigValue:(NSString *)key { +-(id)getConfigValue:(NSString *)key __deprecated { return [self.bridge.config getPluginConfigValue:self.pluginName :key]; } +-(PluginConfig*)getConfig { + return [self.bridge.config getPluginConfig:self.pluginName]; +} + -(void)load {} - (void)addEventListener:(NSString *)eventName listener:(CAPPluginCall *)listener { diff --git a/ios/Capacitor/Capacitor/PluginConfig.swift b/ios/Capacitor/Capacitor/PluginConfig.swift new file mode 100644 index 000000000..dbe5e1378 --- /dev/null +++ b/ios/Capacitor/Capacitor/PluginConfig.swift @@ -0,0 +1,56 @@ +import Foundation + +@objc public class PluginConfig: NSObject { + + // The object containing the plugin config values + private var config: JSObject + + init(config: JSObject) { + self.config = config + } + + public func getString(configKey: String, defaultValue: String? = nil) -> String? { + if let val = (self.config)[keyPath: KeyPath(configKey)] as? String { + return val + } + return defaultValue + } + + public func getBoolean(configKey: String, defaultValue: Bool) -> Bool { + if let val = (self.config)[keyPath: KeyPath(configKey)] as? Bool { + return val + } + return defaultValue + } + + public func getInt(configKey: String, defaultValue: Int) -> Int { + if let val = (self.config)[keyPath: KeyPath(configKey)] as? Int { + return val + } + return defaultValue + } + + public func getArray(configKey: String, defaultValue: JSArray? = nil) -> JSArray? { + if let val = (self.config)[keyPath: KeyPath(configKey)] as? JSArray { + return val + } + return defaultValue + } + + public func getObject(configKey: String) -> JSObject? { + return (self.config)[keyPath: KeyPath(configKey)] as? JSObject + } + + public func isEmpty() -> Bool { + return self.config.isEmpty + } + + /** + * Gets the JSObject containing the config of the the provided plugin ID. + * + * @return The config for that plugin + */ + public func getConfigJSON() -> JSObject { + return self.config + } +} From 7b4edd36089a763c8cad4f4d7e8ada98c1ceba19 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Tue, 15 Mar 2022 14:09:49 -0400 Subject: [PATCH 017/179] chore(android): bump core min sdk (#5503) * bump core and template min sdk * adjust min sdks further * Update build.gradle * Update build.gradle * adjust minSDK to 22 --- android/capacitor/build.gradle | 2 +- capacitor-cordova-android-plugins/build.gradle | 2 +- cli/src/config.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index b4de33b57..a69e8f78a 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -28,7 +28,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 31 defaultConfig { - minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 21 + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 31 versionCode 1 versionName "1.0" diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index d5cb07ca1..9e3a8fd0d 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -18,7 +18,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 31 defaultConfig { - minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 21 + minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 31 versionCode 1 versionName "1.0" diff --git a/cli/src/config.ts b/cli/src/config.ts index 7ce649967..b39dc1949 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -239,7 +239,7 @@ async function loadAndroidConfig( return { name, - minVersion: '21', + minVersion: '22', studioPath, platformDir, platformDirAbs, From 2722bd62a02ce1b7854f36eb56fbcf2cfe95cb07 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Tue, 15 Mar 2022 14:17:31 -0400 Subject: [PATCH 018/179] chore(android-template): bump min sdk android template (#5504) * bump min sdk * Update variables.gradle --- android-template/variables.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-template/variables.gradle b/android-template/variables.gradle index c012a3414..940ba9b33 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -1,5 +1,5 @@ ext { - minSdkVersion = 21 + minSdkVersion = 22 compileSdkVersion = 31 targetSdkVersion = 31 androidxActivityVersion = '1.4.0' From 5ce6aa83ac68186fc4fe9ab3a927ea113d8ecb8a Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Tue, 15 Mar 2022 15:05:13 -0400 Subject: [PATCH 019/179] chore(iOS-template): bump iOS template min iOS version to 13 (#5508) --- ios-template/App/App.xcodeproj/project.pbxproj | 8 ++++---- ios-template/App/Podfile | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ios-template/App/App.xcodeproj/project.pbxproj b/ios-template/App/App.xcodeproj/project.pbxproj index 64b192a61..b9fdfb287 100644 --- a/ios-template/App/App.xcodeproj/project.pbxproj +++ b/ios-template/App/App.xcodeproj/project.pbxproj @@ -281,7 +281,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -332,7 +332,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -348,7 +348,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.0; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; @@ -368,7 +368,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.App; diff --git a/ios-template/App/Podfile b/ios-template/App/Podfile index 5ebd08e75..8594fc2cd 100644 --- a/ios-template/App/Podfile +++ b/ios-template/App/Podfile @@ -1,4 +1,4 @@ -platform :ios, '12.0' +platform :ios, '13.0' use_frameworks! # workaround to avoid Xcode caching of Pods that requires From a8c42d11ec030da71f2ecfffcf5eec408b04dd28 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 16 Mar 2022 07:56:48 -0400 Subject: [PATCH 020/179] chore(ios): bump deployment target to 13 (#5507) --- cli/src/config.ts | 2 +- ios/Capacitor.podspec | 2 +- ios/Capacitor/Capacitor.xcodeproj/project.pbxproj | 4 ++-- ios/CapacitorCordova.podspec | 2 +- .../CapacitorCordova.xcodeproj/project.pbxproj | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/src/config.ts b/cli/src/config.ts index b39dc1949..c6f75c34f 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -293,7 +293,7 @@ async function loadIOSConfig( return { name, - minVersion: '12.0', + minVersion: '13.0', platformDir, platformDirAbs, scheme, diff --git a/ios/Capacitor.podspec b/ios/Capacitor.podspec index 4d37301b9..adb9c627a 100644 --- a/ios/Capacitor.podspec +++ b/ios/Capacitor.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| s.social_media_url = 'https://twitter.com/capacitorjs' s.license = 'MIT' s.homepage = 'https://capacitorjs.com/' - s.ios.deployment_target = '12.0' + s.ios.deployment_target = '13.0' s.authors = { 'Ionic Team' => 'hi@ionicframework.com' } s.source = { :git => 'https://github.com/ionic-team/capacitor.git', :branch => "portals-dev" } s.source_files = 'Capacitor/Capacitor/*.{swift,h,m}', 'Capacitor/Capacitor/Plugins/*.{swift,h,m}', 'Capacitor/Capacitor/Plugins/**/*.{swift,h,m}' diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index 02352358f..c686ed0d9 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -738,7 +738,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -796,7 +796,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; diff --git a/ios/CapacitorCordova.podspec b/ios/CapacitorCordova.podspec index c7eb45b75..e114a2158 100644 --- a/ios/CapacitorCordova.podspec +++ b/ios/CapacitorCordova.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.license = 'MIT' s.authors = { 'Ionic Team' => 'hi@ionicframework.com' } s.source = { :git => 'https://github.com/ionic-team/capacitor', :tag => s.version.to_s } - s.platform = :ios, 12.0 + s.platform = :ios, 13.0 s.source_files = 'CapacitorCordova/CapacitorCordova/**/*.{h,m}' s.public_header_files = 'CapacitorCordova/CapacitorCordova/Classes/Public/*.h', 'CapacitorCordova/CapacitorCordova/CapacitorCordova.h' s.module_map = 'CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap' diff --git a/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj b/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj index 5a93193d2..905dfd656 100644 --- a/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj +++ b/ios/CapacitorCordova/CapacitorCordova.xcodeproj/project.pbxproj @@ -312,7 +312,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -367,7 +367,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; From 75d65bc22ebf83b332b6af5400df5e28a7082f8d Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 21 Mar 2022 10:46:58 +0100 Subject: [PATCH 021/179] chore(android): update cordovaAndroidVersion default value (#5513) --- android/capacitor/build.gradle | 2 +- capacitor-cordova-android-plugins/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index a69e8f78a..a21c4b46c 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -7,7 +7,7 @@ ext { junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.3' androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.4.0' - cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '7.0.0' + cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '10.1.1' } diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index 9e3a8fd0d..b7004e652 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -1,6 +1,6 @@ ext { androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.4.1' - cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '7.0.0' + cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '10.1.1' } buildscript { From d6c4356288e6527534fa4ab3cf7efb7e0d03c771 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 21 Mar 2022 10:59:39 +0100 Subject: [PATCH 022/179] chore(android-template): Update cordovaAndroidVersion to latest (#5514) --- android-template/variables.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-template/variables.gradle b/android-template/variables.gradle index 940ba9b33..2c907f115 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -10,5 +10,5 @@ ext { junitVersion = '4.13.2' androidxJunitVersion = '1.1.3' androidxEspressoCoreVersion = '3.4.0' - cordovaAndroidVersion = '7.0.0' + cordovaAndroidVersion = '10.1.1' } \ No newline at end of file From 68e0ff8db41b998585deea48243c0ba8459ffe85 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 23 Mar 2022 10:22:05 +0100 Subject: [PATCH 023/179] chore(android): Remove jcenter repository (#5520) --- android/capacitor/build.gradle | 1 - capacitor-cordova-android-plugins/build.gradle | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index a21c4b46c..3cfad8949 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -55,7 +55,6 @@ android { repositories { google() mavenCentral() - jcenter() } dependencies { diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index b7004e652..ee3cd76e5 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -6,7 +6,7 @@ ext { buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:7.1.1' @@ -35,7 +35,6 @@ android { repositories { google() mavenCentral() - jcenter() flatDir{ dirs 'src/main/libs', 'libs' } From 7faec09fd724a0af7436f3d30c83856b7bb5ce51 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 23 Mar 2022 10:56:33 +0100 Subject: [PATCH 024/179] chore(android-template): Replace jcenter with mavenCentral (#5519) --- android-template/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android-template/build.gradle b/android-template/build.gradle index 414a402df..0b92c299f 100644 --- a/android-template/build.gradle +++ b/android-template/build.gradle @@ -4,7 +4,7 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:7.1.1' @@ -20,7 +20,7 @@ apply from: "variables.gradle" allprojects { repositories { google() - jcenter() + mavenCentral() } } From 10408c05c967c34dd09da8044497508b07b1e225 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 29 Mar 2022 13:59:59 +0200 Subject: [PATCH 025/179] chore: Alpha.1 release changes (#5543) --- CHANGELOG.md | 21 +++++++++++++++++++++ android/CHANGELOG.md | 12 ++++++++++++ cli/CHANGELOG.md | 8 ++++++++ core/CHANGELOG.md | 8 ++++++++ ios/CHANGELOG.md | 11 +++++++++++ lerna.json | 4 ++-- package.json | 2 ++ 7 files changed, 64 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 200ad7775..a0765bdc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) + + +### Bug Fixes + +* **cli:** Use CURRENT_PROJECT_VERSION variable on ios template ([#5418](https://github.com/ionic-team/capacitor/issues/5418)) ([0a07648](https://github.com/ionic-team/capacitor/commit/0a07648b4d1c5fb1fd7de3c72ac1bbcb30f48203)) + + +### Features + +* **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3)) +* **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad)) +* **cli:** export android-template activity for SDK 31 support ([#5351](https://github.com/ionic-team/capacitor/issues/5351)) ([79b4a3c](https://github.com/ionic-team/capacitor/commit/79b4a3c56ce9704bc8f5b0b8ce6d5e60f86d4d2c)) +* **cli:** set targetSDK to 31 in android-template ([#5443](https://github.com/ionic-team/capacitor/issues/5443)) ([8793c58](https://github.com/ionic-team/capacitor/commit/8793c58f24611d7780aff80d547b367f4114f7c5)) +* **cli:** Upgrade Gradle to 7.4 in the template ([#5446](https://github.com/ionic-team/capacitor/issues/5446)) ([11b648d](https://github.com/ionic-team/capacitor/commit/11b648d21567c5ab8e7e195fdefec1e1254ce62a)) +* **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09)) + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index db575e60c..0f1a858a7 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) + + +### Features + +* **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3)) +* **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad)) + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) **Note:** Version bump only for package @capacitor/android diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 660e21163..23c28ca9c 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) + +**Note:** Version bump only for package @capacitor/cli + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index e0b2733f6..4b98d1884 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) + +**Note:** Version bump only for package @capacitor/core + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) **Note:** Version bump only for package @capacitor/core diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 44e15fb2d..9cdfb5f6a 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) + + +### Features + +* **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09)) + + + + + ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09) diff --git a/lerna.json b/lerna.json index 9973ed0a1..a8449f8b3 100644 --- a/lerna.json +++ b/lerna.json @@ -10,12 +10,12 @@ "hoist": true }, "version": { - "allowBranch": "main", + "allowBranch": "capacitor-4", "conventionalCommits": true, "createRelease": "github", "message": "Release %s", "tagVersionPrefix": "" } }, - "version": "3.4.1" + "version": "4.0.0-alpha.1" } diff --git a/package.json b/package.json index a28fafa1b..fac27454f 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "scripts": { "build:nativebridge": "lerna run build:nativebridge", "sync-peer-dependencies": "node scripts/sync-peer-dependencies.mjs", + "lerna:publish:alpha": "lerna publish prerelease --preid alpha --dist-tag next --force-publish --no-git-tag-version --no-push", "lerna:publish:dev": "lerna publish prerelease --conventional-commits --conventional-prerelease --no-changelog --preid dev-$(git rev-parse --short HEAD) --dist-tag dev --force-publish --no-git-tag-version --no-push", "lerna:version:rc": "lerna version prerelease --force-publish --preid rc", "lerna:version:latest": "lerna version --force-publish --conventional-commits", @@ -15,6 +16,7 @@ "eslint": "eslint . --ext ts", "swiftlint": "node-swiftlint", "postinstall": "lerna bootstrap", + "release:alpha": "lerna run test && lerna run build && npm run lerna:publish:alpha", "release:dev": "lerna run test && lerna run build && npm run lerna:publish:dev", "release:rc": "npm run lerna:version:rc", "release:latest": "npm run lerna:version:latest", From 6f4d858ea879d97109c0c7da2d664d04806adc2a Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 29 Mar 2022 14:34:44 +0200 Subject: [PATCH 026/179] fix(android): make removeAllListeners return a promise (#5527) --- android/capacitor/src/main/java/com/getcapacitor/Plugin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Plugin.java b/android/capacitor/src/main/java/com/getcapacitor/Plugin.java index 7dff82f1d..5dccc054d 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Plugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Plugin.java @@ -745,9 +745,10 @@ public void removeListener(PluginCall call) { * @param call */ @SuppressWarnings("unused") - @PluginMethod(returnType = PluginMethod.RETURN_NONE) + @PluginMethod(returnType = PluginMethod.RETURN_PROMISE) public void removeAllListeners(PluginCall call) { eventListeners.clear(); + call.resolve(); } /** From d4a0deaa37eda4476f0be030e266c2c1260fc6e8 Mon Sep 17 00:00:00 2001 From: Joey Pender Date: Tue, 29 Mar 2022 09:33:59 -0500 Subject: [PATCH 027/179] fix(android): prevent app from loading if server.url is invalid --- android/capacitor/src/main/java/com/getcapacitor/Bridge.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 5828f4904..8e861f18a 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -215,7 +215,10 @@ private void loadWebView() { try { URL appUrlObject = new URL(appUrlConfig); authorities.add(appUrlObject.getAuthority()); - } catch (Exception ex) {} + } catch (Exception ex) { + Logger.error("Provided server url is invalid: " + ex.getMessage()); + return; + } localUrl = appUrlConfig; appUrl = appUrlConfig; } else { From 815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 29 Mar 2022 17:01:25 +0200 Subject: [PATCH 028/179] fix(ios): make removeAllListeners return a promise (#5526) --- ios/Capacitor/Capacitor/CAPPlugin.m | 1 + ios/Capacitor/Capacitor/JSExport.swift | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/Capacitor/Capacitor/CAPPlugin.m b/ios/Capacitor/Capacitor/CAPPlugin.m index 9ef4aabc1..7a7f76099 100644 --- a/ios/Capacitor/Capacitor/CAPPlugin.m +++ b/ios/Capacitor/Capacitor/CAPPlugin.m @@ -109,6 +109,7 @@ - (void)removeListener:(CAPPluginCall *)call { - (void)removeAllListeners:(CAPPluginCall *)call { [self.eventListeners removeAllObjects]; + [call resolve]; } - (NSArray*)getListeners:(NSString *)eventName { diff --git a/ios/Capacitor/Capacitor/JSExport.swift b/ios/Capacitor/Capacitor/JSExport.swift index c9eddd3a0..bde73bfcb 100644 --- a/ios/Capacitor/Capacitor/JSExport.swift +++ b/ios/Capacitor/Capacitor/JSExport.swift @@ -97,7 +97,7 @@ internal class JSExport { let methods = [ PluginHeaderMethod(name: "addListener", rtype: nil), PluginHeaderMethod(name: "removeListener", rtype: nil), - PluginHeaderMethod(name: "removeAllListeners", rtype: nil), + PluginHeaderMethod(name: "removeAllListeners", rtype: "promise"), PluginHeaderMethod(name: "checkPermissions", rtype: "promise"), PluginHeaderMethod(name: "requestPermissions", rtype: "promise") ] From 8875d5e2721e8a8ee763ce70cb672db383f36efa Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Wed, 30 Mar 2022 11:35:16 -0500 Subject: [PATCH 029/179] feat(ios): Add overrideable routing for CAPBridgeViewController subclasses (#5546) --- .../Capacitor.xcodeproj/project.pbxproj | 8 +++++ .../Capacitor/CAPBridgeViewController.swift | 8 +++-- ios/Capacitor/Capacitor/Router.swift | 27 +++++++++++++++++ .../Capacitor/WebViewAssetHandler.swift | 15 ++++++---- .../CapacitorTests/CapacitorTests.swift | 2 +- .../CapacitorTests/RouterTests.swift | 30 +++++++++++++++++++ 6 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 ios/Capacitor/Capacitor/Router.swift create mode 100644 ios/Capacitor/CapacitorTests/RouterTests.swift diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index a0d4ea61d..979e61bad 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -80,6 +80,8 @@ 62FABD1A25AE5C01007B3814 /* Array+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */; }; 62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */; }; 62FABD2B25AE6182007B3814 /* BridgedTypesHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */; }; + A71289E627F380A500DADDF3 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289E527F380A500DADDF3 /* Router.swift */; }; + A71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289EA27F380FD00DADDF3 /* RouterTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -217,6 +219,8 @@ 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Capacitor.swift"; sourceTree = ""; }; 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BridgedTypesTests.m; sourceTree = ""; }; 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgedTypesHelper.swift; sourceTree = ""; }; + A71289E527F380A500DADDF3 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; + A71289EA27F380FD00DADDF3 /* RouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -292,6 +296,7 @@ 621ECCCD254204C400D3D615 /* CapacitorTests-Bridging-Header.h */, 62E79C712638B23300414164 /* JSExportTests.swift */, 62959BBD2526510200A3D7F1 /* Info.plist */, + A71289EA27F380FD00DADDF3 /* RouterTests.swift */, ); path = CapacitorTests; sourceTree = ""; @@ -349,6 +354,7 @@ 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */, 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */, 62E79C562638AF7500414164 /* assets */, + A71289E527F380A500DADDF3 /* Router.swift */, ); path = Capacitor; sourceTree = ""; @@ -580,6 +586,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A71289E627F380A500DADDF3 /* Router.swift in Sources */, 62959B362524DA7800A3D7F1 /* CAPBridgeViewController.swift in Sources */, 621ECCB72542045900D3D615 /* CAPBridgedJSTypes.m in Sources */, 62959B402524DA7800A3D7F1 /* TmpViewController.swift in Sources */, @@ -634,6 +641,7 @@ 62A91C3425535F5700861508 /* ConfigurationTests.swift in Sources */, 62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */, 621ECCC3254204B700D3D615 /* BridgedTypesTests.swift in Sources */, + A71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */, 6263686025F6EC0100576C1C /* PluginCallAccessorTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index 12c3abaaa..865909b52 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -42,7 +42,7 @@ import Cordova setScreenOrientationDefaults() // get the web view - let assetHandler = WebViewAssetHandler() + let assetHandler = WebViewAssetHandler(router: router()) assetHandler.setAssetPath(configuration.appLocation.path) let delegationHandler = WebViewDelegationHandler() prepareWebView(with: configuration, assetHandler: assetHandler, delegationHandler: delegationHandler) @@ -89,7 +89,11 @@ import Cordova } return descriptor } - + + open func router() -> Router { + return _Router() + } + /** The WKWebViewConfiguration to use for the webview. diff --git a/ios/Capacitor/Capacitor/Router.swift b/ios/Capacitor/Capacitor/Router.swift new file mode 100644 index 000000000..6ac417d31 --- /dev/null +++ b/ios/Capacitor/Capacitor/Router.swift @@ -0,0 +1,27 @@ +// +// Router.swift +// Capacitor +// +// Created by Steven Sherry on 3/29/22. +// Copyright © 2022 Drifty Co. All rights reserved. +// + +import Foundation + +public protocol Router { + func route(for path: String) -> String +} + +// swiftlint:disable:next type_name +internal struct _Router: Router { + func route(for path: String) -> String { + let pathUrl = URL(string: path) + + // if the pathUrl is null, then it is an invalid url (meaning it is empty or just plain invalid) then we want to route to /index.html + if pathUrl?.pathExtension.isEmpty ?? true { + return "/index.html" + } + + return path + } +} diff --git a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift index 9dbff7a0c..da63326d3 100644 --- a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift @@ -3,8 +3,13 @@ import MobileCoreServices @objc(CAPWebViewAssetHandler) internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { - + private let router: Router private var basePath: String = "" + + init(router: Router) { + self.router = router + super.init() + } func setAssetPath(_ assetPath: String) { self.basePath = assetPath @@ -14,14 +19,14 @@ internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { var startPath = self.basePath let url = urlSchemeTask.request.url! let stringToLoad = url.path - + + let resolvedRoute = router.route(for: stringToLoad) if stringToLoad.starts(with: CapacitorBridge.fileStartIdentifier) { startPath = stringToLoad.replacingOccurrences(of: CapacitorBridge.fileStartIdentifier, with: "") - } else if stringToLoad.isEmpty || url.pathExtension.isEmpty { - startPath.append("/index.html") } else { - startPath.append(stringToLoad) + startPath.append(resolvedRoute) } + let localUrl = URL.init(string: url.absoluteString)! let fileUrl = URL.init(fileURLWithPath: startPath) diff --git a/ios/Capacitor/CapacitorTests/CapacitorTests.swift b/ios/Capacitor/CapacitorTests/CapacitorTests.swift index 00131f439..beb4b3445 100644 --- a/ios/Capacitor/CapacitorTests/CapacitorTests.swift +++ b/ios/Capacitor/CapacitorTests/CapacitorTests.swift @@ -23,7 +23,7 @@ class CapacitorTests: XCTestCase { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. let descriptor = InstanceDescriptor.init() - bridge = MockBridge(with: InstanceConfiguration(with: descriptor, isDebug: true), delegate: MockBridgeViewController(), cordovaConfiguration: descriptor.cordovaConfiguration, assetHandler: MockAssetHandler(), delegationHandler: MockDelegationHandler()) + bridge = MockBridge(with: InstanceConfiguration(with: descriptor, isDebug: true), delegate: MockBridgeViewController(), cordovaConfiguration: descriptor.cordovaConfiguration, assetHandler: MockAssetHandler(router: _Router()), delegationHandler: MockDelegationHandler()) } override func tearDown() { diff --git a/ios/Capacitor/CapacitorTests/RouterTests.swift b/ios/Capacitor/CapacitorTests/RouterTests.swift new file mode 100644 index 000000000..f328c9c7c --- /dev/null +++ b/ios/Capacitor/CapacitorTests/RouterTests.swift @@ -0,0 +1,30 @@ +// +// RouterTests.swift +// CapacitorTests +// +// Created by Steven Sherry on 3/29/22. +// Copyright © 2022 Drifty Co. All rights reserved. +// + +import XCTest +@testable import Capacitor + +class RouterTests: XCTestCase { + let router = _Router() + + func testRouterReturnsIndexWhenProvidedInvalidPath() { + XCTAssertEqual(router.route(for: "/skull.💀"), "/index.html") + } + + func testRouterReturnsIndexWhenProvidedEmptyPath() { + XCTAssertEqual(router.route(for: ""), "/index.html") + } + + func testRouterReturnsIndexWhenProviedPathWithoutExtension() { + XCTAssertEqual(router.route(for: "/a/valid/path/no/ext"), "/index.html") + } + + func testRouterReturnsPathWhenProvidedValidPath() { + XCTAssertEqual(router.route(for: "/a/valid/path.ext"), "/a/valid/path.ext") + } +} From ff3967a95f206684e04297ff051b2335f447e04c Mon Sep 17 00:00:00 2001 From: Ionitron Date: Wed, 30 Mar 2022 12:49:31 -0400 Subject: [PATCH 030/179] chore(readme): add new contributor (#5551) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d080e0282..8161a6e77 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Made possible by the Capacitor community. 💖

+ From e47959fcbd6a89b97b1275a5814fdb4e7ce30672 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 31 Mar 2022 14:15:26 +0200 Subject: [PATCH 031/179] feat(android): Use java 11 (#5552) --- android/capacitor/build.gradle | 4 ++-- capacitor-cordova-android-plugins/build.gradle | 4 ++-- cli/src/android/update.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 3cfad8949..b96b05a52 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -47,8 +47,8 @@ android { warningsAsErrors true } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } } diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index ee3cd76e5..3fbe9797f 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -27,8 +27,8 @@ android { abortOnError false } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } } diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index 6978ac8cc..262b66352 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -254,8 +254,8 @@ project(':${getGradlePackageName( android { compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } } From 3bb288e848c5c0e49c1e58c0782e0b1ffd7b1f31 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Thu, 31 Mar 2022 09:52:08 -0500 Subject: [PATCH 032/179] feat(android): Add overridable routing for WebViewLocalServer (#5553) --- .../src/main/java/com/getcapacitor/Bridge.java | 18 ++++++++++++++++++ .../java/com/getcapacitor/BridgeActivity.java | 10 +++++----- .../java/com/getcapacitor/RouteProcessor.java | 8 ++++++++ .../com/getcapacitor/WebViewLocalServer.java | 12 +++++++++++- 4 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 android/capacitor/src/main/java/com/getcapacitor/RouteProcessor.java diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 5828f4904..50d961f2a 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -130,6 +130,9 @@ public class Bridge { // A list of listeners that trigger when webView events occur private List webViewListeners = new ArrayList<>(); + // An interface to manipulate route resolving + private RouteProcessor routeProcessor; + /** * Create the Bridge with a reference to the main {@link Activity} for the * app, and a reference to the {@link WebView} our app will use. @@ -1190,6 +1193,14 @@ void setWebViewListeners(List webViewListeners) { this.webViewListeners = webViewListeners; } + RouteProcessor getRouteProcessor() { + return routeProcessor; + } + + void setRouteProcessor(RouteProcessor routeProcessor) { + this.routeProcessor = routeProcessor; + } + /** * Add a listener that the WebViewClient can trigger on certain events. * @param webViewListener A {@link WebViewListener} to add. @@ -1213,6 +1224,7 @@ public static class Builder { private List> plugins = new ArrayList<>(); private AppCompatActivity activity; private Fragment fragment; + private RouteProcessor routeProcessor; private final List webViewListeners = new ArrayList<>(); public Builder(AppCompatActivity activity) { @@ -1265,6 +1277,11 @@ public Builder addWebViewListeners(List webViewListeners) { return this; } + public Builder setRouteProcessor(RouteProcessor routeProcessor) { + this.routeProcessor = routeProcessor; + return this; + } + public Bridge create() { // Cordova initialization ConfigXmlParser parser = new ConfigXmlParser(); @@ -1288,6 +1305,7 @@ public Bridge create() { Bridge bridge = new Bridge(activity, fragment, webView, plugins, cordovaInterface, pluginManager, preferences, config); bridge.setCordovaWebView(mockWebView); bridge.setWebViewListeners(webViewListeners); + bridge.setRouteProcessor(routeProcessor); if (instanceState != null) { bridge.restoreInstanceState(instanceState); diff --git a/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java b/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java index 18a7ea07e..b260414d2 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java +++ b/android/capacitor/src/main/java/com/getcapacitor/BridgeActivity.java @@ -12,11 +12,11 @@ public class BridgeActivity extends AppCompatActivity { protected Bridge bridge; protected boolean keepRunning = true; - private CapConfig config; + protected CapConfig config; - private int activityDepth = 0; - private List> initialPlugins = new ArrayList<>(); - private final Bridge.Builder bridgeBuilder = new Bridge.Builder(this); + protected int activityDepth = 0; + protected List> initialPlugins = new ArrayList<>(); + protected final Bridge.Builder bridgeBuilder = new Bridge.Builder(this); @Override protected void onCreate(Bundle savedInstanceState) { @@ -66,7 +66,7 @@ protected void load(Bundle savedInstanceState) { this.load(); } - private void load() { + protected void load() { Logger.debug("Starting BridgeActivity"); bridge = bridgeBuilder.addPlugins(initialPlugins).setConfig(config).create(); diff --git a/android/capacitor/src/main/java/com/getcapacitor/RouteProcessor.java b/android/capacitor/src/main/java/com/getcapacitor/RouteProcessor.java new file mode 100644 index 000000000..077c20c6a --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/RouteProcessor.java @@ -0,0 +1,8 @@ +package com.getcapacitor; + +/** + * An interface used in the processing of routes + */ +public interface RouteProcessor { + String process(String path); +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java index e38cb05b8..36ddb6bb3 100755 --- a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java +++ b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; @@ -256,6 +255,10 @@ private WebResourceResponse handleLocalRequest(WebResourceRequest request, PathH InputStream responseStream; try { String startPath = this.basePath + "/index.html"; + if (bridge.getRouteProcessor() != null) { + startPath = this.basePath + bridge.getRouteProcessor().process("/index.html"); + } + if (isAsset) { responseStream = protocolHandler.openAsset(startPath); } else { @@ -467,6 +470,13 @@ private void createHostingDetails() { public InputStream handle(Uri url) { InputStream stream = null; String path = url.getPath(); + + // Pass path to routeProcessor if present + RouteProcessor routeProcessor = bridge.getRouteProcessor(); + if (routeProcessor != null) { + path = bridge.getRouteProcessor().process(path); + } + try { if (path.startsWith(capacitorContentStart)) { stream = protocolHandler.openContentUrl(url); From 01285ba253d602b08a41240ad2ccf370730d51a3 Mon Sep 17 00:00:00 2001 From: Joey Pender Date: Tue, 5 Apr 2022 10:56:41 -0500 Subject: [PATCH 033/179] feat(android): don't allow server.androidScheme to be set to schemes handled by WebView --- .../main/java/com/getcapacitor/CapConfig.java | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java b/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java index 2e4450334..cb9156c6b 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java +++ b/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java @@ -9,8 +9,10 @@ import androidx.annotation.Nullable; import com.getcapacitor.util.JSONUtils; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Locale; import java.util.Map; import org.json.JSONException; @@ -105,7 +107,11 @@ private CapConfig(Builder builder) { this.html5mode = builder.html5mode; this.serverUrl = builder.serverUrl; this.hostname = builder.hostname; - this.androidScheme = builder.androidScheme; + + if (this.validateScheme(builder.androidScheme)) { + this.androidScheme = builder.androidScheme; + } + this.allowNavigation = builder.allowNavigation; // Android Config @@ -148,7 +154,12 @@ private void deserializeConfig(@Nullable Context context) { html5mode = JSONUtils.getBoolean(configJSON, "server.html5mode", html5mode); serverUrl = JSONUtils.getString(configJSON, "server.url", null); hostname = JSONUtils.getString(configJSON, "server.hostname", hostname); - androidScheme = JSONUtils.getString(configJSON, "server.androidScheme", androidScheme); + + String configSchema = JSONUtils.getString(configJSON, "server.androidScheme", androidScheme); + if (this.validateScheme(configSchema)) { + androidScheme = configSchema; + } + allowNavigation = JSONUtils.getArray(configJSON, "server.allowNavigation", null); // Android @@ -191,6 +202,16 @@ private void deserializeConfig(@Nullable Context context) { pluginsConfiguration = deserializePluginsConfig(JSONUtils.getObject(configJSON, "plugins")); } + private boolean validateScheme(String scheme) { + List invalidSchemes = Arrays.asList("file", "ftp", "ftps", "ws", "wss", "about", "blob", "data"); + if (invalidSchemes.contains(scheme)) { + Logger.warn(scheme + " is not an allowed scheme. Defaulting to http."); + return false; + } + + return true; + } + public boolean isHTML5Mode() { return html5mode; } From cdd5a6e9738de47e735b8440b68fffa96bfef077 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 20 Apr 2022 18:00:03 +0200 Subject: [PATCH 034/179] chore(ci): Remove unused action (#5572) --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c08952ca..161e64adf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,11 +27,6 @@ jobs: with: path: ~/.npm key: ${{ runner.OS }}-dependency-cache-${{ hashFiles('**/package.json') }} - - name: Get Package Version - id: package-version - uses: martinbeentjes/npm-get-version-action@master - with: - path: core/ lint: runs-on: macos-latest timeout-minutes: 30 From 20a207553ac052569c9c314fde5781bc22952ef3 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 20 Apr 2022 18:40:04 +0200 Subject: [PATCH 035/179] chore(ci): Remove unused action (#5574) --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c08952ca..161e64adf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,11 +27,6 @@ jobs: with: path: ~/.npm key: ${{ runner.OS }}-dependency-cache-${{ hashFiles('**/package.json') }} - - name: Get Package Version - id: package-version - uses: martinbeentjes/npm-get-version-action@master - with: - path: core/ lint: runs-on: macos-latest timeout-minutes: 30 From ae045143d22a3e345a94f16997e2b23906f1144e Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 20 Apr 2022 19:39:13 +0200 Subject: [PATCH 036/179] chore(android): Change Unable to read file log level to warn (#5571) --- android/capacitor/src/main/java/com/getcapacitor/JSExport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/JSExport.java b/android/capacitor/src/main/java/com/getcapacitor/JSExport.java index 49533907b..8d21f72c6 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/JSExport.java +++ b/android/capacitor/src/main/java/com/getcapacitor/JSExport.java @@ -94,7 +94,7 @@ public static String getFilesContent(Context context, String path) { return readFile(context.getAssets(), path); } } catch (IOException ex) { - Logger.error("Unable to read file at path " + path); + Logger.warn("Unable to read file at path " + path); } return builder.toString(); } From 6810a19ae2bbda1f4b2afad61d37ca822ca157f5 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Thu, 21 Apr 2022 15:52:08 -0500 Subject: [PATCH 037/179] feat(cli): support capacitor portals plugin changes needed (#5558) --- cli/src/declarations.ts | 16 ++++++++ cli/src/definitions.ts | 3 +- cli/src/tasks/copy.ts | 81 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index f609d095e..dc17d5bef 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -474,6 +474,12 @@ export interface CapacitorConfig { includePlugins?: string[]; } +export interface Portal { + name: string; + webDir: string; + appId?: string; +} + export interface PluginsConfig { /** * Plugin configuration by class name. @@ -485,4 +491,14 @@ export interface PluginsConfig { [key: string]: any; } | undefined; + + /** + * Capacitor Portals plugin configuration + * + * @since 3.5.0 + */ + Portals?: { + shell: Portal; + apps: Portal[]; + }; } diff --git a/cli/src/definitions.ts b/cli/src/definitions.ts index ba79b7120..445d5312b 100644 --- a/cli/src/definitions.ts +++ b/cli/src/definitions.ts @@ -1,4 +1,4 @@ -import type { CapacitorConfig } from './declarations'; +import type { CapacitorConfig, PluginsConfig } from './declarations'; type DeepReadonly = { readonly [P in keyof T]: DeepReadonly }; @@ -119,6 +119,7 @@ export interface Config { readonly web: WebConfig; readonly cli: CLIConfig; readonly app: AppConfig; + readonly plugins?: PluginsConfig; } export interface FrameworkConfig { diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 8129cb8b4..2cde358c8 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -1,5 +1,5 @@ import { copy as fsCopy, pathExists, remove, writeJSON } from '@ionic/utils-fs'; -import { basename, join, relative } from 'path'; +import { basename, join, relative, resolve } from 'path'; import c from '../colors'; import { @@ -15,9 +15,11 @@ import { handleCordovaPluginsJS, writeCordovaAndroidManifest, } from '../cordova'; +import type { Portal } from '../declarations'; import type { Config } from '../definitions'; import { isFatal } from '../errors'; import { logger } from '../log'; +import { getPlugins } from '../plugin'; import { allSerial } from '../util/promise'; import { copyWeb } from '../web/copy'; @@ -70,23 +72,56 @@ export async function copy( 'capacitor:copy:before', ); + const allPlugins = await getPlugins(config, platformName); + let usesCapacitorPortals = false; + if ( + allPlugins.filter( + plugin => plugin.id === '@ionic-enterprise/capacitor-portals', + ).length > 0 + ) { + usesCapacitorPortals = true; + } + if (platformName === config.ios.name) { - await copyWebDir(config, await config.ios.webDirAbs); + if (usesCapacitorPortals) { + await copyFederatedWebDirs(config, await config.ios.webDirAbs); + } else { + await copyWebDir( + config, + await config.ios.webDirAbs, + config.app.webDirAbs, + ); + } await copyCapacitorConfig(config, config.ios.nativeTargetDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName); } else if (platformName === config.android.name) { - await copyWebDir(config, config.android.webDirAbs); + if (usesCapacitorPortals) { + await copyFederatedWebDirs(config, config.android.webDirAbs); + } else { + await copyWebDir( + config, + config.android.webDirAbs, + config.app.webDirAbs, + ); + } await copyCapacitorConfig(config, config.android.assetsDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName); await writeCordovaAndroidManifest(cordovaPlugins, config, platformName); } else if (platformName === config.web.name) { - await copyWeb(config); + if (usesCapacitorPortals) { + logger.info( + 'Capacitor Portals Plugin installed, skipping web bundling...', + ); + } else { + await copyWeb(config); + } } else { throw `Platform ${platformName} is not valid.`; } }); + await runPlatformHook( config, platformName, @@ -110,8 +145,11 @@ async function copyCapacitorConfig(config: Config, nativeAbsDir: string) { ); } -async function copyWebDir(config: Config, nativeAbsDir: string) { - const webAbsDir = config.app.webDirAbs; +async function copyWebDir( + config: Config, + nativeAbsDir: string, + webAbsDir: string, +) { const webRelDir = basename(webAbsDir); const nativeRelDir = relative(config.app.rootDir, nativeAbsDir); @@ -138,3 +176,34 @@ async function copyWebDir(config: Config, nativeAbsDir: string) { }, ); } + +async function copyFederatedWebDirs(config: Config, nativeAbsDir: string) { + logger.info('Capacitor Portals Plugin Loaded - Copying Web Assets'); + + if (!config.app.extConfig?.plugins?.Portals) { + throw `Capacitor Portals plugin is present but no valid config is defined.`; + } + + const portalsConfig = config.app.extConfig.plugins.Portals; + if (!isPortal(portalsConfig.shell)) { + throw `Capacitor Portals plugin is present but no valid Shell application is defined in the config.`; + } + + if (!portalsConfig.apps.every(isPortal)) { + throw `Capacitor Portals plugin is present but there is a problem with the apps defined in the config.`; + } + + await Promise.all( + [...portalsConfig.apps, portalsConfig.shell].map(app => { + const appDir = resolve(config.app.rootDir, app.webDir); + return copyWebDir(config, resolve(nativeAbsDir, app.name), appDir); + }), + ); +} + +function isPortal(config: any): config is Portal { + return ( + (config as Portal).webDir !== undefined && + (config as Portal).name !== undefined + ); +} From f03e1a67140c8af25288456f9e99dd6234b50f25 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 22 Apr 2022 18:07:46 +0200 Subject: [PATCH 038/179] Release 3.5.0 --- CHANGELOG.md | 13 +++++++++++++ android/CHANGELOG.md | 11 +++++++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 11 +++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 8 ++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 11 +++++++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 61 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f5ddc535..e39066203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) + + +### Features + +* **android:** Add overridable routing for WebViewLocalServer ([#5553](https://github.com/ionic-team/capacitor/issues/5553)) ([3bb288e](https://github.com/ionic-team/capacitor/commit/3bb288e848c5c0e49c1e58c0782e0b1ffd7b1f31)) +* **cli:** support capacitor portals plugin changes needed ([#5558](https://github.com/ionic-team/capacitor/issues/5558)) ([6810a19](https://github.com/ionic-team/capacitor/commit/6810a19ae2bbda1f4b2afad61d37ca822ca157f5)) +* **ios:** Add overrideable routing for CAPBridgeViewController subclasses ([#5546](https://github.com/ionic-team/capacitor/issues/5546)) ([8875d5e](https://github.com/ionic-team/capacitor/commit/8875d5e2721e8a8ee763ce70cb672db383f36efa)) + + + + + ## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 1fb80f850..4520662d0 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) + + +### Features + +* **android:** Add overridable routing for WebViewLocalServer ([#5553](https://github.com/ionic-team/capacitor/issues/5553)) ([3bb288e](https://github.com/ionic-team/capacitor/commit/3bb288e848c5c0e49c1e58c0782e0b1ffd7b1f31)) + + + + + ## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 7558ab6d1..70050b44f 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "3.4.3", + "version": "3.5.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -22,7 +22,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^3.4.0" + "@capacitor/core": "^3.5.0" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 5771e991d..9e179a65d 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) + + +### Features + +* **cli:** support capacitor portals plugin changes needed ([#5558](https://github.com/ionic-team/capacitor/issues/5558)) ([6810a19](https://github.com/ionic-team/capacitor/commit/6810a19ae2bbda1f4b2afad61d37ca822ca157f5)) + + + + + ## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) diff --git a/cli/package.json b/cli/package.json index d93279208..cca0a347b 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "3.4.3", + "version": "3.5.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index df1041e6b..7c5ef69f4 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) + +**Note:** Version bump only for package @capacitor/core + + + + + ## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index ade19d4e0..b1e1bd9ad 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "3.4.3", + "version": "3.5.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index f2ac186fe..2da6990ea 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) + + +### Features + +* **ios:** Add overrideable routing for CAPBridgeViewController subclasses ([#5546](https://github.com/ionic-team/capacitor/issues/5546)) ([8875d5e](https://github.com/ionic-team/capacitor/commit/8875d5e2721e8a8ee763ce70cb672db383f36efa)) + + + + + ## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index 7338f7ebc..35d5230b1 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "3.4.3", + "version": "3.5.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -24,7 +24,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^3.4.0" + "@capacitor/core": "^3.5.0" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index aa9e7894d..bb4547681 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "3.4.3" + "version": "3.5.0" } From f35d96b185f5890600a64b78e6bf939c336cbb2d Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 2 May 2022 19:07:37 +0200 Subject: [PATCH 039/179] fix(core): Make cordova bridge use Promise instead of setTimeout (#5586) --- core/cordova.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/cordova.js b/core/cordova.js index 0e46e6694..3c42fcab5 100644 --- a/core/cordova.js +++ b/core/cordova.js @@ -1116,7 +1116,7 @@ ) { var success = status === 0 || status === 1; var args = convertMessageToArgsNativeToJs(message); - setTimeout(function () { + Promise.resolve().then(function () { cordova.callbackFromNative( callbackId, success, @@ -1124,7 +1124,7 @@ args, keepCallback, ); // eslint-disable-line - }, 0); + }); }; // for backwards compatibility From 5b6dfa3fe29c85632546b299f03cc04a77cf7475 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 3 May 2022 16:00:55 +0200 Subject: [PATCH 040/179] feat(ios): Add preferredContentMode configuration option (#5583) --- cli/src/declarations.ts | 12 ++++++++++++ .../Capacitor/CAPBridgeViewController.swift | 9 +++++++++ ios/Capacitor/Capacitor/CAPInstanceConfiguration.h | 1 + ios/Capacitor/Capacitor/CAPInstanceConfiguration.m | 1 + ios/Capacitor/Capacitor/CAPInstanceDescriptor.h | 5 +++++ ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift | 3 +++ 6 files changed, 31 insertions(+) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index f609d095e..ddb43112c 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -346,6 +346,18 @@ export interface CapacitorConfig { * @default false */ limitsNavigationsToAppBoundDomains?: boolean; + + /** + * The content mode for the web view to use when it loads and renders web content. + * + * - 'recommended': The content mode that is appropriate for the current device. + * - 'desktop': The content mode that represents a desktop experience. + * - 'mobile': The content mode that represents a mobile experience. + * + * @since 4.0.0 + * @default recommended + */ + preferredContentMode?: 'recommended' | 'desktop' | 'mobile'; }; server?: { diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index 12c3abaaa..c9d1d9106 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -115,6 +115,15 @@ import Cordova webViewConfiguration.applicationNameForUserAgent = appendUserAgent } } + if let preferredContentMode = instanceConfiguration.preferredContentMode { + var mode = WKWebpagePreferences.ContentMode.recommended + if preferredContentMode == "mobile" { + mode = WKWebpagePreferences.ContentMode.mobile + } else if preferredContentMode == "desktop" { + mode = WKWebpagePreferences.ContentMode.desktop + } + webViewConfiguration.defaultWebpagePreferences.preferredContentMode = mode + } return webViewConfiguration } diff --git a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h index 045024bf0..a72f43192 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h +++ b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.h @@ -23,6 +23,7 @@ NS_SWIFT_NAME(InstanceConfiguration) @property (nonatomic, readonly, nonnull) NSURL *appLocation; @property (nonatomic, readonly, nullable) NSString *appStartPath; @property (nonatomic, readonly) BOOL limitsNavigationsToAppBoundDomains; +@property (nonatomic, readonly, nullable) NSString *preferredContentMode; @property (nonatomic, readonly, nonnull) NSDictionary *legacyConfig DEPRECATED_MSG_ATTRIBUTE("Use direct properties instead"); diff --git a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m index 17a864c47..9b5593d9a 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m +++ b/ios/Capacitor/Capacitor/CAPInstanceConfiguration.m @@ -35,6 +35,7 @@ - (instancetype)initWithDescriptor:(CAPInstanceDescriptor *)descriptor isDebug:( _appLocation = descriptor.appLocation; _appStartPath = descriptor.appStartPath; _limitsNavigationsToAppBoundDomains = descriptor.limitsNavigationsToAppBoundDomains; + _preferredContentMode = descriptor.preferredContentMode; _pluginConfigurations = descriptor.pluginConfigurations; _legacyConfig = descriptor.legacyConfig; // construct the necessary URLs diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h index 6d0fda76b..3c17e9680 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.h @@ -108,6 +108,11 @@ NS_SWIFT_NAME(InstanceDescriptor) @discussion Defaults to @c false. Set by @c ios.limitsNavigationsToAppBoundDomains in the configuration file. Required to be @c true for plugins to work if the app includes @c WKAppBoundDomains in the Info.plist. */ @property (nonatomic, assign) BOOL limitsNavigationsToAppBoundDomains; +/** + @brief The content mode for the web view to use when it loads and renders web content. + @discussion Defaults to @c recommended. Set by @c ios.preferredContentMode in the configuration file. + */ +@property (nonatomic, copy, nullable) NSString *preferredContentMode; /** @brief The parser used to load the cofiguration for Cordova plugins. */ diff --git a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift index 72ef99cbd..7cf3e52b1 100644 --- a/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift +++ b/ios/Capacitor/Capacitor/CAPInstanceDescriptor.swift @@ -125,6 +125,9 @@ internal extension InstanceDescriptor { if let limitsNavigations = config[keyPath: "ios.limitsNavigationsToAppBoundDomains"] as? Bool { limitsNavigationsToAppBoundDomains = limitsNavigations } + if let preferredMode = (config[keyPath: "ios.preferredContentMode"] as? String) { + preferredContentMode = preferredMode + } } } // swiftlint:enable cyclomatic_complexity From 8b4e861514b0fbe08e9296f49c280234f54742e1 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Wed, 4 May 2022 10:16:28 -0500 Subject: [PATCH 041/179] fix(android): move initialFocus on webview into config (#5579) --- .../src/main/java/com/getcapacitor/Bridge.java | 5 ++++- .../src/main/java/com/getcapacitor/CapConfig.java | 14 ++++++++++++++ cli/src/declarations.ts | 8 ++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 50d961f2a..e2816d82b 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -447,7 +447,10 @@ private void initWebView() { Logger.debug("WebView background color not applied"); } - webView.requestFocusFromTouch(); + if (config.isInitialFocus()) { + webView.requestFocusFromTouch(); + } + WebView.setWebContentsDebuggingEnabled(this.config.isWebContentsDebuggingEnabled()); } diff --git a/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java b/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java index 2e4450334..22f3db1a3 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java +++ b/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java @@ -40,6 +40,7 @@ public class CapConfig { private boolean captureInput = false; private boolean webContentsDebuggingEnabled = false; private boolean loggingEnabled = true; + private boolean initialFocus = true; // Embedded private String startPath; @@ -116,6 +117,7 @@ private CapConfig(Builder builder) { this.captureInput = builder.captureInput; this.webContentsDebuggingEnabled = builder.webContentsDebuggingEnabled; this.loggingEnabled = builder.loggingEnabled; + this.initialFocus = builder.initialFocus; // Embedded this.startPath = builder.startPath; @@ -187,6 +189,8 @@ private void deserializeConfig(@Nullable Context context) { loggingEnabled = isDebug; } + initialFocus = JSONUtils.getBoolean(configJSON, "android.initialFocus", initialFocus); + // Plugins pluginsConfiguration = deserializePluginsConfig(JSONUtils.getObject(configJSON, "plugins")); } @@ -243,6 +247,10 @@ public boolean isLoggingEnabled() { return loggingEnabled; } + public boolean isInitialFocus() { + return initialFocus; + } + public PluginConfig getPluginConfiguration(String pluginId) { PluginConfig pluginConfig = pluginsConfiguration.get(pluginId); if (pluginConfig == null) { @@ -398,6 +406,7 @@ public static class Builder { private boolean captureInput = false; private Boolean webContentsDebuggingEnabled = null; private boolean loggingEnabled = true; + private boolean initialFocus = false; // Embedded private String startPath = null; @@ -496,5 +505,10 @@ public Builder setLoggingEnabled(boolean enabled) { this.loggingEnabled = enabled; return this; } + + public Builder setInitialFocus(boolean focus) { + this.initialFocus = focus; + return this; + } } } diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index dc17d5bef..0c12bbb76 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -203,6 +203,14 @@ export interface CapacitorConfig { * @since 3.1.0 */ flavor?: string; + + /** + * Whether to give the webview initial focus. + * + * @since 3.5.1 + * @default true + */ + initialFocus?: boolean; }; ios?: { From 009064dbf01cbc5900bbc16053b6d7535ad4c2b4 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 4 May 2022 19:17:02 +0200 Subject: [PATCH 042/179] Release 3.5.1 --- CHANGELOG.md | 12 ++++++++++++ android/CHANGELOG.md | 11 +++++++++++ android/package.json | 2 +- cli/CHANGELOG.md | 11 +++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 11 +++++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 8 ++++++++ ios/package.json | 2 +- lerna.json | 2 +- 10 files changed, 58 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39066203..403f9806c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04) + + +### Bug Fixes + +* **android:** move initialFocus on webview into config ([#5579](https://github.com/ionic-team/capacitor/issues/5579)) ([8b4e861](https://github.com/ionic-team/capacitor/commit/8b4e861514b0fbe08e9296f49c280234f54742e1)) +* **core:** Make cordova bridge use Promise instead of setTimeout ([#5586](https://github.com/ionic-team/capacitor/issues/5586)) ([f35d96b](https://github.com/ionic-team/capacitor/commit/f35d96b185f5890600a64b78e6bf939c336cbb2d)) + + + + + # [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 4520662d0..1e5925862 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04) + + +### Bug Fixes + +* **android:** move initialFocus on webview into config ([#5579](https://github.com/ionic-team/capacitor/issues/5579)) ([8b4e861](https://github.com/ionic-team/capacitor/commit/8b4e861514b0fbe08e9296f49c280234f54742e1)) + + + + + # [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) diff --git a/android/package.json b/android/package.json index 70050b44f..56b835105 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "3.5.0", + "version": "3.5.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 9e179a65d..1381112f3 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04) + + +### Bug Fixes + +* **android:** move initialFocus on webview into config ([#5579](https://github.com/ionic-team/capacitor/issues/5579)) ([8b4e861](https://github.com/ionic-team/capacitor/commit/8b4e861514b0fbe08e9296f49c280234f54742e1)) + + + + + # [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) diff --git a/cli/package.json b/cli/package.json index cca0a347b..e2fecfdd9 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "3.5.0", + "version": "3.5.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 7c5ef69f4..16e7ccc66 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04) + + +### Bug Fixes + +* **core:** Make cordova bridge use Promise instead of setTimeout ([#5586](https://github.com/ionic-team/capacitor/issues/5586)) ([f35d96b](https://github.com/ionic-team/capacitor/commit/f35d96b185f5890600a64b78e6bf939c336cbb2d)) + + + + + # [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index b1e1bd9ad..4b0060445 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "3.5.0", + "version": "3.5.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 2da6990ea..401bde047 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04) + +**Note:** Version bump only for package @capacitor/ios + + + + + # [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22) diff --git a/ios/package.json b/ios/package.json index 35d5230b1..2e88ee96a 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "3.5.0", + "version": "3.5.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index bb4547681..008b5dc05 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "3.5.0" + "version": "3.5.1" } From 3749026d45429b86dccb787dc7dd1a2dafaed1b6 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Mon, 9 May 2022 11:08:36 -0400 Subject: [PATCH 043/179] chore(ci): change ci to use xcode 13 (#5590) * chore: change CI to use Xcode 13 * chore: try and run on macos-12 env * chore: use macos-12 on all mac actions --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/publish-ios.yml | 4 ++-- .github/workflows/publish-xcframework.yml | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 161e64adf..d78ca9a3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: path: ~/.npm key: ${{ runner.OS }}-dependency-cache-${{ hashFiles('**/package.json') }} lint: - runs-on: macos-latest + runs-on: macos-12 timeout-minutes: 30 steps: - uses: actions/setup-node@v1 @@ -43,7 +43,7 @@ jobs: - run: npm install - run: npm run lint test-cli: - runs-on: macos-latest + runs-on: macos-12 timeout-minutes: 30 needs: - setup @@ -85,7 +85,7 @@ jobs: - run: npm test working-directory: ./core test-ios: - runs-on: macos-latest + runs-on: macos-12 timeout-minutes: 30 needs: - setup @@ -93,7 +93,7 @@ jobs: strategy: matrix: xcode: - - /Applications/Xcode_12.4.app + - /Applications/Xcode_13.3.1.app steps: - run: sudo xcode-select --switch ${{ matrix.xcode }} - uses: actions/setup-node@v1 @@ -128,7 +128,7 @@ jobs: - run: npm run verify working-directory: ./android deploy-next: - runs-on: macos-latest + runs-on: macos-12 if: github.event_name == 'push' && github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'Release') && (contains(github.event.head_commit.message, 'rc') || contains(github.event.head_commit.message, 'beta') || contains(github.event.head_commit.message, 'alpha')) timeout-minutes: 30 needs: @@ -154,7 +154,7 @@ jobs: - run: npm install - run: npm run lerna:publish:next || true deploy-latest: - runs-on: macos-latest + runs-on: macos-12 if: github.event_name == 'push' && github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'Release') && (!contains(github.event.head_commit.message, 'rc') && !contains(github.event.head_commit.message, 'beta') && !contains(github.event.head_commit.message, 'alpha')) timeout-minutes: 30 needs: diff --git a/.github/workflows/publish-ios.yml b/.github/workflows/publish-ios.yml index ce50cda72..c76fd42b1 100644 --- a/.github/workflows/publish-ios.yml +++ b/.github/workflows/publish-ios.yml @@ -3,10 +3,10 @@ name: Publish Native iOS Library on: workflow_dispatch jobs: publish-ios: - runs-on: macos-11 + runs-on: macos-12 timeout-minutes: 30 steps: - - run: sudo xcode-select --switch /Applications/Xcode_12.5.app + - run: sudo xcode-select --switch /Applications/Xcode_13.3.1.app - uses: actions/setup-node@v1 with: node-version: 14.x diff --git a/.github/workflows/publish-xcframework.yml b/.github/workflows/publish-xcframework.yml index b8a722f85..624ef6b5e 100644 --- a/.github/workflows/publish-xcframework.yml +++ b/.github/workflows/publish-xcframework.yml @@ -3,10 +3,10 @@ name: Attach .xcframework file to release on: workflow_dispatch jobs: publish-xcframework: - runs-on: macos-11 + runs-on: macos-12 timeout-minutes: 30 steps: - - run: sudo xcode-select --switch /Applications/Xcode_12.5.app + - run: sudo xcode-select --switch /Applications/Xcode_13.3.1.app - run: brew install ghr - uses: actions/setup-node@v1 with: From 5fac1b2da5aa5882087716cb2aa862d89173f4a1 Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Tue, 10 May 2022 13:21:51 -0500 Subject: [PATCH 044/179] fix(ios): Use `URL(fileURLWithPath:)` instead of `URL(string:)` (#5603) * fix(ios): Creating a url from `URL(string:)` returns nil if a path contains spaces. `URL(fileURLWithPath:)` does not. * chore(ios): Remove test checking for invalid path. File urls are less strict in the characters accepted and never returns nil. * chore(ios): Add test case for file path with spaces --- ios/Capacitor/Capacitor/Router.swift | 6 +++--- ios/Capacitor/CapacitorTests/RouterTests.swift | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/Capacitor/Capacitor/Router.swift b/ios/Capacitor/Capacitor/Router.swift index 6ac417d31..24d4f3a91 100644 --- a/ios/Capacitor/Capacitor/Router.swift +++ b/ios/Capacitor/Capacitor/Router.swift @@ -15,10 +15,10 @@ public protocol Router { // swiftlint:disable:next type_name internal struct _Router: Router { func route(for path: String) -> String { - let pathUrl = URL(string: path) + let pathUrl = URL(fileURLWithPath: path) - // if the pathUrl is null, then it is an invalid url (meaning it is empty or just plain invalid) then we want to route to /index.html - if pathUrl?.pathExtension.isEmpty ?? true { + // If there's no path extension it also means the path is empty or a SPA route + if pathUrl.pathExtension.isEmpty { return "/index.html" } diff --git a/ios/Capacitor/CapacitorTests/RouterTests.swift b/ios/Capacitor/CapacitorTests/RouterTests.swift index f328c9c7c..8a6e4d45d 100644 --- a/ios/Capacitor/CapacitorTests/RouterTests.swift +++ b/ios/Capacitor/CapacitorTests/RouterTests.swift @@ -12,10 +12,6 @@ import XCTest class RouterTests: XCTestCase { let router = _Router() - func testRouterReturnsIndexWhenProvidedInvalidPath() { - XCTAssertEqual(router.route(for: "/skull.💀"), "/index.html") - } - func testRouterReturnsIndexWhenProvidedEmptyPath() { XCTAssertEqual(router.route(for: ""), "/index.html") } @@ -27,4 +23,8 @@ class RouterTests: XCTestCase { func testRouterReturnsPathWhenProvidedValidPath() { XCTAssertEqual(router.route(for: "/a/valid/path.ext"), "/a/valid/path.ext") } + + func testRouterReturnsPathWhenProvidedValidPathWithExtensionAndSpaces() { + XCTAssertEqual(router.route(for: "/a/valid/file path.ext"), "/a/valid/file path.ext") + } } From 05503cd13c5f08ad2353cbe9bb69d9e8e1c95f73 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 11 May 2022 15:23:42 +0200 Subject: [PATCH 045/179] chore(android): Remove hardcoded SDK version check (#5605) --- .../main/java/com/getcapacitor/BridgeWebChromeClient.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java index a7e34dbfa..05e934982 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java +++ b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java @@ -286,8 +286,10 @@ public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermiss callback.invoke(origin, true, false); } else { final String[] coarsePermission = { Manifest.permission.ACCESS_COARSE_LOCATION }; - // TODO replace with Build.VERSION_CODES.S once we target SDK 31 - if (Build.VERSION.SDK_INT >= 31 && PermissionHelper.hasPermissions(bridge.getContext(), coarsePermission)) { + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && + PermissionHelper.hasPermissions(bridge.getContext(), coarsePermission) + ) { callback.invoke(origin, true, false); } else { callback.invoke(origin, false, false); From 6c6630a93d2036f7a77d5112a65e7b974a360529 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 11 May 2022 18:57:21 +0200 Subject: [PATCH 046/179] chore(cli): Update dependencies to latest minor (#5597) --- cli/package.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cli/package.json b/cli/package.json index 404e52b0d..a08fc6835 100644 --- a/cli/package.json +++ b/cli/package.json @@ -44,21 +44,21 @@ "watch": "npm run assets && tsc -w" }, "dependencies": { - "@ionic/cli-framework-output": "^2.2.1", + "@ionic/cli-framework-output": "^2.2.4", "@ionic/utils-fs": "^3.1.5", - "@ionic/utils-subprocess": "^2.1.6", - "@ionic/utils-terminal": "^2.3.0", + "@ionic/utils-subprocess": "^2.1.10", + "@ionic/utils-terminal": "^2.3.2", "commander": "^6.0.0", - "debug": "^4.2.0", + "debug": "^4.3.4", "env-paths": "^2.2.0", - "kleur": "^4.1.1", + "kleur": "^4.1.4", "native-run": "^1.5.0", "open": "^7.4.2", - "plist": "^3.0.2", - "prompts": "^2.3.2", - "semver": "^7.3.2", + "plist": "^3.0.5", + "prompts": "^2.4.2", + "semver": "^7.3.7", "tar": "^6.1.11", - "tslib": "^2.1.0", + "tslib": "^2.4.0", "xml2js": "^0.4.23" }, "devDependencies": { From 7ec2aa137ddb32199bd348e814337e8d139c2e63 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 12 May 2022 10:32:29 +0200 Subject: [PATCH 047/179] chore: remove labels from PluginConfig accessors (#5610) --- ios/Capacitor/Capacitor/PluginConfig.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ios/Capacitor/Capacitor/PluginConfig.swift b/ios/Capacitor/Capacitor/PluginConfig.swift index dbe5e1378..f2461e102 100644 --- a/ios/Capacitor/Capacitor/PluginConfig.swift +++ b/ios/Capacitor/Capacitor/PluginConfig.swift @@ -9,39 +9,39 @@ import Foundation self.config = config } - public func getString(configKey: String, defaultValue: String? = nil) -> String? { + @objc public func getString(_ configKey: String, _ defaultValue: String? = nil) -> String? { if let val = (self.config)[keyPath: KeyPath(configKey)] as? String { return val } return defaultValue } - public func getBoolean(configKey: String, defaultValue: Bool) -> Bool { + @objc public func getBoolean(_ configKey: String, _ defaultValue: Bool) -> Bool { if let val = (self.config)[keyPath: KeyPath(configKey)] as? Bool { return val } return defaultValue } - public func getInt(configKey: String, defaultValue: Int) -> Int { + @objc public func getInt(_ configKey: String, _ defaultValue: Int) -> Int { if let val = (self.config)[keyPath: KeyPath(configKey)] as? Int { return val } return defaultValue } - public func getArray(configKey: String, defaultValue: JSArray? = nil) -> JSArray? { + public func getArray(_ configKey: String, _ defaultValue: JSArray? = nil) -> JSArray? { if let val = (self.config)[keyPath: KeyPath(configKey)] as? JSArray { return val } return defaultValue } - public func getObject(configKey: String) -> JSObject? { + public func getObject(_ configKey: String) -> JSObject? { return (self.config)[keyPath: KeyPath(configKey)] as? JSObject } - public func isEmpty() -> Bool { + @objc public func isEmpty() -> Bool { return self.config.isEmpty } From 1c7670d6b4c237a77b4692dc948d1ca6e69637ec Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 12 May 2022 10:45:38 +0200 Subject: [PATCH 048/179] chore(ios): Remove unused code (#5608) --- ios/Capacitor/Capacitor/CAPPlugin.h | 2 +- ios/Capacitor/Capacitor/CAPPlugin.m | 4 ---- ios/Capacitor/Capacitor/WebViewDelegationHandler.swift | 3 --- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPPlugin.h b/ios/Capacitor/Capacitor/CAPPlugin.h index d09bdd157..7f1112bd4 100644 --- a/ios/Capacitor/Capacitor/CAPPlugin.h +++ b/ios/Capacitor/Capacitor/CAPPlugin.h @@ -49,6 +49,6 @@ -(id _Nullable)getConfigValue:(NSString* _Nonnull)key __deprecated_msg("use getConfig() and access config values using the methods available depending on the type."); -(PluginConfig* _Nonnull)getConfig; -(void)setCenteredPopover:(UIViewController* _Nonnull)vc; --(BOOL)supportsPopover; +-(BOOL)supportsPopover DEPRECATED_MSG_ATTRIBUTE("All iOS 13+ devices support popover"); @end diff --git a/ios/Capacitor/Capacitor/CAPPlugin.m b/ios/Capacitor/Capacitor/CAPPlugin.m index 7a7f76099..99b5abd11 100644 --- a/ios/Capacitor/Capacitor/CAPPlugin.m +++ b/ios/Capacitor/Capacitor/CAPPlugin.m @@ -146,11 +146,7 @@ -(void)setCenteredPopover:(UIViewController *) vc { } -(BOOL)supportsPopover { - if (@available(iOS 13, *)) { return YES; - } else { - return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad; - } } - (NSNumber*)shouldOverrideLoad:(WKNavigationAction*)navigationAction { diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift index 33c8689e2..ade2db18a 100644 --- a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift @@ -47,8 +47,6 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel bridge?.reset() } - // TODO: remove once Xcode 12 support is dropped - #if compiler(>=5.5) @available(iOS 15, *) func webView( _ webView: WKWebView, @@ -67,7 +65,6 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel decisionHandler: @escaping (WKPermissionDecision) -> Void) { decisionHandler(.grant) } - #endif public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { // post a notification for any listeners From 5c588d5bd15b2b939c6efc25b7db9d6af29afae0 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 12 May 2022 11:00:29 +0200 Subject: [PATCH 049/179] chore(android): Fix deprecation warnings on cordova compat code (#5607) --- .../getcapacitor/cordova/CapacitorCordovaCookieManager.java | 3 +-- .../com/getcapacitor/cordova/MockCordovaWebViewImpl.java | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java index 3a2a1bd9c..72ac4ee79 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/CapacitorCordovaCookieManager.java @@ -12,7 +12,6 @@ class CapacitorCordovaCookieManager implements ICordovaCookieManager { public CapacitorCordovaCookieManager(WebView webview) { webView = webview; cookieManager = CookieManager.getInstance(); - CookieManager.setAcceptFileSchemeCookies(true); cookieManager.setAcceptThirdPartyCookies(webView, true); } @@ -33,7 +32,7 @@ public String getCookie(final String url) { @Override public void clearCookies() { - cookieManager.removeAllCookie(); + cookieManager.removeAllCookies(null); } @Override diff --git a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java index b95bb031b..d5b77cddb 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java +++ b/android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java @@ -112,6 +112,7 @@ public boolean canGoBack() { @Override public void clearCache() {} + @Deprecated @Override public void clearCache(boolean b) {} @@ -181,6 +182,7 @@ public void handleDestroy() { this.pluginManager.onDestroy(); } + @Deprecated @Override public void sendJavascript(String statememt) { nativeToJsMessageQueue.addJavaScript(statememt); @@ -198,14 +200,17 @@ public void triggerDocumentEvent(final String eventName) { @Override public void showWebPage(String url, boolean openExternal, boolean clearHistory, Map params) {} + @Deprecated @Override public boolean isCustomViewShowing() { return false; } + @Deprecated @Override public void showCustomView(View view, WebChromeClient.CustomViewCallback callback) {} + @Deprecated @Override public void hideCustomView() {} From 4e8f895562ae360878f7d1780301745dfe11ab9b Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 12 May 2022 11:13:25 +0200 Subject: [PATCH 050/179] Release 4.0.0-alpha.2 --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ android/CHANGELOG.md | 20 ++++++++++++++++++++ cli/CHANGELOG.md | 12 ++++++++++++ core/CHANGELOG.md | 8 ++++++++ ios/CHANGELOG.md | 17 +++++++++++++++++ lerna.json | 2 +- 6 files changed, 85 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0765bdc6..199b951f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12) + + +### Bug Fixes + +* **android:** make removeAllListeners return a promise ([#5527](https://github.com/ionic-team/capacitor/issues/5527)) ([6f4d858](https://github.com/ionic-team/capacitor/commit/6f4d858ea879d97109c0c7da2d664d04806adc2a)) +* **android:** prevent app from loading if server.url is invalid ([d4a0dea](https://github.com/ionic-team/capacitor/commit/d4a0deaa37eda4476f0be030e266c2c1260fc6e8)) +* **cli:** Use CURRENT_PROJECT_VERSION variable on ios template ([#5418](https://github.com/ionic-team/capacitor/issues/5418)) ([0a07648](https://github.com/ionic-team/capacitor/commit/0a07648b4d1c5fb1fd7de3c72ac1bbcb30f48203)) +* **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc)) + + +### Features + +* **android:** don't allow server.androidScheme to be set to schemes handled by WebView ([01285ba](https://github.com/ionic-team/capacitor/commit/01285ba253d602b08a41240ad2ccf370730d51a3)) +* **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3)) +* **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad)) +* **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672)) +* **cli:** export android-template activity for SDK 31 support ([#5351](https://github.com/ionic-team/capacitor/issues/5351)) ([79b4a3c](https://github.com/ionic-team/capacitor/commit/79b4a3c56ce9704bc8f5b0b8ce6d5e60f86d4d2c)) +* **cli:** set targetSDK to 31 in android-template ([#5443](https://github.com/ionic-team/capacitor/issues/5443)) ([8793c58](https://github.com/ionic-team/capacitor/commit/8793c58f24611d7780aff80d547b367f4114f7c5)) +* **cli:** Upgrade Gradle to 7.4 in the template ([#5446](https://github.com/ionic-team/capacitor/issues/5446)) ([11b648d](https://github.com/ionic-team/capacitor/commit/11b648d21567c5ab8e7e195fdefec1e1254ce62a)) +* **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09)) +* **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475)) + + + + + # [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 0f1a858a7..55b56996c 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12) + + +### Bug Fixes + +* **android:** make removeAllListeners return a promise ([#5527](https://github.com/ionic-team/capacitor/issues/5527)) ([6f4d858](https://github.com/ionic-team/capacitor/commit/6f4d858ea879d97109c0c7da2d664d04806adc2a)) +* **android:** prevent app from loading if server.url is invalid ([d4a0dea](https://github.com/ionic-team/capacitor/commit/d4a0deaa37eda4476f0be030e266c2c1260fc6e8)) + + +### Features + +* **android:** don't allow server.androidScheme to be set to schemes handled by WebView ([01285ba](https://github.com/ionic-team/capacitor/commit/01285ba253d602b08a41240ad2ccf370730d51a3)) +* **android:** set default targetSDK to 31 ([#5442](https://github.com/ionic-team/capacitor/issues/5442)) ([4442459](https://github.com/ionic-team/capacitor/commit/4442459b24cdbac25cb1e4de11583d22c21452b3)) +* **android:** Upgrade gradle to 7.4 ([#5445](https://github.com/ionic-team/capacitor/issues/5445)) ([28eaf18](https://github.com/ionic-team/capacitor/commit/28eaf1851fa7a912917dbb40c68fb4dd583d08ad)) +* **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672)) + + + + + # [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 23c28ca9c..32146fcab 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12) + + +### Features + +* **android:** Use java 11 ([#5552](https://github.com/ionic-team/capacitor/issues/5552)) ([e47959f](https://github.com/ionic-team/capacitor/commit/e47959fcbd6a89b97b1275a5814fdb4e7ce30672)) +* **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475)) + + + + + # [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) **Note:** Version bump only for package @capacitor/cli diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 4b98d1884..a6f70c303 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12) + +**Note:** Version bump only for package @capacitor/core + + + + + # [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) **Note:** Version bump only for package @capacitor/core diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 9cdfb5f6a..56e050018 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12) + + +### Bug Fixes + +* **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc)) + + +### Features + +* **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09)) +* **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475)) + + + + + # [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25) diff --git a/lerna.json b/lerna.json index a8449f8b3..fcd37aac5 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "4.0.0-alpha.1" + "version": "4.0.0-alpha.2" } From 8b8be4706b7534e346738479865e5f66a25005bf Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 13 May 2022 14:53:01 +0200 Subject: [PATCH 051/179] feat(cli): set targetSDK to 32 in android-template (#5612) --- android-template/variables.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android-template/variables.gradle b/android-template/variables.gradle index 2c907f115..fcae8c3e1 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -1,7 +1,7 @@ ext { minSdkVersion = 22 - compileSdkVersion = 31 - targetSdkVersion = 31 + compileSdkVersion = 32 + targetSdkVersion = 32 androidxActivityVersion = '1.4.0' androidxAppCompatVersion = '1.4.1' androidxCoordinatorLayoutVersion = '1.2.0' From 416b9662fbf6233d23216c0c0441862603c3a723 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 13 May 2022 15:31:35 +0200 Subject: [PATCH 052/179] feat(android): set default targetSDK to 32 (#5611) --- android/capacitor/build.gradle | 4 ++-- capacitor-cordova-android-plugins/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index b96b05a52..a79c17302 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -26,10 +26,10 @@ tasks.withType(Javadoc).all { enabled = false } apply plugin: 'com.android.library' android { - compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 31 + compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 32 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 31 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 32 versionCode 1 versionName "1.0" consumerProguardFiles 'proguard-rules.pro' diff --git a/capacitor-cordova-android-plugins/build.gradle b/capacitor-cordova-android-plugins/build.gradle index 3fbe9797f..6896e52e2 100644 --- a/capacitor-cordova-android-plugins/build.gradle +++ b/capacitor-cordova-android-plugins/build.gradle @@ -16,10 +16,10 @@ buildscript { apply plugin: 'com.android.library' android { - compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 31 + compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 32 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 - targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 31 + targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 32 versionCode 1 versionName "1.0" } From 6e3ed55389e09d503297acc9107e38867d373915 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 13 May 2022 18:11:41 +0200 Subject: [PATCH 053/179] chore(android): Add deprecated annotation to shouldOverrideUrlLoading (#5616) --- .../src/main/java/com/getcapacitor/BridgeWebViewClient.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebViewClient.java b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebViewClient.java index b8f3c7034..1a1acc662 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/BridgeWebViewClient.java +++ b/android/capacitor/src/main/java/com/getcapacitor/BridgeWebViewClient.java @@ -28,6 +28,7 @@ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request return bridge.launchIntent(url); } + @Deprecated @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return bridge.launchIntent(Uri.parse(url)); From 84fe693f8d66827dbba733f6e32185ae34a3e41e Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 17 May 2022 16:37:37 +0200 Subject: [PATCH 054/179] chore: Silence NativePlugin deprecation warnings (#5625) --- .../main/java/com/getcapacitor/Plugin.java | 23 +++++++++++-------- .../java/com/getcapacitor/PluginHandle.java | 4 ++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Plugin.java b/android/capacitor/src/main/java/com/getcapacitor/Plugin.java index 5dccc054d..cfcc98bc5 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Plugin.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Plugin.java @@ -789,15 +789,7 @@ public void checkPermissions(PluginCall pluginCall) { public void requestPermissions(PluginCall call) { CapacitorPlugin annotation = handle.getPluginAnnotation(); if (annotation == null) { - // handle permission requests for plugins defined with @NativePlugin (prior to 3.0.0) - NativePlugin legacyAnnotation = this.handle.getLegacyPluginAnnotation(); - String[] perms = legacyAnnotation.permissions(); - if (perms.length > 0) { - saveCall(call); - pluginRequestPermissions(perms, legacyAnnotation.permissionRequestCode()); - } else { - call.resolve(); - } + handleLegacyPermission(call); } else { // handle permission requests for plugins defined with @CapacitorPlugin (since 3.0.0) String[] permAliases = null; @@ -863,6 +855,19 @@ public void requestPermissions(PluginCall call) { } } + @SuppressWarnings("deprecation") + private void handleLegacyPermission(PluginCall call) { + // handle permission requests for plugins defined with @NativePlugin (prior to 3.0.0) + NativePlugin legacyAnnotation = this.handle.getLegacyPluginAnnotation(); + String[] perms = legacyAnnotation.permissions(); + if (perms.length > 0) { + saveCall(call); + pluginRequestPermissions(perms, legacyAnnotation.permissionRequestCode()); + } else { + call.resolve(); + } + } + /** * Handle request permissions result. A plugin using the deprecated {@link NativePlugin} * should override this to handle the result, or this method will handle the result diff --git a/android/capacitor/src/main/java/com/getcapacitor/PluginHandle.java b/android/capacitor/src/main/java/com/getcapacitor/PluginHandle.java index b08cbad54..cb1bf448e 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/PluginHandle.java +++ b/android/capacitor/src/main/java/com/getcapacitor/PluginHandle.java @@ -20,11 +20,14 @@ public class PluginHandle { private final String pluginId; + @SuppressWarnings("deprecation") private NativePlugin legacyPluginAnnotation; + private CapacitorPlugin pluginAnnotation; private Plugin instance; + @SuppressWarnings("deprecation") public PluginHandle(Bridge bridge, Class pluginClass) throws InvalidPluginException, PluginLoadException { this.bridge = bridge; this.pluginClass = pluginClass; @@ -67,6 +70,7 @@ public String getId() { return this.pluginId; } + @SuppressWarnings("deprecation") public NativePlugin getLegacyPluginAnnotation() { return this.legacyPluginAnnotation; } From d7d549b46f830822ec2c43faf54dba12422b1586 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 18 May 2022 18:27:13 +0200 Subject: [PATCH 055/179] chore(android): Silence warning on FileUtils (#5628) --- .../src/main/java/com/getcapacitor/FileUtils.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java b/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java index 9930917ce..4e5e2bd85 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/FileUtils.java @@ -77,7 +77,7 @@ public static String getFileUrlForUri(final Context context, final Uri uri) { final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { - return Environment.getExternalStorageDirectory() + "/" + split[1]; + return legacyPrimaryPath(split[1]); } else { final int splitIndex = docId.indexOf(':', 1); final String tag = docId.substring(0, splitIndex); @@ -136,6 +136,11 @@ else if ("file".equalsIgnoreCase(uri.getScheme())) { return null; } + @SuppressWarnings("deprecation") + private static String legacyPrimaryPath(String pathPart) { + return Environment.getExternalStorageDirectory() + "/" + pathPart; + } + /** * Read a plaintext file. * From c7db269777b5dfe99e4907a6aec9b50243f00c71 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Thu, 19 May 2022 09:20:41 -0400 Subject: [PATCH 056/179] chore(iOS): scanHexInt32 is deprecated so use scanHexInt64 instead (#5631) --- ios/Capacitor/Capacitor/UIColor.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Capacitor/Capacitor/UIColor.swift b/ios/Capacitor/Capacitor/UIColor.swift index 677ba54c3..b33bd3d4a 100644 --- a/ios/Capacitor/Capacitor/UIColor.swift +++ b/ios/Capacitor/Capacitor/UIColor.swift @@ -26,14 +26,14 @@ public extension CapacitorExtensionTypeWrapper where T: UIColor { with: "" ) - var argb: UInt32 = 0 + var argb: UInt64 = 0 var red: CGFloat = 0.0 var green: CGFloat = 0.0 var blue: CGFloat = 0.0 var alpha: CGFloat = 1.0 - guard Scanner(string: hexString).scanHexInt32(&argb) else { return nil } + guard Scanner(string: hexString).scanHexInt64(&argb) else { return nil } if hexString.count == 6 { red = CGFloat((argb & 0xFF0000) >> 16) / 255.0 From 9e2a5e4fa12f4049a1255da0ca849ecaf7ac3e86 Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Thu, 19 May 2022 14:02:58 -0500 Subject: [PATCH 057/179] Router base path (#5630) * The undocumented Router protocol currently only allows for specifyingrelative paths. This change makes it possible to point to an entirely different base url (which is needed for portals for capacitor) * chore: fix added router test * chore: Move router call in the else branch of WebViewAssetHandler. No need to run the method if it's not needed. --- ios/Capacitor/Capacitor/Router.swift | 6 ++++-- .../Capacitor/WebViewAssetHandler.swift | 10 ++++------ .../CapacitorTests/RouterTests.swift | 19 ++++++++++++++----- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/ios/Capacitor/Capacitor/Router.swift b/ios/Capacitor/Capacitor/Router.swift index 24d4f3a91..4a125a036 100644 --- a/ios/Capacitor/Capacitor/Router.swift +++ b/ios/Capacitor/Capacitor/Router.swift @@ -10,18 +10,20 @@ import Foundation public protocol Router { func route(for path: String) -> String + var basePath: String { get set } } // swiftlint:disable:next type_name internal struct _Router: Router { + var basePath: String = "" func route(for path: String) -> String { let pathUrl = URL(fileURLWithPath: path) // If there's no path extension it also means the path is empty or a SPA route if pathUrl.pathExtension.isEmpty { - return "/index.html" + return basePath + "/index.html" } - return path + return basePath + path } } diff --git a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift index da63326d3..225299f74 100644 --- a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift @@ -3,8 +3,7 @@ import MobileCoreServices @objc(CAPWebViewAssetHandler) internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { - private let router: Router - private var basePath: String = "" + private var router: Router init(router: Router) { self.router = router @@ -12,19 +11,18 @@ internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { } func setAssetPath(_ assetPath: String) { - self.basePath = assetPath + router.basePath = assetPath } func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { - var startPath = self.basePath + let startPath: String let url = urlSchemeTask.request.url! let stringToLoad = url.path - let resolvedRoute = router.route(for: stringToLoad) if stringToLoad.starts(with: CapacitorBridge.fileStartIdentifier) { startPath = stringToLoad.replacingOccurrences(of: CapacitorBridge.fileStartIdentifier, with: "") } else { - startPath.append(resolvedRoute) + startPath = router.route(for: stringToLoad) } let localUrl = URL.init(string: url.absoluteString)! diff --git a/ios/Capacitor/CapacitorTests/RouterTests.swift b/ios/Capacitor/CapacitorTests/RouterTests.swift index 8a6e4d45d..cb50e12d3 100644 --- a/ios/Capacitor/CapacitorTests/RouterTests.swift +++ b/ios/Capacitor/CapacitorTests/RouterTests.swift @@ -10,21 +10,30 @@ import XCTest @testable import Capacitor class RouterTests: XCTestCase { - let router = _Router() func testRouterReturnsIndexWhenProvidedEmptyPath() { - XCTAssertEqual(router.route(for: ""), "/index.html") + checkRouter(path: "", expected: "/index.html") } func testRouterReturnsIndexWhenProviedPathWithoutExtension() { - XCTAssertEqual(router.route(for: "/a/valid/path/no/ext"), "/index.html") + checkRouter(path: "/a/valid/path/no/ext", expected: "/index.html") } func testRouterReturnsPathWhenProvidedValidPath() { - XCTAssertEqual(router.route(for: "/a/valid/path.ext"), "/a/valid/path.ext") + checkRouter(path: "/a/valid/path.ext", expected: "/a/valid/path.ext") } func testRouterReturnsPathWhenProvidedValidPathWithExtensionAndSpaces() { - XCTAssertEqual(router.route(for: "/a/valid/file path.ext"), "/a/valid/file path.ext") + checkRouter(path: "/a/valid/file path.ext", expected: "/a/valid/file path.ext") } + + func checkRouter(path: String, expected: String) { + XCTContext.runActivity(named: "router creates route path correctly") { _ in + var router = _Router() + XCTAssertEqual(router.route(for: path), expected) + router.basePath = "/A/Route" + XCTAssertEqual(router.route(for: path), "/A/Route" + expected) + } + } + } From 2d9d0a75ca5d57b3acc33cb9444d968e574a3c66 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 24 May 2022 16:41:44 +0200 Subject: [PATCH 058/179] chore(android): Silence deprecation warnings from CapacitorWebView (#5642) --- .../src/main/java/com/getcapacitor/CapacitorWebView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java b/android/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java index 7fd14bf51..2320bdd09 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java +++ b/android/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java @@ -30,6 +30,7 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { } @Override + @SuppressWarnings("deprecation") public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { evaluateJavascript("document.activeElement.value = document.activeElement.value + '" + event.getCharacters() + "';", null); From 9d76869129cec6ea67c083a850447b4bfcf48947 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 26 May 2022 09:59:33 +0200 Subject: [PATCH 059/179] feat(android-template): use DayNight theme as default (#5643) --- android-template/app/src/main/res/values/styles.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-template/app/src/main/res/values/styles.xml b/android-template/app/src/main/res/values/styles.xml index bb45498f6..ad257d316 100644 --- a/android-template/app/src/main/res/values/styles.xml +++ b/android-template/app/src/main/res/values/styles.xml @@ -9,7 +9,7 @@ @color/colorAccent - - - \ No newline at end of file diff --git a/android-template/variables.gradle b/android-template/variables.gradle index 4c7ddfce2..0ef63ed0c 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -7,6 +7,7 @@ ext { androidxCoordinatorLayoutVersion = '1.2.0' androidxCoreVersion = '1.8.0' androidxFragmentVersion = '1.4.1' + coreSplashScreenVersion = '1.0.0-rc01' junitVersion = '4.13.2' androidxJunitVersion = '1.1.3' androidxEspressoCoreVersion = '3.4.0' From dd164786d087550d095e8f554a38b85f31e8dea0 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 26 Jul 2022 18:43:15 +0200 Subject: [PATCH 116/179] chore(android-template): Add androidxWebkitVersion variable (#5792) --- android-template/variables.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/android-template/variables.gradle b/android-template/variables.gradle index 0ef63ed0c..777bd7e87 100644 --- a/android-template/variables.gradle +++ b/android-template/variables.gradle @@ -8,6 +8,7 @@ ext { androidxCoreVersion = '1.8.0' androidxFragmentVersion = '1.4.1' coreSplashScreenVersion = '1.0.0-rc01' + androidxWebkitVersion = '1.4.0' junitVersion = '4.13.2' androidxJunitVersion = '1.1.3' androidxEspressoCoreVersion = '3.4.0' From f5afa94b3b9c246d87b2af03359840f503bace90 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 27 Jul 2022 10:19:59 -0400 Subject: [PATCH 117/179] feat(iOS): post install script for deployment target (#5783) * feat: post install script for deployment target * chore: fix path to helper file * chore: put post_install in better spot Co-authored-by: jcesarmobile --- ios-template/App/Podfile | 6 ++++++ ios/scripts/pods_helpers.rb | 12 ++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 ios/scripts/pods_helpers.rb diff --git a/ios-template/App/Podfile b/ios-template/App/Podfile index 8594fc2cd..affb695cb 100644 --- a/ios-template/App/Podfile +++ b/ios-template/App/Podfile @@ -1,3 +1,5 @@ +require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers' + platform :ios, '13.0' use_frameworks! @@ -15,3 +17,7 @@ target 'App' do capacitor_pods # Add your Pods here end + +post_install do |installer| + assertDeploymentTarget(installer) +end diff --git a/ios/scripts/pods_helpers.rb b/ios/scripts/pods_helpers.rb new file mode 100644 index 000000000..7a9fa41f4 --- /dev/null +++ b/ios/scripts/pods_helpers.rb @@ -0,0 +1,12 @@ +def assertDeploymentTarget(installer) + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 13.0 + deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f + should_upgrade = deployment_target < 13.0 && deployment_target != 0.0 + if should_upgrade + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + end + end + end +end \ No newline at end of file From 7cb660a34d9a87274761d4492d0d77c9ef44ace8 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 27 Jul 2022 14:33:38 -0400 Subject: [PATCH 118/179] feat(cli): add migrator for cap3 to cap4 (#5762) * feat(cli): add migrator for cap3 to cap4 * chore: fmt * chore: verbage * chore: version fix for betas * chore: cleanup * chore: touch ups for spinners and funcs * chore: fmt * chore: modify packagejson replacements * chore: fmt * chore: use paths from config object * chore: get variables from template * Update cli/src/tasks/migrate.ts Co-authored-by: jcesarmobile * chore: add android 12 splash and podfile postinstall * chore: npm install fix * chore: fmt * chore: activity path fix * chore: post_install add if already exists * chore: remove comment for init() * chore: path fix for require_relative * chore: look for already migrated post_install * chore: fmt * chore: remove unused var * chore: remove more unused * chore: move app build.gradle edit logic * chore: only add if not exists * chore: check for each dep * Update cli/src/tasks/migrate.ts Co-authored-by: jcesarmobile * chore: fmt * chore: more fmt * chore: use latest Co-authored-by: jcesarmobile --- cli/package.json | 3 +- cli/src/index.ts | 14 +- cli/src/tasks/migrate.ts | 903 +++++++++++++++++++++++++++++++++++++++ cli/src/util/fs.ts | 23 + 4 files changed, 941 insertions(+), 2 deletions(-) create mode 100644 cli/src/tasks/migrate.ts diff --git a/cli/package.json b/cli/package.json index 1de02f3cd..2cb35c9d9 100644 --- a/cli/package.json +++ b/cli/package.json @@ -56,6 +56,7 @@ "open": "^8.4.0", "plist": "^3.0.5", "prompts": "^2.4.2", + "rimraf": "^3.0.2", "semver": "^7.3.7", "tar": "^6.1.11", "tslib": "^2.4.0", @@ -66,12 +67,12 @@ "@types/jest": "^26.0.4", "@types/plist": "^3.0.2", "@types/prompts": "^2.0.14", + "@types/rimraf": "^3.0.2", "@types/semver": "^7.3.10", "@types/tar": "^6.1.1", "@types/tmp": "^0.2.3", "@types/xml2js": "0.4.5", "jest": "^26.1.0", - "rimraf": "^3.0.2", "tmp": "^0.2.1", "ts-jest": "^26.1.3", "typescript": "~4.1.2" diff --git a/cli/src/index.ts b/cli/src/index.ts index c5f1be25c..c6fe92409 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -20,7 +20,7 @@ export async function run(): Promise { try { const config = await loadConfig(); runProgram(config); - } catch (e) { + } catch (e: any) { process.exitCode = isFatal(e) ? e.exitCode : 1; logger.error(e.message ? e.message : String(e)); } @@ -224,6 +224,18 @@ export function runProgram(config: Config): void { }), ); + program + .command('migrate') + .description( + 'Migrate your current Capacitor app to the latest major version of Capacitor.', + ) + .action( + wrapAction(async () => { + const { migrateCommand } = await import('./tasks/migrate'); + await migrateCommand(config); + }), + ); + program.arguments('[command]').action( wrapAction(async cmd => { if (typeof cmd === 'undefined') { diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts new file mode 100644 index 000000000..69318d886 --- /dev/null +++ b/cli/src/tasks/migrate.ts @@ -0,0 +1,903 @@ +import { writeFileSync, readFileSync, existsSync } from '@ionic/utils-fs'; +import { join } from 'path'; +import rimraf from 'rimraf'; + +import { runTask } from '../common'; +import type { Config } from '../definitions'; +import { fatal } from '../errors'; +import { logger, logPrompt, logSuccess } from '../log'; +import { deleteFolderRecursive } from '../util/fs'; +import { getCommandOutput } from '../util/subprocess'; +import { extractTemplate } from '../util/template'; +import { readXML } from '../util/xml'; + +// eslint-disable-next-line prefer-const +let allDependencies: { [key: string]: any } = {}; +const libs = [ + '@capacitor/core', + '@capacitor/cli', + '@capacitor/ios', + '@capacitor/android', +]; +const plugins = [ + '@capacitor/action-sheet', + '@capacitor/app', + '@capacitor/app-launcher', + '@capacitor/browser', + '@capacitor/camera', + '@capacitor/clipboard', + '@capacitor/device', + '@capacitor/dialog', + '@capacitor/filesystem', + '@capacitor/geolocation', + '@capacitor/google-maps', + '@capacitor/haptics', + '@capacitor/keyboard', + '@capacitor/local-notifications', + '@capacitor/motion', + '@capacitor/network', + '@capacitor/push-notifications', + '@capacitor/screen-reader', + '@capacitor/share', + '@capacitor/splash-screen', + '@capacitor/status-bar', + '@capacitor/text-zoom', + '@capacitor/toast', +]; +const coreVersion = 'latest'; +const pluginVersion = 'latest'; + +export async function migrateCommand(config: Config): Promise { + if (config === null) { + fatal('Config data missing'); + } + + const variablesAndClasspaths: + | { + 'variables': any; + 'com.android.tools.build:gradle': string; + 'com.google.gms:google-services': string; + } + | undefined = await getAndroidVarriablesAndClasspaths(config); + + if (!variablesAndClasspaths) { + fatal('Variable and Classpath info could not be read.'); + } + + //* + + allDependencies = { + ...config.app.package.dependencies, + ...config.app.package.devDependencies, + }; + + const monorepoWarning = + 'Please note this tool is not intended for use in a mono-repo enviroment, please check out the Ionic vscode extension for this functionality.'; + + logger.info(monorepoWarning); + + const { migrateconfirm } = await logPrompt( + `Capacitor 4 sets a deployment target of iOS 13 and Android 12 (SDK 32). \n`, + { + type: 'text', + name: 'migrateconfirm', + message: `Are you sure you want to migrate? (Y/n)`, + initial: 'y', + }, + ); + + if ( + typeof migrateconfirm === 'string' && + migrateconfirm.toLowerCase() === 'y' + ) { + try { + const { npmInstallConfirm } = await logPrompt( + `Would you like the migrator to run npm install to install the latest versions of capacitor packages? (Those using other package managers should answer N)`, + { + type: 'text', + name: 'npmInstallConfirm', + message: `Run Npm Install? (Y/n)`, + initial: 'y', + }, + ); + const runNpmInstall = + typeof npmInstallConfirm === 'string' && + npmInstallConfirm.toLowerCase() === 'y'; + + await runTask(`Installing Latest NPM Modules.`, () => { + return installLatestNPMLibs(runNpmInstall, config); + }); + + await runTask( + `Migrating @capacitor/storage to @capacitor/preferences.`, + () => { + return migrateStoragePluginToPreferences(runNpmInstall); + }, + ); + + if ( + allDependencies['@capacitor/ios'] && + existsSync(config.ios.platformDirAbs) + ) { + // Set deployment target to 13.0 + await runTask(`Migrating deployment target to 13.0.`, () => { + return updateFile( + config, + join(config.ios.nativeXcodeProjDirAbs, 'project.pbxproj'), + 'IPHONEOS_DEPLOYMENT_TARGET = ', + ';', + '13.0', + ); + }); + + // Update Podfile to 13.0 + await runTask(`Migrating Podfile to 13.0.`, () => { + return updateFile( + config, + join(config.ios.nativeProjectDirAbs, 'Podfile'), + `platform :ios, '`, + `'`, + '13.0', + ); + }); + + await runTask(`Migrating Podfile to use post_install script.`, () => { + return podfileAssertDeploymentTarget( + join(config.ios.nativeProjectDirAbs, 'Podfile'), + ); + }); + + // Remove touchesBegan + await runTask( + `Migrating AppDelegate.swift by removing touchesBegan.`, + () => { + return updateFile( + config, + join(config.ios.nativeTargetDirAbs, 'AppDelegate.swift'), + `override func touchesBegan`, + `}`, + undefined, + true, + ); + }, + ); + + // Remove NSAppTransportSecurity + await runTask( + `Migrating Info.plist by removing NSAppTransportSecurity key.`, + () => { + return removeKey( + join(config.ios.nativeTargetDirAbs, 'Info.plist'), + 'NSAppTransportSecurity', + ); + }, + ); + + // Remove USE_PUSH + await runTask(`Migrating by removing USE_PUSH.`, () => { + return replacePush( + join(config.ios.nativeXcodeProjDirAbs, 'project.pbxproj'), + ); + }); + + // Remove from App Delegate + await runTask(`Migrating App Delegate.`, () => { + return replaceIfUsePush(config); + }); + } + + if ( + allDependencies['@capacitor/android'] && + existsSync(config.android.platformDirAbs) + ) { + // AndroidManifest.xml add attribute: { + return updateAndroidManifest( + join(config.android.srcMainDirAbs, 'AndroidManifest.xml'), + ); + }, + ); + + // Update build.gradle + const { leaveJCenterPrompt } = await logPrompt( + `Some projects still require JCenter to function. If your project does, please answer yes below.`, + { + type: 'text', + name: 'leaveJCenterPrompt', + message: `Keep JCenter if present? (y/N)`, + initial: 'n', + }, + ); + await runTask(`Migrating build.gradle file.`, () => { + return updateBuildGradle( + join(config.android.platformDirAbs, 'build.gradle'), + typeof leaveJCenterPrompt === 'string' && + leaveJCenterPrompt.toLowerCase() === 'y', + variablesAndClasspaths, + ); + }); + + // Update app.gradle + await runTask(`Migrating app/build.gradle file.`, () => { + return updateAppBuildGradle( + join(config.android.appDirAbs, 'build.gradle'), + ); + }); + + // Update gradle-wrapper.properties + await runTask( + `Migrating gradle-wrapper.properties by updating gradle version from 7.0 to 7.4.2.`, + () => { + return updateGradleWrapper( + join( + config.android.platformDirAbs, + 'gradle', + 'wrapper', + 'gradle-wrapper.properties', + ), + ); + }, + ); + + // Variables gradle + await runTask(`Migrating variables.gradle file.`, () => { + return (async (): Promise => { + for (const variable of Object.keys( + variablesAndClasspaths.variables, + )) { + if ( + !(await updateFile( + config, + join(config.android.platformDirAbs, 'variables.gradle'), + `${variable} = '`, + `'`, + variablesAndClasspaths.variables[variable].toString(), + true, + )) + ) { + const didWork = await updateFile( + config, + join(config.android.platformDirAbs, 'variables.gradle'), + `${variable} = `, + `\n`, + variablesAndClasspaths.variables[variable].toString(), + true, + ); + if (!didWork) { + let file = readFile( + join(config.android.platformDirAbs, 'variables.gradle'), + ); + if (file) { + file = file.replace( + 'ext {', + `ext {\n ${variable} = '${variablesAndClasspaths.variables[ + variable + ].toString()}'`, + ); + writeFileSync( + join(config.android.platformDirAbs, 'variables.gradle'), + file, + ); + } + } + } + } + })(); + }); + + // remove init + await runTask('Migrating MainActivity by removing init().', () => { + return removeOldInitAndroid(config); + }); + + rimraf.sync(join(config.android.appDirAbs, 'build')); + + // add new splashscreen + await runTask('Migrate to Android 12 Splashscreen.', () => { + return addNewSplashScreen(config); + }); + } + + // Run Cap Sync + await runTask(`Running cap sync.`, () => { + return getCommandOutput('npx', ['cap', 'sync']); + }); + + // Write all breaking changes + await runTask(`Writing breaking changes.`, () => { + return writeBreakingChanges(); + }); + + logSuccess( + `Migration to Capacitor ${coreVersion} is complete. Run and test your app!`, + ); + } catch (err) { + fatal(`Failed to migrate: ${err}`); + } + } else { + fatal(`User canceled migration.`); + } + //*/ +} + +async function installLatestNPMLibs(runInstall: boolean, config: Config) { + const pkgJsonPath = join(config.app.rootDir, 'package.json'); + const pkgJsonFile = readFile(pkgJsonPath); + if (!pkgJsonFile) { + return; + } + const pkgJson: any = JSON.parse(pkgJsonFile); + + for (const devDepKey of Object.keys(pkgJson['devDependencies'])) { + if (libs.includes(devDepKey)) { + pkgJson['devDependencies'][devDepKey] = coreVersion; + } else if (plugins.includes(devDepKey)) { + pkgJson['devDependencies'][devDepKey] = pluginVersion; + } + } + for (const depKey of Object.keys(pkgJson['dependencies'])) { + if (libs.includes(depKey)) { + pkgJson['dependencies'][depKey] = coreVersion; + } else if (plugins.includes(depKey)) { + pkgJson['dependencies'][depKey] = pluginVersion; + } + } + + writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2), { + encoding: 'utf-8', + }); + + if (runInstall) { + rimraf.sync(join(config.app.rootDir, 'package-lock.json')); + rimraf.sync(join(config.app.rootDir, 'node_modules/@capacitor/!(cli)')); + await getCommandOutput('npm', ['i']); + } else { + logger.info( + `Please run an install command with your package manager of choice. (ex: yarn install)`, + ); + } +} + +async function migrateStoragePluginToPreferences(runInstall: boolean) { + if (allDependencies['@capacitor/storage']) { + logger.info( + 'NOTE: @capacitor/storage was renamed to @capacitor/preferences, please be sure to replace occurances in your code.', + ); + if (runInstall) { + await getCommandOutput('npm', ['uninstall', '@capacitor/storage']); + await getCommandOutput('npm', [ + 'i', + `@capacitor/preferences@${pluginVersion}`, + ]); + } else { + logger.info( + `Please manually uninstall @capacitor/storage and replace it with @capacitor/preferences@${pluginVersion}`, + ); + } + } +} + +async function writeBreakingChanges() { + const breaking = [ + '@capacitor/storage', + '@capacitor/camera', + '@capacitor/push-notifications', + '@capacitor/local-notifications', + ]; + const broken = []; + for (const lib of breaking) { + if (allDependencies[lib]) { + broken.push(lib); + } + } + if (broken.length > 0) { + logger.info( + `IMPORTANT: Review https://next.capacitorjs.com/docs/updating/4-0#plugins for breaking changes in these plugins that you use: ${broken.join( + ', ', + )}.`, + ); + } + if (allDependencies['@capacitor/android']) { + logger.info( + 'Warning: The Android Gradle plugin was updated and it requires Java 11 to run. You may need to select this in Android Studio.', + ); + } +} + +async function updateAndroidManifest(filename: string) { + const txt = readFile(filename); + if (!txt) { + return; + } + + const hasAndroidExportedAlreadySet = new RegExp( + /]*(android:exported=")[^>]*)>/g, + ).test(txt); + let isAndroidExportedSetToFalse = false; + if (hasAndroidExportedAlreadySet) { + isAndroidExportedSetToFalse = new RegExp( + /]*(android:exported="false")[^>]*)>/g, + ).test(txt); + } + + // AndroidManifest.xml add attribute: tag`); + return; + } + writeFileSync(filename, replaced, 'utf-8'); +} + +async function updateBuildGradle( + filename: string, + leaveJCenter: boolean, + variablesAndClasspaths: { + 'variables': any; + 'com.android.tools.build:gradle': string; + 'com.google.gms:google-services': string; + }, +) { + // In build.gradle add dependencies: + // classpath 'com.android.tools.build:gradle:7.2.1' + // classpath 'com.google.gms:google-services:4.3.10' + const txt = readFile(filename); + if (!txt) { + return; + } + const neededDeps: { [key: string]: string } = { + 'com.android.tools.build:gradle': + variablesAndClasspaths['com.android.tools.build:gradle'], + 'com.google.gms:google-services': + variablesAndClasspaths['com.google.gms:google-services'], + }; + let replaced = txt; + + for (const dep of Object.keys(neededDeps)) { + if (!replaced.includes(`classpath '${dep}`)) { + replaced = txt.replace( + 'dependencies {', + `dependencies {\n classpath '${dep}:${neededDeps[dep]}'`, + ); + } else { + // Update + replaced = setAllStringIn( + replaced, + `classpath '${dep}:`, + `'`, + neededDeps[dep], + ); + logger.info(`Set ${dep} = ${neededDeps[dep]}.`); + } + } + + // Replace jcenter() + const lines = replaced.split('\n'); + let inRepositories = false; + let hasMavenCentral = false; + let final = ''; + for (const line of lines) { + if (line.includes('repositories {')) { + inRepositories = true; + hasMavenCentral = false; + } else if (line.trim() == '}') { + // Make sure we have mavenCentral() + if (inRepositories && !hasMavenCentral) { + final += ' mavenCentral()\n'; + logger.info(`Added mavenCentral().`); + } + inRepositories = false; + } + if (inRepositories && line.trim() === 'mavenCentral()') { + hasMavenCentral = true; + } + if (inRepositories && line.trim() === 'jcenter()' && !leaveJCenter) { + // skip jCentral() + logger.info(`Removed jcenter().`); + } else { + final += line + '\n'; + } + } + + if (txt !== final) { + writeFileSync(filename, final, 'utf-8'); + return; + } +} + +async function getAndroidVarriablesAndClasspaths(config: Config) { + const tempAndroidTemplateFolder = join( + config.cli.assetsDirAbs, + 'tempAndroidTemplate', + ); + await extractTemplate( + config.cli.assets.android.platformTemplateArchiveAbs, + tempAndroidTemplateFolder, + ); + const variablesGradleFile = readFile( + join(tempAndroidTemplateFolder, 'variables.gradle'), + ); + const buildGradleFile = readFile( + join(tempAndroidTemplateFolder, 'build.gradle'), + ); + if (!variablesGradleFile || !buildGradleFile) { + return; + } + deleteFolderRecursive(tempAndroidTemplateFolder); + + const firstIndxOfCATBGV = + buildGradleFile.indexOf(`classpath 'com.android.tools.build:gradle:`) + 42; + const firstIndxOfCGGGS = + buildGradleFile.indexOf(`com.google.gms:google-services:`) + 31; + const comAndroidToolsBuildGradleVersion = + '' + + buildGradleFile.substring( + firstIndxOfCATBGV, + buildGradleFile.indexOf("'", firstIndxOfCATBGV), + ); + const comGoogleGmsGoogleServices = + '' + + buildGradleFile.substring( + firstIndxOfCGGGS, + buildGradleFile.indexOf("'", firstIndxOfCGGGS), + ); + + const variablesGradleAsJSON = JSON.parse( + variablesGradleFile + .replace('ext ', '') + .replace(/=/g, ':') + .replace(/\n/g, ',') + .replace(/,([^:]+):/g, function (_k, p1) { + return `,"${p1}":`; + }) + .replace('{,', '{') + .replace(',}', '}') + .replace(/\s/g, '') + .replace(/'/g, '"'), + ); + + return { + 'variables': variablesGradleAsJSON, + 'com.android.tools.build:gradle': comAndroidToolsBuildGradleVersion, + 'com.google.gms:google-services': comGoogleGmsGoogleServices, + }; +} + +function readFile(filename: string): string | undefined { + try { + if (!existsSync(filename)) { + logger.error(`Unable to find ${filename}. Try updating it manually`); + return; + } + return readFileSync(filename, 'utf-8'); + } catch (err) { + logger.error( + `Unable to read ${filename}. Verify it is not already open. ${err}`, + ); + } +} + +async function updateAppBuildGradle(filename: string) { + const txt = readFile(filename); + if (!txt) { + return; + } + let replaced = txt; + if (!txt.includes('androidx.coordinatorlayout:coordinatorlayout:')) { + replaced = replaced.replace( + 'dependencies {', + 'dependencies {\n implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"', + ); + } + if (!txt.includes('androidx.core:core-splashscreen:')) { + replaced = replaced.replace( + 'dependencies {', + 'dependencies {\n implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"', + ); + } + // const lines = txt.split('\n'); + if (replaced !== txt) { + writeFileSync(filename, replaced, 'utf-8'); + } +} + +async function updateGradleWrapper(filename: string) { + const txt = readFile(filename); + if (!txt) { + return; + } + const replaced = setAllStringIn( + txt, + 'distributionUrl=', + '\n', + // eslint-disable-next-line no-useless-escape + `https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip`, + ); + writeFileSync(filename, replaced, 'utf-8'); +} + +async function updateFile( + config: Config, + filename: string, + textStart: string, + textEnd: string, + replacement?: string, + skipIfNotFound?: boolean, +): Promise { + if (config === null) { + return false; + } + const path = filename; + let txt = readFile(path); + if (!txt) { + return false; + } + if (txt.includes(textStart)) { + if (replacement) { + txt = setAllStringIn(txt, textStart, textEnd, replacement); + writeFileSync(path, txt, { encoding: 'utf-8' }); + } else { + // Replacing in code so we need to count the number of brackets to find the end of the function in swift + const lines = txt.split('\n'); + let replaced = ''; + let keep = true; + let brackets = 0; + for (const line of lines) { + if (line.includes(textStart)) { + keep = false; + } + if (!keep) { + brackets += (line.match(/{/g) || []).length; + brackets -= (line.match(/}/g) || []).length; + if (brackets == 0) { + keep = true; + } + } else { + replaced += line + '\n'; + } + } + writeFileSync(path, replaced, { encoding: 'utf-8' }); + } + return true; + } else if (!skipIfNotFound) { + logger.error( + `Unable to find "${textStart}" in ${filename}. Try updating it manually`, + ); + } + + return false; +} + +function setAllStringIn( + data: string, + start: string, + end: string, + replacement: string, +): string { + let position = 0; + let result = data; + let replaced = true; + while (replaced) { + const foundIdx = result.indexOf(start, position); + if (foundIdx == -1) { + replaced = false; + } else { + const idx = foundIdx + start.length; + position = idx + replacement.length; + result = + result.substring(0, idx) + + replacement + + result.substring(result.indexOf(end, idx)); + } + } + return result; +} + +async function replaceIfUsePush(config: Config) { + const startLine = '#if USE_PUSH'; + const endLine = '#endif'; + const filename = join(config.ios.nativeTargetDirAbs, 'AppDelegate.swift'); + const txt = readFile(filename); + if (!txt) { + return; + } + const lines = txt.split('\n'); + let startLineIndex: number | null = null; + let endLineIndex: number | null = null; + for (const [key, item] of lines.entries()) { + if (item.includes(startLine)) { + startLineIndex = key; + break; + } + } + if (startLineIndex !== null) { + for (const [key, item] of lines.entries()) { + if (item.includes(endLine) && key > startLineIndex) { + endLineIndex = key; + break; + } + } + if (endLineIndex !== null) { + lines[endLineIndex] = ''; + lines[startLineIndex] = ''; + writeFileSync(filename, lines.join('\n'), 'utf-8'); + } + } +} + +async function replacePush(filename: string) { + const txt = readFile(filename); + if (!txt) { + return; + } + let replaced = txt; + replaced = replaced.replace('DEBUG USE_PUSH', 'DEBUG'); + replaced = replaced.replace('USE_PUSH', '""'); + if (replaced != txt) { + writeFileSync(filename, replaced, 'utf-8'); + } +} + +async function removeKey(filename: string, key: string) { + const txt = readFile(filename); + if (!txt) { + return; + } + let lines = txt.split('\n'); + let removed = false; + let removing = false; + lines = lines.filter(line => { + if (removing && line.includes('')) { + removing = false; + return false; + } + if (line.includes(`${key} { + const activityChildNodes: any[] = applicationChildNode.activity; + if (!Array.isArray(activityChildNodes)) { + return false; + } + + const mainActivityNode = activityChildNodes.find(activityChildNode => { + const intentFilterChildNodes: any[] = activityChildNode['intent-filter']; + if (!Array.isArray(intentFilterChildNodes)) { + return false; + } + + return intentFilterChildNodes.find(intentFilterChildNode => { + const actionChildNodes: any[] = intentFilterChildNode.action; + if (!Array.isArray(actionChildNodes)) { + return false; + } + + const mainActionChildNode = actionChildNodes.find(actionChildNode => { + const androidName = actionChildNode.$['android:name']; + return androidName === 'android.intent.action.MAIN'; + }); + + if (!mainActionChildNode) { + return false; + } + + const categoryChildNodes: any[] = intentFilterChildNode.category; + if (!Array.isArray(categoryChildNodes)) { + return false; + } + + return categoryChildNodes.find(categoryChildNode => { + const androidName = categoryChildNode.$['android:name']; + return androidName === 'android.intent.category.LAUNCHER'; + }); + }); + }); + + if (mainActivityNode) { + mainActivityClassPath = mainActivityNode.$['android:name']; + } + + return mainActivityNode; + }); + const mainActivityClassName: any = mainActivityClassPath.split('.').pop(); + const mainActivityPathArray = mainActivityClassPath.split('.'); + mainActivityPathArray.pop(); + const mainActivityClassFileName = `${mainActivityClassName}.java`; + const mainActivityClassFilePath = join( + join(config.android.srcMainDirAbs, 'java'), + ...mainActivityPathArray, + mainActivityClassFileName, + ); + + let data = readFile(mainActivityClassFilePath); + + if (data) { + const bindex = data.indexOf('this.init(savedInstanceState'); + if (bindex == -1) return; + const eindex = data.indexOf('}});', bindex) + 4; + + data = data.replace(data.substring(bindex, eindex), ''); + + data = data.replace('// Initializes the Bridge', ''); + + writeFileSync(mainActivityClassFilePath, data); + } +} + +async function addNewSplashScreen(config: Config) { + const stylePath = join( + config.android.srcMainDirAbs, + 'res', + 'values', + 'styles.xml', + ); + let stylesXml = readFile(stylePath); + + if (!stylesXml) return; + + stylesXml = stylesXml.replace('AppTheme.NoActionBar', 'Theme.SplashScreen'); + writeFileSync(stylePath, stylesXml); +} diff --git a/cli/src/util/fs.ts b/cli/src/util/fs.ts index b5c12e4b6..dc8aeb355 100644 --- a/cli/src/util/fs.ts +++ b/cli/src/util/fs.ts @@ -1,3 +1,26 @@ +import { + existsSync, + lstatSync, + readdirSync, + rmdirSync, + unlinkSync, +} from '@ionic/utils-fs'; +import { join } from 'path'; + export const convertToUnixPath = (path: string): string => { return path.replace(/\\/g, '/'); }; + +export const deleteFolderRecursive = (directoryPath: any): void => { + if (existsSync(directoryPath)) { + readdirSync(directoryPath).forEach(file => { + const curPath = join(directoryPath, file); + if (lstatSync(curPath).isDirectory()) { + deleteFolderRecursive(curPath); + } else { + unlinkSync(curPath); + } + }); + rmdirSync(directoryPath); + } +}; From d8bf8792b57a86282f915f4a9828848fa5883af6 Mon Sep 17 00:00:00 2001 From: IT-MikeS <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 27 Jul 2022 14:39:54 -0400 Subject: [PATCH 119/179] chore: fix release script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e709645d..82fe7cf02 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "ci:publish:alpha": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid alpha --dist-tag next --force-publish --no-verify-access --yes", "ci:publish:beta": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid beta --dist-tag next --force-publish --no-verify-access --yes", "ci:publish:rc": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid rc --dist-tag next --force-publish --no-verify-access --yes", - "ci:publish:latest": "lerna publish --conventional-commits --dist-tag latest --force-publish --no-verify-access --yes", + "ci:publish:latest": "lerna publish --conventional-graduate --conventional-commits --dist-tag latest --force-publish --no-verify-access --yes", "ci:publish:dev": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid dev-$(git rev-parse --short HEAD) --dist-tag dev --force-publish --no-verify-access --no-changelog --no-git-tag-version --no-push --yes", "build:nativebridge": "lerna run build:nativebridge", "sync-peer-dependencies": "node scripts/sync-peer-dependencies.mjs", From 9b585370639ed7af80f7fe3c7b72ac63995b19fb Mon Sep 17 00:00:00 2001 From: IT-MikeS <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 27 Jul 2022 14:47:28 -0400 Subject: [PATCH 120/179] chore: patch up release script --- .github/workflows/publish-npm-latest.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-npm-latest.yml b/.github/workflows/publish-npm-latest.yml index 91b8d0d77..dc7794791 100644 --- a/.github/workflows/publish-npm-latest.yml +++ b/.github/workflows/publish-npm-latest.yml @@ -8,12 +8,13 @@ jobs: runs-on: macos-12 timeout-minutes: 30 steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + token: ${{ secrets.CAP_GH_RELEASE_TOKEN }} - uses: actions/setup-node@v1 with: node-version: 14.x - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - name: Restore Dependency Cache uses: actions/cache@v2 with: @@ -25,8 +26,8 @@ jobs: npm install - name: Version & Publish env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }} run: | git config user.name "Github Workflow (on behalf of ${{ github.actor }})" git config user.email "users.noreply.github.com" From 434b1a0ff32f513973ef35258032a4df0cb331c1 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of IT-MikeS)" Date: Wed, 27 Jul 2022 18:50:59 +0000 Subject: [PATCH 121/179] Release 4.0.0 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ android/CHANGELOG.md | 19 +++++++++++++++++++ android/package.json | 2 +- cli/CHANGELOG.md | 13 +++++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 12 ++++++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 20 ++++++++++++++++++++ ios/package.json | 2 +- lerna.json | 2 +- 10 files changed, 93 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81fe6ae6c..eae7ee72d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) + + +### Bug Fixes + +* **android:** Publish proguard-rules.pro on npm ([#5761](https://github.com/ionic-team/capacitor/issues/5761)) ([df77103](https://github.com/ionic-team/capacitor/commit/df77103ca411fa452239099769289eeeea2404d2)) +* **ios:** error data is optional ([#5782](https://github.com/ionic-team/capacitor/issues/5782)) ([da48d79](https://github.com/ionic-team/capacitor/commit/da48d798c3463de9de188ae6a6475fd6afba6091)) + + +### Features + +* **android:** Add android.minWebviewVersion configuration option ([#5768](https://github.com/ionic-team/capacitor/issues/5768)) ([ad83827](https://github.com/ionic-team/capacitor/commit/ad838279e9cd190ce6f1a020a0ac9e3916786324)) +* **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625)) +* **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264)) +* **android-template:** Use Android 12 splash API ([#5777](https://github.com/ionic-team/capacitor/issues/5777)) ([f3ab951](https://github.com/ionic-team/capacitor/commit/f3ab9519e1f08d5dfeb2db61b6939725be92b4f3)) +* **cli:** add migrator for cap3 to cap4 ([#5762](https://github.com/ionic-team/capacitor/issues/5762)) ([7cb660a](https://github.com/ionic-team/capacitor/commit/7cb660a34d9a87274761d4492d0d77c9ef44ace8)) +* **ios:** Add overrideable router var for CAPWebView. ([#5743](https://github.com/ionic-team/capacitor/issues/5743)) ([c1de1c0](https://github.com/ionic-team/capacitor/commit/c1de1c0138aad188a760118e35983d10d257f8e7)) +* **iOS:** post install script for deployment target ([#5783](https://github.com/ionic-team/capacitor/issues/5783)) ([f5afa94](https://github.com/ionic-team/capacitor/commit/f5afa94b3b9c246d87b2af03359840f503bace90)) +* Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3)) + + + + + # [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 1e7fecaae..2d2ee1c5a 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) + + +### Bug Fixes + +* **android:** Publish proguard-rules.pro on npm ([#5761](https://github.com/ionic-team/capacitor/issues/5761)) ([df77103](https://github.com/ionic-team/capacitor/commit/df77103ca411fa452239099769289eeeea2404d2)) + + +### Features + +* **android:** Add android.minWebviewVersion configuration option ([#5768](https://github.com/ionic-team/capacitor/issues/5768)) ([ad83827](https://github.com/ionic-team/capacitor/commit/ad838279e9cd190ce6f1a020a0ac9e3916786324)) +* **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625)) +* **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264)) +* Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3)) + + + + + # [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 0d8f7dba8..41611ea3e 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "4.0.0-beta.2", + "version": "4.0.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 1005e600b..057b10ec5 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) + + +### Features + +* **android:** Add android.minWebviewVersion configuration option ([#5768](https://github.com/ionic-team/capacitor/issues/5768)) ([ad83827](https://github.com/ionic-team/capacitor/commit/ad838279e9cd190ce6f1a020a0ac9e3916786324)) +* **cli:** add migrator for cap3 to cap4 ([#5762](https://github.com/ionic-team/capacitor/issues/5762)) ([7cb660a](https://github.com/ionic-team/capacitor/commit/7cb660a34d9a87274761d4492d0d77c9ef44ace8)) +* Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3)) + + + + + # [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08) **Note:** Version bump only for package @capacitor/cli diff --git a/cli/package.json b/cli/package.json index 2cb35c9d9..eac1e183a 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "4.0.0-beta.2", + "version": "4.0.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 534343965..52947edbb 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) + + +### Features + +* **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625)) +* **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264)) + + + + + # [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index 1f92231eb..d360546b0 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "4.0.0-beta.2", + "version": "4.0.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 890613a7f..0e3acd5b4 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) + + +### Bug Fixes + +* **ios:** error data is optional ([#5782](https://github.com/ionic-team/capacitor/issues/5782)) ([da48d79](https://github.com/ionic-team/capacitor/commit/da48d798c3463de9de188ae6a6475fd6afba6091)) + + +### Features + +* **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625)) +* **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264)) +* **ios:** Add overrideable router var for CAPWebView. ([#5743](https://github.com/ionic-team/capacitor/issues/5743)) ([c1de1c0](https://github.com/ionic-team/capacitor/commit/c1de1c0138aad188a760118e35983d10d257f8e7)) +* **iOS:** post install script for deployment target ([#5783](https://github.com/ionic-team/capacitor/issues/5783)) ([f5afa94](https://github.com/ionic-team/capacitor/commit/f5afa94b3b9c246d87b2af03359840f503bace90)) +* Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3)) + + + + + # [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08) diff --git a/ios/package.json b/ios/package.json index 474ee70f2..7804502f5 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "4.0.0-beta.2", + "version": "4.0.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index 59cde4942..8aed45a1c 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "4.0.0-beta.2" + "version": "4.0.0" } From 604f03a29bc500d2841987d0a0f1b20d34fba7d6 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 Jul 2022 15:48:56 +0200 Subject: [PATCH 122/179] fix(ios): publish Podfile script (#5799) --- ios/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/package.json b/ios/package.json index 7804502f5..dcd817b54 100644 --- a/ios/package.json +++ b/ios/package.json @@ -16,7 +16,8 @@ "Capacitor/Capacitor/", "CapacitorCordova/CapacitorCordova/", "Capacitor.podspec", - "CapacitorCordova.podspec" + "CapacitorCordova.podspec", + "scripts/pods_helpers.rb" ], "scripts": { "verify": "npm run xc:build:Capacitor && npm run xc:build:CapacitorCordova", From aff88a8e95e08cddda88f9c81d2ed3392257bc19 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 Jul 2022 16:01:00 +0200 Subject: [PATCH 123/179] chore(ios/android): update core peerDependency to ^4.0.0 (#5800) --- android/package.json | 2 +- ios/package.json | 2 +- scripts/sync-peer-dependencies.mjs | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/android/package.json b/android/package.json index 41611ea3e..6f3a7c1bd 100644 --- a/android/package.json +++ b/android/package.json @@ -23,7 +23,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "next" + "@capacitor/core": "^4.0.0" }, "publishConfig": { "access": "public" diff --git a/ios/package.json b/ios/package.json index dcd817b54..547b63a4e 100644 --- a/ios/package.json +++ b/ios/package.json @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "next" + "@capacitor/core": "^4.0.0" }, "publishConfig": { "access": "public" diff --git a/scripts/sync-peer-dependencies.mjs b/scripts/sync-peer-dependencies.mjs index fe68a7b06..34f9ccfd1 100644 --- a/scripts/sync-peer-dependencies.mjs +++ b/scripts/sync-peer-dependencies.mjs @@ -5,7 +5,6 @@ import { execute } from './lib/cli.mjs'; import { ls } from './lib/lerna.mjs'; import { setPackageJsonDependencies } from './lib/version.mjs'; -/* execute(async () => { const CORE_DEPENDENTS = ['@capacitor/android', '@capacitor/ios']; const pkgs = await ls(); @@ -27,4 +26,3 @@ execute(async () => { ); } }); -//*/ From 95f1029d6a2dd20e601133db489c6231e418e430 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 Jul 2022 16:18:43 +0200 Subject: [PATCH 124/179] chore(cli): Use ^4.0.0 instead of latest (#5801) --- cli/src/tasks/migrate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 69318d886..ad2ee813b 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -44,8 +44,8 @@ const plugins = [ '@capacitor/text-zoom', '@capacitor/toast', ]; -const coreVersion = 'latest'; -const pluginVersion = 'latest'; +const coreVersion = '^4.0.0'; +const pluginVersion = '^4.0.0'; export async function migrateCommand(config: Config): Promise { if (config === null) { From ca61cd9e8aa3c3559fb4260e5a35fbf312a1a482 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 Jul 2022 16:29:09 +0200 Subject: [PATCH 125/179] chore(cli): Put new variables at the end (#5802) --- cli/src/tasks/migrate.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index ad2ee813b..f8a970a6e 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -271,10 +271,10 @@ export async function migrateCommand(config: Config): Promise { ); if (file) { file = file.replace( - 'ext {', - `ext {\n ${variable} = '${variablesAndClasspaths.variables[ + '}', + ` ${variable} = '${variablesAndClasspaths.variables[ variable - ].toString()}'`, + ].toString()}'\n}`, ); writeFileSync( join(config.android.platformDirAbs, 'variables.gradle'), From ebd43efaceda16c00bcc26053d3d4a4eab2c932b Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 Jul 2022 16:51:45 +0200 Subject: [PATCH 126/179] chore(cli): Update plugins breaking changes link (#5803) --- cli/src/tasks/migrate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index f8a970a6e..3c9a3dcac 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -394,7 +394,7 @@ async function writeBreakingChanges() { } if (broken.length > 0) { logger.info( - `IMPORTANT: Review https://next.capacitorjs.com/docs/updating/4-0#plugins for breaking changes in these plugins that you use: ${broken.join( + `IMPORTANT: Review https://capacitorjs.com/docs/updating/4-0#plugins for breaking changes in these plugins that you use: ${broken.join( ', ', )}.`, ); From 25b82a84425bf09b2be45b213788b0e13982b9b3 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 Jul 2022 17:30:28 +0200 Subject: [PATCH 127/179] fix(cli): Correct Splash theme update (#5805) --- cli/src/tasks/migrate.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 3c9a3dcac..dd504fcfd 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -898,6 +898,9 @@ async function addNewSplashScreen(config: Config) { if (!stylesXml) return; - stylesXml = stylesXml.replace('AppTheme.NoActionBar', 'Theme.SplashScreen'); + stylesXml = stylesXml.replace( + `parent="AppTheme.NoActionBar"`, + `parent="Theme.SplashScreen"`, + ); writeFileSync(stylePath, stylesXml); } From 471feedc07bef357ac798fcba664bd373e9f8ebf Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 Jul 2022 18:00:14 +0200 Subject: [PATCH 128/179] fix(cli): Revert some splash migration errors (#5806) --- cli/src/tasks/migrate.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index dd504fcfd..82d753f34 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -902,5 +902,16 @@ async function addNewSplashScreen(config: Config) { `parent="AppTheme.NoActionBar"`, `parent="Theme.SplashScreen"`, ); + + // revert wrong replaces + stylesXml = stylesXml.replace( + `name="Theme.SplashScreen"`, + `name="AppTheme.NoActionBar"`, + ); + stylesXml = stylesXml.replace( + `name="Theme.SplashScreenLaunch"`, + `name="AppTheme.NoActionBarLaunch"`, + ); + writeFileSync(stylePath, stylesXml); } From 95b6bedacc587e19de584608a367dc728a604d6d Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 28 Jul 2022 19:14:11 +0200 Subject: [PATCH 129/179] chore(cli): Replace Dark theme with DayNight theme (#5807) --- cli/src/tasks/migrate.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 82d753f34..4cc2bca86 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -295,9 +295,12 @@ export async function migrateCommand(config: Config): Promise { rimraf.sync(join(config.android.appDirAbs, 'build')); // add new splashscreen - await runTask('Migrate to Android 12 Splashscreen.', () => { - return addNewSplashScreen(config); - }); + await runTask( + 'Migrate to Android 12 Splashscreen and apply DayNight theme.', + () => { + return addNewSplashScreen(config); + }, + ); } // Run Cap Sync @@ -913,5 +916,11 @@ async function addNewSplashScreen(config: Config) { `name="AppTheme.NoActionBarLaunch"`, ); + // Apply DayNight theme + stylesXml = stylesXml.replace( + `parent="Theme.AppCompat.NoActionBar"`, + `parent="Theme.AppCompat.DayNight.NoActionBar"`, + ); + writeFileSync(stylePath, stylesXml); } From a14824b660ccd39b7ed150a26debb15e9a161303 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of IT-MikeS)" Date: Thu, 28 Jul 2022 18:29:48 +0000 Subject: [PATCH 130/179] Release 4.0.1 --- CHANGELOG.md | 13 +++++++++++++ android/CHANGELOG.md | 8 ++++++++ android/package.json | 2 +- cli/CHANGELOG.md | 12 ++++++++++++ cli/package.json | 2 +- ios/CHANGELOG.md | 11 +++++++++++ ios/package.json | 2 +- lerna.json | 2 +- 8 files changed, 48 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eae7ee72d..4dfc495ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) + + +### Bug Fixes + +* **cli:** Correct Splash theme update ([#5805](https://github.com/ionic-team/capacitor/issues/5805)) ([25b82a8](https://github.com/ionic-team/capacitor/commit/25b82a84425bf09b2be45b213788b0e13982b9b3)) +* **cli:** Revert some splash migration errors ([#5806](https://github.com/ionic-team/capacitor/issues/5806)) ([471feed](https://github.com/ionic-team/capacitor/commit/471feedc07bef357ac798fcba664bd373e9f8ebf)) +* **ios:** publish Podfile script ([#5799](https://github.com/ionic-team/capacitor/issues/5799)) ([604f03a](https://github.com/ionic-team/capacitor/commit/604f03a29bc500d2841987d0a0f1b20d34fba7d6)) + + + + + # [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 2d2ee1c5a..15104a785 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) + +**Note:** Version bump only for package @capacitor/android + + + + + # [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) diff --git a/android/package.json b/android/package.json index 6f3a7c1bd..c9030b1ca 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "4.0.0", + "version": "4.0.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 057b10ec5..5dc4c3a3e 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) + + +### Bug Fixes + +* **cli:** Correct Splash theme update ([#5805](https://github.com/ionic-team/capacitor/issues/5805)) ([25b82a8](https://github.com/ionic-team/capacitor/commit/25b82a84425bf09b2be45b213788b0e13982b9b3)) +* **cli:** Revert some splash migration errors ([#5806](https://github.com/ionic-team/capacitor/issues/5806)) ([471feed](https://github.com/ionic-team/capacitor/commit/471feedc07bef357ac798fcba664bd373e9f8ebf)) + + + + + # [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) diff --git a/cli/package.json b/cli/package.json index eac1e183a..ffa88bcbd 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "4.0.0", + "version": "4.0.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 0e3acd5b4..93e56a956 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) + + +### Bug Fixes + +* **ios:** publish Podfile script ([#5799](https://github.com/ionic-team/capacitor/issues/5799)) ([604f03a](https://github.com/ionic-team/capacitor/commit/604f03a29bc500d2841987d0a0f1b20d34fba7d6)) + + + + + # [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) diff --git a/ios/package.json b/ios/package.json index 547b63a4e..0ed23c1eb 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "4.0.0", + "version": "4.0.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/lerna.json b/lerna.json index 8aed45a1c..eb4e9d164 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "4.0.0" + "version": "4.0.1" } From 77175b0d696757e8528df124e27f25b388d18ab6 Mon Sep 17 00:00:00 2001 From: IT-MikeS <20338451+IT-MikeS@users.noreply.github.com> Date: Thu, 28 Jul 2022 15:29:14 -0400 Subject: [PATCH 131/179] chore: bump core to 4.0.1 --- core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/package.json b/core/package.json index d360546b0..0fe0848fb 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "4.0.0", + "version": "4.0.1", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", From d744cbb1be647c7979d05128b63e6eeb820f464c Mon Sep 17 00:00:00 2001 From: IT-MikeS <20338451+IT-MikeS@users.noreply.github.com> Date: Thu, 28 Jul 2022 15:32:21 -0400 Subject: [PATCH 132/179] chore: update core changelog --- core/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 52947edbb..cd93d9a8e 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) + +**Note:** Version bump only for package @capacitor/core + + + + + # [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27) From 5a2b050a28f91b34911ed2c96c613518fa814df1 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 1 Aug 2022 12:15:41 +0200 Subject: [PATCH 133/179] chore(cli): make migrator use gradle 7.4.2-all (#5810) --- cli/src/tasks/migrate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 4cc2bca86..287e7d9fe 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -631,7 +631,7 @@ async function updateGradleWrapper(filename: string) { 'distributionUrl=', '\n', // eslint-disable-next-line no-useless-escape - `https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip`, + `https\\://services.gradle.org/distributions/gradle-7.4.2-all.zip`, ); writeFileSync(filename, replaced, 'utf-8'); } From 59e64b7c548341c27a8477ddc867290592c43815 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 3 Aug 2022 18:48:33 +0200 Subject: [PATCH 134/179] fix: update @types/tar to prevent core build failure (#5822) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 82fe7cf02..79ee598de 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@ionic/eslint-config": "^0.3.0", "@ionic/prettier-config": "~1.0.1", "@ionic/swiftlint-config": "^1.1.2", - "@types/tar": "^4.0.4", + "@types/tar": "^6.1.2", "eslint": "~7.27.0", "lerna": "^3.22.1", "prettier": "~2.3.0", From 66954ef6bc93f2038d85a386ef2f8b582af11bc3 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 3 Aug 2022 19:06:03 +0200 Subject: [PATCH 135/179] fix(ios): Prevent Xcode 14 warning on CAPWebView (#5821) --- ios/Capacitor/Capacitor/CAPWebView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Capacitor/Capacitor/CAPWebView.swift b/ios/Capacitor/Capacitor/CAPWebView.swift index 553670c13..a7cac0bf1 100644 --- a/ios/Capacitor/Capacitor/CAPWebView.swift +++ b/ios/Capacitor/Capacitor/CAPWebView.swift @@ -123,7 +123,7 @@ open class CAPWebView: UIView { extension CAPWebView { - open func webViewConfiguration(for instanceConfiguration: InstanceConfiguration) -> WKWebViewConfiguration { + public func webViewConfiguration(for instanceConfiguration: InstanceConfiguration) -> WKWebViewConfiguration { let webViewConfiguration = WKWebViewConfiguration() webViewConfiguration.allowsInlineMediaPlayback = true webViewConfiguration.suppressesIncrementalRendering = false From 48ff9e6461e8037a5c6da87c90efc6bc872d7f08 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 4 Aug 2022 17:10:49 +0200 Subject: [PATCH 136/179] fix(cli): Don't add google-services plugin if missing (#5825) --- cli/src/tasks/migrate.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 287e7d9fe..9af9a9902 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -460,7 +460,7 @@ async function updateBuildGradle( ) { // In build.gradle add dependencies: // classpath 'com.android.tools.build:gradle:7.2.1' - // classpath 'com.google.gms:google-services:4.3.10' + // classpath 'com.google.gms:google-services:4.3.13' const txt = readFile(filename); if (!txt) { return; @@ -474,13 +474,7 @@ async function updateBuildGradle( let replaced = txt; for (const dep of Object.keys(neededDeps)) { - if (!replaced.includes(`classpath '${dep}`)) { - replaced = txt.replace( - 'dependencies {', - `dependencies {\n classpath '${dep}:${neededDeps[dep]}'`, - ); - } else { - // Update + if (replaced.includes(`classpath '${dep}`)) { replaced = setAllStringIn( replaced, `classpath '${dep}:`, From 7184097da88ed34f3e754119f967d262aa5e2add Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 5 Aug 2022 14:05:27 +0200 Subject: [PATCH 137/179] fix(cli): remove double space in cap 2 variables file (#5826) --- cli/src/tasks/migrate.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 9af9a9902..ec949098b 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -244,13 +244,23 @@ export async function migrateCommand(config: Config): Promise { // Variables gradle await runTask(`Migrating variables.gradle file.`, () => { return (async (): Promise => { + const variablesPath = join( + config.android.platformDirAbs, + 'variables.gradle', + ); + let txt = readFile(variablesPath); + if (!txt) { + return; + } + txt = txt.replace(/= {2}'/g, `= '`); + writeFileSync(variablesPath, txt, { encoding: 'utf-8' }); for (const variable of Object.keys( variablesAndClasspaths.variables, )) { if ( !(await updateFile( config, - join(config.android.platformDirAbs, 'variables.gradle'), + variablesPath, `${variable} = '`, `'`, variablesAndClasspaths.variables[variable].toString(), @@ -259,16 +269,14 @@ export async function migrateCommand(config: Config): Promise { ) { const didWork = await updateFile( config, - join(config.android.platformDirAbs, 'variables.gradle'), + variablesPath, `${variable} = `, `\n`, variablesAndClasspaths.variables[variable].toString(), true, ); if (!didWork) { - let file = readFile( - join(config.android.platformDirAbs, 'variables.gradle'), - ); + let file = readFile(variablesPath); if (file) { file = file.replace( '}', @@ -276,10 +284,7 @@ export async function migrateCommand(config: Config): Promise { variable ].toString()}'\n}`, ); - writeFileSync( - join(config.android.platformDirAbs, 'variables.gradle'), - file, - ); + writeFileSync(variablesPath, file); } } } From b9d5954ca0b333f2caa20179b96b049379860ea5 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Sat, 6 Aug 2022 08:01:33 +0200 Subject: [PATCH 138/179] fix(cli): Also update preferences plugin if present (#5831) --- cli/src/tasks/migrate.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index ec949098b..d71c2176e 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -36,6 +36,7 @@ const plugins = [ '@capacitor/local-notifications', '@capacitor/motion', '@capacitor/network', + '@capacitor/preferences', '@capacitor/push-notifications', '@capacitor/screen-reader', '@capacitor/share', From 7ce6dd4b6fb5cdc395add6f656fbedc785178ae3 Mon Sep 17 00:00:00 2001 From: Joey Pender Date: Fri, 12 Aug 2022 09:24:34 -0500 Subject: [PATCH 139/179] feat(cli): Option to inline JS source maps during sync (#5843) --- cli/src/index.ts | 9 ++++-- cli/src/tasks/add.ts | 2 +- cli/src/tasks/run.ts | 2 +- cli/src/tasks/sourcemaps.ts | 55 +++++++++++++++++++++++++++++++++++++ cli/src/tasks/sync.ts | 8 +++++- 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 cli/src/tasks/sourcemaps.ts diff --git a/cli/src/index.ts b/cli/src/index.ts index c6fe92409..f3c4eaf87 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -83,12 +83,17 @@ export function runProgram(config: Config): void { '--deployment', "Optional: if provided, Podfile.lock won't be deleted and pod install will use --deployment option", ) + .option( + '--inline', + 'Optional: if true, all source maps will be inlined for easier debugging on mobile devices', + false, + ) .action( wrapAction( - telemetryAction(config, async (platform, { deployment }) => { + telemetryAction(config, async (platform, { deployment, inline }) => { checkExternalConfig(config.app); const { syncCommand } = await import('./tasks/sync'); - await syncCommand(config, platform, deployment); + await syncCommand(config, platform, deployment, inline); }), ), ); diff --git a/cli/src/tasks/add.ts b/cli/src/tasks/add.ts index e027eb528..6094221c1 100644 --- a/cli/src/tasks/add.ts +++ b/cli/src/tasks/add.ts @@ -104,7 +104,7 @@ export async function addCommand( await editPlatforms(config, platformName); if (await pathExists(config.app.webDirAbs)) { - await sync(config, platformName, false); + await sync(config, platformName, false, false); if (platformName === config.android.name) { await runTask('Syncing Gradle', async () => { return createLocalProperties(config.android.platformDirAbs); diff --git a/cli/src/tasks/run.ts b/cli/src/tasks/run.ts index 6644e9368..17f2ae60a 100644 --- a/cli/src/tasks/run.ts +++ b/cli/src/tasks/run.ts @@ -80,7 +80,7 @@ export async function runCommand( try { if (options.sync) { - await sync(config, platformName, false); + await sync(config, platformName, false, true); } await run(config, platformName, options); diff --git a/cli/src/tasks/sourcemaps.ts b/cli/src/tasks/sourcemaps.ts new file mode 100644 index 000000000..54b79fe5d --- /dev/null +++ b/cli/src/tasks/sourcemaps.ts @@ -0,0 +1,55 @@ +import { + readdirSync, + existsSync, + readFileSync, + writeFileSync, + unlinkSync, + lstatSync, +} from '@ionic/utils-fs'; +import { join, extname } from 'path'; + +import type { Config } from '../definitions'; +import { logger } from '../log'; + +function walkDirectory(dirPath: string) { + const files = readdirSync(dirPath); + files.forEach(file => { + const targetFile = join(dirPath, file); + if (existsSync(targetFile) && lstatSync(targetFile).isDirectory()) { + walkDirectory(targetFile); + } else { + const mapFile = join(dirPath, `${file}.map`); + if (extname(file) === '.js' && existsSync(mapFile)) { + const bufMap = readFileSync(mapFile).toString('base64'); + const bufFile = readFileSync(targetFile, 'utf8'); + const result = bufFile.replace( + `sourceMappingURL=${file}.map`, + 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + + bufMap, + ); + writeFileSync(targetFile, result); + unlinkSync(mapFile); + } + } + }); +} + +export async function inlineSourceMaps( + config: Config, + platformName: string, +): Promise { + let buildDir = ''; + + if (platformName == config.ios.name) { + buildDir = await config.ios.webDirAbs; + } + + if (platformName == config.android.name) { + buildDir = await config.android.webDirAbs; + } + + if (buildDir) { + logger.info('Inlining sourcemaps'); + walkDirectory(buildDir); + } +} diff --git a/cli/src/tasks/sync.ts b/cli/src/tasks/sync.ts index 9ce3cd9cb..8af82c2af 100644 --- a/cli/src/tasks/sync.ts +++ b/cli/src/tasks/sync.ts @@ -12,6 +12,7 @@ import { logger } from '../log'; import { allSerial } from '../util/promise'; import { copy, copyCommand } from './copy'; +import { inlineSourceMaps } from './sourcemaps'; import { update, updateChecks, updateCommand } from './update'; /** @@ -21,6 +22,7 @@ export async function syncCommand( config: Config, selectedPlatformName: string, deployment: boolean, + inline: boolean, ): Promise { if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) { try { @@ -40,7 +42,7 @@ export async function syncCommand( ]); await allSerial( platforms.map( - platformName => () => sync(config, platformName, deployment), + platformName => () => sync(config, platformName, deployment, inline), ), ); const now = +new Date(); @@ -60,6 +62,7 @@ export async function sync( config: Config, platformName: string, deployment: boolean, + inline: boolean, ): Promise { await runPlatformHook( config, @@ -70,6 +73,9 @@ export async function sync( try { await copy(config, platformName); + if (inline) { + await inlineSourceMaps(config, platformName); + } } catch (e) { logger.error(e.stack ?? e); } From ea2d95ba43467cd2d4c4637aacab6bf655d9c596 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 15 Aug 2022 12:43:04 +0200 Subject: [PATCH 140/179] fix(cli): Support of BoM dependencies on cordova plugins (#5827) --- cli/src/android/update.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cli/src/android/update.ts b/cli/src/android/update.ts index aa9599024..2a1b17889 100644 --- a/cli/src/android/update.ts +++ b/cli/src/android/update.ts @@ -239,7 +239,11 @@ project(':${getGradlePackageName( `apply from: "${relativePluginPath}/${framework.$.src}"`, ); } else if (!framework.$.type && !framework.$.custom) { - frameworksArray.push(` implementation "${framework.$.src}"`); + if (framework.$.src.startsWith('platform(')) { + frameworksArray.push(` implementation ${framework.$.src}`); + } else { + frameworksArray.push(` implementation "${framework.$.src}"`); + } } }); prefsArray = prefsArray.concat(getAllElements(p, platform, 'preference')); @@ -324,7 +328,11 @@ export async function handleCordovaPluginsGradle( }); let frameworkString = frameworksArray .map(f => { - return ` implementation "${f}"`; + if (f.startsWith('platform(')) { + return ` implementation ${f}`; + } else { + return ` implementation "${f}"`; + } }) .join('\n'); frameworkString = await replaceFrameworkVariables( From 8cd3373133903f97a836fd6ac6b7ce4e1ba9317e Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 15 Aug 2022 13:05:23 +0200 Subject: [PATCH 141/179] fix(cli): Migrator put registerPlugin before super.onCreate (#5828) --- cli/src/tasks/migrate.ts | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index d71c2176e..871515dc8 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -294,8 +294,8 @@ export async function migrateCommand(config: Config): Promise { }); // remove init - await runTask('Migrating MainActivity by removing init().', () => { - return removeOldInitAndroid(config); + await runTask('Migrating MainActivity', () => { + return migrateMainActivity(config); }); rimraf.sync(join(config.android.appDirAbs, 'build')); @@ -813,7 +813,7 @@ async function podfileAssertDeploymentTarget(filename: string) { writeFileSync(filename, replaced, 'utf-8'); } -async function removeOldInitAndroid(config: Config) { +async function migrateMainActivity(config: Config) { const xmlData = await readXML( join(config.android.srcMainDirAbs, 'AndroidManifest.xml'), ); @@ -879,12 +879,35 @@ async function removeOldInitAndroid(config: Config) { if (data) { const bindex = data.indexOf('this.init(savedInstanceState'); - if (bindex == -1) return; - const eindex = data.indexOf('}});', bindex) + 4; + if (bindex !== -1) { + const eindex = data.indexOf('}});', bindex) + 4; - data = data.replace(data.substring(bindex, eindex), ''); + data = data.replace(data.substring(bindex, eindex), ''); - data = data.replace('// Initializes the Bridge', ''); + data = data.replace('// Initializes the Bridge', ''); + } + + const rindex = data.indexOf('registerPlugin'); + if (rindex !== -1) { + if (data.indexOf('super.onCreate(savedInstanceState);') < rindex) { + data = data.replace( + 'super.onCreate(savedInstanceState);\n ', + '', + ); + const eindex = data.lastIndexOf('.class);') + 8; + data = data.replace( + data.substring(bindex, eindex), + `${data.substring( + bindex, + eindex, + )}\n super.onCreate(savedInstanceState);`, + ); + } + } + + if (bindex == -1 && rindex == -1) { + return; + } writeFileSync(mainActivityClassFilePath, data); } From 0ad41d0567e69d31e65c92d4890fc8860d0344ac Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Mon, 15 Aug 2022 15:07:58 -0400 Subject: [PATCH 142/179] chore: enable nightly releases (#5859) --- .github/workflows/publish-npm-nightly.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish-npm-nightly.yml b/.github/workflows/publish-npm-nightly.yml index 0782a5c0d..2c90226e7 100644 --- a/.github/workflows/publish-npm-nightly.yml +++ b/.github/workflows/publish-npm-nightly.yml @@ -1,8 +1,8 @@ name: Publish NPM Nightly -on: workflow_dispatch -# schedule: -# - cron: '0 15 * * 1-5' +on: + schedule: + - cron: '0 15 * * 1-5' jobs: deploy-npm-nightly: @@ -10,12 +10,13 @@ jobs: runs-on: macos-12 timeout-minutes: 30 steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + token: ${{ secrets.CAP_GH_RELEASE_TOKEN }} - uses: actions/setup-node@v1 with: node-version: 14.x - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - name: Restore Dependency Cache uses: actions/cache@v2 with: @@ -27,7 +28,7 @@ jobs: npm install - name: Version & Publish env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: | git config user.name "Github Workflow (on behalf of ${{ github.actor }})" From 474ad1f4d4a9ea0636a457836c938dac9f6534e8 Mon Sep 17 00:00:00 2001 From: Tim Zook Date: Wed, 17 Aug 2022 06:04:09 -0500 Subject: [PATCH 143/179] fix(cli): prevent error on migrate when devDependencies is missing (#5863) --- cli/src/tasks/migrate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 871515dc8..ac1085b09 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -339,7 +339,7 @@ async function installLatestNPMLibs(runInstall: boolean, config: Config) { } const pkgJson: any = JSON.parse(pkgJsonFile); - for (const devDepKey of Object.keys(pkgJson['devDependencies'])) { + for (const devDepKey of Object.keys(pkgJson['devDependencies'] || {})) { if (libs.includes(devDepKey)) { pkgJson['devDependencies'][devDepKey] = coreVersion; } else if (plugins.includes(devDepKey)) { From a1543edde2f3bb6f9fa270acd3377fad0e120b28 Mon Sep 17 00:00:00 2001 From: Ionitron Date: Wed, 17 Aug 2022 07:21:19 -0400 Subject: [PATCH 144/179] chore(readme): add new contributor (#5867) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e069dd94c..66ae54cd7 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Made possible by the Capacitor community. 💖

+ From c70025644c77b1ab42e2fa55feb842124fe57ea3 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 17 Aug 2022 13:35:17 +0200 Subject: [PATCH 145/179] chore(readme): remove undefined contributor (#5868) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 66ae54cd7..1d9039ca8 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,6 @@ Made possible by the Capacitor community. 💖

- From 76f28e70a5c0a03e4c6b9a93a0c068666a2c38ff Mon Sep 17 00:00:00 2001 From: Steven Sherry Date: Wed, 17 Aug 2022 09:30:09 -0500 Subject: [PATCH 146/179] feat(ios): Add `setServerBasePath(_:)` to CAPBridgeProtocol (#5860) --- ios/Capacitor/Capacitor/CAPBridgeProtocol.swift | 1 + .../Capacitor/CAPBridgeViewController.swift | 14 ++++---------- ios/Capacitor/Capacitor/CapacitorBridge.swift | 7 +++++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift index c7e59fc69..82bb626dc 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeProtocol.swift @@ -70,6 +70,7 @@ import WebKit // MARK: - Paths, Files, Assets func localURL(fromWebURL webURL: URL?) -> URL? func portablePath(fromLocalURL localURL: URL?) -> URL? + func setServerBasePath(_ path: String) // MARK: - View Presentation func showAlertWith(title: String, message: String, buttonTitle: String) diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index d7abcbc4e..a8aa1d9b0 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -263,16 +263,10 @@ extension CAPBridgeViewController { } @objc public func setServerBasePath(path: String) { - let url = URL(fileURLWithPath: path, isDirectory: true) - guard let capBridge = capacitorBridge, FileManager.default.fileExists(atPath: url.path) else { - return - } - capBridge.config = capBridge.config.updatingAppLocation(url) - capBridge.webViewAssetHandler.setAssetPath(url.path) - if let url = capacitorBridge?.config.serverURL { - DispatchQueue.main.async { [weak self] in - _ = self?.webView?.load(URLRequest(url: url)) - } + guard let capBridge = capacitorBridge else { return } + capBridge.setServerBasePath(path) + DispatchQueue.main.async { [weak self] in + _ = self?.webView?.load(URLRequest(url: capBridge.config.serverURL)) } } } diff --git a/ios/Capacitor/Capacitor/CapacitorBridge.swift b/ios/Capacitor/Capacitor/CapacitorBridge.swift index 54faf2df5..78bbd6a8a 100644 --- a/ios/Capacitor/Capacitor/CapacitorBridge.swift +++ b/ios/Capacitor/Capacitor/CapacitorBridge.swift @@ -156,6 +156,13 @@ internal class CapacitorBridge: NSObject, CAPBridgeProtocol { statusBarAnimation = animation } + public func setServerBasePath(_ path: String) { + let url = URL(fileURLWithPath: path, isDirectory: true) + guard FileManager.default.fileExists(atPath: url.path) else { return } + config = config.updatingAppLocation(url) + webViewAssetHandler.setAssetPath(url.path) + } + // MARK: - Static Methods /** From 478d48c3e322cffc6f0ff7ce590b635de4b41279 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 17 Aug 2022 18:07:26 +0200 Subject: [PATCH 147/179] fix(cli): make migrator also update plugin variables (#5871) --- cli/src/tasks/migrate.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index ac1085b09..65dedb9c4 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -290,6 +290,23 @@ export async function migrateCommand(config: Config): Promise { } } } + const pluginVariables: { [key: string]: string } = { + firebaseMessagingVersion: '23.0.5', + playServicesLocationVersion: '20.0.0', + androidxBrowserVersion: '1.4.0', + androidxMaterialVersion: '1.6.1', + androidxExifInterfaceVersion: '1.3.3', + }; + for (const variable of Object.keys(pluginVariables)) { + await updateFile( + config, + variablesPath, + `${variable} = '`, + `'`, + pluginVariables[variable], + true, + ); + } })(); }); From 325b6fe83939efaaef44c7e8624e33de742a57e2 Mon Sep 17 00:00:00 2001 From: Arnold Plakolli <8707442+arnold-plakolli@users.noreply.github.com> Date: Thu, 18 Aug 2022 17:10:36 +0200 Subject: [PATCH 148/179] fix(ios): return proper mimeType on M1 x86_64 simulators (#5853) * Fixed issue #5793 * add TODO * fmt * Disable type_body_length rule Accommodating the big MIME list Co-authored-by: jcesarmobile Co-authored-by: giralte-ionic <97970732+giralte-ionic@users.noreply.github.com> --- .../Capacitor/WebViewAssetHandler.swift | 353 ++++++++++++++++++ 1 file changed, 353 insertions(+) diff --git a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift index 3dc013dc5..6dce404ef 100644 --- a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift @@ -2,6 +2,7 @@ import Foundation import MobileCoreServices @objc(CAPWebViewAssetHandler) +// swiftlint:disable type_body_length internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { private var router: Router @@ -90,6 +91,10 @@ internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { return mimetype as String } } + // TODO: Remove in the future if Apple fixes the issue + if let mimeType = mimeTypes[pathExtension] { + return mimeType + } return "application/octet-stream" } return "text/html" @@ -103,4 +108,352 @@ internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { } return false } + + let mimeTypes = [ + "aaf": "application/octet-stream", + "aca": "application/octet-stream", + "accdb": "application/msaccess", + "accde": "application/msaccess", + "accdt": "application/msaccess", + "acx": "application/internet-property-stream", + "afm": "application/octet-stream", + "ai": "application/postscript", + "aif": "audio/x-aiff", + "aifc": "audio/aiff", + "aiff": "audio/aiff", + "application": "application/x-ms-application", + "art": "image/x-jg", + "asd": "application/octet-stream", + "asf": "video/x-ms-asf", + "asi": "application/octet-stream", + "asm": "text/plain", + "asr": "video/x-ms-asf", + "asx": "video/x-ms-asf", + "atom": "application/atom+xml", + "au": "audio/basic", + "avi": "video/x-msvideo", + "axs": "application/olescript", + "bas": "text/plain", + "bcpio": "application/x-bcpio", + "bin": "application/octet-stream", + "bmp": "image/bmp", + "c": "text/plain", + "cab": "application/octet-stream", + "calx": "application/vnd.ms-office.calx", + "cat": "application/vnd.ms-pki.seccat", + "cdf": "application/x-cdf", + "chm": "application/octet-stream", + "class": "application/x-java-applet", + "clp": "application/x-msclip", + "cmx": "image/x-cmx", + "cnf": "text/plain", + "cod": "image/cis-cod", + "cpio": "application/x-cpio", + "cpp": "text/plain", + "crd": "application/x-mscardfile", + "crl": "application/pkix-crl", + "crt": "application/x-x509-ca-cert", + "csh": "application/x-csh", + "css": "text/css", + "csv": "application/octet-stream", + "cur": "application/octet-stream", + "dcr": "application/x-director", + "deploy": "application/octet-stream", + "der": "application/x-x509-ca-cert", + "dib": "image/bmp", + "dir": "application/x-director", + "disco": "text/xml", + "dll": "application/x-msdownload", + "dll.config": "text/xml", + "dlm": "text/dlm", + "doc": "application/msword", + "docm": "application/vnd.ms-word.document.macroEnabled.12", + "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "dot": "application/msword", + "dotm": "application/vnd.ms-word.template.macroEnabled.12", + "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", + "dsp": "application/octet-stream", + "dtd": "text/xml", + "dvi": "application/x-dvi", + "dwf": "drawing/x-dwf", + "dwp": "application/octet-stream", + "dxr": "application/x-director", + "eml": "message/rfc822", + "emz": "application/octet-stream", + "eot": "application/octet-stream", + "eps": "application/postscript", + "etx": "text/x-setext", + "evy": "application/envoy", + "exe": "application/octet-stream", + "exe.config": "text/xml", + "fdf": "application/vnd.fdf", + "fif": "application/fractals", + "fla": "application/octet-stream", + "flr": "x-world/x-vrml", + "flv": "video/x-flv", + "gif": "image/gif", + "gtar": "application/x-gtar", + "gz": "application/x-gzip", + "h": "text/plain", + "hdf": "application/x-hdf", + "hdml": "text/x-hdml", + "hhc": "application/x-oleobject", + "hhk": "application/octet-stream", + "hhp": "application/octet-stream", + "hlp": "application/winhlp", + "hqx": "application/mac-binhex40", + "hta": "application/hta", + "htc": "text/x-component", + "htm": "text/html", + "html": "text/html", + "htt": "text/webviewhtml", + "hxt": "text/html", + "ico": "image/x-icon", + "ics": "application/octet-stream", + "ief": "image/ief", + "iii": "application/x-iphone", + "inf": "application/octet-stream", + "ins": "application/x-internet-signup", + "isp": "application/x-internet-signup", + "IVF": "video/x-ivf", + "jar": "application/java-archive", + "java": "application/octet-stream", + "jck": "application/liquidmotion", + "jcz": "application/liquidmotion", + "jfif": "image/pjpeg", + "jpb": "application/octet-stream", + "jpe": "image/jpeg", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "js": "application/x-javascript", + "jsx": "text/jscript", + "latex": "application/x-latex", + "lit": "application/x-ms-reader", + "lpk": "application/octet-stream", + "lsf": "video/x-la-asf", + "lsx": "video/x-la-asf", + "lzh": "application/octet-stream", + "m13": "application/x-msmediaview", + "m14": "application/x-msmediaview", + "m1v": "video/mpeg", + "m3u": "audio/x-mpegurl", + "man": "application/x-troff-man", + "manifest": "application/x-ms-manifest", + "map": "text/plain", + "mdb": "application/x-msaccess", + "mdp": "application/octet-stream", + "me": "application/x-troff-me", + "mht": "message/rfc822", + "mhtml": "message/rfc822", + "mid": "audio/mid", + "midi": "audio/mid", + "mix": "application/octet-stream", + "mmf": "application/x-smaf", + "mno": "text/xml", + "mny": "application/x-msmoney", + "mov": "video/quicktime", + "movie": "video/x-sgi-movie", + "mp2": "video/mpeg", + "mp3": "audio/mpeg", + "mpa": "video/mpeg", + "mpe": "video/mpeg", + "mpeg": "video/mpeg", + "mpg": "video/mpeg", + "mpp": "application/vnd.ms-project", + "mpv2": "video/mpeg", + "ms": "application/x-troff-ms", + "msi": "application/octet-stream", + "mso": "application/octet-stream", + "mvb": "application/x-msmediaview", + "mvc": "application/x-miva-compiled", + "nc": "application/x-netcdf", + "nsc": "video/x-ms-asf", + "nws": "message/rfc822", + "ocx": "application/octet-stream", + "oda": "application/oda", + "odc": "text/x-ms-odc", + "ods": "application/oleobject", + "one": "application/onenote", + "onea": "application/onenote", + "onetoc": "application/onenote", + "onetoc2": "application/onenote", + "onetmp": "application/onenote", + "onepkg": "application/onenote", + "osdx": "application/opensearchdescription+xml", + "p10": "application/pkcs10", + "p12": "application/x-pkcs12", + "p7b": "application/x-pkcs7-certificates", + "p7c": "application/pkcs7-mime", + "p7m": "application/pkcs7-mime", + "p7r": "application/x-pkcs7-certreqresp", + "p7s": "application/pkcs7-signature", + "pbm": "image/x-portable-bitmap", + "pcx": "application/octet-stream", + "pcz": "application/octet-stream", + "pdf": "application/pdf", + "pfb": "application/octet-stream", + "pfm": "application/octet-stream", + "pfx": "application/x-pkcs12", + "pgm": "image/x-portable-graymap", + "pko": "application/vnd.ms-pki.pko", + "pma": "application/x-perfmon", + "pmc": "application/x-perfmon", + "pml": "application/x-perfmon", + "pmr": "application/x-perfmon", + "pmw": "application/x-perfmon", + "png": "image/png", + "pnm": "image/x-portable-anymap", + "pnz": "image/png", + "pot": "application/vnd.ms-powerpoint", + "potm": "application/vnd.ms-powerpoint.template.macroEnabled.12", + "potx": "application/vnd.openxmlformats-officedocument.presentationml.template", + "ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12", + "ppm": "image/x-portable-pixmap", + "pps": "application/vnd.ms-powerpoint", + "ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", + "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", + "ppt": "application/vnd.ms-powerpoint", + "pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12", + "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "prf": "application/pics-rules", + "prm": "application/octet-stream", + "prx": "application/octet-stream", + "ps": "application/postscript", + "psd": "application/octet-stream", + "psm": "application/octet-stream", + "psp": "application/octet-stream", + "pub": "application/x-mspublisher", + "qt": "video/quicktime", + "qtl": "application/x-quicktimeplayer", + "qxd": "application/octet-stream", + "ra": "audio/x-pn-realaudio", + "ram": "audio/x-pn-realaudio", + "rar": "application/octet-stream", + "ras": "image/x-cmu-raster", + "rf": "image/vnd.rn-realflash", + "rgb": "image/x-rgb", + "rm": "application/vnd.rn-realmedia", + "rmi": "audio/mid", + "roff": "application/x-troff", + "rpm": "audio/x-pn-realaudio-plugin", + "rtf": "application/rtf", + "rtx": "text/richtext", + "scd": "application/x-msschedule", + "sct": "text/scriptlet", + "sea": "application/octet-stream", + "setpay": "application/set-payment-initiation", + "setreg": "application/set-registration-initiation", + "sgml": "text/sgml", + "sh": "application/x-sh", + "shar": "application/x-shar", + "sit": "application/x-stuffit", + "sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12", + "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide", + "smd": "audio/x-smd", + "smi": "application/octet-stream", + "smx": "audio/x-smd", + "smz": "audio/x-smd", + "snd": "audio/basic", + "snp": "application/octet-stream", + "spc": "application/x-pkcs7-certificates", + "spl": "application/futuresplash", + "src": "application/x-wais-source", + "ssm": "application/streamingmedia", + "sst": "application/vnd.ms-pki.certstore", + "stl": "application/vnd.ms-pki.stl", + "sv4cpio": "application/x-sv4cpio", + "sv4crc": "application/x-sv4crc", + "svg": "image/svg+xml", + "swf": "application/x-shockwave-flash", + "t": "application/x-troff", + "tar": "application/x-tar", + "tcl": "application/x-tcl", + "tex": "application/x-tex", + "texi": "application/x-texinfo", + "texinfo": "application/x-texinfo", + "tgz": "application/x-compressed", + "thmx": "application/vnd.ms-officetheme", + "thn": "application/octet-stream", + "tif": "image/tiff", + "tiff": "image/tiff", + "toc": "application/octet-stream", + "tr": "application/x-troff", + "trm": "application/x-msterminal", + "tsv": "text/tab-separated-values", + "ttf": "application/octet-stream", + "txt": "text/plain", + "u32": "application/octet-stream", + "uls": "text/iuls", + "ustar": "application/x-ustar", + "vbs": "text/vbscript", + "vcf": "text/x-vcard", + "vcs": "text/plain", + "vdx": "application/vnd.ms-visio.viewer", + "vml": "text/xml", + "vsd": "application/vnd.visio", + "vss": "application/vnd.visio", + "vst": "application/vnd.visio", + "vsto": "application/x-ms-vsto", + "vsw": "application/vnd.visio", + "vsx": "application/vnd.visio", + "vtx": "application/vnd.visio", + "wav": "audio/wav", + "wax": "audio/x-ms-wax", + "wbmp": "image/vnd.wap.wbmp", + "wcm": "application/vnd.ms-works", + "wdb": "application/vnd.ms-works", + "wks": "application/vnd.ms-works", + "wm": "video/x-ms-wm", + "wma": "audio/x-ms-wma", + "wmd": "application/x-ms-wmd", + "wmf": "application/x-msmetafile", + "wml": "text/vnd.wap.wml", + "wmlc": "application/vnd.wap.wmlc", + "wmls": "text/vnd.wap.wmlscript", + "wmlsc": "application/vnd.wap.wmlscriptc", + "wmp": "video/x-ms-wmp", + "wmv": "video/x-ms-wmv", + "wmx": "video/x-ms-wmx", + "wmz": "application/x-ms-wmz", + "wps": "application/vnd.ms-works", + "wri": "application/x-mswrite", + "wrl": "x-world/x-vrml", + "wrz": "x-world/x-vrml", + "wsdl": "text/xml", + "wvx": "video/x-ms-wvx", + "x": "application/directx", + "xaf": "x-world/x-vrml", + "xaml": "application/xaml+xml", + "xap": "application/x-silverlight-app", + "xbap": "application/x-ms-xbap", + "xbm": "image/x-xbitmap", + "xdr": "text/plain", + "xht": "application/xhtml+xml", + "xhtml": "application/xhtml+xml", + "xla": "application/vnd.ms-excel", + "xlam": "application/vnd.ms-excel.addin.macroEnabled.12", + "xlc": "application/vnd.ms-excel", + "xlm": "application/vnd.ms-excel", + "xls": "application/vnd.ms-excel", + "xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12", + "xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12", + "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "xlt": "application/vnd.ms-excel", + "xltm": "application/vnd.ms-excel.template.macroEnabled.12", + "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template", + "xlw": "application/vnd.ms-excel", + "xml": "text/xml", + "xof": "x-world/x-vrml", + "xpm": "image/x-xpixmap", + "xps": "application/vnd.ms-xpsdocument", + "xsd": "text/xml", + "xsf": "text/xml", + "xsl": "text/xml", + "xslt": "text/xml", + "xsn": "application/octet-stream", + "xtp": "application/octet-stream", + "xwd": "image/x-xwindowdump", + "z": "application/x-compress", + "zip": "application/x-zip-compressed" + ] } From 0b5c2b740e9613512a100051b8822fd1361f4aeb Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of giralte-ionic)" Date: Thu, 18 Aug 2022 15:16:47 +0000 Subject: [PATCH 149/179] Release 4.1.0 --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ cli/CHANGELOG.md | 22 ++++++++++++++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 8 ++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 17 +++++++++++++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 8 files changed, 78 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dfc495ed..68de7d3ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,32 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) + + +### Bug Fixes + +* **cli:** Also update preferences plugin if present ([#5831](https://github.com/ionic-team/capacitor/issues/5831)) ([b9d5954](https://github.com/ionic-team/capacitor/commit/b9d5954ca0b333f2caa20179b96b049379860ea5)) +* **cli:** Don't add google-services plugin if missing ([#5825](https://github.com/ionic-team/capacitor/issues/5825)) ([48ff9e6](https://github.com/ionic-team/capacitor/commit/48ff9e6461e8037a5c6da87c90efc6bc872d7f08)) +* **cli:** make migrator also update plugin variables ([#5871](https://github.com/ionic-team/capacitor/issues/5871)) ([478d48c](https://github.com/ionic-team/capacitor/commit/478d48c3e322cffc6f0ff7ce590b635de4b41279)) +* **cli:** Migrator put registerPlugin before super.onCreate ([#5828](https://github.com/ionic-team/capacitor/issues/5828)) ([8cd3373](https://github.com/ionic-team/capacitor/commit/8cd3373133903f97a836fd6ac6b7ce4e1ba9317e)) +* **cli:** prevent error on migrate when devDependencies is missing ([#5863](https://github.com/ionic-team/capacitor/issues/5863)) ([474ad1f](https://github.com/ionic-team/capacitor/commit/474ad1f4d4a9ea0636a457836c938dac9f6534e8)) +* **cli:** remove double space in cap 2 variables file ([#5826](https://github.com/ionic-team/capacitor/issues/5826)) ([7184097](https://github.com/ionic-team/capacitor/commit/7184097da88ed34f3e754119f967d262aa5e2add)) +* **cli:** Support of BoM dependencies on cordova plugins ([#5827](https://github.com/ionic-team/capacitor/issues/5827)) ([ea2d95b](https://github.com/ionic-team/capacitor/commit/ea2d95ba43467cd2d4c4637aacab6bf655d9c596)) +* **ios:** Prevent Xcode 14 warning on CAPWebView ([#5821](https://github.com/ionic-team/capacitor/issues/5821)) ([66954ef](https://github.com/ionic-team/capacitor/commit/66954ef6bc93f2038d85a386ef2f8b582af11bc3)) +* **ios:** return proper mimeType on M1 x86_64 simulators ([#5853](https://github.com/ionic-team/capacitor/issues/5853)) ([325b6fe](https://github.com/ionic-team/capacitor/commit/325b6fe83939efaaef44c7e8624e33de742a57e2)), closes [#5793](https://github.com/ionic-team/capacitor/issues/5793) +* update @types/tar to prevent core build failure ([#5822](https://github.com/ionic-team/capacitor/issues/5822)) ([59e64b7](https://github.com/ionic-team/capacitor/commit/59e64b7c548341c27a8477ddc867290592c43815)) + + +### Features + +* **cli:** Option to inline JS source maps during sync ([#5843](https://github.com/ionic-team/capacitor/issues/5843)) ([7ce6dd4](https://github.com/ionic-team/capacitor/commit/7ce6dd4b6fb5cdc395add6f656fbedc785178ae3)) +* **ios:** Add `setServerBasePath(_:)` to CAPBridgeProtocol ([#5860](https://github.com/ionic-team/capacitor/issues/5860)) ([76f28e7](https://github.com/ionic-team/capacitor/commit/76f28e70a5c0a03e4c6b9a93a0c068666a2c38ff)) + + + + + ## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 5dc4c3a3e..5c0a8dcf1 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) + + +### Bug Fixes + +* **cli:** Also update preferences plugin if present ([#5831](https://github.com/ionic-team/capacitor/issues/5831)) ([b9d5954](https://github.com/ionic-team/capacitor/commit/b9d5954ca0b333f2caa20179b96b049379860ea5)) +* **cli:** Don't add google-services plugin if missing ([#5825](https://github.com/ionic-team/capacitor/issues/5825)) ([48ff9e6](https://github.com/ionic-team/capacitor/commit/48ff9e6461e8037a5c6da87c90efc6bc872d7f08)) +* **cli:** make migrator also update plugin variables ([#5871](https://github.com/ionic-team/capacitor/issues/5871)) ([478d48c](https://github.com/ionic-team/capacitor/commit/478d48c3e322cffc6f0ff7ce590b635de4b41279)) +* **cli:** Migrator put registerPlugin before super.onCreate ([#5828](https://github.com/ionic-team/capacitor/issues/5828)) ([8cd3373](https://github.com/ionic-team/capacitor/commit/8cd3373133903f97a836fd6ac6b7ce4e1ba9317e)) +* **cli:** prevent error on migrate when devDependencies is missing ([#5863](https://github.com/ionic-team/capacitor/issues/5863)) ([474ad1f](https://github.com/ionic-team/capacitor/commit/474ad1f4d4a9ea0636a457836c938dac9f6534e8)) +* **cli:** remove double space in cap 2 variables file ([#5826](https://github.com/ionic-team/capacitor/issues/5826)) ([7184097](https://github.com/ionic-team/capacitor/commit/7184097da88ed34f3e754119f967d262aa5e2add)) +* **cli:** Support of BoM dependencies on cordova plugins ([#5827](https://github.com/ionic-team/capacitor/issues/5827)) ([ea2d95b](https://github.com/ionic-team/capacitor/commit/ea2d95ba43467cd2d4c4637aacab6bf655d9c596)) + + +### Features + +* **cli:** Option to inline JS source maps during sync ([#5843](https://github.com/ionic-team/capacitor/issues/5843)) ([7ce6dd4](https://github.com/ionic-team/capacitor/commit/7ce6dd4b6fb5cdc395add6f656fbedc785178ae3)) + + + + + ## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) diff --git a/cli/package.json b/cli/package.json index ffa88bcbd..bb52d5bdd 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "4.0.1", + "version": "4.1.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index cd93d9a8e..1dee93234 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) + +**Note:** Version bump only for package @capacitor/core + + + + + ## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index 0fe0848fb..25a2dbda2 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "4.0.1", + "version": "4.1.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 93e56a956..2b646a59f 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) + + +### Bug Fixes + +* **ios:** Prevent Xcode 14 warning on CAPWebView ([#5821](https://github.com/ionic-team/capacitor/issues/5821)) ([66954ef](https://github.com/ionic-team/capacitor/commit/66954ef6bc93f2038d85a386ef2f8b582af11bc3)) +* **ios:** return proper mimeType on M1 x86_64 simulators ([#5853](https://github.com/ionic-team/capacitor/issues/5853)) ([325b6fe](https://github.com/ionic-team/capacitor/commit/325b6fe83939efaaef44c7e8624e33de742a57e2)), closes [#5793](https://github.com/ionic-team/capacitor/issues/5793) + + +### Features + +* **ios:** Add `setServerBasePath(_:)` to CAPBridgeProtocol ([#5860](https://github.com/ionic-team/capacitor/issues/5860)) ([76f28e7](https://github.com/ionic-team/capacitor/commit/76f28e70a5c0a03e4c6b9a93a0c068666a2c38ff)) + + + + + ## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) diff --git a/ios/package.json b/ios/package.json index 0ed23c1eb..3e6ec288d 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "4.0.1", + "version": "4.1.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^4.0.0" + "@capacitor/core": "^4.1.0" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index eb4e9d164..e07a0efaf 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "4.0.1" + "version": "4.1.0" } From 26d1d7d15280a7b1e76f621a6fdc9085d4407cb5 Mon Sep 17 00:00:00 2001 From: IT-MikeS <20338451+IT-MikeS@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:22:57 -0400 Subject: [PATCH 150/179] chore: bump android to 4.1.0 --- android/CHANGELOG.md | 8 ++++++++ android/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 15104a785..864276059 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) + +**Note:** Version bump only for package @capacitor/android + + + + + ## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index c9030b1ca..73f5466b9 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "4.0.1", + "version": "4.1.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", From fb51378e19ebfb97f5525baba7dacf9bdc048bb0 Mon Sep 17 00:00:00 2001 From: Ionitron Date: Thu, 18 Aug 2022 13:37:50 -0400 Subject: [PATCH 151/179] chore(readme): add new contributor (#5875) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1d9039ca8..99a86e06a 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Made possible by the Capacitor community. 💖

+ From 29ccfcabe76efb04c5812a384192ce0b2fd0ea91 Mon Sep 17 00:00:00 2001 From: IT-MikeS <20338451+IT-MikeS@users.noreply.github.com> Date: Thu, 18 Aug 2022 14:13:09 -0400 Subject: [PATCH 152/179] chore: fix publish-latest workflow skipping pkgs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79ee598de..f01f5a687 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "ci:publish:alpha": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid alpha --dist-tag next --force-publish --no-verify-access --yes", "ci:publish:beta": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid beta --dist-tag next --force-publish --no-verify-access --yes", "ci:publish:rc": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid rc --dist-tag next --force-publish --no-verify-access --yes", - "ci:publish:latest": "lerna publish --conventional-graduate --conventional-commits --dist-tag latest --force-publish --no-verify-access --yes", + "ci:publish:latest": "lerna publish --conventional-commits --dist-tag latest --force-publish --no-verify-access --yes", "ci:publish:dev": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid dev-$(git rev-parse --short HEAD) --dist-tag dev --force-publish --no-verify-access --no-changelog --no-git-tag-version --no-push --yes", "build:nativebridge": "lerna run build:nativebridge", "sync-peer-dependencies": "node scripts/sync-peer-dependencies.mjs", From 22c2fb505ee80dfce335c05f841ffafd21b7842c Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 19 Aug 2022 13:34:57 +0200 Subject: [PATCH 153/179] chore(cli): Change CocoaPods install link (#5876) --- cli/src/ios/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/ios/common.ts b/cli/src/ios/common.ts index 55d9a2cab..6cd050a33 100644 --- a/cli/src/ios/common.ts +++ b/cli/src/ios/common.ts @@ -19,7 +19,7 @@ export async function checkCocoaPods(config: Config): Promise { return ( `CocoaPods is not installed.\n` + `See this install guide: ${c.strong( - 'https://guides.cocoapods.org/using/getting-started.html#installation', + 'https://capacitorjs.com/docs/getting-started/environment-setup#homebrew', )}` ); } From 9658fe15213a375e2107e122357da3fd259ef9a2 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 30 Aug 2022 10:54:19 +0200 Subject: [PATCH 154/179] chore: format license files properly (#5887) --- LICENSE | 36 +++++++++++++++++------------------- android/LICENSE | 36 +++++++++++++++++------------------- cli/LICENSE | 36 +++++++++++++++++------------------- core/LICENSE | 36 +++++++++++++++++------------------- ios/LICENSE | 36 +++++++++++++++++------------------- 5 files changed, 85 insertions(+), 95 deletions(-) diff --git a/LICENSE b/LICENSE index 623c70a83..c3e903bdd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,23 +1,21 @@ -Copyright 2015-present Drifty Co. -http://drifty.com/ - MIT License -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Copyright (c) 2017-present Drifty Co. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/android/LICENSE b/android/LICENSE index 941061474..c3e903bdd 100644 --- a/android/LICENSE +++ b/android/LICENSE @@ -1,23 +1,21 @@ -Copyright 2017-present Ionic -https://ionic.io - MIT License -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Copyright (c) 2017-present Drifty Co. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cli/LICENSE b/cli/LICENSE index 941061474..c3e903bdd 100644 --- a/cli/LICENSE +++ b/cli/LICENSE @@ -1,23 +1,21 @@ -Copyright 2017-present Ionic -https://ionic.io - MIT License -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Copyright (c) 2017-present Drifty Co. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/core/LICENSE b/core/LICENSE index 941061474..c3e903bdd 100644 --- a/core/LICENSE +++ b/core/LICENSE @@ -1,23 +1,21 @@ -Copyright 2017-present Ionic -https://ionic.io - MIT License -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Copyright (c) 2017-present Drifty Co. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ios/LICENSE b/ios/LICENSE index 941061474..c3e903bdd 100644 --- a/ios/LICENSE +++ b/ios/LICENSE @@ -1,23 +1,21 @@ -Copyright 2017-present Ionic -https://ionic.io - MIT License -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Copyright (c) 2017-present Drifty Co. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 0f17177b1c64c0f69f86e990e4e150b820da497b Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Wed, 7 Sep 2022 09:52:48 -0500 Subject: [PATCH 155/179] feat(cli): copy signature when using secure live updates (#5896) --- cli/src/declarations.ts | 8 ++++++++ cli/src/tasks/copy.ts | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index 63b69d1d4..eddc22c9a 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -523,6 +523,7 @@ export interface LiveUpdateConfig { channel: string; autoUpdateMethod: AutoUpdateMethod; maxVersions?: number; + key?: string; } export type AutoUpdateMethod = 'none' | 'background'; @@ -548,4 +549,11 @@ export interface PluginsConfig { shell: Portal; apps: Portal[]; }; + + /** + * Capacitor Live Updates plugin configuration + * + * @since 4.2.0 + */ + LiveUpdates?: LiveUpdateConfig; } diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 2cde358c8..9e7782726 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -82,6 +82,14 @@ export async function copy( usesCapacitorPortals = true; } + let usesLiveUpdates = false; + if ( + allPlugins.filter(plugin => plugin.id === '@capacitor/live-updates') + .length > 0 + ) { + usesLiveUpdates = true; + } + if (platformName === config.ios.name) { if (usesCapacitorPortals) { await copyFederatedWebDirs(config, await config.ios.webDirAbs); @@ -92,6 +100,9 @@ export async function copy( config.app.webDirAbs, ); } + if (usesLiveUpdates) { + await copySecureLiveUpdatesKey(config, config.ios.nativeTargetDirAbs); + } await copyCapacitorConfig(config, config.ios.nativeTargetDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName); @@ -105,6 +116,9 @@ export async function copy( config.app.webDirAbs, ); } + if (usesLiveUpdates) { + await copySecureLiveUpdatesKey(config, config.android.assetsDirAbs); + } await copyCapacitorConfig(config, config.android.assetsDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); await handleCordovaPluginsJS(cordovaPlugins, config, platformName); @@ -207,3 +221,34 @@ function isPortal(config: any): config is Portal { (config as Portal).name !== undefined ); } + +async function copySecureLiveUpdatesKey(config: Config, nativeAbsDir: string) { + if (!config.app.extConfig?.plugins?.LiveUpdates?.key) { + return; + } + + const secureLiveUpdatesKeyFile = config.app.extConfig.plugins.LiveUpdates.key; + const keyAbsFromPath = join(config.app.rootDir, secureLiveUpdatesKeyFile); + const keyAbsToPath = join(nativeAbsDir, basename(keyAbsFromPath)); + const keyRelToDir = relative(config.app.rootDir, nativeAbsDir); + + if (!(await pathExists(keyAbsFromPath))) { + logger.warn( + `Cannot copy Secure Live Updates signature file from ${c.strong( + keyAbsFromPath, + )} to ${keyRelToDir}\n` + + `Signature file does not exist at specified key path.`, + ); + + return; + } + + await runTask( + `Copying Secure Live Updates key from ${c.strong( + secureLiveUpdatesKeyFile, + )} to ${keyRelToDir}`, + async () => { + return fsCopy(keyAbsFromPath, keyAbsToPath); + }, + ); +} From e4c143d4da653533570215964808c2f32f5469d3 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 7 Sep 2022 11:03:51 -0400 Subject: [PATCH 156/179] feat(cli): add scheme and flavor options to run command (#5873) * feat: add scheme and flavor options to run command * chore: no lodash merge/clone --- cli/src/android/run.ts | 6 ++++-- cli/src/index.ts | 19 +++++++++++++++---- cli/src/ios/run.ts | 8 +++++--- cli/src/tasks/run.ts | 2 ++ 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/cli/src/android/run.ts b/cli/src/android/run.ts index e8910331f..324c96ae4 100644 --- a/cli/src/android/run.ts +++ b/cli/src/android/run.ts @@ -12,14 +12,16 @@ const debug = Debug('capacitor:android:run'); export async function runAndroid( config: Config, - { target: selectedTarget }: RunCommandOptions, + { target: selectedTarget, flavor: selectedFlavor }: RunCommandOptions, ): Promise { const target = await promptForPlatformTarget( await getPlatformTargets('android'), selectedTarget, ); - const arg = `assemble${config.android?.flavor || ''}Debug`; + const runFlavor = selectedFlavor || config.android?.flavor || ''; + + const arg = `assemble${runFlavor}Debug`; const gradleArgs = [arg]; debug('Invoking ./gradlew with args: %O', gradleArgs); diff --git a/cli/src/index.ts b/cli/src/index.ts index f3c4eaf87..4d3c8c485 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -137,6 +137,8 @@ export function runProgram(config: Config): void { .description( `runs ${c.input('sync')}, then builds and deploys the native app`, ) + .option('--scheme ', 'set the scheme of the iOS project') + .option('--flavor ', 'set the flavor of the Android project') .option('--list', 'list targets, then quit') // TODO: remove once --json is a hidden option (https://github.com/tj/commander.js/issues/1106) .allowUnknownOption(true) @@ -144,10 +146,19 @@ export function runProgram(config: Config): void { .option('--no-sync', `do not run ${c.input('sync')}`) .action( wrapAction( - telemetryAction(config, async (platform, { list, target, sync }) => { - const { runCommand } = await import('./tasks/run'); - await runCommand(config, platform, { list, target, sync }); - }), + telemetryAction( + config, + async (platform, { scheme, flavor, list, target, sync }) => { + const { runCommand } = await import('./tasks/run'); + await runCommand(config, platform, { + scheme, + flavor, + list, + target, + sync, + }); + }, + ), ), ); diff --git a/cli/src/ios/run.ts b/cli/src/ios/run.ts index 4347c562f..4f3d8393f 100644 --- a/cli/src/ios/run.ts +++ b/cli/src/ios/run.ts @@ -12,13 +12,15 @@ const debug = Debug('capacitor:ios:run'); export async function runIOS( config: Config, - { target: selectedTarget }: RunCommandOptions, + { target: selectedTarget, scheme: selectedScheme }: RunCommandOptions, ): Promise { const target = await promptForPlatformTarget( await getPlatformTargets('ios'), selectedTarget, ); + const runScheme = selectedScheme || config.ios.scheme; + const derivedDataPath = resolve( config.ios.platformDirAbs, 'DerivedData', @@ -29,7 +31,7 @@ export async function runIOS( '-workspace', basename(await config.ios.nativeXcodeWorkspaceDirAbs), '-scheme', - config.ios.scheme, + runScheme, '-configuration', 'Debug', '-destination', @@ -46,7 +48,7 @@ export async function runIOS( }), ); - const appName = `${config.ios.scheme}.app`; + const appName = `${runScheme}.app`; const appPath = resolve( derivedDataPath, 'Build/Products', diff --git a/cli/src/tasks/run.ts b/cli/src/tasks/run.ts index 17f2ae60a..89ecc0e9a 100644 --- a/cli/src/tasks/run.ts +++ b/cli/src/tasks/run.ts @@ -19,6 +19,8 @@ import { getPlatformTargets } from '../util/native-run'; import { sync } from './sync'; export interface RunCommandOptions { + scheme?: string; + flavor?: string; list?: boolean; target?: string; sync?: boolean; From d84352d89bce5c2896f8f4397bfa1cd773054d07 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 7 Sep 2022 12:19:43 -0400 Subject: [PATCH 157/179] chore: use date instead of git rev nightly & dev (#5902) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f01f5a687..1cdd7a329 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,12 @@ "name": "capacitor", "private": true, "scripts": { - "ci:publish:nightly": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid nightly-$(git rev-parse --short HEAD) --dist-tag nightly --force-publish --no-verify-access --no-changelog --no-git-tag-version --no-push --yes", + "ci:publish:nightly": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid nightly-$(date +\"%Y%m%dT%H%M%S\") --dist-tag nightly --force-publish --no-verify-access --no-changelog --no-git-tag-version --no-push --yes", "ci:publish:alpha": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid alpha --dist-tag next --force-publish --no-verify-access --yes", "ci:publish:beta": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid beta --dist-tag next --force-publish --no-verify-access --yes", "ci:publish:rc": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid rc --dist-tag next --force-publish --no-verify-access --yes", "ci:publish:latest": "lerna publish --conventional-commits --dist-tag latest --force-publish --no-verify-access --yes", - "ci:publish:dev": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid dev-$(git rev-parse --short HEAD) --dist-tag dev --force-publish --no-verify-access --no-changelog --no-git-tag-version --no-push --yes", + "ci:publish:dev": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid dev-$(date +\"%Y%m%dT%H%M%S\") --dist-tag dev --force-publish --no-verify-access --no-changelog --no-git-tag-version --no-push --yes", "build:nativebridge": "lerna run build:nativebridge", "sync-peer-dependencies": "node scripts/sync-peer-dependencies.mjs", "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint", From 17fbabb2a77d1b356d24048efc5883bd4d049104 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 7 Sep 2022 15:16:23 -0400 Subject: [PATCH 158/179] feat(cli): add inline option to copy command (#5901) * feat(cli): add inline option to copy command * chore: fix update default passed to copy * chore: make inlines optional with false default --- cli/src/index.ts | 9 +++++++-- cli/src/tasks/copy.ts | 9 ++++++++- cli/src/tasks/sync.ts | 12 ++++-------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/cli/src/index.ts b/cli/src/index.ts index 4d3c8c485..acb6e135f 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -122,12 +122,17 @@ export function runProgram(config: Config): void { program .command('copy [platform]') .description('copies the web app build into the native app') + .option( + '--inline', + 'Optional: if true, all source maps will be inlined for easier debugging on mobile devices', + false, + ) .action( wrapAction( - telemetryAction(config, async platform => { + telemetryAction(config, async (platform, { inline }) => { checkExternalConfig(config.app); const { copyCommand } = await import('./tasks/copy'); - await copyCommand(config, platform); + await copyCommand(config, platform, inline); }), ), ); diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 9e7782726..63151887f 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -23,9 +23,12 @@ import { getPlugins } from '../plugin'; import { allSerial } from '../util/promise'; import { copyWeb } from '../web/copy'; +import { inlineSourceMaps } from './sourcemaps'; + export async function copyCommand( config: Config, selectedPlatformName: string, + inline = false, ): Promise { if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) { const platformDir = resolvePlatform(config, selectedPlatformName); @@ -43,7 +46,7 @@ export async function copyCommand( const platforms = await selectPlatforms(config, selectedPlatformName); try { await allSerial( - platforms.map(platformName => () => copy(config, platformName)), + platforms.map(platformName => () => copy(config, platformName, inline)), ); } catch (e) { if (isFatal(e)) { @@ -58,6 +61,7 @@ export async function copyCommand( export async function copy( config: Config, platformName: string, + inline = false, ): Promise { await runTask(c.success(c.strong(`copy ${platformName}`)), async () => { const result = await checkWebDir(config); @@ -134,6 +138,9 @@ export async function copy( } else { throw `Platform ${platformName} is not valid.`; } + if (inline) { + await inlineSourceMaps(config, platformName); + } }); await runPlatformHook( diff --git a/cli/src/tasks/sync.ts b/cli/src/tasks/sync.ts index 8af82c2af..050347323 100644 --- a/cli/src/tasks/sync.ts +++ b/cli/src/tasks/sync.ts @@ -12,7 +12,6 @@ import { logger } from '../log'; import { allSerial } from '../util/promise'; import { copy, copyCommand } from './copy'; -import { inlineSourceMaps } from './sourcemaps'; import { update, updateChecks, updateCommand } from './update'; /** @@ -22,11 +21,11 @@ export async function syncCommand( config: Config, selectedPlatformName: string, deployment: boolean, - inline: boolean, + inline = false, ): Promise { if (selectedPlatformName && !(await isValidPlatform(selectedPlatformName))) { try { - await copyCommand(config, selectedPlatformName); + await copyCommand(config, selectedPlatformName, inline); } catch (e) { logger.error(e.stack ?? e); } @@ -62,7 +61,7 @@ export async function sync( config: Config, platformName: string, deployment: boolean, - inline: boolean, + inline = false, ): Promise { await runPlatformHook( config, @@ -72,10 +71,7 @@ export async function sync( ); try { - await copy(config, platformName); - if (inline) { - await inlineSourceMaps(config, platformName); - } + await copy(config, platformName, inline); } catch (e) { logger.error(e.stack ?? e); } From 101a5681ab37147de8c702b2048ae3185b28a5b2 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of IT-MikeS)" Date: Thu, 8 Sep 2022 20:33:04 +0000 Subject: [PATCH 159/179] Release 4.2.0 --- CHANGELOG.md | 13 +++++++++++++ android/CHANGELOG.md | 8 ++++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 13 +++++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 8 ++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 8 ++++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 57 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68de7d3ac..ebe39d1bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) + + +### Features + +* **cli:** add inline option to copy command ([#5901](https://github.com/ionic-team/capacitor/issues/5901)) ([17fbabb](https://github.com/ionic-team/capacitor/commit/17fbabb2a77d1b356d24048efc5883bd4d049104)) +* **cli:** add scheme and flavor options to run command ([#5873](https://github.com/ionic-team/capacitor/issues/5873)) ([e4c143d](https://github.com/ionic-team/capacitor/commit/e4c143d4da653533570215964808c2f32f5469d3)) +* **cli:** copy signature when using secure live updates ([#5896](https://github.com/ionic-team/capacitor/issues/5896)) ([0f17177](https://github.com/ionic-team/capacitor/commit/0f17177b1c64c0f69f86e990e4e150b820da497b)) + + + + + # [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 864276059..3dc84a025 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) + +**Note:** Version bump only for package @capacitor/android + + + + + ## [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 73f5466b9..95a629276 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "4.1.0", + "version": "4.2.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -23,7 +23,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^4.0.0" + "@capacitor/core": "^4.2.0" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 5c0a8dcf1..872e097d1 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) + + +### Features + +* **cli:** add inline option to copy command ([#5901](https://github.com/ionic-team/capacitor/issues/5901)) ([17fbabb](https://github.com/ionic-team/capacitor/commit/17fbabb2a77d1b356d24048efc5883bd4d049104)) +* **cli:** add scheme and flavor options to run command ([#5873](https://github.com/ionic-team/capacitor/issues/5873)) ([e4c143d](https://github.com/ionic-team/capacitor/commit/e4c143d4da653533570215964808c2f32f5469d3)) +* **cli:** copy signature when using secure live updates ([#5896](https://github.com/ionic-team/capacitor/issues/5896)) ([0f17177](https://github.com/ionic-team/capacitor/commit/0f17177b1c64c0f69f86e990e4e150b820da497b)) + + + + + # [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) diff --git a/cli/package.json b/cli/package.json index bb52d5bdd..0741c0bb4 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "4.1.0", + "version": "4.2.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 1dee93234..7d070d2c6 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) + +**Note:** Version bump only for package @capacitor/core + + + + + # [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index 25a2dbda2..b1e5d4c83 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "4.1.0", + "version": "4.2.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 2b646a59f..e8161d110 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) + +**Note:** Version bump only for package @capacitor/ios + + + + + # [4.1.0](https://github.com/ionic-team/capacitor/compare/4.0.1...4.1.0) (2022-08-18) diff --git a/ios/package.json b/ios/package.json index 3e6ec288d..7a93d364e 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "4.1.0", + "version": "4.2.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^4.1.0" + "@capacitor/core": "^4.2.0" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index e07a0efaf..5f2fde156 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "4.1.0" + "version": "4.2.0" } From d92467ff9230644890fa3deb81344980a50a8eda Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 12 Sep 2022 17:58:07 +0200 Subject: [PATCH 160/179] chore(cli): Document iOS schemes limitations (#5905) --- cli/src/declarations.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index eddc22c9a..39894087b 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -398,6 +398,7 @@ export interface CapacitorConfig { /** * Configure the local scheme on iOS. * + * [Can't be set to schemes that the WKWebView already handles, such as http or https](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler) * This can be useful when migrating from * [`cordova-plugin-ionic-webview`](https://github.com/ionic-team/cordova-plugin-ionic-webview), * where the default scheme on iOS is `ionic`. From c41d28f8cc829c6bf69d776280c9f1fdba9f866f Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 13 Sep 2022 17:07:02 +0200 Subject: [PATCH 161/179] fix(cli): Make update from windows use proper paths on Podfile (#5906) --- cli/src/ios/update.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cli/src/ios/update.ts b/cli/src/ios/update.ts index 74deabc58..c64958098 100644 --- a/cli/src/ios/update.ts +++ b/cli/src/ios/update.ts @@ -164,9 +164,8 @@ async function generatePodFile( return ''; } - return ` pod '${p.ios.name}', :path => '${relative( - podfilePath, - await realpath(p.rootPath), + return ` pod '${p.ios.name}', :path => '${convertToUnixPath( + relative(podfilePath, await realpath(p.rootPath)), )}'\n`; }), ); From aa60a75d9c2c784e127a4d89e4079b412fbe7262 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Tue, 13 Sep 2022 17:24:40 +0200 Subject: [PATCH 162/179] fix(cli): show error if npm install on migration failed (#5904) --- cli/src/tasks/migrate.ts | 45 ++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 65dedb9c4..68d4b67fa 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -2,12 +2,13 @@ import { writeFileSync, readFileSync, existsSync } from '@ionic/utils-fs'; import { join } from 'path'; import rimraf from 'rimraf'; +import c from '../colors'; import { runTask } from '../common'; import type { Config } from '../definitions'; import { fatal } from '../errors'; import { logger, logPrompt, logSuccess } from '../log'; import { deleteFolderRecursive } from '../util/fs'; -import { getCommandOutput } from '../util/subprocess'; +import { runCommand, getCommandOutput } from '../util/subprocess'; import { extractTemplate } from '../util/template'; import { readXML } from '../util/xml'; @@ -105,16 +106,32 @@ export async function migrateCommand(config: Config): Promise { typeof npmInstallConfirm === 'string' && npmInstallConfirm.toLowerCase() === 'y'; - await runTask(`Installing Latest NPM Modules.`, () => { - return installLatestNPMLibs(runNpmInstall, config); - }); + try { + await runTask(`Installing Latest NPM Modules.`, () => { + return installLatestNPMLibs(runNpmInstall, config); + }); + } catch (ex) { + logger.error( + `npm install failed. Try deleting node_modules folder and running ${c.input( + 'npm install --force', + )} manually.`, + ); + } - await runTask( - `Migrating @capacitor/storage to @capacitor/preferences.`, - () => { - return migrateStoragePluginToPreferences(runNpmInstall); - }, - ); + try { + await runTask( + `Migrating @capacitor/storage to @capacitor/preferences.`, + () => { + return migrateStoragePluginToPreferences(runNpmInstall); + }, + ); + } catch (ex) { + logger.error( + `@capacitor/preferences failed to install. Try deleting node_modules folder and running ${c.input( + 'npm install @capacitor/preferences --force', + )} manually.`, + ); + } if ( allDependencies['@capacitor/ios'] && @@ -376,9 +393,8 @@ async function installLatestNPMLibs(runInstall: boolean, config: Config) { }); if (runInstall) { - rimraf.sync(join(config.app.rootDir, 'package-lock.json')); rimraf.sync(join(config.app.rootDir, 'node_modules/@capacitor/!(cli)')); - await getCommandOutput('npm', ['i']); + await runCommand('npm', ['i']); } else { logger.info( `Please run an install command with your package manager of choice. (ex: yarn install)`, @@ -393,10 +409,7 @@ async function migrateStoragePluginToPreferences(runInstall: boolean) { ); if (runInstall) { await getCommandOutput('npm', ['uninstall', '@capacitor/storage']); - await getCommandOutput('npm', [ - 'i', - `@capacitor/preferences@${pluginVersion}`, - ]); + await runCommand('npm', ['i', `@capacitor/preferences@${pluginVersion}`]); } else { logger.info( `Please manually uninstall @capacitor/storage and replace it with @capacitor/preferences@${pluginVersion}`, From 5ef6a3889121dd39a9159ff80250df18854bc557 Mon Sep 17 00:00:00 2001 From: Tim Lancina <804331+tlancina@users.noreply.github.com> Date: Tue, 13 Sep 2022 12:45:02 -0500 Subject: [PATCH 163/179] fix(android): set all cookies on proxied requests (#5781) --- .../main/java/com/getcapacitor/WebViewLocalServer.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java index c9def872e..097e25af4 100755 --- a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java +++ b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java @@ -29,6 +29,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -360,9 +361,12 @@ private WebResourceResponse handleProxyRequest(WebResourceRequest request, PathH String base64 = Base64.encodeToString(userInfoBytes, Base64.NO_WRAP); conn.setRequestProperty("Authorization", "Basic " + base64); } - String cookie = conn.getHeaderField("Set-Cookie"); - if (cookie != null) { - CookieManager.getInstance().setCookie(url, cookie); + + List cookies = conn.getHeaderFields().get("Set-Cookie"); + if (cookies != null) { + for (String cookie : cookies) { + CookieManager.getInstance().setCookie(url, cookie); + } } InputStream responseStream = conn.getInputStream(); responseStream = jsInjector.getInjectedStream(responseStream); From b8b9b1f96249908435017eea6c427221f1971836 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 14 Sep 2022 09:59:19 +0200 Subject: [PATCH 164/179] fix(cli): Make migrator update gradle wrapper files (#5910) --- cli/src/tasks/migrate.ts | 41 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/cli/src/tasks/migrate.ts b/cli/src/tasks/migrate.ts index 68d4b67fa..1fa75f635 100644 --- a/cli/src/tasks/migrate.ts +++ b/cli/src/tasks/migrate.ts @@ -48,6 +48,7 @@ const plugins = [ ]; const coreVersion = '^4.0.0'; const pluginVersion = '^4.0.0'; +const gradleVersion = '7.4.2'; export async function migrateCommand(config: Config): Promise { if (config === null) { @@ -246,7 +247,7 @@ export async function migrateCommand(config: Config): Promise { // Update gradle-wrapper.properties await runTask( - `Migrating gradle-wrapper.properties by updating gradle version from 7.0 to 7.4.2.`, + `Migrating gradle-wrapper.properties by updating gradle version from 7.0 to ${gradleVersion}.`, () => { return updateGradleWrapper( join( @@ -348,6 +349,24 @@ export async function migrateCommand(config: Config): Promise { return getCommandOutput('npx', ['cap', 'sync']); }); + try { + await runTask(`Upgrading gradle wrapper files`, () => { + return updateGradleWrapperFiles(config.android.platformDirAbs); + }); + } catch (e) { + if (e.includes('EACCES')) { + logger.error( + `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${c.input( + `chmod +x ./${config.android.platformDir}/gradlew`, + )} and ${c.input( + `cd ${config.android.platformDir} && ./gradlew wrapper --distribution-type all --gradle-version ${gradleVersion} --warning-mode all`, + )} to update the files manually`, + ); + } else { + logger.error(`gradle wrapper files were not updated`); + } + } + // Write all breaking changes await runTask(`Writing breaking changes.`, () => { return writeBreakingChanges(); @@ -661,11 +680,29 @@ async function updateGradleWrapper(filename: string) { 'distributionUrl=', '\n', // eslint-disable-next-line no-useless-escape - `https\\://services.gradle.org/distributions/gradle-7.4.2-all.zip`, + `https\\://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip`, ); writeFileSync(filename, replaced, 'utf-8'); } +async function updateGradleWrapperFiles(platformDir: string) { + await runCommand( + `./gradlew`, + [ + 'wrapper', + '--distribution-type', + 'all', + '--gradle-version', + gradleVersion, + '--warning-mode', + 'all', + ], + { + cwd: platformDir, + }, + ); +} + async function updateFile( config: Config, filename: string, From 7553ede93170971e21ab3dec1798443d084ead2a Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 14 Sep 2022 16:16:03 +0200 Subject: [PATCH 165/179] fix(android): open external links in browser (#5913) --- android/capacitor/src/main/java/com/getcapacitor/Bridge.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 9c0ac5661..7ef29e474 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -351,7 +351,7 @@ public boolean launchIntent(Uri url) { } } - if (!url.toString().contains(appUrl) && !appAllowNavigationMask.matches(url.getHost())) { + if (!url.toString().startsWith(appUrl) && !appAllowNavigationMask.matches(url.getHost())) { try { Intent openIntent = new Intent(Intent.ACTION_VIEW, url); getContext().startActivity(openIntent); From 9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Thu, 15 Sep 2022 09:59:53 -0400 Subject: [PATCH 166/179] fix(core): Exception object was not set on Cap (#5917) --- .../src/main/assets/native-bridge.js | 30 ++++++++++++++++++- core/native-bridge.ts | 3 ++ .../Capacitor/assets/native-bridge.js | 30 ++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/android/capacitor/src/main/assets/native-bridge.js b/android/capacitor/src/main/assets/native-bridge.js index 30e97b22c..269b59ae1 100644 --- a/android/capacitor/src/main/assets/native-bridge.js +++ b/android/capacitor/src/main/assets/native-bridge.js @@ -2,9 +2,36 @@ /*! Capacitor: https://capacitorjs.com/ - MIT License */ /* Generated File. Do not edit. */ -const nativeBridge = (function (exports) { +var nativeBridge = (function (exports) { 'use strict'; + var ExceptionCode; + (function (ExceptionCode) { + /** + * API is not implemented. + * + * This usually means the API can't be used because it is not implemented for + * the current platform. + */ + ExceptionCode["Unimplemented"] = "UNIMPLEMENTED"; + /** + * API is not available. + * + * This means the API can't be used right now because: + * - it is currently missing a prerequisite, such as network connectivity + * - it requires a particular platform or browser version + */ + ExceptionCode["Unavailable"] = "UNAVAILABLE"; + })(ExceptionCode || (ExceptionCode = {})); + class CapacitorException extends Error { + constructor(message, code, data) { + super(message); + this.message = message; + this.code = code; + this.data = data; + } + } + // For removing exports for iOS/Android, keep let for reassignment // eslint-disable-next-line let dummy = {}; @@ -468,6 +495,7 @@ const nativeBridge = (function (exports) { }); }; cap.withPlugin = (_pluginId, _fn) => dummy; + cap.Exception = CapacitorException; initEvents(win, cap); initLegacyHandlers(win, cap); initVendor(win, cap); diff --git a/core/native-bridge.ts b/core/native-bridge.ts index e13628fe4..10b4ffd56 100644 --- a/core/native-bridge.ts +++ b/core/native-bridge.ts @@ -10,6 +10,7 @@ import type { PluginResult, WindowCapacitor, } from './src/definitions-internal'; +import { CapacitorException } from './src/util'; // For removing exports for iOS/Android, keep let for reassignment // eslint-disable-next-line @@ -552,6 +553,8 @@ const initBridge = (w: any): void => { cap.withPlugin = (_pluginId, _fn) => dummy; + cap.Exception = CapacitorException; + initEvents(win, cap); initLegacyHandlers(win, cap); initVendor(win, cap); diff --git a/ios/Capacitor/Capacitor/assets/native-bridge.js b/ios/Capacitor/Capacitor/assets/native-bridge.js index 30e97b22c..269b59ae1 100644 --- a/ios/Capacitor/Capacitor/assets/native-bridge.js +++ b/ios/Capacitor/Capacitor/assets/native-bridge.js @@ -2,9 +2,36 @@ /*! Capacitor: https://capacitorjs.com/ - MIT License */ /* Generated File. Do not edit. */ -const nativeBridge = (function (exports) { +var nativeBridge = (function (exports) { 'use strict'; + var ExceptionCode; + (function (ExceptionCode) { + /** + * API is not implemented. + * + * This usually means the API can't be used because it is not implemented for + * the current platform. + */ + ExceptionCode["Unimplemented"] = "UNIMPLEMENTED"; + /** + * API is not available. + * + * This means the API can't be used right now because: + * - it is currently missing a prerequisite, such as network connectivity + * - it requires a particular platform or browser version + */ + ExceptionCode["Unavailable"] = "UNAVAILABLE"; + })(ExceptionCode || (ExceptionCode = {})); + class CapacitorException extends Error { + constructor(message, code, data) { + super(message); + this.message = message; + this.code = code; + this.data = data; + } + } + // For removing exports for iOS/Android, keep let for reassignment // eslint-disable-next-line let dummy = {}; @@ -468,6 +495,7 @@ const nativeBridge = (function (exports) { }); }; cap.withPlugin = (_pluginId, _fn) => dummy; + cap.Exception = CapacitorException; initEvents(win, cap); initLegacyHandlers(win, cap); initVendor(win, cap); From 020ed8eaeb7864399d4b93f54ab7601c607d8e0d Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 15 Sep 2022 16:11:55 +0200 Subject: [PATCH 167/179] fix(android): set WebViewClient on the WebView (#5919) --- android/capacitor/src/main/java/com/getcapacitor/Bridge.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 7ef29e474..f218074b1 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -1299,6 +1299,7 @@ public BridgeWebViewClient getWebViewClient() { public void setWebViewClient(BridgeWebViewClient client) { this.webViewClient = client; + webView.setWebViewClient(client); } List getWebViewListeners() { From f7029acb885ec60f85a434b6f71e4f2a633c7651 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 15 Sep 2022 16:23:37 +0200 Subject: [PATCH 168/179] fix(cli): Find the Info.plist when using scheme (#5914) --- cli/src/cordova.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cli/src/cordova.ts b/cli/src/cordova.ts index 9acbad61d..289208462 100644 --- a/cli/src/cordova.ts +++ b/cli/src/cordova.ts @@ -353,6 +353,12 @@ export async function logCordovaManualSteps( async function logiOSPlist(configElement: any, config: Config, plugin: Plugin) { let plistPath = resolve(config.ios.nativeTargetDirAbs, 'Info.plist'); + if (config.app.extConfig.ios?.scheme) { + plistPath = resolve( + config.ios.nativeProjectDirAbs, + `${config.app.extConfig.ios?.scheme}-Info.plist`, + ); + } if (!(await pathExists(plistPath))) { plistPath = resolve( config.ios.nativeTargetDirAbs, From 4706f8325864d6c32bc0ad7ff9c07848bcf32ccd Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Thu, 15 Sep 2022 18:53:50 +0200 Subject: [PATCH 169/179] docs(cli): Improve information about minWebViewVersion and errorPath (#5924) --- cli/src/declarations.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index 39894087b..697036b67 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -217,6 +217,10 @@ export interface CapacitorConfig { * * The minimum supported cannot be lower than version `55`, which is required for Capacitor. * + * If the device uses a lower WebView version, an error message will be shown on Logcat. + * If `server.errorPath` is configured, the WebView will redirect to that file, so can be + * used to show a custom error. + * * @since 4.0.0 * @default 60 */ @@ -457,6 +461,7 @@ export interface CapacitorConfig { /** * Specify path to a local html page to display in case of errors. + * On Android the html file won't have access to Capacitor plugins. * * @since 4.0.0 * @default null From d4047cfa947676777f400389a8d65defae140b45 Mon Sep 17 00:00:00 2001 From: Chace Daniels Date: Wed, 21 Sep 2022 14:51:45 -0700 Subject: [PATCH 170/179] feat: Capacitor Cookies & Capacitor Http core plugins * feat: capacitor core http initial implementation * chore: run build and run fmt * feat: merge cookies and http * chore: allow branch for ci dev release * chore: allow dev release * feat: add support for disabling http via config * fix: angular zone.js race condition * fix: default http method to GET * fix: default cap http to opt-in * chore: run fmt * fix: get response headers for XHR * chore(ios): swiftlint fixes * feat: add opt-in config for cookies * fix(ci): verify tests * Update declarations.ts --- .../src/main/assets/native-bridge.js | 270 +++++++++++- .../main/java/com/getcapacitor/Bridge.java | 2 + .../main/java/com/getcapacitor/JSValue.java | 65 +++ .../plugin/CapacitorCookieManager.java | 172 ++++++++ .../getcapacitor/plugin/CapacitorCookies.java | 129 ++++++ .../getcapacitor/plugin/CapacitorHttp.java | 81 ++++ .../util/CapacitorHttpUrlConnection.java | 381 ++++++++++++++++ .../plugin/util/HttpRequestHandler.java | 410 ++++++++++++++++++ .../util/ICapacitorHttpUrlConnection.java | 15 + .../getcapacitor/plugin/util/MimeType.java | 17 + cli/src/declarations.ts | 28 ++ core/native-bridge.ts | 314 ++++++++++++++ core/src/core-plugins.ts | 403 +++++++++++++++++ core/src/definitions-internal.ts | 5 + core/src/index.ts | 15 +- .../Capacitor.xcodeproj/project.pbxproj | 20 + .../Plugins/CapacitorCookieManager.swift | 66 +++ .../Capacitor/Plugins/CapacitorCookies.swift | 52 +++ .../Capacitor/Plugins/CapacitorHttp.swift | 40 ++ .../Plugins/CapacitorUrlRequest.swift | 150 +++++++ .../Capacitor/Plugins/DefaultPlugins.m | 16 + .../Plugins/HttpRequestHandler.swift | 174 ++++++++ .../Capacitor/WebViewDelegationHandler.swift | 28 ++ .../Capacitor/assets/native-bridge.js | 270 +++++++++++- 24 files changed, 3119 insertions(+), 4 deletions(-) create mode 100644 android/capacitor/src/main/java/com/getcapacitor/JSValue.java create mode 100644 android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java create mode 100644 android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java create mode 100644 android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java create mode 100644 android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java create mode 100644 android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java create mode 100644 android/capacitor/src/main/java/com/getcapacitor/plugin/util/ICapacitorHttpUrlConnection.java create mode 100644 android/capacitor/src/main/java/com/getcapacitor/plugin/util/MimeType.java create mode 100644 ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift create mode 100644 ios/Capacitor/Capacitor/Plugins/CapacitorCookies.swift create mode 100644 ios/Capacitor/Capacitor/Plugins/CapacitorHttp.swift create mode 100644 ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift create mode 100644 ios/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift diff --git a/android/capacitor/src/main/assets/native-bridge.js b/android/capacitor/src/main/assets/native-bridge.js index 269b59ae1..dbed700c4 100644 --- a/android/capacitor/src/main/assets/native-bridge.js +++ b/android/capacitor/src/main/assets/native-bridge.js @@ -2,7 +2,7 @@ /*! Capacitor: https://capacitorjs.com/ - MIT License */ /* Generated File. Do not edit. */ -var nativeBridge = (function (exports) { +const nativeBridge = (function (exports) { 'use strict'; var ExceptionCode; @@ -268,6 +268,274 @@ var nativeBridge = (function (exports) { } return String(msg); }; + /** + * Safely web decode a string value (inspired by js-cookie) + * @param str The string value to decode + */ + const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); + const platform = getPlatformId(win); + if (platform == 'android' || platform == 'ios') { + // patch document.cookie on Android/iOS + win.CapacitorCookiesDescriptor = + Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || + Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); + let doPatchCookies = false; + // check if capacitor cookies is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor cookies config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.isEnabled', + }; + const isCookiesEnabled = prompt(JSON.stringify(payload)); + if (isCookiesEnabled === 'true') { + doPatchCookies = true; + } + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled(); + if (isCookiesEnabled === true) { + doPatchCookies = true; + } + } + if (doPatchCookies) { + Object.defineProperty(document, 'cookie', { + get: function () { + if (platform === 'ios') { + // Use prompt to synchronously get cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies', + }; + const res = prompt(JSON.stringify(payload)); + return res; + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + return win.CapacitorCookiesAndroidInterface.getCookies(); + } + }, + set: function (val) { + const cookiePairs = val.split(';'); + for (const cookiePair of cookiePairs) { + const cookieKey = cookiePair.split('=')[0]; + const cookieValue = cookiePair.split('=')[1]; + if (null == cookieValue) { + continue; + } + cap.toNative('CapacitorCookies', 'setCookie', { + key: cookieKey, + value: decode(cookieValue), + }); + } + }, + }); + } + // patch fetch / XHR on Android/iOS + // store original fetch & XHR functions + win.CapacitorWebFetch = window.fetch; + win.CapacitorWebXMLHttpRequest = { + abort: window.XMLHttpRequest.prototype.abort, + open: window.XMLHttpRequest.prototype.open, + send: window.XMLHttpRequest.prototype.send, + setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, + }; + let doPatchHttp = false; + // check if capacitor http is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor http config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorHttp', + }; + const isHttpEnabled = prompt(JSON.stringify(payload)); + if (isHttpEnabled === 'true') { + doPatchHttp = true; + } + } + else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { + const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); + if (isHttpEnabled === true) { + doPatchHttp = true; + } + } + if (doPatchHttp) { + // fetch patch + window.fetch = async (resource, options) => { + if (resource.toString().startsWith('data:') || + resource.toString().startsWith('blob:')) { + return win.CapacitorWebFetch(resource, options); + } + try { + // intercept request & pass to the bridge + const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', { + url: resource, + method: (options === null || options === void 0 ? void 0 : options.method) ? options.method : undefined, + data: (options === null || options === void 0 ? void 0 : options.body) ? options.body : undefined, + headers: (options === null || options === void 0 ? void 0 : options.headers) ? options.headers : undefined, + }); + const data = typeof nativeResponse.data === 'string' + ? nativeResponse.data + : JSON.stringify(nativeResponse.data); + // intercept & parse response before returning + const response = new Response(data, { + headers: nativeResponse.headers, + status: nativeResponse.status, + }); + return response; + } + catch (error) { + return Promise.reject(error); + } + }; + // XHR event listeners + const addEventListeners = function () { + this.addEventListener('abort', function () { + if (typeof this.onabort === 'function') + this.onabort(); + }); + this.addEventListener('error', function () { + if (typeof this.onerror === 'function') + this.onerror(); + }); + this.addEventListener('load', function () { + if (typeof this.onload === 'function') + this.onload(); + }); + this.addEventListener('loadend', function () { + if (typeof this.onloadend === 'function') + this.onloadend(); + }); + this.addEventListener('loadstart', function () { + if (typeof this.onloadstart === 'function') + this.onloadstart(); + }); + this.addEventListener('readystatechange', function () { + if (typeof this.onreadystatechange === 'function') + this.onreadystatechange(); + }); + this.addEventListener('timeout', function () { + if (typeof this.ontimeout === 'function') + this.ontimeout(); + }); + }; + // XHR patch abort + window.XMLHttpRequest.prototype.abort = function () { + this.readyState = 0; + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }; + // XHR patch open + window.XMLHttpRequest.prototype.open = function (method, url) { + Object.defineProperties(this, { + _headers: { + value: {}, + writable: true, + }, + readyState: { + get: function () { + var _a; + return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0; + }, + set: function (val) { + this._readyState = val; + this.dispatchEvent(new Event('readystatechange')); + }, + }, + response: { + value: '', + writable: true, + }, + responseText: { + value: '', + writable: true, + }, + responseURL: { + value: '', + writable: true, + }, + status: { + value: 0, + writable: true, + }, + }); + addEventListeners.call(this); + this._method = method; + this._url = url; + this.readyState = 1; + }; + // XHR patch set request header + window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) { + this._headers[header] = value; + }; + // XHR patch send + window.XMLHttpRequest.prototype.send = function (body) { + try { + this.readyState = 2; + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: body !== null ? body : undefined, + headers: this._headers, + }) + .then((nativeResponse) => { + // intercept & parse response before returning + if (this.readyState == 2) { + this.dispatchEvent(new Event('loadstart')); + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + this.response = nativeResponse.data; + this.responseText = + typeof nativeResponse.data === 'string' + ? nativeResponse.data + : JSON.stringify(nativeResponse.data); + this.responseURL = nativeResponse.url; + this.readyState = 4; + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + } + }) + .catch((error) => { + this.dispatchEvent(new Event('loadstart')); + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; + this.readyState = 4; + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + } + catch (error) { + this.dispatchEvent(new Event('loadstart')); + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + } + }; + // XHR patch getAllResponseHeaders + window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; + } + } + return returnString; + }; + // XHR patch getResponseHeader + window.XMLHttpRequest.prototype.getResponseHeader = function (name) { + return this._headers[name]; + }; + } + } // patch window.console on iOS and store original console fns const isIos = getPlatformId(win) === 'ios'; if (win.console && isIos) { diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index f218074b1..ee6e6b7fa 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -556,7 +556,9 @@ private void initWebView() { * Register our core Plugin APIs */ private void registerAllPlugins() { + this.registerPlugin(com.getcapacitor.plugin.CapacitorCookies.class); this.registerPlugin(com.getcapacitor.plugin.WebView.class); + this.registerPlugin(com.getcapacitor.plugin.CapacitorHttp.class); for (Class pluginClass : this.initialPlugins) { this.registerPlugin(pluginClass); diff --git a/android/capacitor/src/main/java/com/getcapacitor/JSValue.java b/android/capacitor/src/main/java/com/getcapacitor/JSValue.java new file mode 100644 index 000000000..d97ba91bf --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/JSValue.java @@ -0,0 +1,65 @@ +package com.getcapacitor; + +import org.json.JSONException; + +/** + * Represents a single user-data value of any type on the capacitor PluginCall object. + */ +public class JSValue { + + private final Object value; + + /** + * @param call The capacitor plugin call, used for accessing the value safely. + * @param name The name of the property to access. + */ + public JSValue(PluginCall call, String name) { + this.value = this.toValue(call, name); + } + + /** + * Returns the coerced but uncasted underlying value. + */ + public Object getValue() { + return this.value; + } + + @Override + public String toString() { + return this.getValue().toString(); + } + + /** + * Returns the underlying value as a JSObject, or throwing if it cannot. + * + * @throws JSONException If the underlying value is not a JSObject. + */ + public JSObject toJSObject() throws JSONException { + if (this.value instanceof JSObject) return (JSObject) this.value; + throw new JSONException("JSValue could not be coerced to JSObject."); + } + + /** + * Returns the underlying value as a JSArray, or throwing if it cannot. + * + * @throws JSONException If the underlying value is not a JSArray. + */ + public JSArray toJSArray() throws JSONException { + if (this.value instanceof JSArray) return (JSArray) this.value; + throw new JSONException("JSValue could not be coerced to JSArray."); + } + + /** + * Returns the underlying value this object represents, coercing it into a capacitor-friendly object if supported. + */ + private Object toValue(PluginCall call, String name) { + Object value = null; + value = call.getArray(name, null); + if (value != null) return value; + value = call.getObject(name, null); + if (value != null) return value; + value = call.getString(name, null); + if (value != null) return value; + return call.getData().opt(name); + } +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java new file mode 100644 index 000000000..9785318c0 --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java @@ -0,0 +1,172 @@ +package com.getcapacitor.plugin; + +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.net.CookieStore; +import java.net.HttpCookie; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class CapacitorCookieManager extends CookieManager { + + private final android.webkit.CookieManager webkitCookieManager; + + /** + * Create a new cookie manager with the default cookie store and policy + */ + public CapacitorCookieManager() { + this(null, null); + } + + /** + * Create a new cookie manager with specified cookie store and cookie policy. + * @param store a {@code CookieStore} to be used by CookieManager. if {@code null}, cookie + * manager will use a default one, which is an in-memory CookieStore implementation. + * @param policy a {@code CookiePolicy} instance to be used by cookie manager as policy + * callback. if {@code null}, ACCEPT_ORIGINAL_SERVER will be used. + */ + public CapacitorCookieManager(CookieStore store, CookiePolicy policy) { + super(store, policy); + webkitCookieManager = android.webkit.CookieManager.getInstance(); + } + + /** + * Gets the cookies for the given URL. + * @param url the URL for which the cookies are requested + * @return value the cookies as a string, using the format of the 'Cookie' HTTP request header + */ + public String getCookieString(String url) { + return webkitCookieManager.getCookie(url); + } + + /** + * Gets a cookie value for the given URL and key. + * @param url the URL for which the cookies are requested + * @param key the key of the cookie to search for + * @return the {@code HttpCookie} value of the cookie at the key, + * otherwise it will return a new empty {@code HttpCookie} + */ + public HttpCookie getCookie(String url, String key) { + HttpCookie[] cookies = getCookies(url); + for (HttpCookie cookie : cookies) { + if (cookie.getName().equals(key)) { + return cookie; + } + } + + return null; + } + + /** + * Gets an array of {@code HttpCookie} given a URL. + * @param url the URL for which the cookies are requested + * @return an {@code HttpCookie} array of non-expired cookies + */ + public HttpCookie[] getCookies(String url) { + try { + ArrayList cookieList = new ArrayList<>(); + String cookieString = getCookieString(url); + if (cookieString != null) { + String[] singleCookie = cookieString.split(";"); + for (String c : singleCookie) { + HttpCookie parsed = HttpCookie.parse(c).get(0); + parsed.setValue(parsed.getValue()); + cookieList.add(parsed); + } + } + HttpCookie[] cookies = new HttpCookie[cookieList.size()]; + return cookieList.toArray(cookies); + } catch (Exception ex) { + return new HttpCookie[0]; + } + } + + /** + * Sets a cookie for the given URL. Any existing cookie with the same host, path and name will + * be replaced with the new cookie. The cookie being set will be ignored if it is expired. + * @param url the URL for which the cookie is to be set + * @param value the cookie as a string, using the format of the 'Set-Cookie' HTTP response header + */ + public void setCookie(String url, String value) { + webkitCookieManager.setCookie(url, value); + flush(); + } + + /** + * Sets a cookie for the given URL. Any existing cookie with the same host, path and name will + * be replaced with the new cookie. The cookie being set will be ignored if it is expired. + * @param url the URL for which the cookie is to be set + * @param key the {@code HttpCookie} name to use for lookup + * @param value the value of the {@code HttpCookie} given a key + */ + public void setCookie(String url, String key, String value) { + String cookieValue = key + "=" + value; + setCookie(url, cookieValue); + } + + /** + * Removes all cookies. This method is asynchronous. + */ + public void removeAllCookies() { + webkitCookieManager.removeAllCookies(null); + flush(); + } + + /** + * Ensures all cookies currently accessible through the getCookie API are written to persistent + * storage. This call will block the caller until it is done and may perform I/O. + */ + public void flush() { + webkitCookieManager.flush(); + } + + @Override + public void put(URI uri, Map> responseHeaders) { + // make sure our args are valid + if ((uri == null) || (responseHeaders == null)) return; + + // save our url once + String url = uri.toString(); + + // go over the headers + for (String headerKey : responseHeaders.keySet()) { + // ignore headers which aren't cookie related + if ((headerKey == null) || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey.equalsIgnoreCase("Set-Cookie"))) continue; + + // process each of the headers + for (String headerValue : Objects.requireNonNull(responseHeaders.get(headerKey))) { + setCookie(url, headerValue); + } + } + } + + @Override + public Map> get(URI uri, Map> requestHeaders) { + // make sure our args are valid + if ((uri == null) || (requestHeaders == null)) throw new IllegalArgumentException("Argument is null"); + + // save our url once + String url = uri.toString(); + + // prepare our response + Map> res = new HashMap<>(); + + // get the cookie + String cookie = getCookieString(url); + + // return it + if (cookie != null) res.put("Cookie", Collections.singletonList(cookie)); + return res; + } + + @Override + public CookieStore getCookieStore() { + // we don't want anyone to work with this cookie store directly + throw new UnsupportedOperationException(); + } +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java new file mode 100644 index 000000000..4d3175980 --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java @@ -0,0 +1,129 @@ +package com.getcapacitor.plugin; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.util.Log; +import android.webkit.JavascriptInterface; +import androidx.annotation.Nullable; +import com.getcapacitor.CapConfig; +import com.getcapacitor.JSObject; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginConfig; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; +import java.net.CookieHandler; +import java.net.HttpCookie; +import java.net.URI; + +@CapacitorPlugin +public class CapacitorCookies extends Plugin { + + CapacitorCookieManager cookieManager; + + @Override + public void load() { + this.bridge.getWebView().addJavascriptInterface(this, "CapacitorCookiesAndroidInterface"); + this.cookieManager = new CapacitorCookieManager(null, java.net.CookiePolicy.ACCEPT_ALL); + CookieHandler.setDefault(cookieManager); + super.load(); + } + + @JavascriptInterface + public boolean isEnabled() { + PluginConfig pluginConfig = getBridge().getConfig().getPluginConfiguration("CapacitorCookies"); + return pluginConfig.getBoolean("enabled", false); + } + + /** + * Helper function for getting the serverUrl from the Capacitor Config. Returns an empty + * string if it is invalid and will auto-reject through {@code call} + * @param call the {@code PluginCall} context + * @return the string of the server specified in the Capacitor config + */ + private String getServerUrl(@Nullable PluginCall call) { + String url = (call == null) ? this.bridge.getServerUrl() : call.getString("url", this.bridge.getServerUrl()); + + if (url == null || url.isEmpty()) { + url = this.bridge.getLocalUrl(); + } + + URI uri = getUri(url); + if (uri == null) { + if (call != null) { + call.reject("Invalid URL. Check that \"server\" is passed in correctly"); + } + + return ""; + } + + return url; + } + + /** + * Try to parse a url string and if it can't be parsed, return null + * @param url the url string to try to parse + * @return a parsed URI + */ + private URI getUri(String url) { + try { + return new URI(url); + } catch (Exception ex) { + return null; + } + } + + @JavascriptInterface + public String getCookies() { + try { + String url = getServerUrl(null); + if (!url.isEmpty()) { + return cookieManager.getCookieString(url); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return ""; + } + + @PluginMethod + public void setCookie(PluginCall call) { + String key = call.getString("key"); + String value = call.getString("value"); + String url = getServerUrl(call); + + if (!url.isEmpty()) { + cookieManager.setCookie(url, key, value); + call.resolve(); + } + } + + @PluginMethod + public void deleteCookie(PluginCall call) { + String key = call.getString("key"); + String url = getServerUrl(call); + if (!url.isEmpty()) { + cookieManager.setCookie(url, key + "=; Expires=Wed, 31 Dec 2000 23:59:59 GMT"); + call.resolve(); + } + } + + @PluginMethod + public void clearCookies(PluginCall call) { + String url = getServerUrl(call); + if (!url.isEmpty()) { + HttpCookie[] cookies = cookieManager.getCookies(url); + for (HttpCookie cookie : cookies) { + cookieManager.setCookie(url, cookie.getName() + "=; Expires=Wed, 31 Dec 2000 23:59:59 GMT"); + } + call.resolve(); + } + } + + @PluginMethod + public void clearAllCookies(PluginCall call) { + cookieManager.removeAllCookies(); + call.resolve(); + } +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java new file mode 100644 index 000000000..eb9c624ac --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java @@ -0,0 +1,81 @@ +package com.getcapacitor.plugin; + +import android.Manifest; +import android.webkit.JavascriptInterface; +import com.getcapacitor.CapConfig; +import com.getcapacitor.JSObject; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginConfig; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; +import com.getcapacitor.annotation.Permission; +import com.getcapacitor.plugin.util.HttpRequestHandler; + +@CapacitorPlugin( + permissions = { + @Permission(strings = { Manifest.permission.WRITE_EXTERNAL_STORAGE }, alias = "HttpWrite"), + @Permission(strings = { Manifest.permission.READ_EXTERNAL_STORAGE }, alias = "HttpRead") + } +) +public class CapacitorHttp extends Plugin { + + @Override + public void load() { + this.bridge.getWebView().addJavascriptInterface(this, "CapacitorHttpAndroidInterface"); + super.load(); + } + + private void http(final PluginCall call, final String httpMethod) { + Runnable asyncHttpCall = new Runnable() { + @Override + public void run() { + try { + JSObject response = HttpRequestHandler.request(call, httpMethod); + call.resolve(response); + } catch (Exception e) { + System.out.println(e.toString()); + call.reject(e.getClass().getSimpleName(), e); + } + } + }; + Thread httpThread = new Thread(asyncHttpCall); + httpThread.start(); + } + + @JavascriptInterface + public boolean isEnabled() { + PluginConfig pluginConfig = getBridge().getConfig().getPluginConfiguration("CapacitorHttp"); + return pluginConfig.getBoolean("enabled", false); + } + + @PluginMethod + public void request(final PluginCall call) { + this.http(call, null); + } + + @PluginMethod + public void get(final PluginCall call) { + this.http(call, "GET"); + } + + @PluginMethod + public void post(final PluginCall call) { + this.http(call, "POST"); + } + + @PluginMethod + public void put(final PluginCall call) { + this.http(call, "PUT"); + } + + @PluginMethod + public void patch(final PluginCall call) { + this.http(call, "PATCH"); + } + + @PluginMethod + public void delete(final PluginCall call) { + this.http(call, "DELETE"); + } +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java new file mode 100644 index 000000000..70850e6c9 --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java @@ -0,0 +1,381 @@ +package com.getcapacitor.plugin.util; + +import android.os.Build; +import android.os.LocaleList; +import android.text.TextUtils; +import com.getcapacitor.JSArray; +import com.getcapacitor.JSObject; +import com.getcapacitor.JSValue; +import com.getcapacitor.PluginCall; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.ProtocolException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLEncoder; +import java.net.UnknownServiceException; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import org.json.JSONException; + +public class CapacitorHttpUrlConnection implements ICapacitorHttpUrlConnection { + + private final HttpURLConnection connection; + + /** + * Make a new CapacitorHttpUrlConnection instance, which wraps around HttpUrlConnection + * and provides some helper functions for setting request headers and the request body + * @param conn the base HttpUrlConnection. You can pass the value from + * {@code (HttpUrlConnection) URL.openConnection()} + */ + public CapacitorHttpUrlConnection(HttpURLConnection conn) { + connection = conn; + this.setDefaultRequestProperties(); + } + + /** + * Returns the underlying HttpUrlConnection value + * @return the underlying HttpUrlConnection value + */ + public HttpURLConnection getHttpConnection() { + return connection; + } + + /** + * Set the value of the {@code allowUserInteraction} field of + * this {@code URLConnection}. + * + * @param isAllowedInteraction the new value. + * @throws IllegalStateException if already connected + */ + public void setAllowUserInteraction(boolean isAllowedInteraction) { + connection.setAllowUserInteraction(isAllowedInteraction); + } + + /** + * Set the method for the URL request, one of: + *

    + *
  • GET + *
  • POST + *
  • HEAD + *
  • OPTIONS + *
  • PUT + *
  • DELETE + *
  • TRACE + *
are legal, subject to protocol restrictions. The default + * method is GET. + * + * @param method the HTTP method + * @exception ProtocolException if the method cannot be reset or if + * the requested method isn't valid for HTTP. + * @exception SecurityException if a security manager is set and the + * method is "TRACE", but the "allowHttpTrace" + * NetPermission is not granted. + */ + public void setRequestMethod(String method) throws ProtocolException { + connection.setRequestMethod(method); + } + + /** + * Sets a specified timeout value, in milliseconds, to be used + * when opening a communications link to the resource referenced + * by this URLConnection. If the timeout expires before the + * connection can be established, a + * java.net.SocketTimeoutException is raised. A timeout of zero is + * interpreted as an infinite timeout. + * + *

Warning: If the hostname resolves to multiple IP + * addresses, Android's default implementation of {@link HttpURLConnection} + * will try each in + * RFC 3484 order. If + * connecting to each of these addresses fails, multiple timeouts will + * elapse before the connect attempt throws an exception. Host names + * that support both IPv6 and IPv4 always have at least 2 IP addresses. + * + * @param timeout an {@code int} that specifies the connect + * timeout value in milliseconds + * @throws IllegalArgumentException if the timeout parameter is negative + */ + public void setConnectTimeout(int timeout) { + if (timeout < 0) { + throw new IllegalArgumentException("timeout can not be negative"); + } + connection.setConnectTimeout(timeout); + } + + /** + * Sets the read timeout to a specified timeout, in + * milliseconds. A non-zero value specifies the timeout when + * reading from Input stream when a connection is established to a + * resource. If the timeout expires before there is data available + * for read, a java.net.SocketTimeoutException is raised. A + * timeout of zero is interpreted as an infinite timeout. + * + * @param timeout an {@code int} that specifies the timeout + * value to be used in milliseconds + * @throws IllegalArgumentException if the timeout parameter is negative + */ + public void setReadTimeout(int timeout) { + if (timeout < 0) { + throw new IllegalArgumentException("timeout can not be negative"); + } + connection.setReadTimeout(timeout); + } + + /** + * Sets whether automatic HTTP redirects should be disabled + * @param disableRedirects the flag to determine if redirects should be followed + */ + public void setDisableRedirects(boolean disableRedirects) { + connection.setInstanceFollowRedirects(!disableRedirects); + } + + /** + * Sets the request headers given a JSObject of key-value pairs + * @param headers the JSObject values to map to the HttpUrlConnection request headers + */ + public void setRequestHeaders(JSObject headers) { + Iterator keys = headers.keys(); + while (keys.hasNext()) { + String key = keys.next(); + String value = headers.getString(key); + connection.setRequestProperty(key, value); + } + } + + /** + * Sets the value of the {@code doOutput} field for this + * {@code URLConnection} to the specified value. + *

+ * A URL connection can be used for input and/or output. Set the DoOutput + * flag to true if you intend to use the URL connection for output, + * false if not. The default is false. + * + * @param shouldDoOutput the new value. + * @throws IllegalStateException if already connected + */ + public void setDoOutput(boolean shouldDoOutput) { + connection.setDoOutput(shouldDoOutput); + } + + /** + * + * @param call + * @throws JSONException + * @throws IOException + */ + public void setRequestBody(PluginCall call, JSValue body) throws JSONException, IOException { + String contentType = connection.getRequestProperty("Content-Type"); + String dataString = ""; + + if (contentType == null || contentType.isEmpty()) return; + + if (contentType.contains("application/json")) { + JSArray jsArray = null; + if (body != null) { + dataString = body.toString(); + } else { + jsArray = call.getArray("data", null); + } + if (jsArray != null) { + dataString = jsArray.toString(); + } else if (body == null) { + dataString = call.getString("data"); + } + this.writeRequestBody(dataString != null ? dataString : ""); + } else if (contentType.contains("application/x-www-form-urlencoded")) { + StringBuilder builder = new StringBuilder(); + + JSObject obj = body.toJSObject(); + Iterator keys = obj.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object d = obj.get(key); + builder.append(key).append("=").append(URLEncoder.encode(d.toString(), "UTF-8")); + + if (keys.hasNext()) { + builder.append("&"); + } + } + this.writeRequestBody(builder.toString()); + } else { + this.writeRequestBody(body.toString()); + } + } + + /** + * Writes the provided string to the HTTP connection managed by this instance. + * + * @param body The string value to write to the connection stream. + */ + private void writeRequestBody(String body) throws IOException { + try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) { + os.write(body.getBytes(StandardCharsets.UTF_8)); + os.flush(); + } + } + + /** + * Opens a communications link to the resource referenced by this + * URL, if such a connection has not already been established. + *

+ * If the {@code connect} method is called when the connection + * has already been opened (indicated by the {@code connected} + * field having the value {@code true}), the call is ignored. + *

+ * URLConnection objects go through two phases: first they are + * created, then they are connected. After being created, and + * before being connected, various options can be specified + * (e.g., doInput and UseCaches). After connecting, it is an + * error to try to set them. Operations that depend on being + * connected, like getContentLength, will implicitly perform the + * connection, if necessary. + * + * @throws SocketTimeoutException if the timeout expires before + * the connection can be established + * @exception IOException if an I/O error occurs while opening the + * connection. + */ + public void connect() throws IOException { + connection.connect(); + } + + /** + * Gets the status code from an HTTP response message. + * For example, in the case of the following status lines: + *

+     * HTTP/1.0 200 OK
+     * HTTP/1.0 401 Unauthorized
+     * 
+ * It will return 200 and 401 respectively. + * Returns -1 if no code can be discerned + * from the response (i.e., the response is not valid HTTP). + * @throws IOException if an error occurred connecting to the server. + * @return the HTTP Status-Code, or -1 + */ + public int getResponseCode() throws IOException { + return connection.getResponseCode(); + } + + /** + * Returns the value of this {@code URLConnection}'s {@code URL} + * field. + * + * @return the value of this {@code URLConnection}'s {@code URL} + * field. + */ + public URL getURL() { + return connection.getURL(); + } + + /** + * Returns the error stream if the connection failed + * but the server sent useful data nonetheless. The + * typical example is when an HTTP server responds + * with a 404, which will cause a FileNotFoundException + * to be thrown in connect, but the server sent an HTML + * help page with suggestions as to what to do. + * + *

This method will not cause a connection to be initiated. If + * the connection was not connected, or if the server did not have + * an error while connecting or if the server had an error but + * no error data was sent, this method will return null. This is + * the default. + * + * @return an error stream if any, null if there have been no + * errors, the connection is not connected or the server sent no + * useful data. + */ + @Override + public InputStream getErrorStream() { + return connection.getErrorStream(); + } + + /** + * Returns the value of the named header field. + *

+ * If called on a connection that sets the same header multiple times + * with possibly different values, only the last value is returned. + * + * + * @param name the name of a header field. + * @return the value of the named header field, or {@code null} + * if there is no such field in the header. + */ + @Override + public String getHeaderField(String name) { + return connection.getHeaderField(name); + } + + /** + * Returns an input stream that reads from this open connection. + * + * A SocketTimeoutException can be thrown when reading from the + * returned input stream if the read timeout expires before data + * is available for read. + * + * @return an input stream that reads from this open connection. + * @exception IOException if an I/O error occurs while + * creating the input stream. + * @exception UnknownServiceException if the protocol does not support + * input. + * @see #setReadTimeout(int) + */ + @Override + public InputStream getInputStream() throws IOException { + return connection.getInputStream(); + } + + /** + * Returns an unmodifiable Map of the header fields. + * The Map keys are Strings that represent the + * response-header field names. Each Map value is an + * unmodifiable List of Strings that represents + * the corresponding field values. + * + * @return a Map of header fields + */ + public Map> getHeaderFields() { + return connection.getHeaderFields(); + } + + /** + * Sets the default request properties on the newly created connection. + * This is called as early as possible to allow overrides by user-provided values. + */ + private void setDefaultRequestProperties() { + connection.setRequestProperty("Accept-Charset", StandardCharsets.UTF_8.name()); + String acceptLanguage = buildDefaultAcceptLanguageProperty(); + if (!TextUtils.isEmpty(acceptLanguage)) { + connection.setRequestProperty("Accept-Language", acceptLanguage); + } + } + + /** + * Builds and returns a locale string describing the device's current locale preferences. + */ + private String buildDefaultAcceptLanguageProperty() { + Locale locale; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + locale = LocaleList.getDefault().get(0); + } else { + locale = Locale.getDefault(); + } + String result = ""; + String lang = locale.getLanguage(); + String country = locale.getCountry(); + if (!TextUtils.isEmpty(lang)) { + if (!TextUtils.isEmpty(country)) { + result = String.format("%s-%s,%s;q=0.5", lang, country, lang); + } else { + result = String.format("%s;q=0.5", lang); + } + } + return result; + } +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java new file mode 100644 index 000000000..97b065a1e --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java @@ -0,0 +1,410 @@ +package com.getcapacitor.plugin.util; + +import android.text.TextUtils; +import android.util.Base64; +import com.getcapacitor.JSArray; +import com.getcapacitor.JSObject; +import com.getcapacitor.JSValue; +import com.getcapacitor.PluginCall; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class HttpRequestHandler { + + /** + * An enum specifying conventional HTTP Response Types + * See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType + */ + public enum ResponseType { + ARRAY_BUFFER("arraybuffer"), + BLOB("blob"), + DOCUMENT("document"), + JSON("json"), + TEXT("text"); + + private final String name; + + ResponseType(String name) { + this.name = name; + } + + static final ResponseType DEFAULT = TEXT; + + static ResponseType parse(String value) { + for (ResponseType responseType : values()) { + if (responseType.name.equalsIgnoreCase(value)) { + return responseType; + } + } + return DEFAULT; + } + } + + /** + * Internal builder class for building a CapacitorHttpUrlConnection + */ + private static class HttpURLConnectionBuilder { + + private Integer connectTimeout; + private Integer readTimeout; + private Boolean disableRedirects; + private JSObject headers; + private String method; + private URL url; + + private CapacitorHttpUrlConnection connection; + + public HttpURLConnectionBuilder setConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + public HttpURLConnectionBuilder setReadTimeout(Integer readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + public HttpURLConnectionBuilder setDisableRedirects(Boolean disableRedirects) { + this.disableRedirects = disableRedirects; + return this; + } + + public HttpURLConnectionBuilder setHeaders(JSObject headers) { + this.headers = headers; + return this; + } + + public HttpURLConnectionBuilder setMethod(String method) { + this.method = method; + return this; + } + + public HttpURLConnectionBuilder setUrl(URL url) { + this.url = url; + return this; + } + + public HttpURLConnectionBuilder openConnection() throws IOException { + connection = new CapacitorHttpUrlConnection((HttpURLConnection) url.openConnection()); + + connection.setAllowUserInteraction(false); + connection.setRequestMethod(method); + + if (connectTimeout != null) connection.setConnectTimeout(connectTimeout); + if (readTimeout != null) connection.setReadTimeout(readTimeout); + if (disableRedirects != null) connection.setDisableRedirects(disableRedirects); + + connection.setRequestHeaders(headers); + return this; + } + + public HttpURLConnectionBuilder setUrlParams(JSObject params) throws MalformedURLException, URISyntaxException, JSONException { + return this.setUrlParams(params, true); + } + + public HttpURLConnectionBuilder setUrlParams(JSObject params, boolean shouldEncode) + throws URISyntaxException, MalformedURLException { + String initialQuery = url.getQuery(); + String initialQueryBuilderStr = initialQuery == null ? "" : initialQuery; + + Iterator keys = params.keys(); + + if (!keys.hasNext()) { + return this; + } + + StringBuilder urlQueryBuilder = new StringBuilder(initialQueryBuilderStr); + + // Build the new query string + while (keys.hasNext()) { + String key = keys.next(); + + // Attempt as JSONArray and fallback to string if it fails + try { + StringBuilder value = new StringBuilder(); + JSONArray arr = params.getJSONArray(key); + for (int x = 0; x < arr.length(); x++) { + value.append(key).append("=").append(arr.getString(x)); + if (x != arr.length() - 1) { + value.append("&"); + } + } + if (urlQueryBuilder.length() > 0) { + urlQueryBuilder.append("&"); + } + urlQueryBuilder.append(value); + } catch (JSONException e) { + if (urlQueryBuilder.length() > 0) { + urlQueryBuilder.append("&"); + } + urlQueryBuilder.append(key).append("=").append(params.getString(key)); + } + } + + String urlQuery = urlQueryBuilder.toString(); + + URI uri = url.toURI(); + if (shouldEncode) { + URI encodedUri = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), urlQuery, uri.getFragment()); + this.url = encodedUri.toURL(); + } else { + String unEncodedUrlString = + uri.getScheme() + + "://" + + uri.getAuthority() + + uri.getPath() + + ((!urlQuery.equals("")) ? "?" + urlQuery : "") + + ((uri.getFragment() != null) ? uri.getFragment() : ""); + this.url = new URL(unEncodedUrlString); + } + + return this; + } + + public CapacitorHttpUrlConnection build() { + return connection; + } + } + + /** + * Builds an HTTP Response given CapacitorHttpUrlConnection and ResponseType objects. + * Defaults to ResponseType.DEFAULT + * @param connection The CapacitorHttpUrlConnection to respond with + * @throws IOException Thrown if the InputStream is unable to be parsed correctly + * @throws JSONException Thrown if the JSON is unable to be parsed + */ + private static JSObject buildResponse(CapacitorHttpUrlConnection connection) throws IOException, JSONException { + return buildResponse(connection, ResponseType.DEFAULT); + } + + /** + * Builds an HTTP Response given CapacitorHttpUrlConnection and ResponseType objects + * @param connection The CapacitorHttpUrlConnection to respond with + * @param responseType The requested ResponseType + * @return A JSObject that contains the HTTPResponse to return to the browser + * @throws IOException Thrown if the InputStream is unable to be parsed correctly + * @throws JSONException Thrown if the JSON is unable to be parsed + */ + private static JSObject buildResponse(CapacitorHttpUrlConnection connection, ResponseType responseType) + throws IOException, JSONException { + int statusCode = connection.getResponseCode(); + + JSObject output = new JSObject(); + output.put("status", statusCode); + output.put("headers", buildResponseHeaders(connection)); + output.put("url", connection.getURL()); + output.put("data", readData(connection, responseType)); + + InputStream errorStream = connection.getErrorStream(); + if (errorStream != null) { + output.put("error", true); + } + + return output; + } + + /** + * Read the existing ICapacitorHttpUrlConnection data + * @param connection The ICapacitorHttpUrlConnection object to read in + * @param responseType The type of HTTP response to return to the API + * @return The parsed data from the connection + * @throws IOException Thrown if the InputStreams cannot be properly parsed + * @throws JSONException Thrown if the JSON is malformed when parsing as JSON + */ + static Object readData(ICapacitorHttpUrlConnection connection, ResponseType responseType) throws IOException, JSONException { + InputStream errorStream = connection.getErrorStream(); + String contentType = connection.getHeaderField("Content-Type"); + + if (errorStream != null) { + if (isOneOf(contentType, MimeType.APPLICATION_JSON, MimeType.APPLICATION_VND_API_JSON)) { + return parseJSON(readStreamAsString(errorStream)); + } else { + return readStreamAsString(errorStream); + } + } else if (contentType != null && contentType.contains(MimeType.APPLICATION_JSON.getValue())) { + // backward compatibility + return parseJSON(readStreamAsString(connection.getInputStream())); + } else { + InputStream stream = connection.getInputStream(); + switch (responseType) { + case ARRAY_BUFFER: + case BLOB: + return readStreamAsBase64(stream); + case JSON: + return parseJSON(readStreamAsString(stream)); + case DOCUMENT: + case TEXT: + default: + return readStreamAsString(stream); + } + } + } + + /** + * Helper function for determining if the Content-Type is a typeof an existing Mime-Type + * @param contentType The Content-Type string to check for + * @param mimeTypes The Mime-Type values to check against + * @return + */ + private static boolean isOneOf(String contentType, MimeType... mimeTypes) { + if (contentType != null) { + for (MimeType mimeType : mimeTypes) { + if (contentType.contains(mimeType.getValue())) { + return true; + } + } + } + return false; + } + + /** + * Build the JSObject response headers based on the connection header map + * @param connection The CapacitorHttpUrlConnection connection + * @return A JSObject of the header values from the CapacitorHttpUrlConnection + */ + private static JSObject buildResponseHeaders(CapacitorHttpUrlConnection connection) { + JSObject output = new JSObject(); + + for (Map.Entry> entry : connection.getHeaderFields().entrySet()) { + String valuesString = TextUtils.join(", ", entry.getValue()); + output.put(entry.getKey(), valuesString); + } + + return output; + } + + /** + * Returns a JSObject or a JSArray based on a string-ified input + * @param input String-ified JSON that needs parsing + * @return A JSObject or JSArray + * @throws JSONException thrown if the JSON is malformed + */ + private static Object parseJSON(String input) throws JSONException { + JSONObject json = new JSONObject(); + try { + if ("null".equals(input.trim())) { + return JSONObject.NULL; + } else if ("true".equals(input.trim())) { + return new JSONObject().put("flag", "true"); + } else if ("false".equals(input.trim())) { + return new JSONObject().put("flag", "false"); + } else { + try { + return new JSObject(input); + } catch (JSONException e) { + return new JSArray(input); + } + } + } catch (JSONException e) { + return new JSArray(input); + } + } + + /** + * Returns a string based on a base64 InputStream + * @param in The base64 InputStream to convert to a String + * @return String value of InputStream + * @throws IOException thrown if the InputStream is unable to be read as base64 + */ + private static String readStreamAsBase64(InputStream in) throws IOException { + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; + int readBytes; + while ((readBytes = in.read(buffer)) != -1) { + out.write(buffer, 0, readBytes); + } + byte[] result = out.toByteArray(); + return Base64.encodeToString(result, 0, result.length, Base64.DEFAULT); + } + } + + /** + * Returns a string based on an InputStream + * @param in The InputStream to convert to a String + * @return String value of InputStream + * @throws IOException thrown if the InputStream is unable to be read + */ + private static String readStreamAsString(InputStream in) throws IOException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { + StringBuilder builder = new StringBuilder(); + String line = reader.readLine(); + while (line != null) { + builder.append(line); + line = reader.readLine(); + if (line != null) { + builder.append(System.getProperty("line.separator")); + } + } + return builder.toString(); + } + } + + /** + * Makes an Http Request based on the PluginCall parameters + * @param call The Capacitor PluginCall that contains the options need for an Http request + * @param httpMethod The HTTP method that overrides the PluginCall HTTP method + * @throws IOException throws an IO request when a connection can't be made + * @throws URISyntaxException thrown when the URI is malformed + * @throws JSONException thrown when the incoming JSON is malformed + */ + public static JSObject request(PluginCall call, String httpMethod) throws IOException, URISyntaxException, JSONException { + String urlString = call.getString("url", ""); + JSObject headers = call.getObject("headers"); + JSObject params = call.getObject("params"); + Integer connectTimeout = call.getInt("connectTimeout"); + Integer readTimeout = call.getInt("readTimeout"); + Boolean disableRedirects = call.getBoolean("disableRedirects"); + Boolean shouldEncode = call.getBoolean("shouldEncodeUrlParams", true); + ResponseType responseType = ResponseType.parse(call.getString("responseType")); + + String method = httpMethod != null ? httpMethod.toUpperCase(Locale.ROOT) : call.getString("method", "GET").toUpperCase(Locale.ROOT); + + boolean isHttpMutate = method.equals("DELETE") || method.equals("PATCH") || method.equals("POST") || method.equals("PUT"); + + URL url = new URL(urlString); + HttpURLConnectionBuilder connectionBuilder = new HttpURLConnectionBuilder() + .setUrl(url) + .setMethod(method) + .setHeaders(headers) + .setUrlParams(params, shouldEncode) + .setConnectTimeout(connectTimeout) + .setReadTimeout(readTimeout) + .setDisableRedirects(disableRedirects) + .openConnection(); + + CapacitorHttpUrlConnection connection = connectionBuilder.build(); + + // Set HTTP body on a non GET or HEAD request + if (isHttpMutate) { + JSValue data = new JSValue(call, "data"); + if (data.getValue() != null) { + connection.setDoOutput(true); + connection.setRequestBody(call, data); + } + } + + connection.connect(); + + return buildResponse(connection, responseType); + } + + @FunctionalInterface + public interface ProgressEmitter { + void emit(Integer bytes, Integer contentLength); + } +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/ICapacitorHttpUrlConnection.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/ICapacitorHttpUrlConnection.java new file mode 100644 index 000000000..6d7cddd03 --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/ICapacitorHttpUrlConnection.java @@ -0,0 +1,15 @@ +package com.getcapacitor.plugin.util; + +import java.io.IOException; +import java.io.InputStream; + +/** + * This interface was extracted from {@link CapacitorHttpUrlConnection} to enable mocking that class. + */ +interface ICapacitorHttpUrlConnection { + InputStream getErrorStream(); + + String getHeaderField(String name); + + InputStream getInputStream() throws IOException; +} diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/MimeType.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/MimeType.java new file mode 100644 index 000000000..cfc90f824 --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/MimeType.java @@ -0,0 +1,17 @@ +package com.getcapacitor.plugin.util; + +enum MimeType { + APPLICATION_JSON("application/json"), + APPLICATION_VND_API_JSON("application/vnd.api+json"), // https://jsonapi.org + TEXT_HTML("text/html"); + + private final String value; + + MimeType(String value) { + this.value = value; + } + + String getValue() { + return value; + } +} diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index 697036b67..8bdbcf225 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -562,4 +562,32 @@ export interface PluginsConfig { * @since 4.2.0 */ LiveUpdates?: LiveUpdateConfig; + + /** + * Capacitor Cookies plugin configuration + * + * @since 4.3.0 + */ + CapacitorCookies?: { + /** + * Enable CapacitorCookies to override the global `document.cookie` on native. + * + * @default false + */ + enabled?: boolean; + }; + + /** + * Capacitor Http plugin configuration + * + * @since 4.3.0 + */ + CapacitorHttp?: { + /** + * Enable CapacitorHttp to override the global `fetch` and `XMLHttpRequest` on native. + * + * @default false + */ + enabled?: boolean; + }; } diff --git a/core/native-bridge.ts b/core/native-bridge.ts index 10b4ffd56..fdc6753dd 100644 --- a/core/native-bridge.ts +++ b/core/native-bridge.ts @@ -2,6 +2,7 @@ * Note: When making changes to this file, run `npm run build:nativebridge` * afterwards to build the nativebridge.js files to the android and iOS projects. */ +import type { HttpResponse } from './src/core-plugins'; import type { CallData, CapacitorInstance, @@ -287,6 +288,319 @@ const initBridge = (w: any): void => { return String(msg); }; + /** + * Safely web decode a string value (inspired by js-cookie) + * @param str The string value to decode + */ + const decode = (str: string): string => + str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); + + const platform = getPlatformId(win); + + if (platform == 'android' || platform == 'ios') { + // patch document.cookie on Android/iOS + win.CapacitorCookiesDescriptor = + Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || + Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); + + let doPatchCookies = false; + + // check if capacitor cookies is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor cookies config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + + const payload = { + type: 'CapacitorCookies.isEnabled', + }; + + const isCookiesEnabled = prompt(JSON.stringify(payload)); + if (isCookiesEnabled === 'true') { + doPatchCookies = true; + } + } else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + const isCookiesEnabled = + win.CapacitorCookiesAndroidInterface.isEnabled(); + if (isCookiesEnabled === true) { + doPatchCookies = true; + } + } + + if (doPatchCookies) { + Object.defineProperty(document, 'cookie', { + get: function () { + if (platform === 'ios') { + // Use prompt to synchronously get cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + + const payload = { + type: 'CapacitorCookies', + }; + + const res = prompt(JSON.stringify(payload)); + return res; + } else if ( + typeof win.CapacitorCookiesAndroidInterface !== 'undefined' + ) { + return win.CapacitorCookiesAndroidInterface.getCookies(); + } + }, + set: function (val) { + const cookiePairs = val.split(';'); + for (const cookiePair of cookiePairs) { + const cookieKey = cookiePair.split('=')[0]; + const cookieValue = cookiePair.split('=')[1]; + + if (null == cookieValue) { + continue; + } + + cap.toNative('CapacitorCookies', 'setCookie', { + key: cookieKey, + value: decode(cookieValue), + }); + } + }, + }); + } + + // patch fetch / XHR on Android/iOS + // store original fetch & XHR functions + win.CapacitorWebFetch = window.fetch; + win.CapacitorWebXMLHttpRequest = { + abort: window.XMLHttpRequest.prototype.abort, + open: window.XMLHttpRequest.prototype.open, + send: window.XMLHttpRequest.prototype.send, + setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, + }; + + let doPatchHttp = false; + + // check if capacitor http is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor http config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + + const payload = { + type: 'CapacitorHttp', + }; + + const isHttpEnabled = prompt(JSON.stringify(payload)); + if (isHttpEnabled === 'true') { + doPatchHttp = true; + } + } else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { + const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); + if (isHttpEnabled === true) { + doPatchHttp = true; + } + } + + if (doPatchHttp) { + // fetch patch + window.fetch = async ( + resource: RequestInfo | URL, + options?: RequestInit, + ) => { + if ( + resource.toString().startsWith('data:') || + resource.toString().startsWith('blob:') + ) { + return win.CapacitorWebFetch(resource, options); + } + + try { + // intercept request & pass to the bridge + const nativeResponse: HttpResponse = await cap.nativePromise( + 'CapacitorHttp', + 'request', + { + url: resource, + method: options?.method ? options.method : undefined, + data: options?.body ? options.body : undefined, + headers: options?.headers ? options.headers : undefined, + }, + ); + + const data = + typeof nativeResponse.data === 'string' + ? nativeResponse.data + : JSON.stringify(nativeResponse.data); + // intercept & parse response before returning + const response = new Response(data, { + headers: nativeResponse.headers, + status: nativeResponse.status, + }); + + return response; + } catch (error) { + return Promise.reject(error); + } + }; + + // XHR event listeners + const addEventListeners = function () { + this.addEventListener('abort', function () { + if (typeof this.onabort === 'function') this.onabort(); + }); + + this.addEventListener('error', function () { + if (typeof this.onerror === 'function') this.onerror(); + }); + + this.addEventListener('load', function () { + if (typeof this.onload === 'function') this.onload(); + }); + + this.addEventListener('loadend', function () { + if (typeof this.onloadend === 'function') this.onloadend(); + }); + + this.addEventListener('loadstart', function () { + if (typeof this.onloadstart === 'function') this.onloadstart(); + }); + + this.addEventListener('readystatechange', function () { + if (typeof this.onreadystatechange === 'function') + this.onreadystatechange(); + }); + + this.addEventListener('timeout', function () { + if (typeof this.ontimeout === 'function') this.ontimeout(); + }); + }; + + // XHR patch abort + window.XMLHttpRequest.prototype.abort = function () { + this.readyState = 0; + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }; + + // XHR patch open + window.XMLHttpRequest.prototype.open = function ( + method: string, + url: string, + ) { + Object.defineProperties(this, { + _headers: { + value: {}, + writable: true, + }, + readyState: { + get: function () { + return this._readyState ?? 0; + }, + set: function (val: number) { + this._readyState = val; + this.dispatchEvent(new Event('readystatechange')); + }, + }, + response: { + value: '', + writable: true, + }, + responseText: { + value: '', + writable: true, + }, + responseURL: { + value: '', + writable: true, + }, + status: { + value: 0, + writable: true, + }, + }); + addEventListeners.call(this); + this._method = method; + this._url = url; + this.readyState = 1; + }; + + // XHR patch set request header + window.XMLHttpRequest.prototype.setRequestHeader = function ( + header: string, + value: string, + ) { + this._headers[header] = value; + }; + + // XHR patch send + window.XMLHttpRequest.prototype.send = function ( + body?: Document | XMLHttpRequestBodyInit, + ) { + try { + this.readyState = 2; + + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: body !== null ? body : undefined, + headers: this._headers, + }) + .then((nativeResponse: any) => { + // intercept & parse response before returning + if (this.readyState == 2) { + this.dispatchEvent(new Event('loadstart')); + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + this.response = nativeResponse.data; + this.responseText = + typeof nativeResponse.data === 'string' + ? nativeResponse.data + : JSON.stringify(nativeResponse.data); + this.responseURL = nativeResponse.url; + this.readyState = 4; + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + } + }) + .catch((error: any) => { + this.dispatchEvent(new Event('loadstart')); + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; + this.readyState = 4; + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + } catch (error) { + this.dispatchEvent(new Event('loadstart')); + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + } + }; + + // XHR patch getAllResponseHeaders + window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; + } + } + return returnString; + }; + + // XHR patch getResponseHeader + window.XMLHttpRequest.prototype.getResponseHeader = function (name) { + return this._headers[name]; + }; + } + } + // patch window.console on iOS and store original console fns const isIos = getPlatformId(win) === 'ios'; if (win.console && isIos) { diff --git a/core/src/core-plugins.ts b/core/src/core-plugins.ts index beda630c0..bd0d5fa7a 100644 --- a/core/src/core-plugins.ts +++ b/core/src/core-plugins.ts @@ -1,6 +1,8 @@ import type { Plugin } from './definitions'; import { registerPlugin } from './global'; +import { WebPlugin } from './web-plugin'; +/******** WEB VIEW PLUGIN ********/ export interface WebViewPlugin extends Plugin { setServerBasePath(options: WebViewPath): Promise; getServerBasePath(): Promise; @@ -12,3 +14,404 @@ export interface WebViewPath { } export const WebView = /*#__PURE__*/ registerPlugin('WebView'); +/******** END WEB VIEW PLUGIN ********/ + +/******** COOKIES PLUGIN ********/ +/** + * Safely web encode a string value (inspired by js-cookie) + * @param str The string value to encode + */ +const encode = (str: string) => + encodeURIComponent(str) + .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent) + .replace(/[()]/g, escape); + +export interface CapacitorCookiesPlugin { + setCookie(options: SetCookieOptions): Promise; + deleteCookie(options: DeleteCookieOptions): Promise; + clearCookies(options: ClearCookieOptions): Promise; + clearAllCookies(): Promise; +} + +interface HttpCookie { + url?: string; + key: string; + value: string; +} + +interface HttpCookieExtras { + path?: string; + expires?: string; +} + +export type SetCookieOptions = HttpCookie & HttpCookieExtras; +export type DeleteCookieOptions = Omit; +export type ClearCookieOptions = Omit; + +export class CapacitorCookiesPluginWeb + extends WebPlugin + implements CapacitorCookiesPlugin +{ + async setCookie(options: SetCookieOptions): Promise { + try { + // Safely Encoded Key/Value + const encodedKey = encode(options.key); + const encodedValue = encode(options.value); + + // Clean & sanitize options + const expires = `; expires=${(options.expires || '').replace( + 'expires=', + '', + )}`; // Default is "; expires=" + + const path = (options.path || '/').replace('path=', ''); // Default is "path=/" + + document.cookie = `${encodedKey}=${ + encodedValue || '' + }${expires}; path=${path}`; + } catch (error) { + return Promise.reject(error); + } + } + + async deleteCookie(options: DeleteCookieOptions): Promise { + try { + document.cookie = `${options.key}=; Max-Age=0`; + } catch (error) { + return Promise.reject(error); + } + } + + async clearCookies(): Promise { + try { + const cookies = document.cookie.split(';') || []; + for (const cookie of cookies) { + document.cookie = cookie + .replace(/^ +/, '') + .replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`); + } + } catch (error) { + return Promise.reject(error); + } + } + + async clearAllCookies(): Promise { + try { + await this.clearCookies(); + } catch (error) { + return Promise.reject(error); + } + } +} + +export const CapacitorCookies = registerPlugin( + 'CapacitorCookies', + { + web: () => new CapacitorCookiesPluginWeb(), + }, +); + +/******** END COOKIES PLUGIN ********/ + +/******** HTTP PLUGIN ********/ +export interface CapacitorHttpPlugin { + request(options: HttpOptions): Promise; + get(options: HttpOptions): Promise; + post(options: HttpOptions): Promise; + put(options: HttpOptions): Promise; + patch(options: HttpOptions): Promise; + delete(options: HttpOptions): Promise; +} + +export type HttpResponseType = + | 'arraybuffer' + | 'blob' + | 'json' + | 'text' + | 'document'; + +export interface HttpOptions { + url: string; + method?: string; + params?: HttpParams; + data?: any; + headers?: HttpHeaders; + /** + * How long to wait to read additional data. Resets each time new + * data is received + */ + readTimeout?: number; + /** + * How long to wait for the initial connection. + */ + connectTimeout?: number; + /** + * Sets whether automatic HTTP redirects should be disabled + */ + disableRedirects?: boolean; + /** + * Extra arguments for fetch when running on the web + */ + webFetchExtra?: RequestInit; + /** + * This is used to parse the response appropriately before returning it to + * the requestee. If the response content-type is "json", this value is ignored. + */ + responseType?: HttpResponseType; + /** + * Use this option if you need to keep the URL unencoded in certain cases + * (already encoded, azure/firebase testing, etc.). The default is _true_. + */ + shouldEncodeUrlParams?: boolean; +} + +export interface HttpParams { + [key: string]: string | string[]; +} + +export interface HttpHeaders { + [key: string]: string; +} + +export interface HttpResponse { + data: any; + status: number; + headers: HttpHeaders; + url: string; +} + +// UTILITY FUNCTIONS + +/** + * Read in a Blob value and return it as a base64 string + * @param blob The blob value to convert to a base64 string + */ +export const readBlobAsBase64 = async (blob: Blob): Promise => + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + const base64String = reader.result as string; + // remove prefix "data:application/pdf;base64," + resolve( + base64String.indexOf(',') >= 0 + ? base64String.split(',')[1] + : base64String, + ); + }; + reader.onerror = (error: any) => reject(error); + reader.readAsDataURL(blob); + }); + +/** + * Normalize an HttpHeaders map by lowercasing all of the values + * @param headers The HttpHeaders object to normalize + */ +const normalizeHttpHeaders = (headers: HttpHeaders = {}): HttpHeaders => { + const originalKeys = Object.keys(headers); + const loweredKeys = Object.keys(headers).map(k => k.toLocaleLowerCase()); + const normalized = loweredKeys.reduce((acc, key, index) => { + acc[key] = headers[originalKeys[index]]; + return acc; + }, {}); + return normalized; +}; + +/** + * Builds a string of url parameters that + * @param params A map of url parameters + * @param shouldEncode true if you should encodeURIComponent() the values (true by default) + */ +const buildUrlParams = ( + params?: HttpParams, + shouldEncode = true, +): string | null => { + if (!params) return null; + + const output = Object.entries(params).reduce((accumulator, entry) => { + const [key, value] = entry; + + let encodedValue: string; + let item: string; + if (Array.isArray(value)) { + item = ''; + value.forEach(str => { + encodedValue = shouldEncode ? encodeURIComponent(str) : str; + item += `${key}=${encodedValue}&`; + }); + // last character will always be "&" so slice it off + item.slice(0, -1); + } else { + encodedValue = shouldEncode ? encodeURIComponent(value) : value; + item = `${key}=${encodedValue}`; + } + + return `${accumulator}&${item}`; + }, ''); + + // Remove initial "&" from the reduce + return output.substr(1); +}; + +/** + * Build the RequestInit object based on the options passed into the initial request + * @param options The Http plugin options + * @param extra Any extra RequestInit values + */ +export const buildRequestInit = ( + options: HttpOptions, + extra: RequestInit = {}, +): RequestInit => { + const output: RequestInit = { + method: options.method || 'GET', + headers: options.headers, + ...extra, + }; + + // Get the content-type + const headers = normalizeHttpHeaders(options.headers); + const type = headers['content-type'] || ''; + + // If body is already a string, then pass it through as-is. + if (typeof options.data === 'string') { + output.body = options.data; + } + // Build request initializers based off of content-type + else if (type.includes('application/x-www-form-urlencoded')) { + const params = new URLSearchParams(); + for (const [key, value] of Object.entries(options.data || {})) { + params.set(key, value as any); + } + output.body = params.toString(); + } else if (type.includes('multipart/form-data')) { + const form = new FormData(); + if (options.data instanceof FormData) { + options.data.forEach((value, key) => { + form.append(key, value); + }); + } else { + for (const key of Object.keys(options.data)) { + form.append(key, options.data[key]); + } + } + output.body = form; + const headers = new Headers(output.headers); + headers.delete('content-type'); // content-type will be set by `window.fetch` to includy boundary + output.headers = headers; + } else if ( + type.includes('application/json') || + typeof options.data === 'object' + ) { + output.body = JSON.stringify(options.data); + } + + return output; +}; + +// WEB IMPLEMENTATION +export class CapacitorHttpPluginWeb + extends WebPlugin + implements CapacitorHttpPlugin +{ + /** + * Perform an Http request given a set of options + * @param options Options to build the HTTP request + */ + async request(options: HttpOptions): Promise { + const requestInit = buildRequestInit(options, options.webFetchExtra); + const urlParams = buildUrlParams( + options.params, + options.shouldEncodeUrlParams, + ); + const url = urlParams ? `${options.url}?${urlParams}` : options.url; + + const response = await fetch(url, requestInit); + const contentType = response.headers.get('content-type') || ''; + + // Default to 'text' responseType so no parsing happens + let { responseType = 'text' } = response.ok ? options : {}; + + // If the response content-type is json, force the response to be json + if (contentType.includes('application/json')) { + responseType = 'json'; + } + + let data: any; + let blob: any; + switch (responseType) { + case 'arraybuffer': + case 'blob': + blob = await response.blob(); + data = await readBlobAsBase64(blob); + break; + case 'json': + data = await response.json(); + break; + case 'document': + case 'text': + default: + data = await response.text(); + } + + // Convert fetch headers to Capacitor HttpHeaders + const headers = {} as HttpHeaders; + response.headers.forEach((value: string, key: string) => { + headers[key] = value; + }); + + return { + data, + headers, + status: response.status, + url: response.url, + }; + } + + /** + * Perform an Http GET request given a set of options + * @param options Options to build the HTTP request + */ + async get(options: HttpOptions): Promise { + return this.request({ ...options, method: 'GET' }); + } + + /** + * Perform an Http POST request given a set of options + * @param options Options to build the HTTP request + */ + async post(options: HttpOptions): Promise { + return this.request({ ...options, method: 'POST' }); + } + + /** + * Perform an Http PUT request given a set of options + * @param options Options to build the HTTP request + */ + async put(options: HttpOptions): Promise { + return this.request({ ...options, method: 'PUT' }); + } + + /** + * Perform an Http PATCH request given a set of options + * @param options Options to build the HTTP request + */ + async patch(options: HttpOptions): Promise { + return this.request({ ...options, method: 'PATCH' }); + } + + /** + * Perform an Http DELETE request given a set of options + * @param options Options to build the HTTP request + */ + async delete(options: HttpOptions): Promise { + return this.request({ ...options, method: 'DELETE' }); + } +} + +export const CapacitorHttp = registerPlugin( + 'CapacitorHttp', + { + web: () => new CapacitorHttpPluginWeb(), + }, +); + +/******** END HTTP PLUGIN ********/ diff --git a/core/src/definitions-internal.ts b/core/src/definitions-internal.ts index 6137d3dec..2f058dffa 100644 --- a/core/src/definitions-internal.ts +++ b/core/src/definitions-internal.ts @@ -172,6 +172,11 @@ export interface CapacitorCustomPlatformInstance { export interface WindowCapacitor { Capacitor?: CapacitorInstance; + CapacitorCookiesAndroidInterface?: any; + CapacitorCookiesDescriptor?: PropertyDescriptor; + CapacitorHttpAndroidInterface?: any; + CapacitorWebFetch?: any; + CapacitorWebXMLHttpRequest?: any; /** * @deprecated Use `CapacitorCustomPlatform` instead */ diff --git a/core/src/index.ts b/core/src/index.ts index 303012a5c..e874a7294 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -20,10 +20,21 @@ export { Capacitor, registerPlugin } from './global'; export { WebPlugin, WebPluginConfig, ListenerCallback } from './web-plugin'; // Core Plugins APIs -export { WebView } from './core-plugins'; +export { CapacitorCookies, CapacitorHttp, WebView } from './core-plugins'; // Core Plugin definitions -export type { WebViewPath, WebViewPlugin } from './core-plugins'; +export type { + ClearCookieOptions, + DeleteCookieOptions, + SetCookieOptions, + HttpHeaders, + HttpOptions, + HttpParams, + HttpResponse, + HttpResponseType, + WebViewPath, + WebViewPlugin, +} from './core-plugins'; // Constants export { CapacitorException, ExceptionCode } from './util'; diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index 8fb7f7ec1..825395624 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -82,6 +82,11 @@ 62FABD1A25AE5C01007B3814 /* Array+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */; }; 62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */; }; 62FABD2B25AE6182007B3814 /* BridgedTypesHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */; }; + A327E6B628DB8B2900CA8B0A /* HttpRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */; }; + A327E6B728DB8B2900CA8B0A /* CapacitorHttp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */; }; + A327E6B828DB8B2900CA8B0A /* CapacitorUrlRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */; }; + A38C3D7728484E76004B3680 /* CapacitorCookies.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38C3D7628484E76004B3680 /* CapacitorCookies.swift */; }; + A38C3D7B2848BE6F004B3680 /* CapacitorCookieManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */; }; A71289E627F380A500DADDF3 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289E527F380A500DADDF3 /* Router.swift */; }; A71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289EA27F380FD00DADDF3 /* RouterTests.swift */; }; /* End PBXBuildFile section */ @@ -213,6 +218,11 @@ 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Capacitor.swift"; sourceTree = ""; }; 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BridgedTypesTests.m; sourceTree = ""; }; 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgedTypesHelper.swift; sourceTree = ""; }; + A327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpRequestHandler.swift; sourceTree = ""; }; + A327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorHttp.swift; sourceTree = ""; }; + A327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CapacitorUrlRequest.swift; sourceTree = ""; }; + A38C3D7628484E76004B3680 /* CapacitorCookies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorCookies.swift; sourceTree = ""; }; + A38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapacitorCookieManager.swift; sourceTree = ""; }; A71289E527F380A500DADDF3 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; A71289EA27F380FD00DADDF3 /* RouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -359,9 +369,14 @@ 62959AEA2524DA7700A3D7F1 /* Plugins */ = { isa = PBXGroup; children = ( + A327E6B428DB8B2900CA8B0A /* CapacitorHttp.swift */, + A327E6B528DB8B2900CA8B0A /* CapacitorUrlRequest.swift */, + A327E6B228DB8B2800CA8B0A /* HttpRequestHandler.swift */, 62959AEF2524DA7700A3D7F1 /* Console.swift */, 62959AFD2524DA7700A3D7F1 /* DefaultPlugins.m */, 62959AF32524DA7700A3D7F1 /* WebView.swift */, + A38C3D7628484E76004B3680 /* CapacitorCookies.swift */, + A38C3D7A2848BE6F004B3680 /* CapacitorCookieManager.swift */, ); path = Plugins; sourceTree = ""; @@ -583,7 +598,9 @@ buildActionMask = 2147483647; files = ( 55F6736A26C371E6001E7AB9 /* CAPWebView.swift in Sources */, + A38C3D7728484E76004B3680 /* CapacitorCookies.swift in Sources */, A71289E627F380A500DADDF3 /* Router.swift in Sources */, + A327E6B828DB8B2900CA8B0A /* CapacitorUrlRequest.swift in Sources */, 62959B362524DA7800A3D7F1 /* CAPBridgeViewController.swift in Sources */, 621ECCB72542045900D3D615 /* CAPBridgedJSTypes.m in Sources */, 62959B402524DA7800A3D7F1 /* TmpViewController.swift in Sources */, @@ -595,6 +612,7 @@ 621ECCDA254205C400D3D615 /* CapacitorBridge.swift in Sources */, 62959B382524DA7800A3D7F1 /* CAPPluginCall.m in Sources */, 623D690A254C6FDF002D01D1 /* CAPInstanceDescriptor.m in Sources */, + A38C3D7B2848BE6F004B3680 /* CapacitorCookieManager.swift in Sources */, 62959B1B2524DA7800A3D7F1 /* CAPFile.swift in Sources */, 62959B462524DA7800A3D7F1 /* CAPBridge.swift in Sources */, 62959B1D2524DA7800A3D7F1 /* UIColor.swift in Sources */, @@ -609,12 +627,14 @@ 62959B1A2524DA7800A3D7F1 /* CAPPluginCall.swift in Sources */, 62959B302524DA7800A3D7F1 /* UIStatusBarManager+CAPHandleTapAction.m in Sources */, 62959B392524DA7800A3D7F1 /* CapacitorExtension.swift in Sources */, + A327E6B628DB8B2900CA8B0A /* HttpRequestHandler.swift in Sources */, 62959B422524DA7800A3D7F1 /* DocLinks.swift in Sources */, 62FABD1A25AE5C01007B3814 /* Array+Capacitor.swift in Sources */, 62959B172524DA7800A3D7F1 /* JSExport.swift in Sources */, 373A69C1255C9360000A6F44 /* NotificationHandlerProtocol.swift in Sources */, 0F83E885285A332E006C43CB /* AppUUID.swift in Sources */, 625AF1ED258963C700869675 /* WebViewAssetHandler.swift in Sources */, + A327E6B728DB8B2900CA8B0A /* CapacitorHttp.swift in Sources */, 0F8F33B327DA980A003F49D6 /* PluginConfig.swift in Sources */, 62959B3C2524DA7800A3D7F1 /* CAPBridgeDelegate.swift in Sources */, 62959B2F2524DA7800A3D7F1 /* DefaultPlugins.m in Sources */, diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift b/ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift new file mode 100644 index 000000000..9249ebd67 --- /dev/null +++ b/ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift @@ -0,0 +1,66 @@ +import Foundation + +public class CapacitorCookieManager { + var config: InstanceConfiguration? + + init(_ capConfig: InstanceConfiguration?) { + self.config = capConfig + } + + public func getServerUrl() -> URL? { + return self.config?.serverURL + } + + public func getServerUrl(_ call: CAPPluginCall) -> URL? { + guard let urlString = call.getString("url") else { + return getServerUrl() + } + + guard let url = URL(string: urlString) else { + return getServerUrl() + } + + return url + } + + public func encode(_ value: String) -> String { + return value.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)! + } + + public func decode(_ value: String) -> String { + return value.removingPercentEncoding! + } + + public func setCookie(_ url: URL, _ key: String, _ value: String) { + let jar = HTTPCookieStorage.shared + let field = ["Set-Cookie": "\(key)=\(value)"] + let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url) + jar.setCookies(cookies, for: url, mainDocumentURL: url) + } + + public func getCookies() -> String { + let jar = HTTPCookieStorage.shared + guard let url = self.getServerUrl() else { return "" } + guard let cookies = jar.cookies(for: url) else { return "" } + return cookies.map({"\($0.name)=\($0.value)"}).joined(separator: ";") + } + + public func deleteCookie(_ url: URL, _ key: String) { + let jar = HTTPCookieStorage.shared + let cookie = jar.cookies(for: url)?.first(where: { (cookie) -> Bool in + return cookie.name == key + }) + + if cookie != nil { jar.deleteCookie(cookie!) } + } + + public func clearCookies(_ url: URL) { + let jar = HTTPCookieStorage.shared + jar.cookies(for: url)?.forEach({ (cookie) in jar.deleteCookie(cookie) }) + } + + public func clearAllCookies() { + let jar = HTTPCookieStorage.shared + jar.cookies?.forEach({ (cookie) in jar.deleteCookie(cookie) }) + } +} diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorCookies.swift b/ios/Capacitor/Capacitor/Plugins/CapacitorCookies.swift new file mode 100644 index 000000000..b53f28fe9 --- /dev/null +++ b/ios/Capacitor/Capacitor/Plugins/CapacitorCookies.swift @@ -0,0 +1,52 @@ +import Foundation + +@objc(CAPCookiesPlugin) +public class CAPCookiesPlugin: CAPPlugin { + var cookieManager: CapacitorCookieManager? + + @objc override public func load() { + cookieManager = CapacitorCookieManager(bridge?.config) + } + + @objc func setCookie(_ call: CAPPluginCall) { + guard let key = call.getString("key") else { return call.reject("Must provide key") } + guard let value = call.getString("value") else { return call.reject("Must provide value") } + + let url = cookieManager!.getServerUrl(call) + if url != nil { + cookieManager!.setCookie(url!, key, cookieManager!.encode(value)) + call.resolve() + } + } + + @objc func deleteCookie(_ call: CAPPluginCall) { + guard let key = call.getString("key") else { return call.reject("Must provide key") } + let url = cookieManager!.getServerUrl(call) + if url != nil { + let jar = HTTPCookieStorage.shared + + let cookie = jar.cookies(for: url!)?.first(where: { (cookie) -> Bool in + return cookie.name == key + }) + + if cookie != nil { + jar.deleteCookie(cookie!) + } + + call.resolve() + } + } + + @objc func clearCookies(_ call: CAPPluginCall) { + let url = cookieManager!.getServerUrl(call) + if url != nil { + cookieManager!.clearCookies(url!) + call.resolve() + } + } + + @objc func clearAllCookies(_ call: CAPPluginCall) { + cookieManager!.clearAllCookies() + call.resolve() + } +} diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorHttp.swift b/ios/Capacitor/Capacitor/Plugins/CapacitorHttp.swift new file mode 100644 index 000000000..6ba1f69b5 --- /dev/null +++ b/ios/Capacitor/Capacitor/Plugins/CapacitorHttp.swift @@ -0,0 +1,40 @@ +import Foundation + +@objc(CAPHttpPlugin) +public class CAPHttpPlugin: CAPPlugin { + @objc func http(_ call: CAPPluginCall, _ httpMethod: String?) { + // Protect against bad values from JS before calling request + guard let url = call.getString("url") else { return call.reject("Must provide a URL"); } + guard var _ = URL(string: url) else { return call.reject("Invalid URL"); } + + do { + try HttpRequestHandler.request(call, httpMethod) + } catch let error { + call.reject(error.localizedDescription) + } + } + + @objc func request(_ call: CAPPluginCall) { + http(call, nil) + } + + @objc func get(_ call: CAPPluginCall) { + http(call, "GET") + } + + @objc func post(_ call: CAPPluginCall) { + http(call, "POST") + } + + @objc func put(_ call: CAPPluginCall) { + http(call, "PUT") + } + + @objc func patch(_ call: CAPPluginCall) { + http(call, "PATCH") + } + + @objc func del(_ call: CAPPluginCall) { + http(call, "DELETE") + } +} diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift b/ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift new file mode 100644 index 000000000..745e11698 --- /dev/null +++ b/ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift @@ -0,0 +1,150 @@ +import Foundation + +public class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate { + private var request: URLRequest + private var headers: [String: String] + + enum CapacitorUrlRequestError: Error { + case serializationError(String?) + } + + init(_ url: URL, method: String) { + request = URLRequest(url: url) + request.httpMethod = method + headers = [:] + if let lang = Locale.autoupdatingCurrent.languageCode { + if let country = Locale.autoupdatingCurrent.regionCode { + headers["Accept-Language"] = "\(lang)-\(country),\(lang);q=0.5" + } else { + headers["Accept-Language"] = "\(lang);q=0.5" + } + request.addValue(headers["Accept-Language"]!, forHTTPHeaderField: "Accept-Language") + } + } + + private func getRequestDataAsJson(_ data: JSValue) throws -> Data? { + // We need to check if the JSON is valid before attempting to serialize, as JSONSerialization.data will not throw an exception that can be caught, and will cause the application to crash if it fails. + if JSONSerialization.isValidJSONObject(data) { + return try JSONSerialization.data(withJSONObject: data) + } else { + throw CapacitorUrlRequest.CapacitorUrlRequestError.serializationError("[ data ] argument for request of content-type [ application/json ] must be serializable to JSON") + } + } + + private func getRequestDataAsFormUrlEncoded(_ data: JSValue) throws -> Data? { + guard var components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false) else { return nil } + components.queryItems = [] + + guard let obj = data as? JSObject else { + // Throw, other data types explicitly not supported + throw CapacitorUrlRequestError.serializationError("[ data ] argument for request with content-type [ multipart/form-data ] may only be a plain javascript object") + } + + obj.keys.forEach { (key: String) in + components.queryItems?.append(URLQueryItem(name: key, value: "\(obj[key] ?? "")")) + } + + if components.query != nil { + return Data(components.query!.utf8) + } + + return nil + } + + private func getRequestDataAsMultipartFormData(_ data: JSValue) throws -> Data { + guard let obj = data as? JSObject else { + // Throw, other data types explicitly not supported. + throw CapacitorUrlRequestError.serializationError("[ data ] argument for request with content-type [ application/x-www-form-urlencoded ] may only be a plain javascript object") + } + + let strings: [String: String] = obj.compactMapValues { any in + any as? String + } + + var data = Data() + let boundary = UUID().uuidString + let contentType = "multipart/form-data; boundary=\(boundary)" + request.setValue(contentType, forHTTPHeaderField: "Content-Type") + headers["Content-Type"] = contentType + + strings.forEach { key, value in + data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!) + data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!) + data.append(value.data(using: .utf8)!) + } + data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!) + + return data + } + + private func getRequestDataAsString(_ data: JSValue) throws -> Data { + guard let stringData = data as? String else { + throw CapacitorUrlRequestError.serializationError("[ data ] argument could not be parsed as string") + } + return Data(stringData.utf8) + } + + func getRequestHeader(_ index: String) -> Any? { + var normalized = [:] as [String: Any] + self.headers.keys.forEach { (key: String) in + normalized[key.lowercased()] = self.headers[key] + } + + return normalized[index.lowercased()] + } + + func getRequestData(_ body: JSValue, _ contentType: String) throws -> Data? { + // If data can be parsed directly as a string, return that without processing. + if let strVal = try? getRequestDataAsString(body) { + return strVal + } else if contentType.contains("application/json") { + return try getRequestDataAsJson(body) + } else if contentType.contains("application/x-www-form-urlencoded") { + return try getRequestDataAsFormUrlEncoded(body) + } else if contentType.contains("multipart/form-data") { + return try getRequestDataAsMultipartFormData(body) + } else { + throw CapacitorUrlRequestError.serializationError("[ data ] argument could not be parsed for content type [ \(contentType) ]") + } + } + + public func setRequestHeaders(_ headers: [String: String]) { + headers.keys.forEach { (key: String) in + let value = headers[key] + request.addValue(value!, forHTTPHeaderField: key) + self.headers[key] = value + } + } + + public func setRequestBody(_ body: JSValue) throws { + let contentType = self.getRequestHeader("Content-Type") as? String + + if contentType != nil { + request.httpBody = try getRequestData(body, contentType!) + } + } + + public func setContentType(_ data: String?) { + request.setValue(data, forHTTPHeaderField: "Content-Type") + } + + public func setTimeout(_ timeout: TimeInterval) { + request.timeoutInterval = timeout + } + + public func getUrlRequest() -> URLRequest { + return request + } + + public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { + completionHandler(nil) + } + + public func getUrlSession(_ call: CAPPluginCall) -> URLSession { + let disableRedirects = call.getBool("disableRedirects") ?? false + if !disableRedirects { + return URLSession.shared + } + return URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil) + } +} diff --git a/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.m b/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.m index 21abbef22..3f9873714 100644 --- a/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.m +++ b/ios/Capacitor/Capacitor/Plugins/DefaultPlugins.m @@ -2,6 +2,22 @@ #import "CAPBridgedPlugin.h" +CAP_PLUGIN(CAPCookiesPlugin, "CapacitorCookies", + CAP_PLUGIN_METHOD(setCookie, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(deleteCookie, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(clearCookies, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(clearAllCookies, CAPPluginReturnPromise); +) + +CAP_PLUGIN(CAPHttpPlugin, "CapacitorHttp", + CAP_PLUGIN_METHOD(request, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(get, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(post, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(put, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(patch, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(delete, CAPPluginReturnPromise); +) + CAP_PLUGIN(CAPConsolePlugin, "Console", CAP_PLUGIN_METHOD(log, CAPPluginReturnNone); ) diff --git a/ios/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift b/ios/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift new file mode 100644 index 000000000..06b96ae31 --- /dev/null +++ b/ios/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift @@ -0,0 +1,174 @@ +import Foundation + +/// See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType +private enum ResponseType: String { + case arrayBuffer = "arraybuffer" + case blob = "blob" + case document = "document" + case json = "json" + case text = "text" + + static let `default`: ResponseType = .text + + init(string: String?) { + guard let string = string else { + self = .default + return + } + + guard let responseType = ResponseType(rawValue: string.lowercased()) else { + self = .default + return + } + + self = responseType + } +} + +/// Helper that safely parses JSON Data. Otherwise returns an error (without throwing) +/// - Parameters: +/// - data: The JSON Data to parse +/// - Returns: The parsed value or an error +func tryParseJson(_ data: Data) -> Any { + do { + return try JSONSerialization.jsonObject(with: data, options: .mutableContainers) + } catch { + return error.localizedDescription + } +} + +class HttpRequestHandler { + private class CapacitorHttpRequestBuilder { + private var url: URL? + private var method: String? + private var params: [String: String]? + private var request: CapacitorUrlRequest? + + /// Set the URL of the HttpRequest + /// - Throws: an error of URLError if the urlString cannot be parsed + /// - Parameters: + /// - urlString: The URL value to parse + /// - Returns: self to continue chaining functions + public func setUrl(_ urlString: String) throws -> CapacitorHttpRequestBuilder { + guard let url = URL(string: urlString) else { + throw URLError(.badURL) + } + self.url = url + return self + } + + public func setMethod(_ method: String) -> CapacitorHttpRequestBuilder { + self.method = method + return self + } + + public func setUrlParams(_ params: [String: Any]) -> CapacitorHttpRequestBuilder { + if params.count != 0 { + // swiftlint:disable force_cast + var cmps = URLComponents(url: url!, resolvingAgainstBaseURL: true) + if cmps?.queryItems == nil { + cmps?.queryItems = [] + } + + var urlSafeParams: [URLQueryItem] = [] + for (key, value) in params { + if let arr = value as? [String] { + arr.forEach { str in + urlSafeParams.append(URLQueryItem(name: key, value: str)) + } + } else { + urlSafeParams.append(URLQueryItem(name: key, value: (value as! String))) + } + } + + cmps!.queryItems?.append(contentsOf: urlSafeParams) + url = cmps!.url! + } + return self + } + + public func openConnection() -> CapacitorHttpRequestBuilder { + request = CapacitorUrlRequest(url!, method: method!) + return self + } + + public func build() -> CapacitorUrlRequest { + return request! + } + } + + private static func buildResponse(_ data: Data?, _ response: HTTPURLResponse, responseType: ResponseType = .default) -> [String: Any] { + var output = [:] as [String: Any] + + output["status"] = response.statusCode + output["headers"] = response.allHeaderFields + output["url"] = response.url?.absoluteString + + guard let data = data else { + output["data"] = "" + return output + } + + let contentType = (response.allHeaderFields["Content-Type"] as? String ?? "application/default").lowercased() + + if contentType.contains("application/json") || responseType == .json { + output["data"] = tryParseJson(data) + } else if responseType == .arrayBuffer || responseType == .blob { + output["data"] = data.base64EncodedString() + } else if responseType == .document || responseType == .text || responseType == .default { + output["data"] = String(data: data, encoding: .utf8) + } + + return output + } + + public static func request(_ call: CAPPluginCall, _ httpMethod: String?) throws { + guard let urlString = call.getString("url") else { throw URLError(.badURL) } + let method = httpMethod ?? call.getString("method", "GET") + + // swiftlint:disable force_cast + let headers = (call.getObject("headers") ?? [:]) as! [String: String] + let params = (call.getObject("params") ?? [:]) as [String: Any] + let responseType = call.getString("responseType") ?? "text" + let connectTimeout = call.getDouble("connectTimeout") + let readTimeout = call.getDouble("readTimeout") + + let request = try CapacitorHttpRequestBuilder() + .setUrl(urlString) + .setMethod(method) + .setUrlParams(params) + .openConnection() + .build() + + request.setRequestHeaders(headers) + + // Timeouts in iOS are in seconds. So read the value in millis and divide by 1000 + let timeout = (connectTimeout ?? readTimeout ?? 600000.0) / 1000.0 + request.setTimeout(timeout) + + if let data = call.options["data"] as? JSValue { + do { + try request.setRequestBody(data) + } catch { + // Explicitly reject if the http request body was not set successfully, + // so as to not send a known malformed request, and to provide the developer with additional context. + call.reject("Error", "REQUEST", error, [:]) + return + } + } + + let urlRequest = request.getUrlRequest() + let urlSession = request.getUrlSession(call) + let task = urlSession.dataTask(with: urlRequest) { (data, response, error) in + urlSession.invalidateAndCancel() + if error != nil { + return + } + + let type = ResponseType(rawValue: responseType) ?? .default + call.resolve(self.buildResponse(data, response as! HTTPURLResponse, responseType: type)) + } + + task.resume() + } +} diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift index 4ec797bc6..cff1b8d7c 100644 --- a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift @@ -243,6 +243,34 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel } public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { + + // Check if this is synchronous cookie or http call + do { + if let dataFromString = prompt.data(using: .utf8, allowLossyConversion: false) { + if let payload = try JSONSerialization.jsonObject(with: dataFromString, options: .fragmentsAllowed) as? [String: AnyObject] { + let type = payload["type"] as? String + + if type == "CapacitorCookies" { + completionHandler(CapacitorCookieManager(bridge!.config).getCookies()) + // Don't present prompt + return + } else if type == "CapacitorCookies.isEnabled" { + let pluginConfig = bridge!.config.getPluginConfig("CapacitorCookies") + completionHandler(String(pluginConfig.getBoolean("enabled", false))) + // Don't present prompt + return + } else if type == "CapacitorHttp" { + let pluginConfig = bridge!.config.getPluginConfig("CapacitorHttp") + completionHandler(String(pluginConfig.getBoolean("enabled", false))) + // Don't present prompt + return + } + } + } + } catch { + // Continue with regular prompt + } + guard let viewController = bridge?.viewController else { return } diff --git a/ios/Capacitor/Capacitor/assets/native-bridge.js b/ios/Capacitor/Capacitor/assets/native-bridge.js index 269b59ae1..dbed700c4 100644 --- a/ios/Capacitor/Capacitor/assets/native-bridge.js +++ b/ios/Capacitor/Capacitor/assets/native-bridge.js @@ -2,7 +2,7 @@ /*! Capacitor: https://capacitorjs.com/ - MIT License */ /* Generated File. Do not edit. */ -var nativeBridge = (function (exports) { +const nativeBridge = (function (exports) { 'use strict'; var ExceptionCode; @@ -268,6 +268,274 @@ var nativeBridge = (function (exports) { } return String(msg); }; + /** + * Safely web decode a string value (inspired by js-cookie) + * @param str The string value to decode + */ + const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); + const platform = getPlatformId(win); + if (platform == 'android' || platform == 'ios') { + // patch document.cookie on Android/iOS + win.CapacitorCookiesDescriptor = + Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') || + Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie'); + let doPatchCookies = false; + // check if capacitor cookies is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor cookies config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.isEnabled', + }; + const isCookiesEnabled = prompt(JSON.stringify(payload)); + if (isCookiesEnabled === 'true') { + doPatchCookies = true; + } + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled(); + if (isCookiesEnabled === true) { + doPatchCookies = true; + } + } + if (doPatchCookies) { + Object.defineProperty(document, 'cookie', { + get: function () { + if (platform === 'ios') { + // Use prompt to synchronously get cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies', + }; + const res = prompt(JSON.stringify(payload)); + return res; + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + return win.CapacitorCookiesAndroidInterface.getCookies(); + } + }, + set: function (val) { + const cookiePairs = val.split(';'); + for (const cookiePair of cookiePairs) { + const cookieKey = cookiePair.split('=')[0]; + const cookieValue = cookiePair.split('=')[1]; + if (null == cookieValue) { + continue; + } + cap.toNative('CapacitorCookies', 'setCookie', { + key: cookieKey, + value: decode(cookieValue), + }); + } + }, + }); + } + // patch fetch / XHR on Android/iOS + // store original fetch & XHR functions + win.CapacitorWebFetch = window.fetch; + win.CapacitorWebXMLHttpRequest = { + abort: window.XMLHttpRequest.prototype.abort, + open: window.XMLHttpRequest.prototype.open, + send: window.XMLHttpRequest.prototype.send, + setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, + }; + let doPatchHttp = false; + // check if capacitor http is disabled before patching + if (platform === 'ios') { + // Use prompt to synchronously get capacitor http config. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorHttp', + }; + const isHttpEnabled = prompt(JSON.stringify(payload)); + if (isHttpEnabled === 'true') { + doPatchHttp = true; + } + } + else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') { + const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled(); + if (isHttpEnabled === true) { + doPatchHttp = true; + } + } + if (doPatchHttp) { + // fetch patch + window.fetch = async (resource, options) => { + if (resource.toString().startsWith('data:') || + resource.toString().startsWith('blob:')) { + return win.CapacitorWebFetch(resource, options); + } + try { + // intercept request & pass to the bridge + const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', { + url: resource, + method: (options === null || options === void 0 ? void 0 : options.method) ? options.method : undefined, + data: (options === null || options === void 0 ? void 0 : options.body) ? options.body : undefined, + headers: (options === null || options === void 0 ? void 0 : options.headers) ? options.headers : undefined, + }); + const data = typeof nativeResponse.data === 'string' + ? nativeResponse.data + : JSON.stringify(nativeResponse.data); + // intercept & parse response before returning + const response = new Response(data, { + headers: nativeResponse.headers, + status: nativeResponse.status, + }); + return response; + } + catch (error) { + return Promise.reject(error); + } + }; + // XHR event listeners + const addEventListeners = function () { + this.addEventListener('abort', function () { + if (typeof this.onabort === 'function') + this.onabort(); + }); + this.addEventListener('error', function () { + if (typeof this.onerror === 'function') + this.onerror(); + }); + this.addEventListener('load', function () { + if (typeof this.onload === 'function') + this.onload(); + }); + this.addEventListener('loadend', function () { + if (typeof this.onloadend === 'function') + this.onloadend(); + }); + this.addEventListener('loadstart', function () { + if (typeof this.onloadstart === 'function') + this.onloadstart(); + }); + this.addEventListener('readystatechange', function () { + if (typeof this.onreadystatechange === 'function') + this.onreadystatechange(); + }); + this.addEventListener('timeout', function () { + if (typeof this.ontimeout === 'function') + this.ontimeout(); + }); + }; + // XHR patch abort + window.XMLHttpRequest.prototype.abort = function () { + this.readyState = 0; + this.dispatchEvent(new Event('abort')); + this.dispatchEvent(new Event('loadend')); + }; + // XHR patch open + window.XMLHttpRequest.prototype.open = function (method, url) { + Object.defineProperties(this, { + _headers: { + value: {}, + writable: true, + }, + readyState: { + get: function () { + var _a; + return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0; + }, + set: function (val) { + this._readyState = val; + this.dispatchEvent(new Event('readystatechange')); + }, + }, + response: { + value: '', + writable: true, + }, + responseText: { + value: '', + writable: true, + }, + responseURL: { + value: '', + writable: true, + }, + status: { + value: 0, + writable: true, + }, + }); + addEventListeners.call(this); + this._method = method; + this._url = url; + this.readyState = 1; + }; + // XHR patch set request header + window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) { + this._headers[header] = value; + }; + // XHR patch send + window.XMLHttpRequest.prototype.send = function (body) { + try { + this.readyState = 2; + // intercept request & pass to the bridge + cap + .nativePromise('CapacitorHttp', 'request', { + url: this._url, + method: this._method, + data: body !== null ? body : undefined, + headers: this._headers, + }) + .then((nativeResponse) => { + // intercept & parse response before returning + if (this.readyState == 2) { + this.dispatchEvent(new Event('loadstart')); + this._headers = nativeResponse.headers; + this.status = nativeResponse.status; + this.response = nativeResponse.data; + this.responseText = + typeof nativeResponse.data === 'string' + ? nativeResponse.data + : JSON.stringify(nativeResponse.data); + this.responseURL = nativeResponse.url; + this.readyState = 4; + this.dispatchEvent(new Event('load')); + this.dispatchEvent(new Event('loadend')); + } + }) + .catch((error) => { + this.dispatchEvent(new Event('loadstart')); + this.status = error.status; + this._headers = error.headers; + this.response = error.data; + this.responseText = JSON.stringify(error.data); + this.responseURL = error.url; + this.readyState = 4; + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + }); + } + catch (error) { + this.dispatchEvent(new Event('loadstart')); + this.status = 500; + this._headers = {}; + this.response = error; + this.responseText = error.toString(); + this.responseURL = this._url; + this.readyState = 4; + this.dispatchEvent(new Event('error')); + this.dispatchEvent(new Event('loadend')); + } + }; + // XHR patch getAllResponseHeaders + window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { + let returnString = ''; + for (const key in this._headers) { + if (key != 'Set-Cookie') { + returnString += key + ': ' + this._headers[key] + '\r\n'; + } + } + return returnString; + }; + // XHR patch getResponseHeader + window.XMLHttpRequest.prototype.getResponseHeader = function (name) { + return this._headers[name]; + }; + } + } // patch window.console on iOS and store original console fns const isIos = getPlatformId(win) === 'ios'; if (win.console && isIos) { From 3d4433b505252d263784ec0fafc72a9b7cd8591e Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of ItsChaceD)" Date: Wed, 21 Sep 2022 21:54:09 +0000 Subject: [PATCH 171/179] Release 4.3.0 --- CHANGELOG.md | 23 +++++++++++++++++++++++ android/CHANGELOG.md | 19 +++++++++++++++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 19 +++++++++++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 16 ++++++++++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 16 ++++++++++++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 100 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe39d1bb..ef64c707d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) + + +### Bug Fixes + +* **android:** open external links in browser ([#5913](https://github.com/ionic-team/capacitor/issues/5913)) ([7553ede](https://github.com/ionic-team/capacitor/commit/7553ede93170971e21ab3dec1798443d084ead2a)) +* **android:** set all cookies on proxied requests ([#5781](https://github.com/ionic-team/capacitor/issues/5781)) ([5ef6a38](https://github.com/ionic-team/capacitor/commit/5ef6a3889121dd39a9159ff80250df18854bc557)) +* **android:** set WebViewClient on the WebView ([#5919](https://github.com/ionic-team/capacitor/issues/5919)) ([020ed8e](https://github.com/ionic-team/capacitor/commit/020ed8eaeb7864399d4b93f54ab7601c607d8e0d)) +* **cli:** Find the Info.plist when using scheme ([#5914](https://github.com/ionic-team/capacitor/issues/5914)) ([f7029ac](https://github.com/ionic-team/capacitor/commit/f7029acb885ec60f85a434b6f71e4f2a633c7651)) +* **cli:** Make migrator update gradle wrapper files ([#5910](https://github.com/ionic-team/capacitor/issues/5910)) ([b8b9b1f](https://github.com/ionic-team/capacitor/commit/b8b9b1f96249908435017eea6c427221f1971836)) +* **cli:** Make update from windows use proper paths on Podfile ([#5906](https://github.com/ionic-team/capacitor/issues/5906)) ([c41d28f](https://github.com/ionic-team/capacitor/commit/c41d28f8cc829c6bf69d776280c9f1fdba9f866f)) +* **cli:** show error if npm install on migration failed ([#5904](https://github.com/ionic-team/capacitor/issues/5904)) ([aa60a75](https://github.com/ionic-team/capacitor/commit/aa60a75d9c2c784e127a4d89e4079b412fbe7262)) +* **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e)) + + +### Features + +* Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45)) + + + + + # [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 3dc84a025..45e3bc790 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) + + +### Bug Fixes + +* **android:** open external links in browser ([#5913](https://github.com/ionic-team/capacitor/issues/5913)) ([7553ede](https://github.com/ionic-team/capacitor/commit/7553ede93170971e21ab3dec1798443d084ead2a)) +* **android:** set all cookies on proxied requests ([#5781](https://github.com/ionic-team/capacitor/issues/5781)) ([5ef6a38](https://github.com/ionic-team/capacitor/commit/5ef6a3889121dd39a9159ff80250df18854bc557)) +* **android:** set WebViewClient on the WebView ([#5919](https://github.com/ionic-team/capacitor/issues/5919)) ([020ed8e](https://github.com/ionic-team/capacitor/commit/020ed8eaeb7864399d4b93f54ab7601c607d8e0d)) +* **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e)) + + +### Features + +* Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45)) + + + + + # [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) **Note:** Version bump only for package @capacitor/android diff --git a/android/package.json b/android/package.json index 95a629276..133748363 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "4.2.0", + "version": "4.3.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -23,7 +23,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^4.2.0" + "@capacitor/core": "^4.3.0" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 872e097d1..c60fce52f 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) + + +### Bug Fixes + +* **cli:** Find the Info.plist when using scheme ([#5914](https://github.com/ionic-team/capacitor/issues/5914)) ([f7029ac](https://github.com/ionic-team/capacitor/commit/f7029acb885ec60f85a434b6f71e4f2a633c7651)) +* **cli:** Make migrator update gradle wrapper files ([#5910](https://github.com/ionic-team/capacitor/issues/5910)) ([b8b9b1f](https://github.com/ionic-team/capacitor/commit/b8b9b1f96249908435017eea6c427221f1971836)) +* **cli:** Make update from windows use proper paths on Podfile ([#5906](https://github.com/ionic-team/capacitor/issues/5906)) ([c41d28f](https://github.com/ionic-team/capacitor/commit/c41d28f8cc829c6bf69d776280c9f1fdba9f866f)) +* **cli:** show error if npm install on migration failed ([#5904](https://github.com/ionic-team/capacitor/issues/5904)) ([aa60a75](https://github.com/ionic-team/capacitor/commit/aa60a75d9c2c784e127a4d89e4079b412fbe7262)) + + +### Features + +* Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45)) + + + + + # [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) diff --git a/cli/package.json b/cli/package.json index 0741c0bb4..8db6686c1 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "4.2.0", + "version": "4.3.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 7d070d2c6..f5d6a8c6b 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) + + +### Bug Fixes + +* **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e)) + + +### Features + +* Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45)) + + + + + # [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) **Note:** Version bump only for package @capacitor/core diff --git a/core/package.json b/core/package.json index b1e5d4c83..5c4274239 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "4.2.0", + "version": "4.3.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index e8161d110..8d72c57b2 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) + + +### Bug Fixes + +* **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e)) + + +### Features + +* Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45)) + + + + + # [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08) **Note:** Version bump only for package @capacitor/ios diff --git a/ios/package.json b/ios/package.json index 7a93d364e..464bfa13f 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "4.2.0", + "version": "4.3.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^4.2.0" + "@capacitor/core": "^4.3.0" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index 5f2fde156..68db80ef3 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "4.2.0" + "version": "4.3.0" } From 6d4e620308b6dd97376e3af7de1dd1a530083f1c Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 28 Sep 2022 09:58:17 -0400 Subject: [PATCH 172/179] feat(cli): add build command for android (#5891) * feat(cli): add build command for android * chore: remove console log * chore: mesage if try ios * chore: remove unused * chore: add cap-config file support --- cli/src/android/build.ts | 87 ++++++++++++++++++++++++++++++++++++++++ cli/src/config.ts | 9 +++++ cli/src/declarations.ts | 38 ++++++++++++++++++ cli/src/definitions.ts | 7 ++++ cli/src/index.ts | 47 +++++++++++++++++++++- cli/src/tasks/build.ts | 76 +++++++++++++++++++++++++++++++++++ 6 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 cli/src/android/build.ts create mode 100644 cli/src/tasks/build.ts diff --git a/cli/src/android/build.ts b/cli/src/android/build.ts new file mode 100644 index 000000000..de22a2ded --- /dev/null +++ b/cli/src/android/build.ts @@ -0,0 +1,87 @@ +import { join } from 'path'; + +import c from '../colors'; +import { runTask } from '../common'; +import type { Config } from '../definitions'; +import { logSuccess } from '../log'; +import type { BuildCommandOptions } from '../tasks/build'; +import { runCommand } from '../util/subprocess'; + +export async function buildAndroid( + config: Config, + buildOptions: BuildCommandOptions, +): Promise { + const releaseType = buildOptions.androidreleasetype ?? 'AAB'; + const releaseTypeIsAAB = releaseType === 'AAB'; + const arg = releaseTypeIsAAB ? ':app:bundleRelease' : 'assembleRelease'; + const gradleArgs = [arg]; + + if ( + !buildOptions.keystorepath || + !buildOptions.keystorealias || + !buildOptions.keystorealiaspass || + !buildOptions.keystorepass + ) { + throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password, Keystore Key Alias, Keystore Key Password)'; + } + + try { + await runTask('Running Gradle build', async () => + runCommand('./gradlew', gradleArgs, { + cwd: config.android.platformDirAbs, + }), + ); + } catch (e) { + if ((e as any).includes('EACCES')) { + throw `gradlew file does not have executable permissions. This can happen if the Android platform was added on a Windows machine. Please run ${c.strong( + `chmod +x ./${config.android.platformDir}/gradlew`, + )} and try again.`; + } else { + throw e; + } + } + + const releasePath = join( + config.android.appDirAbs, + 'build', + 'outputs', + releaseTypeIsAAB ? 'bundle' : 'apk', + 'release', + ); + + const unsignedReleaseName = `app${ + config.android.flavor ? `-${config.android.flavor}` : '' + }-release${releaseTypeIsAAB ? '' : '-unsigned'}.${releaseType.toLowerCase()}`; + + const signedReleaseName = unsignedReleaseName.replace( + `-release${ + releaseTypeIsAAB ? '' : '-unsigned' + }.${releaseType.toLowerCase()}`, + `-release-signed.${releaseType.toLowerCase()}`, + ); + + const signingArgs = [ + '-sigalg', + 'SHA1withRSA', + '-digestalg', + 'SHA1', + '-keystore', + buildOptions.keystorepath, + '-keypass', + buildOptions.keystorealiaspass, + '-storepass', + buildOptions.keystorepass, + `-signedjar`, + `${join(releasePath, signedReleaseName)}`, + `${join(releasePath, unsignedReleaseName)}`, + buildOptions.keystorealias, + ]; + + await runTask('Signing Release', async () => { + await runCommand('jarsigner', signingArgs, { + cwd: config.android.platformDirAbs, + }); + }); + + logSuccess(`Successfully generated ${signedReleaseName} at: ${releasePath}`); +} diff --git a/cli/src/config.ts b/cli/src/config.ts index c6f75c34f..903b3619b 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -236,6 +236,14 @@ async function loadAndroidConfig( const buildOutputDir = `${apkPath}/debug`; const cordovaPluginsDir = 'capacitor-cordova-android-plugins'; const studioPath = lazy(() => determineAndroidStudioPath(cliConfig.os)); + const buildOptions = { + keystorePath: extConfig.android?.buildOptions?.keystorePath, + keystorePassword: extConfig.android?.buildOptions?.keystorePassword, + keystoreAlias: extConfig.android?.buildOptions?.keystoreAlias, + keystoreAliasPassword: + extConfig.android?.buildOptions?.keystoreAliasPassword, + releaseType: extConfig.android?.buildOptions?.releaseType, + }; return { name, @@ -261,6 +269,7 @@ async function loadAndroidConfig( buildOutputDir, buildOutputDirAbs: resolve(platformDirAbs, buildOutputDir), flavor, + buildOptions, }; } diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index 8bdbcf225..6b9a316bb 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -225,6 +225,44 @@ export interface CapacitorConfig { * @default 60 */ minWebViewVersion?: number; + + buildOptions?: { + /** + * Path to your keystore + * + * @since 4.3.0 + */ + keystorePath?: string; + + /** + * Password to your keystore + * + * @since 4.3.0 + */ + keystorePassword?: string; + + /** + * Alias in the keystore to use + * + * @since 4.3.0 + */ + keystoreAlias?: string; + + /** + * Password for the alias in the keystore to use + * + * @since 4.3.0 + */ + keystoreAliasPassword?: string; + + /** + * Bundle type for your release build + * + * @since 4.3.0 + * @default "AAB" + */ + releaseType?: 'AAB' | 'APK'; + }; }; ios?: { diff --git a/cli/src/definitions.ts b/cli/src/definitions.ts index 445d5312b..a2e0b6d24 100644 --- a/cli/src/definitions.ts +++ b/cli/src/definitions.ts @@ -91,6 +91,13 @@ export interface AndroidConfig extends PlatformConfig { readonly buildOutputDirAbs: string; readonly apkName: string; readonly flavor: string; + readonly buildOptions: { + keystorePath?: string; + keystorePassword?: string; + keystoreAlias?: string; + keystoreAliasPassword?: string; + releaseType?: 'AAB' | 'APK'; + }; } export interface IOSConfig extends PlatformConfig { diff --git a/cli/src/index.ts b/cli/src/index.ts index acb6e135f..171329f75 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -1,4 +1,4 @@ -import { program } from 'commander'; +import { Option, program } from 'commander'; import c from './colors'; import { checkExternalConfig, loadConfig } from './config'; @@ -137,6 +137,51 @@ export function runProgram(config: Config): void { ), ); + program + .command('build ') + .description('builds the release version of the selected platform') + .option('--keystorepath ', 'Path to the keystore') + .option('--keystorepass ', 'Password to the keystore') + .option('--keystorealias ', 'Key Alias in the keystore') + .option( + '--keystorealiaspass ', + 'Password for the Key Alias', + ) + .addOption( + new Option( + '--androidreleasetype ', + 'Android release type; APK or AAB', + ) + .choices(['AAB', 'APK']) + .default('AAB'), + ) + .action( + wrapAction( + telemetryAction( + config, + async ( + platform, + { + keystorepath, + keystorepass, + keystorealias, + keystorealiaspass, + androidreleasetype, + }, + ) => { + const { buildCommand } = await import('./tasks/build'); + await buildCommand(config, platform, { + keystorepath, + keystorepass, + keystorealias, + keystorealiaspass, + androidreleasetype, + }); + }, + ), + ), + ); + program .command(`run [platform]`) .description( diff --git a/cli/src/tasks/build.ts b/cli/src/tasks/build.ts new file mode 100644 index 000000000..e57e54cf8 --- /dev/null +++ b/cli/src/tasks/build.ts @@ -0,0 +1,76 @@ +import { buildAndroid } from '../android/build'; +import { selectPlatforms, promptForPlatform } from '../common'; +import type { Config } from '../definitions'; +import { fatal, isFatal } from '../errors'; + +export interface BuildCommandOptions { + keystorepath?: string; + keystorepass?: string; + keystorealias?: string; + keystorealiaspass?: string; + androidreleasetype?: 'AAB' | 'APK'; +} + +export async function buildCommand( + config: Config, + selectedPlatformName: string, + buildOptions: BuildCommandOptions, +): Promise { + const platforms = await selectPlatforms(config, selectedPlatformName); + let platformName: string; + if (platforms.length === 1) { + platformName = platforms[0]; + } else { + platformName = await promptForPlatform( + platforms.filter(createBuildablePlatformFilter(config)), + `Please choose a platform to build for:`, + ); + } + + const buildCommandOptions: BuildCommandOptions = { + keystorepath: + buildOptions.keystorepath || config.android.buildOptions.keystorePath, + keystorepass: + buildOptions.keystorepass || config.android.buildOptions.keystorePassword, + keystorealias: + buildOptions.keystorealias || config.android.buildOptions.keystoreAlias, + keystorealiaspass: + buildOptions.keystorealiaspass || + config.android.buildOptions.keystoreAliasPassword, + androidreleasetype: + buildOptions.androidreleasetype || + config.android.buildOptions.releaseType, + }; + + try { + await build(config, platformName, buildCommandOptions); + } catch (e) { + if (!isFatal(e)) { + fatal((e as any).stack ?? e); + } + throw e; + } +} + +export async function build( + config: Config, + platformName: string, + buildOptions: BuildCommandOptions, +): Promise { + if (platformName == config.ios.name) { + throw `Platform "${platformName}" is not available in the build command.`; + } else if (platformName === config.android.name) { + await buildAndroid(config, buildOptions); + } else if (platformName === config.web.name) { + throw `Platform "${platformName}" is not available in the build command.`; + } else { + throw `Platform "${platformName}" is not valid.`; + } +} + +function createBuildablePlatformFilter( + config: Config, +): (platform: string) => boolean { + return platform => + platform === config.ios.name || platform === config.android.name; +} From a309b455fdd190613353bdf0eb04469cf4aa6ccd Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Mon, 3 Oct 2022 13:19:26 -0500 Subject: [PATCH 173/179] feat(cli): supports secure live updates in Portals for Capacitor config (#5955) --- cli/src/declarations.ts | 1 + cli/src/tasks/copy.ts | 45 ++++++++++++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index 6b9a316bb..551d5ba96 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -592,6 +592,7 @@ export interface PluginsConfig { Portals?: { shell: Portal; apps: Portal[]; + liveUpdatesKey?: string; }; /** diff --git a/cli/src/tasks/copy.ts b/cli/src/tasks/copy.ts index 63151887f..01791b617 100644 --- a/cli/src/tasks/copy.ts +++ b/cli/src/tasks/copy.ts @@ -97,6 +97,13 @@ export async function copy( if (platformName === config.ios.name) { if (usesCapacitorPortals) { await copyFederatedWebDirs(config, await config.ios.webDirAbs); + if (config.app.extConfig?.plugins?.Portals?.liveUpdatesKey) { + await copySecureLiveUpdatesKey( + config.app.extConfig.plugins.Portals.liveUpdatesKey, + config.app.rootDir, + config.ios.nativeTargetDirAbs, + ); + } } else { await copyWebDir( config, @@ -104,8 +111,12 @@ export async function copy( config.app.webDirAbs, ); } - if (usesLiveUpdates) { - await copySecureLiveUpdatesKey(config, config.ios.nativeTargetDirAbs); + if (usesLiveUpdates && config.app.extConfig?.plugins?.LiveUpdates?.key) { + await copySecureLiveUpdatesKey( + config.app.extConfig.plugins.LiveUpdates.key, + config.app.rootDir, + config.ios.nativeTargetDirAbs, + ); } await copyCapacitorConfig(config, config.ios.nativeTargetDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); @@ -113,6 +124,13 @@ export async function copy( } else if (platformName === config.android.name) { if (usesCapacitorPortals) { await copyFederatedWebDirs(config, config.android.webDirAbs); + if (config.app.extConfig?.plugins?.Portals?.liveUpdatesKey) { + await copySecureLiveUpdatesKey( + config.app.extConfig.plugins.Portals.liveUpdatesKey, + config.app.rootDir, + config.android.assetsDirAbs, + ); + } } else { await copyWebDir( config, @@ -120,8 +138,12 @@ export async function copy( config.app.webDirAbs, ); } - if (usesLiveUpdates) { - await copySecureLiveUpdatesKey(config, config.android.assetsDirAbs); + if (usesLiveUpdates && config.app.extConfig?.plugins?.LiveUpdates?.key) { + await copySecureLiveUpdatesKey( + config.app.extConfig.plugins.LiveUpdates.key, + config.app.rootDir, + config.android.assetsDirAbs, + ); } await copyCapacitorConfig(config, config.android.assetsDirAbs); const cordovaPlugins = await getCordovaPlugins(config, platformName); @@ -229,15 +251,14 @@ function isPortal(config: any): config is Portal { ); } -async function copySecureLiveUpdatesKey(config: Config, nativeAbsDir: string) { - if (!config.app.extConfig?.plugins?.LiveUpdates?.key) { - return; - } - - const secureLiveUpdatesKeyFile = config.app.extConfig.plugins.LiveUpdates.key; - const keyAbsFromPath = join(config.app.rootDir, secureLiveUpdatesKeyFile); +async function copySecureLiveUpdatesKey( + secureLiveUpdatesKeyFile: string, + rootDir: string, + nativeAbsDir: string, +) { + const keyAbsFromPath = join(rootDir, secureLiveUpdatesKeyFile); const keyAbsToPath = join(nativeAbsDir, basename(keyAbsFromPath)); - const keyRelToDir = relative(config.app.rootDir, nativeAbsDir); + const keyRelToDir = relative(rootDir, nativeAbsDir); if (!(await pathExists(keyAbsFromPath))) { logger.warn( From 8e8414fa6f4ccb245576cc113eb969937613bbf7 Mon Sep 17 00:00:00 2001 From: Mike Summerfeldt <20338451+IT-MikeS@users.noreply.github.com> Date: Wed, 5 Oct 2022 10:39:08 -0400 Subject: [PATCH 174/179] feat(cli): add build command for ios (#5925) * feat(cli): add build command for android * chore: remove console log * chore: mesage if try ios * chore: remove unused * chore: add cap-config file support * feat(cli): add build command for iOS IPA * chore: fmt --- cli/src/index.ts | 3 ++ cli/src/ios/build.ts | 84 ++++++++++++++++++++++++++++++++++++++++++ cli/src/tasks/build.ts | 5 ++- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 cli/src/ios/build.ts diff --git a/cli/src/index.ts b/cli/src/index.ts index 171329f75..5e8bf801a 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -140,6 +140,7 @@ export function runProgram(config: Config): void { program .command('build ') .description('builds the release version of the selected platform') + .option('--scheme ', 'iOS Scheme to build') .option('--keystorepath ', 'Path to the keystore') .option('--keystorepass ', 'Password to the keystore') .option('--keystorealias ', 'Key Alias in the keystore') @@ -162,6 +163,7 @@ export function runProgram(config: Config): void { async ( platform, { + scheme, keystorepath, keystorepass, keystorealias, @@ -171,6 +173,7 @@ export function runProgram(config: Config): void { ) => { const { buildCommand } = await import('./tasks/build'); await buildCommand(config, platform, { + scheme, keystorepath, keystorepass, keystorealias, diff --git a/cli/src/ios/build.ts b/cli/src/ios/build.ts new file mode 100644 index 000000000..c8f8b31ab --- /dev/null +++ b/cli/src/ios/build.ts @@ -0,0 +1,84 @@ +import { writeFileSync, unlinkSync } from '@ionic/utils-fs'; +import { join } from 'path'; +import rimraf from 'rimraf'; + +import { runTask } from '../common'; +import type { Config } from '../definitions'; +import { logSuccess } from '../log'; +import type { BuildCommandOptions } from '../tasks/build'; +import { runCommand } from '../util/subprocess'; + +export async function buildiOS( + config: Config, + buildOptions: BuildCommandOptions, +): Promise { + const theScheme = buildOptions.scheme ?? 'App'; + + await runTask('Building xArchive', async () => + runCommand( + 'xcodebuild', + [ + '-workspace', + `${theScheme}.xcworkspace`, + '-scheme', + `${theScheme}`, + '-destination', + `generic/platform=iOS`, + '-archivePath', + `${theScheme}.xcarchive`, + 'archive', + ], + { + cwd: config.ios.nativeProjectDirAbs, + }, + ), + ); + + const archivePlistContents = ` + + + +method +app-store + +`; + + const archivePlistPath = join( + `${config.ios.nativeProjectDirAbs}`, + 'archive.plist', + ); + + writeFileSync(archivePlistPath, archivePlistContents); + + await runTask('Building IPA', async () => + runCommand( + 'xcodebuild', + [ + 'archive', + '-archivePath', + `${theScheme}.xcarchive`, + '-exportArchive', + '-exportOptionsPlist', + 'archive.plist', + '-exportPath', + 'output', + '-allowProvisioningUpdates', + ], + { + cwd: config.ios.nativeProjectDirAbs, + }, + ), + ); + + await runTask('Cleaning up', async () => { + unlinkSync(archivePlistPath); + rimraf.sync(join(config.ios.nativeProjectDirAbs, `${theScheme}.xcarchive`)); + }); + + logSuccess( + `Successfully generated an IPA at: ${join( + config.ios.nativeProjectDirAbs, + 'output', + )}`, + ); +} diff --git a/cli/src/tasks/build.ts b/cli/src/tasks/build.ts index e57e54cf8..5602f75da 100644 --- a/cli/src/tasks/build.ts +++ b/cli/src/tasks/build.ts @@ -2,8 +2,10 @@ import { buildAndroid } from '../android/build'; import { selectPlatforms, promptForPlatform } from '../common'; import type { Config } from '../definitions'; import { fatal, isFatal } from '../errors'; +import { buildiOS } from '../ios/build'; export interface BuildCommandOptions { + scheme?: string; keystorepath?: string; keystorepass?: string; keystorealias?: string; @@ -28,6 +30,7 @@ export async function buildCommand( } const buildCommandOptions: BuildCommandOptions = { + scheme: buildOptions.scheme || config.ios.scheme, keystorepath: buildOptions.keystorepath || config.android.buildOptions.keystorePath, keystorepass: @@ -58,7 +61,7 @@ export async function build( buildOptions: BuildCommandOptions, ): Promise { if (platformName == config.ios.name) { - throw `Platform "${platformName}" is not available in the build command.`; + await buildiOS(config, buildOptions); } else if (platformName === config.android.name) { await buildAndroid(config, buildOptions); } else if (platformName === config.web.name) { From c4e040a6f8c6b54bac6ae320e5f0f008604fe50f Mon Sep 17 00:00:00 2001 From: Chace Daniels Date: Mon, 17 Oct 2022 08:58:25 -0700 Subject: [PATCH 175/179] fix(http): fix local http requests on native platforms --- .../src/main/assets/native-bridge.js | 31 +++++++-- core/native-bridge.ts | 65 +++++++++++++++++-- .../Capacitor/assets/native-bridge.js | 31 +++++++-- 3 files changed, 115 insertions(+), 12 deletions(-) diff --git a/android/capacitor/src/main/assets/native-bridge.js b/android/capacitor/src/main/assets/native-bridge.js index dbed700c4..32db85285 100644 --- a/android/capacitor/src/main/assets/native-bridge.js +++ b/android/capacitor/src/main/assets/native-bridge.js @@ -335,6 +335,8 @@ const nativeBridge = (function (exports) { win.CapacitorWebFetch = window.fetch; win.CapacitorWebXMLHttpRequest = { abort: window.XMLHttpRequest.prototype.abort, + getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, + getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, open: window.XMLHttpRequest.prototype.open, send: window.XMLHttpRequest.prototype.send, setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, @@ -361,8 +363,8 @@ const nativeBridge = (function (exports) { if (doPatchHttp) { // fetch patch window.fetch = async (resource, options) => { - if (resource.toString().startsWith('data:') || - resource.toString().startsWith('blob:')) { + if (!(resource.toString().startsWith('http:') || + resource.toString().startsWith('https:'))) { return win.CapacitorWebFetch(resource, options); } try { @@ -420,17 +422,28 @@ const nativeBridge = (function (exports) { }; // XHR patch abort window.XMLHttpRequest.prototype.abort = function () { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } this.readyState = 0; this.dispatchEvent(new Event('abort')); this.dispatchEvent(new Event('loadend')); }; // XHR patch open window.XMLHttpRequest.prototype.open = function (method, url) { + this._url = url; + if (!(url.startsWith('http:') || url.toString().startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); + } Object.defineProperties(this, { _headers: { value: {}, writable: true, }, + _method: { + value: method, + writable: true, + }, readyState: { get: function () { var _a; @@ -459,16 +472,20 @@ const nativeBridge = (function (exports) { }, }); addEventListeners.call(this); - this._method = method; - this._url = url; this.readyState = 1; }; // XHR patch set request header window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); + } this._headers[header] = value; }; // XHR patch send window.XMLHttpRequest.prototype.send = function (body) { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); + } try { this.readyState = 2; // intercept request & pass to the bridge @@ -522,6 +539,9 @@ const nativeBridge = (function (exports) { }; // XHR patch getAllResponseHeaders window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); + } let returnString = ''; for (const key in this._headers) { if (key != 'Set-Cookie') { @@ -532,6 +552,9 @@ const nativeBridge = (function (exports) { }; // XHR patch getResponseHeader window.XMLHttpRequest.prototype.getResponseHeader = function (name) { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); + } return this._headers[name]; }; } diff --git a/core/native-bridge.ts b/core/native-bridge.ts index fdc6753dd..3d1224952 100644 --- a/core/native-bridge.ts +++ b/core/native-bridge.ts @@ -369,6 +369,9 @@ const initBridge = (w: any): void => { win.CapacitorWebFetch = window.fetch; win.CapacitorWebXMLHttpRequest = { abort: window.XMLHttpRequest.prototype.abort, + getAllResponseHeaders: + window.XMLHttpRequest.prototype.getAllResponseHeaders, + getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, open: window.XMLHttpRequest.prototype.open, send: window.XMLHttpRequest.prototype.send, setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, @@ -403,8 +406,10 @@ const initBridge = (w: any): void => { options?: RequestInit, ) => { if ( - resource.toString().startsWith('data:') || - resource.toString().startsWith('blob:') + !( + resource.toString().startsWith('http:') || + resource.toString().startsWith('https:') + ) ) { return win.CapacitorWebFetch(resource, options); } @@ -472,6 +477,12 @@ const initBridge = (w: any): void => { // XHR patch abort window.XMLHttpRequest.prototype.abort = function () { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } this.readyState = 0; this.dispatchEvent(new Event('abort')); this.dispatchEvent(new Event('loadend')); @@ -482,11 +493,23 @@ const initBridge = (w: any): void => { method: string, url: string, ) { + this._url = url; + + if ( + !(url.startsWith('http:') || url.toString().startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); + } + Object.defineProperties(this, { _headers: { value: {}, writable: true, }, + _method: { + value: method, + writable: true, + }, readyState: { get: function () { return this._readyState ?? 0; @@ -513,9 +536,8 @@ const initBridge = (w: any): void => { writable: true, }, }); + addEventListeners.call(this); - this._method = method; - this._url = url; this.readyState = 1; }; @@ -524,6 +546,16 @@ const initBridge = (w: any): void => { header: string, value: string, ) { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call( + this, + header, + value, + ); + } this._headers[header] = value; }; @@ -531,6 +563,13 @@ const initBridge = (w: any): void => { window.XMLHttpRequest.prototype.send = function ( body?: Document | XMLHttpRequestBodyInit, ) { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); + } + try { this.readyState = 2; @@ -585,6 +624,15 @@ const initBridge = (w: any): void => { // XHR patch getAllResponseHeaders window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call( + this, + ); + } + let returnString = ''; for (const key in this._headers) { if (key != 'Set-Cookie') { @@ -596,6 +644,15 @@ const initBridge = (w: any): void => { // XHR patch getResponseHeader window.XMLHttpRequest.prototype.getResponseHeader = function (name) { + if ( + this._url == null || + !(this._url.startsWith('http:') || this._url.startsWith('https:')) + ) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call( + this, + name, + ); + } return this._headers[name]; }; } diff --git a/ios/Capacitor/Capacitor/assets/native-bridge.js b/ios/Capacitor/Capacitor/assets/native-bridge.js index dbed700c4..32db85285 100644 --- a/ios/Capacitor/Capacitor/assets/native-bridge.js +++ b/ios/Capacitor/Capacitor/assets/native-bridge.js @@ -335,6 +335,8 @@ const nativeBridge = (function (exports) { win.CapacitorWebFetch = window.fetch; win.CapacitorWebXMLHttpRequest = { abort: window.XMLHttpRequest.prototype.abort, + getAllResponseHeaders: window.XMLHttpRequest.prototype.getAllResponseHeaders, + getResponseHeader: window.XMLHttpRequest.prototype.getResponseHeader, open: window.XMLHttpRequest.prototype.open, send: window.XMLHttpRequest.prototype.send, setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader, @@ -361,8 +363,8 @@ const nativeBridge = (function (exports) { if (doPatchHttp) { // fetch patch window.fetch = async (resource, options) => { - if (resource.toString().startsWith('data:') || - resource.toString().startsWith('blob:')) { + if (!(resource.toString().startsWith('http:') || + resource.toString().startsWith('https:'))) { return win.CapacitorWebFetch(resource, options); } try { @@ -420,17 +422,28 @@ const nativeBridge = (function (exports) { }; // XHR patch abort window.XMLHttpRequest.prototype.abort = function () { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.abort.call(this); + } this.readyState = 0; this.dispatchEvent(new Event('abort')); this.dispatchEvent(new Event('loadend')); }; // XHR patch open window.XMLHttpRequest.prototype.open = function (method, url) { + this._url = url; + if (!(url.startsWith('http:') || url.toString().startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.open.call(this, method, url); + } Object.defineProperties(this, { _headers: { value: {}, writable: true, }, + _method: { + value: method, + writable: true, + }, readyState: { get: function () { var _a; @@ -459,16 +472,20 @@ const nativeBridge = (function (exports) { }, }); addEventListeners.call(this); - this._method = method; - this._url = url; this.readyState = 1; }; // XHR patch set request header window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value); + } this._headers[header] = value; }; // XHR patch send window.XMLHttpRequest.prototype.send = function (body) { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.send.call(this, body); + } try { this.readyState = 2; // intercept request & pass to the bridge @@ -522,6 +539,9 @@ const nativeBridge = (function (exports) { }; // XHR patch getAllResponseHeaders window.XMLHttpRequest.prototype.getAllResponseHeaders = function () { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this); + } let returnString = ''; for (const key in this._headers) { if (key != 'Set-Cookie') { @@ -532,6 +552,9 @@ const nativeBridge = (function (exports) { }; // XHR patch getResponseHeader window.XMLHttpRequest.prototype.getResponseHeader = function (name) { + if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) { + return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name); + } return this._headers[name]; }; } From 2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc Mon Sep 17 00:00:00 2001 From: Chace Daniels Date: Mon, 17 Oct 2022 19:46:00 -0700 Subject: [PATCH 176/179] fix(cookies): make document.cookie setter synchronous * fix(cookies): make document.cookie setter synchronous * fix(cookies): remove unnecessary decoding of cookie before setting on native --- .../src/main/assets/native-bridge.js | 24 ++++++++------ .../getcapacitor/plugin/CapacitorCookies.java | 14 +++++--- core/native-bridge.ts | 32 ++++++++++++------- .../Plugins/CapacitorCookieManager.swift | 8 +++++ .../Capacitor/WebViewDelegationHandler.swift | 11 ++++++- .../Capacitor/assets/native-bridge.js | 24 ++++++++------ 6 files changed, 75 insertions(+), 38 deletions(-) diff --git a/android/capacitor/src/main/assets/native-bridge.js b/android/capacitor/src/main/assets/native-bridge.js index 32db85285..4810b4a18 100644 --- a/android/capacitor/src/main/assets/native-bridge.js +++ b/android/capacitor/src/main/assets/native-bridge.js @@ -268,11 +268,6 @@ const nativeBridge = (function (exports) { } return String(msg); }; - /** - * Safely web decode a string value (inspired by js-cookie) - * @param str The string value to decode - */ - const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); const platform = getPlatformId(win); if (platform == 'android' || platform == 'ios') { // patch document.cookie on Android/iOS @@ -305,7 +300,7 @@ const nativeBridge = (function (exports) { // Use prompt to synchronously get cookies. // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 const payload = { - type: 'CapacitorCookies', + type: 'CapacitorCookies.get', }; const res = prompt(JSON.stringify(payload)); return res; @@ -322,10 +317,19 @@ const nativeBridge = (function (exports) { if (null == cookieValue) { continue; } - cap.toNative('CapacitorCookies', 'setCookie', { - key: cookieKey, - value: decode(cookieValue), - }); + if (platform === 'ios') { + // Use prompt to synchronously set cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.set', + key: cookieKey, + value: cookieValue, + }; + prompt(JSON.stringify(payload)); + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + win.CapacitorCookiesAndroidInterface.setCookie(cookieKey, cookieValue); + } } }, }); diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java index 4d3175980..e1989eac5 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java @@ -1,12 +1,7 @@ package com.getcapacitor.plugin; -import android.app.Activity; -import android.content.SharedPreferences; -import android.util.Log; import android.webkit.JavascriptInterface; import androidx.annotation.Nullable; -import com.getcapacitor.CapConfig; -import com.getcapacitor.JSObject; import com.getcapacitor.Plugin; import com.getcapacitor.PluginCall; import com.getcapacitor.PluginConfig; @@ -87,6 +82,15 @@ public String getCookies() { return ""; } + @JavascriptInterface + public void setCookie(String key, String value) { + String url = getServerUrl(null); + + if (!url.isEmpty()) { + cookieManager.setCookie(url, key, value); + } + } + @PluginMethod public void setCookie(PluginCall call) { String key = call.getString("key"); diff --git a/core/native-bridge.ts b/core/native-bridge.ts index 3d1224952..166088027 100644 --- a/core/native-bridge.ts +++ b/core/native-bridge.ts @@ -288,13 +288,6 @@ const initBridge = (w: any): void => { return String(msg); }; - /** - * Safely web decode a string value (inspired by js-cookie) - * @param str The string value to decode - */ - const decode = (str: string): string => - str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); - const platform = getPlatformId(win); if (platform == 'android' || platform == 'ios') { @@ -334,7 +327,7 @@ const initBridge = (w: any): void => { // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 const payload = { - type: 'CapacitorCookies', + type: 'CapacitorCookies.get', }; const res = prompt(JSON.stringify(payload)); @@ -355,10 +348,25 @@ const initBridge = (w: any): void => { continue; } - cap.toNative('CapacitorCookies', 'setCookie', { - key: cookieKey, - value: decode(cookieValue), - }); + if (platform === 'ios') { + // Use prompt to synchronously set cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + + const payload = { + type: 'CapacitorCookies.set', + key: cookieKey, + value: cookieValue, + }; + + prompt(JSON.stringify(payload)); + } else if ( + typeof win.CapacitorCookiesAndroidInterface !== 'undefined' + ) { + win.CapacitorCookiesAndroidInterface.setCookie( + cookieKey, + cookieValue, + ); + } } }, }); diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift b/ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift index 9249ebd67..e7094d714 100644 --- a/ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift +++ b/ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift @@ -31,6 +31,14 @@ public class CapacitorCookieManager { return value.removingPercentEncoding! } + public func setCookie(_ key: String, _ value: String) { + let url = getServerUrl()! + let jar = HTTPCookieStorage.shared + let field = ["Set-Cookie": "\(key)=\(value)"] + let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url) + jar.setCookies(cookies, for: url, mainDocumentURL: url) + } + public func setCookie(_ url: URL, _ key: String, _ value: String) { let jar = HTTPCookieStorage.shared let field = ["Set-Cookie": "\(key)=\(value)"] diff --git a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift index cff1b8d7c..f0e5256b9 100644 --- a/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewDelegationHandler.swift @@ -250,10 +250,19 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel if let payload = try JSONSerialization.jsonObject(with: dataFromString, options: .fragmentsAllowed) as? [String: AnyObject] { let type = payload["type"] as? String - if type == "CapacitorCookies" { + if type == "CapacitorCookies.get" { completionHandler(CapacitorCookieManager(bridge!.config).getCookies()) // Don't present prompt return + } else if type == "CapacitorCookies.set" { + // swiftlint:disable force_cast + let key = payload["key"] as! String + let value = payload["value"] as! String + CapacitorCookieManager(bridge!.config).setCookie(key, value) + completionHandler("") + // swiftlint:enable force_cast + // Don't present prompt + return } else if type == "CapacitorCookies.isEnabled" { let pluginConfig = bridge!.config.getPluginConfig("CapacitorCookies") completionHandler(String(pluginConfig.getBoolean("enabled", false))) diff --git a/ios/Capacitor/Capacitor/assets/native-bridge.js b/ios/Capacitor/Capacitor/assets/native-bridge.js index 32db85285..4810b4a18 100644 --- a/ios/Capacitor/Capacitor/assets/native-bridge.js +++ b/ios/Capacitor/Capacitor/assets/native-bridge.js @@ -268,11 +268,6 @@ const nativeBridge = (function (exports) { } return String(msg); }; - /** - * Safely web decode a string value (inspired by js-cookie) - * @param str The string value to decode - */ - const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); const platform = getPlatformId(win); if (platform == 'android' || platform == 'ios') { // patch document.cookie on Android/iOS @@ -305,7 +300,7 @@ const nativeBridge = (function (exports) { // Use prompt to synchronously get cookies. // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 const payload = { - type: 'CapacitorCookies', + type: 'CapacitorCookies.get', }; const res = prompt(JSON.stringify(payload)); return res; @@ -322,10 +317,19 @@ const nativeBridge = (function (exports) { if (null == cookieValue) { continue; } - cap.toNative('CapacitorCookies', 'setCookie', { - key: cookieKey, - value: decode(cookieValue), - }); + if (platform === 'ios') { + // Use prompt to synchronously set cookies. + // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323 + const payload = { + type: 'CapacitorCookies.set', + key: cookieKey, + value: cookieValue, + }; + prompt(JSON.stringify(payload)); + } + else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') { + win.CapacitorCookiesAndroidInterface.setCookie(cookieKey, cookieValue); + } } }, }); From 205b6e61806158244846608b1e6c0c7b26ee4ab7 Mon Sep 17 00:00:00 2001 From: Carl Poole Date: Wed, 19 Oct 2022 12:58:06 -0500 Subject: [PATCH 177/179] fix(android): added ServerPath object and building options for setting initial load from portals (#6008) --- .../main/java/com/getcapacitor/Bridge.java | 43 +++++++++++++++++-- .../java/com/getcapacitor/ServerPath.java | 25 +++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 android/capacitor/src/main/java/com/getcapacitor/ServerPath.java diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index ee6e6b7fa..bca83fbaf 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -142,6 +142,9 @@ public class Bridge { // An interface to manipulate route resolving private RouteProcessor routeProcessor; + // A pre-determined path to load the bridge + private ServerPath serverPath; + /** * Create the Bridge with a reference to the main {@link Activity} for the * app, and a reference to the {@link WebView} our app will use. @@ -159,11 +162,12 @@ public Bridge( CordovaPreferences preferences, CapConfig config ) { - this(context, null, webView, initialPlugins, cordovaInterface, pluginManager, preferences, config); + this(context, null, null, webView, initialPlugins, cordovaInterface, pluginManager, preferences, config); } private Bridge( AppCompatActivity context, + ServerPath serverPath, Fragment fragment, WebView webView, List> initialPlugins, @@ -173,6 +177,7 @@ private Bridge( CapConfig config ) { this.app = new App(); + this.serverPath = serverPath; this.context = context; this.fragment = fragment; this.webView = webView; @@ -294,8 +299,17 @@ private void loadWebView() { } } - // Get to work - webView.loadUrl(appUrl); + // If serverPath configured, start server based on provided path + if (serverPath != null) { + if (serverPath.getType() == ServerPath.PathType.ASSET_PATH) { + setServerAssetPath(serverPath.getPath()); + } else { + setServerBasePath(serverPath.getPath()); + } + } else { + // Get to work + webView.loadUrl(appUrl); + } } @SuppressLint("WebViewApiAvailability") @@ -514,6 +528,7 @@ public void reset() { /** * Initialize the WebView, setting required flags */ + @SuppressLint("SetJavaScriptEnabled") private void initWebView() { WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); @@ -1320,6 +1335,10 @@ void setRouteProcessor(RouteProcessor routeProcessor) { this.routeProcessor = routeProcessor; } + ServerPath getServerPath() { + return serverPath; + } + /** * Add a listener that the WebViewClient can trigger on certain events. * @param webViewListener A {@link WebViewListener} to add. @@ -1345,6 +1364,7 @@ public static class Builder { private Fragment fragment; private RouteProcessor routeProcessor; private final List webViewListeners = new ArrayList<>(); + private ServerPath serverPath; public Builder(AppCompatActivity activity) { this.activity = activity; @@ -1401,6 +1421,11 @@ public Builder setRouteProcessor(RouteProcessor routeProcessor) { return this; } + public Builder setServerPath(ServerPath serverPath) { + this.serverPath = serverPath; + return this; + } + public Bridge create() { // Cordova initialization ConfigXmlParser parser = new ConfigXmlParser(); @@ -1421,7 +1446,17 @@ public Bridge create() { cordovaInterface.onCordovaInit(pluginManager); // Bridge initialization - Bridge bridge = new Bridge(activity, fragment, webView, plugins, cordovaInterface, pluginManager, preferences, config); + Bridge bridge = new Bridge( + activity, + serverPath, + fragment, + webView, + plugins, + cordovaInterface, + pluginManager, + preferences, + config + ); bridge.setCordovaWebView(mockWebView); bridge.setWebViewListeners(webViewListeners); bridge.setRouteProcessor(routeProcessor); diff --git a/android/capacitor/src/main/java/com/getcapacitor/ServerPath.java b/android/capacitor/src/main/java/com/getcapacitor/ServerPath.java new file mode 100644 index 000000000..5b34b460c --- /dev/null +++ b/android/capacitor/src/main/java/com/getcapacitor/ServerPath.java @@ -0,0 +1,25 @@ +package com.getcapacitor; + +public class ServerPath { + + public enum PathType { + BASE_PATH, + ASSET_PATH + } + + private final PathType type; + private final String path; + + public ServerPath(PathType type, String path) { + this.type = type; + this.path = path; + } + + public PathType getType() { + return type; + } + + public String getPath() { + return path; + } +} From 1f6e8be9d8813c4397e2c54ac4c06beb55f97b5f Mon Sep 17 00:00:00 2001 From: Chace Daniels Date: Thu, 20 Oct 2022 10:18:48 -0700 Subject: [PATCH 178/179] fix(http): fix exception thrown on 204 responses * fix(http): fix exception thrown on 204 responses * chore: remove native bridge changes * Update native-bridge.js --- .../java/com/getcapacitor/plugin/util/HttpRequestHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java index 97b065a1e..07e77e064 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java @@ -303,6 +303,8 @@ private static Object parseJSON(String input) throws JSONException { return new JSONObject().put("flag", "true"); } else if ("false".equals(input.trim())) { return new JSONObject().put("flag", "false"); + } else if (input.trim().length() <= 0) { + return ""; } else { try { return new JSObject(input); From f9ec5a3059e109dbf2bd656f895243487eebeba5 Mon Sep 17 00:00:00 2001 From: "Github Workflow (on behalf of IT-MikeS)" Date: Fri, 21 Oct 2022 14:11:56 +0000 Subject: [PATCH 179/179] Release 4.4.0 --- CHANGELOG.md | 21 +++++++++++++++++++++ android/CHANGELOG.md | 14 ++++++++++++++ android/package.json | 4 ++-- cli/CHANGELOG.md | 13 +++++++++++++ cli/package.json | 2 +- core/CHANGELOG.md | 12 ++++++++++++ core/package.json | 2 +- ios/CHANGELOG.md | 12 ++++++++++++ ios/package.json | 4 ++-- lerna.json | 2 +- 10 files changed, 79 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef64c707d..802c99f31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21) + + +### Bug Fixes + +* **android:** added ServerPath object and building options for setting initial load from portals ([#6008](https://github.com/ionic-team/capacitor/issues/6008)) ([205b6e6](https://github.com/ionic-team/capacitor/commit/205b6e61806158244846608b1e6c0c7b26ee4ab7)) +* **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/ionic-team/capacitor/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc)) +* **http:** fix exception thrown on 204 responses ([1f6e8be](https://github.com/ionic-team/capacitor/commit/1f6e8be9d8813c4397e2c54ac4c06beb55f97b5f)) +* **http:** fix local http requests on native platforms ([c4e040a](https://github.com/ionic-team/capacitor/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f)) + + +### Features + +* **cli:** add build command for android ([#5891](https://github.com/ionic-team/capacitor/issues/5891)) ([6d4e620](https://github.com/ionic-team/capacitor/commit/6d4e620308b6dd97376e3af7de1dd1a530083f1c)) +* **cli:** add build command for ios ([#5925](https://github.com/ionic-team/capacitor/issues/5925)) ([8e8414f](https://github.com/ionic-team/capacitor/commit/8e8414fa6f4ccb245576cc113eb969937613bbf7)) +* **cli:** supports secure live updates in Portals for Capacitor config ([#5955](https://github.com/ionic-team/capacitor/issues/5955)) ([a309b45](https://github.com/ionic-team/capacitor/commit/a309b455fdd190613353bdf0eb04469cf4aa6ccd)) + + + + + # [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md index 45e3bc790..4ae3a4a39 100644 --- a/android/CHANGELOG.md +++ b/android/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21) + + +### Bug Fixes + +* **android:** added ServerPath object and building options for setting initial load from portals ([#6008](https://github.com/ionic-team/capacitor/issues/6008)) ([205b6e6](https://github.com/ionic-team/capacitor/commit/205b6e61806158244846608b1e6c0c7b26ee4ab7)) +* **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/ionic-team/capacitor/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc)) +* **http:** fix exception thrown on 204 responses ([1f6e8be](https://github.com/ionic-team/capacitor/commit/1f6e8be9d8813c4397e2c54ac4c06beb55f97b5f)) +* **http:** fix local http requests on native platforms ([c4e040a](https://github.com/ionic-team/capacitor/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f)) + + + + + # [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) diff --git a/android/package.json b/android/package.json index 133748363..2ac5c5aad 100644 --- a/android/package.json +++ b/android/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/android", - "version": "4.3.0", + "version": "4.4.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -23,7 +23,7 @@ "verify": "./gradlew clean lint build test -b capacitor/build.gradle" }, "peerDependencies": { - "@capacitor/core": "^4.3.0" + "@capacitor/core": "^4.4.0" }, "publishConfig": { "access": "public" diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index c60fce52f..768a45f60 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21) + + +### Features + +* **cli:** add build command for android ([#5891](https://github.com/ionic-team/capacitor/issues/5891)) ([6d4e620](https://github.com/ionic-team/capacitor/commit/6d4e620308b6dd97376e3af7de1dd1a530083f1c)) +* **cli:** add build command for ios ([#5925](https://github.com/ionic-team/capacitor/issues/5925)) ([8e8414f](https://github.com/ionic-team/capacitor/commit/8e8414fa6f4ccb245576cc113eb969937613bbf7)) +* **cli:** supports secure live updates in Portals for Capacitor config ([#5955](https://github.com/ionic-team/capacitor/issues/5955)) ([a309b45](https://github.com/ionic-team/capacitor/commit/a309b455fdd190613353bdf0eb04469cf4aa6ccd)) + + + + + # [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) diff --git a/cli/package.json b/cli/package.json index 8db6686c1..169df8920 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/cli", - "version": "4.3.0", + "version": "4.4.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index f5d6a8c6b..0c3cc9aa7 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21) + + +### Bug Fixes + +* **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/ionic-team/capacitor/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc)) +* **http:** fix local http requests on native platforms ([c4e040a](https://github.com/ionic-team/capacitor/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f)) + + + + + # [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) diff --git a/core/package.json b/core/package.json index 5c4274239..0864cb5f3 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/core", - "version": "4.3.0", + "version": "4.4.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 8d72c57b2..96f7313fd 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [4.4.0](https://github.com/ionic-team/capacitor/compare/4.3.0...4.4.0) (2022-10-21) + + +### Bug Fixes + +* **cookies:** make document.cookie setter synchronous ([2272abf](https://github.com/ionic-team/capacitor/commit/2272abf3d3d9dc82d9ca0d03b17e2b78f11f61fc)) +* **http:** fix local http requests on native platforms ([c4e040a](https://github.com/ionic-team/capacitor/commit/c4e040a6f8c6b54bac6ae320e5f0f008604fe50f)) + + + + + # [4.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21) diff --git a/ios/package.json b/ios/package.json index 464bfa13f..082b2af47 100644 --- a/ios/package.json +++ b/ios/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor/ios", - "version": "4.3.0", + "version": "4.4.0", "description": "Capacitor: Cross-platform apps with JavaScript and the web", "homepage": "https://capacitorjs.com", "author": "Ionic Team (https://ionic.io)", @@ -25,7 +25,7 @@ "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .." }, "peerDependencies": { - "@capacitor/core": "^4.3.0" + "@capacitor/core": "^4.4.0" }, "publishConfig": { "access": "public" diff --git a/lerna.json b/lerna.json index 68db80ef3..9de358c74 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "tagVersionPrefix": "" } }, - "version": "4.3.0" + "version": "4.4.0" }