From 19a571ac06a7e0869bb7f1130da2943dae6fd6eb Mon Sep 17 00:00:00 2001 From: Omni Devel Date: Thu, 11 Jun 2026 17:32:02 +0300 Subject: [PATCH] refactor: rewrite mobile app in Compose and add error handling for wear --- gradle/libs.versions.toml | 7 + mobile/build.gradle.kts | 23 +- .../baselineProfiles/0/mobile-release.dm | Bin 3633 -> 12384 bytes .../baselineProfiles/1/mobile-release.dm | Bin 3557 -> 12352 bytes mobile/src/main/AndroidManifest.xml | 31 +- .../main/java/ru/omni_devel/cards/CardInfo.kt | 2 +- .../ru/omni_devel/cards/EditCardActivity.kt | 311 ++++++++++++------ .../main/java/ru/omni_devel/cards/Elements.kt | 132 ++++++++ .../java/ru/omni_devel/cards/MainActivity.kt | 243 ++++++++------ .../ru/omni_devel/cards/ShowCardActivity.kt | 121 ++++--- .../main/java/ru/omni_devel/cards/Utils.kt | 21 +- .../ru/omni_devel/cards/ui/theme/Color.kt | 11 + .../ru/omni_devel/cards/ui/theme/Theme.kt | 58 ++++ .../java/ru/omni_devel/cards/ui/theme/Type.kt | 34 ++ .../main/res/layout/activity_edit_card.xml | 87 ----- mobile/src/main/res/layout/activity_main.xml | 73 ---- .../main/res/layout/activity_show_card.xml | 59 ++-- .../src/main/res/layout/content_show_card.xml | 20 ++ mobile/src/main/res/layout/fragment_first.xml | 34 ++ .../src/main/res/layout/fragment_second.xml | 34 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 16 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 16 +- mobile/src/main/res/navigation/nav_graph.xml | 28 ++ mobile/src/main/res/values-land/dimens.xml | 3 + mobile/src/main/res/values-night/themes.xml | 7 + mobile/src/main/res/values-v23/themes.xml | 8 + mobile/src/main/res/values-w1240dp/dimens.xml | 3 + mobile/src/main/res/values-w600dp/dimens.xml | 3 + mobile/src/main/res/values/colors.xml | 37 +-- mobile/src/main/res/values/dimens.xml | 3 + mobile/src/main/res/values/strings.xml | 45 +++ mobile/src/main/res/values/styles.xml | 10 - mobile/src/main/res/values/themes.xml | 33 +- mobile/src/main/res/xml/backup_rules.xml | 13 - .../main/res/xml/data_extraction_rules.xml | 19 -- settings.gradle.kts | 3 +- wear/build.gradle.kts | 4 +- .../baselineProfiles/0/wear-release.dm | Bin 14750 -> 13042 bytes .../baselineProfiles/1/wear-release.dm | Bin 14705 -> 12998 bytes .../cards/presentation/ShowCardActivity.kt | 59 +++- 40 files changed, 1003 insertions(+), 608 deletions(-) create mode 100644 mobile/src/main/java/ru/omni_devel/cards/Elements.kt create mode 100644 mobile/src/main/java/ru/omni_devel/cards/ui/theme/Color.kt create mode 100644 mobile/src/main/java/ru/omni_devel/cards/ui/theme/Theme.kt create mode 100644 mobile/src/main/java/ru/omni_devel/cards/ui/theme/Type.kt delete mode 100644 mobile/src/main/res/layout/activity_edit_card.xml delete mode 100644 mobile/src/main/res/layout/activity_main.xml create mode 100644 mobile/src/main/res/layout/content_show_card.xml create mode 100644 mobile/src/main/res/layout/fragment_first.xml create mode 100644 mobile/src/main/res/layout/fragment_second.xml create mode 100644 mobile/src/main/res/navigation/nav_graph.xml create mode 100644 mobile/src/main/res/values-land/dimens.xml create mode 100644 mobile/src/main/res/values-night/themes.xml create mode 100644 mobile/src/main/res/values-v23/themes.xml create mode 100644 mobile/src/main/res/values-w1240dp/dimens.xml create mode 100644 mobile/src/main/res/values-w600dp/dimens.xml create mode 100644 mobile/src/main/res/values/dimens.xml delete mode 100644 mobile/src/main/res/values/styles.xml delete mode 100644 mobile/src/main/res/xml/backup_rules.xml delete mode 100644 mobile/src/main/res/xml/data_extraction_rules.xml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 63d77b2..04ab44d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,6 +19,9 @@ activityCompose = "1.13.0" coreSplashscreen = "1.2.0" zxing = "3.5.3" zxingAndroid = "4.3.0" +lifecycleRuntimeKtx = "2.6.1" +navigationFragmentKtx = "2.6.0" +navigationUiKtx = "2.6.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -45,6 +48,10 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "coreSplashscreen" } zxing-core = { group = "com.google.zxing", name = "core", version.ref = "zxing" } zxing-android = { group = "com.journeyapps", name = "zxing-android-embedded", version.ref = "zxingAndroid" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" } +androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/mobile/build.gradle.kts b/mobile/build.gradle.kts index 99f9ba8..a90ca7b 100644 --- a/mobile/build.gradle.kts +++ b/mobile/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.compose) kotlin("plugin.serialization") version "2.2.20" } @@ -15,8 +16,8 @@ android { applicationId = "ru.omni_devel.cards" minSdk = 30 targetSdk = 36 - versionCode = 1 - versionName = "1.0" + versionCode = 2 + versionName = "2.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -34,18 +35,34 @@ android { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } + buildFeatures { + compose = true + viewBinding = true + } } dependencies { + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.ktx) implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.ui.graphics) + implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.material3) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) implementation(libs.material) - implementation(libs.play.services.wearable) testImplementation(libs.junit) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.compose.ui.test.junit4) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.junit) + debugImplementation(libs.androidx.compose.ui.test.manifest) + debugImplementation(libs.androidx.compose.ui.tooling) implementation(libs.zxing.core) implementation(libs.zxing.android) implementation("com.google.android.gms:play-services-wearable:18.1.0") diff --git a/mobile/release/baselineProfiles/0/mobile-release.dm b/mobile/release/baselineProfiles/0/mobile-release.dm index 3110a02bbf8f261c79bd501fd46c3d573ec0c726..c3ca3b625969f1d48aa254e0cec396c897cc3218 100644 GIT binary patch literal 12384 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 diff --git a/mobile/release/baselineProfiles/1/mobile-release.dm b/mobile/release/baselineProfiles/1/mobile-release.dm index 91c7ea9cdd8e97ef3af1acdc10785179c646e93c..8cdbfe87471c36e8467e071ae3d0cdf5b4d847a2 100644 GIT binary patch literal 12352 zcmZ{~1yozXw?2x8Vkxx6oub7l5}dZU(-zm_65L%{D8;2n@dBkSh2T=WxH}XL65InM zKuGd>fA6mMzw7?j+uxdX_MDM3Yi9OdGy8mcn)n2dafpeDamqf9n&bQrA^WF$dOL${ zyn_Tiy*(Uo4f%h}{!?&-MTBwiYpU^Zl=*RR0&!2mNLv`+7VI4ihVA%yFe|dMHTQB5 z#M2V65qsx7WDRHEvSkVgUky$4Jt6sfKdzKNs6Uw`e2cK!^>v!QE;r8$vw!_cN zg5W|%(cK>jnA2ODYJu$u!0An~NJ65B=F{@0RhO6j=|D9N&BKh$v^gQ(qikHjWKEfX zJlTEf2lKhsb_PzGCxT?M73$IE-FgbIsrJ)wV%n`xej8Q-51RNh2pEcs5N@gPwe)UM zQ#+#JLa>GXE#x1yk z5P?tBQH(DankWi>p(B6(aKhgJ5Tt0EZQ|G!3DxPtkClj|xtq8c{k{TMe917>HXDbl z)A__e#VnN-C~v_NU-#ExgVm>m^?L$n2SO!lmk}4(ohq0{KzpwnfkP1v>lX3XC%X4n zKY!pCd8(w|-OGHFE&sCMcl^uz=q$Lt%5M`Lr`hjT+>Cc6q~W-U!JCf`mtehYk1%+^ zkz!?vwILhQE1^^Zld)<4r~3fL`ypqUkqvzE|t zCPiwh5my2=GnF!(@O>M)`(g{B_$F`J7l3oM?kGUJ8~1XgsJ#-+b&twM>gbzO_-ny^ zr8q|KqKt+Cp)Pap8@l!@d?#!CK%I1w!?_sI85$Ttkdw^q--x-F%QcMhtc>>XhZE9H z##~IxgdwTcGBfJwYNXN)0MJP+*)kv}sgx;l7J1I$L>tpxQjn?{oW;iABe}1HZ@TQ3 z%?kwC;i%qj-~;X6O{mg6cwS7%=y%RuLMAoRZG(~4q*`ty~ zS%`vALOXuMPD;7cr!m29tv?DTlTUfCrcMq}8tP&oQMGoX+byxOi3-U|3rEwl6sCXO zR}>Zz;anRl!C|SCvB5!^>p61TcO492H_Lor`4W<9L$j_K0uOu-*%@E$s)OD2>CQQ;%CwtlgDzzXLQ9jWQL`K zie(wnu>|R$Dq2nsycpi&Ya9?OB5a<4AjEze9y-7A7hoa0=m{j zCf%u{=YGp+JeRsCepQ=K`A74su~8Krl!6zCSanIrhYD6p<5F z(4bwlzNp&n_hQVZT4Oo6Y-dxlRvqaf5TxE5b@!!1B6JW=2zEo(uQCWQ{H<=ez2gep zK6HAY+(`xx*TIQY-i*02^}rf~{k5En8oMTUM*|Q0{Ffz`?3n;3EPz9w>(&89lx=q* z^4NQ|)9F2TwOcu9O5At9LWFWl7kzxGCEV+c8U2N(d7(q$=YjbW-{JnYJ5f5TcNEn~ zQ0BzS@QtOdG5Wj6)U8PIrNtgHEt%t_)beAL|Csp6)HG>W33$H)WG`4K;KX=s$>?0< zQsl2ewx6G+#s8Cq0l94s;oUQhs2C$Gco&ykJ}@|AepP}n3b|U~cCE6u>SX5c0`1g)u-`yivuM$X(ZOrbDp;UsR2|G3 zi2QFT2J}UCAi4`{EmBtE8W;Gzb{g`k)0&!AROT0%w$)JTD0N#!TkFn&NK4HSNkrQZ zkGA0j_nc?v%s0ilG1u-+amzW!_j3s%QwiXi7F~(c`J%%~hj)=aTKSIPwL9}FIxG() zS?E7QjDp#2q)NR-o2~%bRVpDaC>Oyn-)h;skChR^(TFnQz?{_{q9Ol{1;sf5X>^%y$;0@%tKMtrS-!(>9+X-RwKDI-`9VJD|PSmxNIEKz^_^ z3vP?pyS(YpXLj&+MVW~Rnz7Z-W5A8$RasCM?A7nklcL?d8Y%g9HTQ>GeKXoj{vM2j zyTV_jH=IJxeFVF-xJDwrAKSdo#mE_;30X zuCp1Of~o}n%}$ywAgTsuh(>af5H`5u%Dv|KKFKX&rs=P|j;VhKJe+D85xm0_hPHhf zW_VCv-_LeFtT6PH?rMAx;LscW{Ny`yxtsAXU)%bWQqy1Xs<1%^6d}4oo3&BE`EvInL`N=4ox5g zK{0+qpYF!pQ~iTpvA8Ei0&Z9ID?IFiEH^zuEF7R`5)9e;@=9iLcoI*SF!l7A;AXjM zxq_>_6YH)4@XN<*s`%lFvkQ*|nEUe9CT*^H*5)^iSq4uk{j%id?FM@K3rM$yNJ^dk z+dm8wjGe~te9?&=JUP+<3Ytm86Ou?w|c*&$scYlS=aOxBE=%Yiy~7)*Fn`_L-ci0URm{LrLJZT zcfFK_qa%;&#d|o-LXEK_dk5>mY&tDIV`)pX@>Z~KdFDkM?p9FBlXd{ny3S1)i0UA! zRBOLsv?=uXvcrICCnlvUfop7+JF%-swLrLuT$F9FI)>YK%a#G zLARIHOjcA!L$*3QnJf>7P1hcQdL|?&;D-H}j$&Im++kTSB3*%>h*rj?Ma5dlLLa#d zajp@tk!W<8l+-ek6HY|2h6zM;3BCu2x^*q2+sqPOFa8AlTSEAXXO<0(0XNz*4&fp% zLp{%Zn4yz|JHhL9QkmVEwaOEue{Kp-9|-uwq~1&lH=S=X90q>?{x)&oM%`+}LxMv? z-WFVW*)76>*a7L}UzX)R8V9t0t~K7V^U5{Cx|0o;4Ipk)$dR*zbGIK^*p(;`u5Sz- z$dr}pBk4X>uM~HB&XwAUE~Hn)~?*}aGu%oI(BhfaY2($#+7xf zVn5o3Fw!66_qQV89r0%EZK=1=uNj)8kvH-MEaF7Z{})^a6}i`poFe8=g|77*Qi4UYLTa_?d#6Qy zb?QTXUz7cve3sW3BDg-i^lN+^9yfmWUc|#N4t|tB0~RT+h2DzDu-x$sADJ~L^_U_i z;xLdM{Ec~Ffr3&0fol15_}qVc*D2bMH25~IMZ!6EVucDJ!@Bxj*3j1_tK*aC;GMJS zVBS-DXU#Xi1gD`t{rjN25;GMeV%TcX^4{_uJw82t0TsTv(Yf5m8dBl=0?vg_wX*p7 z1y+F_f}^QfUa;;6MO%{ka6T!h%1!l|eeF86EY~51CIrW4ZL@qgI;A( zzC!)VwmR@mwjWeU}RzK3-htzj$>0 zvhCz`SiNmqUf7K5#e!Q%V3?Y>_KNex^sf}@DmfS3klnHxsKeNx@ryi=t}sH{UO+wM zxyNMAzJ$VMfZ3Wu00)9-ukva2!KGKHYacQR?pqo`>A2<;V9X-ElI3vQ>*gY^g{r=U zeAW+pOz7b^_LQxQVNiqW#E>p2rJ1dDVZTFV8#DX&h#xvW&zJ-A!v`yxbMpFh4mdNi z43xwlDU7^h&elCJl}xz`wki$_4X0f&{FCGQYo5jRuL9q{I;Imq)$BDsPnIdho*^Pm5pA{D7o+3j<8R;?JD zHxJo1eUTTvXm9DbVK!AffyA-o*>MQfDM-a_ZK`&F@p<^5MU5~aEyIC7?U8Bw-(LOt8C}|0)2`nsY(jE0AtGmm5|B!Ez1`d*8Z;JY zJsFVv4QXx|M%W7UTt66flUaCzFpcqqDFu3xNB@?KBZB6(nVvVvW&zkY`Z#pA(8*1* z0&z&?`ta8Av(b!qSGiICbT367Br0j+me@hoIeKB+xnp zR*j9vcf^J6hrNa?BaWtP3r#%tO*-v9VScFx#tlk}D8#`8foCh4Sw9;VB9;K7g18Pt> zb%B&8Htx06=@g>aIa(4yN*Xtku)XWAds%uahZ*(UasoTQK(DR6 zu=+;k!R4=k-vM*@1^c)KqtdlTb6nSv!HROl%zFQMeTe&?>=c^$yl_o?fuh3*JmxX( zt$f=7cLvv+W?KEL`F#@KvHY+Ul6|Ka(ZQ$zVcsVLnlptcK*Wx#+&=`6C;z50+5Ru<<|7}fQ}0ss ztgTQ4B@>^(bc-`@*#!hr6T9!mmDca(e{_BWl->UD9S_=0GW%_0X>JM1-=dTEg=Kvij%$bJU4R$ zsl1w5=(w@@G`wxc)SSaxxl4WdA_s`)IDfVpa<tY}@wue; zd{5_~K|i--z8mIhGJj1FYvm|J{HkZvJa7O;H2dSKCy9f@y<=qfHjCA$oisfCB}PRQ z`2%x#{{a-tJ^y-7HRzlXePTYJ{%?!6+C zwkhvx>Q8Muh7l2vn!8e{l}FR*FrAO*`F2=JsO_zaqvzUC`UkEeG=eaMdCA|p59VQv zRn_e0TD)q=hon@F@&>803BF|aoh^4-RN>*di_OxwumFjh0~;#?;psA(<;0I<&*x%_ z0x6sBD?CPc`?B6S|KjUn9o2qdP$zjF`~aClFRWA6Zfg(X>_Tx7vOntc=6XB~kgk|E7<4*pazrG}32-42x|NY|03!wO& z(?N^&^HZaNlq_cd1p8(?&D`&W4}pQ3vS@>zJ)zIxT$~}*DvrZt`g~7{&r>uxy%hB| z(mRZQdODeuo3*G(`^*tBUG$N(yrHe9^cwAYJORI$^?pDI4S2+ZbCyB@~kR~yTtTcp2hd8 z>V%Z(#3k>qY)|}^Gy4CSi=qpRec#4c3ncx%x%m0!ZQ24cQGpp#(I)N9h}P1xAo)hM zXo8(P+Yg^QpY>Kgp{KVyweGt=WF}NViUyc^YUEnfUGZpBk5zxpseJ?*Fz2<@vlHf~ zF<0z&=BjztE%ro9k4<$g`?%$i0nN;?tl(gadBJM*d#8TI{MLn}(K2Xqsd34m7%;oZ z(9hIsmFDCz84V?1(cB;Dah&5#S>t#Ub_$ms-lbW4k}VSp7!%A5rQCiCaQYj_l4Lv6 zB=xF5-6z2D%lwPV)kxjv3)~_#kLp{9o!=S;#q$~{F`eR8=-nNBe-+I@DXO6RnZ)~| z`Rwt>7ej9pVkr^5bsu?HX3LFeDJ9-!#eL`pXEdL6YgdR28W_aId@A2O5Q%B^x+3;q z78pEvuG-hu z-;3VzgZT9P2z^sqalL&No0J$$A^r@~?nQy!p)Dy{8zyfsnIeB#i0#=bP?-{7Iq_J^ zKlDZ6x1^kYh9cvdrklyUF<#~*S*;L6`xr+y^K!vj^G(^7o%7v=)n}5^Uk0#MywNoo_?UfpUD#kJ$dW$d;Kw4B-Y+K?Kn%nt}beYnXN@BD)A`_KI*eVrxgpSyhq+-C9%MUXY;@eP zrljNSMS*$zh0+HGRlQ&qz<&Hx=#>^)gpI6BP187lgm13lqQV~c6VEuLv9yH26*Eo)ySa znlDYkg5J|i#KB)@RG^)qaxj@Xc^cUqG)tng`H_s~h&Ef4<)l<$l{TLrRGzK{=u8)Z z9)BHU4;X6L)gdkrz+=2)ubtP1WylbIt2~ z)nb+PYlB)fV*Hq-d4s$ZuW)mi4b!t(vidcGo;fY{J(DP=1Xd~L<@M&(GH{(lex+#5 zaF_c#84uG^#QqKan+h+sP-Mw;oE8={CUDMiivN^ShLgkLIZ|PVPgG#T zmJ|8-;jU3_K5u~#K^^~PwP)`vVQuC4t?OXK-KLg)+3?2bO_o7yi)v_eR802RP=ZxO z*ByIY8t=Pq3K{&_URBu`ZqDT)UMVrq*fZf0ai4&DD2?*2C4c`ztfEzBRa$)CiPj=% znPTTCqt|VFCr#EK_a`=z5w^YUGWCs{-zR})ZXQ`{Rb~}+;2ryKkIo}Fa~C$Ex-nR_ z&uFar)Zkw%a_{>90$X|34{berEy*M+!_n4siiyxiU;(S>8ziY3JqHR4`7(gPUM^L* zoMKa+cVZ(pUTa39uq%HMSR;uJ?AZ`H%{RRb+vQjfsxofHb{#7nV{M}+3RfsmSgVFJ zfd4xCF`ZRxu_Mq>?ZK3zsUw)JFuZctK426^VF9KqjDbbL-k)L$?|E7P50kDo~zqZ!;HxzNR*A>esY^Co0iIARk$AE8T=HjiNtnixM zrrK+PNx(jo`n%Eg+f^-v??Q7l9n|>aq2zZj3RFjYd=0#!pekIPm5*Tj`?ZLtJFi2- zcpsPqk~}h+nDv_)_`D?GYh2tsvM}H2_G{d<{N)ZJ%j4p-stk2CCvCr=S=7*>6m-+! z`tY0Ib^Jj0>L3p0?rU0jU>I@HJ+7{{iI>x1t)uq)2~GA5uayu#f(Y>yL&CswRSJoY zj^vN7apP81jB{_3481<1XS6Zjn~N%|RKLV4$@1xrcvNunJmbv^R;Iv=#e;jGT(yj2 z;NPs(X}_7wPAst{0+3%bhHY|)e79aReM`#OZ-)D)zPW1>fcDnjJJG}64++n%0zcYX zHM-`FLzCob>$Z-bR=PD;GVSzdo$@Fw`|zxDF+0!%om?walUBT%0@(jue3%6`DNC8r z4XuEk1gh8~E%N>2ZpA2Aoc^2ww&8}Qm3yGToO~bK0V7%nx$QkytIkhHV1FUdtxdYt z{EYUO#U=b3{!Z__Dmg+PvJYJJH3HHIlU$n;idRt8D}zR(|q)yJ1XOOrnc#y z;TXnA+f>_pd=|#D?u)|w&|;fh>6{hM?i?KMWNEz$4>bTos?L+%)FL6`4L9a)aBwD= z_JossS$FM<&~r*#3LM3lnfBU2xWasyAU6=j78F%sJ|hZPZHn`hX}(BR&ufj1TvM`` z8(hsAtQ&tx>)oBEN3q>((?Hnpk?Ah-zQv)$4Wj?C-tQ}!$w#w|ANX*+uEA*Vl})it z5#cqF{WeW$`$8l&%hwW$HgX9lC1tYo6-VsXeOAD*rCC(gNhHvT+c_%6Z7#}|4s`eV z2Km6c_drbKMfe?|-+6Zzj}^y(K)SV1=&(3G7*}BAipH(UD-$T)fa|+&v`j2z9|^Rd zDb@;6d)cNJr=96Bn??Z9fOQ z70mUZ*@slv!RJeiV3;gLc*A%W-JB{@=CJe`^ns&}toI7Ad*#7Jr1WwkqkL4DlcaT~ zZT9eFqo`LC8dh&h5E@yJ{`dRs2&Sa7Tzt|O6(`TZ67&a_77GGX%y_rsO>{Qz5igU0 zdK_OR>YjgHt5Cgo;)=6)0AyTOY7plzC+~qHg$d8B4(y!f?g*rmn*7{f z?$^EGCVoEn_cCNfc#F7{98r(+mo36&W8~fa%1Qkw%#pc)w6o4jLdf}ntpcSELt4hb zK&{&-ccN^*RkUYboCwu12b}$s2Y4&p#hi)Dkn0xQrd5a}5xDxJu4 zp|aE^56Z9PJ!6c*b>}`8ys+U}kvjLXRCIeSKR|aKU3jNYM1I)L%qqc?eJQxV&tk?h z;`D4#;D(lgrygBH=(qos=@&i`D3YVZ%35f{iS)Q6X@gC`k0V7R$IxocTQf32J^r__d9YGA`MXWqTp zF@CB%^@Yh6sja19q)n}IM(3};wa9My)a#X{!7G~9bvn7NMa}ZJ$;RGxTKljYn*--i zIrUCCufEGSmL$AL&M9`UzvhPqLEN->(zlpcM@tSzeVK$410lxHn>BPm+4$r*dBjcm z989KA&I?T!al((mK&%>VxZMv!&>t6798vuNkFiH5afss(#0+3;q11?4P-=4ZaLBas$Imdi#NGl)lk2mp9^E=k8ZvkTZ_ONGiwysgX-79Qe(d`D!cJ*4u$NM zj|O0hR1-qFWNLbI&_>_9PkTjZ-Xfjhfk9sE?H*j%rm&;c&ygG{y{ESm6{=7LFW4S5 z_(KjV1GT0a@{H;DU+f_0zWQ=FI@EbNe{TU`6z)CnK|=YyZa?6>W=aYo3^j8%bl3K1 z4T|y-oNTA+=5QU4_b_cZyEtmjxuSD``B>%EW%aS1Cu3p;WZSPNn$c3WpA`PKhaC3e zb*2qL+*yjk2h6$V?acr_t!p9mqcaCp8G^xPI?VRcyVtK#3VDa4wv_7;8611rQ)bB1 ztTq!z@SO)jO*zFKp|~Tn-qys>WZEBcB<|O^Go(U93YwJNWI+ zXi!NzNvm9l@E$&Igr;GTaY$D`F24Drkgh-DYwByL`3|Td1$x6D+#APY zn*EK`R1I*g4(VE3_Hk+czvpABgV||w{p>8ks3vC2qjD3uq89in>K=3pmJi2EL8_m3 zXxy|)21X8|G|tyaIxFzcCU7Wp-6H`&On&^?IF1kuH|~`*>?U4%haDhrtVnmKl)_kN zjAg>zlIh6A|Kf9h*PmWW5q_vj2enWP(T^4A5_oVnZp_<7mJ$-SY{!&utlux9n#Gd)~p(bN@x52SPT!wpCoS@=v8b-aHm`u)Y4K=-uZO~whq zebNiE;0D}q++?t$!sg0YBrc!`t*U;O)?kJ+N5}}9rudV#tt&K<^(s%vc2InK)V+Q@ zMHB|Gd5;eW`p0xaMf;ah+)ioJBUr!{Va07~RGV|8mK!*69)F3t)6`VuDo~zN_yDOB zn*z9A^G@lQL?lsIVFrQ8KK|j$f(N2GP7LoppAaTD5d?kD^DbH0}=M@6vXISGobT1M=W^5!2M&J-x`t5}hKp2_;qfk#FX6A_#pT*Bymt z9?-8H#Vp+PK=`jZLrfhH2N_TWCf0A|jNYWA9n zX_{tE)=spUNZT+Gif@omKvU43582R+=D6x(Bg@PPADBcut7v;Vc zlUQ{D&#eDcDTbc6II$I)teF@#)u~LiPwP?KXkYBUZ5(H%uOj<`$gWrLe9Pn7&7!!p zpy@<{agk##YVU6a6O5DD?`bvpM+GBmNEX4GniB`CKp-&@fBzpTD^&sXUQy z0P{hk!?gB*2(R-sxx>^_R#E!^!Hyq1of#atYJ?@}TYQ@2~yDZtnfg zm2Ydp9$E$GcDQV+-}<;gvH{s1R3lJ^2;`|;k({vT9QK>zy~LT>x?}!UO7n|}W`J&x z`{>l?Zo> z*f$p-9Q)1FXsJNe{i%iT-S~!@zQcU@AQ{53D_6WL*UHO-LUUC*IbSu;M@Rz{;Q-QC zA$&;6xBIR=ympsTi7Lm$L9k7nP(nvfg!043VUUEdqtV#E3)?u?*?RxPP+CB}6o+UJ z=!GmXu(mPI98%HbF#fqYd)QE!IG-eAx5a>x#K%pO@tXIdS+`kheUH5o zj%A-Gkg>L4M#hQ7y}t=-o_v$^sO7QgcYm7x%%7@e}Wk1>YtLh(r2>=$|zu6RK9vQkw z>8&WZJ4`h3&oZhCvb00kxdDI{HM@>Mtx!QRX+XPo_~9Nm&8)*pz2mnrj_UcKqFp#8 za@a1H<7cYmGx(k|+mm$><{K>4j7+DVCV&_sPESIE9G4}@qYFtjoieiqVuI*CSR%3jlyl^z(M&57Pf)D zL=n$+;%BhG?ZV>eT+WHplYU<~@ds(@?8#n<`OX~NTHm;5r1MXExM zU&rz4{6Pp8#n@HsLo>1(hqDj;*i!vKGq(b^!#YIi{PWM{&o-R6))`;_?N(6H<~cM-?Jv}~IK z^PLV4pZV0Kv-e%PteH$2&!7ccTql9*F8FPN+?e&Hx)Co@Kqf{d{%y5ZJX1a1MpPce z;Vjf3SoN+mbo0j{jpZ5u)6nD;Mh9OC_&Ic+JxsJdkLX@%^K3-^mTfZ=ga9>4`)=2l z`vdEK{!M~t-VUod9aUfzK9veh`&zELBv8Q(X+h9;nGiGEH{z`{;C`!@EANV^Uh;qq zV#`4&18OK}+~m@A-fSo1uvN>_OU_n1^Jb>v_p`1zpj4mF{YD}*#-*u2aKio#dAVOM zs_~P+!RpgOlkfU}ylP|q2znHqT^ERihT+sS literal 3557 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 - + - + android:theme="@style/Theme.OmniCards"> + android:exported="false" + android:label="@string/title_activity_edit_card" + android:theme="@style/Theme.OmniCards" /> + + + android:exported="true" + android:label="@string/app_name" + android:theme="@style/Theme.OmniCards"> diff --git a/mobile/src/main/java/ru/omni_devel/cards/CardInfo.kt b/mobile/src/main/java/ru/omni_devel/cards/CardInfo.kt index a7e1a9c..b6ad1ec 100644 --- a/mobile/src/main/java/ru/omni_devel/cards/CardInfo.kt +++ b/mobile/src/main/java/ru/omni_devel/cards/CardInfo.kt @@ -14,4 +14,4 @@ class CardInfo ( val name: String, val codeType: BarcodeFormat, val codeValue: String, -) \ No newline at end of file +) diff --git a/mobile/src/main/java/ru/omni_devel/cards/EditCardActivity.kt b/mobile/src/main/java/ru/omni_devel/cards/EditCardActivity.kt index 702bb16..982f7d6 100644 --- a/mobile/src/main/java/ru/omni_devel/cards/EditCardActivity.kt +++ b/mobile/src/main/java/ru/omni_devel/cards/EditCardActivity.kt @@ -1,124 +1,249 @@ package ru.omni_devel.cards -import android.content.Intent +import android.app.Activity import android.os.Bundle -import android.widget.ArrayAdapter -import android.widget.Button -import android.widget.EditText -import android.widget.Spinner -import android.widget.TextView import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Button +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import com.google.zxing.BarcodeFormat import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanOptions +import ru.omni_devel.cards.ui.theme.OmniCardsTheme -class EditCardActivity : AppCompatActivity() { +class EditCardActivity : ComponentActivity() { private lateinit var db: DbHelper - private val barcodeLauncher = registerForActivityResult(ScanContract()) { result -> - if (result.contents != null) { - setCodeType(result.formatName) - - findViewById(R.id.cardValueInput).setText(result.contents) - } - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_edit_card) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets - } - - val actionName = intent.getStringExtra("action") ?: EditCardAction.ADD.name - val action = EditCardAction.valueOf(actionName) db = DbHelper(this, null) - val actionLabel: TextView = findViewById(R.id.editCardActionLabel) - val cardNameInput: EditText = findViewById(R.id.cardNameInput) - val cardValueInput: EditText = findViewById(R.id.cardValueInput) - val scanCodeButton: Button = findViewById(R.id.scanCodeButton) - val cardCodeTypeSelector: Spinner = findViewById(R.id.cardCodeTypeSelector) - val saveCardButton: Button = findViewById(R.id.saveCardButton) + val cardId = intent.getIntExtra("cardId", -1) + val cardInfo = db.getCard(cardId) - val options = BarcodeFormat.entries.map { it.name } - val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, options) - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - cardCodeTypeSelector.adapter = adapter - - scanCodeButton.setOnClickListener { - val options = ScanOptions() - options.setPrompt("Scan code") - options.setBeepEnabled(false) - options.setOrientationLocked(false) - barcodeLauncher.launch(options) - } - - saveCardButton.setOnClickListener { - val cardName = cardNameInput.text.toString() - val cardValue = cardValueInput.text.toString() - val cardCodeType = BarcodeFormat.valueOf(cardCodeTypeSelector.selectedItem.toString()) - - when (action) { - EditCardAction.ADD -> { - db.addCard(cardName, cardValue, cardCodeType) - - finish() - } - EditCardAction.EDIT -> { - val cardId = intent.getIntExtra("cardId", -1) - - db.editCard(cardId, cardName, cardValue, cardCodeType) - - val intent = Intent(this, ShowCardActivity::class.java) - - intent.putExtra("cardId", cardId) - - startActivity(intent) - finish() + setContent { + OmniCardsTheme { + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + EditCardPage(cardInfo, innerPadding, getDbFun = { + return@EditCardPage db + }) } } } + } +} - when (action) { - EditCardAction.ADD -> { - setCodeType("QR_CODE") +@Composable +fun CardEditable( + cardName: String, + onCardNameChange: (String) -> Unit, + codeValue: String, + oncodeValueChange: (String) -> Unit, + codeType: BarcodeFormat, + onCodeTypeChange: (BarcodeFormat) -> Unit +) { + var isCodeTypeDropdownExpanded by remember { mutableStateOf(false) } - actionLabel.text = "Adding card" - } - EditCardAction.EDIT -> { - val cardId = intent.getIntExtra("cardId", -1) + val fieldModifier = Modifier.fillMaxWidth() - val cardInfo = db.getCard(cardId) + val scanLauncher = rememberLauncherForActivityResult( + contract = ScanContract() + ) { result -> + if (result.contents != null) { + oncodeValueChange(result.contents) - if (cardInfo == null) { - Toast.makeText(this, "cardInfo is null", Toast.LENGTH_LONG).show() - return - } - - actionLabel.text = "Editing card" - - cardNameInput.setText(cardInfo.name) - cardValueInput.setText(cardInfo.codeValue) - setCodeType(cardInfo.codeType.name) + result.formatName?.let { formatName -> + try { + onCodeTypeChange(BarcodeFormat.valueOf(formatName)) + } catch (e: IllegalArgumentException) {} } } } - private fun setCodeType(codeTypeString: String) { - val codeTypes = BarcodeFormat.entries.map { it.name } - val codeTypeIndex = codeTypes.indexOf(codeTypeString) - - if (codeTypeIndex >= 0) { - findViewById(R.id.cardCodeTypeSelector).setSelection(codeTypeIndex) + OutlinedTextField( + value = cardName, + onValueChange = onCardNameChange, + modifier = fieldModifier, + label = { Text("Name") } + ) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + OutlinedTextField( + value = codeValue, + onValueChange = oncodeValueChange, + modifier = Modifier.weight(1f), + label = { Text("Value") } + ) + Button( + modifier = Modifier.align(Alignment.CenterVertically), + onClick = { + scanLauncher.launch( + ScanOptions().apply { + setPrompt("Scan your card") + setBeepEnabled(true) + setOrientationLocked(false) + } + ) + } + ) { + Text("Scan") } } -} \ No newline at end of file + + Box( + modifier = fieldModifier, + ) { + OutlinedTextField( + value = codeType.name, + onValueChange = {}, + modifier = fieldModifier, + label = { Text("Code type") }, + readOnly = true, + ) + Box( + modifier = fieldModifier + .matchParentSize() + .clickable { isCodeTypeDropdownExpanded = !isCodeTypeDropdownExpanded } + ) + DropdownMenu( + expanded = isCodeTypeDropdownExpanded, + onDismissRequest = { + isCodeTypeDropdownExpanded = false + }, + modifier = fieldModifier + ) { + BarcodeFormat.entries.forEach { format -> + DropdownMenuItem( + text = { Text(format.name) }, + onClick = { + onCodeTypeChange(format) + isCodeTypeDropdownExpanded = false + } + ) + } + } + } +} + +@Composable +fun AddCard(innerPadding: PaddingValues, getDbFun: () -> DbHelper) { + var cardName by remember { mutableStateOf("") } + var codeValue by remember { mutableStateOf("") } + var codeType by remember { mutableStateOf(BarcodeFormat.QR_CODE) } + + val context = LocalContext.current as Activity + + Page( + "New card", + innerPadding + ) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + CardEditable( + cardName = cardName, + onCardNameChange = { cardName = it }, + codeValue = codeValue, + oncodeValueChange = { codeValue = it }, + codeType = codeType, + onCodeTypeChange = { codeType = it } + ) + + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + val err = checkCardData(cardName, codeValue) + + if (err == null) { + getDbFun().addCard(cardName, codeValue, codeType) + context.finish() + } else { + Toast.makeText(context, err, Toast.LENGTH_SHORT) + } + } + ) { + Text("Save") + } + } + } +} + +@Composable +fun EditCard(cardInfo: CardInfo, innerPadding: PaddingValues, getDbFun: () -> DbHelper) { + var cardName by remember { mutableStateOf(cardInfo.name) } + var codeValue by remember { mutableStateOf(cardInfo.codeValue) } + var codeType by remember { mutableStateOf(cardInfo.codeType) } + + val context = LocalContext.current as Activity + + Page( + "Editing card id${cardInfo.id}", + innerPadding + ) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + CardEditable( + cardName = cardName, + onCardNameChange = { cardName = it }, + codeValue = codeValue, + oncodeValueChange = { codeValue = it }, + codeType = codeType, + onCodeTypeChange = { codeType = it } + ) + + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + val err = checkCardData(cardName, codeValue) + + if (err == null) { + getDbFun().editCard(cardInfo.id, cardName, codeValue, codeType) + context.finish() + } else { + Toast.makeText(context, err, Toast.LENGTH_SHORT) + } + } + ) { + Text("Save") + } + } + } +} + +@Composable +fun EditCardPage(cardInfo: CardInfo?, innerPadding: PaddingValues, getDbFun: () -> DbHelper) { + if (cardInfo == null) { + AddCard(innerPadding, getDbFun) + } else { + EditCard(cardInfo, innerPadding, getDbFun) + } +} diff --git a/mobile/src/main/java/ru/omni_devel/cards/Elements.kt b/mobile/src/main/java/ru/omni_devel/cards/Elements.kt new file mode 100644 index 0000000..1dfbf40 --- /dev/null +++ b/mobile/src/main/java/ru/omni_devel/cards/Elements.kt @@ -0,0 +1,132 @@ +package ru.omni_devel.cards + +import android.content.Intent +import androidx.compose.foundation.background +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.runtime.setValue +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun Page(name: String, innerPadding: PaddingValues, content: @Composable () -> Unit) { + Column( + modifier = Modifier.padding(innerPadding) + ) { + TopBar(name) + + Column( + modifier = Modifier.padding(horizontal = 16.dp) + ) { + content() + } + } +} + +@Composable +fun TopBar(text: String) { + Row( + modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.primaryContainer).padding(horizontal = 12.dp, vertical = 16.dp) + ) { + Text( + text, + fontSize = 24.sp, + color = MaterialTheme.colorScheme.onPrimaryContainer + ) + } +} + +@Composable +fun CardButton(cardInfo: CardInfo, onDeleteCard: (Int) -> Unit, getDbFun: () -> DbHelper) { + val context = LocalContext.current + var menuExpanded by remember { mutableStateOf(false) } + + Box(modifier = Modifier.fillMaxWidth()) { + Surface( + modifier = Modifier + .fillMaxWidth() + .pointerInput(Unit) { + detectTapGestures( + onTap = { + val intent = Intent(context, ShowCardActivity::class.java) + intent.putExtra("cardId", cardInfo.id) + context.startActivity(intent) + }, + onLongPress = { + menuExpanded = true + } + ) + }, + shape = ButtonDefaults.shape, + color = MaterialTheme.colorScheme.primaryContainer, + tonalElevation = 2.dp + ) { + Text( + cardInfo.name, + fontSize = 16.sp, + color = MaterialTheme.colorScheme.onPrimaryContainer, + modifier = Modifier + .padding(vertical = 16.dp, horizontal = 16.dp) + .fillMaxWidth(), + textAlign = TextAlign.Start + ) + } + + DropdownMenu( + expanded = menuExpanded, + onDismissRequest = { menuExpanded = false } + ) { + DropdownMenuItem( + text = { Text("Edit") }, + onClick = { + menuExpanded = false + + val intent = Intent(context, EditCardActivity::class.java) + + intent.putExtra("cardId", cardInfo.id) + + context.startActivity(intent) + } + ) + DropdownMenuItem( + text = { Text("Delete") }, + onClick = { + menuExpanded = false + + onDeleteCard(cardInfo.id) + } + ) + } + } +} + +@Composable +fun FullWidthButton(onClick: () -> Unit, content: @Composable () -> Unit) { + Button( + modifier = Modifier.fillMaxWidth(), + onClick = onClick + ) { + content() + } +} diff --git a/mobile/src/main/java/ru/omni_devel/cards/MainActivity.kt b/mobile/src/main/java/ru/omni_devel/cards/MainActivity.kt index 94fd7ca..f715259 100644 --- a/mobile/src/main/java/ru/omni_devel/cards/MainActivity.kt +++ b/mobile/src/main/java/ru/omni_devel/cards/MainActivity.kt @@ -2,55 +2,64 @@ package ru.omni_devel.cards import android.content.Intent import android.os.Bundle -import android.view.View -import android.widget.Button -import android.widget.LinearLayout -import android.widget.PopupMenu -import android.widget.TextView import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import com.google.android.material.button.MaterialButton +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalUriHandler import kotlinx.serialization.json.Json +import ru.omni_devel.cards.ui.theme.OmniCardsTheme -class MainActivity : AppCompatActivity() { - private lateinit var cardsList: LinearLayout +class MainActivity : ComponentActivity() { private lateinit var db: DbHelper + private val cards = mutableStateListOf() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_main) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets - } db = DbHelper(this, null) - cardsList = findViewById(R.id.cardsList) - val addCardButton: Button = findViewById(R.id.addCardButton) - val syncWithWearOSButton: Button = findViewById(R.id.syncWithWearOSButton) - - addCardButton.setOnClickListener { - val intent = Intent(this, EditCardActivity::class.java) - - intent.putExtra("action", EditCardAction.ADD.name) - - startActivity(intent) - } - - syncWithWearOSButton.setOnClickListener { - Toast.makeText(this, "Sync in progress...", Toast.LENGTH_SHORT).show() - - sendToWatch(this, "/updateCards", Json.encodeToString(db.getCards())) { isSuccess -> - if (isSuccess) { - Toast.makeText(this, "Successfully synced!", Toast.LENGTH_SHORT).show() - } else { - Toast.makeText(this, "Sync failed! Please, check your connection", Toast.LENGTH_LONG).show() + setContent { + OmniCardsTheme { + Scaffold( + modifier = Modifier.fillMaxSize() + ) { innerPadding -> + MainPage(innerPadding, cards, onDeleteCard = { cardId -> + db.removeCard(cardId) + cards.removeIf { it.id == cardId } + }, getDbFun = { + return@MainPage db + }) } } } @@ -58,84 +67,102 @@ class MainActivity : AppCompatActivity() { override fun onResume() { super.onResume() - renderCards() + + cards.clear() + cards.addAll(db.getCards()) } +} - private fun renderCards() { - val cards = db.getCards() +@Composable +fun MainPage( + innerPadding: PaddingValues, + cards: List, + onDeleteCard: (Int) -> Unit, + getDbFun: () -> DbHelper +) { + val context = LocalContext.current + val uriHandler = LocalUriHandler.current - cardsList.removeAllViews() + var isActionBarShowed by remember { mutableStateOf(false) } - if (cards.isEmpty()) { - val text = TextView(this) - - val params = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - - text.text = "Add a card to get started" - text.layoutParams = params - text.textAlignment = View.TEXT_ALIGNMENT_CENTER - - cardsList.addView(text) - } else { - for (card in cards) { - val button = MaterialButton(this) - - val params = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ) - params.setMargins(16, 4, 16, 4) - - button.text = card.name - button.layoutParams = params - button.cornerRadius = (12 * resources.displayMetrics.density).toInt() - - button.setOnClickListener { - val intent = Intent(this, ShowCardActivity::class.java) - - intent.putExtra("cardId", card.id) - - startActivity(intent) - } - - button.setOnLongClickListener { view -> - val popup = PopupMenu(this, view) - - popup.menu.add("Edit") - popup.menu.add("Remove") - - popup.setOnMenuItemClickListener { menuItem -> - when (menuItem.title) { - "Edit" -> { - val intent = Intent(this, EditCardActivity::class.java) - - intent.putExtra("action", EditCardAction.EDIT.name) - intent.putExtra("cardId", card.id) - - startActivity(intent) - - true - } - "Remove" -> { - db.removeCard(card.id) - renderCards() - - true - } - else -> false - } + Box( + modifier = Modifier.fillMaxSize() + ) { + Page( + "OmniCards", + innerPadding + ) { + Column( + modifier = Modifier.verticalScroll(rememberScrollState()).padding(bottom = 64.dp).background(Color.Transparent).padding(top = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + if (cards.isEmpty()) { + Text("Add new card to get started") + } else { + for (card in cards) { + CardButton(card, onDeleteCard, getDbFun) } - - popup.show() - - true } + } + } - cardsList.addView(button) + if (isActionBarShowed) { + Column( + modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp).background(MaterialTheme.colorScheme.inversePrimary, + RoundedCornerShape(24.dp)).padding(16.dp).fillMaxWidth() + ) { + FullWidthButton( + onClick = { + isActionBarShowed = false + + val intent = Intent(context, EditCardActivity::class.java) + + context.startActivity(intent) + } + ) { + Text("Add card") + } + FullWidthButton( + onClick = { + Toast.makeText(context, "Sync in progress...", Toast.LENGTH_SHORT).show() + + sendToWatch(context, "/updateCards", Json.encodeToString(getDbFun().getCards())) { isSuccess -> + if (isSuccess) { + Toast.makeText(context, "Successfully synced!", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(context, "Sync failed! Please, check your connection", Toast.LENGTH_LONG).show() + } + } + + isActionBarShowed = false + } + ) { + Text("Sync with WearOS") + } + FullWidthButton( + onClick = { + uriHandler.openUri("https://omni-devel.ru") + } + ) { + Text("About author") + } + FullWidthButton( + onClick = { + isActionBarShowed = false + } + ) { + Text(">") + } + } + } else { + Button( + modifier = Modifier.align(Alignment.BottomEnd).padding(32.dp), + onClick = { + isActionBarShowed = true + } + ) { + Text("<") } } } -} \ No newline at end of file +} diff --git a/mobile/src/main/java/ru/omni_devel/cards/ShowCardActivity.kt b/mobile/src/main/java/ru/omni_devel/cards/ShowCardActivity.kt index 1eb2d67..75caeb3 100644 --- a/mobile/src/main/java/ru/omni_devel/cards/ShowCardActivity.kt +++ b/mobile/src/main/java/ru/omni_devel/cards/ShowCardActivity.kt @@ -2,35 +2,61 @@ package ru.omni_devel.cards import android.os.Bundle import android.view.WindowManager -import android.widget.ImageView -import android.widget.TextView import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.style.TextAlign +import ru.omni_devel.cards.ui.theme.OmniCardsTheme -class ShowCardActivity : AppCompatActivity() { +class ShowCardActivity : ComponentActivity() { private lateinit var db: DbHelper override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_show_card) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets - } db = DbHelper(this, null) + + val cardId = intent.getIntExtra("cardId", -1) + + val cardInfo = db.getCard(cardId) + + if (cardInfo == null) { + Toast.makeText(this, "Card is not exists", Toast.LENGTH_LONG).show() + finish() + + return + } + + setContent { + OmniCardsTheme { + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + ShowCardPage(innerPadding, cardInfo) + } + } + } } override fun onResume() { super.onResume() - setBrightness(1.0f) - renderCard() + setBrightness(1f) } override fun onPause() { @@ -39,31 +65,46 @@ class ShowCardActivity : AppCompatActivity() { setBrightness(WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE) } - private fun renderCard() { - val cardId = intent.getIntExtra("cardId", 0) - - val cardInfo = db.getCard(cardId) - - if (cardInfo == null) { - Toast.makeText(this, "cardInfo is null", Toast.LENGTH_LONG).show() - return - } - - val cardCodeView: ImageView = findViewById(R.id.cardCodeView) - val cardNameElement: TextView = findViewById(R.id.editCardActionLabel) - - cardNameElement.text = cardInfo.name - - val code = generateCode(cardInfo.codeValue, cardInfo.codeType) - - cardCodeView.setImageBitmap(code) - } - private fun setBrightness(level: Float) { - val params = window.attributes - - params.screenBrightness = level - - window.attributes = params + window.attributes.screenBrightness = level } -} \ No newline at end of file +} + +@Composable +fun ShowCardPage(innerPadding: PaddingValues, cardInfo: CardInfo) { + val bitmap = remember(cardInfo) { + try { + generateCode(cardInfo.codeValue, cardInfo.codeType).asImageBitmap() + } catch (_: Exception) { + null + } + } + + Page(cardInfo.name, innerPadding) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center + ) { + if (bitmap != null) { + Image( + bitmap = bitmap, + contentDescription = cardInfo.codeValue, + modifier = Modifier.fillMaxWidth(), + contentScale = ContentScale.FillWidth + ) + Text( + cardInfo.codeValue, + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + } else { + Text( + "Failed to generate code. Check card data", + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.error + ) + } + } + } +} diff --git a/mobile/src/main/java/ru/omni_devel/cards/Utils.kt b/mobile/src/main/java/ru/omni_devel/cards/Utils.kt index 0cc683f..3ce1351 100644 --- a/mobile/src/main/java/ru/omni_devel/cards/Utils.kt +++ b/mobile/src/main/java/ru/omni_devel/cards/Utils.kt @@ -2,7 +2,6 @@ package ru.omni_devel.cards import android.content.Context import android.graphics.Bitmap -import android.widget.Toast import com.google.android.gms.wearable.Wearable import com.google.zxing.BarcodeFormat import com.google.zxing.MultiFormatWriter @@ -10,16 +9,14 @@ import com.journeyapps.barcodescanner.BarcodeEncoder fun generateCode(value: String, format: BarcodeFormat): Bitmap { val writer = MultiFormatWriter() - val matrix = writer.encode(value, format, 600, 300) + val matrix = writer.encode(value, format, 800, 400) return BarcodeEncoder().createBitmap(matrix) } -fun sendToWatch(context: Context, path: String, message: String, onResult: (Boolean) -> Unit): Boolean { +fun sendToWatch(context: Context, path: String, message: String, onResult: (Boolean) -> Unit) { val nodeClient = Wearable.getNodeClient(context) - var isSuccess = false - nodeClient.connectedNodes.addOnSuccessListener { nodes -> if (nodes.isEmpty()) { onResult(false) @@ -35,7 +32,7 @@ fun sendToWatch(context: Context, path: String, message: String, onResult: (Bool node.id, path, message.toByteArray(Charsets.UTF_8) - ).addOnCompleteListener { task -> + ).addOnCompleteListener(context.mainExecutor) { task -> completedCount++ if (task.isSuccessful) { @@ -45,11 +42,19 @@ fun sendToWatch(context: Context, path: String, message: String, onResult: (Bool if (completedCount == nodes.size) { onResult(isAnySuccess) } - }.addOnFailureListener { + }.addOnFailureListener(context.mainExecutor) { onResult(false) } } } +} - return isSuccess +fun checkCardData(name: String, value: String): String? { + if (name.trim().isEmpty()) { + return "Name should not be empty" + } else if (value.trim().isEmpty()) { + return "Code value should not be empty" + } + + return null } diff --git a/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Color.kt b/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Color.kt new file mode 100644 index 0000000..326e98f --- /dev/null +++ b/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package ru.omni_devel.cards.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Theme.kt b/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Theme.kt new file mode 100644 index 0000000..94aca9c --- /dev/null +++ b/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package ru.omni_devel.cards.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun OmniCardsTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Type.kt b/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Type.kt new file mode 100644 index 0000000..dc47ec7 --- /dev/null +++ b/mobile/src/main/java/ru/omni_devel/cards/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package ru.omni_devel.cards.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/mobile/src/main/res/layout/activity_edit_card.xml b/mobile/src/main/res/layout/activity_edit_card.xml deleted file mode 100644 index bbefaf4..0000000 --- a/mobile/src/main/res/layout/activity_edit_card.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - -