From f0fd1b00eceb54e5c6d04d352b71e43960efe342 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Mon, 30 Oct 2023 08:46:32 +0300 Subject: [PATCH 01/17] feat: new icon --- app/src/main/ic_launcher-playstore.png | Bin 8961 -> 13140 bytes .../res/drawable/ic_launcher_background.xml | 26 ++++++++++ .../res/drawable/ic_launcher_foreground.xml | 49 ++++-------------- .../res/mipmap-anydpi-v26/ic_launcher.xml | 22 ++------ .../mipmap-anydpi-v26/ic_launcher_round.xml | 22 ++------ app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 1355 -> 0 bytes app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1162 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2666 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 963 -> 0 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 840 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1696 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 1938 -> 0 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1568 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3752 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 2929 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2206 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5748 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 4191 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3086 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 8264 bytes .../res/values/ic_launcher_background.xml | 19 +------ 21 files changed, 44 insertions(+), 94 deletions(-) create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png index cff6de9f761a11a44a30d09b01962a8f01cd6f3b..e1d45b1986f1f2b6b6a1a41644c295ebb847f9df 100644 GIT binary patch literal 13140 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 literal 8961 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 C*8AW9 diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..e80a78c3 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 405b38e4..8b6617b0 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,43 +1,14 @@ - - - - - - - - - - - + android:viewportWidth="71" + android:viewportHeight="27"> + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 2852ef12..7353dbd1 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,21 +1,5 @@ - - + - - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 2852ef12..7353dbd1 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,21 +1,5 @@ - - + - - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 3833035a356c7a6730bdc9fd90ef4be51dd8dd11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1355 zcmV-R1+@B!P)(A?bIPsrL;f@~S}dR@?_O>Av#?eN)e z)(+qQZHZsfmUxi2L}Aqu<*K_a1G-NTZ8%**I8H&FZ~nBzKRM4vR`&>!5hEkW=32@U znVkDRigbq{JVsnXJGhKid`^r%u*B1{`<{{nD+qIp?5NQZWOH@;<^06&FF|mEbmiy> zYDh|HNeV`gi5y))nv#MMWFp5zP(#wR(wyYKAk_)E89cNqn7DbrTHvoBEag0`4m_|b zSluMI0R9RxeY9+NU>&eelGsf8C&;VO19(_nu;b?a3`~$$qX+Pyy5O~>!@HY5g0QUf zkS6e;CUjW#V?j6t;QjSokQYn46CTn89F_3?`Yy=J`3LZjCg41&&1&}Q1$q5=0Upv6 zoJ{h5$ATQhf;teWAl#gH$dqFr-13x%j|#4S$ATQhf;teWAS|I+N_j{Vct8`*Uzif* za@Eyu-vj|{#&}2*ct{g2UjD2tPVV= z4xgS&is#R3u6_n42;iU`2j)DmDm<(z$3MI!?oR#d>R%K56$J2+3lFRc53I`9*VC?k zwZMNt_|Og?-to{X@W3j3eCBtNfAY-LuNL?(2w>UA$G|+eEIhOmK5*g@uI-h9VlI#v=i;Ak*yZY?d;6hTccUk&zMk$IDrRgM+WpI2v-00a@PY z>+3r}SG7m0S&^iDG@1_5wU-(_6lRcNJB$3JtWdBW1>%Y;uDC)`F96VnbBxFar5*qP N002ovPDHLkV1h*jfQkSB diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..88d704f8f286378fd6acf6c5465c0d984367e9cc GIT binary patch literal 1162 zcmV;51a1ONapN5Byf^@f7BZ6uFB?EelyL`(pY8N}Zd zrf{@vQl!+0(1>hP>`t73OOTP1xw}J3=j7K}cS1J8atgQ9)*{Dk z8wrm3Z#Q`@JqH3wTey3t)wV6m`Q2TSO7aaXa)@L)D^N);$z9U_GlLDF>)0~^a;|MV z1m~~Vwr$&HY}>YN+qP}nwpDs&%9NaXH)Tq*d}8a1vo_1vF4zVD(&qm^1w*kL5nLEZ zlF~R|MNmv<2G0IZ0QSEdMHodZIco)@NL17=Wk=NZvi>x6`Z`^mo~g&L-u@Bo5_U9x z*YF?6!jg)_y7r54OOW>A=!Rs9T$&P?Im*-z%)*@#^R%x_)qc)jU22JzR*~WuSO!$g}Nr2hY zo??MX3ok2Y4_N6`r`N!^Nft8e z;DvWrIGJFwK3fQ*2pMWpu=bMeySF*8{mNN&q?CDkLm<;=P08lK>+6GdJs{-dQs<3< z`?T}HGN)g(+Z1?16ig6Y1l>&T;!Al;7ef~Yziz{4u`?;wx)@k!XsA*)Qn9yB0V3#j zeCA%>`QSuoXlSUH?1>ga6|*jKYCcO`=rjvdk=moCFSBs%f#|YQ&n+IZQ|da760Kj# zTqexkhLU%pr^NT581G)m>u;#NZAhk|(Sh>b%yG1LY^1!3R?8|KP zS;WBtGwM*!7o~f5lZ|9D2Os%_`mibMZi#VG*{WMy$!+Jsm|0xu1oG9nJkvI;e*R#EEZ|KXzRVVuq|ha z@|GQO=@8p)<)zMS)!t^_Kx*{Lu5hAiKdbeYul7+V64HIiiw-*z3h6?}gSg6Us!k3Y znev&bvOAH-R;GlZ1{Aq&11UC>N%&r~U2mwcq4B8$VW;lSEJnmA0*Zo)6x)5``&z;8 ziA;Mz#TREqFuW)zJ$U8w>{SgOCQR?F0qNV@|0;cj6piAR;-R8YxzJA|;&zOlQ{*j( zwvdP-CnRDfVqzj@A|@hYB4#2cCSoRH;;0FJ?k|t#{YpSdD8jcyC?6^Tm4r%}{&5+~ c^C*dB5PgfWlYN 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 YUR(*Vr2|(j2$g_%&;=(PbFtTt6h|*24FCWD literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 337243d5eef4115e530afc430fede4cb97161470..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 963 zcmV;!13dhRP)28pB36Gu?ZQQ^2;$Cy-Sr2ED^ZCkxGG(_GI$9jf(r>&sf!wD zvC>+iwAz9MsuaYM*fcTe#ip4|5{~Dsb7bbsJ@m|^&<`Faq%(8gXXad{nWm=gYTzP- z1Sriu6X0{%7CmSu;J^2)T;);kNI&l{l}bm}*VhmG0oq0eqilx7cA(q6vMh%rNy^ZZ z`3_2=OcE`XC)t5scaD*?unghZ0R^ylRhU|b@z3;J^l7jP2Zi2%b)LWqFV@NNWxapUeJM8K-g^Eed%7!&-!-gE)8 z*~Sbv0symXB61fw%=h>tn z0X&!?kQ@46rpeWNGaFp*pCON<_6!Zri4nl9B@`+i%%Pyn+7AJC3^ z1RjRJh!Md2%m=ii9)a2KIWYoQA+b{OA+2yLaPi7(^5bVgi~zv13Lnx6w*n7BlbZH6 zT>wvucxvQ>n&C#^+Rb-ld8H^;0N}Yj7}tk2!l}TyOEK~xF|TV&hycKUP<&V;oC
    `5k*F?tsW4ulA{e!$wM z7nCFtiB~jRi;~f}ls^VKIy%}WCME_`sZ=4G&8{piE-qv1C9KVKI$anaAAi%{-j42r z_txeDC}oEh%F(W_uFl@x-pf5bJ-&dp&(lH{WU>v<3OjapckiYjTPgc|rWIKnhgTN{ l%hc4=EyV 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 66fdefbdfba560b2ffeecb3ff2d07086b8bc7800..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1938 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 SBk6t?K=v^GZ_AH1F9iT(N+}Zn literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..4fc76aa936f4732b31abb427aae86f268ac8f792 GIT binary patch literal 3752 zcmV;Z4p;F~Nk&GX4gdgGMM6+kP&iDK4gdfzU%(d-6^DYhZJ35X?RJ+yL`;Al0s}Z- z(nDMH{~NO9?3b}^&&0NEz1^qqDQ~uI+qP|8+qP{Tc2}iVc2`$-SM8POe{8%}eAV~H zskmc1V^3E&=o6b2597{`)nirM6Jy8Ou`AZ88x1<{74Ef0V%tW7Bq{%R@BV*Ea=(GJ zZE@CL79`20ZN=R@wytg4w(qlT+qP}nw()gs+qSha4&1hlBrDn8X=Zol`hpzW+K%m+ zif!Ar?bx<$+qP}nwrxB4syrWpcL-T2ZQDa&ZB*6-a>rvxojdIKRryDdl?f!Zos_Ap zHf#d`Y4iV|KqB@=b5R&cN-JnaP%uY8?LPyzjT9xvIB@U>xWa(~Ed@wU0UAawX8~d4 z6ksVgQYLOHl>iL#6y0AS(o(@<1dD}U%nyJ@mr zmn}!O0ogz@;5=xc1?POYY}tRm?T-NPk%M8lxTZiT?Iy|BQ`uTYXl~Hxpi!AjG8r@m zlT@XkntH|6?tX2YpFtBs*(x6s`PwgAS*QmePtaHz5fASY>iCO%?cV?lsaW{iRz9Y( zU6QRwa4%@%nqyH6m3x%m_WsWRNLe~Nt|^ep*J0Uu1y5!?8Y5mNN%=-AZ3>|49@Z!T zX*XTAD#0^Jstd^s>`9eyQc_x!~m{)eWtMFED7kONcUf3JQLyC9CIUlmG%o^ZwOP>o2{i0$3IcT+eU>A_y8) zYS82M8UeAaXv(79G(kN|1fCdb(Bj?PC<+$_KrC4Wk5fX3^S)JeAt)jTa9MF36vQ~C zlF>y5ZR#X*BbI|cCeqc-1IAEd@I~LNr}D9eiomp$VkLH-q>|F)ifz_)meO|U`Hzug zog|f%`n-47b)kss zeBdrgP>&Ktz*sTJpvG>d#;|2MMeZ>#qlA(9TN}MXKrJi0-Tap(XpP*bgyD;xuK8sD z7{NJ#sx^xB*-a*{O+(Gf+&tDCMA&L)s9hniq|_`nXj*q2H>|x);KjQPEscHjuU&fT3vS&1Pwo{ zwbj?riVuKp(xjuBu>fc%r9DT5rfe|K;F9m=sDrvrzuLhyZ}(AKAD;&={v0dkUuhMg zxe^kOIBs3k+)4VaHXSbqLs?x}$y3NH+^NJOEDoY#=m|K#pTG zSWgJ4F#YMq?g;{JS)rK-M1lLMt#tv);(KsP}+Be z+lP$dg)Tc4)FoAolZXVvAy{8ok}kt(B6y4Kgp?Q;opyEmg8_3xI^V}MV68OrX+0t! zWEp|W+?;D=#Lnj9^zO!6o>b*iUu9*PW%L82L|^o1?MIQFO3tM|)&c8@LO9~V)vKe! zkt-b^d$rFbMWSM2&yv#GdS$8dB8}r_8WyxocAvd*7c`Qo0 zR&B-ZoegvqN$U&sFx38)AplXjWe+1(ILF=Eh6OK`3KnWc~oSlPjnh82P|s`Looiz z8Ww=+p90hs?&)}fiO+jrg4m7Qe*~FxcpzAZ2ZkW%Y4;+jnFg19TzJcn0x$mPz2{wd z6?ob6F=FM0PnaFC4FKjx=U%0G3%gn@=_z-dGbbRyKBs!A0ui z($9vF5IB`~GdMLh6sI-DX0Ri+kz*AS0KJzmjr`x3O~0x%8B+k;UX$Z&vWy5|MaVjb z1xx@_Ht26~nzdxZbAnLaH=lk2xt3a&R;b|tv3t`=Dna+30N&3ANfM7X&|zwzG8IX8 z$dg8C{fPqU-de&0QM!AHR|rLSdg|q6&az$M)4gHaEPbL7UOsr5$toSDJ=fd4X41WT z6OA_5;m_W^cX}FP+F8kY-Q9=h?~5+gbAb3iUG-rh;0Ve=m7#s&^>jaNb;%to3lDHa z#4+KTD3w3lP-x6^%{4SqTHM8u5ViHiL?_+^?B`hN@|Ql2{}(Ur=wV-(!$ntmh&a2c zhVdbEbN{2g`NfO(A@+1!ytsviQQdK@wwgG5kubk8Vh&(*eM)B=~T_Xz42ggG1@1cKzeYpzY+dqMAl zIgRGUX_e#;aweKYXi|+@dme~6dbn33_qfJT94zoh(!`0U^7F8U|Or* ztSA{xNx)9O@Iv|Y(_7{NA{KiF4?(jvxOcE>J7^R&9y?>#?P|9S?@#Uw0v1_-fb0lT z1cReJ^LrOXLO@@%E9zjgY|L;36ruqE-wIR+X6LSx{SAzt^sF8JmsDfz1r= zM9?5uUAmq>aQRiKp{%<6-Oimiue#cgS#$ztZixPhU|+p{4lzg@YOAIP8eDAqi{S8A z-+^dJY#GuqlK0ocB{+lAX-1h>bG&Bo%`p1vih5lj#qFI+Es z7Kn@iW@kkAf^)hz2u?S?^V6}%XmdmPn0uUdo$gTMB?f8J*16`V?KU-jvUdI+kGW3F zjx9SA&`UvL#Iaa{{T(9u z9t%D6ouO%ZT4KZLp_~oI2$uiaE+S6TF4Ss8T?;zwYwcBWD)S04)Zjf& zJk`3mZAAU+TfchMhN@}$R`SE9g?ckE`PMzgfybe>?tJcDgI(Qw1{;hPLd=^^#^Z6{ z$Mcfud4&-62e006)jlsZ4m|JitsA_3Se}6aIN4!;SahM!j|0ceBKR=R#&fL4)m>Jr z*3qMwHGfxZU~{2@+kd;!PkR;9rnT2_S+D%&tv-)nyU?P47?#B`jlsz-Jrm?JL2ojc zd<(&D95{{x_uVA^kXm=j*O~Lxi_32R>bRUHRoTLt+O@V0UFW?Y)6$y0Sywp~w}0oa z=cjIG4yCng-tO}+`PO|G{m|0J)i8KF1QJ2;s)ug!76hx8{A(OIjRV*9W3tXd+*7l|Zj*Ny5CL=YPhV%)S~Ej7BNTslD&`M~Tdp;1ou`M! Sh=MW4K|m9EOd{bpOvf=6`93!Q literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 4d00f81a6f1443662c00606c60329c0643418da1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2929 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 K5sNL(-}x_00(O)D diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..9a129a6f535bcedfdca48ce4720887cb68b2ed24 GIT binary patch literal 2206 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..f3575dbf833450d7d5e792b46442f2a5198b5cdf GIT binary patch literal 5748 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 m-NJmr!^qBp(%J}NG%{wWqf?6eUrCr> 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..c4782dcc199dfe62afdd28f9e1995122f96692e0 GIT binary patch literal 8264 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 - + - #FAFAFA + #F8FDFF \ No newline at end of file From 0e401624a8f944147e8cca1032db2de2edee975c Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Mon, 30 Oct 2023 08:47:22 +0300 Subject: [PATCH 02/17] feat: new app name --- app/src/main/res/values/strings.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0b6b6e6b..bd840b16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,7 +16,7 @@ SGO app - Главная + че задали? Дневник Объявления Оценки @@ -176,4 +176,6 @@ Неофициальный порт электронного дневника «Сетевой город. Образование» на Android Тема Не заполнено + че задали? + Поделиться приложением \ No newline at end of file From 42b43eb7f88c1d92cbbaf79317873ed5a86ed043 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Mon, 30 Oct 2023 08:47:35 +0300 Subject: [PATCH 03/17] feat: update README.md --- README.md | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/README.md b/README.md index 5689fff3..771a03e9 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,8 @@ -![Frame 445](https://github.com/mezhendosina/sgo-app/assets/80736171/be87674d-33fb-4d52-ba69-7e843fc60bb0) - - [![GitHub release (latest by date)](https://img.shields.io/github/v/release/mezhendosina/che-zadali-app?style=for-the-badge)](https://github.com/mezhendosina/che-zadali-app/releases) -[SGO app](https://sgoapp.ru/) - это новый способ взаимодействия с электронным дневником «Сетевой город. Образование» на Android. -> Приложение не имеет отношения к «ИрТеху» - - -# Вся домашка в твоем телефоне - -https://github.com/mezhendosina/sgo-app/assets/80736171/163428b4-314c-4d1d-b329-1617ac246556 - -В SGO app можно не только узнать домашнее задание, но и отправить его учителю на проверку текстом или файлом - -# А за что оценка? - -В SGO app можно быстро узнать, за что поставлена оценка - -# Удобный просмотр оценок - -https://github.com/mezhendosina/sgo-app/assets/80736171/544d0ebf-3f1b-497a-a6a4-682c191d44de - -Можно посмотреть свои оценки и рассчитать их изменения после получения новых - -# Объявления - -https://github.com/mezhendosina/sgo-app/assets/80736171/412926b6-4aaf-47b2-8e9d-aa72a318faa7 - +че задали - это новый способ взаимодействия с электронным дневником «Сетевой город. Образование» на Android. -Быстро и удобно просматривай объявления от учителей и школьной администрации # Поддерживаемые регионы - Челябинская область From f27de943fef8d96d98d9659e50d04c6df421185d Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Mon, 30 Oct 2023 08:58:06 +0300 Subject: [PATCH 04/17] fix: debug --- .../mezhendosina/sgo/app/activities/LoginActivity.kt | 4 ++-- .../app/ui/gradesFlow/gradeItem/GradeItemFragment.kt | 4 ++-- .../journalFlow/containerLesson/LessonContainer.kt | 12 ++++++------ .../sgo/app/ui/main/container/ContainerFragment.kt | 10 +++++----- .../sgo/app/ui/settingsFlow/SettingsContainer.kt | 2 +- app/src/main/res/layout/container_announcements.xml | 2 +- app/src/main/res/layout/container_lesson.xml | 2 +- app/src/main/res/layout/container_login.xml | 2 +- app/src/main/res/layout/container_main.xml | 2 +- app/src/main/res/layout/container_settings.xml | 2 +- app/src/main/res/layout/fragment_grade_item.xml | 2 +- app/src/main/res/layout/item_lesson_toolbar.xml | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt b/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt index b0fc0c7e..4207cf94 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt @@ -47,13 +47,13 @@ class LoginActivity : AppCompatActivity() { onBackPressedDispatcher.addCallback(onBackPressedCallback) binding = ContainerLoginBinding.inflate(layoutInflater) setContentView(binding!!.root) - setSupportActionBar(binding!!.toolbar) + setSupportActionBar(binding!!.loginToolbar) supportActionBar?.setDisplayShowTitleEnabled(false) val navHost = supportFragmentManager.findFragmentById(R.id.fragmentContainer) as NavHostFragment navController = navHost.navController setupInsets(binding!!.root) - binding!!.toolbar.setupWithNavController(navController) + binding!!.loginToolbar.setupWithNavController(navController) } override fun onDestroy() { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemFragment.kt index a3d710a9..8e68e638 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemFragment.kt @@ -77,9 +77,9 @@ class GradeItemFragment : Fragment(R.layout.fragment_grade_item) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentGradeItemBinding.bind(view) - with(binding!!.toolbar) { + with(binding!!.gradeToolbar) { collapsingtoolbarlayout.title = lesson.name - toolbar.setNavigationOnClickListener { findTopNavController().popBackStack() } + itemToolbar.setNavigationOnClickListener { findTopNavController().popBackStack() } setLessonEmoji(requireContext(), lesson.name) } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt index 56322f5a..7cc567fb 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt @@ -56,13 +56,13 @@ class LessonContainer : Fragment(R.layout.container_lesson) { childFragmentManager.findFragmentById(binding!!.lessonFragmentContainer.id) ?.findNavController() - with(binding!!.toolbar) { + with(binding!!.lessonToolbar) { - toolbar.title = viewModel.lesson?.subjectName ?: "" + itemToolbar.title = viewModel.lesson?.subjectName ?: "" val lessonNameUiEntity = getEmojiLesson(viewModel.lesson?.subjectName ?: "") setLessonEmoji(requireContext(), lessonNameUiEntity?.nameId) - toolbar.setNavigationOnClickListener { + itemToolbar.setNavigationOnClickListener { if (innerNavController?.currentDestination?.id == innerNavController?.graph?.startDestinationId) findNavController().navigateUp() else innerNavController?.navigateUp() } @@ -73,7 +73,7 @@ class LessonContainer : Fragment(R.layout.container_lesson) { innerNavController?.addOnDestinationChangedListener { _, destination, args -> when (destination.id) { R.id.answerFragment -> { - binding!!.toolbar.appbarlayout.setExpanded(false) + binding!!.lessonToolbar.appbarlayout.setExpanded(false) with(binding!!.send) { when (args?.getString("action")) { ADD_ANSWER -> { @@ -92,7 +92,7 @@ class LessonContainer : Fragment(R.layout.container_lesson) { R.id.lessonFragment2 -> { binding!!.send.hide() - binding!!.toolbar.appbarlayout.setExpanded(true) + binding!!.lessonToolbar.appbarlayout.setExpanded(true) } } } @@ -110,7 +110,7 @@ class LessonContainer : Fragment(R.layout.container_lesson) { override fun onDestroy() { super.onDestroy() - TransitionManager.endTransitions(binding!!.toolbar.root) + TransitionManager.endTransitions(binding!!.lessonToolbar.root) binding = null } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt index 2933fb95..96eb6841 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt @@ -125,8 +125,8 @@ class ContainerFragment val journalPagerAdapter = JournalPagerAdapter(childFragmentManager, viewLifecycleOwner.lifecycle) - binding.gradesTopBar.root.background = binding.toolbar.background - binding.toolbar.setOnMenuItemClickListener { setupOnMenuItemClickListener(it) } + binding.gradesTopBar.root.background = binding.mainToolbar.background + binding.mainToolbar.setOnMenuItemClickListener { setupOnMenuItemClickListener(it) } binding.bottomNavigation.setOnItemSelectedListener { onBottomNavItemClickListener(it) } with(binding.grades) { @@ -257,14 +257,14 @@ class ContainerFragment Singleton.mainContainerScreen.observe(viewLifecycleOwner) { when (it) { JOURNAL -> { - binding.toolbar.setTitle(R.string.journal) + binding.mainToolbar.setTitle(R.string.journal) binding.slideDownAnimation() binding.grades.root.visibility = View.GONE binding.journal.visibility = View.VISIBLE } GRADES -> { - binding.toolbar.setTitle(R.string.grades) + binding.mainToolbar.setTitle(R.string.grades) Singleton.updateGradeState.value = LoadStatus.UPDATE binding.slideUpAnimation() binding.journal.visibility = View.GONE @@ -320,7 +320,7 @@ class ContainerFragment ) modalSheet.show(childFragmentManager, UpdateBottomSheetFragment.TAG) } - if (updates.tagName != BuildConfig.VERSION_NAME) binding.toolbar.menu[0].isVisible = + if (updates.tagName != BuildConfig.VERSION_NAME) binding.mainToolbar.menu[0].isVisible = true } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsContainer.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsContainer.kt index 4893023d..a784ba98 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsContainer.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsContainer.kt @@ -57,7 +57,7 @@ class SettingsContainer : Fragment(R.layout.container_settings) { }.build() NavigationUI.setupWithNavController( - binding.toolbar, + binding.settingsToolbar, navController, appBarConfiguration ) diff --git a/app/src/main/res/layout/container_announcements.xml b/app/src/main/res/layout/container_announcements.xml index 077a42fe..fe4cbd31 100644 --- a/app/src/main/res/layout/container_announcements.xml +++ b/app/src/main/res/layout/container_announcements.xml @@ -45,7 +45,7 @@ app:maxLines="10"> diff --git a/app/src/main/res/layout/container_login.xml b/app/src/main/res/layout/container_login.xml index cacbf6ab..663fe0d3 100644 --- a/app/src/main/res/layout/container_login.xml +++ b/app/src/main/res/layout/container_login.xml @@ -31,7 +31,7 @@ app:layout_constraintTop_toTopOf="parent"> diff --git a/app/src/main/res/layout/item_lesson_toolbar.xml b/app/src/main/res/layout/item_lesson_toolbar.xml index 8d037879..2dddf27a 100644 --- a/app/src/main/res/layout/item_lesson_toolbar.xml +++ b/app/src/main/res/layout/item_lesson_toolbar.xml @@ -51,7 +51,7 @@ tool:src="@drawable/lesson_astronomy" /> Date: Mon, 30 Oct 2023 09:59:41 +0300 Subject: [PATCH 05/17] fix: debug --- .../AnnouncementsContainerFragment.kt | 2 +- .../sgo/app/ui/main/container/ContainerFragment.kt | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsContainer/AnnouncementsContainerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsContainer/AnnouncementsContainerFragment.kt index a3c27e12..7b4865a8 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsContainer/AnnouncementsContainerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsContainer/AnnouncementsContainerFragment.kt @@ -43,7 +43,7 @@ class AnnouncementsContainerFragment : Fragment(R.layout.container_announcements childFragmentManager.findFragmentById(binding!!.lessonFragmentContainer.id) ?.findNavController() - binding!!.toolbar.setNavigationOnClickListener { + binding!!.announcementToolbar.setNavigationOnClickListener { if (innerNavController?.currentDestination?.id == innerNavController?.graph?.startDestinationId) { findTopNavController().navigateUp() } else { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt index 96eb6841..e891b676 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt @@ -16,6 +16,7 @@ package com.mezhendosina.sgo.app.ui.main.container +import android.content.Context import android.content.Intent import android.os.Bundle import android.transition.TransitionManager @@ -167,6 +168,11 @@ class ContainerFragment observeShowEngageDialog() } + override fun onAttach(context: Context) { + super.onAttach(context) + + } + override fun onDestroy() { super.onDestroy() file.delete() @@ -271,7 +277,8 @@ class ContainerFragment binding.grades.root.visibility = View.VISIBLE CoroutineScope(Dispatchers.IO).launch { gradesFilterViewModel.getYearsList() - gradesFilterViewModel.getGradeSort(requireContext()) + if (this@ContainerFragment.context != null) + gradesFilterViewModel.getGradeSort(requireContext()) } } } From c02e3b9df5e13d61919c34b3f7c44c7759ce89f5 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Mon, 30 Oct 2023 10:16:20 +0300 Subject: [PATCH 06/17] fix: debug --- .../sgo/app/ui/journalFlow/answer/AnswerViewModel.kt | 2 +- .../sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt index 4df16508..95f789dc 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt @@ -41,7 +41,7 @@ class AnswerViewModel( } fun getHomework() = - if (Singleton.lesson != null) Singleton.lesson!!.homework!!.assignmentName + if (Singleton.lesson?.homework != null) Singleton.lesson!!.homework!!.assignmentName else if (Singleton.pastMandatoryItem != null) Singleton.pastMandatoryItem!!.assignmentName else null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt index b0bb1e52..a2230946 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt @@ -23,7 +23,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.transition.platform.MaterialSharedAxis +import com.google.android.material.transition.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentChooseUserIdBinding import com.mezhendosina.sgo.app.ui.loginFlow.gosuslugiResult.GosuslugiResultFragment From ddd7ecb9964ecb5449f14de6b9e01732443847e4 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Mon, 13 Nov 2023 10:44:43 +0300 Subject: [PATCH 07/17] fix: show all found schools if filtered schools is empty --- .../sgo/data/netschool/api/login/RetrofitLoginSource.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/RetrofitLoginSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/RetrofitLoginSource.kt index 26a23297..05f26491 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/RetrofitLoginSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/RetrofitLoginSource.kt @@ -41,7 +41,9 @@ class RetrofitLoginSource( wrapRetrofitExceptions(false) { val schools = loginApi.getSchools(query) - schools.filter { it.name.contains("(МБОУ)|(МКОУ)|(СОШ)|(МАОУ)|(МКШ)|(СУНЦ)|(ШНОР)|(ШВОР)|(ГБПОУ)|(ГКОУ)|(ГБОУ)".toRegex()) } + val filterSchools = + schools.filter { it.name.contains("МБОУ|МКОУ|СОШ|МАОУ|МКШ|СУНЦ|ШНОР|ШВОР|ГБПОУ|ГКОУ|ГБОУ|МОУ".toRegex()) } + if (schools.isNotEmpty() && filterSchools.isEmpty()) schools else filterSchools } override suspend fun getData(): GetDataResponseEntity = From 3ed631e9a5350321bcf03b8684771994f77fefdd Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Mon, 27 Nov 2023 17:54:34 +0300 Subject: [PATCH 08/17] feat: add di --- app/build.gradle | 13 +- .../sgo/app/NetSchoolUnitTests.kt | 210 +++++++++--------- app/src/main/AndroidManifest.xml | 1 + .../java/com/mezhendosina/sgo/Singleton.kt | 8 +- .../sgo/app/activities/LoginActivity.kt | 2 + .../sgo/app/activities/MainActivity.kt | 7 +- .../sgo/app/activities/MainViewModel.kt | 30 ++- .../sgo/app/activities/SplashActivity.kt | 21 +- .../sgo/app/model/ContainerRepository.kt | 9 +- .../announcements/AnnouncementsRepository.kt | 10 +- .../sgo/app/model/answer/AnswerRepository.kt | 3 +- .../attachments/AttachmentsRepository.kt | 72 ++---- .../app/model/attachments/AttachmentsUtils.kt | 50 +++++ .../sgo/app/model/grades/GradesRepository.kt | 13 +- .../app/model/journal/JournalRepository.kt | 8 +- .../announcements/AnnouncementsFragment.kt | 2 + .../AnnouncementsAdapter.kt | 3 + .../BottomSheetAnnouncementsViewModel.kt | 8 +- .../AnnouncementsContainerFragment.kt | 2 + .../AnnouncementsFragmentViewModel.kt | 28 +-- .../AnnouncementsItemFragment.kt | 8 +- .../ChooseUserIdBottomSheet.kt | 7 +- .../ChooseYearAdapter.kt | 3 + .../ChooseYearBottomSheet.kt | 2 + .../ui/gradesFlow/filter/FilterBottomSheet.kt | 2 + .../filter/GradesFilterViewModel.kt | 27 ++- .../gradeItem/CalculateGradeAdapter.kt | 3 + .../gradesFlow/gradeItem/CountGradeAdapter.kt | 3 + .../gradesFlow/gradeItem/GradeItemFragment.kt | 2 + .../gradeItem/GradeItemViewModel.kt | 5 +- .../app/ui/gradesFlow/grades/GradeAdapter.kt | 3 + .../ui/gradesFlow/grades/GradesFragment.kt | 4 +- .../ui/gradesFlow/grades/GradesViewModel.kt | 32 +-- .../journalFlow/answer/AnswerFileAdapter.kt | 3 + .../ui/journalFlow/answer/AnswerFragment.kt | 2 + .../ui/journalFlow/answer/AnswerViewModel.kt | 21 +- .../containerLesson/LessonContainer.kt | 2 + .../LessonContainerViewModel.kt | 16 +- .../ui/journalFlow/journal/JournalFragment.kt | 8 +- .../journal/JournalPagerAdapter.kt | 3 + .../journalItem/JournalItemFragment.kt | 2 + .../journalItem/JournalItemViewModel.kt | 14 +- .../journalItem/adapters/DiaryAdapter.kt | 3 + .../journalItem/adapters/HomeworkAdapter.kt | 3 + .../adapters/HomeworkGradeAdapter.kt | 3 + .../adapters/PastMandatoryAdapter.kt | 3 + .../journalFlow/lessonItem/LessonFragment.kt | 11 +- .../journalFlow/lessonItem/LessonViewModel.kt | 125 ++--------- .../journalFlow/lessonItem/WhyGradeAdapter.kt | 3 + .../chooseRegion/ChooseRegionAdapter.kt | 3 + .../chooseRegion/ChooseRegionFragment.kt | 2 + .../chooseRegion/ChooseRegionViewModel.kt | 17 +- .../chooseSchool/ChooseSchoolAdapter.kt | 3 + .../chooseSchool/ChooseSchoolFragment.kt | 2 + .../chooseSchool/ChooseSchoolViewModel.kt | 8 +- .../chooseUserId/ChooseUserIdFragment.kt | 2 + .../chooseUserId/ChooseUserIdViewModel.kt | 12 +- .../loginFlow/chooseUserId/UserIdAdapter.kt | 3 + .../loginFlow/gosuslugi/GosuslugiFragment.kt | 17 +- .../loginFlow/gosuslugi/GosuslugiViewModel.kt | 9 +- .../GosuslugiResultFragment.kt | 11 +- .../GosuslugiResultViewModel.kt | 14 +- .../app/ui/loginFlow/login/LoginFragment.kt | 17 +- .../app/ui/loginFlow/login/LoginViewModel.kt | 21 +- .../requestRegion/RequestRegionFragment.kt | 2 + .../requestRegion/RequestRegionViewModel.kt | 5 +- .../ui/loginFlow/welcome/WelcomeFragment.kt | 2 + .../ui/main/container/ContainerFragment.kt | 28 ++- .../ui/main/container/ContainerViewModel.kt | 32 +-- .../UpdateBottomSheetFragment.kt | 2 +- .../app/ui/settingsFlow/SettingsFragment.kt | 15 +- .../app/ui/settingsFlow/SettingsViewModel.kt | 123 +--------- .../changeEmail/ChangeEmailViewModel.kt | 9 +- .../changePassword/ChangePasswordFragment.kt | 12 +- .../changePassword/ChangePasswordViewModel.kt | 23 +- .../changePhone/ChangePhoneViewModel.kt | 8 +- .../changeTheme/ChangeThemeFragment.kt | 2 +- .../changeTheme/ChangeThemeViewModel.kt | 12 +- .../com/mezhendosina/sgo/data/AppSettings.kt | 22 ++ .../sgo/data/SettingsDataStore.kt | 34 ++- .../sgo/data/netschool/NetSchoolSingleton.kt | 84 +------ .../RetrofitAnnouncementsSource.kt | 5 +- .../attachments/RetrofitAttachmentsSource.kt | 5 +- .../api/diary/RetrofitDiarySource.kt | 5 +- ...adesService.kt => RetrofitGradesSource.kt} | 6 +- .../api/homework/RetrofitHomeworkSource.kt | 5 +- .../sgo/data/netschool/api/login/LoginApi.kt | 3 + .../data/netschool/api/login/LoginSource.kt | 6 +- .../api/login/RetrofitLoginSource.kt | 16 +- .../api/settings/RetrofitSettingsSource.kt | 11 +- .../netschool/api/settings/SettingsSource.kt | 5 +- .../data/netschool/base/BaseRetrofitSource.kt | 20 +- .../sgo/data/netschool/base/Exceptions.kt | 2 +- .../sgo/data/netschool/base/RetrofitConfig.kt | 8 +- .../netschool/base/RetrofitSourcesProvider.kt | 68 ------ .../netschool/base/SourceProviderHolder.kt | 147 ------------ .../data/netschool/base/SourcesProvider.kt | 45 ---- .../data/netschool/repo/LessonRepository.kt | 15 +- .../data/netschool/repo/LoginRepository.kt | 96 +++++--- .../data/netschool/repo/RegionsRepository.kt | 10 +- .../data/netschool/repo/SettingsRepository.kt | 58 +++-- .../com/mezhendosina/sgo/di/BaseRetrofit.kt | 95 ++++++++ .../mezhendosina/sgo/di/BaseUrlInterceptor.kt | 29 +++ .../mezhendosina/sgo/di/HeadersInterceptor.kt | 47 ++++ .../com/mezhendosina/sgo/di/MyCookieJar.kt | 43 ++++ .../com/mezhendosina/sgo/di/SettingsModule.kt | 18 ++ .../com/mezhendosina/sgo/di/SourcesModule.kt | 60 +++++ .../mezhendosina/sgo/di/StateInterceptor.kt | 21 ++ build.gradle | 2 + 109 files changed, 1189 insertions(+), 1041 deletions(-) create mode 100644 app/src/main/java/com/mezhendosina/sgo/data/AppSettings.kt rename app/src/main/java/com/mezhendosina/sgo/data/netschool/api/grades/{RetrofitGradesService.kt => RetrofitGradesSource.kt} (89%) delete mode 100644 app/src/main/java/com/mezhendosina/sgo/data/netschool/base/RetrofitSourcesProvider.kt delete mode 100644 app/src/main/java/com/mezhendosina/sgo/data/netschool/base/SourceProviderHolder.kt delete mode 100644 app/src/main/java/com/mezhendosina/sgo/data/netschool/base/SourcesProvider.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/di/BaseRetrofit.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/di/BaseUrlInterceptor.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/di/HeadersInterceptor.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/di/MyCookieJar.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/di/SettingsModule.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/di/SourcesModule.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/di/StateInterceptor.kt diff --git a/app/build.gradle b/app/build.gradle index 4cd651c2..29898235 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,8 @@ plugins { id 'com.google.firebase.firebase-perf' id 'com.google.firebase.crashlytics' -// id "kotlin-kapt" + id 'kotlin-kapt' + id 'com.google.dagger.hilt.android' } android { @@ -67,11 +68,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } viewBinding { enabled true @@ -176,4 +177,8 @@ dependencies { // get color pallete from image implementation 'androidx.palette:palette-ktx:1.0.0' + // hilt + implementation "com.google.dagger:hilt-android:2.48.1" + kapt "com.google.dagger:hilt-compiler:2.48.1" + } \ No newline at end of file diff --git a/app/src/androidTest/java/com/mezhendosina/sgo/app/NetSchoolUnitTests.kt b/app/src/androidTest/java/com/mezhendosina/sgo/app/NetSchoolUnitTests.kt index d23cf96b..7c319814 100644 --- a/app/src/androidTest/java/com/mezhendosina/sgo/app/NetSchoolUnitTests.kt +++ b/app/src/androidTest/java/com/mezhendosina/sgo/app/NetSchoolUnitTests.kt @@ -1,106 +1,106 @@ -/* - * Copyright 2023 Eugene Menshenin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - +///* +// * Copyright 2023 Eugene Menshenin +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// package com.mezhendosina.sgo.app - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import com.mezhendosina.sgo.app.netschool.api.login.entities.SchoolEntity -import com.mezhendosina.sgo.data.netschool.NetSchoolExpectedResults -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton -import junit.framework.TestCase.assertEquals -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class NetSchoolUnitTests : NetSchoolTestInterface { - - private val netSchoolSingleton = NetSchoolSingleton - - @Before - override fun login() { - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - val login = System.getenv("SGO_LOGIN") - val password = System.getenv("SGO_PASSWORD") - runBlocking { - if (!login.isNullOrEmpty() && !password.isNullOrEmpty()) { - netSchoolSingleton.loginRepository.login( - appContext, - login, - password, - 89 - ) - } else { - throw RuntimeException("Login or password is null or empty") - } - } - } - - @Test - override fun region() { - assertEquals( - NetSchoolExpectedResults.region, - netSchoolSingleton.regionsRepository.getRegions() - ) - } - - @Test - override fun school() { - runBlocking { - netSchoolSingleton.loginRepository.findSchool("68") - val itemsListType = object : TypeToken>() {}.type - assertEquals( - netSchoolSingleton.schools, - Gson().fromJson(NetSchoolExpectedResults.schools, itemsListType) - ) - } - } - - @Test - override fun announcements() { - runBlocking { - netSchoolSingleton.announcementsRepository.announcements() - } - } - - @Test - override fun attachments() { - - } - - @Test - override fun grades() { - } - - @Test - override fun diary() { - } - - @Test - override fun settings() { - } - - @After - override fun logout() { - } - - -} \ No newline at end of file +// +//import androidx.test.ext.junit.runners.AndroidJUnit4 +//import androidx.test.platform.app.InstrumentationRegistry +//import com.google.gson.Gson +//import com.google.gson.reflect.TypeToken +//import com.mezhendosina.sgo.app.netschool.api.login.entities.SchoolEntity +//import com.mezhendosina.sgo.data.netschool.NetSchoolExpectedResults +//import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +//import junit.framework.TestCase.assertEquals +//import kotlinx.coroutines.runBlocking +//import org.junit.After +//import org.junit.Before +//import org.junit.Test +//import org.junit.runner.RunWith +// +//@RunWith(AndroidJUnit4::class) +//class NetSchoolUnitTests : NetSchoolTestInterface { +// +// private val netSchoolSingleton = NetSchoolSingleton +// +// @Before +// override fun login() { +// val appContext = InstrumentationRegistry.getInstrumentation().targetContext +// val login = System.getenv("SGO_LOGIN") +// val password = System.getenv("SGO_PASSWORD") +// runBlocking { +// if (!login.isNullOrEmpty() && !password.isNullOrEmpty()) { +// netSchoolSingleton.loginRepository.login( +// appContext, +// login, +// password, +// 89 +// ) +// } else { +// throw RuntimeException("Login or password is null or empty") +// } +// } +// } +// +// @Test +// override fun region() { +// assertEquals( +// NetSchoolExpectedResults.region, +// netSchoolSingleton.regionsRepository.getRegions() +// ) +// } +// +// @Test +// override fun school() { +// runBlocking { +// netSchoolSingleton.loginRepository.findSchool("68") +// val itemsListType = object : TypeToken>() {}.type +// assertEquals( +// netSchoolSingleton.schools, +// Gson().fromJson(NetSchoolExpectedResults.schools, itemsListType) +// ) +// } +// } +// +// @Test +// override fun announcements() { +// runBlocking { +// netSchoolSingleton.announcementsRepository.announcements() +// } +// } +// +// @Test +// override fun attachments() { +// +// } +// +// @Test +// override fun grades() { +// } +// +// @Test +// override fun diary() { +// } +// +// @Test +// override fun settings() { +// } +// +// @After +// override fun logout() { +// } +// +// +//} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index db7ed54a..d55a9515 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,6 +27,7 @@ android:maxSdkVersion="28" /> = emptyList() var selectedAnnouncement: AnnouncementsResponseEntity? = null @@ -65,11 +67,5 @@ object Singleton { val updateGradeState = MutableLiveData() val mainContainerScreen = MutableLiveData(ContainerFragment.JOURNAL) - - // --- database -// val database: AppDatabase by lazy { -// Room.databaseBuilder(applicationContext, AppDatabase::class.java, "database.db").build() -// } - } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt b/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt index 4207cf94..4b275114 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt @@ -25,7 +25,9 @@ import androidx.navigation.ui.setupWithNavController import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.ContainerLoginBinding import com.mezhendosina.sgo.app.utils.setupInsets +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class LoginActivity : AppCompatActivity() { private lateinit var navController: NavController diff --git a/app/src/main/java/com/mezhendosina/sgo/app/activities/MainActivity.kt b/app/src/main/java/com/mezhendosina/sgo/app/activities/MainActivity.kt index bcd798a9..2a6d1727 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/activities/MainActivity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/activities/MainActivity.kt @@ -34,12 +34,13 @@ import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.databinding.ContainerMainActivityBinding import com.mezhendosina.sgo.app.utils.errorDialog import com.mezhendosina.sgo.app.utils.setupInsets +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext - +@AndroidEntryPoint class MainActivity : AppCompatActivity() { private lateinit var binding: ContainerMainActivityBinding @@ -87,7 +88,7 @@ class MainActivity : AppCompatActivity() { errorDialog(this@MainActivity, it) } } - viewModel.login(this@MainActivity) + viewModel.login() withContext(Dispatchers.Main) { TransitionManager.beginDelayedTransition( @@ -106,7 +107,7 @@ class MainActivity : AppCompatActivity() { override fun onRestart() { super.onRestart() CoroutineScope(Dispatchers.IO).launch { - viewModel.login(this@MainActivity) + viewModel.login() } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/activities/MainViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/activities/MainViewModel.kt index e7380162..ca3cf3ba 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/activities/MainViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/activities/MainViewModel.kt @@ -22,40 +22,36 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class MainViewModel( - private val loginRepository: LoginRepository = NetSchoolSingleton.loginRepository + +@HiltViewModel +class MainViewModel +@Inject constructor( + private val loginRepository: LoginRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _errorMessage = MutableLiveData() val errorMessage: LiveData = _errorMessage - suspend fun login(context: Context) { + suspend fun login() { try { - val login = SettingsDataStore.LOGIN.getValue(context, "").first() + val login = settingsDataStore.getValue(SettingsDataStore.LOGIN).first() ?: "" if (login.isEmpty()) { - loginRepository.gosuslugiLogin( - context, - SettingsDataStore.ESIA_LOGIN_STATE.getValue(context, "").first(), - SettingsDataStore.ESIA_USER_ID.getValue(context, "").first() - ) + loginRepository.gosuslugiLogin() } else { - loginRepository.login( - context, - SettingsDataStore.LOGIN.getValue(context, "").first(), - SettingsDataStore.PASSWORD.getValue(context, "").first(), - SettingsDataStore.SCHOOL_ID.getValue(context, -1).first(), - false - ) + loginRepository.login(firstLogin = false) } } catch (e: Exception) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/activities/SplashActivity.kt b/app/src/main/java/com/mezhendosina/sgo/app/activities/SplashActivity.kt index 0f691212..b5ce09d4 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/activities/SplashActivity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/activities/SplashActivity.kt @@ -19,32 +19,33 @@ package com.mezhendosina.sgo.app.activities import android.app.Activity import android.content.Intent import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import com.google.android.material.color.DynamicColors import com.google.firebase.FirebaseApp import com.mezhendosina.sgo.app.BuildConfig +import com.mezhendosina.sgo.data.AppSettings import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.getValue -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import javax.inject.Inject -class SplashActivity : Activity() { +@AndroidEntryPoint +class SplashActivity : AppCompatActivity() { + + @Inject lateinit var settingsDataStore: AppSettings override fun onCreate(savedInstanceState: Bundle?) { runBlocking { AppCompatDelegate.setDefaultNightMode( - SettingsDataStore.THEME.getValue( - this@SplashActivity, - AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - ).first() + settingsDataStore.getValue(SettingsDataStore.THEME).first() + ?: AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM ) - NetSchoolSingleton.baseUrl = - SettingsDataStore.REGION_URL.getValue(this@SplashActivity, "").first() } if (!BuildConfig.DEBUG) DynamicColors.applyToActivitiesIfAvailable(this.application) // DynamicColors.applyToActivitiesIfAvailable(this.application) @@ -55,7 +56,7 @@ class SplashActivity : Activity() { FirebaseApp.initializeApp(this@SplashActivity) val intent = - if (SettingsDataStore.LOGGED_IN.getValue(this@SplashActivity, false).first()) { + if (settingsDataStore.getValue(SettingsDataStore.LOGGED_IN).first() == true) { Intent(this@SplashActivity, MainActivity::class.java) } else { Intent(this@SplashActivity, LoginActivity::class.java) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/ContainerRepository.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/ContainerRepository.kt index edf22a0b..db18aa24 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/ContainerRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/ContainerRepository.kt @@ -22,6 +22,10 @@ import com.mezhendosina.sgo.data.netschool.base.Download import com.mezhendosina.sgo.data.netschool.base.RetrofitConfig import com.mezhendosina.sgo.data.netschool.base.downloadToFileWithProgress import com.mezhendosina.sgo.data.requests.github.checkUpdates.CheckUpdates +import dagger.Module +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.flow.Flow import okhttp3.OkHttpClient import okhttp3.ResponseBody @@ -32,6 +36,7 @@ import retrofit2.http.GET import retrofit2.http.Streaming import retrofit2.http.Url import java.io.File +import javax.inject.Inject interface UpdateApi { @@ -43,7 +48,9 @@ interface UpdateApi { suspend fun downloadFile(@Url url: String): ResponseBody } -class ContainerRepository { +@Module +@InstallIn(SingletonComponent::class) +class ContainerRepository @Inject constructor() { private val loginInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC) private val client = OkHttpClient.Builder() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/announcements/AnnouncementsRepository.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/announcements/AnnouncementsRepository.kt index fd10258a..48f677f6 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/announcements/AnnouncementsRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/announcements/AnnouncementsRepository.kt @@ -18,12 +18,20 @@ package com.mezhendosina.sgo.app.model.announcements import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.data.netschool.api.announcements.AnnouncementsResponseEntity +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject typealias AnnouncementsActionListener = (announcements: List) -> Unit -class AnnouncementsRepository( +@Module +@InstallIn(SingletonComponent::class) +class AnnouncementsRepository +@Inject constructor( private val announcementsSource: AnnouncementsSource ) { private var announcements = mutableListOf() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/answer/AnswerRepository.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/answer/AnswerRepository.kt index 4465d04f..607bbcb5 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/answer/AnswerRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/answer/AnswerRepository.kt @@ -27,8 +27,9 @@ import kotlinx.coroutines.withContext import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.toRequestBody +import javax.inject.Inject -class AnswerRepository( +class AnswerRepository @Inject constructor( private val attachmentsSource: AttachmentsSource, private val lessonRepository: LessonRepository ) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsRepository.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsRepository.kt index ceb4ba72..78e1a10b 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsRepository.kt @@ -23,30 +23,35 @@ import android.webkit.MimeTypeMap import androidx.core.content.FileProvider import com.mezhendosina.sgo.app.utils.PermissionNotGranted import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.qualifiers.ActivityContext +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.android.scopes.ActivityScoped +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.withContext import java.io.File +import javax.inject.Inject +import javax.inject.Singleton -class AttachmentsRepository( +@Module +@InstallIn(SingletonComponent::class) +class AttachmentsRepository +@Inject constructor( private val attachmentsSource: AttachmentsSource ) { - suspend fun downloadAttachment( - context: Context, - attachmentId: Int, - attachmentName: String, - ) { - if (!AttachmentsUtils.checkPermissions(context)) throw PermissionNotGranted() - val downloadsFolder = AttachmentsUtils.getDownloadsFolder(context) - val file = File(downloadsFolder, attachmentName) - val isExist = withContext(Dispatchers.IO) { - file.createNewFile() - } - - val a = if (isExist) CoroutineScope(Dispatchers.IO).async { + suspend fun downloadAttachment( + file: File?, + attachmentId: Int, + ): File? { + val a = if (file != null) CoroutineScope(Dispatchers.IO).async { attachmentsSource.downloadAttachment( attachmentId, file @@ -54,38 +59,9 @@ class AttachmentsRepository( } else null a?.await() - openFile(context, file) - } - - private fun openFile(context: Context, file: File) { - val contentType = getMimeType(file.toURI().toString()) - - val fileUri = FileProvider.getUriForFile( - context, - context.applicationContext.packageName + ".provider", - file - ) - - val intent = Intent(Intent.ACTION_VIEW).apply { - setDataAndType(fileUri, contentType) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } - context.startActivity(intent) + return file } - fun openFile(context: Context, file: Uri) { - val contentType = getMimeType(file.toString()) - - val intent = Intent(Intent.ACTION_VIEW).apply { - setDataAndType(file, contentType) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } - context.startActivity(intent) - } - - suspend fun deleteAttachment(assignmentId: Int, attachmentId: Int) { attachmentsSource.deleteAttachment(assignmentId, attachmentId) } @@ -94,12 +70,4 @@ class AttachmentsRepository( attachmentsSource.editAttachmentDescription(attachmentId, description) - private fun getMimeType(url: String?): String? { - var type: String? = null - val extension = MimeTypeMap.getFileExtensionFromUrl(url) - if (extension != null) { - type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) - } - return type - } } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsUtils.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsUtils.kt index 80154400..f9b78096 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsUtils.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsUtils.kt @@ -18,14 +18,36 @@ package com.mezhendosina.sgo.app.model.attachments import android.Manifest import android.content.Context +import android.content.Intent import android.content.pm.PackageManager import android.os.Environment +import android.webkit.MimeTypeMap import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider +import com.mezhendosina.sgo.app.utils.PermissionNotGranted +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.io.File class AttachmentsUtils { companion object { + + + fun getFile(context: Context, name: String): File { + if (!checkPermissions(context)) throw PermissionNotGranted() + val downloadsFolder = getDownloadsFolder(context) + return File(downloadsFolder, name) + } + + suspend fun createFile(context: Context, name: String): File? { + val file = getFile(context, name) + val isExist = withContext(Dispatchers.IO) { + file.createNewFile() + } + return if (isExist) file else null + } + fun checkPermissions(context: Context) = ContextCompat.checkSelfPermission( context, @@ -39,6 +61,34 @@ class AttachmentsUtils { appDownloadFolder.mkdir() return appDownloadFolder } + + fun openFile(context: Context, file: File) { + val contentType = getMimeType(file.toURI().toString()) + + val fileUri = FileProvider.getUriForFile( + context, + context.applicationContext.packageName + ".provider", + file + ) + + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(fileUri, contentType) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + context.startActivity(intent) + } + + private fun getMimeType(url: String?): String? { + var type: String? = null + val extension = MimeTypeMap.getFileExtensionFromUrl(url) + if (extension != null) { + type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + } + return type + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/grades/GradesRepository.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/grades/GradesRepository.kt index f8b023bc..65938e7f 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/grades/GradesRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/grades/GradesRepository.kt @@ -22,12 +22,19 @@ import com.mezhendosina.sgo.data.grades.GradesFromHtml import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem import com.mezhendosina.sgo.data.netschool.api.grades.entities.gradeOptions.GradeOptions +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject typealias GradeActionListener = (grade: List) -> Unit -class GradesRepository( +@Module +@InstallIn(SingletonComponent::class) +class GradesRepository + @Inject constructor( private val gradesSource: GradesSource, private val diarySource: DiarySource ) { @@ -38,13 +45,13 @@ class GradesRepository( suspend fun loadGradesOptions(): GradeOptions { val parentInfoLetter = - gradesSource.getParentInfoLetter(NetSchoolSingleton.at).body()?.string() ?: "" + gradesSource.getParentInfoLetter(Singleton.at).body()?.string() ?: "" return GradesFromHtml().getOptions(parentInfoLetter) } suspend fun loadGrades(gradeOptions: GradeOptions, termid: String, sortType: Int) { - val at = NetSchoolSingleton.at + val at = Singleton.at val getGradesRequest = gradesSource.getGrades( at, gradeOptions.PCLID.value, diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/journal/JournalRepository.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/journal/JournalRepository.kt index 9e25e685..66f2b993 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/journal/JournalRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/journal/JournalRepository.kt @@ -30,8 +30,14 @@ import com.mezhendosina.sgo.data.netschool.api.diary.entities.Assignment import com.mezhendosina.sgo.data.netschool.api.diary.entities.DiaryResponseEntity import com.mezhendosina.sgo.data.netschool.api.diary.entities.Lesson import com.mezhendosina.sgo.data.netschool.api.diary.entities.PastMandatoryEntity +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Inject -class JournalRepository( +@Module +@InstallIn(SingletonComponent::class) +class JournalRepository @Inject constructor( private val attachmentsSource: AttachmentsSource, private val diarySource: DiarySource ) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcements/AnnouncementsFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcements/AnnouncementsFragment.kt index e20ce5b8..8097f9a5 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcements/AnnouncementsFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcements/AnnouncementsFragment.kt @@ -28,7 +28,9 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentAnnouncementsBinding import com.mezhendosina.sgo.app.ui.announcementsFlow.announcementsBottomSheet.AnnouncementsAdapter import com.mezhendosina.sgo.app.ui.announcementsFlow.announcementsBottomSheet.BottomSheetAnnouncementsViewModel +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class AnnouncementsFragment : Fragment(R.layout.fragment_announcements) { private var binding: FragmentAnnouncementsBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsBottomSheet/AnnouncementsAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsBottomSheet/AnnouncementsAdapter.kt index ee917a8f..7755037b 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsBottomSheet/AnnouncementsAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsBottomSheet/AnnouncementsAdapter.kt @@ -23,10 +23,13 @@ import androidx.core.text.parseAsHtml import androidx.recyclerview.widget.RecyclerView import com.mezhendosina.sgo.app.databinding.ItemAnnouncementBinding import com.mezhendosina.sgo.data.netschool.api.announcements.AnnouncementsResponseEntity +import dagger.Module import org.jsoup.Jsoup +import javax.inject.Singleton typealias OnAnnouncementClickListener = (AnnouncementsResponseEntity) -> Unit +@Singleton class AnnouncementsAdapter( private val onAnnouncementClickListener: OnAnnouncementClickListener, ) : diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsBottomSheet/BottomSheetAnnouncementsViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsBottomSheet/BottomSheetAnnouncementsViewModel.kt index ff1dccba..12f54402 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsBottomSheet/BottomSheetAnnouncementsViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsBottomSheet/BottomSheetAnnouncementsViewModel.kt @@ -25,13 +25,17 @@ import com.mezhendosina.sgo.app.model.announcements.AnnouncementsRepository import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.announcements.AnnouncementsResponseEntity +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class BottomSheetAnnouncementsViewModel( - private val announcementsRepository: AnnouncementsRepository = NetSchoolSingleton.announcementsRepository +@HiltViewModel +class BottomSheetAnnouncementsViewModel +@Inject constructor( + private val announcementsRepository: AnnouncementsRepository ) : ViewModel() { private val _loading = MutableLiveData(false) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsContainer/AnnouncementsContainerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsContainer/AnnouncementsContainerFragment.kt index 7b4865a8..504f81c2 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsContainer/AnnouncementsContainerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsContainer/AnnouncementsContainerFragment.kt @@ -25,7 +25,9 @@ import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.ContainerAnnouncementsBinding import com.mezhendosina.sgo.app.utils.findTopNavController +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class AnnouncementsContainerFragment : Fragment(R.layout.container_announcements) { private var binding: ContainerAnnouncementsBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsFragmentViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsFragmentViewModel.kt index cdf3e1d4..5585e7be 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsFragmentViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsFragmentViewModel.kt @@ -22,39 +22,31 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.mezhendosina.sgo.app.model.answer.FileUiEntity import com.mezhendosina.sgo.app.model.attachments.AttachmentsRepository +import com.mezhendosina.sgo.app.model.attachments.AttachmentsUtils import com.mezhendosina.sgo.app.utils.PermissionNotGranted import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject -class AnnouncementsFragmentViewModel( - private val attachmentsRepository: AttachmentsRepository = NetSchoolSingleton.attachmentsRepository +@HiltViewModel +class AnnouncementsFragmentViewModel +@Inject constructor( + private val attachmentsRepository: AttachmentsRepository ) : ViewModel() { private val _errorMessage = MutableLiveData() val errorMessage: LiveData = _errorMessage suspend fun downloadAttachment( - context: Context, attachment: FileUiEntity, + context: Context ) { - try { - attachmentsRepository.downloadAttachment( - context, - attachment.id!!, - attachment.fileName - ) - } catch (e: Exception) { - if (e is PermissionNotGranted) { - throw PermissionNotGranted() - } else { - withContext(Dispatchers.Main) { - _errorMessage.value = e.toDescription() - } - } - } + TODO() } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsItemFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsItemFragment.kt index 73beb7e7..2299bae8 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsItemFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsItemFragment.kt @@ -35,6 +35,7 @@ import com.mezhendosina.sgo.app.utils.AttachmentAdapter import com.mezhendosina.sgo.app.utils.AttachmentClickListener import com.mezhendosina.sgo.data.DateManipulation import com.mezhendosina.sgo.data.netschool.api.announcements.AnnouncementsResponseEntity +import dagger.hilt.android.AndroidEntryPoint import io.noties.markwon.Markwon import io.noties.markwon.html.HtmlPlugin import kotlinx.coroutines.CoroutineScope @@ -43,6 +44,7 @@ import kotlinx.coroutines.launch import org.jsoup.Jsoup +@AndroidEntryPoint class AnnouncementsItemFragment : Fragment(R.layout.fragment_announcement_item) { private lateinit var binding: FragmentAnnouncementItemBinding @@ -94,10 +96,8 @@ class AnnouncementsItemFragment : Fragment(R.layout.fragment_announcement_item) requireContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) if (permission == PackageManager.PERMISSION_GRANTED) { CoroutineScope(Dispatchers.Main).launch { - viewModel.downloadAttachment( - requireContext(), - attachment, - ) + TODO() +// viewModel.downloadAttachment(attachment) } } else { storagePermission.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseUserIdBottomSheet/ChooseUserIdBottomSheet.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseUserIdBottomSheet/ChooseUserIdBottomSheet.kt index 5b3f098b..35d4b376 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseUserIdBottomSheet/ChooseUserIdBottomSheet.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseUserIdBottomSheet/ChooseUserIdBottomSheet.kt @@ -25,17 +25,20 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentChooseUserIdBinding import com.mezhendosina.sgo.app.ui.loginFlow.chooseUserId.UserIdAdapter import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.editPreference +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch +import javax.inject.Inject +@AndroidEntryPoint class ChooseUserIdBottomSheet : BottomSheetDialogFragment(R.layout.fragment_choose_user_id) { private lateinit var binding: FragmentChooseUserIdBinding + @Inject lateinit var settingsDataStore: SettingsDataStore private val adapter = UserIdAdapter { lifecycleScope.launch { - SettingsDataStore.CURRENT_USER_ID.editPreference(requireContext(), it.userId ?: -1) + settingsDataStore.setValue(SettingsDataStore.CURRENT_USER_ID, it.userId ?: -1) } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseYearBottomSheet/ChooseYearAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseYearBottomSheet/ChooseYearAdapter.kt index aa865573..3aac5803 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseYearBottomSheet/ChooseYearAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseYearBottomSheet/ChooseYearAdapter.kt @@ -22,9 +22,12 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.mezhendosina.sgo.app.databinding.ItemChooseYearBinding import com.mezhendosina.sgo.data.netschool.api.settings.entities.YearListResponseEntity +import dagger.Module +import javax.inject.Singleton typealias OnYearClickListener = (year: YearListResponseEntity) -> Unit +@Singleton class ChooseYearAdapter( private val onYearClickListener: OnYearClickListener ) : RecyclerView.Adapter(), diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseYearBottomSheet/ChooseYearBottomSheet.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseYearBottomSheet/ChooseYearBottomSheet.kt index 4ffe99e7..cfa48e19 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseYearBottomSheet/ChooseYearBottomSheet.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/chooseYearBottomSheet/ChooseYearBottomSheet.kt @@ -24,7 +24,9 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.BottomSheetChooseYearBinding import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.settings.entities.YearListResponseEntity +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class ChooseYearBottomSheet( private val yearList: List ) : BottomSheetDialogFragment(R.layout.bottom_sheet_choose_year) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/filter/FilterBottomSheet.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/filter/FilterBottomSheet.kt index d6fe7f5d..2e6f4f29 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/filter/FilterBottomSheet.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/filter/FilterBottomSheet.kt @@ -30,8 +30,10 @@ import com.google.android.material.radiobutton.MaterialRadioButton import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.BottomSheetFilterBinding import com.mezhendosina.sgo.app.uiEntities.FilterUiEntity +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class FilterBottomSheet( private val header: String, private val content: List, diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/filter/GradesFilterViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/filter/GradesFilterViewModel.kt index ac8faa1e..05348ca7 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/filter/GradesFilterViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/filter/GradesFilterViewModel.kt @@ -28,18 +28,22 @@ import com.mezhendosina.sgo.app.utils.LoadStatus import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.editPreference -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.SettingsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class GradesFilterViewModel( - private val settingsRepository: SettingsRepository = NetSchoolSingleton.settingsRepository +@HiltViewModel +class GradesFilterViewModel +@Inject constructor( + private val settingsRepository: SettingsRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _gradesSortType: MutableLiveData = MutableLiveData() @@ -58,9 +62,9 @@ class GradesFilterViewModel( val isLoading = _isLoading.toLiveData() - fun setGradeSort(context: Context, sortBy: Int) { + fun setGradeSort(sortBy: Int) { viewModelScope.launch { - SettingsDataStore.SORT_GRADES_BY.editPreference(context, sortBy) + settingsDataStore.setValue(SettingsDataStore.SORT_GRADES_BY, sortBy) _gradesSortType.value = sortBy Singleton.updateGradeState.value = LoadStatus.UPDATE } @@ -103,22 +107,21 @@ class GradesFilterViewModel( } } - fun getGradeSort(context: Context) { + fun getGradeSort(@ApplicationContext context: Context) { viewModelScope.launch { _gradesSortType.value = - SettingsDataStore.SORT_GRADES_BY.getValue(context, GradeSortType.BY_LESSON_NAME) - .first()!! + settingsDataStore.getValue(SettingsDataStore.SORT_GRADES_BY) + .first() ?: GradeSortType.BY_LESSON_NAME } } - fun changeTrimId(context: Context, id: Int) { + fun changeTrimId(id: Int) { CoroutineScope(Dispatchers.IO).launch { - SettingsDataStore.TRIM_ID.editPreference(context, id) + settingsDataStore.setValue(SettingsDataStore.TRIM_ID, id) val checkItem = Singleton.gradesTerms.value?.checkItem(id) withContext(Dispatchers.Main) { Singleton.gradesTerms.value = checkItem Singleton.updateGradeState.value = LoadStatus.UPDATE - } } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/CalculateGradeAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/CalculateGradeAdapter.kt index db295cca..397cbd56 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/CalculateGradeAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/CalculateGradeAdapter.kt @@ -24,6 +24,8 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.ItemChangeCalculatedGradeBinding +import dagger.Module +import javax.inject.Singleton interface ChangeGradeClickListener { @@ -34,6 +36,7 @@ interface ChangeGradeClickListener { fun manualEditGrade(grade: Int, value: Int) } +@Singleton class CalculateGradeAdapter( private val changeGradeClickListener: ChangeGradeClickListener ) : RecyclerView.Adapter(), View.OnClickListener { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/CountGradeAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/CountGradeAdapter.kt index 3da542cf..3bb3996e 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/CountGradeAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/CountGradeAdapter.kt @@ -25,7 +25,10 @@ import com.mezhendosina.sgo.app.model.grades.entities.CountGradeEntity import com.mezhendosina.sgo.app.utils.GradesType import com.mezhendosina.sgo.app.utils.setupColorWithGrade import com.mezhendosina.sgo.app.utils.setupGrade +import dagger.Module +import javax.inject.Singleton +@Singleton class CountGradeAdapter : RecyclerView.Adapter() { var countGrades = emptyList() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemFragment.kt index 8e68e638..362a65e5 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemFragment.kt @@ -35,7 +35,9 @@ import com.mezhendosina.sgo.app.utils.setupColorWithGrade import com.mezhendosina.sgo.app.utils.setupGrade import com.mezhendosina.sgo.app.utils.toGradeType import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class GradeItemFragment : Fragment(R.layout.fragment_grade_item) { var binding: FragmentGradeItemBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemViewModel.kt index b0fc1f69..d86f4a42 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/gradeItem/GradeItemViewModel.kt @@ -21,8 +21,11 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.mezhendosina.sgo.data.grades.CalculateGradeItem import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject -class GradeItemViewModel : ViewModel() { +@HiltViewModel +class GradeItemViewModel @Inject constructor() : ViewModel() { private val _calculatedGrade = MutableLiveData() val calculatedGrade: LiveData = _calculatedGrade diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradeAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradeAdapter.kt index 17878ddc..506df541 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradeAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradeAdapter.kt @@ -27,9 +27,12 @@ import com.mezhendosina.sgo.app.utils.setupAsLessonEmoji import com.mezhendosina.sgo.app.utils.setupGrade import com.mezhendosina.sgo.app.utils.toGradeType import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem +import dagger.Module +import javax.inject.Singleton typealias OnGradeClickListener = (GradesItem, View) -> Unit +@Singleton class GradeAdapter(private val onGradeClickListener: OnGradeClickListener) : RecyclerView.Adapter(), View.OnClickListener { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradesFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradesFragment.kt index e5450f18..08eb1b5b 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradesFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradesFragment.kt @@ -33,10 +33,12 @@ import com.mezhendosina.sgo.app.databinding.FragmentGradesBinding import com.mezhendosina.sgo.app.utils.LoadStatus import com.mezhendosina.sgo.app.utils.findTopNavController import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +@AndroidEntryPoint class GradesFragment : Fragment(R.layout.fragment_grades) { private var binding: FragmentGradesBinding? = null @@ -109,7 +111,7 @@ class GradesFragment : Fragment(R.layout.fragment_grades) { when (it) { LoadStatus.UPDATE -> { CoroutineScope(Dispatchers.IO).launch { - viewModel.load(requireContext()) + viewModel.load() } if (binding != null) { TransitionManager.beginDelayedTransition( diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradesViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradesViewModel.kt index f213e423..5a6ef7b8 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradesViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/gradesFlow/grades/GradesViewModel.kt @@ -31,17 +31,22 @@ import com.mezhendosina.sgo.app.uiEntities.checkItem import com.mezhendosina.sgo.app.utils.LoadStatus import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.editPreference -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem import com.mezhendosina.sgo.data.netschool.api.grades.entities.gradeOptions.GradeOptions +import dagger.hilt.InstallIn +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext +import javax.inject.Inject -class GradesViewModel( - private val gradeServices: GradesRepository = NetSchoolSingleton.gradesRepository +@HiltViewModel +class GradesViewModel +@Inject constructor( + private val gradeServices: GradesRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _grades = MutableLiveData>() @@ -61,7 +66,7 @@ class GradesViewModel( gradeServices.addListener(gradeActionListener) } - suspend fun load(context: Context) { + suspend fun load() { if (Singleton.grades.isNotEmpty() && Singleton.gradesRecyclerViewLoaded.value == false) { withContext(Dispatchers.Main) { _grades.value = Singleton.grades @@ -87,26 +92,25 @@ class GradesViewModel( } // find saved termId in response - val currentTrimId = SettingsDataStore.TRIM_ID.getValue(context, -1) + val currentTrimId = settingsDataStore.getValue(SettingsDataStore.TRIM_ID).first() ?: -1 val findId = _gradeOptions.value!!.TERMID.find { - it.value == currentTrimId.first().toString() + it.value == currentTrimId.toString() } // if termId not find save and set selected termId - if (findId == null) SettingsDataStore.TRIM_ID.editPreference( - context, + if (findId == null) settingsDataStore.setValue( + SettingsDataStore.TRIM_ID, _gradeOptions.value!!.TERMID.first { it.is_selected }.value.toInt() ) val sortedGradesBy = - SettingsDataStore.SORT_GRADES_BY.getValue(context, GradeSortType.BY_LESSON_NAME) + settingsDataStore.getValue(SettingsDataStore.SORT_GRADES_BY).first() + ?: GradeSortType.BY_LESSON_NAME loadGrades( - _gradeOptions.value!!, - currentTrimId.first().toString(), - sortedGradesBy.first() + _gradeOptions.value!!, currentTrimId.toString(), sortedGradesBy ) // Save terms into Singleton val trims = _gradeOptions.value!!.getTerms() - val checkSelectedTrim = trims.checkItem(currentTrimId.first()) + val checkSelectedTrim = trims.checkItem(currentTrimId) withContext(Dispatchers.Main) { Singleton.gradesTerms.value = checkSelectedTrim Singleton.updateGradeState.value = LoadStatus.FINISHED diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFileAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFileAdapter.kt index c36dc3ff..d7b9b083 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFileAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFileAdapter.kt @@ -23,6 +23,8 @@ import androidx.recyclerview.widget.RecyclerView import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.ItemAttachmentBinding import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import dagger.Module +import javax.inject.Singleton interface FileActionListener { fun onClick(file: FileUiEntity) @@ -33,6 +35,7 @@ interface FileActionListener { } +@Singleton class AnswerFileAdapter( private val viewModel: AnswerViewModel, private val fileActionListener: FileActionListener diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt index 52c835fa..8b5b8d0d 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt @@ -29,10 +29,12 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentAnswerBinding import com.mezhendosina.sgo.app.model.answer.FileUiEntity import com.mezhendosina.sgo.app.utils.getFileNameFromUri +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +@AndroidEntryPoint class AnswerFragment : Fragment(R.layout.fragment_answer) { private var binding: FragmentAnswerBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt index 95f789dc..40a73b90 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt @@ -21,14 +21,18 @@ import androidx.lifecycle.ViewModel import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.model.answer.FileUiEntity import com.mezhendosina.sgo.app.model.attachments.AttachmentsRepository +import com.mezhendosina.sgo.app.model.attachments.AttachmentsUtils import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.LessonRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject -class AnswerViewModel( - private val lessonRepository: LessonRepository = NetSchoolSingleton.lessonRepository, - private val attachmentsRepository: AttachmentsRepository = NetSchoolSingleton.attachmentsRepository +@HiltViewModel +class AnswerViewModel @Inject constructor( + private val lessonRepository: LessonRepository, + private val attachmentsRepository: AttachmentsRepository ) : ViewModel() { @@ -46,16 +50,7 @@ class AnswerViewModel( else null suspend fun openFile(context: Context, fileUiEntity: FileUiEntity) { - if (fileUiEntity.file != null) attachmentsRepository.openFile(context, fileUiEntity.file) - else { - withContext(Dispatchers.IO) { - attachmentsRepository.downloadAttachment( - context, - fileUiEntity.id!!, - fileUiEntity.fileName - ) - } - } + TODO() } fun addFile(fileUiEntity: FileUiEntity) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt index 7cc567fb..6e5a4a02 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt @@ -32,10 +32,12 @@ import com.mezhendosina.sgo.app.ui.journalFlow.answer.AnswerFragment.Companion.E import com.mezhendosina.sgo.app.utils.addOnToolbarCollapseListener import com.mezhendosina.sgo.app.utils.getEmojiLesson import com.mezhendosina.sgo.app.utils.setLessonEmoji +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +@AndroidEntryPoint class LessonContainer : Fragment(R.layout.container_lesson) { private var binding: ContainerLessonBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainerViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainerViewModel.kt index cd6c272f..445c9e89 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainerViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainerViewModel.kt @@ -22,18 +22,21 @@ import androidx.lifecycle.ViewModel import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.model.answer.AnswerRepository -import com.mezhendosina.sgo.app.netschool.base.toDescription +import com.mezhendosina.sgo.data.netschool.base.toDescription import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.LessonRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext +import javax.inject.Inject -class LessonContainerViewModel( - private val answerRepository: AnswerRepository = NetSchoolSingleton.answerRepository, - private val lessonRepository: LessonRepository = NetSchoolSingleton.lessonRepository +@HiltViewModel +class LessonContainerViewModel @Inject constructor( + private val answerRepository: AnswerRepository, + private val lessonRepository: LessonRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { val lesson = if (Singleton.lesson != null) { @@ -45,7 +48,8 @@ class LessonContainerViewModel( suspend fun sendAnswers(context: Context) { try { - val currentUserId = SettingsDataStore.CURRENT_USER_ID.getValue(context, -1).first() + val currentUserId = + settingsDataStore.getValue(SettingsDataStore.CURRENT_USER_ID).first() ?: -1 val assignId = lesson?.homework?.id ?: -1 answerRepository.sendTextAnswer( assignId, diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journal/JournalFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journal/JournalFragment.kt index b74046c3..e863de17 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journal/JournalFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journal/JournalFragment.kt @@ -27,8 +27,10 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentJournalBinding import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.currentWeekStart -import com.mezhendosina.sgo.data.getValue +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject +@AndroidEntryPoint class JournalFragment : Fragment(R.layout.fragment_journal) { private var binding: FragmentJournalBinding? = null @@ -37,6 +39,8 @@ class JournalFragment : Fragment(R.layout.fragment_journal) { private var tabLayoutMediator: TabLayoutMediator? = null + @Inject lateinit var settingsDataStore: SettingsDataStore + private val onPageChangeCallback = object : OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) @@ -60,7 +64,7 @@ class JournalFragment : Fragment(R.layout.fragment_journal) { } private fun observeUserId() { - SettingsDataStore.CURRENT_USER_ID.getValue(requireContext(), -1).asLiveData() + settingsDataStore.getValue(SettingsDataStore.CURRENT_USER_ID).asLiveData() .observe(viewLifecycleOwner) { if (binding != null) binding!!.journalPager.invalidate() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journal/JournalPagerAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journal/JournalPagerAdapter.kt index dbe66f15..599d8e5b 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journal/JournalPagerAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journal/JournalPagerAdapter.kt @@ -24,7 +24,10 @@ import androidx.recyclerview.widget.DiffUtil import androidx.viewpager2.adapter.FragmentStateAdapter import com.mezhendosina.sgo.app.ui.journalFlow.journalItem.JournalItemFragment import com.mezhendosina.sgo.data.WeekStartEndEntity +import dagger.Module +import javax.inject.Singleton +@Singleton class JournalPagerAdapter( val fragment: FragmentManager, val lifecycle: Lifecycle diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/JournalItemFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/JournalItemFragment.kt index 457eccbd..c0070589 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/JournalItemFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/JournalItemFragment.kt @@ -33,10 +33,12 @@ import com.mezhendosina.sgo.app.ui.journalFlow.journalItem.adapters.DiaryAdapter import com.mezhendosina.sgo.app.ui.journalFlow.journalItem.adapters.OnHomeworkClickListener import com.mezhendosina.sgo.app.ui.journalFlow.journalItem.adapters.PastMandatoryAdapter import com.mezhendosina.sgo.app.utils.findTopNavController +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +@AndroidEntryPoint class JournalItemFragment : Fragment(R.layout.fragment_item_journal) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/JournalItemViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/JournalItemViewModel.kt index e6901436..183b4f7c 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/JournalItemViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/JournalItemViewModel.kt @@ -26,14 +26,17 @@ import com.mezhendosina.sgo.app.model.journal.entities.DiaryUiEntity import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.WeekStartEndEntity -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext +import javax.inject.Inject -class JournalItemViewModel( - private val journalRepository: JournalRepository = NetSchoolSingleton.journalRepository +@HiltViewModel +class JournalItemViewModel @Inject constructor( + private val journalRepository: JournalRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _week = MutableLiveData() @@ -62,9 +65,10 @@ class JournalItemViewModel( val a = if (Singleton.currentDiaryUiEntity.value?.weekStart == weekStart) { Singleton.currentDiaryUiEntity.value!! } else { - val currentUserId = SettingsDataStore.CURRENT_USER_ID.getValue(context, -1) + val currentUserId = + settingsDataStore.getValue(SettingsDataStore.CURRENT_USER_ID).first() ?: -1 val getWeek = journalRepository.getWeek( - currentUserId.first(), + currentUserId, WeekStartEndEntity(weekStart!!, weekEnd!!), NetSchoolSingleton.journalYearId.value ?: 0 ) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/DiaryAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/DiaryAdapter.kt index a4c1442d..013cefaf 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/DiaryAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/DiaryAdapter.kt @@ -23,7 +23,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.mezhendosina.sgo.app.databinding.ItemDiaryBinding import com.mezhendosina.sgo.app.model.journal.entities.WeekDayUiEntity +import dagger.Module +import javax.inject.Singleton +@Singleton class DiaryAdapter( private val onHomeworkClickListener: OnHomeworkClickListener ) : RecyclerView.Adapter() { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/HomeworkAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/HomeworkAdapter.kt index 8cc41a68..69d63008 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/HomeworkAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/HomeworkAdapter.kt @@ -27,9 +27,12 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.ItemHomeworkBinding import com.mezhendosina.sgo.app.model.journal.entities.LessonUiEntity import com.mezhendosina.sgo.app.utils.setupAsLessonEmoji +import dagger.Module +import javax.inject.Singleton typealias OnHomeworkClickListener = (LessonUiEntity, View) -> Unit +@Singleton class HomeworkAdapter( private val onHomeworkClickListener: OnHomeworkClickListener ) : RecyclerView.Adapter(), View.OnClickListener { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/HomeworkGradeAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/HomeworkGradeAdapter.kt index 0d6f6c16..8031dfac 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/HomeworkGradeAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/HomeworkGradeAdapter.kt @@ -24,7 +24,10 @@ import com.mezhendosina.sgo.app.databinding.ItemGradeValueBinding import com.mezhendosina.sgo.app.model.journal.entities.AssignmentUiEntity import com.mezhendosina.sgo.app.utils.setupGrade import com.mezhendosina.sgo.app.utils.toGradeType +import dagger.Module +import javax.inject.Singleton +@Singleton class HomeworkGradeAdapter : RecyclerView.Adapter() { var grades: List = emptyList() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/PastMandatoryAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/PastMandatoryAdapter.kt index 06d7ae53..7ae92bfe 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/PastMandatoryAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/journalItem/adapters/PastMandatoryAdapter.kt @@ -23,9 +23,12 @@ import androidx.recyclerview.widget.RecyclerView import com.mezhendosina.sgo.app.databinding.ItemPastMandatoryBinding import com.mezhendosina.sgo.data.DateManipulation import com.mezhendosina.sgo.data.netschool.api.diary.entities.PastMandatoryEntity +import dagger.Module +import javax.inject.Singleton typealias PastMandatoryClickListener = (PastMandatoryEntity) -> Unit +@Singleton class PastMandatoryAdapter( private val pastMandatoryClickListener: PastMandatoryClickListener ) : RecyclerView.Adapter(), diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt index c9d9bd32..b399f438 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt @@ -38,10 +38,12 @@ import com.mezhendosina.sgo.app.model.answer.FileUiEntity import com.mezhendosina.sgo.app.ui.journalFlow.answer.AnswerFragment import com.mezhendosina.sgo.app.utils.AttachmentAdapter import com.mezhendosina.sgo.app.utils.AttachmentClickListener +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +@AndroidEntryPoint class LessonFragment : Fragment(R.layout.fragment_item_lesson) { internal val viewModel: LessonViewModel by viewModels() @@ -57,7 +59,7 @@ class LessonFragment : Fragment(R.layout.fragment_item_lesson) { requireContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) if (permission == PackageManager.PERMISSION_GRANTED) { CoroutineScope(Dispatchers.Main).launch { - viewModel.downloadAttachment(requireContext(), attachment) + viewModel.downloadAttachment(attachment) } } else { storagePermission.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) @@ -77,13 +79,8 @@ class LessonFragment : Fragment(R.layout.fragment_item_lesson) { whyGradeAdapter = WhyGradeAdapter() attachmentAdapter = AttachmentAdapter(onAttachmentClickListener) answerFileAdapter = AttachmentAdapter(onAttachmentClickListener) - - CoroutineScope(Dispatchers.IO).launch { - viewModel.init(requireContext()) - } } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentItemLessonBinding.bind(view) @@ -213,7 +210,7 @@ class LessonFragment : Fragment(R.layout.fragment_item_lesson) { Singleton.answerUpdated.observe(viewLifecycleOwner) { if (it) { CoroutineScope(Dispatchers.IO).launch { - viewModel.init(requireContext()) + viewModel.loadLesson() } Singleton.answerUpdated.value = false } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt index 81858ebe..c691df91 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt @@ -19,24 +19,31 @@ package com.mezhendosina.sgo.app.ui.journalFlow.lessonItem import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.model.answer.FileUiEntity -import com.mezhendosina.sgo.app.netschool.base.PermissionNotGranted -import com.mezhendosina.sgo.app.netschool.base.toDescription +import com.mezhendosina.sgo.app.model.attachments.AttachmentsRepository +import com.mezhendosina.sgo.data.netschool.base.PermissionNotGranted +import com.mezhendosina.sgo.data.netschool.base.toDescription import com.mezhendosina.sgo.app.uiEntities.AboutLessonUiEntity import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.getValue -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton.attachmentsRepository import com.mezhendosina.sgo.data.netschool.repo.LessonActionListener import com.mezhendosina.sgo.data.netschool.repo.LessonRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class LessonViewModel( - private val lessonRepository: LessonRepository = NetSchoolSingleton.lessonRepository +@HiltViewModel +class LessonViewModel +@Inject constructor( + private val lessonRepository: LessonRepository, + private val attachmentsRepository: AttachmentsRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _lesson = MutableLiveData() @@ -51,16 +58,19 @@ class LessonViewModel( init { lessonRepository.addListener(lessonListener) + viewModelScope.launch { + loadLesson() + } } - suspend fun init(context: Context) { + suspend fun loadLesson() { try { withContext(Dispatchers.Main) { _error.value = "" } lessonRepository.getAboutLesson( if (Singleton.lesson != null) Singleton.lesson!! else Singleton.pastMandatoryItem!!.toLessonEntity(), - SettingsDataStore.CURRENT_USER_ID.getValue(context, -1).first() + settingsDataStore.getValue(SettingsDataStore.CURRENT_USER_ID).first() ?: -1 ) } catch (e: Exception) { withContext(Dispatchers.Main) { @@ -70,16 +80,11 @@ class LessonViewModel( } suspend fun downloadAttachment( - context: Context, attachment: FileUiEntity, ) { try { withContext(Dispatchers.IO) { - attachmentsRepository.downloadAttachment( - context, - attachment.id!!, - attachment.fileName - ) + TODO() } } catch (e: Exception) { if (e is PermissionNotGranted) { @@ -94,92 +99,4 @@ class LessonViewModel( super.onCleared() lessonRepository.removeListener(lessonListener) } -} - - -//class LessonViewModel( -// private val attachmentsRepository: AttachmentsRepository = NetSchoolSingleton.attachmentsRepository, -// val answerRepository: AnswerRepository = NetSchoolSingleton.answerRepository -//) : ViewModel() { -// -// private val _lesson = MutableLiveData() -// val lesson: LiveData = _lesson -// -// private val _types = MutableLiveData>() -// val types: LiveData> = _types -// -// private val _errorMessage = MutableLiveData() -// val errorMessage: LiveData = _errorMessage -// -// private val _snackBar = MutableLiveData(false) -// val snackbar: LiveData = _snackBar -// -// -// suspend fun init(context: Context, fullUpdate: Boolean = false) { -// if (fullUpdate) { -// if (Singleton.lesson != null) { -// loadHomework(context) -// loadGrades() -// } else if (Singleton.pastMandatoryItem != null) { -// loadHomework(context, Singleton.pastMandatoryItem!!.id) -// } -// } else { -// if (Singleton.lesson != null) { -// withContext(Dispatchers.Main) { -// _lesson.value = Singleton.lesson -// } -// loadGrades() -// } else if (Singleton.pastMandatoryItem != null) { -// withContext(Dispatchers.Main) { -// _lesson.value = Singleton.pastMandatoryItem!!.toLessonEntity() -// } -// loadHomework(context, Singleton.pastMandatoryItem!!.id) -// } -// } -// } -// -// -// fun deleteFile(context: Context, fileId: Int, onComplete: () -> Unit) { -// CoroutineScope(Dispatchers.Main).launch { -// try { -// withContext(Dispatchers.IO) { -// val assignmentId = _lesson.value?.assignments?.find { it.typeId == 3 }?.id ?: 0 -// attachmentsRepository.deleteAttachment(assignmentId, fileId) -// withContext(Dispatchers.Main) { -// onComplete.invoke() -// } -// loadHomework(context) -// } -// } catch (e: Exception) { -// _errorMessage.value = e.toDescription() -// } -// } -// } -// -// fun loadHomework(context: Context, id: Int? = null) { -// val settings = Settings(context) -// CoroutineScope(Dispatchers.IO).launch { -// try { -// val homeworkId = -// id ?: _lesson.value?.assignments?.first { it.typeId == 3 }?.id ?: -1 -// val studentId = settings.currentUserId.first() -// val response = homeworkSource.getAboutAssign( -// homeworkId, -// studentId -// ) -// answerRepository.loadAnswer(studentId, homeworkId) -// withContext(Dispatchers.Main) { -// _homework.value = response -// _attachments.value = response.attachments -// } -// } catch (e: Exception) { -// withContext(Dispatchers.Main) { -// _errorMessage.value = e.toDescription() -// } -// } -// } -// } -// -// - -//} \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/WhyGradeAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/WhyGradeAdapter.kt index f43a5884..1395b8e9 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/WhyGradeAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/WhyGradeAdapter.kt @@ -24,7 +24,10 @@ import com.mezhendosina.sgo.app.databinding.ItemWhyGradeBinding import com.mezhendosina.sgo.app.uiEntities.WhyGradeEntity import com.mezhendosina.sgo.app.utils.setupGrade import com.mezhendosina.sgo.app.utils.toGradeType +import dagger.Module +import javax.inject.Singleton +@Singleton class WhyGradeAdapter : RecyclerView.Adapter() { var grades: List = emptyList() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionAdapter.kt index f8bfe207..2df5891d 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionAdapter.kt @@ -29,9 +29,12 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.ItemRegionBinding import com.mezhendosina.sgo.app.ui.loginFlow.chooseRegion.entities.ChooseRegionUiEntityItem import com.mezhendosina.sgo.data.netschool.api.regions.Regions +import dagger.Module +import javax.inject.Singleton typealias OnRegionClickListener = (regionName: String) -> Unit +@Singleton class ChooseRegionAdapter( private val onRegionClickListener: OnRegionClickListener ) : RecyclerView.Adapter(), diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionFragment.kt index 9517c0d5..fdf8a2bb 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionFragment.kt @@ -27,7 +27,9 @@ import com.google.android.material.transition.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentChooseRegionBinding import com.mezhendosina.sgo.app.ui.loginFlow.welcome.WelcomeFragment +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class ChooseRegionFragment : Fragment(R.layout.fragment_choose_region) { private var binding: FragmentChooseRegionBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionViewModel.kt index 1efdea0a..425156ad 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseRegion/ChooseRegionViewModel.kt @@ -25,16 +25,22 @@ import com.mezhendosina.sgo.app.ui.loginFlow.chooseRegion.entities.ChooseRegionU import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.editPreference import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.RegionsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class ChooseRegionViewModel( - private val regionsRepository: RegionsRepository = NetSchoolSingleton.regionsRepository + +@HiltViewModel +class ChooseRegionViewModel +@Inject constructor( + private val regionsRepository: RegionsRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _regions = MutableLiveData() @@ -69,12 +75,11 @@ class ChooseRegionViewModel( _selectedRegion.value = _regions.value?.first { it.name == newValue }!! } - fun setRegion(context: Context, onComplete: () -> Unit) { + fun setRegion(@ApplicationContext context: Context, onComplete: () -> Unit) { val regionUrl = _selectedRegion.value!!.url CoroutineScope(Dispatchers.IO).launch { - SettingsDataStore.REGION_URL.editPreference(context, regionUrl) + settingsDataStore.setValue(SettingsDataStore.REGION_URL, regionUrl) withContext(Dispatchers.Main) { - NetSchoolSingleton.baseUrl = regionUrl onComplete.invoke() } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolAdapter.kt index 9a3b652e..79d1f091 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolAdapter.kt @@ -23,9 +23,12 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.mezhendosina.sgo.app.databinding.ItemSchoolBinding import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity +import dagger.Module +import javax.inject.Singleton typealias OnSchoolClickListener = (SchoolUiEntity) -> Unit +@Singleton class ChooseSchoolAdapter(private val onSchoolClickListener: OnSchoolClickListener) : RecyclerView.Adapter(), View.OnClickListener { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolFragment.kt index 1c4cc7f2..6b431c0d 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolFragment.kt @@ -33,11 +33,13 @@ import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity import com.mezhendosina.sgo.app.utils.DividerItemDecoration import com.mezhendosina.sgo.app.utils.hideAnimation import com.mezhendosina.sgo.app.utils.showAnimation +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch +@AndroidEntryPoint class ChooseSchoolFragment : Fragment(R.layout.fragment_choose_school) { private var binding: FragmentChooseSchoolBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolViewModel.kt index fa6f3341..c49d021e 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolViewModel.kt @@ -25,13 +25,17 @@ import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class ChooseSchoolViewModel( - private val loginRepository: LoginRepository = NetSchoolSingleton.loginRepository +@HiltViewModel +class ChooseSchoolViewModel +@Inject constructor( + private val loginRepository: LoginRepository ) : ViewModel() { private val _schools = MutableLiveData>() val schools: LiveData> = _schools diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt index a2230946..15fd10f9 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdFragment.kt @@ -27,7 +27,9 @@ import com.google.android.material.transition.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentChooseUserIdBinding import com.mezhendosina.sgo.app.ui.loginFlow.gosuslugiResult.GosuslugiResultFragment +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class ChooseUserIdFragment : Fragment(R.layout.fragment_choose_user_id) { private lateinit var binding: FragmentChooseUserIdBinding diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdViewModel.kt index 413a92c4..6cc4eaef 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/ChooseUserIdViewModel.kt @@ -28,10 +28,14 @@ import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.activities.MainActivity import com.mezhendosina.sgo.app.uiEntities.UserUIEntity import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.editPreference +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import javax.inject.Inject -class ChooseUserIdViewModel : ViewModel() { +@HiltViewModel +class ChooseUserIdViewModel @Inject constructor( + private val settingsDataStore: SettingsDataStore +) : ViewModel() { private val _usersId = MutableLiveData>() val usersId: LiveData> = _usersId @@ -44,8 +48,8 @@ class ChooseUserIdViewModel : ViewModel() { fun login(context: Context, userUIEntity: UserUIEntity) { try { viewModelScope.launch { - SettingsDataStore.CURRENT_USER_ID.editPreference(context, userUIEntity.userId!!) - SettingsDataStore.LOGGED_IN.editPreference(context, true) + settingsDataStore.setValue(SettingsDataStore.CURRENT_USER_ID, userUIEntity.userId!!) + settingsDataStore.setValue(SettingsDataStore.LOGGED_IN, true) } val intent = Intent(context, MainActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/UserIdAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/UserIdAdapter.kt index d8fadcbe..f1195e28 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/UserIdAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseUserId/UserIdAdapter.kt @@ -22,7 +22,10 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.mezhendosina.sgo.app.databinding.ItemUserIdBinding import com.mezhendosina.sgo.app.uiEntities.UserUIEntity +import dagger.Module +import javax.inject.Singleton +@Singleton class UserIdAdapter( private val onUserClickListener: (UserUIEntity) -> Unit ) : RecyclerView.Adapter(), View.OnClickListener { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiFragment.kt index 1377b2da..2dd7e422 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiFragment.kt @@ -25,15 +25,22 @@ import androidx.navigation.fragment.findNavController import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentGosuslugiBinding import com.mezhendosina.sgo.app.ui.loginFlow.gosuslugiResult.GosuslugiResultFragment +import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch - +import javax.inject.Inject +@AndroidEntryPoint class GosuslugiFragment : Fragment(R.layout.fragment_gosuslugi) { private val viewModel by viewModels() + @Inject + lateinit var settingsDataStore: SettingsDataStore + private val webView = GosuslugiWebView { loginState -> CoroutineScope(Dispatchers.IO).launch { viewModel.login( @@ -63,7 +70,13 @@ class GosuslugiFragment : Fragment(R.layout.fragment_gosuslugi) { binding = FragmentGosuslugiBinding.bind(view) with(binding!!.root) { - loadUrl("${NetSchoolSingleton.baseUrl}/webapi/sso/esia/crosslogin?esia_permissions=1&esia_role=1") + CoroutineScope(Dispatchers.Main).launch { + loadUrl( + "${ + settingsDataStore.getValue(SettingsDataStore.REGION_URL).first() + }/webapi/sso/esia/crosslogin?esia_permissions=1&esia_role=1" + ) + } settings.apply { loadsImagesAutomatically = true javaScriptEnabled = true diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiViewModel.kt index 5f3c96cf..b0b34d63 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiViewModel.kt @@ -21,11 +21,16 @@ import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.login.entities.accountInfo.toUiEntity import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject -class GosuslugiViewModel( - private val loginRepository: LoginRepository = NetSchoolSingleton.loginRepository + +@HiltViewModel +class GosuslugiViewModel + @Inject constructor( + private val loginRepository: LoginRepository ) : ViewModel() { suspend fun login( loginState: String, diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultFragment.kt index afbe0b99..39259db5 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultFragment.kt @@ -25,12 +25,14 @@ import androidx.fragment.app.viewModels import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.activities.MainActivity import com.mezhendosina.sgo.app.databinding.FragmentGosuslugiResultBinding +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +@AndroidEntryPoint class GosuslugiResultFragment : Fragment(R.layout.fragment_gosuslugi_result) { @@ -41,11 +43,10 @@ class GosuslugiResultFragment : Fragment(R.layout.fragment_gosuslugi_result) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) CoroutineScope(Dispatchers.IO).launch { - viewModel.auth( - requireContext(), - arguments?.getString(LOGIN_STATE)!!, - arguments?.getString(USER_ID)!! - ) +// viewModel.auth( +// arguments?.getString(LOGIN_STATE)!!, +// arguments?.getString(USER_ID)!! +// ) } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultViewModel.kt index 30b6e602..eec0f41a 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultViewModel.kt @@ -23,11 +23,17 @@ import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject -class GosuslugiResultViewModel( - private val loginRepository: LoginRepository = NetSchoolSingleton.loginRepository + +@HiltViewModel +class GosuslugiResultViewModel +@Inject constructor( + private val loginRepository: LoginRepository ) : ViewModel() { private val _loggedIn = MutableLiveData(null) @@ -36,9 +42,9 @@ class GosuslugiResultViewModel( private val _error = MutableLiveData() val error = _error.toLiveData() - suspend fun auth(context: Context, loginState: String, id: String) { + suspend fun auth() { try { - loginRepository.gosuslugiLogin(context, loginState, id, true) + loginRepository.gosuslugiLogin(true) withContext(Dispatchers.Main) { _loggedIn.value = true } } catch (e: Exception) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginFragment.kt index 141a9a49..84754fb0 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginFragment.kt @@ -30,7 +30,12 @@ import com.mezhendosina.sgo.app.databinding.FragmentLoginBinding import com.mezhendosina.sgo.app.utils.findTopNavController import com.mezhendosina.sgo.app.utils.hideAnimation import com.mezhendosina.sgo.app.utils.showAnimation +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +@AndroidEntryPoint class LoginFragment : Fragment(R.layout.fragment_login) { private lateinit var binding: FragmentLoginBinding @@ -55,7 +60,9 @@ class LoginFragment : Fragment(R.layout.fragment_login) { binding = FragmentLoginBinding.bind(view) schoolId = requireArguments().getInt(ARG_SCHOOL_ID) - binding.selectedSchool.text = viewModel.findSchool(schoolId!!).name + CoroutineScope(Dispatchers.Main).launch { + viewModel.findSchool(schoolId ?: -1) + } binding.selectedSchoolCard.setOnClickListener { findNavController().popBackStack() } @@ -64,6 +71,14 @@ class LoginFragment : Fragment(R.layout.fragment_login) { observeErrors() observeLoading() + observeFoundSchool() + } + + private fun observeFoundSchool() { + viewModel.foundSchool.observe(viewLifecycleOwner) { + if (it != null) + binding.selectedSchool.text = it.name + } } private fun observeErrors() { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginViewModel.kt index f6df5a21..c66a418d 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginViewModel.kt @@ -22,23 +22,29 @@ import androidx.core.content.ContextCompat.startActivity import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import androidx.navigation.NavController import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.activities.MainActivity import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity import com.mezhendosina.sgo.app.utils.toDescription -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.login.entities.toUiEntity import com.mezhendosina.sgo.data.netschool.base.toMD5 import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class LoginViewModel( - private val loginRepository: LoginRepository = NetSchoolSingleton.loginRepository + +@HiltViewModel +class LoginViewModel +@Inject constructor( + private val loginRepository: LoginRepository ) : ViewModel() { private val _isLoading = MutableLiveData(false) @@ -47,6 +53,8 @@ class LoginViewModel( private val _errorMessage = MutableLiveData() val errorMessage: LiveData = _errorMessage + private val _foundSchool = MutableLiveData() + val foundSchool: LiveData = _foundSchool fun login( context: Context, @@ -61,10 +69,9 @@ class LoginViewModel( val passwordHash = password.toMD5() val school = findSchool(schoolId) loginRepository.login( - context, login, passwordHash, - school.id, + _foundSchool.value?.id, onOneUser = { val intent = Intent(context, MainActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) @@ -89,8 +96,8 @@ class LoginViewModel( } - fun findSchool(schoolId: Int): SchoolUiEntity { - return NetSchoolSingleton.schools.first { it.id == schoolId } + suspend fun findSchool(schoolId: Int) { + _foundSchool.value = loginRepository.schools.first().first { it.id == schoolId } } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/requestRegion/RequestRegionFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/requestRegion/RequestRegionFragment.kt index eee46807..02ca1c12 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/requestRegion/RequestRegionFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/requestRegion/RequestRegionFragment.kt @@ -21,7 +21,9 @@ import android.view.View import androidx.fragment.app.Fragment import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentRequestRegionBinding +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class RequestRegionFragment : Fragment(R.layout.fragment_request_region) { private lateinit var binding: FragmentRequestRegionBinding diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/requestRegion/RequestRegionViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/requestRegion/RequestRegionViewModel.kt index 6bf55284..d6c41a32 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/requestRegion/RequestRegionViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/requestRegion/RequestRegionViewModel.kt @@ -21,19 +21,22 @@ import androidx.lifecycle.viewModelScope import com.google.gson.Gson import com.mezhendosina.sgo.data.netschool.base.BaseRetrofitSource import com.mezhendosina.sgo.data.netschool.base.RetrofitConfig +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.http.POST +import javax.inject.Inject interface RegionRequest { @POST("/requestRegion") suspend fun requestRegion() } -class RequestRegionViewModel : ViewModel() { +@HiltViewModel +class RequestRegionViewModel @Inject constructor() : ViewModel() { private val loginInterceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/welcome/WelcomeFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/welcome/WelcomeFragment.kt index 7bff65db..e9719e9e 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/welcome/WelcomeFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/welcome/WelcomeFragment.kt @@ -27,11 +27,13 @@ import com.google.android.material.transition.MaterialSharedAxis import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentWelcomeBinding +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch +@AndroidEntryPoint class WelcomeFragment : Fragment(R.layout.fragment_welcome) { private var binding: FragmentWelcomeBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt index e891b676..abfafed8 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt @@ -59,18 +59,21 @@ import com.mezhendosina.sgo.app.utils.slideDownAnimation import com.mezhendosina.sgo.app.utils.slideUpAnimation import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.currentWeekStart -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import java.io.File +import javax.inject.Inject class ContainerFragment : Fragment(R.layout.container_main), GradesFilterInterface, GradesActionsInterface, ContainerNavigationInterface { + @Inject + lateinit var settingsDataStore: SettingsDataStore + private lateinit var binding: ContainerMainBinding private val file: File = File.createTempFile("app", "apk") @@ -115,7 +118,7 @@ class ContainerFragment CoroutineScope(Dispatchers.IO).launch { containerViewModel.checkUpdates() containerViewModel.loadWeeks() - containerViewModel.showUpdateDialog(requireContext()) + containerViewModel.showUpdateDialog() } } @@ -203,9 +206,11 @@ class ContainerFragment } override fun observeUserId() { - SettingsDataStore.CURRENT_USER_ID.getValue(requireContext(), -1).asLiveData() + settingsDataStore.getValue(SettingsDataStore.CURRENT_USER_ID).asLiveData() .observe(viewLifecycleOwner) { - binding.journal.invalidate() + if (it != null) { + binding.journal.invalidate() + } } } @@ -237,7 +242,7 @@ class ContainerFragment private fun observeDiaryStyle() { val firebaseAnalytics = FirebaseAnalytics.getInstance(requireContext()) CoroutineScope(Dispatchers.Main).launch { - SettingsDataStore.DIARY_STYLE.getValue(requireContext(), DiaryStyle.AS_CARD).collect { + settingsDataStore.getValue(SettingsDataStore.DIARY_STYLE).collect { firebaseAnalytics.setUserProperty("diary_style", it) } } @@ -352,7 +357,7 @@ class ContainerFragment binding.gradesTopBar.term.visibility = View.VISIBLE CoroutineScope(Dispatchers.Main).launch { val trimId = - SettingsDataStore.TRIM_ID.getValue(requireContext(), -1).first() + settingsDataStore.getValue(SettingsDataStore.TRIM_ID).first() binding.gradesTopBar.term.text = gradeOptions.firstOrNull { it.id == trimId }?.name } @@ -369,7 +374,7 @@ class ContainerFragment requireContext().getString(R.string.selected_grade_period), Singleton.gradesTerms.value!! ) { - gradesFilterViewModel.changeTrimId(requireContext(), it) + gradesFilterViewModel.changeTrimId(it) } filterBottomSheet.show( @@ -421,9 +426,8 @@ class ContainerFragment override fun onGradesSortClickListener() { binding.gradesTopBar.sortGrades.setOnClickListener { CoroutineScope(Dispatchers.Main).launch { - val selectedItem = SettingsDataStore.SORT_GRADES_BY.getValue( - requireContext(), - GradeSortType.BY_LESSON_NAME + val selectedItem = settingsDataStore.getValue( + SettingsDataStore.SORT_GRADES_BY ).first() val list = listOf( FilterUiEntity( @@ -449,7 +453,7 @@ class ContainerFragment requireContext().getString(R.string.sort_grades_by), list, ) { id -> - gradesFilterViewModel.setGradeSort(requireContext(), id) + gradesFilterViewModel.setGradeSort(id) } bottomSheet.show( requireActivity().supportFragmentManager, @@ -479,7 +483,7 @@ class ContainerFragment when (it) { LoadStatus.UPDATE -> { CoroutineScope(Dispatchers.IO).launch { - gradesViewModel.load(requireContext()) + gradesViewModel.load() } TransitionManager.beginDelayedTransition( loading.root, diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerViewModel.kt index 36522bd4..ba718f90 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerViewModel.kt @@ -28,23 +28,27 @@ import com.mezhendosina.sgo.app.model.ContainerRepository import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.WeekStartEndEntity -import com.mezhendosina.sgo.data.editPreference -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.getWeeksList -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.base.Download import com.mezhendosina.sgo.data.netschool.base.uriFromFile import com.mezhendosina.sgo.data.requests.github.checkUpdates.CheckUpdates +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File +import javax.inject.Inject -class ContainerViewModel( + +@HiltViewModel +class ContainerViewModel +@Inject constructor( // private val githubUpdateDownloader: GithubUpdateDownloader = NetSchoolSingleton.githubUpdateDownloader - private val containerRepository: ContainerRepository = NetSchoolSingleton.containerRepository + private val containerRepository: ContainerRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _errorMessage = MutableLiveData("") @@ -78,16 +82,16 @@ class ContainerViewModel( } } - fun showUpdateDialog(context: Context) { + fun showUpdateDialog() { viewModelScope.launch { val lastVersionNumber = - SettingsDataStore.LAST_VERSION_NUMBER.getValue(context, -1).first() + settingsDataStore.getValue(SettingsDataStore.LAST_VERSION_NUMBER).first() ?: -1 val showUpdateDialog = - SettingsDataStore.SHOW_UPDATE_DIALOG.getValue(context, true).first() + settingsDataStore.getValue(SettingsDataStore.SHOW_UPDATE_DIALOG).first() ?: true if (BuildConfig.VERSION_CODE > lastVersionNumber) { - SettingsDataStore.SHOW_UPDATE_DIALOG.editPreference(context, true) - SettingsDataStore.LAST_VERSION_NUMBER.editPreference( - context, + settingsDataStore.setValue(SettingsDataStore.SHOW_UPDATE_DIALOG, true) + settingsDataStore.setValue( + SettingsDataStore.LAST_VERSION_NUMBER, BuildConfig.VERSION_CODE ) if (BuildConfig.VERSION_CODE == 35) { @@ -101,13 +105,13 @@ class ContainerViewModel( } } - fun changeUpdateDialogState(context: Context, b: Boolean) { + fun changeUpdateDialogState(b: Boolean) { viewModelScope.launch { - SettingsDataStore.SHOW_UPDATE_DIALOG.editPreference(context, b) + settingsDataStore.setValue(SettingsDataStore.SHOW_UPDATE_DIALOG, b) } } - fun downloadUpdate(context: Context, file: File, url: String) { + fun downloadUpdate(@ApplicationContext context: Context, file: File, url: String) { // //TODO exception handling // githubUpdateDownloader.downloadUpdate(context, url) { progress, uri -> // when (progress) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/updateBottomSheet/UpdateBottomSheetFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/updateBottomSheet/UpdateBottomSheetFragment.kt index 98e67b8b..1dbc9df4 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/updateBottomSheet/UpdateBottomSheetFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/updateBottomSheet/UpdateBottomSheetFragment.kt @@ -88,7 +88,7 @@ class UpdateBottomSheetFragment( } }, onCancel = { - viewModel.changeUpdateDialogState(context, false) + viewModel.changeUpdateDialogState(false) } ) return modalSheet diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsFragment.kt index 834c01c3..80acb465 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsFragment.kt @@ -16,8 +16,10 @@ package com.mezhendosina.sgo.app.ui.settingsFlow +import android.content.Intent import android.os.Bundle import android.view.View +import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -25,6 +27,7 @@ import androidx.navigation.fragment.findNavController import com.google.android.material.snackbar.Snackbar import com.google.android.material.transition.platform.MaterialSharedAxis import com.mezhendosina.sgo.app.R +import com.mezhendosina.sgo.app.activities.LoginActivity import com.mezhendosina.sgo.app.databinding.FragmentSettingsBinding import com.mezhendosina.sgo.app.ui.settingsFlow.changeControlQuestion.ChangeControlQuestionFragment import com.mezhendosina.sgo.app.ui.settingsFlow.changeEmail.ChangeEmailFragment @@ -32,6 +35,7 @@ import com.mezhendosina.sgo.app.ui.settingsFlow.changePhone.ChangePhoneFragment import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File class SettingsFragment : Fragment(R.layout.fragment_settings) { @@ -125,7 +129,16 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) { findNavController().navigate(R.id.action_settingsFragment_to_aboutAppFragment) } - binding.logoutButton.setOnClickListener { viewModel.logout(requireContext()) } + binding.logoutButton.setOnClickListener { + CoroutineScope(Dispatchers.IO).launch { + viewModel.logout() + withContext(Dispatchers.Main) { + val intent = Intent(requireContext(), LoginActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + ContextCompat.startActivity(requireContext(), intent, null) + } + } + } observeMySettings() // observeGradesNotifications() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsViewModel.kt index 453dabc0..3bdc3abd 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsViewModel.kt @@ -29,19 +29,24 @@ import com.mezhendosina.sgo.app.activities.LoginActivity import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.settings.entities.MySettingsResponseEntity import com.mezhendosina.sgo.data.netschool.repo.SettingsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File +import javax.inject.Inject -class SettingsViewModel( - private val settingsRepository: SettingsRepository = NetSchoolSingleton.settingsRepository +@HiltViewModel +class SettingsViewModel +@Inject constructor( + private val settingsRepository: SettingsRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _mySettingsResponseEntity = MutableLiveData() @@ -54,12 +59,6 @@ class SettingsViewModel( private val controlQuestion = MutableLiveData() private val controlAnswer = MutableLiveData() - private val _enableGradeNotifications = MutableLiveData() - val enableGradeNotifications: LiveData = _enableGradeNotifications - - private val _gradesNotificationsLoading = MutableLiveData(false) - val gradesNotificationsLoading = _gradesNotificationsLoading.toLiveData() - private val _errorMessage = MutableLiveData() val errorMessage: LiveData = _errorMessage @@ -74,8 +73,6 @@ class SettingsViewModel( _loading.value = true } val settingsResponse = settingsRepository.getMySettings() -// val settings = Settings(context) -// isGradesNotifySignIn(settings) withContext(Dispatchers.Main) { _mySettingsResponseEntity.value = settingsResponse phoneNumber.value = settingsResponse.mobilePhone ?: "" @@ -106,9 +103,8 @@ class SettingsViewModel( try { settingsRepository.loadProfilePhoto( - SettingsDataStore.CURRENT_USER_ID.getValue(context, -1).first(), - photoFile, - isExist + settingsDataStore.getValue(SettingsDataStore.CURRENT_USER_ID).first() ?: -1, + photoFile ) withContext(Dispatchers.Main) { @@ -121,105 +117,10 @@ class SettingsViewModel( } } } - -// suspend fun changeProfilePhoto(context: Context, photo: Uri?) { -// try { -// val settings = Settings(context) -// settingsRepository.changeProfilePhoto(context, photo, settings.currentUserId.first()) -// } catch (e: Exception) { -// withContext(Dispatchers.Main) { -// _errorMessage.value = e.toDescription() -// } -// } -// } - -// suspend fun changeGradeNotifications(context: Context) { -// withContext(Dispatchers.Main) { -// _gradesNotificationsLoading.value = true -// } -// try { -// val token = -// if (firebaseToken != null) firebaseToken -// else { -//// isGradesNotifySignIn(settings) -// firebaseToken -// } -// val loginData = settings.getLoginData() -// val userId = settings.currentUserId.first() -// val user = NotificationUserEntity( -// userId, -// token ?: "", -// loginData.UN, -// loginData.PW, -// settings.regionUrl.first().dropLast(1), -// loginData.schoolId, -// true -// ) -// if (!_enableGradeNotifications.value!!) { -// settingsRepository.registerGradesNotifications(user) -// } else { -// settingsRepository.unregisterGradesNotifications(userId, token ?: "") -// } -// withContext(Dispatchers.Main) { -// _enableGradeNotifications.value = !_enableGradeNotifications.value!! -// } -// } catch (e: Exception) { -// withContext(Dispatchers.Main) { -// _errorMessage.value = e.toDescription() -// _enableGradeNotifications.value = _enableGradeNotifications.value!! -// } -// } finally { -// withContext(Dispatchers.Main) { -// _gradesNotificationsLoading.value = false -// } -// } -// } - - fun logout(context: Context) { - CoroutineScope(Dispatchers.IO).launch { - SettingsDataStore().logout(context) - withContext(Dispatchers.Main) { - val intent = Intent(context, LoginActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) - startActivity(context, intent, null) - } - } + suspend fun logout() { + settingsDataStore.logout() } -// private suspend fun isGradesNotifySignIn(context: Context) { -// if (firebaseToken == null) { -// withContext(Dispatchers.Main) { -// _gradesNotificationsLoading.value = true -// } -// FirebaseMessaging.getInstance().token.addOnSuccessListener { -// CoroutineScope(Dispatchers.IO).launch { -// val isExist = settingsRepository.isGradesNotifyUserExist( -// settings.currentUserId.first(), -// it -// ) -// withContext(Dispatchers.Main) { -// _enableGradeNotifications.value = isExist -// firebaseToken = it -// _gradesNotificationsLoading.value = false -// } -// } -// } -// } else { -// CoroutineScope(Dispatchers.IO).launch { -// withContext(Dispatchers.Main) { -// _gradesNotificationsLoading.value = true -// } -// val isExist = settingsRepository.isGradesNotifyUserExist( -// settings.currentUserId.first(), -// firebaseToken ?: "" -// ) -// withContext(Dispatchers.Main) { -// _enableGradeNotifications.value = isExist -// _gradesNotificationsLoading.value = false -// } -// } -// } -// } companion object { const val CONTROL_QUESTION = "control_question" diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeEmail/ChangeEmailViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeEmail/ChangeEmailViewModel.kt index ba0e5a24..5c6d6309 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeEmail/ChangeEmailViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeEmail/ChangeEmailViewModel.kt @@ -24,13 +24,18 @@ import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.settings.entities.MySettingsResponseEntity import com.mezhendosina.sgo.data.netschool.repo.SettingsRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class ChangeEmailViewModel( - private val settingsRepository: SettingsRepository = NetSchoolSingleton.settingsRepository + +@HiltViewModel +class ChangeEmailViewModel + @Inject constructor( + private val settingsRepository: SettingsRepository ) : ViewModel() { private val _errorDescription = MutableLiveData() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePassword/ChangePasswordFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePassword/ChangePasswordFragment.kt index 965ab7de..b860d073 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePassword/ChangePasswordFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePassword/ChangePasswordFragment.kt @@ -26,14 +26,17 @@ import com.google.android.material.transition.platform.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentChangePasswordBinding import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.base.toMD5 +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import javax.inject.Inject -class ChangePasswordFragment : Fragment(R.layout.fragment_change_password) { +@AndroidEntryPoint +class ChangePasswordFragment @Inject constructor(private val settingsDataStore: SettingsDataStore) : + Fragment(R.layout.fragment_change_password) { private lateinit var binding: FragmentChangePasswordBinding private var currentPassword = "" @@ -48,7 +51,7 @@ class ChangePasswordFragment : Fragment(R.layout.fragment_change_password) { CoroutineScope(Dispatchers.Main).launch { - currentPassword = SettingsDataStore.PASSWORD.getValue(requireContext(), "").first() + currentPassword = settingsDataStore.getValue(SettingsDataStore.PASSWORD).first() ?: "" } } @@ -117,7 +120,8 @@ class ChangePasswordFragment : Fragment(R.layout.fragment_change_password) { requireContext() ) CoroutineScope(Dispatchers.IO).launch { - currentPassword = SettingsDataStore.PASSWORD.getValue(requireContext(), "").first() + currentPassword = + settingsDataStore.getValue(SettingsDataStore.PASSWORD).first() ?: "" } findNavController().navigateUp() } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePassword/ChangePasswordViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePassword/ChangePasswordViewModel.kt index 3978fcec..d9f7f714 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePassword/ChangePasswordViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePassword/ChangePasswordViewModel.kt @@ -22,34 +22,43 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.editPreference -import com.mezhendosina.sgo.data.getValue import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.settings.entities.ChangePasswordEntity import com.mezhendosina.sgo.data.netschool.base.toMD5 import com.mezhendosina.sgo.data.netschool.repo.SettingsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import javax.inject.Inject -class ChangePasswordViewModel( - private val settingsRepository: SettingsRepository = NetSchoolSingleton.settingsRepository + +@HiltViewModel +class ChangePasswordViewModel +@Inject constructor( + private val settingsRepository: SettingsRepository, + private val settingsDataStore: SettingsDataStore ) : ViewModel() { private val _errorMessage = MutableLiveData() val errorMessage: LiveData = _errorMessage - fun changePassword(oldPassword: String, newPassword: String, context: Context) { + fun changePassword( + oldPassword: String, + newPassword: String, + @ApplicationContext context: Context + ) { CoroutineScope(Dispatchers.IO).launch { try { val password = newPassword.toMD5() settingsRepository.changePassword( - SettingsDataStore.CURRENT_USER_ID.getValue(context, -1).first(), + settingsDataStore.getValue(SettingsDataStore.CURRENT_USER_ID).first() ?: -1, ChangePasswordEntity(oldPassword, password) ) - SettingsDataStore.PASSWORD.editPreference(context, password) + settingsDataStore.setValue(SettingsDataStore.PASSWORD, password) } catch (e: Exception) { withContext(Dispatchers.Main) { _errorMessage.value = e.toDescription() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePhone/ChangePhoneViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePhone/ChangePhoneViewModel.kt index 5448d08c..df8399f7 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePhone/ChangePhoneViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePhone/ChangePhoneViewModel.kt @@ -24,11 +24,15 @@ import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.settings.entities.MySettingsResponseEntity import com.mezhendosina.sgo.data.netschool.repo.SettingsRepository +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject -class ChangePhoneViewModel( - private val settingsRepository: SettingsRepository = NetSchoolSingleton.settingsRepository +@HiltViewModel +class ChangePhoneViewModel + @Inject constructor( + private val settingsRepository: SettingsRepository ) : ViewModel() { private val _errorDescription = MutableLiveData() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeFragment.kt index 3a820ad2..7adcaa5b 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeFragment.kt @@ -50,7 +50,7 @@ class ChangeThemeFragment : Fragment(R.layout.fragment_settings_theme) { binding!!.changeThemeRadioGroup.setOnCheckedChangeListener { _, checkedId -> - viewModel.changeTheme(checkedId, requireContext()) + viewModel.changeTheme(checkedId) } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeViewModel.kt index 3ca6dcb3..430f7ba0 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeViewModel.kt @@ -21,14 +21,18 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.lifecycle.ViewModel import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.editPreference +import dagger.hilt.android.AndroidEntryPoint +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import javax.inject.Inject -class ChangeThemeViewModel : ViewModel() { +@HiltViewModel +class ChangeThemeViewModel @Inject constructor(private val settingsDataStore: SettingsDataStore) : + ViewModel() { - fun changeTheme(selectedThemeId: Int, context: Context) { + fun changeTheme(selectedThemeId: Int) { val themeId = when (selectedThemeId) { R.id.light_theme -> AppCompatDelegate.MODE_NIGHT_NO R.id.dark_theme -> AppCompatDelegate.MODE_NIGHT_YES @@ -36,7 +40,7 @@ class ChangeThemeViewModel : ViewModel() { } CoroutineScope(Dispatchers.IO).launch { - SettingsDataStore.THEME.editPreference(context, themeId) + settingsDataStore.setValue(SettingsDataStore.THEME, themeId) } AppCompatDelegate.setDefaultNightMode(themeId) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/AppSettings.kt b/app/src/main/java/com/mezhendosina/sgo/data/AppSettings.kt new file mode 100644 index 00000000..dc385f39 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/data/AppSettings.kt @@ -0,0 +1,22 @@ +package com.mezhendosina.sgo.data + +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import com.mezhendosina.sgo.data.netschool.api.login.LoginEntity +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +interface AppSettings { + + + fun getValue(value: Preferences.Key): Flow + + suspend fun setValue(value: Preferences.Key, key: T) + + + suspend fun saveLogin(loginData: LoginEntity, loggedIn: Boolean = true) + + suspend fun saveEsiaLogin(loginState: String, userId: String) + + suspend fun logout() +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt b/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt index b186e9a6..fbbab320 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt @@ -9,21 +9,21 @@ import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import com.mezhendosina.sgo.data.netschool.api.login.LoginEntity +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.qualifiers.ActivityContext +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import javax.inject.Inject val Context.dataStore: DataStore by preferencesDataStore(name = "settings") -fun Preferences.Key.getValue(context: Context, defaultValue: T): Flow = - context.dataStore.data.map { it[this] ?: defaultValue } - -suspend fun Preferences.Key.editPreference(context: Context, value: T) = - context.dataStore.edit { - it[this] = value - } - -class SettingsDataStore { +class SettingsDataStore @Inject constructor(@ApplicationContext private val context: Context) : + AppSettings { companion object { val REGION_URL = stringPreferencesKey("region") @@ -51,7 +51,17 @@ class SettingsDataStore { val SKIP_SUNDAY = booleanPreferencesKey("skip_sunday") } - suspend fun saveLogin(context: Context, loginData: LoginEntity, loggedIn: Boolean = true) { + override fun getValue(value: Preferences.Key): Flow { + return context.dataStore.data.map { it[value] } + } + + override suspend fun setValue(value: Preferences.Key, key: T) { + context.dataStore.edit { + it[value] = key + } + } + + override suspend fun saveLogin(loginData: LoginEntity, loggedIn: Boolean) { context.dataStore.edit { prefs -> prefs[LOGGED_IN] = loggedIn prefs[LOGIN] = loginData.login @@ -60,7 +70,7 @@ class SettingsDataStore { } } - suspend fun saveEsiaLogin(context: Context, loginState: String, userId: String) { + override suspend fun saveEsiaLogin(loginState: String, userId: String) { context.dataStore.edit { prefs -> prefs[LOGIN] = "" prefs[PASSWORD] = "" @@ -70,7 +80,7 @@ class SettingsDataStore { } } - suspend fun logout(context: Context) { + override suspend fun logout() { context.dataStore.edit { prefs -> prefs[LOGGED_IN] = false prefs[LOGIN] = "" diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/NetSchoolSingleton.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/NetSchoolSingleton.kt index 72e0537a..4e0a5b33 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/NetSchoolSingleton.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/NetSchoolSingleton.kt @@ -32,95 +32,15 @@ import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource import com.mezhendosina.sgo.data.netschool.api.homework.HomeworkSource import com.mezhendosina.sgo.data.netschool.api.login.LoginSource import com.mezhendosina.sgo.data.netschool.api.settings.SettingsSource -import com.mezhendosina.sgo.data.netschool.base.SourceProviderHolder -import com.mezhendosina.sgo.data.netschool.base.SourcesProvider import com.mezhendosina.sgo.data.netschool.repo.LessonRepository import com.mezhendosina.sgo.data.netschool.repo.LoginRepository import com.mezhendosina.sgo.data.netschool.repo.RegionsRepository import com.mezhendosina.sgo.data.netschool.repo.SettingsRepository +import kotlinx.coroutines.flow.MutableStateFlow object NetSchoolSingleton { - var baseUrl = "" - var at = "" val journalYearId = MutableLiveData() - var assignTypes: List? = null - var schools = emptyList() - private val sourcesProvider: SourcesProvider by lazy { - SourceProviderHolder.sourcesProvider - } - - var loggedIn = false - // --- sources - - private val loginSource: LoginSource by lazy { - sourcesProvider.getLoginSource() - } - - private val settingsSource: SettingsSource by lazy { - sourcesProvider.getSettingsSource() - } - - private val attachmentsSource: AttachmentsSource by lazy { - sourcesProvider.getAttachmentsSource() - } - - private val diarySource: DiarySource by lazy { - sourcesProvider.getDiarySource() - } - private val homeworkSource: HomeworkSource by lazy { - sourcesProvider.getHomeworkSource() - } - private val announcementsSource: AnnouncementsSource by lazy { - sourcesProvider.getAnnouncementsSource() - } - private val gradesSource: GradesSource by lazy { - sourcesProvider.getGradesSource() - } - // --- repositories - - val regionsRepository: RegionsRepository by lazy { - RegionsRepository() - } - - val loginRepository: LoginRepository by lazy { - LoginRepository(loginSource, settingsSource) - } - - val announcementsRepository by lazy { - AnnouncementsRepository(announcementsSource) - } - - val gradesRepository by lazy { - GradesRepository(gradesSource, diarySource) - } - - val settingsRepository by lazy { - SettingsRepository(settingsSource) - } - - val githubUpdateDownloader by lazy { - sourcesProvider.getGithubUpdateDownloader() - } - - val containerRepository by lazy { - ContainerRepository() - } - - val attachmentsRepository by lazy { - AttachmentsRepository(attachmentsSource) - } - - val journalRepository by lazy { - JournalRepository(attachmentsSource, diarySource) - } - - val answerRepository by lazy { - AnswerRepository(attachmentsSource, lessonRepository) - } - - val lessonRepository by lazy { - LessonRepository(homeworkSource, attachmentsSource) - } + val loggedIn = MutableStateFlow(false) } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/announcements/RetrofitAnnouncementsSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/announcements/RetrofitAnnouncementsSource.kt index b7be5fe5..521028f5 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/announcements/RetrofitAnnouncementsSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/announcements/RetrofitAnnouncementsSource.kt @@ -24,8 +24,11 @@ import com.mezhendosina.sgo.data.netschool.NetSchoolExpectedResults import com.mezhendosina.sgo.data.netschool.base.BaseRetrofitSource import com.mezhendosina.sgo.data.netschool.base.RetrofitConfig import java.lang.reflect.Type +import javax.inject.Inject +import javax.inject.Singleton -class RetrofitAnnouncementsSource( +@Singleton +class RetrofitAnnouncementsSource @Inject constructor( config: RetrofitConfig ) : BaseRetrofitSource(config), AnnouncementsSource { diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/attachments/RetrofitAttachmentsSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/attachments/RetrofitAttachmentsSource.kt index 4b7c4ff4..8f06d115 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/attachments/RetrofitAttachmentsSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/attachments/RetrofitAttachmentsSource.kt @@ -26,8 +26,11 @@ import okhttp3.MultipartBody import okhttp3.ResponseBody import retrofit2.Response import java.io.File +import javax.inject.Inject +import javax.inject.Singleton -class RetrofitAttachmentsSource(config: RetrofitConfig) : +@Singleton +class RetrofitAttachmentsSource @Inject constructor(config: RetrofitConfig) : BaseRetrofitSource(config), AttachmentsSource { private val attachmentsSource = retrofit.create(AttachmentsApi::class.java) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/diary/RetrofitDiarySource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/diary/RetrofitDiarySource.kt index 92db873f..6cb40243 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/diary/RetrofitDiarySource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/diary/RetrofitDiarySource.kt @@ -24,8 +24,11 @@ import com.mezhendosina.sgo.data.netschool.api.diary.entities.DiaryResponseEntit import com.mezhendosina.sgo.data.netschool.api.diary.entities.PastMandatoryEntity import com.mezhendosina.sgo.data.netschool.base.BaseRetrofitSource import com.mezhendosina.sgo.data.netschool.base.RetrofitConfig +import javax.inject.Inject -class RetrofitDiarySource(config: RetrofitConfig) : BaseRetrofitSource(config), DiarySource { +@javax.inject.Singleton +class RetrofitDiarySource @Inject constructor(config: RetrofitConfig) : BaseRetrofitSource(config), + DiarySource { private val diaryApi = retrofit.create(DiaryApi::class.java) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/grades/RetrofitGradesService.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/grades/RetrofitGradesSource.kt similarity index 89% rename from app/src/main/java/com/mezhendosina/sgo/data/netschool/api/grades/RetrofitGradesService.kt rename to app/src/main/java/com/mezhendosina/sgo/data/netschool/api/grades/RetrofitGradesSource.kt index 82af7415..fb482475 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/grades/RetrofitGradesService.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/grades/RetrofitGradesSource.kt @@ -21,8 +21,12 @@ import com.mezhendosina.sgo.data.netschool.base.BaseRetrofitSource import com.mezhendosina.sgo.data.netschool.base.RetrofitConfig import okhttp3.ResponseBody import retrofit2.Response +import javax.inject.Inject +import javax.inject.Singleton -class RetrofitGradesService(config: RetrofitConfig) : BaseRetrofitSource(config), GradesSource { +@Singleton +class RetrofitGradesSource @Inject constructor(config: RetrofitConfig) : + BaseRetrofitSource(config), GradesSource { private val gradesApi = retrofit.create(GradesApi::class.java) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/homework/RetrofitHomeworkSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/homework/RetrofitHomeworkSource.kt index 1865b054..910d2ce5 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/homework/RetrofitHomeworkSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/homework/RetrofitHomeworkSource.kt @@ -21,9 +21,12 @@ import com.mezhendosina.sgo.data.netschool.api.homework.entities.AssignResponseE import com.mezhendosina.sgo.data.netschool.api.homework.entities.GetAnswerResponseEntity import com.mezhendosina.sgo.data.netschool.base.BaseRetrofitSource import com.mezhendosina.sgo.data.netschool.base.RetrofitConfig +import javax.inject.Inject +import javax.inject.Singleton -class RetrofitHomeworkSource(config: RetrofitConfig) : +@Singleton +class RetrofitHomeworkSource @Inject constructor(config: RetrofitConfig) : BaseRetrofitSource(config), HomeworkSource { private val homeworkSource = retrofit.create(HomeworkApi::class.java) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/LoginApi.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/LoginApi.kt index 8e0a041e..4fa69006 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/LoginApi.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/LoginApi.kt @@ -21,6 +21,9 @@ import com.mezhendosina.sgo.data.netschool.api.login.entities.StudentResponseEnt import com.mezhendosina.sgo.data.netschool.api.login.entities.accountInfo.AccountInfoResponseEntity import com.mezhendosina.sgo.data.requests.sgo.login.entities.GetDataResponseEntity import com.mezhendosina.sgo.data.requests.sgo.login.entities.LoginResponseEntity +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import okhttp3.ResponseBody import retrofit2.Response import retrofit2.http.Field diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/LoginSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/LoginSource.kt index 4450829e..7d9faa39 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/LoginSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/LoginSource.kt @@ -22,9 +22,13 @@ import com.mezhendosina.sgo.data.netschool.api.login.entities.accountInfo.Accoun import com.mezhendosina.sgo.data.requests.sgo.login.entities.GetDataResponseEntity import com.mezhendosina.sgo.data.requests.sgo.login.entities.LoginResponseEntity import com.mezhendosina.sgo.data.requests.sgo.login.entities.LogoutRequestEntity +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import okhttp3.ResponseBody import retrofit2.Response + interface LoginSource { suspend fun loginData() @@ -43,5 +47,5 @@ interface LoginSource { suspend fun getStudents(): List? - suspend fun logout(logoutRequestEntity: LogoutRequestEntity) + suspend fun logout() } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/RetrofitLoginSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/RetrofitLoginSource.kt index 05f26491..90da0177 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/RetrofitLoginSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/login/RetrofitLoginSource.kt @@ -28,9 +28,12 @@ import com.mezhendosina.sgo.data.requests.sgo.login.entities.LoginResponseEntity import com.mezhendosina.sgo.data.requests.sgo.login.entities.LogoutRequestEntity import okhttp3.ResponseBody import retrofit2.Response +import javax.inject.Inject +import javax.inject.Singleton -class RetrofitLoginSource( - config: RetrofitConfig +@Singleton +class RetrofitLoginSource @Inject constructor( + config: RetrofitConfig, ) : BaseRetrofitSource(config), LoginSource { private val loginApi = retrofit.create(LoginApi::class.java) @@ -65,7 +68,8 @@ class RetrofitLoginSource( lt = loginEntity.lt, ver = loginEntity.ver ) - NetSchoolSingleton.loggedIn = true + com.mezhendosina.sgo.Singleton.at = resp.at + com.mezhendosina.sgo.Singleton.loggedIn = true resp } @@ -89,10 +93,10 @@ class RetrofitLoginSource( loginApi.getStudents().body() } - override suspend fun logout(logoutRequestEntity: LogoutRequestEntity) = + override suspend fun logout() = wrapRetrofitExceptions { - val resp = loginApi.logout(logoutRequestEntity.at) - NetSchoolSingleton.loggedIn = false + val resp = loginApi.logout(com.mezhendosina.sgo.Singleton.at) + com.mezhendosina.sgo.Singleton.loggedIn = false resp } } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/settings/RetrofitSettingsSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/settings/RetrofitSettingsSource.kt index 67325db4..e6e9147e 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/settings/RetrofitSettingsSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/settings/RetrofitSettingsSource.kt @@ -26,8 +26,12 @@ import com.mezhendosina.sgo.data.netschool.base.RetrofitConfig import okhttp3.MultipartBody import okhttp3.ResponseBody import retrofit2.Response +import javax.inject.Inject +import javax.inject.Singleton -class RetrofitSettingsSource(config: RetrofitConfig) : BaseRetrofitSource(config), SettingsSource { +@Singleton +class RetrofitSettingsSource @Inject constructor(config: RetrofitConfig) : + BaseRetrofitSource(config), SettingsSource { private val settingsApi = retrofit.create(SettingsApi::class.java) @@ -47,9 +51,10 @@ class RetrofitSettingsSource(config: RetrofitConfig) : BaseRetrofitSource(config } - override suspend fun getProfilePhoto(at: String, userId: Int): ByteArray? = + override suspend fun getProfilePhoto(userId: Int): ByteArray? = wrapRetrofitExceptions { - settingsApi.getProfilePhoto(at, userId).body()?.byteStream()?.readBytes() + settingsApi.getProfilePhoto(com.mezhendosina.sgo.Singleton.at, userId).body() + ?.byteStream()?.readBytes() } override suspend fun changePassword(userId: Int, password: ChangePasswordEntity) = diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/settings/SettingsSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/settings/SettingsSource.kt index 07bc24b8..851790bd 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/settings/SettingsSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/settings/SettingsSource.kt @@ -20,6 +20,9 @@ import com.mezhendosina.sgo.data.netschool.api.settings.entities.ChangePasswordE import com.mezhendosina.sgo.data.netschool.api.settings.entities.MySettingsRequestEntity import com.mezhendosina.sgo.data.netschool.api.settings.entities.MySettingsResponseEntity import com.mezhendosina.sgo.data.netschool.api.settings.entities.YearListResponseEntity +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import okhttp3.MultipartBody import okhttp3.ResponseBody import retrofit2.Response @@ -32,7 +35,7 @@ interface SettingsSource { suspend fun sendSettings(mySettingsRequestEntity: MySettingsRequestEntity) - suspend fun getProfilePhoto(at: String, userId: Int): ByteArray? + suspend fun getProfilePhoto(userId: Int): ByteArray? suspend fun changePassword(userId: Int, password: ChangePasswordEntity) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/BaseRetrofitSource.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/BaseRetrofitSource.kt index 8630dcfd..1acbd3da 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/BaseRetrofitSource.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/BaseRetrofitSource.kt @@ -17,26 +17,28 @@ package com.mezhendosina.sgo.data.netschool.base import com.google.gson.JsonParseException -import com.mezhendosina.sgo.app.netschool.base.BackendException -import com.mezhendosina.sgo.app.netschool.base.ConnectionException -import com.mezhendosina.sgo.app.netschool.base.ParseBackendResponseException -import com.mezhendosina.sgo.app.netschool.base.TimeOutError +import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.delay import okio.IOException import retrofit2.HttpException import java.net.SocketTimeoutException +import javax.inject.Inject -open class BaseRetrofitSource(retrofitConfig: RetrofitConfig) { +@Module +@InstallIn(SingletonComponent::class) +open class BaseRetrofitSource +@Inject constructor(retrofitConfig: RetrofitConfig) { val retrofit = retrofitConfig.retrofit - private val errorAdapter = retrofitConfig.gson.getAdapter(Error::class.java) suspend fun wrapRetrofitExceptions( - requireLogin: Boolean = true, - block: suspend () -> T + requireLogin: Boolean = true, block: suspend () -> T ): T { - if (NetSchoolSingleton.loggedIn || !requireLogin) { + if (Singleton.loggedIn || !requireLogin) { return try { block() } catch (e: JsonParseException) { diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/Exceptions.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/Exceptions.kt index 823c8769..6b76f59b 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/Exceptions.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/Exceptions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.mezhendosina.sgo.app.netschool.base +package com.mezhendosina.sgo.data.netschool.base import android.content.ActivityNotFoundException diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/RetrofitConfig.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/RetrofitConfig.kt index f658a0c9..f41ae726 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/RetrofitConfig.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/RetrofitConfig.kt @@ -18,8 +18,12 @@ package com.mezhendosina.sgo.data.netschool.base import com.google.gson.Gson import retrofit2.Retrofit +import javax.inject.Inject +import javax.inject.Singleton -class RetrofitConfig( + +@Singleton +class RetrofitConfig @Inject constructor( val retrofit: Retrofit, - val gson: Gson + val gson: Gson, ) \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/RetrofitSourcesProvider.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/RetrofitSourcesProvider.kt deleted file mode 100644 index a28b703b..00000000 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/RetrofitSourcesProvider.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2023 Eugene Menshenin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.mezhendosina.sgo.data.netschool.base - -import com.mezhendosina.sgo.app.model.announcements.AnnouncementsSource -import com.mezhendosina.sgo.app.model.grades.GradesSource -import com.mezhendosina.sgo.app.model.journal.DiarySource -import com.mezhendosina.sgo.data.github.GithubUpdateDownloader -import com.mezhendosina.sgo.data.netschool.api.announcements.RetrofitAnnouncementsSource -import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource -import com.mezhendosina.sgo.data.netschool.api.attachments.RetrofitAttachmentsSource -import com.mezhendosina.sgo.data.netschool.api.diary.RetrofitDiarySource -import com.mezhendosina.sgo.data.netschool.api.grades.RetrofitGradesService -import com.mezhendosina.sgo.data.netschool.api.homework.HomeworkSource -import com.mezhendosina.sgo.data.netschool.api.homework.RetrofitHomeworkSource -import com.mezhendosina.sgo.data.netschool.api.login.LoginSource -import com.mezhendosina.sgo.data.netschool.api.login.RetrofitLoginSource -import com.mezhendosina.sgo.data.netschool.api.settings.RetrofitSettingsSource -import com.mezhendosina.sgo.data.netschool.api.settings.SettingsSource - -class RetrofitSourcesProvider(private val config: RetrofitConfig) : SourcesProvider { - override fun getLoginSource(): LoginSource { - return RetrofitLoginSource(config) - } - - override fun getAttachmentsSource(): AttachmentsSource { - return RetrofitAttachmentsSource(config) - } - - - override fun getDiarySource(): DiarySource { - return RetrofitDiarySource(config) - } - - override fun getHomeworkSource(): HomeworkSource { - return RetrofitHomeworkSource(config) - } - - override fun getAnnouncementsSource(): AnnouncementsSource { - return RetrofitAnnouncementsSource(config) - } - - override fun getSettingsSource(): SettingsSource { - return RetrofitSettingsSource(config) - } - - override fun getGradesSource(): GradesSource { - return RetrofitGradesService(config) - } - - override fun getGithubUpdateDownloader(): GithubUpdateDownloader { - return GithubUpdateDownloader(config) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/SourceProviderHolder.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/SourceProviderHolder.kt deleted file mode 100644 index 20e93ea9..00000000 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/SourceProviderHolder.kt +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2023 Eugene Menshenin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.mezhendosina.sgo.data.netschool.base - -import com.google.gson.Gson -import com.mezhendosina.sgo.app.BuildConfig -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton.baseUrl -import okhttp3.Cookie -import okhttp3.CookieJar -import okhttp3.Headers -import okhttp3.HttpUrl -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import java.net.CookieManager -import java.net.CookiePolicy - -private val cookiesList = mutableListOf() - -class MyCookieJar : CookieJar { - - override fun loadForRequest(url: HttpUrl): List { - return cookiesList - } - - override fun saveFromResponse(url: HttpUrl, cookies: List) { - val cookieCopy = cookies.toMutableList() - - cookiesList.replaceAll { oldCookie -> - val findCookie = cookieCopy.find { it.name == oldCookie.name } - cookieCopy.remove(findCookie) - findCookie ?: oldCookie - } - cookiesList.addAll(cookieCopy) - - } -} - -object SourceProviderHolder { - - val sourcesProvider: SourcesProvider by lazy { - val gson = Gson().newBuilder().setLenient().create() - - - val config = RetrofitConfig( - retrofit = createRetrofit(gson), - gson = gson - ) - - RetrofitSourcesProvider(config) - } - - - private fun createRetrofit(gson: Gson): Retrofit { - return Retrofit.Builder() - .baseUrl("https://localhost/") - .client(createOkHttpClient()) - .addConverterFactory(GsonConverterFactory.create(gson)) - .build() - } - - private fun createOkHttpClient(): OkHttpClient { - val cookieManager = CookieManager() - cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL) - return OkHttpClient.Builder() - .cookieJar(MyCookieJar()) - .addInterceptor(createBaseUrlInterceptor()) - .addInterceptor(createHeadersInterceptor()) - .addInterceptor(createLoggingInterceptor()) - .build() - } - - private fun createHeadersInterceptor(): Interceptor { - return Interceptor { chain -> - val newBuilder = chain.request().newBuilder() - val headers = Headers.Builder() - .add("Host", baseUrl.replace("https://", "").dropLast(1)) - .add("Origin", baseUrl) - .add("UserAgent", "SGO app v${BuildConfig.VERSION_NAME}") - .add("X-Requested-With", "XMLHttpRequest") - .add("Sec-Fetch-Site", "same-origin") - .add("Sec-Fetch-Mode", "cors") - .add("Sec-Fetch-Dest", "empty") - .add("Referer", baseUrl) - .add( - "sec-ch-ua", - "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"105\", \"Microsoft Edge\";v=\"105\"" - ) - .add("Cookie", cookiesList.toCookieString()) - .add("at", NetSchoolSingleton.at) - .build() - newBuilder.headers(headers) - - return@Interceptor chain.proceed(newBuilder.build()) - } - } - - private fun createBaseUrlInterceptor(): Interceptor { - return Interceptor { chain -> - - val newBuilder = chain.request().newBuilder() - val url = chain.request().url.toString() - val a = if (baseUrl.isNotEmpty()) { - newBuilder.url(url.replace("https://localhost/", baseUrl)).build() - } else { - newBuilder.url(url).build() - } - return@Interceptor chain.proceed(a) - } - } - - private fun createLoggingInterceptor(): Interceptor { - return HttpLoggingInterceptor() - .setLevel(HttpLoggingInterceptor.Level.BODY) - } - - private fun List.toCookieString(): String { - var cookiesString = "" - - this.forEach { - if (it != null) { - cookiesString += "${it.name}=${it.value}; " - } - } - return cookiesString - } -} - - - diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/SourcesProvider.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/SourcesProvider.kt deleted file mode 100644 index f35b942e..00000000 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/base/SourcesProvider.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2023 Eugene Menshenin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.mezhendosina.sgo.data.netschool.base - -import com.mezhendosina.sgo.app.model.announcements.AnnouncementsSource -import com.mezhendosina.sgo.app.model.grades.GradesSource -import com.mezhendosina.sgo.app.model.journal.DiarySource -import com.mezhendosina.sgo.data.github.GithubUpdateDownloader -import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource -import com.mezhendosina.sgo.data.netschool.api.homework.HomeworkSource -import com.mezhendosina.sgo.data.netschool.api.login.LoginSource -import com.mezhendosina.sgo.data.netschool.api.settings.SettingsSource - - -interface SourcesProvider { - - fun getLoginSource(): LoginSource - - fun getAttachmentsSource(): AttachmentsSource - - fun getDiarySource(): DiarySource - fun getHomeworkSource(): HomeworkSource - - fun getAnnouncementsSource(): AnnouncementsSource - - fun getSettingsSource(): SettingsSource - - fun getGradesSource(): GradesSource - - fun getGithubUpdateDownloader(): GithubUpdateDownloader -} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepository.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepository.kt index cae17737..037ce247 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepository.kt @@ -19,20 +19,29 @@ package com.mezhendosina.sgo.data.netschool.repo import com.mezhendosina.sgo.app.model.answer.FileUiEntity import com.mezhendosina.sgo.app.model.journal.entities.LessonUiEntity import com.mezhendosina.sgo.app.uiEntities.AboutLessonUiEntity +import com.mezhendosina.sgo.app.uiEntities.AssignTypeUiEntity import com.mezhendosina.sgo.app.uiEntities.WhyGradeEntity import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource import com.mezhendosina.sgo.data.netschool.api.attachments.entities.AttachmentsRequestEntity import com.mezhendosina.sgo.data.netschool.api.homework.HomeworkSource +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import javax.inject.Inject typealias LessonActionListener = (lesson: AboutLessonUiEntity?) -> Unit -class LessonRepository( +@Module +@InstallIn(SingletonComponent::class) +class LessonRepository @Inject constructor( private val homeworkSource: HomeworkSource, private val attachmentsSource: AttachmentsSource, ) { + private var assignTypes: List? = null + private var lesson: AboutLessonUiEntity? = null private val listeners = mutableSetOf() @@ -85,7 +94,7 @@ class LessonRepository( private suspend fun loadGrades(lessonUiEntity: LessonUiEntity): List { val gradesList = mutableListOf() - if (NetSchoolSingleton.assignTypes.isNullOrEmpty()) NetSchoolSingleton.assignTypes = + if (assignTypes.isNullOrEmpty()) assignTypes = homeworkSource.assignmentTypes().map { it.toUiEntity() } lessonUiEntity.assignments?.forEach { assign -> @@ -94,7 +103,7 @@ class LessonRepository( WhyGradeEntity( assign.assignmentName, assign.mark, - NetSchoolSingleton.assignTypes?.firstOrNull { it.id == assign.typeId }?.name + assignTypes?.firstOrNull { it.id == assign.typeId }?.name ) ) } diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepository.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepository.kt index fd1c0c35..deda3022 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepository.kt @@ -17,9 +17,9 @@ package com.mezhendosina.sgo.data.netschool.repo import android.content.Context +import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.editPreference import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.login.LoginEntity import com.mezhendosina.sgo.data.netschool.api.login.LoginSource @@ -28,52 +28,77 @@ import com.mezhendosina.sgo.data.netschool.api.login.entities.accountInfo.User import com.mezhendosina.sgo.data.netschool.api.settings.SettingsSource import com.mezhendosina.sgo.data.requests.sgo.login.entities.LoginResponseEntity import com.mezhendosina.sgo.data.requests.sgo.login.entities.LogoutRequestEntity +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.qualifiers.ActivityContext +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext +import javax.inject.Inject -class LoginRepository( + +@Module +@InstallIn(ActivityComponent::class) +class LoginRepository +@Inject constructor( private val loginSource: LoginSource, + private val settingsDataStore: SettingsDataStore, private val settingsSource: SettingsSource ) { - val schools = MutableSharedFlow>() - + private val _schools = MutableSharedFlow>() + val schools: SharedFlow> = _schools suspend fun findSchool(name: String) { val schoolsList = loginSource.getSchools(name).map { it.toUiEntity() } withContext(Dispatchers.Main) { - schools.emit(schoolsList) - NetSchoolSingleton.schools = schoolsList + _schools.emit(schoolsList) } } suspend fun login( - context: Context, - login: String, - password: String, - schoolId: Int, + login: String? = null, + password: String? = null, + schoolId: Int? = null, firstLogin: Boolean = true, onOneUser: () -> Unit = {}, onMoreUser: (List) -> Unit = {} ) { + + loginSource.loginData() val getData = loginSource.getData() - val loginEntity = LoginEntity( - schoolId, - login, - password, - getData.lt, - getData.salt, - getData.ver - ) + + val loginEntity = + if (login.isNullOrEmpty() || password.isNullOrEmpty() || schoolId == null) { + LoginEntity( + settingsDataStore.getValue(SettingsDataStore.SCHOOL_ID).first() ?: -1, + settingsDataStore.getValue(SettingsDataStore.LOGIN).first() ?: "", + settingsDataStore.getValue(SettingsDataStore.PASSWORD).first() ?: "", + getData.lt, + getData.salt, + getData.ver + ) + } else { + LoginEntity( + schoolId, + login, + password, + getData.lt, + getData.salt, + getData.ver + ) + } val loginRequest = loginSource.login(loginEntity) - NetSchoolSingleton.at = loginRequest.at - postLogin(context, loginEntity, loginRequest, firstLogin, onOneUser, onMoreUser) + postLogin(loginEntity, loginRequest, firstLogin, onOneUser, onMoreUser) } private suspend fun postLogin( - context: Context, loginEntity: LoginEntity, loginRequest: LoginResponseEntity, firstLogin: Boolean, @@ -86,23 +111,23 @@ class LoginRepository( withContext(Dispatchers.Main) { if (studentsRequest != null) { if (studentsRequest.size <= 1) { - SettingsDataStore.CURRENT_USER_ID.editPreference( - context, + settingsDataStore.setValue( + SettingsDataStore.CURRENT_USER_ID, studentsRequest.first().id ) onOneUser.invoke() - SettingsDataStore().saveLogin(context, loginEntity) + settingsDataStore.saveLogin(loginEntity) } else { onMoreUser.invoke(studentsRequest) - SettingsDataStore().saveLogin(context, loginEntity, false) + settingsDataStore.saveLogin(loginEntity, false) } } else { - SettingsDataStore.CURRENT_USER_ID.editPreference( - context, + settingsDataStore.setValue( + SettingsDataStore.CURRENT_USER_ID, loginRequest.accountInfo.user.id ) onOneUser.invoke() - SettingsDataStore().saveLogin(context, loginEntity) + settingsDataStore.saveLogin(loginEntity) } } } @@ -117,11 +142,12 @@ class LoginRepository( loginSource.getGosuslugiAccountInfo(loginState).users suspend fun gosuslugiLogin( - context: Context, - loginState: String, - userId: String, firstLogin: Boolean = false ) { + val loginState = + settingsDataStore.getValue(SettingsDataStore.ESIA_LOGIN_STATE).first() ?: "" + val userId = settingsDataStore.getValue(SettingsDataStore.ESIA_USER_ID).first() ?: "" + val login = if (!firstLogin) { loginSource.crossLogin() loginSource.gosuslugiLogin(loginState, userId) @@ -131,13 +157,13 @@ class LoginRepository( withContext(Dispatchers.Main) { if (firstLogin) { - SettingsDataStore().saveEsiaLogin(context, loginState, userId) - SettingsDataStore.LOGGED_IN.editPreference(context, true) + settingsDataStore.saveEsiaLogin(loginState, userId) + settingsDataStore.setValue(SettingsDataStore.LOGGED_IN, true) } - NetSchoolSingleton.at = login.at + Singleton.at = login.at } } - suspend fun logout() = loginSource.logout(LogoutRequestEntity(NetSchoolSingleton.at)) + suspend fun logout() = loginSource.logout() } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/RegionsRepository.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/RegionsRepository.kt index ebf70792..8cd45bb9 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/RegionsRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/RegionsRepository.kt @@ -20,8 +20,16 @@ import com.google.gson.Gson import com.mezhendosina.sgo.app.ui.loginFlow.chooseRegion.entities.ChooseRegionUiEntity import com.mezhendosina.sgo.data.netschool.api.regions.Regions import com.mezhendosina.sgo.data.netschool.api.regions.RegionsApi +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.components.SingletonComponent +import javax.inject.Inject -class RegionsRepository : RegionsApi { + +@Module +@InstallIn(ActivityComponent::class) +class RegionsRepository @Inject constructor() : RegionsApi { override fun getRegions(): ChooseRegionUiEntity = Gson().fromJson( Regions.REGIONS, ChooseRegionUiEntity::class.java diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/SettingsRepository.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/SettingsRepository.kt index 57346f6d..7ce8fecf 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/SettingsRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/SettingsRepository.kt @@ -26,34 +26,51 @@ import com.mezhendosina.sgo.data.netschool.api.settings.SettingsSource import com.mezhendosina.sgo.data.netschool.api.settings.entities.ChangePasswordEntity import com.mezhendosina.sgo.data.netschool.api.settings.entities.MySettingsRequestEntity import com.mezhendosina.sgo.data.netschool.api.settings.entities.MySettingsResponseEntity +import com.mezhendosina.sgo.data.netschool.base.BaseRetrofitSource import com.mezhendosina.sgo.data.requests.notifications.NotificationsSource import com.mezhendosina.sgo.data.requests.notifications.entities.NotificationUserEntity import com.mezhendosina.sgo.data.requests.notifications.entities.UnregisterUserEntity +import com.mezhendosina.sgo.di.BaseRetrofit +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.qualifiers.ActivityContext +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.toRequestBody import java.io.File +import javax.inject.Inject -class SettingsRepository(private val settingsSource: SettingsSource) { - private val notificationsSource = NotificationsSource() +@Module +@InstallIn(ActivityComponent::class) +class SettingsRepository +@Inject constructor( + private val settingsSource: SettingsSource +) { suspend fun getMySettings(): MySettingsResponseEntity { return if (Singleton.mySettings.value != null) Singleton.mySettings.value!! else settingsSource.getSettings() } - suspend fun loadProfilePhoto(userId: Int, file: File, isExist: Boolean) { - if (isExist) { - val photo = settingsSource.getProfilePhoto(NetSchoolSingleton.at, userId) - if (photo != null) { - file.writeBytes(photo) - } + suspend fun loadProfilePhoto(userId: Int, file: File) { + val photo = settingsSource.getProfilePhoto(userId) + if (photo != null) { + file.writeBytes(photo) } } - suspend fun changeProfilePhoto(context: Context, uri: Uri?, studentId: Int) { + + suspend fun changeProfilePhoto( + context: Context, + uri: Uri?, + studentId: Int + ) { val contentResolver = context.contentResolver val a = uri?.let { contentResolver.openInputStream(it) } @@ -69,29 +86,6 @@ class SettingsRepository(private val settingsSource: SettingsSource) { } } - - suspend fun registerGradesNotifications(userEntity: NotificationUserEntity) { - notificationsSource.notificationsSource.registerUser(userEntity) - } - - suspend fun isGradesNotifyUserExist(userId: Int, firebaseToken: String): Boolean = - notificationsSource.notificationsSource.isUserExist( - UnregisterUserEntity( - userId, - firebaseToken - ) - ) - - - suspend fun unregisterGradesNotifications(userId: Int, token: String) { - notificationsSource.notificationsSource.unregisterUser( - UnregisterUserEntity( - userId, - token - ) - ) - } - suspend fun sendSettings(mySettingsRequestEntity: MySettingsRequestEntity) { settingsSource.sendSettings(mySettingsRequestEntity) withContext(Dispatchers.Main) { diff --git a/app/src/main/java/com/mezhendosina/sgo/di/BaseRetrofit.kt b/app/src/main/java/com/mezhendosina/sgo/di/BaseRetrofit.kt new file mode 100644 index 00000000..c0721021 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/di/BaseRetrofit.kt @@ -0,0 +1,95 @@ +package com.mezhendosina.sgo.di + +import com.google.gson.Gson +import com.mezhendosina.sgo.app.BuildConfig +import com.mezhendosina.sgo.data.SettingsDataStore +import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.flow.first +import okhttp3.Cookie +import okhttp3.Headers +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.net.CookieManager +import java.net.CookiePolicy +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.math.log + +data class BaseUrl(val baseUrl: String) + +@Module +@InstallIn(SingletonComponent::class) +class BaseRetrofit { + + @Inject + lateinit var settingsDataStore: SettingsDataStore + + var baseUrl = BaseUrl("") + + @Provides + @Singleton + fun createBaseUrl(): BaseUrl = baseUrl + + @Provides + @Singleton + fun createMyCookieJar(): MyCookieJar = MyCookieJar() + + @Provides + @Singleton + fun createRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit { + return Retrofit.Builder() + .baseUrl("https://localhost/") + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build() + } + + @Provides + @Singleton + fun createGson(): Gson = Gson() + + + @Provides + @Singleton + fun createOkHttpClient( + baseUrlInterceptor: BaseUrlInterceptor, + headersInterceptor: HeadersInterceptor, + loggingInterceptor: Interceptor + ): OkHttpClient { + val cookieManager = CookieManager() + cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL) + return OkHttpClient.Builder() + .cookieJar(MyCookieJar()) + .addInterceptor(baseUrlInterceptor) + .addInterceptor(headersInterceptor) + .addInterceptor(loggingInterceptor) + .build() + } + + @Provides + @Singleton + fun createHeadersInterceptor( + myCookieJar: MyCookieJar, + baseUrl: BaseUrl, + ): HeadersInterceptor = + HeadersInterceptor(myCookieJar, baseUrl.baseUrl) + + @Provides + @Singleton + fun createBaseUrlInterceptor(baseUrl: BaseUrl): BaseUrlInterceptor = + BaseUrlInterceptor(baseUrl.baseUrl) + + @Provides + @Singleton + fun createLoggingInterceptor(): Interceptor { + return HttpLoggingInterceptor() + .setLevel(HttpLoggingInterceptor.Level.BODY) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/BaseUrlInterceptor.kt b/app/src/main/java/com/mezhendosina/sgo/di/BaseUrlInterceptor.kt new file mode 100644 index 00000000..b32d5d25 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/di/BaseUrlInterceptor.kt @@ -0,0 +1,29 @@ +package com.mezhendosina.sgo.di + +import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.Interceptor +import okhttp3.Response +import javax.inject.Inject +import javax.inject.Singleton + + +@Module +@InstallIn(SingletonComponent::class) +class BaseUrlInterceptor @Inject constructor( + private val baseUrl: String +) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val newBuilder = chain.request().newBuilder() + val url = chain.request().url.toString() + val a = if (baseUrl.isNotEmpty()) { + newBuilder.url(url.replace("https://localhost/", baseUrl)) + .build() + } else { + newBuilder.url(url).build() + } + return chain.proceed(a) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/HeadersInterceptor.kt b/app/src/main/java/com/mezhendosina/sgo/di/HeadersInterceptor.kt new file mode 100644 index 00000000..d0e620ff --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/di/HeadersInterceptor.kt @@ -0,0 +1,47 @@ +package com.mezhendosina.sgo.di + +import com.mezhendosina.sgo.Singleton +import com.mezhendosina.sgo.app.BuildConfig +import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.Headers +import okhttp3.Interceptor +import okhttp3.Response +import javax.inject.Inject + + +@Module +@InstallIn(SingletonComponent::class) +class HeadersInterceptor +@Inject constructor( + private val myCookieJar: MyCookieJar, + private val baseUrl: String, +) : Interceptor { + + + override fun intercept(chain: Interceptor.Chain): Response { + val newBuilder = chain.request().newBuilder() + val headers = Headers.Builder() + .add("Host", baseUrl.replace("https://", "").dropLast(1)) + .add("Origin", baseUrl) + .add("UserAgent", "SGO app v${BuildConfig.VERSION_NAME}") + .add("X-Requested-With", "XMLHttpRequest") + .add("Sec-Fetch-Site", "same-origin") + .add("Sec-Fetch-Mode", "cors") + .add("Sec-Fetch-Dest", "empty") + .add("Referer", baseUrl) + .add( + "sec-ch-ua", + "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"105\", \"Microsoft Edge\";v=\"105\"" + ) + .add("Cookie", myCookieJar.toCookieString()) + .add("at", Singleton.at) + .build() + newBuilder.headers(headers) + + return chain.proceed(newBuilder.build()) + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/MyCookieJar.kt b/app/src/main/java/com/mezhendosina/sgo/di/MyCookieJar.kt new file mode 100644 index 00000000..b24c1540 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/di/MyCookieJar.kt @@ -0,0 +1,43 @@ +package com.mezhendosina.sgo.di + +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.Cookie +import okhttp3.CookieJar +import okhttp3.HttpUrl + +@Module +@InstallIn(SingletonComponent::class) +class MyCookieJar : CookieJar { + + private val cookiesList = mutableListOf() + + + override fun loadForRequest(url: HttpUrl): List { + return cookiesList + } + + override fun saveFromResponse(url: HttpUrl, cookies: List) { + val cookieCopy = cookies.toMutableList() + + cookiesList.replaceAll { oldCookie -> + val findCookie = cookieCopy.find { it.name == oldCookie.name } + cookieCopy.remove(findCookie) + findCookie ?: oldCookie + } + cookiesList.addAll(cookieCopy) + + } + + fun toCookieString(): String { + var cookiesString = "" + + cookiesList.forEach { + if (it != null) { + cookiesString += "${it.name}=${it.value}; " + } + } + return cookiesString + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/SettingsModule.kt b/app/src/main/java/com/mezhendosina/sgo/di/SettingsModule.kt new file mode 100644 index 00000000..2f464450 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/di/SettingsModule.kt @@ -0,0 +1,18 @@ +package com.mezhendosina.sgo.di + +import com.mezhendosina.sgo.data.AppSettings +import com.mezhendosina.sgo.data.SettingsDataStore +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +abstract class SettingsModule { + + @Binds + abstract fun bindSettingsDataStore( + settingsDataStore: SettingsDataStore + ): AppSettings +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/SourcesModule.kt b/app/src/main/java/com/mezhendosina/sgo/di/SourcesModule.kt new file mode 100644 index 00000000..2456d4e4 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/di/SourcesModule.kt @@ -0,0 +1,60 @@ +package com.mezhendosina.sgo.di + +import com.mezhendosina.sgo.app.model.announcements.AnnouncementsSource +import com.mezhendosina.sgo.app.model.grades.GradesSource +import com.mezhendosina.sgo.app.model.journal.DiarySource +import com.mezhendosina.sgo.data.netschool.api.announcements.RetrofitAnnouncementsSource +import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource +import com.mezhendosina.sgo.data.netschool.api.attachments.RetrofitAttachmentsSource +import com.mezhendosina.sgo.data.netschool.api.diary.RetrofitDiarySource +import com.mezhendosina.sgo.data.netschool.api.grades.RetrofitGradesSource +import com.mezhendosina.sgo.data.netschool.api.homework.HomeworkSource +import com.mezhendosina.sgo.data.netschool.api.homework.RetrofitHomeworkSource +import com.mezhendosina.sgo.data.netschool.api.login.LoginSource +import com.mezhendosina.sgo.data.netschool.api.login.RetrofitLoginSource +import com.mezhendosina.sgo.data.netschool.api.settings.RetrofitSettingsSource +import com.mezhendosina.sgo.data.netschool.api.settings.SettingsSource +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +abstract class SourcesModule { + + @Binds + abstract fun bindAnnouncementsSource( + retrofitAnnouncementsSource: RetrofitAnnouncementsSource + ): AnnouncementsSource + + @Binds + abstract fun bindAttachmentsSource( + retrofitAttachmentsSource: RetrofitAttachmentsSource + ): AttachmentsSource + + @Binds + abstract fun bindDiarySource( + retrofitDiarySource: RetrofitDiarySource + ): DiarySource + + @Binds + abstract fun bindGradesSource( + retrofitGradesSource: RetrofitGradesSource + ): GradesSource + + @Binds + abstract fun bindHomeworkSource( + retrofitHomeworkSource: RetrofitHomeworkSource + ): HomeworkSource + + @Binds + abstract fun bindLoginSource( + retrofitLoginSource: RetrofitLoginSource + ): LoginSource + + @Binds + abstract fun bindSettingsSource( + retrofitSettingsSource: RetrofitSettingsSource + ): SettingsSource +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/StateInterceptor.kt b/app/src/main/java/com/mezhendosina/sgo/di/StateInterceptor.kt new file mode 100644 index 00000000..2dbe93e6 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/di/StateInterceptor.kt @@ -0,0 +1,21 @@ +package com.mezhendosina.sgo.di + +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.Interceptor +import okhttp3.Response +import javax.inject.Inject + +@Module +@InstallIn(SingletonComponent::class) +class StateInterceptor @Inject constructor( +) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val newBuilder = chain.request().newBuilder() +// if (chain.request().url.encodedPath.contains("login") || loggedIn == LoggedIn.TRUE) { + return chain.proceed(newBuilder.build()) + +// } + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3804d05c..7242d84e 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,8 @@ plugins { id 'com.android.application' version '8.1.0' apply false id 'com.android.library' version '8.1.0' apply false id 'org.jetbrains.kotlin.android' version '1.7.10' apply false + + id 'com.google.dagger.hilt.android' version '2.44' apply false } task clean(type: Delete) { From 4878609efc267c4d76d5d7ae499909f1d0d2da53 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Tue, 28 Nov 2023 10:43:45 +0300 Subject: [PATCH 09/17] fix: debug di --- .../sgo/app/activities/SplashActivity.kt | 5 +- .../ui/main/container/ContainerFragment.kt | 2 + .../UpdateBottomSheetFragment.kt | 2 + .../app/ui/settingsFlow/SettingsContainer.kt | 2 + .../app/ui/settingsFlow/SettingsFragment.kt | 3 + .../ui/settingsFlow/about/AboutAppFragment.kt | 2 + .../ChangeControlQuestionFragment.kt | 2 + .../ChangeControlQuestionViewModel.kt | 2 + .../changeEmail/ChangeEmailFragment.kt | 3 +- .../changePhone/ChangePhoneFragment.kt | 2 + .../changeTheme/ChangeThemeFragment.kt | 2 + .../sgo/data/SettingsDataStore.kt | 4 +- .../com/mezhendosina/sgo/di/BaseRetrofit.kt | 31 +++------- .../mezhendosina/sgo/di/BaseUrlInterceptor.kt | 21 ++++++- .../mezhendosina/sgo/di/HeadersInterceptor.kt | 56 ++++++++++++------- 15 files changed, 88 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/activities/SplashActivity.kt b/app/src/main/java/com/mezhendosina/sgo/app/activities/SplashActivity.kt index b5ce09d4..1801c30a 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/activities/SplashActivity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/activities/SplashActivity.kt @@ -38,9 +38,11 @@ import javax.inject.Inject @AndroidEntryPoint class SplashActivity : AppCompatActivity() { - @Inject lateinit var settingsDataStore: AppSettings + @Inject + lateinit var settingsDataStore: AppSettings override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) runBlocking { AppCompatDelegate.setDefaultNightMode( settingsDataStore.getValue(SettingsDataStore.THEME).first() @@ -49,7 +51,6 @@ class SplashActivity : AppCompatActivity() { } if (!BuildConfig.DEBUG) DynamicColors.applyToActivitiesIfAvailable(this.application) // DynamicColors.applyToActivitiesIfAvailable(this.application) - super.onCreate(savedInstanceState) CoroutineScope(Dispatchers.Main).launch { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt index abfafed8..b2efe4bc 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt @@ -60,6 +60,7 @@ import com.mezhendosina.sgo.app.utils.slideUpAnimation import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.currentWeekStart import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first @@ -67,6 +68,7 @@ import kotlinx.coroutines.launch import java.io.File import javax.inject.Inject +@AndroidEntryPoint class ContainerFragment : Fragment(R.layout.container_main), GradesFilterInterface, GradesActionsInterface, ContainerNavigationInterface { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/updateBottomSheet/UpdateBottomSheetFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/updateBottomSheet/UpdateBottomSheetFragment.kt index 1dbc9df4..ea962f75 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/updateBottomSheet/UpdateBottomSheetFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/updateBottomSheet/UpdateBottomSheetFragment.kt @@ -26,6 +26,7 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.ModalSheetUpdateBinding import com.mezhendosina.sgo.app.ui.main.container.ContainerViewModel import com.mezhendosina.sgo.data.requests.github.checkUpdates.CheckUpdates +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -33,6 +34,7 @@ import java.io.File typealias onUpdateClickListener = () -> Unit +@AndroidEntryPoint class UpdateBottomSheetFragment( private val updateLog: CheckUpdates, private val onUpdateClickListener: onUpdateClickListener, diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsContainer.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsContainer.kt index a784ba98..4997748e 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsContainer.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsContainer.kt @@ -25,11 +25,13 @@ import androidx.navigation.ui.NavigationUI import com.google.android.material.transition.platform.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.ContainerSettingsBinding +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch +@AndroidEntryPoint class SettingsContainer : Fragment(R.layout.container_settings) { lateinit var binding: ContainerSettingsBinding diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsFragment.kt index 80acb465..cc31d2d2 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/SettingsFragment.kt @@ -32,12 +32,15 @@ import com.mezhendosina.sgo.app.databinding.FragmentSettingsBinding import com.mezhendosina.sgo.app.ui.settingsFlow.changeControlQuestion.ChangeControlQuestionFragment import com.mezhendosina.sgo.app.ui.settingsFlow.changeEmail.ChangeEmailFragment import com.mezhendosina.sgo.app.ui.settingsFlow.changePhone.ChangePhoneFragment +import dagger.hilt.android.AndroidEntryPoint +import dagger.hilt.android.HiltAndroidApp import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File +@AndroidEntryPoint class SettingsFragment : Fragment(R.layout.fragment_settings) { private val viewModel: SettingsViewModel by viewModels() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/about/AboutAppFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/about/AboutAppFragment.kt index f4e3f759..ee8b1d75 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/about/AboutAppFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/about/AboutAppFragment.kt @@ -25,9 +25,11 @@ import com.google.android.material.transition.platform.MaterialSharedAxis import com.mezhendosina.sgo.app.BuildConfig import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentAboutAppBinding +import dagger.hilt.android.AndroidEntryPoint import io.noties.markwon.Markwon import io.noties.markwon.html.HtmlPlugin +@AndroidEntryPoint class AboutAppFragment : Fragment(R.layout.fragment_about_app) { private lateinit var binding: FragmentAboutAppBinding diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionFragment.kt index 423320be..7199956d 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionFragment.kt @@ -30,7 +30,9 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentChangeControlQuestionBinding import com.mezhendosina.sgo.app.ui.settingsFlow.SettingsViewModel import com.mezhendosina.sgo.data.netschool.base.toMD5 +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class ChangeControlQuestionFragment : Fragment(R.layout.fragment_change_control_question) { private lateinit var binding: FragmentChangeControlQuestionBinding diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionViewModel.kt index 3fd291ea..afbfb821 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionViewModel.kt @@ -19,7 +19,9 @@ package com.mezhendosina.sgo.app.ui.settingsFlow.changeControlQuestion import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +@HiltViewModel class ChangeControlQuestionViewModel : ViewModel() { private val _questions = MutableLiveData>() val questions: LiveData> = _questions diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeEmail/ChangeEmailFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeEmail/ChangeEmailFragment.kt index 12491ea8..c464d292 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeEmail/ChangeEmailFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeEmail/ChangeEmailFragment.kt @@ -24,10 +24,11 @@ import androidx.navigation.fragment.findNavController import com.google.android.material.transition.platform.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentChangeEmailBinding +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch - +@AndroidEntryPoint class ChangeEmailFragment : Fragment(R.layout.fragment_change_email) { lateinit var binding: FragmentChangeEmailBinding diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePhone/ChangePhoneFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePhone/ChangePhoneFragment.kt index b2ce512f..652b7eb0 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePhone/ChangePhoneFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changePhone/ChangePhoneFragment.kt @@ -26,10 +26,12 @@ import androidx.navigation.fragment.findNavController import com.google.android.material.transition.platform.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentChangePhoneNumberBinding +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +@AndroidEntryPoint class ChangePhoneFragment : Fragment(R.layout.fragment_change_phone_number) { lateinit var binding: FragmentChangePhoneNumberBinding diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeFragment.kt index 7adcaa5b..598f1391 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeTheme/ChangeThemeFragment.kt @@ -24,7 +24,9 @@ import androidx.fragment.app.viewModels import com.google.android.material.transition.platform.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentSettingsThemeBinding +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class ChangeThemeFragment : Fragment(R.layout.fragment_settings_theme) { private var binding: FragmentSettingsThemeBinding? = null diff --git a/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt b/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt index fbbab320..9d063e2e 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt @@ -11,8 +11,6 @@ import androidx.datastore.preferences.preferencesDataStore import com.mezhendosina.sgo.data.netschool.api.login.LoginEntity import dagger.Module import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent -import dagger.hilt.android.qualifiers.ActivityContext import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.flow.Flow @@ -22,6 +20,8 @@ import javax.inject.Inject val Context.dataStore: DataStore by preferencesDataStore(name = "settings") +@Module +@InstallIn(SingletonComponent::class) class SettingsDataStore @Inject constructor(@ApplicationContext private val context: Context) : AppSettings { diff --git a/app/src/main/java/com/mezhendosina/sgo/di/BaseRetrofit.kt b/app/src/main/java/com/mezhendosina/sgo/di/BaseRetrofit.kt index c0721021..17230fa2 100644 --- a/app/src/main/java/com/mezhendosina/sgo/di/BaseRetrofit.kt +++ b/app/src/main/java/com/mezhendosina/sgo/di/BaseRetrofit.kt @@ -1,16 +1,11 @@ package com.mezhendosina.sgo.di import com.google.gson.Gson -import com.mezhendosina.sgo.app.BuildConfig import com.mezhendosina.sgo.data.SettingsDataStore -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.flow.first -import okhttp3.Cookie -import okhttp3.Headers import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -18,25 +13,12 @@ import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.net.CookieManager import java.net.CookiePolicy -import javax.inject.Inject import javax.inject.Singleton -import kotlin.math.log - -data class BaseUrl(val baseUrl: String) @Module @InstallIn(SingletonComponent::class) class BaseRetrofit { - @Inject - lateinit var settingsDataStore: SettingsDataStore - - var baseUrl = BaseUrl("") - - @Provides - @Singleton - fun createBaseUrl(): BaseUrl = baseUrl - @Provides @Singleton fun createMyCookieJar(): MyCookieJar = MyCookieJar() @@ -61,12 +43,13 @@ class BaseRetrofit { fun createOkHttpClient( baseUrlInterceptor: BaseUrlInterceptor, headersInterceptor: HeadersInterceptor, + myCookieJar: MyCookieJar, loggingInterceptor: Interceptor ): OkHttpClient { val cookieManager = CookieManager() cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL) return OkHttpClient.Builder() - .cookieJar(MyCookieJar()) + .cookieJar(myCookieJar) .addInterceptor(baseUrlInterceptor) .addInterceptor(headersInterceptor) .addInterceptor(loggingInterceptor) @@ -77,14 +60,14 @@ class BaseRetrofit { @Singleton fun createHeadersInterceptor( myCookieJar: MyCookieJar, - baseUrl: BaseUrl, + settingsDataStore: SettingsDataStore ): HeadersInterceptor = - HeadersInterceptor(myCookieJar, baseUrl.baseUrl) + HeadersInterceptor(myCookieJar, settingsDataStore) @Provides @Singleton - fun createBaseUrlInterceptor(baseUrl: BaseUrl): BaseUrlInterceptor = - BaseUrlInterceptor(baseUrl.baseUrl) + fun createBaseUrlInterceptor(settingsDataStore: SettingsDataStore): BaseUrlInterceptor = + BaseUrlInterceptor(settingsDataStore) @Provides @Singleton @@ -92,4 +75,6 @@ class BaseRetrofit { return HttpLoggingInterceptor() .setLevel(HttpLoggingInterceptor.Level.BODY) } + + } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/BaseUrlInterceptor.kt b/app/src/main/java/com/mezhendosina/sgo/di/BaseUrlInterceptor.kt index b32d5d25..9780640e 100644 --- a/app/src/main/java/com/mezhendosina/sgo/di/BaseUrlInterceptor.kt +++ b/app/src/main/java/com/mezhendosina/sgo/di/BaseUrlInterceptor.kt @@ -1,20 +1,35 @@ package com.mezhendosina.sgo.di -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import com.mezhendosina.sgo.data.SettingsDataStore import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import okhttp3.Interceptor import okhttp3.Response import javax.inject.Inject -import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) class BaseUrlInterceptor @Inject constructor( - private val baseUrl: String + private val settingsDataStore: SettingsDataStore ) : Interceptor { + + private var baseUrl = "" + + init { + CoroutineScope(Dispatchers.IO).launch { + settingsDataStore.getValue(SettingsDataStore.REGION_URL).collect { + if (it != null){ + baseUrl = it + } + } + } + } + override fun intercept(chain: Interceptor.Chain): Response { val newBuilder = chain.request().newBuilder() val url = chain.request().url.toString() diff --git a/app/src/main/java/com/mezhendosina/sgo/di/HeadersInterceptor.kt b/app/src/main/java/com/mezhendosina/sgo/di/HeadersInterceptor.kt index d0e620ff..1bb43548 100644 --- a/app/src/main/java/com/mezhendosina/sgo/di/HeadersInterceptor.kt +++ b/app/src/main/java/com/mezhendosina/sgo/di/HeadersInterceptor.kt @@ -2,10 +2,13 @@ package com.mezhendosina.sgo.di import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.BuildConfig -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import com.mezhendosina.sgo.data.SettingsDataStore import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import okhttp3.Headers import okhttp3.Interceptor import okhttp3.Response @@ -17,31 +20,44 @@ import javax.inject.Inject class HeadersInterceptor @Inject constructor( private val myCookieJar: MyCookieJar, - private val baseUrl: String, + private val settingsDataStore: SettingsDataStore, ) : Interceptor { + private var baseUrl = "" + + init { + CoroutineScope(Dispatchers.IO).launch { + settingsDataStore.getValue(SettingsDataStore.REGION_URL).collect { + if (it != null){ + baseUrl = it + } + } + } + } override fun intercept(chain: Interceptor.Chain): Response { val newBuilder = chain.request().newBuilder() - val headers = Headers.Builder() - .add("Host", baseUrl.replace("https://", "").dropLast(1)) - .add("Origin", baseUrl) - .add("UserAgent", "SGO app v${BuildConfig.VERSION_NAME}") - .add("X-Requested-With", "XMLHttpRequest") - .add("Sec-Fetch-Site", "same-origin") - .add("Sec-Fetch-Mode", "cors") - .add("Sec-Fetch-Dest", "empty") - .add("Referer", baseUrl) - .add( - "sec-ch-ua", - "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"105\", \"Microsoft Edge\";v=\"105\"" - ) - .add("Cookie", myCookieJar.toCookieString()) - .add("at", Singleton.at) - .build() - newBuilder.headers(headers) - + if (baseUrl.isNotEmpty()) { + val headers = Headers.Builder() + .add("Host", baseUrl.replace("https://", "").dropLast(1)) + .add("Origin", baseUrl) + .add("UserAgent", "SGO app v${BuildConfig.VERSION_NAME}") + .add("X-Requested-With", "XMLHttpRequest") + .add("Sec-Fetch-Site", "same-origin") + .add("Sec-Fetch-Mode", "cors") + .add("Sec-Fetch-Dest", "empty") + .add("Referer", baseUrl) + .add( + "sec-ch-ua", + "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"105\", \"Microsoft Edge\";v=\"105\"" + ) + .add("Cookie", myCookieJar.toCookieString()) + .add("at", Singleton.at) + .build() + newBuilder.headers(headers) + } return chain.proceed(newBuilder.build()) + } } \ No newline at end of file From e9332a4f71be6974c983e3aab1101a31216bb714 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Tue, 28 Nov 2023 23:13:35 +0300 Subject: [PATCH 10/17] feat: rewrite download manager --- .../java/com/mezhendosina/sgo/Singleton.kt | 14 ++++ .../sgo/app/activities/LoginActivity.kt | 2 + .../attachments/AttachmentDownloadManager.kt | 80 +++++++++++++++++++ .../AttachmentDownloadManagerInterface.kt | 23 ++++++ .../attachments/AttachmentsRepository.kt | 1 + .../ui/journalFlow/answer/AnswerFragment.kt | 10 +++ .../ui/journalFlow/answer/AnswerViewModel.kt | 5 +- .../containerLesson/LessonContainer.kt | 8 +- .../journalFlow/lessonItem/LessonFragment.kt | 11 ++- .../journalFlow/lessonItem/LessonViewModel.kt | 25 +++++- .../ChangeControlQuestionViewModel.kt | 3 +- 11 files changed, 172 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManager.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManagerInterface.kt diff --git a/app/src/main/java/com/mezhendosina/sgo/Singleton.kt b/app/src/main/java/com/mezhendosina/sgo/Singleton.kt index 67b47034..ef251dd8 100644 --- a/app/src/main/java/com/mezhendosina/sgo/Singleton.kt +++ b/app/src/main/java/com/mezhendosina/sgo/Singleton.kt @@ -19,6 +19,7 @@ package com.mezhendosina.sgo import androidx.lifecycle.MutableLiveData import com.google.android.material.tabs.TabLayout +import com.mezhendosina.sgo.app.BuildConfig import com.mezhendosina.sgo.app.model.journal.DiaryStyle import com.mezhendosina.sgo.app.model.journal.entities.DiaryUiEntity import com.mezhendosina.sgo.app.model.journal.entities.LessonUiEntity @@ -31,6 +32,7 @@ import com.mezhendosina.sgo.data.netschool.api.announcements.AnnouncementsRespon import com.mezhendosina.sgo.data.netschool.api.diary.entities.PastMandatoryEntity import com.mezhendosina.sgo.data.netschool.api.grades.entities.GradesItem import com.mezhendosina.sgo.data.netschool.api.settings.entities.MySettingsResponseEntity +import okhttp3.internal.http2.Header object Singleton { var loggedIn = false @@ -67,5 +69,17 @@ object Singleton { val updateGradeState = MutableLiveData() val mainContainerScreen = MutableLiveData(ContainerFragment.JOURNAL) + + val STATIC_HEADERS = mutableListOf( + Header("UserAgent", "che-zadali-app v${BuildConfig.VERSION_NAME}"), + Header("X-Requested-With", "XMLHttpRequest"), + Header("Sec-Fetch-Site", "same-origin"), + Header("Sec-Fetch-Mode", "cors"), + Header("Sec-Fetch-Dest", "empty"), + Header( + "sec-ch-ua", + "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"105\", \"Microsoft Edge\";v=\"105\"" + ) + ) } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt b/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt index 4b275114..6e4c32ba 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/activities/LoginActivity.kt @@ -16,6 +16,7 @@ package com.mezhendosina.sgo.app.activities +import android.app.DownloadManager import android.os.Bundle import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity @@ -46,6 +47,7 @@ class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + onBackPressedDispatcher.addCallback(onBackPressedCallback) binding = ContainerLoginBinding.inflate(layoutInflater) setContentView(binding!!.root) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManager.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManager.kt new file mode 100644 index 00000000..96cb35b5 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManager.kt @@ -0,0 +1,80 @@ +package com.mezhendosina.sgo.app.model.attachments + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Environment +import androidx.core.content.FileProvider +import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import javax.inject.Inject + + +const val ANNOUNCEMENT = "announcement" +const val HOMEWORK = "homework" + +@Module +@InstallIn(SingletonComponent::class) +class AttachmentDownloadManager @Inject constructor( + private val attachmentsSource: AttachmentsSource +) : AttachmentDownloadManagerInterface { + override suspend fun downloadFile( + context: Context, + assignType: String, + assignId: Int, + fileUiEntity: FileUiEntity + ): String? { + val file = getFolder(context, assignType, assignId, fileUiEntity.fileName) + + withContext(Dispatchers.IO) { + file.createNewFile() + } + + return attachmentsSource.downloadAttachment(fileUiEntity.id!!, file) + } + + override fun editDescription(attachmentId: Int, description: String?) { + TODO("Not yet implemented") + } + + override fun openFile( + context: Context, + assignType: String, + assignId: Int, + attachmentName: String + ) { + val file = getFolder(context, assignType, assignId, attachmentName) + val intent = Intent( + Intent.ACTION_VIEW, + FileProvider.getUriForFile( + context, + context.applicationContext.packageName + ".provider", + file + )) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + context.startActivity(intent) + } + + override fun getFolder( + context: Context, + assignType: String, + assignId: Int, + attachmentName: String + ): File { + val downloads = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + downloads?.mkdirs() + val typesFolder = File(downloads, assignType) + typesFolder.mkdirs() + val idFolder = File(typesFolder, assignId.toString()) + idFolder.mkdirs() + return File(typesFolder, attachmentName) + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManagerInterface.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManagerInterface.kt new file mode 100644 index 00000000..0d4b246d --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManagerInterface.kt @@ -0,0 +1,23 @@ +package com.mezhendosina.sgo.app.model.attachments + +import android.accounts.AuthenticatorDescription +import android.app.DownloadManager.Request +import android.content.Context +import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import java.io.File + +interface AttachmentDownloadManagerInterface { + + suspend fun downloadFile( + context: Context, + assignType: String, + assignId: Int, + fileUiEntity: FileUiEntity + ): String? + + fun editDescription(attachmentId: Int, description: String?) + + fun openFile(context: Context, assignType: String, assignId: Int, attachmentName: String) + + fun getFolder(context: Context, assignType: String, assignId: Int, attachmentName: String): File +} diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsRepository.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsRepository.kt index 78e1a10b..da5e987c 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentsRepository.kt @@ -16,6 +16,7 @@ package com.mezhendosina.sgo.app.model.attachments +import android.app.DownloadManager import android.content.Context import android.content.Intent import android.net.Uri diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt index 8b5b8d0d..aecad486 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt @@ -16,7 +16,10 @@ package com.mezhendosina.sgo.app.ui.journalFlow.answer +import android.app.DownloadManager +import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.os.Bundle import android.view.View import androidx.activity.result.contract.ActivityResultContracts @@ -28,15 +31,20 @@ import com.google.android.material.transition.platform.MaterialSharedAxis import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentAnswerBinding import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import com.mezhendosina.sgo.app.model.attachments.AttachmentDownloadManager import com.mezhendosina.sgo.app.utils.getFileNameFromUri import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import javax.inject.Inject @AndroidEntryPoint class AnswerFragment : Fragment(R.layout.fragment_answer) { + @Inject + lateinit var attachmentDownloadManager: AttachmentDownloadManager + private var binding: FragmentAnswerBinding? = null internal val viewModel by viewModels() @@ -56,8 +64,10 @@ class AnswerFragment : Fragment(R.layout.fragment_answer) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) + adapter = AnswerFileAdapter( viewModel, object : FileActionListener { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt index 40a73b90..b978f2c4 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt @@ -20,6 +20,7 @@ import android.content.Context import androidx.lifecycle.ViewModel import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import com.mezhendosina.sgo.app.model.attachments.AttachmentDownloadManager import com.mezhendosina.sgo.app.model.attachments.AttachmentsRepository import com.mezhendosina.sgo.app.model.attachments.AttachmentsUtils import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton @@ -32,7 +33,7 @@ import javax.inject.Inject @HiltViewModel class AnswerViewModel @Inject constructor( private val lessonRepository: LessonRepository, - private val attachmentsRepository: AttachmentsRepository + private val attachmentDownloadManager: AttachmentDownloadManager, ) : ViewModel() { @@ -50,7 +51,7 @@ class AnswerViewModel @Inject constructor( else null suspend fun openFile(context: Context, fileUiEntity: FileUiEntity) { - TODO() + } fun addFile(fileUiEntity: FileUiEntity) { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt index 6e5a4a02..a8cb9c1d 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainer.kt @@ -19,6 +19,7 @@ package com.mezhendosina.sgo.app.ui.journalFlow.containerLesson import android.annotation.SuppressLint import android.os.Bundle import android.view.View +import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.NavController @@ -44,20 +45,23 @@ class LessonContainer : Fragment(R.layout.container_lesson) { private val viewModel by viewModels() + val r = registerForActivityResult(ActivityResultContracts.RequestPermission()) { + + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = MaterialContainerTransform() sharedElementReturnTransition = MaterialContainerTransform() } - @SuppressLint("RestrictedApi") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = ContainerLessonBinding.bind(view) val innerNavController = childFragmentManager.findFragmentById(binding!!.lessonFragmentContainer.id) ?.findNavController() - + r.launch(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) with(binding!!.lessonToolbar) { itemToolbar.title = viewModel.lesson?.subjectName ?: "" diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt index b399f438..6a1e59af 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt @@ -17,9 +17,11 @@ package com.mezhendosina.sgo.app.ui.journalFlow.lessonItem import android.Manifest +import android.app.DownloadManager import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import android.content.IntentFilter import android.content.pm.PackageManager import android.os.Bundle import android.view.View @@ -35,6 +37,7 @@ import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentItemLessonBinding import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import com.mezhendosina.sgo.app.model.attachments.AttachmentDownloadManager import com.mezhendosina.sgo.app.ui.journalFlow.answer.AnswerFragment import com.mezhendosina.sgo.app.utils.AttachmentAdapter import com.mezhendosina.sgo.app.utils.AttachmentClickListener @@ -42,6 +45,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import javax.inject.Inject @AndroidEntryPoint class LessonFragment : Fragment(R.layout.fragment_item_lesson) { @@ -49,17 +53,21 @@ class LessonFragment : Fragment(R.layout.fragment_item_lesson) { internal val viewModel: LessonViewModel by viewModels() private var binding: FragmentItemLessonBinding? = null + @Inject + lateinit var attachmentDownloadManager: AttachmentDownloadManager + private val storagePermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { } private val onAttachmentClickListener = object : AttachmentClickListener { override fun invoke(attachment: FileUiEntity, loadingList: MutableList) { + viewModel.downloadAttachment(requireContext(),attachment) + val permission = requireContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) if (permission == PackageManager.PERMISSION_GRANTED) { CoroutineScope(Dispatchers.Main).launch { - viewModel.downloadAttachment(attachment) } } else { storagePermission.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) @@ -76,6 +84,7 @@ class LessonFragment : Fragment(R.layout.fragment_item_lesson) { enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) + whyGradeAdapter = WhyGradeAdapter() attachmentAdapter = AttachmentAdapter(onAttachmentClickListener) answerFileAdapter = AttachmentAdapter(onAttachmentClickListener) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt index c691df91..1068763a 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt @@ -22,7 +22,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import com.mezhendosina.sgo.app.model.attachments.AttachmentDownloadManager import com.mezhendosina.sgo.app.model.attachments.AttachmentsRepository +import com.mezhendosina.sgo.app.model.attachments.HOMEWORK import com.mezhendosina.sgo.data.netschool.base.PermissionNotGranted import com.mezhendosina.sgo.data.netschool.base.toDescription import com.mezhendosina.sgo.app.uiEntities.AboutLessonUiEntity @@ -32,6 +34,7 @@ import com.mezhendosina.sgo.data.netschool.repo.LessonActionListener import com.mezhendosina.sgo.data.netschool.repo.LessonRepository import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -42,7 +45,7 @@ import javax.inject.Inject class LessonViewModel @Inject constructor( private val lessonRepository: LessonRepository, - private val attachmentsRepository: AttachmentsRepository, + private val attachmentDownloadManager: AttachmentDownloadManager, private val settingsDataStore: SettingsDataStore ) : ViewModel() { @@ -79,13 +82,27 @@ class LessonViewModel } } - suspend fun downloadAttachment( + fun downloadAttachment( + context: Context, attachment: FileUiEntity, ) { try { - withContext(Dispatchers.IO) { - TODO() + CoroutineScope(Dispatchers.IO).launch { + attachmentDownloadManager.downloadFile( + context, + HOMEWORK, + lesson.value!!.id, + attachment + ) + withContext(Dispatchers.Main) { + attachmentDownloadManager.openFile( + context, HOMEWORK, + lesson.value!!.id, + attachment.fileName + ) + } } + } catch (e: Exception) { if (e is PermissionNotGranted) { throw PermissionNotGranted() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionViewModel.kt index afbfb821..a7a263ea 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/settingsFlow/changeControlQuestion/ChangeControlQuestionViewModel.kt @@ -20,9 +20,10 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject @HiltViewModel -class ChangeControlQuestionViewModel : ViewModel() { +class ChangeControlQuestionViewModel @Inject constructor() : ViewModel() { private val _questions = MutableLiveData>() val questions: LiveData> = _questions From 00141e8d92139192109989cb7b99f6eb449e3fd9 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Tue, 28 Nov 2023 23:15:23 +0300 Subject: [PATCH 11/17] fix: add App file --- app/src/main/java/com/mezhendosina/sgo/app/App.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 app/src/main/java/com/mezhendosina/sgo/app/App.kt diff --git a/app/src/main/java/com/mezhendosina/sgo/app/App.kt b/app/src/main/java/com/mezhendosina/sgo/app/App.kt new file mode 100644 index 00000000..c1f93755 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/app/App.kt @@ -0,0 +1,8 @@ +package com.mezhendosina.sgo.app + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + + +@HiltAndroidApp +class App : Application() \ No newline at end of file From bf1f9e3dd78354558f6322bce0c65c724e1f80a1 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Wed, 29 Nov 2023 14:39:38 +0300 Subject: [PATCH 12/17] feat: refactoring attachment downloading --- app/src/main/AndroidManifest.xml | 12 +-- .../sgo/app/model/answer/AnswerRepository.kt | 47 +++++------ .../sgo/app/model/answer/AnswerUiEntity.kt | 6 +- .../attachments/AttachmentDownloadManager.kt | 61 ++++++++++---- .../AttachmentDownloadManagerInterface.kt | 14 ++-- .../AnnouncementsItemFragment.kt | 3 +- .../journalFlow/answer/AnswerFileAdapter.kt | 2 +- .../ui/journalFlow/answer/AnswerFragment.kt | 43 +++++----- .../ui/journalFlow/answer/AnswerViewModel.kt | 81 ++++++++++++++----- .../answer/AnswerViewModelInterface.kt | 17 ++++ .../LessonContainerViewModel.kt | 17 ++-- .../journalFlow/lessonItem/LessonFragment.kt | 64 ++++++++------- .../journalFlow/lessonItem/LessonViewModel.kt | 10 +-- .../ui/main/container/ContainerFragment.kt | 42 +++++----- .../mezhendosina/sgo/app/utils/Exceptions.kt | 1 + .../sgo/data/SettingsDataStore.kt | 2 + .../sgo/data/netschool/NetSchoolSingleton.kt | 19 ----- .../entities/AttachmentsResponseEntity.kt | 4 +- .../entities/GetAnswerResponseEntity.kt | 8 +- .../data/netschool/repo/LessonRepository.kt | 41 +++++++--- .../repo/LessonRepositoryInterface.kt | 32 ++++++++ .../com/mezhendosina/sgo/di/RepoModule.kt | 18 +++++ app/src/main/res/layout/activity_splash.xml | 6 +- app/src/main/res/layout/fragment_answer.xml | 2 + .../main/res/navigation/tabs_navigaiton.xml | 2 +- 25 files changed, 352 insertions(+), 202 deletions(-) create mode 100644 app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModelInterface.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepositoryInterface.kt create mode 100644 app/src/main/java/com/mezhendosina/sgo/di/RepoModule.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d55a9515..1221c232 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,12 +19,12 @@ - - + + + + + + , - assignId: Int - ): List { - lessonRepository.getLesson()?.answerFiles?.forEach { - if (!files.contains(it) && it.id != null) { - attachmentsSource.deleteAttachment(assignId, it.id) - } - } - val out = files.map { - if (it.file != null) { - val fileId = sendFile(context, assignId, it.file, it.description) - it.addId(fileId) - } else { - it - } - } - return out - } - +// +// suspend fun sendFiles( +// context: Context, +// files: List, +// assignId: Int +// ): List { +// lessonRepository.getLesson()?.answerFiles?.forEach { +// if (!files.contains(it) && it.id != null) { +// attachmentsSource.deleteAttachment(assignId, it.id) +// } +// } +// val out = files.map { +// if (it.file != null) { +// val fileId = sendFile(context, assignId, it.file, it.description) +// it.addId(fileId) +// } else { +// it +// } +// } +// return out +// } +// private suspend fun sendFile( context: Context, diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/answer/AnswerUiEntity.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/answer/AnswerUiEntity.kt index a47426d3..e8847f33 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/answer/AnswerUiEntity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/answer/AnswerUiEntity.kt @@ -25,9 +25,11 @@ data class AnswerUiEntity( data class FileUiEntity( val id: Int?, + val assignType: String, + val assignId: Int, val fileName: String, val description: String?, - val file: Uri? = null ) { - fun addId(id: Int?): FileUiEntity = FileUiEntity(id, fileName, description, file) + fun addId(id: Int?): FileUiEntity = + FileUiEntity(id, assignType, assignId, fileName, description) } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManager.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManager.kt index 96cb35b5..ffdc98f8 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManager.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManager.kt @@ -7,30 +7,34 @@ import android.os.Environment import androidx.core.content.FileProvider import com.mezhendosina.sgo.app.model.answer.FileUiEntity import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource +import com.mezhendosina.sgo.data.netschool.api.attachments.entities.SendFileRequestEntity import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.toRequestBody import java.io.File import javax.inject.Inject const val ANNOUNCEMENT = "announcement" const val HOMEWORK = "homework" +const val ANSWERS = "answers" @Module @InstallIn(SingletonComponent::class) class AttachmentDownloadManager @Inject constructor( private val attachmentsSource: AttachmentsSource ) : AttachmentDownloadManagerInterface { - override suspend fun downloadFile( - context: Context, - assignType: String, - assignId: Int, - fileUiEntity: FileUiEntity - ): String? { - val file = getFolder(context, assignType, assignId, fileUiEntity.fileName) + + override suspend fun downloadFile(context: Context, fileUiEntity: FileUiEntity): String? { + val file = + getFile(context, fileUiEntity.assignType, fileUiEntity.assignId, fileUiEntity.fileName) withContext(Dispatchers.IO) { file.createNewFile() @@ -39,29 +43,53 @@ class AttachmentDownloadManager @Inject constructor( return attachmentsSource.downloadAttachment(fileUiEntity.id!!, file) } + override suspend fun uploadFiles(context: Context, files: List) { + files.forEach { + if (it.id == null) { + val file = getFile(context, it.assignType, it.assignId, it.fileName) + val inputStream = context.contentResolver.openInputStream(Uri.fromFile(file)) + val body = inputStream?.readBytes()?.toRequestBody("*/*".toMediaTypeOrNull()) + if (body != null) { + val part = MultipartBody.Part.createFormData("file", it.fileName, body) + val data = SendFileRequestEntity( + true, + it.assignId, + it.description, + it.fileName + ) + withContext(Dispatchers.IO) { + attachmentsSource.sendFileAttachment(part, data) + inputStream.close() + } + } + } + } + } + override fun editDescription(attachmentId: Int, description: String?) { TODO("Not yet implemented") } - override fun openFile( - context: Context, - assignType: String, - assignId: Int, - attachmentName: String - ) { - val file = getFolder(context, assignType, assignId, attachmentName) + override fun openFile(context: Context, fileUiEntity: FileUiEntity) { + val file = getFile( + context, + fileUiEntity.assignType, + fileUiEntity.assignId, + fileUiEntity.fileName + ) val intent = Intent( Intent.ACTION_VIEW, FileProvider.getUriForFile( context, context.applicationContext.packageName + ".provider", file - )) + ) + ) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) context.startActivity(intent) } - override fun getFolder( + override fun getFile( context: Context, assignType: String, assignId: Int, @@ -74,7 +102,6 @@ class AttachmentDownloadManager @Inject constructor( val idFolder = File(typesFolder, assignId.toString()) idFolder.mkdirs() return File(typesFolder, attachmentName) - } } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManagerInterface.kt b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManagerInterface.kt index 0d4b246d..24239e9f 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManagerInterface.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/model/attachments/AttachmentDownloadManagerInterface.kt @@ -1,7 +1,5 @@ package com.mezhendosina.sgo.app.model.attachments -import android.accounts.AuthenticatorDescription -import android.app.DownloadManager.Request import android.content.Context import com.mezhendosina.sgo.app.model.answer.FileUiEntity import java.io.File @@ -10,14 +8,18 @@ interface AttachmentDownloadManagerInterface { suspend fun downloadFile( context: Context, - assignType: String, - assignId: Int, fileUiEntity: FileUiEntity ): String? + suspend fun uploadFiles( + context: Context, + files: List + ) + + fun editDescription(attachmentId: Int, description: String?) - fun openFile(context: Context, assignType: String, assignId: Int, attachmentName: String) + fun openFile(context: Context, fileUiEntity: FileUiEntity) - fun getFolder(context: Context, assignType: String, assignId: Int, attachmentName: String): File + fun getFile(context: Context, assignType: String, assignId: Int, attachmentName: String): File } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsItemFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsItemFragment.kt index 2299bae8..761ef379 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsItemFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/announcementsFlow/announcementsItem/AnnouncementsItemFragment.kt @@ -31,6 +31,7 @@ import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentAnnouncementItemBinding import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import com.mezhendosina.sgo.app.model.attachments.ANNOUNCEMENT import com.mezhendosina.sgo.app.utils.AttachmentAdapter import com.mezhendosina.sgo.app.utils.AttachmentClickListener import com.mezhendosina.sgo.data.DateManipulation @@ -106,7 +107,7 @@ class AnnouncementsItemFragment : Fragment(R.layout.fragment_announcement_item) } ) attachmentAdapter.attachments = - announcement.attachments.map { it.toUiEntity() } + announcement.attachments.map { it.toUiEntity(ANNOUNCEMENT, announcement.id) } binding.attachmentsList.attachmentsListRecyclerView.adapter = attachmentAdapter binding.attachmentsList.attachmentsListRecyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFileAdapter.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFileAdapter.kt index d7b9b083..39b640a2 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFileAdapter.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFileAdapter.kt @@ -41,7 +41,7 @@ class AnswerFileAdapter( private val fileActionListener: FileActionListener ) : RecyclerView.Adapter(), View.OnClickListener { - var files: List = viewModel.getAnswerFiles() + var files: List = emptyList() set(value) { field = value notifyDataSetChanged() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt index aecad486..a41755e5 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerFragment.kt @@ -32,6 +32,7 @@ import com.mezhendosina.sgo.app.R import com.mezhendosina.sgo.app.databinding.FragmentAnswerBinding import com.mezhendosina.sgo.app.model.answer.FileUiEntity import com.mezhendosina.sgo.app.model.attachments.AttachmentDownloadManager +import com.mezhendosina.sgo.app.model.attachments.HOMEWORK import com.mezhendosina.sgo.app.utils.getFileNameFromUri import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope @@ -42,25 +43,21 @@ import javax.inject.Inject @AndroidEntryPoint class AnswerFragment : Fragment(R.layout.fragment_answer) { - @Inject - lateinit var attachmentDownloadManager: AttachmentDownloadManager - private var binding: FragmentAnswerBinding? = null internal val viewModel by viewModels() private var adapter: AnswerFileAdapter? = null - private val selectFileLauncher = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - val path = it.data?.data - if (path?.path != null) { - val fileName = getFileNameFromUri(requireContext(), path) - val fileEntity = FileUiEntity(null, fileName ?: "", null, path) - viewModel.addFile(fileEntity) - adapter!!.files = adapter!!.files.plus(fileEntity) - } - } +// private val selectFileLauncher = +// registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { +// val path = it.data?.data +// if (path?.path != null) { +// val fileName = getFileNameFromUri(requireContext(), path) +// viewModel.addFile() +// adapter!!.files = adapter!!.files.plus(fileEntity) +// } +// } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -99,22 +96,21 @@ class AnswerFragment : Fragment(R.layout.fragment_answer) { super.onViewCreated(view, savedInstanceState) binding = FragmentAnswerBinding.bind(view) - binding!!.answerEditText.setText(viewModel.getAnswerText()) + binding!!.answerEditText.setText(viewModel.getAnswer()) hideUnusedElementsInHomework() setupHomework() setupAttachments() - addOnAttachFileClickListener() setupOnEditAnswer() } - private fun addOnAttachFileClickListener() { - binding!!.attachFile.setOnClickListener { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) - intent.type = "*/*" - selectFileLauncher.launch(intent) - } - } +// private fun addOnAttachFileClickListener() { +// binding!!.attachFile.setOnClickListener { +// val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) +// intent.type = "*/*" +// selectFileLauncher.launch(intent) +// } +// } private fun setupAttachments() { @@ -130,7 +126,8 @@ class AnswerFragment : Fragment(R.layout.fragment_answer) { private fun setupOnEditAnswer() { binding!!.answer.editText?.addTextChangedListener { - viewModel.editTextAnswer(it.toString()) + viewModel.editAnswerText(it.toString()) + } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt index b978f2c4..702a25d6 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModel.kt @@ -17,52 +17,91 @@ package com.mezhendosina.sgo.app.ui.journalFlow.answer import android.content.Context +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.mezhendosina.sgo.Singleton +import androidx.lifecycle.viewModelScope import com.mezhendosina.sgo.app.model.answer.FileUiEntity import com.mezhendosina.sgo.app.model.attachments.AttachmentDownloadManager -import com.mezhendosina.sgo.app.model.attachments.AttachmentsRepository -import com.mezhendosina.sgo.app.model.attachments.AttachmentsUtils -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import com.mezhendosina.sgo.app.utils.LessonNotFoundException +import com.mezhendosina.sgo.app.utils.toLiveData +import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.netschool.repo.LessonRepository +import com.mezhendosina.sgo.data.netschool.repo.LessonRepositoryInterface import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel class AnswerViewModel @Inject constructor( - private val lessonRepository: LessonRepository, + private val lessonRepository: LessonRepositoryInterface, private val attachmentDownloadManager: AttachmentDownloadManager, -) : ViewModel() { + private val settingsDataStore: SettingsDataStore +) : ViewModel(), AnswerViewModelInterface { + private val _files = MutableLiveData>(mutableListOf()) + val files = _files.toLiveData() + override fun getHomework(): String { + val lesson = lessonRepository.getLesson() ?: throw LessonNotFoundException() + return lesson.homework + } + + + override fun getAnswer(): String? { + val lesson = lessonRepository.getLesson() ?: throw LessonNotFoundException() + _files.value = lesson.answerFiles?.toMutableList() + return lesson.answerText + } - fun getAnswerText(): String { - return lessonRepository.answerText + fun editAnswerText(answerText: String) { + lessonRepository.editAnswerText(answerText) } - fun getAnswerFiles(): List { - return lessonRepository.answerFiles + override suspend fun sendAnswer(answerText: String?) { + } - fun getHomework() = - if (Singleton.lesson?.homework != null) Singleton.lesson!!.homework!!.assignmentName - else if (Singleton.pastMandatoryItem != null) Singleton.pastMandatoryItem!!.assignmentName - else null + override suspend fun uploadFiles(context: Context) { + if (_files.value != null) attachmentDownloadManager.uploadFiles( + context, + _files.value!! + ) + } - suspend fun openFile(context: Context, fileUiEntity: FileUiEntity) { + override suspend fun downloadFiles(context: Context) { + viewModelScope.launch { + if (settingsDataStore.getValue(SettingsDataStore.DOWNLOAD_ALL_FILES).first() == true) { + _files.value?.forEach { + withContext(Dispatchers.IO) { + downloadFile(context, it) + } + } + } + } + } + override suspend fun downloadFile(context: Context, fileUiEntity: FileUiEntity) { + attachmentDownloadManager.downloadFile(context, fileUiEntity) } - fun addFile(fileUiEntity: FileUiEntity) { - lessonRepository.answerFiles = lessonRepository.answerFiles.plus(fileUiEntity) + override fun openFile(context: Context, fileUiEntity: FileUiEntity) { + attachmentDownloadManager.openFile(context, fileUiEntity) } - fun deleteFile(fileUiEntity: FileUiEntity) { - lessonRepository.answerFiles = lessonRepository.answerFiles.minus(fileUiEntity) + override fun addFile(fileUiEntity: FileUiEntity) { + _files.value?.add(fileUiEntity) } - fun editTextAnswer(text: String) { - lessonRepository.answerText = text + override fun deleteFile(fileUiEntity: FileUiEntity) { + if (fileUiEntity.id == null) { + _files.value?.remove(fileUiEntity) + } else { + TODO() + } } + } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModelInterface.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModelInterface.kt new file mode 100644 index 00000000..f665af03 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/answer/AnswerViewModelInterface.kt @@ -0,0 +1,17 @@ +package com.mezhendosina.sgo.app.ui.journalFlow.answer + +import android.content.Context +import com.mezhendosina.sgo.app.model.answer.FileUiEntity + +interface AnswerViewModelInterface { + + fun getHomework(): String + fun getAnswer(): String? + suspend fun sendAnswer(answerText: String?) + suspend fun uploadFiles(context: Context) + suspend fun downloadFiles(context: Context) + suspend fun downloadFile(context: Context, fileUiEntity: FileUiEntity) + fun openFile(context: Context, fileUiEntity: FileUiEntity) + fun addFile(fileUiEntity: FileUiEntity) + fun deleteFile(fileUiEntity: FileUiEntity) +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainerViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainerViewModel.kt index 445c9e89..b6e5e54f 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainerViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/containerLesson/LessonContainerViewModel.kt @@ -26,6 +26,7 @@ import com.mezhendosina.sgo.data.netschool.base.toDescription import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.LessonRepository +import com.mezhendosina.sgo.data.netschool.repo.LessonRepositoryInterface import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first @@ -35,7 +36,7 @@ import javax.inject.Inject @HiltViewModel class LessonContainerViewModel @Inject constructor( private val answerRepository: AnswerRepository, - private val lessonRepository: LessonRepository, + private val lessonRepository: LessonRepositoryInterface, private val settingsDataStore: SettingsDataStore ) : ViewModel() { @@ -53,16 +54,16 @@ class LessonContainerViewModel @Inject constructor( val assignId = lesson?.homework?.id ?: -1 answerRepository.sendTextAnswer( assignId, - lessonRepository.answerText, + lessonRepository.getAnswerText(), currentUserId ) - val files = answerRepository.sendFiles( - context, - lessonRepository.answerFiles, - lesson?.homework?.id ?: -1 - ) // TODO null exception handler +// val files = answerRepository.sendFiles( +// context, +// lessonRepository.answerFiles, +// lesson?.homework?.id ?: -1 +// ) // TODO null exception handler withContext(Dispatchers.Main) { - lessonRepository.editAnswers(files) +// lessonRepository.editAnswers(files) } withContext(Dispatchers.Main) { Toast.makeText( diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt index 6a1e59af..564057a7 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonFragment.kt @@ -136,42 +136,48 @@ class LessonFragment : Fragment(R.layout.fragment_item_lesson) { if (lesson != null) with(binding!!) { homework.homeworkBody.text = lesson.homework - if (!lesson.homeworkComment.isNullOrEmpty()) { - homework.commentBody.text = lesson.homeworkComment - homework.commentBody.visibility = View.VISIBLE - homework.commentHeader.visibility = View.VISIBLE - } else { - homework.commentBody.visibility = View.GONE - homework.commentHeader.visibility = View.GONE - } - - if (!lesson.attachments.isNullOrEmpty()) { - homework.attachmentsList.root.visibility = View.VISIBLE - attachmentAdapter?.attachments = lesson.attachments - } else { - homework.attachmentsList.root.visibility = View.GONE - } - if (lesson.answerText.isNullOrBlank() && lesson.answerFiles.isNullOrEmpty()) { - sendHomework.root.visibility = View.GONE - homework.addAnswerButton.visibility = View.VISIBLE - } else { - sendHomework.root.visibility = View.VISIBLE - homework.addAnswerButton.visibility = View.GONE + if (lesson.homework.isEmpty()){ + homework.root.visibility = View.GONE + } else{ + if (!lesson.homeworkComment.isNullOrEmpty()) { + homework.commentBody.text = lesson.homeworkComment + homework.commentBody.visibility = View.VISIBLE + homework.commentHeader.visibility = View.VISIBLE + } else { + homework.commentBody.visibility = View.GONE + homework.commentHeader.visibility = View.GONE + } - if (lesson.answerFiles.isNullOrEmpty()) { - sendHomework.sendAttachmentList.visibility = View.GONE + if (!lesson.attachments.isNullOrEmpty()) { + homework.attachmentsList.root.visibility = View.VISIBLE + attachmentAdapter?.attachments = lesson.attachments } else { - sendHomework.sendAttachmentList.visibility = View.VISIBLE - answerFileAdapter?.attachments = lesson.answerFiles + homework.attachmentsList.root.visibility = View.GONE } - if (lesson.answerText.isNullOrEmpty()) { - sendHomework.answerText.visibility = View.GONE + if (lesson.answerText.isNullOrBlank() && lesson.answerFiles.isNullOrEmpty()) { + sendHomework.root.visibility = View.GONE + homework.addAnswerButton.visibility = View.VISIBLE } else { - sendHomework.answerText.visibility = View.VISIBLE - sendHomework.answerText.text = lesson.answerText + sendHomework.root.visibility = View.VISIBLE + homework.addAnswerButton.visibility = View.GONE + + if (lesson.answerFiles.isNullOrEmpty()) { + sendHomework.sendAttachmentList.visibility = View.GONE + } else { + sendHomework.sendAttachmentList.visibility = View.VISIBLE + answerFileAdapter?.attachments = lesson.answerFiles + } + if (lesson.answerText.isNullOrEmpty()) { + sendHomework.answerText.visibility = View.GONE + } else { + sendHomework.answerText.visibility = View.VISIBLE + sendHomework.answerText.text = lesson.answerText + } } } + + if (!lesson.whyGradeEntity.isNullOrEmpty()) { itemWhyGrade.root.visibility = View.VISIBLE whyGradeAdapter?.grades = lesson.whyGradeEntity diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt index 1068763a..ec7b41eb 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/journalFlow/lessonItem/LessonViewModel.kt @@ -32,6 +32,7 @@ import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.netschool.repo.LessonActionListener import com.mezhendosina.sgo.data.netschool.repo.LessonRepository +import com.mezhendosina.sgo.data.netschool.repo.LessonRepositoryInterface import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope @@ -44,7 +45,7 @@ import javax.inject.Inject @HiltViewModel class LessonViewModel @Inject constructor( - private val lessonRepository: LessonRepository, + private val lessonRepository: LessonRepositoryInterface, private val attachmentDownloadManager: AttachmentDownloadManager, private val settingsDataStore: SettingsDataStore ) : ViewModel() { @@ -90,15 +91,12 @@ class LessonViewModel CoroutineScope(Dispatchers.IO).launch { attachmentDownloadManager.downloadFile( context, - HOMEWORK, - lesson.value!!.id, attachment ) withContext(Dispatchers.Main) { attachmentDownloadManager.openFile( - context, HOMEWORK, - lesson.value!!.id, - attachment.fileName + context, + attachment ) } } diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt index b2efe4bc..784f5031 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/main/container/ContainerFragment.kt @@ -185,26 +185,26 @@ class ContainerFragment } private fun observeShowEngageDialog() { - containerViewModel.showEngageDialog.observe(viewLifecycleOwner) { - if (it) { - MaterialAlertDialogBuilder(requireContext()) - .setTitle("Понравилось приложение?") - .setMessage("Если да, то поделись им с одноклассниками, чтобы они тоже могли воспользоваться самым удобным дневником!") - .setPositiveButton("Поделиться") { dialog, _ -> - val sendIntent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, "https://sgoapp.ru") - type = "text/plain" - } - val shareIntent = - Intent.createChooser(sendIntent, "Поделиться ссылкой на SGO app") - startActivity(shareIntent) - dialog.dismiss() - }.setNegativeButton("Нет") { dialog, _ -> - dialog.cancel() - }.show() - } - } +// containerViewModel.showEngageDialog.observe(viewLifecycleOwner) { +// if (it) { +// MaterialAlertDialogBuilder(requireContext()) +// .setTitle("Понравилось приложение?") +// .setMessage("Если да, то поделись им с одноклассниками, чтобы они тоже могли воспользоваться самым удобным дневником!") +// .setPositiveButton("Поделиться") { dialog, _ -> +// val sendIntent = Intent().apply { +// action = Intent.ACTION_SEND +// putExtra(Intent.EXTRA_TEXT, "https://sgoapp.ru") +// type = "text/plain" +// } +// val shareIntent = +// Intent.createChooser(sendIntent, "Поделиться ссылкой на SGO app") +// startActivity(shareIntent) +// dialog.dismiss() +// }.setNegativeButton("Нет") { dialog, _ -> +// dialog.cancel() +// }.show() +// } +// } } override fun observeUserId() { @@ -270,7 +270,7 @@ class ContainerFragment Singleton.mainContainerScreen.observe(viewLifecycleOwner) { when (it) { JOURNAL -> { - binding.mainToolbar.setTitle(R.string.journal) + binding.mainToolbar.setTitle(R.string.che_zadali) binding.slideDownAnimation() binding.grades.root.visibility = View.GONE binding.journal.visibility = View.VISIBLE diff --git a/app/src/main/java/com/mezhendosina/sgo/app/utils/Exceptions.kt b/app/src/main/java/com/mezhendosina/sgo/app/utils/Exceptions.kt index 5db98adc..027eff4b 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/utils/Exceptions.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/utils/Exceptions.kt @@ -39,6 +39,7 @@ class ParseBackendResponseException( cause: Throwable ) : AppException(cause = cause) +class LessonNotFoundException() : NullPointerException() fun Exception.toDescription(): String { println(this.stackTraceToString()) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt b/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt index 9d063e2e..068fb076 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/SettingsDataStore.kt @@ -49,6 +49,8 @@ class SettingsDataStore @Inject constructor(@ApplicationContext private val cont val DIARY_STYLE = stringPreferencesKey("diary_style") val SKIP_SUNDAY = booleanPreferencesKey("skip_sunday") + + val DOWNLOAD_ALL_FILES = booleanPreferencesKey("download_all_files") } override fun getValue(value: Preferences.Key): Flow { diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/NetSchoolSingleton.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/NetSchoolSingleton.kt index 4e0a5b33..4710e08b 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/NetSchoolSingleton.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/NetSchoolSingleton.kt @@ -17,25 +17,6 @@ package com.mezhendosina.sgo.data.netschool import androidx.lifecycle.MutableLiveData -import com.mezhendosina.sgo.app.model.ContainerRepository -import com.mezhendosina.sgo.app.model.announcements.AnnouncementsRepository -import com.mezhendosina.sgo.app.model.announcements.AnnouncementsSource -import com.mezhendosina.sgo.app.model.answer.AnswerRepository -import com.mezhendosina.sgo.app.model.attachments.AttachmentsRepository -import com.mezhendosina.sgo.app.model.grades.GradesRepository -import com.mezhendosina.sgo.app.model.grades.GradesSource -import com.mezhendosina.sgo.app.model.journal.DiarySource -import com.mezhendosina.sgo.app.model.journal.JournalRepository -import com.mezhendosina.sgo.app.uiEntities.AssignTypeUiEntity -import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity -import com.mezhendosina.sgo.data.netschool.api.attachments.AttachmentsSource -import com.mezhendosina.sgo.data.netschool.api.homework.HomeworkSource -import com.mezhendosina.sgo.data.netschool.api.login.LoginSource -import com.mezhendosina.sgo.data.netschool.api.settings.SettingsSource -import com.mezhendosina.sgo.data.netschool.repo.LessonRepository -import com.mezhendosina.sgo.data.netschool.repo.LoginRepository -import com.mezhendosina.sgo.data.netschool.repo.RegionsRepository -import com.mezhendosina.sgo.data.netschool.repo.SettingsRepository import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/attachments/entities/AttachmentsResponseEntity.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/attachments/entities/AttachmentsResponseEntity.kt index 900b5949..ebfc95e0 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/attachments/entities/AttachmentsResponseEntity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/attachments/entities/AttachmentsResponseEntity.kt @@ -40,8 +40,10 @@ data class Attachment( @SerializedName("originalFileName") val originalFileName: String ) { - fun toUiEntity(): FileUiEntity = FileUiEntity( + fun toUiEntity(assignType: String, assignmentId: Int): FileUiEntity = FileUiEntity( id, + assignType, + assignmentId, originalFileName, description ) diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/homework/entities/GetAnswerResponseEntity.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/homework/entities/GetAnswerResponseEntity.kt index 012d8f77..72478424 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/homework/entities/GetAnswerResponseEntity.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/api/homework/entities/GetAnswerResponseEntity.kt @@ -26,9 +26,9 @@ data class GetAnswerResponseEntity( val studentId: Int, val text: TextAnswer? ) { - fun toUiEntity() = AnswerUiEntity( + fun toUiEntity(assignType: String, assignId: Int) = AnswerUiEntity( text?.answer, - files.map { it.toUiEntity() } + files.map { it.toUiEntity(assignType, assignId) } ) } @@ -41,6 +41,8 @@ data class FileEntity( val saved: Int, val userId: Int? ) { - fun toUiEntity() = FileUiEntity(id, fileName, description) + fun toUiEntity(assignType: String, assignId: Int) = + FileUiEntity(id, assignType, assignId, fileName, description) + fun toAttachmentEntity() = Attachment(description, id, fileName, fileName) } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepository.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepository.kt index 037ce247..2e23b48f 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepository.kt @@ -17,6 +17,8 @@ package com.mezhendosina.sgo.data.netschool.repo import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import com.mezhendosina.sgo.app.model.attachments.ANSWERS +import com.mezhendosina.sgo.app.model.attachments.HOMEWORK import com.mezhendosina.sgo.app.model.journal.entities.LessonUiEntity import com.mezhendosina.sgo.app.uiEntities.AboutLessonUiEntity import com.mezhendosina.sgo.app.uiEntities.AssignTypeUiEntity @@ -34,12 +36,10 @@ import javax.inject.Inject typealias LessonActionListener = (lesson: AboutLessonUiEntity?) -> Unit -@Module -@InstallIn(SingletonComponent::class) class LessonRepository @Inject constructor( private val homeworkSource: HomeworkSource, private val attachmentsSource: AttachmentsSource, -) { +) : LessonRepositoryInterface { private var assignTypes: List? = null @@ -47,11 +47,20 @@ class LessonRepository @Inject constructor( private val listeners = mutableSetOf() var answerFiles: List = emptyList() - var answerText: String = "" + private var answerText: String = "" + override fun getAnswerText(): String = answerText - fun getLesson(): AboutLessonUiEntity? = lesson + override fun editAnswerText(text: String) { + answerText = text + } + + + override fun getLesson(): AboutLessonUiEntity? { + return lesson + + } - suspend fun getAboutLesson( + override suspend fun getAboutLesson( lessonUiEntity: LessonUiEntity, studentId: Int ) { @@ -60,8 +69,18 @@ class LessonRepository @Inject constructor( AttachmentsRequestEntity(lessonUiEntity.assignments?.map { it.id } ?: emptyList()) ) val answerFilesResponse = - attachmentsR.firstOrNull()?.answerFiles?.map { it.attachment.toUiEntity() } - val attachments = attachmentsR.firstOrNull()?.attachments?.map { it.toUiEntity() } + attachmentsR.firstOrNull()?.answerFiles?.map { + it.attachment.toUiEntity( + ANSWERS, + lessonUiEntity.classmeetingId + ) + } + val attachments = attachmentsR.firstOrNull()?.attachments?.map { + it.toUiEntity( + HOMEWORK, + lessonUiEntity.classmeetingId + ) + } val aboutAssign = lessonUiEntity.homework?.id?.let { homeworkSource.getAboutAssign(it, studentId) } val answerTextResponse = @@ -87,7 +106,7 @@ class LessonRepository @Inject constructor( } } - fun editAnswers(files: List?) { + override fun editAnswers(files: List?) { lesson = lesson?.editAnswers(answerText, files) notifyListeners() } @@ -112,11 +131,11 @@ class LessonRepository @Inject constructor( } - fun addListener(listener: LessonActionListener) { + override fun addListener(listener: LessonActionListener) { listeners.add(listener) } - fun removeListener(listener: LessonActionListener) { + override fun removeListener(listener: LessonActionListener) { listeners.remove(listener) } diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepositoryInterface.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepositoryInterface.kt new file mode 100644 index 00000000..97e48fb9 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LessonRepositoryInterface.kt @@ -0,0 +1,32 @@ +package com.mezhendosina.sgo.data.netschool.repo + +import com.mezhendosina.sgo.app.model.answer.FileUiEntity +import com.mezhendosina.sgo.app.model.attachments.ANSWERS +import com.mezhendosina.sgo.app.model.attachments.HOMEWORK +import com.mezhendosina.sgo.app.model.journal.entities.LessonUiEntity +import com.mezhendosina.sgo.app.uiEntities.AboutLessonUiEntity +import com.mezhendosina.sgo.app.uiEntities.WhyGradeEntity +import com.mezhendosina.sgo.data.netschool.api.attachments.entities.AttachmentsRequestEntity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +interface LessonRepositoryInterface { + + fun getAnswerText(): String + fun editAnswerText(text: String) + + fun getLesson(): AboutLessonUiEntity? + + suspend fun getAboutLesson( + lessonUiEntity: LessonUiEntity, + studentId: Int + ) + + fun editAnswers(files: List?) + + + fun addListener(listener: LessonActionListener) + + fun removeListener(listener: LessonActionListener) + +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/RepoModule.kt b/app/src/main/java/com/mezhendosina/sgo/di/RepoModule.kt new file mode 100644 index 00000000..dbdeda15 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/di/RepoModule.kt @@ -0,0 +1,18 @@ +package com.mezhendosina.sgo.di + +import com.mezhendosina.sgo.data.netschool.repo.LessonRepository +import com.mezhendosina.sgo.data.netschool.repo.LessonRepositoryInterface +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class RepoModule { + + @Binds + @Singleton + abstract fun bindLessonRepository(lessonRepository: LessonRepository): LessonRepositoryInterface +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml index 80ee9464..d3ac3fcc 100644 --- a/app/src/main/res/layout/activity_splash.xml +++ b/app/src/main/res/layout/activity_splash.xml @@ -22,10 +22,10 @@ diff --git a/app/src/main/res/navigation/tabs_navigaiton.xml b/app/src/main/res/navigation/tabs_navigaiton.xml index 6f7067ad..92ee0772 100644 --- a/app/src/main/res/navigation/tabs_navigaiton.xml +++ b/app/src/main/res/navigation/tabs_navigaiton.xml @@ -23,7 +23,7 @@ Date: Wed, 29 Nov 2023 17:30:10 +0300 Subject: [PATCH 13/17] fix: debug --- .../sgo/app/activities/MainViewModel.kt | 3 +- .../chooseSchool/ChooseSchoolViewModel.kt | 9 ++- .../loginFlow/gosuslugi/GosuslugiViewModel.kt | 3 +- .../GosuslugiResultViewModel.kt | 3 +- .../app/ui/loginFlow/login/LoginFragment.kt | 8 ++- .../app/ui/loginFlow/login/LoginViewModel.kt | 15 +++-- .../data/netschool/repo/LoginRepository.kt | 56 +++++++++---------- .../repo/LoginRepositoryInterface.kt | 39 +++++++++++++ .../com/mezhendosina/sgo/di/RepoModule.kt | 6 ++ 9 files changed, 97 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepositoryInterface.kt diff --git a/app/src/main/java/com/mezhendosina/sgo/app/activities/MainViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/activities/MainViewModel.kt index ca3cf3ba..39fb300f 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/activities/MainViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/activities/MainViewModel.kt @@ -24,6 +24,7 @@ import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.data.SettingsDataStore import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import com.mezhendosina.sgo.data.netschool.repo.LoginRepositoryInterface import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope @@ -37,7 +38,7 @@ import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( - private val loginRepository: LoginRepository, + private val loginRepository: LoginRepositoryInterface, private val settingsDataStore: SettingsDataStore ) : ViewModel() { diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolViewModel.kt index c49d021e..680ceff3 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/chooseSchool/ChooseSchoolViewModel.kt @@ -23,8 +23,7 @@ import androidx.lifecycle.viewModelScope import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.app.utils.toLiveData -import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton -import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import com.mezhendosina.sgo.data.netschool.repo.LoginRepositoryInterface import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest @@ -35,7 +34,7 @@ import javax.inject.Inject @HiltViewModel class ChooseSchoolViewModel @Inject constructor( - private val loginRepository: LoginRepository + private val loginRepository: LoginRepositoryInterface ) : ViewModel() { private val _schools = MutableLiveData>() val schools: LiveData> = _schools @@ -55,7 +54,7 @@ class ChooseSchoolViewModel init { viewModelScope.launch { - loginRepository.schools.collectLatest { + loginRepository.getSchools().collectLatest { _schools.value = it } } @@ -67,7 +66,7 @@ class ChooseSchoolViewModel _isLoading.value = true } try { - loginRepository.findSchool(string) + loginRepository.mapSchools(string) } catch (e: Exception) { withContext(Dispatchers.Main) { _errorMessage.value = e.toDescription() diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiViewModel.kt index b0b34d63..ab9f2246 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugi/GosuslugiViewModel.kt @@ -21,6 +21,7 @@ import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.api.login.entities.accountInfo.toUiEntity import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import com.mezhendosina.sgo.data.netschool.repo.LoginRepositoryInterface import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -30,7 +31,7 @@ import javax.inject.Inject @HiltViewModel class GosuslugiViewModel @Inject constructor( - private val loginRepository: LoginRepository + private val loginRepository: LoginRepositoryInterface ) : ViewModel() { suspend fun login( loginState: String, diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultViewModel.kt index eec0f41a..a84ae6ba 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/gosuslugiResult/GosuslugiResultViewModel.kt @@ -23,6 +23,7 @@ import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.app.utils.toLiveData import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import com.mezhendosina.sgo.data.netschool.repo.LoginRepositoryInterface import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers @@ -33,7 +34,7 @@ import javax.inject.Inject @HiltViewModel class GosuslugiResultViewModel @Inject constructor( - private val loginRepository: LoginRepository + private val loginRepository: LoginRepositoryInterface ) : ViewModel() { private val _loggedIn = MutableLiveData(null) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginFragment.kt index 84754fb0..bbd58142 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginFragment.kt @@ -53,6 +53,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) { enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -60,9 +61,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) { binding = FragmentLoginBinding.bind(view) schoolId = requireArguments().getInt(ARG_SCHOOL_ID) - CoroutineScope(Dispatchers.Main).launch { - viewModel.findSchool(schoolId ?: -1) - } + binding.selectedSchoolCard.setOnClickListener { findNavController().popBackStack() } @@ -75,6 +74,9 @@ class LoginFragment : Fragment(R.layout.fragment_login) { } private fun observeFoundSchool() { + CoroutineScope(Dispatchers.IO).launch { + viewModel.findSchool(schoolId ?: -1) + } viewModel.foundSchool.observe(viewLifecycleOwner) { if (it != null) binding.selectedSchool.text = it.name diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginViewModel.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginViewModel.kt index c66a418d..334535cf 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginViewModel.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/login/LoginViewModel.kt @@ -22,7 +22,6 @@ import androidx.core.content.ContextCompat.startActivity import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import androidx.navigation.NavController import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.R @@ -31,10 +30,11 @@ import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity import com.mezhendosina.sgo.app.utils.toDescription import com.mezhendosina.sgo.data.netschool.api.login.entities.toUiEntity import com.mezhendosina.sgo.data.netschool.base.toMD5 -import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import com.mezhendosina.sgo.data.netschool.repo.LoginRepositoryInterface import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -44,7 +44,7 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( - private val loginRepository: LoginRepository + private val loginRepository: LoginRepositoryInterface ) : ViewModel() { private val _isLoading = MutableLiveData(false) @@ -67,7 +67,6 @@ class LoginViewModel CoroutineScope(Dispatchers.IO).launch { try { val passwordHash = password.toMD5() - val school = findSchool(schoolId) loginRepository.login( login, passwordHash, @@ -97,7 +96,13 @@ class LoginViewModel suspend fun findSchool(schoolId: Int) { - _foundSchool.value = loginRepository.schools.first().first { it.id == schoolId } + loginRepository.getSchools().collectLatest { schoolUiEntities -> + val findSchool = schoolUiEntities.firstOrNull { it.id == schoolId} + withContext(Dispatchers.Main) { + _foundSchool.value = findSchool + } + } + } } diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepository.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepository.kt index deda3022..7ebac000 100644 --- a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepository.kt +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepository.kt @@ -16,7 +16,6 @@ package com.mezhendosina.sgo.data.netschool.repo -import android.content.Context import com.mezhendosina.sgo.Singleton import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity import com.mezhendosina.sgo.data.SettingsDataStore @@ -27,32 +26,33 @@ import com.mezhendosina.sgo.data.netschool.api.login.entities.StudentResponseEnt import com.mezhendosina.sgo.data.netschool.api.login.entities.accountInfo.User import com.mezhendosina.sgo.data.netschool.api.settings.SettingsSource import com.mezhendosina.sgo.data.requests.sgo.login.entities.LoginResponseEntity -import com.mezhendosina.sgo.data.requests.sgo.login.entities.LogoutRequestEntity -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent -import dagger.hilt.android.qualifiers.ActivityContext -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.last import kotlinx.coroutines.withContext import javax.inject.Inject - -@Module -@InstallIn(ActivityComponent::class) -class LoginRepository -@Inject constructor( +class LoginRepository @Inject constructor( private val loginSource: LoginSource, private val settingsDataStore: SettingsDataStore, private val settingsSource: SettingsSource -) { - private val _schools = MutableSharedFlow>() - val schools: SharedFlow> = _schools - suspend fun findSchool(name: String) { +) : LoginRepositoryInterface { + private val _schools = MutableStateFlow>(emptyList()) + override suspend fun findSchool(schoolId: Int): SchoolUiEntity { + return _schools.last().first{ it.id == schoolId} + } + + + override fun getSchools(): StateFlow> { + return _schools + } + + + override suspend fun mapSchools(name: String) { val schoolsList = loginSource.getSchools(name).map { it.toUiEntity() } withContext(Dispatchers.Main) { @@ -60,13 +60,13 @@ class LoginRepository } } - suspend fun login( - login: String? = null, - password: String? = null, - schoolId: Int? = null, - firstLogin: Boolean = true, - onOneUser: () -> Unit = {}, - onMoreUser: (List) -> Unit = {} + override suspend fun login( + login: String?, + password: String?, + schoolId: Int?, + firstLogin: Boolean, + onOneUser: () -> Unit, + onMoreUser: (List) -> Unit ) { @@ -138,12 +138,10 @@ class LoginRepository } } - suspend fun getGosuslugiUsers(loginState: String): List = + override suspend fun getGosuslugiUsers(loginState: String): List = loginSource.getGosuslugiAccountInfo(loginState).users - suspend fun gosuslugiLogin( - firstLogin: Boolean = false - ) { + override suspend fun gosuslugiLogin(firstLogin: Boolean) { val loginState = settingsDataStore.getValue(SettingsDataStore.ESIA_LOGIN_STATE).first() ?: "" val userId = settingsDataStore.getValue(SettingsDataStore.ESIA_USER_ID).first() ?: "" @@ -164,6 +162,6 @@ class LoginRepository } } - suspend fun logout() = loginSource.logout() + override suspend fun logout() = loginSource.logout() } \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepositoryInterface.kt b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepositoryInterface.kt new file mode 100644 index 00000000..ded81583 --- /dev/null +++ b/app/src/main/java/com/mezhendosina/sgo/data/netschool/repo/LoginRepositoryInterface.kt @@ -0,0 +1,39 @@ +package com.mezhendosina.sgo.data.netschool.repo + +import com.mezhendosina.sgo.Singleton +import com.mezhendosina.sgo.app.uiEntities.SchoolUiEntity +import com.mezhendosina.sgo.data.SettingsDataStore +import com.mezhendosina.sgo.data.netschool.NetSchoolSingleton +import com.mezhendosina.sgo.data.netschool.api.login.LoginEntity +import com.mezhendosina.sgo.data.netschool.api.login.entities.StudentResponseEntity +import com.mezhendosina.sgo.data.netschool.api.login.entities.accountInfo.User +import com.mezhendosina.sgo.data.requests.sgo.login.entities.LoginResponseEntity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withContext + +interface LoginRepositoryInterface { + suspend fun findSchool(schoolId: Int): SchoolUiEntity + + fun getSchools(): StateFlow> + + suspend fun mapSchools(name: String) + + suspend fun login( + login: String? = null, + password: String? = null, + schoolId: Int? = null, + firstLogin: Boolean = true, + onOneUser: () -> Unit = {}, + onMoreUser: (List) -> Unit = {} + ) + + suspend fun getGosuslugiUsers(loginState: String): List + suspend fun gosuslugiLogin( + firstLogin: Boolean = false + ) + + suspend fun logout() +} \ No newline at end of file diff --git a/app/src/main/java/com/mezhendosina/sgo/di/RepoModule.kt b/app/src/main/java/com/mezhendosina/sgo/di/RepoModule.kt index dbdeda15..53f86478 100644 --- a/app/src/main/java/com/mezhendosina/sgo/di/RepoModule.kt +++ b/app/src/main/java/com/mezhendosina/sgo/di/RepoModule.kt @@ -2,6 +2,8 @@ package com.mezhendosina.sgo.di import com.mezhendosina.sgo.data.netschool.repo.LessonRepository import com.mezhendosina.sgo.data.netschool.repo.LessonRepositoryInterface +import com.mezhendosina.sgo.data.netschool.repo.LoginRepository +import com.mezhendosina.sgo.data.netschool.repo.LoginRepositoryInterface import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -15,4 +17,8 @@ abstract class RepoModule { @Binds @Singleton abstract fun bindLessonRepository(lessonRepository: LessonRepository): LessonRepositoryInterface + + @Binds + @Singleton + abstract fun bindLoginRepository(loginRepository: LoginRepository): LoginRepositoryInterface } \ No newline at end of file From 0fc7cd84c60d489cd09b2f6cffc6ec5cf98126e6 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Wed, 29 Nov 2023 17:30:39 +0300 Subject: [PATCH 14/17] feat: prepare for rustore release --- app/src/main/AndroidManifest.xml | 12 ++++++------ .../app/ui/main/container/ContainerViewModel.kt | 15 ++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1221c232..bcba690c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,12 +19,12 @@ - - - - - - + + + + + + > = _weeks fun checkUpdates() { - CoroutineScope(Dispatchers.IO).launch { - try { - val checkUpdates = containerRepository.checkUpdates() - withContext(Dispatchers.Main) { - _latestUpdate.value = checkUpdates + if (BuildConfig.BUILD_TYPE != "rustore_release") + CoroutineScope(Dispatchers.IO).launch { + try { + val checkUpdates = containerRepository.checkUpdates() + withContext(Dispatchers.Main) { + _latestUpdate.value = checkUpdates + } + } catch (_: Exception) { } - } catch (_: Exception) { } - } } fun showUpdateDialog() { From 6f360e7706a04e7af3fa6d320dff83561dc3963b Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Wed, 29 Nov 2023 17:31:15 +0300 Subject: [PATCH 15/17] feat: new name --- .../app/ui/loginFlow/welcome/WelcomeFragment.kt | 4 ---- app/src/main/res/layout/fragment_about_app.xml | 1 + app/src/main/res/layout/fragment_welcome.xml | 14 -------------- app/src/main/res/values/strings.xml | 4 ++-- 4 files changed, 3 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/welcome/WelcomeFragment.kt b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/welcome/WelcomeFragment.kt index e9719e9e..6488ab0e 100644 --- a/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/welcome/WelcomeFragment.kt +++ b/app/src/main/java/com/mezhendosina/sgo/app/ui/loginFlow/welcome/WelcomeFragment.kt @@ -67,7 +67,6 @@ class WelcomeFragment : Fragment(R.layout.fragment_welcome) { with(binding!!) { hi.visibility = View.VISIBLE youIn.visibility = View.VISIBLE - sgoApp.visibility = View.VISIBLE whyDisabled.visibility = View.VISIBLE about.visibility = View.VISIBLE gosuslugiLogin.visibility = View.VISIBLE @@ -91,9 +90,6 @@ class WelcomeFragment : Fragment(R.layout.fragment_welcome) { TransitionManager.beginDelayedTransition(binding!!.root, transition) binding!!.youIn.visibility = View.VISIBLE - TransitionManager.beginDelayedTransition(binding!!.root, transition) - binding!!.sgoApp.visibility = View.VISIBLE - delay(1000) TransitionManager.beginDelayedTransition(binding!!.root, transition) binding!!.about.visibility = View.VISIBLE diff --git a/app/src/main/res/layout/fragment_about_app.xml b/app/src/main/res/layout/fragment_about_app.xml index 32ed0d74..be93ad4a 100644 --- a/app/src/main/res/layout/fragment_about_app.xml +++ b/app/src/main/res/layout/fragment_about_app.xml @@ -155,6 +155,7 @@ android:gravity="start|center_vertical" android:text="@string/app_web_site" android:textColor="?attr/colorOnBackground" + android:visibility="gone" app:icon="@drawable/ic_web_site" app:iconSize="32dp" app:iconTint="?attr/colorOnBackground" diff --git a/app/src/main/res/layout/fragment_welcome.xml b/app/src/main/res/layout/fragment_welcome.xml index fa2c076c..01262c3f 100644 --- a/app/src/main/res/layout/fragment_welcome.xml +++ b/app/src/main/res/layout/fragment_welcome.xml @@ -46,20 +46,6 @@ app:layout_constraintStart_toStartOf="@id/hi" app:layout_constraintTop_toBottomOf="@id/hi" /> - - - SGO app + че задали че задали? Дневник Объявления @@ -172,7 +172,7 @@ Войти с помощью логина и пароля Войти через Госуслуги Привет! - Ты попал в + Это че задали Неофициальный порт электронного дневника «Сетевой город. Образование» на Android Тема Не заполнено From 7a40daa6d4bbeff2f01ffd8aeafb06fcf9efc11e Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Wed, 29 Nov 2023 17:31:30 +0300 Subject: [PATCH 16/17] feat: new version --- app/build.gradle | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 29898235..37f4e2e7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,6 +12,23 @@ plugins { android { compileSdk 34 signingConfigs { + rustore_release { + def keystoreProperties = new Properties() + def keystorePropsFile = file("keystore/keystore_config") + + if (keystorePropsFile.exists()) { + file("keystore/keystore_config").withInputStream { keystoreProperties.load(it) } + storeFile file("$keystoreProperties.storeFile") + storePassword "$keystoreProperties.storePassword" + keyAlias "$keystoreProperties.keyAlias" + keyPassword "$keystoreProperties.keyPassword" + } else { + storeFile file("keystore/mezhendosina_key.jks") + storePassword System.getenv('KEYSTORE_PASSWORD') + keyAlias System.getenv('RELEASE_SIGN_KEY_ALIAS') + keyPassword System.getenv('RELEASE_SIGN_KEY_PASSWORD') + } + } release { def keystoreProperties = new Properties() def keystorePropsFile = file("keystore/keystore_config") @@ -34,8 +51,8 @@ android { applicationId "com.mezhendosina.sgo.app" minSdk 24 targetSdk 33 - versionCode 37 - versionName '3.0.2' + versionCode 39 + versionName '3.0.3' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -58,6 +75,14 @@ android { resValue("bool", "FIREBASE_ANALYTICS_DEACTIVATED", "true") manifestPlaceholders = [crashlyticsCollectionEnabled: "false"] } + rustore_release { + signingConfig signingConfigs.release + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + manifestPlaceholders = [crashlyticsCollectionEnabled: "true"] + resValue("bool", "FIREBASE_ANALYTICS_DEACTIVATED", "false") + + } release { signingConfig signingConfigs.release minifyEnabled false From f0b6092147e0fa57211231c7faa636812da48e3c Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Wed, 29 Nov 2023 17:34:16 +0300 Subject: [PATCH 17/17] feat: update RELEASE_NOTES.md --- RELEASE_NOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 36dba34e..1d5d45e8 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1 +1,2 @@ -- Исправление багов \ No newline at end of file +- Исправление багов +- Ренейминг: теперь не SGO app, а че задали :) \ No newline at end of file