From 5e3c96b693d30fd2e00b083d6f2d9c73b351dade Mon Sep 17 00:00:00 2001 From: "Dee.H.Y" Date: Thu, 1 Aug 2024 09:09:26 +0800 Subject: [PATCH] feat(app): add system tray functionality - Update pubspec.yaml with tray_manager dependency. - Initialize system tray in app.dart and handle menu item clicks. --- .github/workflows/appimage.yml | 2 +- .github/workflows/ci.yaml | 2 +- .github/workflows/snap.yaml | 2 +- assets/images/tray_icon.ico | Bin 0 -> 188478 bytes assets/images/tray_icon.png | Bin 0 -> 4312 bytes lib/app/view/app.dart | 4 ++ lib/app/view/system_tray.dart | 40 ++++++++++++++++++ lib/persistence_utils.dart | 22 ++++++++++ linux/flutter/generated_plugin_registrant.cc | 4 ++ linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 24 +++++++++++ pubspec.yaml | 3 ++ .../flutter/generated_plugin_registrant.cc | 3 ++ windows/flutter/generated_plugins.cmake | 1 + 15 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 assets/images/tray_icon.ico create mode 100644 assets/images/tray_icon.png create mode 100644 lib/app/view/system_tray.dart diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index ad3fbe54a..27e4a0ac4 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -21,7 +21,7 @@ jobs: channel: 'stable' flutter-version: ${{env.FLUTTER_VERSION}} - run: sudo apt update - - run: sudo apt install -y clang cmake curl libgtk-3-dev ninja-build pkg-config unzip libunwind-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libmpv-dev + - run: sudo apt install -y clang cmake curl libgtk-3-dev ninja-build pkg-config unzip libunwind-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libmpv-dev libappindicator3-1 libappindicator3-dev - run: flutter --disable-analytics - run: flutter pub get - run: flutter build linux --release diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2b9409cc3..579efd144 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -49,6 +49,6 @@ jobs: channel: 'stable' flutter-version: ${{env.FLUTTER_VERSION}} - run: sudo apt update - - run: sudo apt install -y clang cmake curl libgtk-3-dev ninja-build pkg-config unzip libunwind-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libmpv-dev + - run: sudo apt install -y clang cmake curl libgtk-3-dev ninja-build pkg-config unzip libunwind-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libmpv-dev libappindicator3-1 libappindicator3-dev - run: flutter pub get - run: flutter build linux -v diff --git a/.github/workflows/snap.yaml b/.github/workflows/snap.yaml index dd0627687..998dbbb32 100644 --- a/.github/workflows/snap.yaml +++ b/.github/workflows/snap.yaml @@ -19,7 +19,7 @@ jobs: channel: 'stable' flutter-version: ${{env.FLUTTER_VERSION}} - run: sudo apt update - - run: sudo apt install -y clang cmake curl libgtk-3-dev ninja-build pkg-config unzip libunwind-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libmpv-dev + - run: sudo apt install -y clang cmake curl libgtk-3-dev ninja-build pkg-config unzip libunwind-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libmpv-dev libappindicator3-1 libappindicator3-dev - run: flutter pub get - uses: snapcore/action-build@v1 id: build diff --git a/assets/images/tray_icon.ico b/assets/images/tray_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2c7869b948619f5cb3822a421a5a8be728c821b4 GIT binary patch literal 188478 zcmeI537jNFnYXiPU^r(65gcJ=su>PJL0=bx346P1;jm06XUl~tYZ@Aq^Z8OIxM zL`FtLWmP-Qkh8iobEbnk;{37X%ppJ~B zW}%L_{H7d}=FaBaj=3)VFNJwtc_!X=l_n~Gjcd2UFW_)^8;ro}V9L6Q2B3u9(o6kU# zMC3O%GVU5=+62^Q-8Zts!!Q}lZ-_^e6IxANqrM0j}ZAW7zV!%<4@q8N1Ar>7zuU;V|UvfBHqk@nse;2 ztaM8uiF}4ccDb_ID4YVCAMOU-JF>-Lpt(co?}sEZMj}(bZ1r(ayP`y6X-iPLr=k*B z@pkz;5g%8!$#y;-&7=Fl4e&hJqOp7q=$ifw@0O8&!UdYr*oBc4JrskAf|-vBuLCiZtbixw;HB z2rFOLR)yEVK_Huc0^SAd!5DN~zUqiVrP=feb;XZ8p2Zv*Rs<~&pWLi|bl*Dhb#Y|(Q=_rP^< z7O0JH2Cap&C||_{MQwaqk!Gxgxw;HBxCc6tuyRct1*-fFA<>#8OaW6?nCq2q)->wJ zli)&_0NJ3egq3UJ$ZW=f?dthD0&j&+!I^L^=vnbkXcALaluKf}>@dH^q~^07;6Gpy z+yw5k%9#<{#cs9Z{9SHX75~r@!ZTOInQH-8C2Jiv0=s~}4$}rOAn0Rs*M0OM(7NUU z&@)gx2#Pxxl*XhFl+;y)%|IvI5T_|?lFK;uvA%h?|C+w-JJ|1|Mwox5#lEx)B2MPB~=LnQ@_`qBu#hIWZ3>WrtUB|N}&wu*WC9M91A*!-3Wqb zS1n`C=tJ6W`@c>ZMxMnp0T+Sx5c`6jW$VH)wCLf*jo?l|C*$9)Uq#pw+%q1*#V{MG z?yn`nlX9#ztE6$VMN4Jz+5vQNOCOWf=j@{c?LzzpC}6o!!lKG8p@7 z{AVVn{B|xCs($UsZ-nKLiL|TNT&FVCuXT>rxw_9%@7c-IBBuOyE*VvS5VNP(J$5*p z1=qo&pnI{cw5!*?PGQ0NgJC`MHTP@-I@dT6E`Ylrbf06F--(}cgE|DmYmi1~)qB8k zpn2dSaL14{L;>V*ocfUCjHAL>q#C+1IWERv9O^K+q`@feFgWuYX_PL8I@hRG=M?&^ zXaV@=6q^tT2>J%wr88u$H;;z%LF-c4SpBYj#eJaog`jjw-&97Rid=(%y0!lNHb}>I z<5bs`?mM7-J!yki?=bFua5r=!6J**SRF2<{Tn+CP?$x&d_X+4mmXP@%C|duWsB;3c zkUE8I2W}hU>~GR^;@ahs|0j^8e3Qa7-`dsqe#J+iu{RU6x1S3ez&g-0CJFobRVZPd zgJvmuPQ4U-osCCv0ahWb{rzEZ2Ix%W7MSo{otG&76QFcTA1q@jaaqb;6gC1zZP!&e zfaa|4!u^m$lM&*e0+009eD?vk9XgS} zlgZ2jQTl8BdQB&V$K=sxY|`JLIqDm5A1sGB(e-=bYp^@q1Cl8H=Wwl4`?v*%{CSZ$ zqq}i+UvCK?gGF!|==}9oxDhS^&3lK!7NBu48|om5(qH!1`Dmu1Nsa=e(70L~)(7dh z9;^k5xAW8px6@yF)K{5`KS_HY3b*x2>aUTno&G9IpUaqZc)ORN{%ON#AiZi}^oklk zN+%XTR&(8ohh$pCoBOMVPD%CG^H+Q5JD?NM9``wuu|u`}Y?LFL)*w=UCHB(UuI9RL zL7KVl>!jBi-2}MeE7GBdLK5j8(FQZueUx%egSffwm0Z^xPy>xoS2|SN&qg7M^^a(S z>64^$-A2^eGyP4&y>=Vj)3~M3-?Sr*>$SPk-!fX-@o!~K?XB$JYO+DLzt^_bMt2xf z(D>2$!(5|#nA@THW{%B!{?YnG>l@XxW|;oLxTNjz^5v~R)wt*EdPZIdCxiCaI&0Vd z!JK;-dxpAUZ6dGI7NI}YG|4hTkATkR&VnQ09WW2pfY3JB@oJA3?uGtT-6GErWBTYM z{HBj|_NX84Q<^vmDN=t=>lIEKUBl8=`meBMC%!P7riruc ze@`d%cbg}Tu3>3C{b%Fe06LE>RKj#h6Nj?8f3+sq8ukS}i}Y@U=4j1-xk?&c!_uOh z^bevnfu56k{%EamCg{1OHH1I+;qO|)bV?hC5<&fgVXX(WF3>x4b{}c2-Bll@)ip_a zbPDPp7FHi=ZJ<6n96IWwB>Hrl2HnE+560Pjq;K_)? zXAPTy&ODC;%?tXh%5RI~Od}{Q)O@)^L3f!m&5d8ei$uC!*I(Rm*Kvl%I&sImd=%>D z=VP3pJFaqxuY0b(BCqf=&t=8vhTFK#!bUn8>ZW&`g`PX?x%xs^qwRUPV~&K62R(Ok ztP!4c+oAWgc7ZYQ-_ue=gs5{u&}Un_!Le{QXkK{?{^_C5EA%`#2Q&_L2jyugE27$- zM~CT3++5JR85e`v=#OjsQKB?Tt300qmCd9W+Q8FmIb+h#d`c0aE ztG2xf4u?f>8R*@eJ3(s``4x9M=$?KHC{K$mOnqF-a=xdySdp#$b%D{rcDdf7tCvS> zir>Q%kW`kCP(Qzg!(!{R{jFY`qDl66L)dv<9)i zN71hRvq}FqC`xqC+wC|04T%3JTnswXNFsX1>TF17fggccpmpg47%5x(-9?<>y%4?y{|P&Sz0a)?u7i(As&^j$w z$*R4j^`vgUD)I6K-Py!_%ZMrq_<*NVv=SVgFtMT7A>z~9oc$p4UwaUHP2&68bYsVtt@?UB z|J{c!qo8%W-|i@X#|;i-w94;w`eiB(S7+K*~2@-#7emqnkWm@)D?{2h-G@)|q#L;F#; zNz53zhS)RV2F_#>n|xISKZIBx7Vy+8;DX*cf>SiPTxhLuS;#!7&#I2ta%^w^8Aazrm6<^|>8{J%^0?oNKs6%O`n>NPfrNLGE ztNyPs4yy5A+PlBa!a42+Z*$GM|C^pSWiv&aW^{bnYyWKA>p{<+QcA}SY?G13YW~-r zc0Oo*sx^q#B3hFa`OZ1jMN6AzbbOf_)&3w_i)u}(wW;wV*MgXU%zVJ7~}MB7{l? zY8GAu4GEh!f^ln;?iH{X90$5*9)KoWj62RKaXLWNyG3%RdG2t8JH#&yjFN7M|8=Wh zaL^AY49j(YNCCG_0P)>%H<<2+9mdEHPTh0)l>;|CIliO&0}(8#e3vw|eVJRITsNJo%U|0XmNA)&- z4?=yZ2)PY=;K8W+orqoGE8zc5#NGrVgj^~kjMa(PXBi)XZ-Le|dXL%Me|q;)XZ^Y- z&xSGB2j)Onc@Y|vH#(vndUkyP^!Y{|WQQc8`}8Te0v3Y4JFR)H$yVjHMzdx?y$Nf) zXl}X)mO{6(m;&^^@Fvh?BZbQ@0iE@o?k~d=kfkiAFn!jxJ80hX?bJv1*$BOV40-OU zHE%5dJp=o~MxLH}Xa4(8gy{F4_Jn@1kLJ$*14-v!l6EcEQNor`dK;nRj!=i9<*QWa zzx&Y1JVbuqzTNoa>ZV-jsrj>Lxr++70Jn{CI@;HUhKAbNMEAo?(6dHsuo`RtFNHNB zHosk${G`7DdLg=>^|@wZ`x+k)SC5fxC#CYTEBiC_J?giYnf1c z89Q-3>t{JdCYB>enaDmSo#kqrYzJeYw(IOR>3yIx&HLa7@IjD$TkPbGpJ%w%D_Kaz zvGy_71MbFzH4YX-x1w=84q6k3jVIZ$SE9a8V;`+8cLSYUCLJpzO(eRHG#3ZQlJ3b~ zNvwTP%C(*6>ieylH?+pgOSCrrIcR*Ex(~+Rt9vYsefB`l2cZc036-o1vXRD9uf|ZM zeLUSvA8IYBHDyuq0u}ua=oug#-T-$%(UQbI((x-rD@J{~k4M4u(ZAupABvPD_Spo5 zZYxqLtFBY<8~bSf+Xrs8u4n1hxtXvfiG8Hgb9SZz{WnpUv5#@}S;h}R>#eLrHa`Kh zcef>peLi7lDA@lr^%=X^u6`%sU*UXs96AxLyNq45&#J)$*pkFPN7)$)_UlOB73#I^ zWBfCT-vv&D-^1OYaiw*_a*)W?eWQCo<3npVoh2Oz^Fa5l%Bw*gY#Ae)m> zxH^A%6nY{-y#l(~dUort(ZG73kz<72a)Wx9c-)u3MbHxo>J=77v5stG%JTBpa67S& z>iz&c1%-=gL!K^;QX6Z~seRTU?^hwXJ|HSz$TY+8`oKFaImGwue2e+q@#$h09( zw^RGfoH^6j#<+7R>)Wst@{pG2WS(|0Wz!I~uLgCnWsHE^c9S;fn(LOU=jeaJt&o*y z9rz>oS+KyMK6!+xvt#>6A1{Bdaa1oZJZ)g3f1m28~B!`!W1Y5^PHzVJhs}K1MIE zEYlv_U5#t{OlLdLS@HpJ7#t4&4)24vg3bo@j#$%s0EAW67&M8gH&2%eRd)^QV2kw8 zwN%y5n>WwRWAbL`N}nc4rC5iFsZi~yK^<(7KDyRn$&GBLjI^$))XFD)n#82Z)1^Y$ zpaylYMf&Jkp4GQ4+vtEReVU|g<_r>0p=?ltI@lt8bS;BgIxoWLfGd5Pr1Mgf=cGc} zpaylYMf&Jk^1^aRX>`DqK24HCbuC3wp=?ltI@lt8bgd;%kr74*TtR}K3DwGXsPzPJ2kFG`LE#iXF0ayAoNrCm(sJOCEh{3Uo z#z&oD?Q=59tOa3v=_Xr|#trA8uPGb1 zZ1&Mv%>^Jk9RNC;m<6jqsD14CtZZeKSvLEinlCy7l)bKjGvP?kyL3ALiaM`pv0*1R zqkL0NneBs`O`@|uoeS%{&y2DC@oS9f94zcJTGP%Zx1G%>%cSoo`=EA$sq!bWuG8A^U|JC=M&mPYMrn3zV?<{|Cd5? zFh@&~G&E?Nu&~CL_KP~3Fk|dI{2F6A!_YpnaA|2w&Ji>)%r;?hIe&V>)v+^O&BMsm54pMN5={w#M1XT(jq~caTT#`)Z6`2@k?j zh?EjFw<}d>ps`01SDy{N9Nq|r!l`g0=!~pM#=YOg)F&Qe{NAN|(bF`_zfc#vG`4@# ziXY{hn+v@(qx^8?4CAjiepFMz(Q*AYV?#zgSB~x9*^0OQJ6iFb`o}55HV zpBx(+GihjEli%p0c-wFESN!N0g(0qg-1Rp6-j9m9@v0DU{i@LSQ$P>=t|DH&`t?+! zgZqP^CY}p^JY8-1hb=#;8|`vE{pI(1$kl(+(!cS;qm8ySetgvUmp4+;kI1+i(C?7$ z1*e1mJER8_h!HZY3&PO-srB4GpwCKv12@C{pnF@-j%VQkxCPDzrF#!-1}j5YxiQ-G z*QJQ|={quS1wCuEhu3}~={{cRlwR|v@~fP6pv8{;WvNzeL)tWrxVOO1;c>`9o}iFl zg349-O?GV6sy||a?I5i4B8?Z_i+PC3{Swrm?`;@p!&ZcU1usILqWSXIuqE`h4K4lk zy~|6XNckg`souV|p{Kjfmo(S)LR9Z8@NL*1GhahX^wz$140?5Tsd`nv^l)w1XZCvo zdQU(vrB3BK`*Q6kw^KRjpo9D}?D9JGx6^}iPk_9i7gP}|dY&usw=D7+hepT@65Az4>RG4&EKpEs*lvoqoHiYRK7H86jxY{#~fX*jB0jI(5 zKfzs)nzImYhq4u=(AkD*22GRazLr82tlQ!ULENQQEGHCDfG$g&h zluqgY3d;XBh}|=Bz2$6*xL(L@RJti7vY$z3xw`+fx6vLa%RSR?DNlB6xxY0Ii7(6e zyB%E;*^hL=`)Uqx8ZX-04mzxFWN%Ot%=J5bHdqGz1W zowf$shQ||L2ECTH_CrBiZ~0#ZMemvPyxqpqWqbU0La!yQ{j_KKS&x*~c-NjyeYgsI z7M_P*N*ep=JoXrPp+^eqemxdsJK0cc$e%zjCDwi@X5YK>3E$mIWvTlf*a?hYTj2j4 z^h(m$@6#x$`Mj5+`o9XsesVX5UqI3`dXjc5!S*M$-x}1Y`KJtW4NBPdlV6|zYHnE! zdB}Yfx(FT#6rR+6((l?p-d+XW&-$o5XhY4(7(*=h0!5sLxr`Ue=~d$u7f3zbe*bal=e{?9BYfuMUlG;z}ma>e$ zrh_*03bUc&hM)#-gG1q)p!1T;KyCR8$WE7l_KRPKL*Pv?8;)jKu;yAoq5I6SlOWl z-PliMX#d%({V!EFBriKfl}oKz*-zzc34ef|NmM)Yil?C++fO=q`E6G={5fd7l()1z zv+}Z2RJk;;EBi&*u!ud=ndtstu!Ex7nOA%k_KUEg_Dovq{|Fv|EJS;zAHpcS5^jSg z$;(br2nQx&!o>$gL|fdwV&)rd)uKkqxMEK!0ef{21>eT8fg2q8H?>~o8_#6rHBBU$kwKT&e7r#7fa= zD*Hv-Cdrk`eo3qpou;y1v~7}HsqB};O3`U5`$gL($(4S!pQX3I2VHyZwUabC(=?VI zW#9km^W5OO#lG#VcKC6b8Y-eNtKPEP?>Th+1Dp&8zz(2ux@q9sEY2Tcg07;i`pRxU zRQ2U~d=J6Za26a1yTN9lcScr%LTqZ)(@*w8?IzLZhW~&&;CwJ+bbI`%#wh8XRaRLg z@t9P4kAAivO=uA_Mz7*Z*cc6deqrp;;>H=1vQ7FyvtQ7l_82{niyEW*L+UYV+mrlL z#eOukMKnhL4jQBS{QC%aCul6`bECv#)XP)m&o5CichMMq3Su%zJ&=qagnF<5d#)_PPyL#YYtW(#ab*>K zA4c!a%WaY(Y-ZO%<^5>CCY#v-yKP1IEBke4<;s4Q{i^%dwm^6O?nYDk+|~V?-ez`= z%6_&r()&I8jo{t`eJ|-P&|MA;ruCn`Q+OS`0DUdpStEUJj3xW60G|ER=%ru(w@2M79rS;D ze7{DJiPz;7es>tS>4{soBHkSuCZ6axbnhi~T zpzt6|NPG2kego)S?O7;FRM&;DGfbI22((r6{GMGe6g8{U^ z&bf|*7ocpS_c}iZ(lPh(Z|dmO?PP3F*X!UmD62@fts%F2z%Dxrf0}-vzW;{jp{$~N zKl>%+2%615p-^Hm*-cUt}TMYKzTNY=@3~*m~Nz3nPrzhq2MfV+ZNW2xHyw9 z#MRl8)*f2x{s8WPCqVaHQc+rc9wa;d7*v+ZY}&sN4%MA_DR}C+Vg=w&{b{<1gzxl*s;8Thyj4OWYn*bNACb;2QhuIkh+30cDd;?EeuOQRX>S zy6GM;_Lr-@+PgsSC6r0h+J7~aKeJ2?h|uNYi2eC?r2h##6Ok{EXq^G*oF{BQp4R^B zQ_kgiD4B7YizDo>d*P+5en$#UMr}7RM>ipH}srJ z^U*8dXvq5gD~)q~7w@yM1*p$!FabeHYyTPrs87o-{(Gn9EDMEYhalR&Yc2gg(7dX3 z(qoWjZTC;oKMZ=;Y7uBJJ{RmW5sljk2ufP}>ob$ff`ybZZ0v5k=oz%X9bS{`o57pl z5cnGW0xkm0xw=n;+ONHjo+JMc4hHQ-)sL%#U8eDm5I+GyNo#+ttG^d4q>N$P?qPm( zHnxd!S0;_xtIv-#?lh)_{A+;r*SdeA^4swvq?>@Cq_w}w{1}vZ{pq$b%;sTnB)098 znO}8TWsl&WfS`0@|E(xQ^JJ+d%+_h*Xq;`oNPk?JRvBH{Uv*ez>HJ{~^xP?}#Mz~& zYZ%AaElc~WOqF**X^pRjmq4V3N7nXNxjRD0u}eV}K-yWturl^A_{>0%HwUNag0YKv8-@oS&A9sCTQfuclpT?LPYRoF|r zo4V1<*eEmmNA!W}Sr>G!bt9zNzmq(b=v+%@P^wGMAg!TW#Mq#hE=uKMf72$bd{dA1 zgRg@BfotI(kd-_|LFQabp8=^(ouN%x`^Vf1>IZYKbq4+{&$aa2H~p=5$ELLXWBS0@ zDDGVAkJwN5i3px+eVDj*=USEh&18g&y^>&`YpqY7xgPplM17U!T&uFbWq;d?9h3A$ z>NKjuD!a13ZS(|w%XYRMTl{fFS!GoAk26$G*DCwxWY~noR`yS5sGO2j_Rq<%35%`l zpU_Y_C9CY8lVKAUTiHLMp>j%A**_=4CM>qHe?mj$l&rFUPKHfbY-Rt1hRP{fW&fNE zo3Pl*{s|40Q?knbIT7bUmf!=RccAo6kM5Cofo!l2B)&(w?GNfQeLt1#PqUjOc#oXO)dOLB&^@B>P8mDJ zx@r5vDt9W|A2YOw?veXJ@5SnSdY^{fKx?d-ko5a%et(7a4ds~YmHjbBj4bDx)>yZ} zxuECB0?>10L+I!pAzx*GjL@MhCEsGubL4c;bL5?%dt?Ua{WxRac5XHPF;z7+N6d3%GyJzei-bKlijG4m zGj2xkPqZl6KN0%Qg}F!0#{XqF7#6_Y&>}_KI;=jF8Wj7t*q4asL46YXJ>)QZ6di|d zgKU4A7cE8GI;=jltLz_Un1bRe`xj*Bh-xbPM;NA{=*s>D89Jhx%Kj0CDJZ(Ke?f+h zsHU=igkcJbuIyisp(CmZ*uPGA%1DGs3XR6tX8$z@|23eq-zg!58akpf3~lVM@04!~ zI@i_bggV30d9Th-^f}_dNrXuXjmD_P{L+ZmvDOLV4t1e^{!>(!a>v(WcasG%b& z!-%%s!!)aM_I z;7ZVEhx&Y^)Y5Z9CsY6<8k@Is^;x;zPuw0306j>laqTB|gM%|MY9eWP`j-YeM-7Qh$bJkV#fdJm!PEv>(wuqW#DV z5Gw^VsjsWRcu}?wi`QC9-vika-V4XTd2kPeKGXFNcr?*Tmf+V-CKaCxZ!vIbilt>G5wiV=a1vKf2X;B9k&+G@7C`5-9{S!IQfl# z(it7|^pU-y{f)LX(lpxJ>TmozpGG{UMLhqw<*!?Qw@@z)Dz0SGPcCzo`~JzvKJdE( z)YH?^ZD9P)sO4vP`|*sz75*X1@6=YrJI>AxJIH9xbj$DA){P%Lzqz9{Hr&a8cDjD= zHgf%wb+`2m|BA6k*uBt*Z_EM4uk6OJdt!4K1BW|#$Qh9jY24NgfAYVStFq%*h3o6V zHn0bL7JdqUfa_rbo`B_`a{_(0OLON9Fb+S5G1wDShRRZzadkSYufBTnMpP*~vOr89 zsDE@Gv^$vdQvHtbeV{XJ^|Mf)i=dczlTLY*S7qp2>C3P;Xs-}E7CLHJl_}N&*gWdq zo58i$!;x?O8P?TtH zP@Uh0x4;_EG7gfpuu2tWfiRmAr_W-xg5%&82%F1^E<{wfbdWC6NxD_zAgV)C#E03D zIGvq-7%lHJckqiWx>5zO=aid!G_-jUYkP3sxLe!GJHG~8Z^=8hhGB! zGlDdBNt$-mRacGd2kJK0q=WuHxYk+kfWzP$@I%mheOi0n2zS7po?mh2!jC{_ai0R^ zRT(NvYj#t9sGFn>lravn)gX$_v2_2q_DN!ov}sJ)A+GvI_wc*nBsd4IgZrVA^A+V$ zUX`JD55EHYz=jYx7TUFkyc4h-22u1G(FkPP|E9bMS7#49z)_(4w}|&6j-(#Flc9Gr z+_nbYsBu8LM`2L=|61Cn`Lc-qU!8j145x$MRd^PPl4qz$b$%aqgS8;hI3V3)FsS{1 z18v*d*dpp4NE&a-3AlQ;YmDjK@h<4KE$is;Ign2N+6TWs9_;oKKLNeg85MGo%=#bg zeEV5`tt}1#J!gu%->EKEq{}5B-ChbUx|=cJr5n`sUncz@Wi(rp`vx6_9?;rq2{#Thvw+-$gKKbXYcz%y}!-jYOnLC1Hs-Ks(o4q z3_|;8hA&z5zt&FrJaJIuk2FO0ygkS1>`>2vr(jSdi~e5~V|*IQw7>7Rf3$Yh-gO$- zW8me4PY10P20=RNe;RJvq()fhJA)=y(IEe96u)WnCiu0+eGd9vvgrT4F~Xz$Zio>5 zwT?X$OuyT%_6XmEC!n9D+y2+O=y>SI{a(9%)0x0`t#e?z|K-PXrHKe3@(5&t$*}vx)A;g?uRJpw*Pg;|GTKd`#fHs`8{g- z-?+r7--En@xP@>d^zySJ)%{mE8njN)bLuXLl5YEdGYY;Us_+4bf7bN7acNVq?@d^H zh3(->a1#`{KGfW!v3C-@5zM}2Bm8;xzsA@g?fYp?lQe!@%5=WvlFIaZ&|QOT8V5(f zui-w(V@}ZerP3|2{u{>6Q2WKZiO7@m!Q=hwZpDkxpl{oPW&lq0<%@BUXk?}I@+ z|DjnJvO`hUBC*x9>Mf%GHD)h?ffU=WS^6pM|6Du zr|GY((^`E~ME`3nY!2fvaFTW36j2_XVD8VN`hO+r+!yX0v;j~=e`Q@q5Bgu@ZEYBX zCt+YD>%J+XJdB_F{jYXd^%#GRxbML8Fc4Bie`Q?8R#=py#o;`{nYP9R~lpNUugfg83Svg*C^<7FZDr@??>ot@VLaG z!u(br+5Hvi4=W=o&MLd=|C089RNrM0U-ds0Xo&W{x(D3*Dhu28Lm8&MUfZkvf7SmC zg>dPI@f_5VmYC<{Jp6Zsx6F;L?*HoiubTg3Zjj);)em8sQ{DeD9c9_4TK`w;ziRzg zt^XpQ05Q`@X)jsxTebcR=_SiP)%;)0|JD3o&HvT>&ls30QqBMVaIemPs`H=f{Kvl? zT0Q+ZCtCAcwf+z3K+8VW`oCKLSL^?3{ogwAdodx?N9m)i`K_A&Lwd`yPc{En^M5t} zSMz^0|9fM=nj1rA&cTSX+E~s1(f{|*yhG6BE?&I2?*~EE|NeA$H@p-yDG}Z5aeaBAUx4R6^0R4ZfJwe}Kj(z)8Q!49I+*=2ZhZM)K&FAM(D9I1vBN zpml_PCukT0-aeV z9S>FiW2`}%9=G8}O4<85Og|b^^?ziK z`WK$Yr{{*oMx=zDFeH@oM%)VuhtNK5#N7G#^dtZm?SM`6oK8{NrX5X^MRsA2=qv@`dy|2UctNK4( zAIGH*vv1kss{W7b(RA0!-q&IJRsEl?kK|6G@s{iA9G~Knb_jQ0=+k> zccb*3t$RT4OzGXJN-PVMy|2UcLw&p7qx^aYPVYwQ88HeA-~`Y+l-I)}@H|ZEIU>xy z{S-(4MfHDJyyghKL;Why`;z+p#dqOPFaeq)rqp=or@n5dD}5K%=W%hGBi4W#jKTuY z`_;b!eJA5_(04Qjc1*Oh=^$LE|Ki3#@S47ZrFFy>uqPY|T1V*n7<$)O-{Be<8HD~# z+6Fo;*w;zI`i|zBpm)u8f&<_r_$B-m9))M2FD0q%200xamt-8+Y4lxgeW!a%*b9z; z(?H+HxD#|9(HGB*LGIUX+eRPSeIDl@BCX~KeXm=;0io|_X&s?C;%3l!L@DQpZf!JG z^3#8D{U3HsbHqzPdkr&3EW&>&XdR*Punc-CQ>Fj%YA2l+*7sp?nj_W*ts`C!IVQbJ{<8U|~#$f{V9j;tI*O+SkRCIgk zzqm0Fc5McEMnIoydA2w#QVik4_8g@3=V45&FFc%@OlKXO=pT zSOnv6FKDmP@fXi`!P4b1E4wLMQFPREsG5* zJsK9E&w~BkF047ie6F!A{`bJ~a5iY45&Qk`$uZSSDkH8M3r%QpnH#S8;U$4^-3@CQ z(~xmDtRu6AjJaW{*bq191j8#7Xk2#|Hm7$+I|z?EsNt1YBR}4SR#+{WW`&huh3gd6?fN7mAY8|tpe z9~B;NB&nI}!EmGh9shbT?CIrS_uAoy8{=(7{z+A|niW<7J6K^x;OJ7vZ?8KJItUwk zn*7Gzk>N4)K>X|DZg54o9teAe@Y8#S@WY-0e%Ld_im(cpwW56Y&fCEXHw>{Ny?jgU z>s2_pJ-i9Xw;wY>lRdo|YQ^{%cedzi_?*n6_xZ0||$=Jv0zNZ{(jpD{0o2{+A8iX3kwFxSnKY(=<{ VU`4p0lohV41S{;;X@@oA{4WXULQVhx literal 0 HcmV?d00001 diff --git a/assets/images/tray_icon.png b/assets/images/tray_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..27b14684efb9051231e8e897e9e8d0a92988eba8 GIT binary patch literal 4312 zcmd6rXEb6obq_0D%$iGa<5Q(3HFW;OH@4Tl0sHG%=Jl(MgB zks$9Qo>zy_GKKW^l10;zt4voRGBv)qt#xB=^T-VzS(65d~w)DrVZRYuNy zPn~2UINSFD%PxoR*c5?j>90QYe6l^I*5%!f#Z86_$q^_&FLeTLmUilidGNgsS;$N; zIVMrt~6$ucj9Go5SM5=TQj=brrI2K2) z3C8>z2~Se4)zgC~^<_Z^ab}l)bF?51Xd!xIh=cEAKs+foxf&2B7?GF;Uk;j(nAimW z-{UIn!)cy3u?8{jN>%Yc@@@;%Co#>^f!?3XbkV<9iVL?sS&~pvdcB{BJa5M;MUKx% zK^zQqL5E*t_^8yaW1z&PruW|Pq!wdXQcG`3;f06nRm=y-T|x6^!0%fr;Ky)sF=4Aj zp=KJGv{L{UM|DgZ;D&pDHf=xGLAKhck@%p50<063MMNg~$GUXX{Vh~``;5Oj4|LGd z8REcjQqR1FqIq!B-JT3G6PtuJSJvZ?pIp{%I%R!QBp*n}?l6c33rec-2F#L=qhg?@ z+K3Rc%_lebrzu$Ev%N0sBr!R?68ZDWn4)4IJh6z6lNR^GwMhPa#L{756Q1;}Ga!JO z%zrH>AW>O8k!gYQA2m);M8!ZWIHdV+A+sz}VbB_hV*!bOv5bl>>VuX)kprX4kOT*H zkEW#kJFafz)181{uyHKu+=UKtWtzzu+z(yzT8jpE# zT#vA|CH{%dkKJ$9f7RWJUqZ4U_2w+}tsq7}xD?4RZAs;cw+Ru8y54i+Qc?Gf`P45( zUkJvAy5FMD*656IGZVJ0Zit}+XN6YGkdg}F8n z0{QqE2KjiUG2kn7(AMq~nejc`q+*k#DDjH>T%#$hn#)S15P^1e1zSvUAtUY=)Z^Yp zZIde)ZfxCJhxqSQb+l{RH87xL7avP__UQv?c;3SJ%KtJ;zo}^M*vE!M@Te8cYw4W? zB}Kg$B`VC#fl%uU5ZW>fLjO4n z4jF$d{a3y_=&wE+^k?Y^LgB&QHl!2E-%5u|K=~ z2z~k-g#O>zzbtlS)yqJm9Hp?!qw37_z2a}xYv^6YPLaZ%0Ac8$sZE!?`@>Gcc6#5w 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 with WidgetsBindingObserver { late Future _initFuture; + late SystemTray _tray; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); + _tray = SystemTray(); + _tray.init(); _initFuture = _init(); } diff --git a/lib/app/view/system_tray.dart b/lib/app/view/system_tray.dart new file mode 100644 index 000000000..edc5f167c --- /dev/null +++ b/lib/app/view/system_tray.dart @@ -0,0 +1,40 @@ +import 'package:tray_manager/tray_manager.dart'; +import 'package:window_manager/window_manager.dart'; + +import '../../persistence_utils.dart'; + +class SystemTray with TrayListener { + Future init() async { + trayManager.addListener(this); + await trayManager.setIcon(trayIcon()); + await trayManager.setContextMenu(Menu(items: trayMenuItems)); + } + + // KDE@Arch Linux, this feature does not work. + // @override + // void onTrayIconMouseDown() { + // print('onTrayIconMouseDown'); + // } + + // KDE@Arch Linux, this feature does not work. + // @override + // void onTrayIconRightMouseDown() { + // print('onTrayIconRightMouseDown'); + // } + + @override + void onTrayMenuItemClick(MenuItem menuItem) { + switch (menuItem.key) { + case 'restore_window': + windowManager.isVisible().then((value) { + if (value) { + windowManager.hide(); + } else { + windowManager.show(); + } + }); + case 'close_application': + windowManager.close(); + } + } +} diff --git a/lib/persistence_utils.dart b/lib/persistence_utils.dart index d8373df50..47c53b01c 100644 --- a/lib/persistence_utils.dart +++ b/lib/persistence_utils.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; +import 'package:tray_manager/tray_manager.dart'; import 'package:xdg_directories/xdg_directories.dart'; import 'constants.dart'; @@ -278,3 +279,24 @@ Future?> readUint8ListMap(String fileName) async { return theMap; } + +/// TODO: how to l10n labels? +List trayMenuItems = [ + MenuItem( + key: 'restore_window', + label: 'Hide/Restore', + ), + MenuItem.separator(), + MenuItem( + key: 'close_application', + label: 'Close Application', + ), +]; + +String trayIcon() { + if (Platform.isWindows) { + return 'assets/images/tray_icon.ico'; + } else { + return 'assets/images/tray_icon.png'; + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 1af123b9b..6bc70a71e 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) system_theme_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "SystemThemePlugin"); system_theme_plugin_register_with_registrar(system_theme_registrar); + g_autoptr(FlPluginRegistrar) tray_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); + tray_manager_plugin_register_with_registrar(tray_manager_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index d52676c1e..a5d30a295 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -12,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST screen_retriever super_native_extensions system_theme + tray_manager url_launcher_linux window_manager yaru_window_linux diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e4b5885aa..96bd53b63 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -20,6 +20,7 @@ import screen_retriever import sqflite import super_native_extensions import system_theme +import tray_manager import url_launcher_macos import wakelock_plus import window_manager @@ -40,6 +41,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin")) + TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) diff --git a/pubspec.lock b/pubspec.lock index a38fd07c7..605fbc6a0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -880,6 +880,14 @@ packages: url: "https://github.com/media-kit/media-kit" source: git version: "1.2.4" + menu_base: + dependency: transitive + description: + name: menu_base + sha256: "820368014a171bd1241030278e6c2617354f492f5c703d7b7d4570a6b8b84405" + url: "https://pub.dev" + source: hosted + version: "0.1.1" meta: dependency: transitive description: @@ -1265,6 +1273,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + shortid: + dependency: transitive + description: + name: shortid + sha256: d0b40e3dbb50497dad107e19c54ca7de0d1a274eb9b4404991e443dadb9ebedb + url: "https://pub.dev" + source: hosted + version: "0.1.2" sky_engine: dependency: transitive description: flutter @@ -1430,6 +1446,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + tray_manager: + dependency: "direct main" + description: + name: tray_manager + sha256: c9a63fd88bd3546287a7eb8ccc978d707eef82c775397af17dda3a4f4c039e64 + url: "https://pub.dev" + source: hosted + version: "0.2.3" tuple: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6b2a40375..4703ddfac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -69,6 +69,7 @@ dependencies: smtc_windows: ^0.1.2 super_drag_and_drop: ^0.8.15 system_theme: ^2.3.1 + tray_manager: ^0.2.3 url_launcher: ^6.2.5 watch_it: ^1.4.1 window_manager: ^0.3.8 @@ -92,6 +93,8 @@ flutter: assets: - snap/gui/musicpod.png - assets/images/media-optical.png + - assets/images/tray_icon.ico + - assets/images/tray_icon.png dependency_overrides: intl: 0.19.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index b34aa2b54..820f3252e 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi")); SystemThemePluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("SystemThemePlugin")); + TrayManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("TrayManagerPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowManagerPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 8b941b22e..f2b18ea3c 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -12,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST screen_retriever super_native_extensions system_theme + tray_manager url_launcher_windows window_manager )