From 96fd5b5dca98e274faacd1079a4e28374b92587f Mon Sep 17 00:00:00 2001 From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com> Date: Mon, 28 Nov 2022 16:19:21 -0400 Subject: [PATCH] Android VPN client --- cmd/skywirevisormobile/android/README.md | 66 ++ .../android/app/build.gradle | 61 ++ .../android/app/src/main/AndroidManifest.xml | 54 ++ .../java/com/skywire/skycoin/vpn/App.java | 118 ++++ .../com/skywire/skycoin/vpn/Receiver.java | 23 + .../vpn/activities/apps/AppListButton.java | 124 ++++ .../activities/apps/AppListOptionButton.java | 62 ++ .../vpn/activities/apps/AppListRow.java | 115 +++ .../vpn/activities/apps/AppListSeparator.java | 29 + .../vpn/activities/apps/AppsActivity.java | 50 ++ .../vpn/activities/apps/AppsAdapter.java | 339 +++++++++ .../vpn/activities/index/IndexActivity.java | 185 +++++ .../activities/index/IndexPageAdapter.java | 49 ++ .../vpn/activities/main/MainActivity.java | 303 ++++++++ .../activities/servers/ConditionsList.java | 147 ++++ .../activities/servers/FilterModalWindow.java | 167 +++++ .../activities/servers/ServerListButton.java | 176 +++++ .../servers/ServerListOptionButton.java | 53 ++ .../activities/servers/ServerListOptions.java | 121 ++++ .../servers/ServerListTableHeader.java | 45 ++ .../servers/ServerListTableRow.java | 162 +++++ .../activities/servers/ServerListTopTab.java | 94 +++ .../vpn/activities/servers/ServerLists.java | 8 + .../activities/servers/ServersActivity.java | 437 ++++++++++++ .../activities/servers/VpnServerForList.java | 32 + .../activities/servers/VpnServersAdapter.java | 559 +++++++++++++++ .../settings/CustomDnsModalWindow.java | 108 +++ .../activities/settings/SettingsActivity.java | 196 ++++++ .../activities/settings/SettingsOption.java | 106 +++ .../vpn/activities/start/MapBackground.java | 139 ++++ .../vpn/activities/start/StartActivity.java | 297 ++++++++ .../activities/start/StartViewRightPanel.java | 329 +++++++++ .../vpn/activities/start/connected/Chart.java | 158 +++++ .../start/connected/StartViewConnected.java | 509 ++++++++++++++ .../start/connected/StopButton.java | 89 +++ .../disconnected/CurrentServerButton.java | 89 +++ .../start/disconnected/StartButton.java | 95 +++ .../disconnected/StartViewDisconnected.java | 156 +++++ .../vpn/controls/BoxRowBackground.java | 66 ++ .../skycoin/vpn/controls/BoxRowLayout.java | 219 ++++++ .../skycoin/vpn/controls/BoxRowRipple.java | 68 ++ .../vpn/controls/ClickableLinearLayout.java | 66 ++ .../vpn/controls/ConfirmationModalWindow.java | 65 ++ .../controls/EditServerValueModalWindow.java | 122 ++++ .../vpn/controls/ManualServerModalWindow.java | 154 ++++ .../skycoin/vpn/controls/ModalBase.java | 115 +++ .../vpn/controls/ModalWindowButton.java | 93 +++ .../skywire/skycoin/vpn/controls/Select.java | 143 ++++ .../vpn/controls/ServerInfoModalWindow.java | 276 ++++++++ .../skycoin/vpn/controls/ServerName.java | 151 ++++ .../vpn/controls/ServerNotesModalWindow.java | 69 ++ .../controls/ServerPasswordModalWindow.java | 101 +++ .../skycoin/vpn/controls/SettingsButton.java | 63 ++ .../com/skywire/skycoin/vpn/controls/Tab.java | 96 +++ .../skycoin/vpn/controls/TabletTopBar.java | 119 ++++ .../vpn/controls/TabletTopBarStats.java | 148 ++++ .../skycoin/vpn/controls/TabletTopBarTab.java | 94 +++ .../skywire/skycoin/vpn/controls/TopBar.java | 94 +++ .../skycoin/vpn/controls/TopBarButton.java | 60 ++ .../skywire/skycoin/vpn/controls/TopTab.java | 22 + .../vpn/controls/options/OptionsItem.java | 116 +++ .../controls/options/OptionsModalWindow.java | 69 ++ .../skycoin/vpn/extensible/ButtonBase.java | 71 ++ .../skycoin/vpn/extensible/ClickEvent.java | 7 + .../vpn/extensible/ClickWithIndexEvent.java | 5 + .../vpn/extensible/ListButtonBase.java | 81 +++ .../vpn/extensible/ListViewHolder.java | 11 + .../skycoin/vpn/helpers/AlphaSpan.java | 24 + .../skycoin/vpn/helpers/BoxRowTypes.java | 8 + .../vpn/helpers/ClickTimeManagement.java | 40 ++ .../skycoin/vpn/helpers/CountriesList.java | 267 +++++++ .../skywire/skycoin/vpn/helpers/Globals.java | 70 ++ .../skycoin/vpn/helpers/HelperFunctions.java | 662 ++++++++++++++++++ .../skycoin/vpn/helpers/MaterialFontSpan.java | 32 + .../skycoin/vpn/helpers/Notifications.java | 162 +++++ .../skycoin/vpn/helpers/UiMaterialIcons.java | 6 + .../skycoin/vpn/network/ApiClient.java | 69 ++ .../vpn/network/models/GeoInfoModel.java | 8 + .../skycoin/vpn/network/models/IpModel.java | 5 + .../vpn/network/models/VpnServerModel.java | 6 + .../skycoin/vpn/objects/LocalServerData.java | 18 + .../vpn/objects/ManualVpnServerData.java | 8 + .../skycoin/vpn/objects/ServerFlags.java | 7 + .../skycoin/vpn/objects/ServerRatings.java | 38 + .../skycoin/vpn/vpn/SkywireVPNConnection.java | 312 +++++++++ .../skycoin/vpn/vpn/SkywireVPNService.java | 508 ++++++++++++++ .../skycoin/vpn/vpn/VPNCoordinator.java | 315 +++++++++ .../skycoin/vpn/vpn/VPNDataManager.java | 85 +++ .../vpn/vpn/VPNGeneralPersistentData.java | 238 +++++++ .../skywire/skycoin/vpn/vpn/VPNRunnable.java | 354 ++++++++++ .../vpn/vpn/VPNServersPersistentData.java | 322 +++++++++ .../skywire/skycoin/vpn/vpn/VPNStates.java | 267 +++++++ .../skycoin/vpn/vpn/VPNWorkInterface.java | 242 +++++++ .../skycoin/vpn/vpn/VisorRunnable.java | 218 ++++++ .../main/res/animator/anim_start_button.xml | 58 ++ .../app/src/main/res/animator/anim_state.xml | 32 + .../main/res/drawable-hdpi/bronze_rating.png | Bin 0 -> 1950 bytes .../main/res/drawable-hdpi/gold_rating.png | Bin 0 -> 2314 bytes .../modal_background_pattern.png | Bin 0 -> 957 bytes .../main/res/drawable-hdpi/silver_rating.png | Bin 0 -> 1905 bytes .../res/drawable-xhdpi/background_box1.9.png | Bin 0 -> 5965 bytes .../res/drawable-xhdpi/background_box2.9.png | Bin 0 -> 1231 bytes .../res/drawable-xhdpi/background_box3.9.png | Bin 0 -> 6732 bytes .../res/drawable-xhdpi/background_box4.9.png | Bin 0 -> 11278 bytes .../res/drawable-xhdpi/background_box5.9.png | Bin 0 -> 10298 bytes .../main/res/drawable-xhdpi/box_pattern.png | Bin 0 -> 989 bytes .../src/main/res/drawable-xhdpi/logo_vpn.png | Bin 0 -> 3840 bytes .../main/res/drawable-xhdpi/map_phones.png | Bin 0 -> 16857 bytes .../src/main/res/drawable-xhdpi/red_btn.9.png | Bin 0 -> 7024 bytes .../main/res/drawable-xhdpi/select_arrow.png | Bin 0 -> 1179 bytes .../src/main/res/drawable-xhdpi/start_btn.png | Bin 0 -> 19755 bytes .../app/src/main/res/drawable-xxhdpi/ab.png | Bin 0 -> 846 bytes .../app/src/main/res/drawable-xxhdpi/ad.png | Bin 0 -> 3061 bytes .../app/src/main/res/drawable-xxhdpi/ae.png | Bin 0 -> 234 bytes .../app/src/main/res/drawable-xxhdpi/af.png | Bin 0 -> 2987 bytes .../app/src/main/res/drawable-xxhdpi/ag.png | Bin 0 -> 1766 bytes .../app/src/main/res/drawable-xxhdpi/ai.png | Bin 0 -> 3142 bytes .../app/src/main/res/drawable-xxhdpi/al.png | Bin 0 -> 1872 bytes .../app/src/main/res/drawable-xxhdpi/am.png | Bin 0 -> 378 bytes .../app/src/main/res/drawable-xxhdpi/ao.png | Bin 0 -> 1307 bytes .../app/src/main/res/drawable-xxhdpi/aq.png | Bin 0 -> 1048 bytes .../app/src/main/res/drawable-xxhdpi/ar.png | Bin 0 -> 1875 bytes .../app/src/main/res/drawable-xxhdpi/as.png | Bin 0 -> 3608 bytes .../app/src/main/res/drawable-xxhdpi/at.png | Bin 0 -> 152 bytes .../app/src/main/res/drawable-xxhdpi/au.png | Bin 0 -> 2904 bytes .../app/src/main/res/drawable-xxhdpi/aw.png | Bin 0 -> 814 bytes .../app/src/main/res/drawable-xxhdpi/ax.png | Bin 0 -> 341 bytes .../app/src/main/res/drawable-xxhdpi/az.png | Bin 0 -> 1556 bytes .../app/src/main/res/drawable-xxhdpi/ba.png | Bin 0 -> 2006 bytes .../app/src/main/res/drawable-xxhdpi/bb.png | Bin 0 -> 1107 bytes .../app/src/main/res/drawable-xxhdpi/bd.png | Bin 0 -> 538 bytes .../app/src/main/res/drawable-xxhdpi/be.png | Bin 0 -> 376 bytes .../app/src/main/res/drawable-xxhdpi/bf.png | Bin 0 -> 1107 bytes .../app/src/main/res/drawable-xxhdpi/bg.png | Bin 0 -> 378 bytes .../app/src/main/res/drawable-xxhdpi/bh.png | Bin 0 -> 450 bytes .../app/src/main/res/drawable-xxhdpi/bi.png | Bin 0 -> 1799 bytes .../app/src/main/res/drawable-xxhdpi/bj.png | Bin 0 -> 398 bytes .../app/src/main/res/drawable-xxhdpi/bl.png | Bin 0 -> 7828 bytes .../app/src/main/res/drawable-xxhdpi/bm.png | Bin 0 -> 4400 bytes .../app/src/main/res/drawable-xxhdpi/bn.png | Bin 0 -> 4561 bytes .../app/src/main/res/drawable-xxhdpi/bo.png | Bin 0 -> 396 bytes .../app/src/main/res/drawable-xxhdpi/bq.png | Bin 0 -> 378 bytes .../app/src/main/res/drawable-xxhdpi/br.png | Bin 0 -> 2578 bytes .../app/src/main/res/drawable-xxhdpi/bs.png | Bin 0 -> 1245 bytes .../app/src/main/res/drawable-xxhdpi/bt.png | Bin 0 -> 4500 bytes .../app/src/main/res/drawable-xxhdpi/bv.png | Bin 0 -> 616 bytes .../app/src/main/res/drawable-xxhdpi/bw.png | Bin 0 -> 387 bytes .../app/src/main/res/drawable-xxhdpi/by.png | Bin 0 -> 1504 bytes .../app/src/main/res/drawable-xxhdpi/bz.png | Bin 0 -> 3125 bytes .../app/src/main/res/drawable-xxhdpi/ca.png | Bin 0 -> 1044 bytes .../app/src/main/res/drawable-xxhdpi/cc.png | Bin 0 -> 3051 bytes .../app/src/main/res/drawable-xxhdpi/cd.png | Bin 0 -> 2653 bytes .../app/src/main/res/drawable-xxhdpi/cf.png | Bin 0 -> 611 bytes .../app/src/main/res/drawable-xxhdpi/cg.png | Bin 0 -> 1122 bytes .../app/src/main/res/drawable-xxhdpi/ch.png | Bin 0 -> 232 bytes .../app/src/main/res/drawable-xxhdpi/ci.png | Bin 0 -> 379 bytes .../app/src/main/res/drawable-xxhdpi/ck.png | Bin 0 -> 2986 bytes .../app/src/main/res/drawable-xxhdpi/cl.png | Bin 0 -> 631 bytes .../app/src/main/res/drawable-xxhdpi/cm.png | Bin 0 -> 763 bytes .../app/src/main/res/drawable-xxhdpi/cn.png | Bin 0 -> 782 bytes .../app/src/main/res/drawable-xxhdpi/co.png | Bin 0 -> 377 bytes .../app/src/main/res/drawable-xxhdpi/cr.png | Bin 0 -> 183 bytes .../app/src/main/res/drawable-xxhdpi/cu.png | Bin 0 -> 1439 bytes .../app/src/main/res/drawable-xxhdpi/cv.png | Bin 0 -> 1562 bytes .../app/src/main/res/drawable-xxhdpi/cw.png | Bin 0 -> 653 bytes .../app/src/main/res/drawable-xxhdpi/cx.png | Bin 0 -> 2698 bytes .../app/src/main/res/drawable-xxhdpi/cy.png | Bin 0 -> 1949 bytes .../app/src/main/res/drawable-xxhdpi/cz.png | Bin 0 -> 885 bytes .../app/src/main/res/drawable-xxhdpi/de.png | Bin 0 -> 375 bytes .../app/src/main/res/drawable-xxhdpi/dj.png | Bin 0 -> 1150 bytes .../app/src/main/res/drawable-xxhdpi/dk.png | Bin 0 -> 257 bytes .../app/src/main/res/drawable-xxhdpi/dm.png | Bin 0 -> 2232 bytes .../src/main/res/drawable-xxhdpi/do_flag.png | Bin 0 -> 777 bytes .../app/src/main/res/drawable-xxhdpi/dz.png | Bin 0 -> 1530 bytes .../app/src/main/res/drawable-xxhdpi/ec.png | Bin 0 -> 4071 bytes .../app/src/main/res/drawable-xxhdpi/ee.png | Bin 0 -> 375 bytes .../app/src/main/res/drawable-xxhdpi/eg.png | Bin 0 -> 914 bytes .../app/src/main/res/drawable-xxhdpi/eh.png | Bin 0 -> 1274 bytes .../app/src/main/res/drawable-xxhdpi/er.png | Bin 0 -> 3413 bytes .../app/src/main/res/drawable-xxhdpi/es.png | Bin 0 -> 3364 bytes .../app/src/main/res/drawable-xxhdpi/et.png | Bin 0 -> 1603 bytes .../app/src/main/res/drawable-xxhdpi/fi.png | Bin 0 -> 484 bytes .../app/src/main/res/drawable-xxhdpi/fj.png | Bin 0 -> 3973 bytes .../app/src/main/res/drawable-xxhdpi/fk.png | Bin 0 -> 5415 bytes .../app/src/main/res/drawable-xxhdpi/fm.png | Bin 0 -> 930 bytes .../app/src/main/res/drawable-xxhdpi/fo.png | Bin 0 -> 302 bytes .../app/src/main/res/drawable-xxhdpi/fr.png | Bin 0 -> 363 bytes .../app/src/main/res/drawable-xxhdpi/ga.png | Bin 0 -> 378 bytes .../app/src/main/res/drawable-xxhdpi/gb.png | Bin 0 -> 1651 bytes .../app/src/main/res/drawable-xxhdpi/gd.png | Bin 0 -> 1790 bytes .../app/src/main/res/drawable-xxhdpi/ge.png | Bin 0 -> 919 bytes .../app/src/main/res/drawable-xxhdpi/gf.png | Bin 0 -> 1345 bytes .../app/src/main/res/drawable-xxhdpi/gg.png | Bin 0 -> 373 bytes .../app/src/main/res/drawable-xxhdpi/gi.png | Bin 0 -> 2665 bytes .../app/src/main/res/drawable-xxhdpi/gl.png | Bin 0 -> 890 bytes .../app/src/main/res/drawable-xxhdpi/gm.png | Bin 0 -> 424 bytes .../app/src/main/res/drawable-xxhdpi/gn.png | Bin 0 -> 379 bytes .../app/src/main/res/drawable-xxhdpi/gp.png | Bin 0 -> 3437 bytes .../app/src/main/res/drawable-xxhdpi/gq.png | Bin 0 -> 1321 bytes .../app/src/main/res/drawable-xxhdpi/gr.png | Bin 0 -> 486 bytes .../app/src/main/res/drawable-xxhdpi/gs.png | Bin 0 -> 5719 bytes .../app/src/main/res/drawable-xxhdpi/gt.png | Bin 0 -> 2745 bytes .../app/src/main/res/drawable-xxhdpi/gu.png | Bin 0 -> 2915 bytes .../app/src/main/res/drawable-xxhdpi/gw.png | Bin 0 -> 1080 bytes .../app/src/main/res/drawable-xxhdpi/gy.png | Bin 0 -> 2852 bytes .../app/src/main/res/drawable-xxhdpi/hk.png | Bin 0 -> 1369 bytes .../app/src/main/res/drawable-xxhdpi/hm.png | Bin 0 -> 2847 bytes .../app/src/main/res/drawable-xxhdpi/hn.png | Bin 0 -> 811 bytes .../app/src/main/res/drawable-xxhdpi/hr.png | Bin 0 -> 2233 bytes .../app/src/main/res/drawable-xxhdpi/ht.png | Bin 0 -> 369 bytes .../app/src/main/res/drawable-xxhdpi/hu.png | Bin 0 -> 388 bytes .../app/src/main/res/drawable-xxhdpi/id.png | Bin 0 -> 382 bytes .../app/src/main/res/drawable-xxhdpi/ie.png | Bin 0 -> 363 bytes .../app/src/main/res/drawable-xxhdpi/il.png | Bin 0 -> 1111 bytes .../app/src/main/res/drawable-xxhdpi/im.png | Bin 0 -> 2354 bytes .../app/src/main/res/drawable-xxhdpi/in.png | Bin 0 -> 2250 bytes .../app/src/main/res/drawable-xxhdpi/io.png | Bin 0 -> 6741 bytes .../app/src/main/res/drawable-xxhdpi/iq.png | Bin 0 -> 1703 bytes .../app/src/main/res/drawable-xxhdpi/ir.png | Bin 0 -> 2316 bytes .../app/src/main/res/drawable-xxhdpi/is.png | Bin 0 -> 284 bytes .../app/src/main/res/drawable-xxhdpi/it.png | Bin 0 -> 363 bytes .../app/src/main/res/drawable-xxhdpi/je.png | Bin 0 -> 2028 bytes .../app/src/main/res/drawable-xxhdpi/jm.png | Bin 0 -> 1307 bytes .../app/src/main/res/drawable-xxhdpi/jo.png | Bin 0 -> 1030 bytes .../app/src/main/res/drawable-xxhdpi/jp.png | Bin 0 -> 495 bytes .../app/src/main/res/drawable-xxhdpi/ke.png | Bin 0 -> 2158 bytes .../app/src/main/res/drawable-xxhdpi/kg.png | Bin 0 -> 2375 bytes .../app/src/main/res/drawable-xxhdpi/kh.png | Bin 0 -> 1502 bytes .../app/src/main/res/drawable-xxhdpi/ki.png | Bin 0 -> 4026 bytes .../app/src/main/res/drawable-xxhdpi/km.png | Bin 0 -> 1664 bytes .../app/src/main/res/drawable-xxhdpi/kn.png | Bin 0 -> 2572 bytes .../app/src/main/res/drawable-xxhdpi/kp.png | Bin 0 -> 1126 bytes .../app/src/main/res/drawable-xxhdpi/kr.png | Bin 0 -> 3236 bytes .../app/src/main/res/drawable-xxhdpi/kw.png | Bin 0 -> 751 bytes .../app/src/main/res/drawable-xxhdpi/ky.png | Bin 0 -> 4738 bytes .../app/src/main/res/drawable-xxhdpi/kz.png | Bin 0 -> 3588 bytes .../app/src/main/res/drawable-xxhdpi/la.png | Bin 0 -> 440 bytes .../app/src/main/res/drawable-xxhdpi/lb.png | Bin 0 -> 1431 bytes .../app/src/main/res/drawable-xxhdpi/lc.png | Bin 0 -> 1792 bytes .../app/src/main/res/drawable-xxhdpi/li.png | Bin 0 -> 1533 bytes .../app/src/main/res/drawable-xxhdpi/lk.png | Bin 0 -> 1848 bytes .../app/src/main/res/drawable-xxhdpi/lr.png | Bin 0 -> 787 bytes .../app/src/main/res/drawable-xxhdpi/ls.png | Bin 0 -> 1100 bytes .../app/src/main/res/drawable-xxhdpi/lt.png | Bin 0 -> 373 bytes .../app/src/main/res/drawable-xxhdpi/lu.png | Bin 0 -> 388 bytes .../app/src/main/res/drawable-xxhdpi/lv.png | Bin 0 -> 360 bytes .../app/src/main/res/drawable-xxhdpi/ly.png | Bin 0 -> 469 bytes .../app/src/main/res/drawable-xxhdpi/ma.png | Bin 0 -> 1588 bytes .../app/src/main/res/drawable-xxhdpi/mc.png | Bin 0 -> 354 bytes .../app/src/main/res/drawable-xxhdpi/md.png | Bin 0 -> 3024 bytes .../app/src/main/res/drawable-xxhdpi/me.png | Bin 0 -> 3635 bytes .../app/src/main/res/drawable-xxhdpi/mf.png | Bin 0 -> 2458 bytes .../app/src/main/res/drawable-xxhdpi/mg.png | Bin 0 -> 415 bytes .../app/src/main/res/drawable-xxhdpi/mh.png | Bin 0 -> 3230 bytes .../app/src/main/res/drawable-xxhdpi/mk.png | Bin 0 -> 2129 bytes .../app/src/main/res/drawable-xxhdpi/ml.png | Bin 0 -> 377 bytes .../app/src/main/res/drawable-xxhdpi/mm.png | Bin 0 -> 935 bytes .../app/src/main/res/drawable-xxhdpi/mn.png | Bin 0 -> 865 bytes .../app/src/main/res/drawable-xxhdpi/mo.png | Bin 0 -> 1673 bytes .../app/src/main/res/drawable-xxhdpi/mp.png | Bin 0 -> 4595 bytes .../app/src/main/res/drawable-xxhdpi/mq.png | Bin 0 -> 2714 bytes .../app/src/main/res/drawable-xxhdpi/mr.png | Bin 0 -> 1122 bytes .../app/src/main/res/drawable-xxhdpi/ms.png | Bin 0 -> 3535 bytes .../app/src/main/res/drawable-xxhdpi/mt.png | Bin 0 -> 806 bytes .../app/src/main/res/drawable-xxhdpi/mu.png | Bin 0 -> 400 bytes .../app/src/main/res/drawable-xxhdpi/mv.png | Bin 0 -> 979 bytes .../app/src/main/res/drawable-xxhdpi/mw.png | Bin 0 -> 1215 bytes .../app/src/main/res/drawable-xxhdpi/mx.png | Bin 0 -> 2131 bytes .../app/src/main/res/drawable-xxhdpi/my.png | Bin 0 -> 964 bytes .../app/src/main/res/drawable-xxhdpi/mz.png | Bin 0 -> 1340 bytes .../app/src/main/res/drawable-xxhdpi/na.png | Bin 0 -> 2764 bytes .../app/src/main/res/drawable-xxhdpi/nc.png | Bin 0 -> 1251 bytes .../app/src/main/res/drawable-xxhdpi/ne.png | Bin 0 -> 512 bytes .../app/src/main/res/drawable-xxhdpi/nf.png | Bin 0 -> 1955 bytes .../app/src/main/res/drawable-xxhdpi/ng.png | Bin 0 -> 359 bytes .../app/src/main/res/drawable-xxhdpi/ni.png | Bin 0 -> 2227 bytes .../app/src/main/res/drawable-xxhdpi/nl.png | Bin 0 -> 378 bytes .../app/src/main/res/drawable-xxhdpi/no.png | Bin 0 -> 604 bytes .../app/src/main/res/drawable-xxhdpi/np.png | Bin 0 -> 2191 bytes .../app/src/main/res/drawable-xxhdpi/nr.png | Bin 0 -> 835 bytes .../app/src/main/res/drawable-xxhdpi/nu.png | Bin 0 -> 3811 bytes .../app/src/main/res/drawable-xxhdpi/nz.png | Bin 0 -> 2863 bytes .../app/src/main/res/drawable-xxhdpi/om.png | Bin 0 -> 984 bytes .../app/src/main/res/drawable-xxhdpi/pa.png | Bin 0 -> 975 bytes .../app/src/main/res/drawable-xxhdpi/pe.png | Bin 0 -> 373 bytes .../app/src/main/res/drawable-xxhdpi/pf.png | Bin 0 -> 2097 bytes .../app/src/main/res/drawable-xxhdpi/pg.png | Bin 0 -> 1837 bytes .../app/src/main/res/drawable-xxhdpi/ph.png | Bin 0 -> 2181 bytes .../app/src/main/res/drawable-xxhdpi/pk.png | Bin 0 -> 988 bytes .../app/src/main/res/drawable-xxhdpi/pl.png | Bin 0 -> 359 bytes .../app/src/main/res/drawable-xxhdpi/pm.png | Bin 0 -> 7839 bytes .../app/src/main/res/drawable-xxhdpi/pn.png | Bin 0 -> 5116 bytes .../app/src/main/res/drawable-xxhdpi/pr.png | Bin 0 -> 1496 bytes .../app/src/main/res/drawable-xxhdpi/ps.png | Bin 0 -> 653 bytes .../app/src/main/res/drawable-xxhdpi/pt.png | Bin 0 -> 2539 bytes .../app/src/main/res/drawable-xxhdpi/pw.png | Bin 0 -> 876 bytes .../app/src/main/res/drawable-xxhdpi/py.png | Bin 0 -> 826 bytes .../app/src/main/res/drawable-xxhdpi/qa.png | Bin 0 -> 416 bytes .../app/src/main/res/drawable-xxhdpi/re.png | Bin 0 -> 363 bytes .../app/src/main/res/drawable-xxhdpi/ro.png | Bin 0 -> 379 bytes .../app/src/main/res/drawable-xxhdpi/rs.png | Bin 0 -> 2661 bytes .../app/src/main/res/drawable-xxhdpi/ru.png | Bin 0 -> 378 bytes .../app/src/main/res/drawable-xxhdpi/rw.png | Bin 0 -> 1005 bytes .../app/src/main/res/drawable-xxhdpi/sa.png | Bin 0 -> 2353 bytes .../app/src/main/res/drawable-xxhdpi/sb.png | Bin 0 -> 1788 bytes .../app/src/main/res/drawable-xxhdpi/sc.png | Bin 0 -> 2142 bytes .../app/src/main/res/drawable-xxhdpi/sd.png | Bin 0 -> 832 bytes .../app/src/main/res/drawable-xxhdpi/se.png | Bin 0 -> 341 bytes .../app/src/main/res/drawable-xxhdpi/sg.png | Bin 0 -> 917 bytes .../app/src/main/res/drawable-xxhdpi/sh.png | Bin 0 -> 4273 bytes .../app/src/main/res/drawable-xxhdpi/si.png | Bin 0 -> 1025 bytes .../app/src/main/res/drawable-xxhdpi/sj.png | Bin 0 -> 597 bytes .../app/src/main/res/drawable-xxhdpi/sk.png | Bin 0 -> 1422 bytes .../app/src/main/res/drawable-xxhdpi/sl.png | Bin 0 -> 397 bytes .../app/src/main/res/drawable-xxhdpi/sm.png | Bin 0 -> 2843 bytes .../app/src/main/res/drawable-xxhdpi/sn.png | Bin 0 -> 694 bytes .../app/src/main/res/drawable-xxhdpi/so.png | Bin 0 -> 772 bytes .../app/src/main/res/drawable-xxhdpi/sr.png | Bin 0 -> 733 bytes .../app/src/main/res/drawable-xxhdpi/ss.png | Bin 0 -> 1170 bytes .../app/src/main/res/drawable-xxhdpi/st.png | Bin 0 -> 1349 bytes .../app/src/main/res/drawable-xxhdpi/sv.png | Bin 0 -> 2893 bytes .../app/src/main/res/drawable-xxhdpi/sx.png | Bin 0 -> 2472 bytes .../app/src/main/res/drawable-xxhdpi/sy.png | Bin 0 -> 657 bytes .../app/src/main/res/drawable-xxhdpi/sz.png | Bin 0 -> 2370 bytes .../app/src/main/res/drawable-xxhdpi/tc.png | Bin 0 -> 3558 bytes .../app/src/main/res/drawable-xxhdpi/td.png | Bin 0 -> 363 bytes .../app/src/main/res/drawable-xxhdpi/tf.png | Bin 0 -> 1084 bytes .../app/src/main/res/drawable-xxhdpi/tg.png | Bin 0 -> 705 bytes .../app/src/main/res/drawable-xxhdpi/th.png | Bin 0 -> 189 bytes .../app/src/main/res/drawable-xxhdpi/tj.png | Bin 0 -> 986 bytes .../app/src/main/res/drawable-xxhdpi/tk.png | Bin 0 -> 2043 bytes .../app/src/main/res/drawable-xxhdpi/tl.png | Bin 0 -> 1679 bytes .../app/src/main/res/drawable-xxhdpi/tm.png | Bin 0 -> 3089 bytes .../app/src/main/res/drawable-xxhdpi/tn.png | Bin 0 -> 1354 bytes .../app/src/main/res/drawable-xxhdpi/to.png | Bin 0 -> 391 bytes .../app/src/main/res/drawable-xxhdpi/tr.png | Bin 0 -> 1244 bytes .../app/src/main/res/drawable-xxhdpi/tt.png | Bin 0 -> 2187 bytes .../app/src/main/res/drawable-xxhdpi/tv.png | Bin 0 -> 3300 bytes .../app/src/main/res/drawable-xxhdpi/tw.png | Bin 0 -> 1146 bytes .../app/src/main/res/drawable-xxhdpi/tz.png | Bin 0 -> 1997 bytes .../app/src/main/res/drawable-xxhdpi/ua.png | Bin 0 -> 373 bytes .../app/src/main/res/drawable-xxhdpi/ug.png | Bin 0 -> 953 bytes .../app/src/main/res/drawable-xxhdpi/um.png | Bin 0 -> 2400 bytes .../app/src/main/res/drawable-xxhdpi/us.png | Bin 0 -> 2400 bytes .../app/src/main/res/drawable-xxhdpi/uy.png | Bin 0 -> 1801 bytes .../app/src/main/res/drawable-xxhdpi/uz.png | Bin 0 -> 897 bytes .../app/src/main/res/drawable-xxhdpi/va.png | Bin 0 -> 2127 bytes .../app/src/main/res/drawable-xxhdpi/vc.png | Bin 0 -> 1159 bytes .../app/src/main/res/drawable-xxhdpi/ve.png | Bin 0 -> 660 bytes .../app/src/main/res/drawable-xxhdpi/vg.png | Bin 0 -> 4517 bytes .../app/src/main/res/drawable-xxhdpi/vi.png | Bin 0 -> 4928 bytes .../app/src/main/res/drawable-xxhdpi/vn.png | Bin 0 -> 874 bytes .../app/src/main/res/drawable-xxhdpi/vu.png | Bin 0 -> 1535 bytes .../app/src/main/res/drawable-xxhdpi/wf.png | Bin 0 -> 746 bytes .../app/src/main/res/drawable-xxhdpi/ws.png | Bin 0 -> 512 bytes .../app/src/main/res/drawable-xxhdpi/xk.png | Bin 0 -> 1390 bytes .../app/src/main/res/drawable-xxhdpi/ye.png | Bin 0 -> 375 bytes .../app/src/main/res/drawable-xxhdpi/yt.png | Bin 0 -> 3917 bytes .../app/src/main/res/drawable-xxhdpi/za.png | Bin 0 -> 2027 bytes .../app/src/main/res/drawable-xxhdpi/zm.png | Bin 0 -> 1013 bytes .../app/src/main/res/drawable-xxhdpi/zw.png | Bin 0 -> 2165 bytes .../app/src/main/res/drawable-xxhdpi/zz.png | Bin 0 -> 1627 bytes .../main/res/drawable-xxxhdpi/ic_alert.png | Bin 0 -> 3437 bytes .../main/res/drawable-xxxhdpi/ic_error.png | Bin 0 -> 2651 bytes .../main/res/drawable-xxxhdpi/ic_filled.png | Bin 0 -> 3543 bytes .../main/res/drawable-xxxhdpi/ic_lines.png | Bin 0 -> 3787 bytes .../drawable-xxxhdpi/modal_background.9.png | Bin 0 -> 20661 bytes .../app/src/main/res/drawable/background.png | Bin 0 -> 11942 bytes .../box_background_pattern_tiling.xml | 4 + .../main/res/drawable/box_clip_area_left.xml | 9 + .../main/res/drawable/box_clip_area_right.xml | 9 + .../app/src/main/res/drawable/box_ripple.xml | 11 + .../res/drawable/box_row_rounded_box_1.xml | 9 + .../res/drawable/box_row_rounded_box_2.xml | 8 + .../res/drawable/box_row_rounded_box_3.xml | 9 + .../res/drawable/box_row_rounded_box_4.xml | 9 + .../res/drawable/box_row_rounded_box_5.xml | 9 + .../main/res/drawable/clear_box_ripple.xml | 11 + .../res/drawable/current_server_ripple.xml | 11 + .../drawable/current_server_rounded_box.xml | 5 + .../main/res/drawable/flag_rounded_box.xml | 5 + .../internal_box_row_rounded_box_1.xml | 9 + .../internal_box_row_rounded_box_2.xml | 8 + .../internal_box_row_rounded_box_3.xml | 9 + .../internal_box_row_rounded_box_4.xml | 9 + .../android/app/src/main/res/drawable/map.png | Bin 0 -> 16857 bytes .../modal_background_pattern_tiling.xml | 4 + .../modal_button_primary_background.xml | 5 + .../drawable/modal_button_primary_ripple.xml | 11 + .../modal_button_secondary_background.xml | 5 + .../modal_button_secondary_ripple.xml | 11 + .../main/res/drawable/modal_internal_area.xml | 9 + .../drawable/red_button_pattern_tiling.xml | 4 + .../res/drawable/stop_btn_internal_area.xml | 9 + .../main/res/drawable/tablet_tab_border.xml | 9 + .../main/res/drawable/time_rounded_box.xml | 5 + .../src/main/res/drawable/top_bar_shadow.png | Bin 0 -> 979 bytes .../app/src/main/res/font/material_font.ttf | Bin 0 -> 230384 bytes .../app/src/main/res/font/skycoin_font.otf | Bin 0 -> 67292 bytes .../src/main/res/font/skycoin_font_bold.otf | Bin 0 -> 74840 bytes .../src/main/res/layout/activity_app_list.xml | 28 + .../src/main/res/layout/activity_index.xml | 57 ++ .../app/src/main/res/layout/activity_main.xml | 125 ++++ .../main/res/layout/activity_server_list.xml | 114 +++ .../src/main/res/layout/activity_settings.xml | 84 +++ .../src/main/res/layout/activity_start.xml | 11 + .../main/res/layout/view_app_list_item.xml | 67 ++ .../src/main/res/layout/view_app_list_row.xml | 23 + .../layout/view_app_list_selection_option.xml | 54 ++ .../res/layout/view_app_list_separator.xml | 22 + .../res/layout/view_confirmation_dialog.xml | 48 ++ .../res/layout/view_current_server_button.xml | 88 +++ .../layout/view_edit_server_value_modal.xml | 62 ++ .../res/layout/view_manual_server_modal.xml | 124 ++++ .../src/main/res/layout/view_modal_base.xml | 80 +++ .../res/layout/view_modal_window_button.xml | 28 + .../app/src/main/res/layout/view_options.xml | 16 + .../src/main/res/layout/view_options_item.xml | 43 ++ .../app/src/main/res/layout/view_select.xml | 42 ++ .../res/layout/view_server_filters_modal.xml | 130 ++++ .../res/layout/view_server_info_modal.xml | 269 +++++++ .../view_server_list_condition_list.xml | 90 +++ .../main/res/layout/view_server_list_item.xml | 302 ++++++++ .../layout/view_server_list_option_button.xml | 23 + .../res/layout/view_server_list_options.xml | 79 +++ .../layout/view_server_list_table_header.xml | 194 +++++ .../res/layout/view_server_list_table_row.xml | 186 +++++ .../res/layout/view_server_list_top_tab.xml | 19 + .../src/main/res/layout/view_server_name.xml | 16 + .../res/layout/view_server_notes_modal.xml | 59 ++ .../res/layout/view_server_password_modal.xml | 59 ++ .../main/res/layout/view_settings_button.xml | 15 + .../res/layout/view_settings_dns_modal.xml | 60 ++ .../res/layout/view_settings_list_item.xml | 71 ++ .../src/main/res/layout/view_start_button.xml | 32 + .../src/main/res/layout/view_start_chart.xml | 95 +++ .../main/res/layout/view_start_connected.xml | 508 ++++++++++++++ .../res/layout/view_start_disconnected.xml | 92 +++ .../res/layout/view_start_right_panel.xml | 247 +++++++ .../src/main/res/layout/view_stop_button.xml | 61 ++ .../app/src/main/res/layout/view_tab.xml | 57 ++ .../main/res/layout/view_tablet_top_bar.xml | 60 ++ .../res/layout/view_tablet_top_bar_stats.xml | 149 ++++ .../res/layout/view_tablet_top_bar_tab.xml | 36 + .../app/src/main/res/layout/view_top_bar.xml | 63 ++ .../main/res/layout/view_top_bar_button.xml | 14 + .../app/src/main/res/layout/view_top_tab.xml | 12 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 6880 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 8670 bytes .../app/src/main/res/values-v26/theme.xml | 6 + .../android/app/src/main/res/values/attrs.xml | 82 +++ .../app/src/main/res/values/colors.xml | 40 ++ .../app/src/main/res/values/dimens.xml | 34 + .../res/values/ic_launcher_background.xml | 4 + .../app/src/main/res/values/strings.xml | 295 ++++++++ .../android/app/src/main/res/values/theme.xml | 24 + cmd/skywirevisormobile/android/build.gradle | 21 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + cmd/skywirevisormobile/android/gradlew | 172 +++++ cmd/skywirevisormobile/android/gradlew.bat | 84 +++ .../android/settings.gradle | 5 + 463 files changed, 18103 insertions(+) create mode 100644 cmd/skywirevisormobile/android/README.md create mode 100644 cmd/skywirevisormobile/android/app/build.gradle create mode 100644 cmd/skywirevisormobile/android/app/src/main/AndroidManifest.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/App.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/Receiver.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListOptionButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListRow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListSeparator.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppsActivity.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppsAdapter.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/index/IndexActivity.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/index/IndexPageAdapter.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/main/MainActivity.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ConditionsList.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/FilterModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListOptionButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListOptions.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTableHeader.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTableRow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTopTab.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerLists.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServersActivity.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/VpnServerForList.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/VpnServersAdapter.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/CustomDnsModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/SettingsActivity.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/SettingsOption.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/MapBackground.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/StartActivity.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/StartViewRightPanel.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/Chart.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/StartViewConnected.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/StopButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/CurrentServerButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/StartButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/StartViewDisconnected.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowBackground.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowLayout.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowRipple.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ClickableLinearLayout.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ConfirmationModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/EditServerValueModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ManualServerModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ModalBase.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ModalWindowButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/Select.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerInfoModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerName.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerNotesModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerPasswordModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/SettingsButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/Tab.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBar.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBarStats.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBarTab.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopBar.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopBarButton.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopTab.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/options/OptionsItem.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/options/OptionsModalWindow.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ButtonBase.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ClickEvent.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ClickWithIndexEvent.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ListButtonBase.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ListViewHolder.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/AlphaSpan.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/BoxRowTypes.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/ClickTimeManagement.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/CountriesList.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/Globals.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/HelperFunctions.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/MaterialFontSpan.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/Notifications.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/UiMaterialIcons.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/ApiClient.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/GeoInfoModel.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/IpModel.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/VpnServerModel.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/LocalServerData.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ManualVpnServerData.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ServerFlags.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ServerRatings.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/SkywireVPNConnection.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/SkywireVPNService.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNCoordinator.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNDataManager.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNGeneralPersistentData.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNRunnable.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNServersPersistentData.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNStates.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNWorkInterface.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VisorRunnable.java create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/animator/anim_start_button.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/animator/anim_state.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/bronze_rating.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/gold_rating.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/modal_background_pattern.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/silver_rating.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box1.9.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box2.9.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box3.9.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box4.9.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box5.9.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/box_pattern.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/logo_vpn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/map_phones.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/red_btn.9.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/select_arrow.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/start_btn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ab.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ad.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ae.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/af.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ag.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ai.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/al.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/am.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ao.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/aq.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ar.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/as.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/at.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/au.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/aw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ax.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/az.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ba.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bb.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bd.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/be.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bf.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bh.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bi.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bj.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bl.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bo.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bq.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/br.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bs.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bt.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bv.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/by.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ca.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cc.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cd.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cf.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ch.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ci.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ck.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cl.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/co.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cu.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cv.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cx.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cy.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/de.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dj.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/do_flag.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ec.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ee.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/eg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/eh.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/er.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/es.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/et.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fi.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fj.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fo.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ga.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gb.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gd.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ge.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gf.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gi.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gl.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gp.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gq.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gs.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gt.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gu.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gy.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ht.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hu.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/id.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ie.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/il.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/im.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/in.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/io.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/iq.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ir.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/is.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/it.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/je.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/jm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/jo.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/jp.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ke.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kh.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ki.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/km.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kp.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ky.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/la.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lb.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lc.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/li.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ls.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lt.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lu.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lv.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ly.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ma.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mc.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/md.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/me.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mf.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mh.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ml.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mo.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mp.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mq.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ms.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mt.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mu.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mv.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mx.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/my.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/na.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/nc.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ne.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/nf.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ng.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ni.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/nl.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/no.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/np.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/nr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/nu.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/nz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/om.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pa.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pe.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pf.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ph.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pl.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ps.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pt.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/py.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/qa.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/re.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ro.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/rs.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ru.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/rw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sa.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sb.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sc.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sd.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/se.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sh.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/si.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sj.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sl.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/so.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ss.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/st.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sv.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sx.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sy.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tc.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/td.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tf.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/th.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tj.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tl.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/to.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tr.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tt.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tv.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ua.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ug.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/um.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/us.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/uy.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/uz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/va.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vc.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ve.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vg.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vi.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vn.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vu.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/wf.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ws.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/xk.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ye.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/yt.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/za.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/zm.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/zw.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/zz.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_alert.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_error.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_filled.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_lines.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/modal_background.9.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/background.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_background_pattern_tiling.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_clip_area_left.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_clip_area_right.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_ripple.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_1.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_2.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_3.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_4.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_5.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/clear_box_ripple.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/current_server_ripple.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/current_server_rounded_box.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/flag_rounded_box.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_1.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_2.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_3.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_4.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/map.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_background_pattern_tiling.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_primary_background.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_primary_ripple.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_secondary_background.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_secondary_ripple.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_internal_area.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/red_button_pattern_tiling.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/stop_btn_internal_area.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/tablet_tab_border.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/time_rounded_box.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/drawable/top_bar_shadow.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/font/material_font.ttf create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/font/skycoin_font.otf create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/font/skycoin_font_bold.otf create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/activity_app_list.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/activity_index.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/activity_main.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/activity_server_list.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/activity_settings.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/activity_start.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_app_list_item.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_app_list_row.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_app_list_selection_option.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_app_list_separator.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_confirmation_dialog.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_current_server_button.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_edit_server_value_modal.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_manual_server_modal.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_modal_base.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_modal_window_button.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_options.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_options_item.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_select.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_filters_modal.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_info_modal.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_list_condition_list.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_list_item.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_list_option_button.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_list_options.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_list_table_header.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_list_table_row.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_list_top_tab.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_name.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_notes_modal.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_server_password_modal.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_settings_button.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_settings_dns_modal.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_settings_list_item.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_start_button.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_start_chart.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_start_connected.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_start_disconnected.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_start_right_panel.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_stop_button.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_tab.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_tablet_top_bar.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_tablet_top_bar_stats.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_tablet_top_bar_tab.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_top_bar.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_top_bar_button.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/layout/view_top_tab.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/values-v26/theme.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/values/attrs.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/values/colors.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/values/dimens.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/values/ic_launcher_background.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/values/strings.xml create mode 100644 cmd/skywirevisormobile/android/app/src/main/res/values/theme.xml create mode 100644 cmd/skywirevisormobile/android/build.gradle create mode 100644 cmd/skywirevisormobile/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 cmd/skywirevisormobile/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 cmd/skywirevisormobile/android/gradlew create mode 100644 cmd/skywirevisormobile/android/gradlew.bat create mode 100644 cmd/skywirevisormobile/android/settings.gradle diff --git a/cmd/skywirevisormobile/android/README.md b/cmd/skywirevisormobile/android/README.md new file mode 100644 index 0000000000..59bacc615a --- /dev/null +++ b/cmd/skywirevisormobile/android/README.md @@ -0,0 +1,66 @@ +# Skywire VPN draft (Android) + +## Prerequisites +This project uses Skywire mobile library (`skywiremob`) to use all the needed Skywire infrastructure. In order to build +and use this library, one needs to install `gomobile` (https://github.com/golang/mobile). + +## Building and Running +To build the library you need to use `gomobile`. Main `Makefile` already contains target `build-android` which +can be used. +IMPORTANT: regardless of go modules and other great stuff done by the Go team, in order to +use `gomobile` you need to put Skywire code according to `GOPATH`. Otherwise you'll get all kinds of errors. +The output file (`.aar` for Android) may be used straight from the mobile app code. + +## Skywire Mobile API +- `PrintString(string)`: Logs string argument using info log level. May be useful to use it instead of standard logging features of mobile apps for debugging. +All the output strings are prefixed with `GoLog`, so printing logs with this func one may grep all the logs both from `skywiremob` +internal and mobile application; +- `IsPKValid(string) string`: Checks if passed pub key is valid. Returns non-empty string with error in case of failure; +- `GetMTU() int`: Gets VPN connection MTU; +- `GetTUNIPPrefix() int`: Gets netmask prefix of TUN IP address; +- `IsVPNReady() bool`: Checks whether VPN client is ready on the Go side. Once it is, the mobile application is free to start +forwarding packets. Starts returning `true` after the `ServerVPN` call; +- `PrepareVisor() string`: Creates and runs visor instance. Returns non-empty string with error in case of failure; +- `NextDmsgSocket() int`: Returns file descriptor of the Dmsg socket. There may be more than one socket in use by the dmsg client, so +this function should be called repeatedly until next call returns 0. +- `PrepareVPNClient(string, string) string`: Creates VPN client instance. First string argument is remote VPN server pub key, second one is passcode to +authenticate within the server. Returns non-empty string with error in case of failure; +- `ShakeHands() string`: Requires `PrepareVPNClient` to be called first. Performs handshake between the client and the server. +Returns non-empty string with error in case of failure; +- `TUNIP() string`: Requires `ShakeHands` to be called first. Returns the assigned TUN IP; +- `TUNGateway() string`: Requires `ShakeHands` to be called first. Returns the assigned TUN gateway; +- `StopVisor() string`: Stops currently running visor. Returns non-empty string with error in case of failure; +- `SetMobileAppAddr(string)`: Passes address of the UDP connection opened on the mobile application side; +- `ServeVPN()`: Starts off the goroutine serving VPN connection. After this call `IsVPNReady` starts returning `true`; +- `StartListeningUDP() string`: Opens UDP listener on the Go side. Returns non-empty string with error in case of failure; +- `IsVisorStarting() bool`: Checks if visor is starting. Will get `false` when it's fully functional; +- `IsVisorRunning() bool`: Checks if visor is running. Will get `true` whn visor is fully functional; +- `WaitVisorReady() string`: Blocks until visor gets fully initialized. Returns non-empty error string in case of failure; +- `StopVPNClient`: Stops VPN client without stopping visor itself; +- `StopListeningUDP`: Closes UDP socket; +- `VPNBandwidthSent`: Returns amount of bandwidth sent over VPN (bytes); +- `VPNBandwidthReceived`: Returns amount of bandwidth received over VPN (bytes); +- `VPNLatency`: Returns latency (ms); +- `VPNThroughput`: Returns throughput (bytes/s). + + +## Mobile/Go Communication +API may seem a bit complicated at first. Currently tested for Android devices, should be used with caution on iOS. +Mobile app communicates with the Go part via UDP. All the packets are sent to the Go part via UDP and then get resent +to the Skywire network. + +To setup the Go side properly you need to call at least: +- `PrepareVisor` to run the visor; +- `PrepareVPNClient` to run the VPN client; +- `ShakeHands` to perform handshake with the server; +- `StartListeningUDP` to open the UDP listener on the Go side; +- `ServeVPN` to start forwarding traffic. + +All other calls should be done as needed. + +### Android +Consult this page: https://developer.android.com/guide/topics/connectivity/vpn + +In the example mobile app communicates with the remote server via `DatagramChannel`. Socket opened to the server gets protected +with the `protect` method. We do the same here. But instead of a remote server we open the `DatagramChannel` to the Go part of the app. +We protect not only the tunnel socket, but also we need to protect all the sockets used for `Dmsg` communication to let traffic go back and forth freely. diff --git a/cmd/skywirevisormobile/android/app/build.gradle b/cmd/skywirevisormobile/android/app/build.gradle new file mode 100644 index 0000000000..debee173bc --- /dev/null +++ b/cmd/skywirevisormobile/android/app/build.gradle @@ -0,0 +1,61 @@ +/* + * Copyright 2015 The Go Authors. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ +apply plugin: 'com.android.application' + +repositories { + flatDir { + dirs '.' + } + maven { url 'https://jitpack.io' } +} + +android { + compileSdkVersion 29 + + defaultConfig { + applicationId "com.skywire.skycoin.vpn" + minSdkVersion 21 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + + +dependencies { + // Appcompat. + implementation "androidx.appcompat:appcompat:1.2.0" + implementation 'com.google.android.material:material:1.2.1' + implementation "androidx.preference:preference:1.1.1" + implementation "androidx.recyclerview:recyclerview:1.1.0" + implementation "androidx.viewpager2:viewpager2:1.0.0" + + // Skywire lib. + implementation(name:'skywire', ext:'aar') + + // RxJava. + implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' + implementation 'io.reactivex.rxjava3:rxjava:3.0.0' + + // Retrofit. + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0' + implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' + + // MPAndroidChart. + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' +} diff --git a/cmd/skywirevisormobile/android/app/src/main/AndroidManifest.xml b/cmd/skywirevisormobile/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..f0a71f93fb --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/App.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/App.java new file mode 100644 index 0000000000..e3627d5d3a --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/App.java @@ -0,0 +1,118 @@ +package com.skywire.skycoin.vpn; + +import android.app.Activity; +import android.app.Application; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.helpers.Notifications; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; + +import io.reactivex.rxjava3.plugins.RxJavaPlugins; + +/** + * Class for the main app instance. + */ +public class App extends Application { + /** + * Class used internally to know when there are activities being displayed. + */ + private static class ActivityLifecycleCallback implements Application.ActivityLifecycleCallbacks { + + // How many activities are being shown. + private static int foregroundActivities = 0; + + // Functions for knowing when activities start and stop being shown. + @Override + public void onActivityResumed(@NonNull final Activity activity) { foregroundActivities++; } + @Override + public void onActivityStopped(@NonNull final Activity activity) { foregroundActivities--; } + + /** + * Returns if there is at least one activity being displayed. + */ + public static boolean isApplicationInForeground() { return foregroundActivities > 0; } + + // Other functions needed by the interface. + @Override + public void onActivityPaused(@NonNull Activity activity) { } + @Override + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { } + @Override + public void onActivityDestroyed(@NonNull Activity activity) { } + @Override + public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { } + @Override + public void onActivityStarted(@NonNull Activity activity) { } + } + + /** + * Reference to the current app instance. + */ + private static Context appContext; + + @Override + public void onCreate() { + super.onCreate(); + // Save the current app instance. + appContext = this; + + // Ensure the singleton is initialized early. + VPNCoordinator.getInstance(); + + // Create the notification channels, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Channel for the VPN service state updates. + NotificationChannel stateChannel = new NotificationChannel( + Notifications.NOTIFICATION_CHANNEL_ID, + getString(R.string.general_app_name), + NotificationManager.IMPORTANCE_DEFAULT + ); + stateChannel.setDescription(getString(R.string.general_notification_channel_description)); + stateChannel.setSound(null,null); + NotificationManager notificationManager = getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(stateChannel); + + // Channel for alerts. + NotificationChannel alertsChannel = new NotificationChannel( + Notifications.ALERT_NOTIFICATION_CHANNEL_ID, + getString(R.string.general_alert_notification_name), + NotificationManager.IMPORTANCE_HIGH + ); + alertsChannel.setDescription(getString(R.string.general_alert_notification_channel_description)); + notificationManager.createNotificationChannel(alertsChannel); + } + + // Code for precessing errors which were not caught by the normal error management + // procedures RxJava has. This prevents the app to be closed by unexpected errors, mainly + // code trying to report events in closed observables. + RxJavaPlugins.setErrorHandler(throwable -> { + HelperFunctions.logError("ERROR INSIDE RX: ", throwable); + }); + + // Detect when activities are started and stopped. + registerActivityLifecycleCallbacks(new ActivityLifecycleCallback()); + } + + /** + * Gets the current app context. + */ + public static Context getContext(){ + return appContext; + } + + /** + * Gets if the UI is being displayed. + */ + public static boolean displayingUI(){ + return ActivityLifecycleCallback.isApplicationInForeground(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/Receiver.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/Receiver.java new file mode 100644 index 0000000000..99aae2322c --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/Receiver.java @@ -0,0 +1,23 @@ +package com.skywire.skycoin.vpn; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; + +/** + * Class for receiving the system boot event broadcast. + */ +public class Receiver extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { + // If the option for starting the service automatically after booting the OS is active + // and the service is not currently running, start the service. + if (VPNGeneralPersistentData.getStartOnBoot() && !VPNCoordinator.getInstance().isServiceRunning()) { + VPNCoordinator.getInstance().activateAutostart(); + } + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListButton.java new file mode 100644 index 0000000000..02f8ae5bf1 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListButton.java @@ -0,0 +1,124 @@ +package com.skywire.skycoin.vpn.activities.apps; + +import android.content.Context; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.CheckBox; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ListButtonBase; + +public class AppListButton extends ListButtonBase implements View.OnTouchListener { + public static final float APROX_HEIGHT_DP = 55; + + private FrameLayout mainLayout; + private LinearLayout internalLayout; + private ImageView imageIcon; + private FrameLayout layoutSeparator; + private TextView textAppName; + private CheckBox checkSelected; + private View separator; + + private RippleDrawable rippleDrawable; + + private String appPackageName; + + public AppListButton(Context context) { + super(context); + } + public AppListButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + public AppListButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_app_list_item, this, true); + + mainLayout = this.findViewById (R.id.mainLayout); + internalLayout = this.findViewById (R.id.internalLayout); + imageIcon = this.findViewById (R.id.imageIcon); + layoutSeparator = this.findViewById (R.id.layoutSeparator); + textAppName = this.findViewById (R.id.textAppName); + checkSelected = this.findViewById (R.id.checkSelected); + separator = this.findViewById (R.id.separator); + + rippleDrawable = (RippleDrawable) mainLayout.getBackground(); + setOnTouchListener(this); + setViewForCheckingClicks(this); + + setUseBigFastClickPrevention(false); + } + + public void setSeparatorVisibility(boolean visible) { + if (visible) { + separator.setVisibility(VISIBLE); + } else { + separator.setVisibility(GONE); + } + } + + public void changeData(ResolveInfo appData) { + if (appData != null) { + appPackageName = appData.activityInfo.packageName; + imageIcon.setImageDrawable(appData.activityInfo.loadIcon(this.getContext().getPackageManager())); + textAppName.setText(appData.activityInfo.loadLabel(this.getContext().getPackageManager())); + imageIcon.setVisibility(VISIBLE); + layoutSeparator.setVisibility(VISIBLE); + setVisibility(VISIBLE); + } else { + setVisibility(INVISIBLE); + } + } + + public void changeData(String appPackageName) { + imageIcon.setVisibility(GONE); + layoutSeparator.setVisibility(GONE); + if (appPackageName != null) { + this.appPackageName = appPackageName; + textAppName.setText(appPackageName); + setVisibility(VISIBLE); + } else { + setVisibility(INVISIBLE); + } + } + + public String getAppPackageName() { + return appPackageName; + } + + public void setChecked(boolean checked) { + checkSelected.setChecked(checked); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + + if (enabled) { + internalLayout.setAlpha(1f); + } else { + internalLayout.setAlpha(0.5f); + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (rippleDrawable != null) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListOptionButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListOptionButton.java new file mode 100644 index 0000000000..52431579ff --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListOptionButton.java @@ -0,0 +1,62 @@ +package com.skywire.skycoin.vpn.activities.apps; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.RadioButton; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.extensible.ListButtonBase; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; + +public class AppListOptionButton extends ListButtonBase { + private BoxRowLayout mainLayout; + private TextView textOption; + private TextView textDescription; + private RadioButton radioSelected; + + public AppListOptionButton(Context context) { + super(context); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_app_list_selection_option, this, true); + + mainLayout = this.findViewById (R.id.mainLayout); + textOption = this.findViewById (R.id.textOption); + textDescription = this.findViewById (R.id.textDescription); + radioSelected = this.findViewById (R.id.radioSelected); + + radioSelected.setChecked(false); + + setClickableBoxView(mainLayout); + } + + public void setBoxRowType(BoxRowTypes type) { + mainLayout.setType(type); + } + + public void changeData(int textResource, int descriptionResource) { + textOption.setText(textResource); + textDescription.setText(descriptionResource); + } + + public void setChecked(boolean checked) { + radioSelected.setChecked(checked); + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + + if (enabled) { + setAlpha(1f); + } else { + setAlpha(0.5f); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListRow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListRow.java new file mode 100644 index 0000000000..1e6888c084 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListRow.java @@ -0,0 +1,115 @@ +package com.skywire.skycoin.vpn.activities.apps; + +import android.content.Context; +import android.content.pm.ResolveInfo; +import android.view.LayoutInflater; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.extensible.ClickWithIndexEvent; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; + +public class AppListRow extends FrameLayout implements ClickWithIndexEvent { + private BoxRowLayout mainLayout; + private LinearLayout buttonsContainer; + + private AppListButton[] buttons; + private ClickWithIndexEvent clickListener; + + public AppListRow(Context context, int buttonsPerRow) { + super(context); + + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_app_list_row, this, true); + + mainLayout = this.findViewById(R.id.mainLayout); + buttonsContainer = this.findViewById(R.id.buttonsContainer); + + buttonsContainer.setClipToOutline(true); + + buttons = new AppListButton[buttonsPerRow]; + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 1f); + for (int i = 0; i < buttonsPerRow; i++) { + AppListButton btn = new AppListButton(context); + btn.setLayoutParams(layoutParams); + btn.setClickWithIndexEventListener(this); + buttons[i] = btn; + buttonsContainer.addView(btn); + } + } + + public void setIndex(int index) { + for (int i = 0; i < buttons.length; i++) { + buttons[i].setIndex(index + i); + } + } + + public void setClickWithIndexEventListener(ClickWithIndexEvent listener) { + clickListener = listener; + } + + public void changeData(ResolveInfo[] appData) { + for (int i = 0; i < buttons.length; i++) { + buttons[i].changeData(appData[i]); + } + } + + public void changeData(String[] appPackageName) { + for (int i = 0; i < buttons.length; i++) { + buttons[i].changeData(appPackageName[i]); + } + } + + public void setBoxRowType(BoxRowTypes type) { + mainLayout.setType(type); + + boolean showSeparator = true; + if (type == BoxRowTypes.TOP) { + buttonsContainer.setBackgroundResource(R.drawable.internal_box_row_rounded_box_1); + } else if (type == BoxRowTypes.MIDDLE) { + buttonsContainer.setBackgroundResource(R.drawable.internal_box_row_rounded_box_2); + } else if (type == BoxRowTypes.BOTTOM) { + buttonsContainer.setBackgroundResource(R.drawable.internal_box_row_rounded_box_3); + showSeparator = false; + } else { + buttonsContainer.setBackgroundResource(R.drawable.internal_box_row_rounded_box_4); + showSeparator = false; + } + + for (int i = 0; i < buttons.length; i++) { + buttons[i].setSeparatorVisibility(showSeparator); + } + } + + public void setChecked(String packageName, boolean checked) { + for (int i = 0; i < buttons.length; i++) { + if (buttons[i].getAppPackageName() != null && buttons[i].getAppPackageName().equals(packageName)) { + buttons[i].setChecked(checked); + } + } + } + + public void setChecked(boolean[] checked) { + for (int i = 0; i < buttons.length; i++) { + buttons[i].setChecked(checked[i]); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + + for (int i = 0; i < buttons.length; i++) { + buttons[i].setEnabled(enabled); + } + } + + @Override + public void onClickWithIndex(int index, Void data) { + if (clickListener != null) { + clickListener.onClickWithIndex(index, data); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListSeparator.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListSeparator.java new file mode 100644 index 0000000000..6c51f71837 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppListSeparator.java @@ -0,0 +1,29 @@ +package com.skywire.skycoin.vpn.activities.apps; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +public class AppListSeparator extends LinearLayout { + private TextView textTitle; + + public AppListSeparator(Context context) { + super(context); + + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_app_list_separator, this, true); + + textTitle = this.findViewById (R.id.textTitle); + + int tabletExtraHorizontalPadding = HelperFunctions.getTabletExtraHorizontalPadding(getContext()); + setPadding(tabletExtraHorizontalPadding, 0, tabletExtraHorizontalPadding, 0); + } + + public void changeTitle(int title) { + textTitle.setText(title); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppsActivity.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppsActivity.java new file mode 100644 index 0000000000..c89673f172 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppsActivity.java @@ -0,0 +1,50 @@ +package com.skywire.skycoin.vpn.activities.apps; + +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +public class AppsActivity extends AppCompatActivity implements AppsAdapter.AppListChangedListener { + public static final String READ_ONLY_EXTRA = "ReadOnly"; + + private RecyclerView recycler; + + private boolean readOnly; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_app_list); + + recycler = findViewById(R.id.recycler); + + readOnly = getIntent().getBooleanExtra(READ_ONLY_EXTRA, false); + + LinearLayoutManager layoutManager = new LinearLayoutManager(this); + recycler.setLayoutManager(layoutManager); + // This could be useful in the future. + // recycler.setHasFixedSize(true); + + AppsAdapter adapter = new AppsAdapter(this, readOnly); + adapter.setAppListChangedEventListener(this); + recycler.setAdapter(adapter); + } + + @Override + protected void onResume() { + super.onResume(); + if (!readOnly) { + HelperFunctions.closeActivityIfServiceRunning(this); + } + } + + @Override + public boolean onAppListChanged() { + return !HelperFunctions.closeActivityIfServiceRunning(this); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppsAdapter.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppsAdapter.java new file mode 100644 index 0000000000..72defac1c9 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/apps/AppsAdapter.java @@ -0,0 +1,339 @@ +package com.skywire.skycoin.vpn.activities.apps; + +import android.content.Context; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; +import com.skywire.skycoin.vpn.extensible.ClickWithIndexEvent; +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.extensible.ListViewHolder; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class AppsAdapter extends RecyclerView.Adapter> implements ClickWithIndexEvent { + public interface AppListChangedListener { + boolean onAppListChanged(); + } + + private final int installedAppsIndexExtra = 10; + private final int uninstalledAppsIndexExtra = 1000000; + + private Context context; + private List appList; + private List uninstalledApps; + private AppListChangedListener appListChangedListener; + + private HashSet selectedApps; + private Globals.AppFilteringModes selectedOption; + + private int[] optionTexts = new int[3]; + private int[] optionDescriptions = new int[3]; + private ArrayList optionButtons = new ArrayList<>(); + private ArrayList appRows = new ArrayList<>(); + + private ArrayList premadeRows = new ArrayList<>(); + private int lastUsedPremadeRowIndex = 0; + + private int elementsPerRow = 1; + + private boolean readOnly; + + public AppsAdapter(Context context, boolean readOnly) { + this.context = context; + this.readOnly = readOnly; + + selectedApps = VPNGeneralPersistentData.getAppList(new HashSet<>()); + changeSelectedOption(VPNGeneralPersistentData.getAppsSelectionMode()); + + appList = HelperFunctions.getDeviceAppsList(); + + HashSet filteredApps = HelperFunctions.filterAvailableApps(selectedApps); + if (filteredApps.size() != selectedApps.size()) { + uninstalledApps = new ArrayList<>(); + + for (String app : selectedApps) { + if (!filteredApps.contains(app)) { + uninstalledApps.add(app); + } + } + } + + optionTexts[0] = R.string.tmp_select_apps_protect_all_button; + optionTexts[1] = R.string.tmp_select_apps_protect_selected_button; + optionTexts[2] = R.string.tmp_select_apps_unprotect_selected_button; + + optionDescriptions[0] = R.string.tmp_select_apps_protect_all_button_desc; + optionDescriptions[1] = R.string.tmp_select_apps_protect_selected_button_desc; + optionDescriptions[2] = R.string.tmp_select_apps_unprotect_selected_button_desc; + + int screenWidthInDP = (int)(Resources.getSystem().getDisplayMetrics().widthPixels / context.getResources().getDisplayMetrics().density); + elementsPerRow = Math.max(screenWidthInDP / 360, 1); + + int screenHeightInDP = (int)(Resources.getSystem().getDisplayMetrics().heightPixels / context.getResources().getDisplayMetrics().density); + int aproxRowsToFillScreen = (int)Math.ceil((screenHeightInDP / AppListButton.APROX_HEIGHT_DP) * 1.3); + + for (int i = 0; i < aproxRowsToFillScreen; i++) { + premadeRows.add(createNewRow()); + } + } + + public void setAppListChangedEventListener(AppListChangedListener listener) { + appListChangedListener = listener; + } + + private int getInstalledAppsRowsCount() { + return (int)Math.ceil((double)appList.size() / (double)elementsPerRow); + } + + private int getUninstalledAppsRowsCount() { + if (uninstalledApps == null) { + return 0; + } + + return (int)Math.ceil((double)uninstalledApps.size() / (double)elementsPerRow); + } + + @Override + public int getItemViewType(int position) { + if (position == 0 || position == 4 || position == 5 + getInstalledAppsRowsCount()) { + return 2; + } + + if (position < 4) { + return 0; + } + + return 1; + } + + @NonNull + @Override + public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + if (viewType == 0) { + AppListOptionButton view = new AppListOptionButton(context); + view.setClickWithIndexEventListener(this); + optionButtons.add(view); + + if (readOnly) { + view.setEnabled(false); + } + + return new ListViewHolder<>(view); + } else if (viewType == 1) { + AppListRow view; + + if (lastUsedPremadeRowIndex < premadeRows.size()) { + view = premadeRows.get(lastUsedPremadeRowIndex); + lastUsedPremadeRowIndex += 1; + } else { + view = createNewRow(); + } + + return new ListViewHolder<>(view); + } + + AppListSeparator view = new AppListSeparator(context); + + return new ListViewHolder<>(view); + } + + private AppListRow createNewRow() { + AppListRow view = new AppListRow(context, elementsPerRow); + view.setClickWithIndexEventListener(this); + view.setEnabled(selectedOption != Globals.AppFilteringModes.PROTECT_ALL); + appRows.add(view); + + if (readOnly) { + view.setEnabled(false); + } + + return view; + } + + @Override + public void onBindViewHolder(@NonNull ListViewHolder holder, int position) { + if (holder.getItemViewType() == 0) { + boolean showChecked = false; + if (position == 1 && selectedOption == Globals.AppFilteringModes.PROTECT_ALL) { showChecked = true; } + if (position == 2 && selectedOption == Globals.AppFilteringModes.PROTECT_SELECTED) { showChecked = true; } + if (position == 3 && selectedOption == Globals.AppFilteringModes.IGNORE_SELECTED) { showChecked = true; } + + ((AppListOptionButton)(holder.itemView)).setIndex(position); + ((AppListOptionButton)(holder.itemView)).changeData(optionTexts[position - 1], optionDescriptions[position - 1]); + ((AppListOptionButton)(holder.itemView)).setChecked(showChecked); + + if (position == 1) { + ((AppListOptionButton)holder.itemView).setBoxRowType(BoxRowTypes.TOP); + } else if (position == 2) { + ((AppListOptionButton)holder.itemView).setBoxRowType(BoxRowTypes.MIDDLE); + } else { + ((AppListOptionButton)holder.itemView).setBoxRowType(BoxRowTypes.BOTTOM); + } + + return; + } else if (holder.getItemViewType() == 2) { + if (position == 0) { + ((AppListSeparator)holder.itemView).changeTitle(R.string.tmp_select_apps_mode_title); + } else if (position == 4) { + if (this.uninstalledApps != null) { + ((AppListSeparator) holder.itemView).changeTitle(R.string.tmp_select_apps_installed_apps_title); + } else { + ((AppListSeparator) holder.itemView).changeTitle(R.string.tmp_select_apps_apps_title); + } + } else { + ((AppListSeparator)holder.itemView).changeTitle(R.string.tmp_select_apps_uninstalled_apps_title); + } + + return; + } + + int initialInstalledAppsRowIndex = 5; + if (position < initialInstalledAppsRowIndex + getInstalledAppsRowsCount()) { + int rowIndex = (position - initialInstalledAppsRowIndex); + + ResolveInfo[] dataForRow = new ResolveInfo[elementsPerRow]; + boolean[] checkedListForRow = new boolean[elementsPerRow]; + for (int i = 0; i < elementsPerRow; i++){ + int appIndex = (rowIndex * elementsPerRow) + i; + if (appIndex < appList.size()) { + dataForRow[i] = appList.get(appIndex); + checkedListForRow[i] = selectedApps.contains(appList.get(appIndex).activityInfo.packageName); + } + } + + ((AppListRow) (holder.itemView)).setIndex(installedAppsIndexExtra + (rowIndex * elementsPerRow)); + ((AppListRow) (holder.itemView)).changeData(dataForRow); + ((AppListRow) (holder.itemView)).setChecked(checkedListForRow); + + if (getInstalledAppsRowsCount() == 1) { + ((AppListRow)holder.itemView).setBoxRowType(BoxRowTypes.SINGLE); + } else if (rowIndex == 0) { + ((AppListRow)holder.itemView).setBoxRowType(BoxRowTypes.TOP); + } else if (rowIndex == getInstalledAppsRowsCount() - 1) { + ((AppListRow)holder.itemView).setBoxRowType(BoxRowTypes.BOTTOM); + } else { + ((AppListRow)holder.itemView).setBoxRowType(BoxRowTypes.MIDDLE); + } + } else { + int initialUninstalledAppsRowIndex = initialInstalledAppsRowIndex + getInstalledAppsRowsCount() + 1; + int rowIndex = (position - initialUninstalledAppsRowIndex); + + String[] dataForRow = new String[elementsPerRow]; + boolean[] checkedListForRow = new boolean[elementsPerRow]; + for (int i = 0; i < elementsPerRow; i++){ + int appIndex = (rowIndex * elementsPerRow) + i; + if (appIndex < uninstalledApps.size()) { + dataForRow[i] = uninstalledApps.get(appIndex); + checkedListForRow[i] = selectedApps.contains(uninstalledApps.get(appIndex)); + } + } + + ((AppListRow) (holder.itemView)).setIndex(uninstalledAppsIndexExtra + (rowIndex * elementsPerRow)); + ((AppListRow) (holder.itemView)).changeData(dataForRow); + ((AppListRow) (holder.itemView)).setChecked(checkedListForRow); + + if (getUninstalledAppsRowsCount() == 1) { + ((AppListRow)holder.itemView).setBoxRowType(BoxRowTypes.SINGLE); + } else if (rowIndex == 0) { + ((AppListRow)holder.itemView).setBoxRowType(BoxRowTypes.TOP); + } else if (rowIndex == getUninstalledAppsRowsCount() - 1) { + ((AppListRow)holder.itemView).setBoxRowType(BoxRowTypes.BOTTOM); + } else { + ((AppListRow)holder.itemView).setBoxRowType(BoxRowTypes.MIDDLE); + } + } + } + + @Override + public int getItemCount() { + int result = 3 + 2 + getInstalledAppsRowsCount(); + + if (getUninstalledAppsRowsCount() > 0) { + result += 1 + getUninstalledAppsRowsCount(); + } + + return result; + } + + @Override + public void onClickWithIndex(int index, Void data) { + if (appListChangedListener != null) { + if (!appListChangedListener.onAppListChanged()) { + return; + } + } + + if (index < installedAppsIndexExtra) { + if (index == 1) { + changeSelectedOption(Globals.AppFilteringModes.PROTECT_ALL); + } else if (index == 2) { + changeSelectedOption(Globals.AppFilteringModes.PROTECT_SELECTED); + } else if (index == 3) { + changeSelectedOption(Globals.AppFilteringModes.IGNORE_SELECTED); + } + } else { + processAppClicked(index); + } + } + + private void changeSelectedOption(Globals.AppFilteringModes option) { + if (option != selectedOption) { + if (option == Globals.AppFilteringModes.PROTECT_ALL) { + for (AppListRow row : appRows) { + row.setEnabled(false); + } + } else if (selectedOption == Globals.AppFilteringModes.PROTECT_ALL) { + for (AppListRow row : appRows) { + row.setEnabled(true); + } + } + + selectedOption = option; + VPNGeneralPersistentData.setAppsSelectionMode(selectedOption); + + for (AppListOptionButton optionButton : optionButtons) { + optionButton.setChecked( + (optionButton.getIndex() == 1 && selectedOption == Globals.AppFilteringModes.PROTECT_ALL) || + (optionButton.getIndex() == 2 && selectedOption == Globals.AppFilteringModes.PROTECT_SELECTED) || + (optionButton.getIndex() == 3 && selectedOption == Globals.AppFilteringModes.IGNORE_SELECTED) + ); + } + } + } + + private void processAppClicked(int index) { + String app; + + if (index < uninstalledAppsIndexExtra) { + app = appList.get(index - installedAppsIndexExtra).activityInfo.packageName; + } else { + app = uninstalledApps.get(index - uninstalledAppsIndexExtra); + } + + boolean showAppChecked; + if (selectedApps.contains(app)) { + selectedApps.remove(app); + showAppChecked = false; + } else { + selectedApps.add(app); + showAppChecked = true; + } + + for (AppListRow row : appRows) { + row.setChecked(app, showAppChecked); + } + + VPNGeneralPersistentData.setAppList(selectedApps); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/index/IndexActivity.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/index/IndexActivity.java new file mode 100644 index 0000000000..dfa267d285 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/index/IndexActivity.java @@ -0,0 +1,185 @@ +package com.skywire.skycoin.vpn.activities.index; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.viewpager2.widget.ViewPager2; + +import com.google.android.material.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.TabletTopBar; +import com.skywire.skycoin.vpn.controls.TopBar; +import com.skywire.skycoin.vpn.controls.TopTab; +import com.skywire.skycoin.vpn.extensible.ClickWithIndexEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; + +public class IndexActivity extends AppCompatActivity implements IndexPageAdapter.RequestTabListener, ClickWithIndexEvent { + private ImageView imageBackground; + private ImageView imageTopBarShadow; + private ViewPager2 pager; + private TopBar topBar; + private TabletTopBar tabletTopBar; + private TabLayout tabs; + + private TabLayoutMediator tabLayoutMediator; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_index); + + imageBackground = findViewById(R.id.imageBackground); + imageTopBarShadow = findViewById(R.id.imageTopBarShadow); + pager = findViewById(R.id.pager); + topBar = findViewById(R.id.topBar); + tabletTopBar = findViewById(R.id.tabletTopBar); + tabs = findViewById(R.id.tabs); + + if (HelperFunctions.showBackgroundForVerticalScreen()) { + imageBackground.setVisibility(View.GONE); + } + + IndexPageAdapter adapter = new IndexPageAdapter(this); + adapter.setRequestTabListener(this); + pager.setAdapter(adapter); + + tabLayoutMediator = new TabLayoutMediator(tabs, pager, (tab, position) -> { + if (position == 0) { + tab.setCustomView(new TopTab(this, R.string.tmp_status_page_title)); + } else if (position == 1) { + tab.setCustomView(new TopTab(this, R.string.tmp_select_server_title)); + } else { + tab.setCustomView(new TopTab(this, R.string.tmp_options_title)); + } + + if (position != 0) { + tab.getCustomView().setAlpha(0.4f); + } + }); + tabLayoutMediator.attach(); + + pager.setOffscreenPageLimit(3); + + if (HelperFunctions.getWidthType(this) == HelperFunctions.WidthTypes.SMALL) { + tabletTopBar.setVisibility(View.GONE); + tabletTopBar.close(); + + tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(TabLayout.Tab tab) { + tab.getCustomView().setAlpha(1f); + } + @Override + public void onTabUnselected(TabLayout.Tab tab) { + tab.getCustomView().setAlpha(0.4f); + } + @Override + public void onTabReselected(TabLayout.Tab tab) { } + }); + } else { + topBar.setVisibility(View.GONE); + tabs.setVisibility(View.GONE); + imageTopBarShadow.setVisibility(View.GONE); + + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)imageBackground.getLayoutParams(); + params.topMargin = 0; + imageBackground.setLayoutParams(params); + + params = (FrameLayout.LayoutParams)pager.getLayoutParams(); + params.topMargin = (int)Math.round(getResources().getDimension(R.dimen.tablet_top_bar_height)); + pager.setLayoutParams(params); + + tabletTopBar.setSelectedTab(TabletTopBar.statusTabIndex); + + pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + + tabletTopBar.setSelectedTab(position); + } + + @Override + public void onPageScrollStateChanged(int state) { + super.onPageScrollStateChanged(state); + } + }); + + tabletTopBar.setClickWithIndexEventListener(this); + } + } + + @Override + public void onResume() { + super.onResume(); + + if (tabletTopBar.getVisibility() != View.GONE) { + tabletTopBar.onResume(); + } + } + + @Override + public void onPause() { + super.onPause(); + + if (tabletTopBar.getVisibility() != View.GONE) { + tabletTopBar.onPause(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + tabLayoutMediator.detach(); + tabletTopBar.close(); + } + + @Override + public void onBackPressed() { + if (pager.getCurrentItem() != 0) { + pager.setCurrentItem(0); + } else { + super.onBackPressed(); + + if (VPNCoordinator.getInstance().isServiceRunning()) { + HelperFunctions.showToast(getString(R.string.general_service_running_notification), false); + } + } + } + + @Override + public void onOpenStatusRequested() { + pager.setCurrentItem(0); + } + + @Override + public void onOpenServerListRequested() { + pager.setCurrentItem(1); + } + + @Override + protected void onActivityResult(int request, int result, Intent data) { + super.onActivityResult(request, result, data); + + if (request == VPNCoordinator.VPN_PREPARATION_REQUEST_CODE) { + VPNCoordinator.getInstance().onActivityResult(request, result, data); + } + } + + @Override + public void onClickWithIndex(int index, Void data) { + pager.setCurrentItem(index); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/index/IndexPageAdapter.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/index/IndexPageAdapter.java new file mode 100644 index 0000000000..08905acac3 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/index/IndexPageAdapter.java @@ -0,0 +1,49 @@ +package com.skywire.skycoin.vpn.activities.index; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; +import androidx.viewpager2.adapter.FragmentStateAdapter; + +import com.skywire.skycoin.vpn.activities.servers.ServersActivity; +import com.skywire.skycoin.vpn.activities.settings.SettingsActivity; +import com.skywire.skycoin.vpn.activities.start.StartActivity; + +public class IndexPageAdapter extends FragmentStateAdapter { + public interface RequestTabListener { + void onOpenStatusRequested(); + void onOpenServerListRequested(); + } + + private StartActivity tab1 = new StartActivity(); + private ServersActivity tab2 = new ServersActivity(); + private SettingsActivity tab3 = new SettingsActivity(); + + public IndexPageAdapter(AppCompatActivity activity) { + super(activity); + } + + public void setRequestTabListener(RequestTabListener listener) { + tab1.setRequestTabListener(listener); + tab2.setRequestTabListener(listener); + } + + @Override + public Fragment createFragment(int position) { + Fragment response; + + if (position == 0) { + response = tab1; + } else if (position == 1) { + response = tab2; + } else { + response = tab3; + } + + return response; + } + + @Override + public int getItemCount() { + return 3; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/main/MainActivity.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/main/MainActivity.java new file mode 100644 index 0000000000..222be391bf --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/main/MainActivity.java @@ -0,0 +1,303 @@ +package com.skywire.skycoin.vpn.activities.main; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.settings.SettingsActivity; +import com.skywire.skycoin.vpn.activities.start.StartActivity; +import com.skywire.skycoin.vpn.helpers.Notifications; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.objects.ManualVpnServerData; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNStates; +import com.skywire.skycoin.vpn.activities.apps.AppsActivity; +import com.skywire.skycoin.vpn.activities.servers.ServersActivity; +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +import java.util.HashSet; + +import io.reactivex.rxjava3.disposables.Disposable; +import skywiremob.Skywiremob; + +public class MainActivity extends AppCompatActivity implements View.OnClickListener { + + private EditText editTextRemotePK; + private EditText editTextPasscode; + private Button buttonStart; + private Button buttonStop; + private Button buttonSelect; + private Button buttonApps; + private Button buttonSettings; + private Button buttonStartPage; + private TextView textLastError1; + private TextView textLastError2; + private TextView textStatus; + private TextView textFinishAlert; + private TextView textStopAlert; + + private Disposable serviceSubscription; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + editTextRemotePK = findViewById(R.id.editTextRemotePK); + editTextPasscode = findViewById(R.id.editTextPasscode); + buttonStart = findViewById(R.id.buttonStart); + buttonStop = findViewById(R.id.buttonStop); + buttonSelect = findViewById(R.id.buttonSelect); + buttonApps = findViewById(R.id.buttonApps); + buttonSettings = findViewById(R.id.buttonSettings); + buttonStartPage = findViewById(R.id.buttonStartPage); + textStatus = findViewById(R.id.textStatus); + textFinishAlert = findViewById(R.id.textFinishAlert); + textLastError1 = findViewById(R.id.textLastError1); + textLastError2 = findViewById(R.id.textLastError2); + textStopAlert = findViewById(R.id.textStopAlert); + + buttonStart.setOnClickListener(this); + buttonStop.setOnClickListener(this); + buttonSelect.setOnClickListener(this); + buttonApps.setOnClickListener(this); + buttonSettings.setOnClickListener(this); + buttonStartPage.setOnClickListener(this); + + LocalServerData currentServer = VPNServersPersistentData.getInstance().getCurrentServer(); + String savedPk = currentServer != null ? currentServer.pk : null; + String savedPassword = currentServer != null && currentServer.password != null ? currentServer.password : ""; + + if (savedPk != null && savedPassword != null) { + editTextRemotePK.setText(savedPk); + editTextPasscode.setText(savedPassword); + } + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + editTextRemotePK.setText(savedInstanceState.getString("pk")); + editTextPasscode.setText(savedInstanceState.getString("password")); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle savedInstanceState) { + super.onSaveInstanceState(savedInstanceState); + savedInstanceState.putString("pk", editTextRemotePK.getText().toString()); + savedInstanceState.putString("password", editTextPasscode.getText().toString()); + } + + @Override + protected void onStart() { + super.onStart(); + + Notifications.removeAllAlertNotifications(); + + displayInitialState(); + + serviceSubscription = VPNCoordinator.getInstance().getEventsObservable().subscribe( + state -> { + if (state.state.val() < 10) { + displayInitialState(); + } else if (state.state != VPNStates.ERROR && state.state != VPNStates.BLOCKING_ERROR && state.state != VPNStates.DISCONNECTED) { + int stateText = VPNStates.getDescriptionForState(state.state); + + displayWorkingState(); + + if (state.startedByTheSystem) { + this.buttonStop.setEnabled(false); + textStopAlert.setVisibility(View.VISIBLE); + } + + if (state.stopRequested) { + this.buttonStop.setEnabled(false); + } + + if (stateText != -1) { + textStatus.setText(stateText); + } + } else if (state.state == VPNStates.DISCONNECTED) { + textStatus.setText(R.string.vpn_state_disconnected); + displayInitialState(); + } else { + textStatus.setText(VPNStates.getDescriptionForState(state.state)); + displayErrorState(state.stopRequested); + } + } + ); + } + + @Override + protected void onStop() { + super.onStop(); + + serviceSubscription.dispose(); + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.buttonStart: + start(); + break; + case R.id.buttonStop: + stop(); + break; + case R.id.buttonSelect: + selectServer(); + break; + case R.id.buttonApps: + selectApps(); + break; + case R.id.buttonSettings: + openSettings(); + break; + case R.id.buttonStartPage: + openStarPage(); + break; + } + } + + @Override + protected void onActivityResult(int request, int result, Intent data) { + super.onActivityResult(request, result, data); + + if (request == VPNCoordinator.VPN_PREPARATION_REQUEST_CODE) { + VPNCoordinator.getInstance().onActivityResult(request, result, data); + } else if (request == 1 && data != null) { + String address = data.getStringExtra(ServersActivity.ADDRESS_DATA_PARAM); + if (address != null) { + editTextRemotePK.setText(address); + editTextPasscode.setText(""); + } + + start(); + } + } + + private void start() { + // Check if the pk is valid. + String remotePK = editTextRemotePK.getText().toString().trim(); + long err = Skywiremob.isPKValid(remotePK).getCode(); + if (err != Skywiremob.ErrCodeNoError) { + HelperFunctions.showToast(getString(R.string.vpn_coordinator_invalid_credentials_error) + remotePK, false); + return; + } else { + Skywiremob.printString("PK is correct"); + } + + Globals.AppFilteringModes selectedMode = VPNGeneralPersistentData.getAppsSelectionMode(); + if (selectedMode != Globals.AppFilteringModes.PROTECT_ALL) { + HashSet selectedApps = HelperFunctions.filterAvailableApps(VPNGeneralPersistentData.getAppList(new HashSet<>())); + + if (selectedApps.size() == 0) { + if (selectedMode == Globals.AppFilteringModes.PROTECT_SELECTED) { + HelperFunctions.showToast(getString(R.string.vpn_no_apps_to_protect_warning), false); + } else { + HelperFunctions.showToast(getString(R.string.vpn_no_apps_to_ignore_warning), false); + } + } + } + + ManualVpnServerData intermediaryServerData = new ManualVpnServerData(); + intermediaryServerData.pk = remotePK; + intermediaryServerData.password = editTextPasscode.getText().toString(); + LocalServerData server = VPNServersPersistentData.getInstance().processFromManual(intermediaryServerData); + + VPNCoordinator.getInstance().startVPN( + this, + server + ); + } + + private void stop() { + VPNCoordinator.getInstance().stopVPN(); + } + + private void selectServer() { + Intent intent = new Intent(this, ServersActivity.class); + startActivityForResult(intent, 1); + } + + private void selectApps() { + Intent intent = new Intent(this, AppsActivity.class); + startActivity(intent); + } + + private void openSettings() { + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + } + + private void openStarPage() { + Intent intent = new Intent(this, StartActivity.class); + startActivity(intent); + } + + private void displayInitialState() { + textStatus.setText(R.string.vpn_state_details_off); + + editTextRemotePK.setEnabled(true); + editTextPasscode.setEnabled(true); + buttonStart.setEnabled(true); + buttonStop.setEnabled(false); + buttonSelect.setEnabled(true); + buttonApps.setEnabled(true); + buttonSettings.setEnabled(true); + textFinishAlert.setVisibility(View.GONE); + textStopAlert.setVisibility(View.GONE); + + String lastError = VPNGeneralPersistentData.getLastError(null); + if (lastError != null) { + textLastError1.setVisibility(View.VISIBLE); + textLastError2.setVisibility(View.VISIBLE); + textLastError2.setText(lastError); + } else { + textLastError1.setVisibility(View.GONE); + textLastError2.setVisibility(View.GONE); + } + } + + private void displayWorkingState() { + editTextRemotePK.setEnabled(false); + editTextPasscode.setEnabled(false); + buttonStart.setEnabled(false); + buttonStop.setEnabled(true); + buttonSelect.setEnabled(false); + buttonApps.setEnabled(false); + buttonSettings.setEnabled(false); + textFinishAlert.setVisibility(View.GONE); + textStopAlert.setVisibility(View.GONE); + + textLastError1.setVisibility(View.GONE); + textLastError2.setVisibility(View.GONE); + } + + private void displayErrorState(boolean stopRequested) { + editTextRemotePK.setEnabled(false); + editTextPasscode.setEnabled(false); + buttonStart.setEnabled(false); + buttonStop.setEnabled(!stopRequested); + buttonSelect.setEnabled(false); + buttonApps.setEnabled(false); + buttonSettings.setEnabled(false); + textFinishAlert.setVisibility(stopRequested ? View.VISIBLE : View.GONE); + textStopAlert.setVisibility(View.GONE); + + textLastError1.setVisibility(View.VISIBLE); + textLastError2.setVisibility(View.VISIBLE); + + String lastError = VPNGeneralPersistentData.getLastError(null); + textLastError2.setText(lastError); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ConditionsList.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ConditionsList.java new file mode 100644 index 0000000000..987dbc6a19 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ConditionsList.java @@ -0,0 +1,147 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; +import com.skywire.skycoin.vpn.helpers.CountriesList; + +public class ConditionsList extends ButtonBase implements View.OnTouchListener { + private FrameLayout mainContainer; + private LinearLayout filtersContainer; + private LinearLayout orderContainer; + private TextView textFilters; + private TextView textOrder; + + public ConditionsList(Context context) { + super(context); + } + public ConditionsList(Context context, AttributeSet attrs) { + super(context, attrs); + } + public ConditionsList(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_server_list_condition_list, this, true); + + mainContainer = this.findViewById (R.id.mainContainer); + filtersContainer = this.findViewById (R.id.filtersContainer); + orderContainer = this.findViewById (R.id.orderContainer); + textFilters = this.findViewById (R.id.textFilters); + textOrder = this.findViewById (R.id.textOrder); + + mainContainer.setVisibility(GONE); + + setOnTouchListener(this); + setViewForCheckingClicks(this); + } + + public void setConditions(VpnServersAdapter.SortableColumns column, boolean sortingReversed, FilterModalWindow.Filters filters) { + if (filters == null && column == VpnServersAdapter.SortableColumns.AUTOMATIC) { + mainContainer.setVisibility(GONE); + } else { + boolean showingValues = false; + + if (filters != null) { + String filterList = ""; + if (filters.countryCode != null && !filters.countryCode.equals("")) { + filterList += getContext().getText(R.string.filter_server_country_label) + " \"" + CountriesList.getCountryName(filters.countryCode) + "\""; + } + + if (filters.name != null && !filters.name.equals("")) { + if (filterList.length() > 0) { + filterList += " / "; + } + + filterList += getContext().getText(R.string.filter_server_name_label) + " \"" + filters.name + "\""; + } + + if (filters.location != null && !filters.location.equals("")) { + if (filterList.length() > 0) { + filterList += " / "; + } + + filterList += getContext().getText(R.string.filter_server_location_label) + " \"" + filters.location + "\""; + } + + if (filters.pk != null && !filters.pk.equals("")) { + if (filterList.length() > 0) { + filterList += " / "; + } + + filterList += getContext().getText(R.string.filter_server_public_key_label) + " \"" + filters.pk + "\""; + } + + if (filters.note != null && !filters.note.equals("")) { + if (filterList.length() > 0) { + filterList += " / "; + } + + filterList += getContext().getText(R.string.filter_server_note_label) + " \"" + filters.note + "\""; + } + + if (filterList.length() > 0) { + filtersContainer.setVisibility(VISIBLE); + textFilters.setText(filterList); + + showingValues = true; + } else { + filtersContainer.setVisibility(GONE); + } + } else { + filtersContainer.setVisibility(GONE); + } + + if (column != VpnServersAdapter.SortableColumns.AUTOMATIC) { + String columnName = getContext().getText(VpnServersAdapter.SortableColumns.getColumnNameId(column)).toString(); + + if (sortingReversed) { + columnName += " " + getContext().getText(R.string.tmp_select_server_reversed_suffix); + } + + orderContainer.setVisibility(VISIBLE); + textOrder.setText(getContext().getText(R.string.tmp_select_server_sorted_by_prefix) + " \"" + columnName + "\""); + + showingValues = true; + } else { + orderContainer.setVisibility(GONE); + } + + if (showingValues) { + mainContainer.setVisibility(VISIBLE); + } else { + mainContainer.setVisibility(GONE); + } + } + } + + public boolean showingFilters() { + return mainContainer.getVisibility() != GONE && filtersContainer.getVisibility() != GONE; + } + + public boolean showingOrder() { + return mainContainer.getVisibility() != GONE && orderContainer.getVisibility() != GONE; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + setAlpha(0.5f); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP || event.getAction() == MotionEvent.ACTION_UP) { + setAlpha(1f); + } + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/FilterModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/FilterModalWindow.java new file mode 100644 index 0000000000..cd08118912 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/FilterModalWindow.java @@ -0,0 +1,167 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.ModalWindowButton; +import com.skywire.skycoin.vpn.controls.Select; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.CountriesList; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; + +public class FilterModalWindow extends Dialog implements ClickEvent { + public static class Filters { + public String countryCode; + public String name; + public String location; + public String pk; + public String note; + } + + public interface Confirmed { + void confirmed(Filters filters); + } + + private Select selectCountry; + private EditText editName; + private EditText editLocation; + private EditText editPk; + private EditText editNote; + private ModalWindowButton buttonCancel; + private ModalWindowButton buttonConfirm; + + private HashSet countries; + private Filters currentFilters; + private Confirmed event; + + public FilterModalWindow(Context ctx, HashSet countries, Filters currentFilters, Confirmed event) { + super(ctx); + + this.countries = countries; + this.currentFilters = currentFilters; + this.event = event; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_server_filters_modal); + + selectCountry = findViewById(R.id.selectCountry); + editName = findViewById(R.id.editName); + editLocation = findViewById(R.id.editLocation); + editPk = findViewById(R.id.editPk); + editNote = findViewById(R.id.editNote); + buttonCancel = findViewById(R.id.buttonCancel); + buttonConfirm = findViewById(R.id.buttonConfirm); + + ArrayList countryOptions = new ArrayList<>(); + Select.SelectOption option = new Select.SelectOption(); + option.text = getContext().getString(R.string.filter_server_any_country_option); + countryOptions.add(option); + + Comparator comparator = (a, b) -> a.compareTo(b); + ArrayList countriesList = new ArrayList<>(countries); + Collections.sort(countriesList, comparator); + + int i = 1; + HashMap countryIndexMap = new HashMap<>(); + for (String countryCode : countriesList) { + countryCode = countryCode.toLowerCase(); + option = new Select.SelectOption(); + option.text = CountriesList.getCountryName(countryCode); + option.value = countryCode; + option.iconId = HelperFunctions.getFlagResourceId(countryCode); + countryOptions.add(option); + + countryIndexMap.put(countryCode, i); + i++; + } + + if (currentFilters != null) { + editName.setText(currentFilters.name); + editLocation.setText(currentFilters.location); + editPk.setText(currentFilters.pk); + editNote.setText(currentFilters.note); + } + + editName.setSelection(editName.getText().length()); + + if (currentFilters != null && currentFilters.countryCode != null) { + int selectedIndex = countryIndexMap.containsKey(currentFilters.countryCode) ? countryIndexMap.get(currentFilters.countryCode) : 0; + selectCountry.setValues(countryOptions, selectedIndex); + } else { + selectCountry.setValues(countryOptions, 0); + } + + editName.setImeOptions(EditorInfo.IME_ACTION_NEXT); + editLocation.setImeOptions(EditorInfo.IME_ACTION_NEXT); + editPk.setImeOptions(EditorInfo.IME_ACTION_NEXT); + editNote.setImeOptions(EditorInfo.IME_ACTION_DONE); + + editNote.setOnEditorActionListener((v, actionId, event) -> { + if ( + actionId == EditorInfo.IME_ACTION_DONE || + (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) + ) { + process(); + dismiss(); + + return true; + } + + return false; + }); + + buttonCancel.setClickEventListener(this); + buttonConfirm.setClickEventListener(this); + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.buttonConfirm) { + process(); + } + + dismiss(); + } + + private void process() { + if (event != null) { + Filters filters = new Filters(); + + filters.countryCode = selectCountry.getSelectedValue(); + + if (editName.getText() != null && !editName.getText().toString().trim().equals("")) { + filters.name = editName.getText().toString().trim(); + } + if (editLocation.getText() != null && !editLocation.getText().toString().trim().equals("")) { + filters.location = editLocation.getText().toString().trim(); + } + if (editPk.getText() != null && !editPk.getText().toString().trim().equals("")) { + filters.pk = editPk.getText().toString().trim(); + } + if (editNote.getText() != null && !editNote.getText().toString().trim().equals("")) { + filters.note = editNote.getText().toString().trim(); + } + + event.confirmed(filters); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListButton.java new file mode 100644 index 0000000000..fc2d4cfab5 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListButton.java @@ -0,0 +1,176 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.controls.ServerName; +import com.skywire.skycoin.vpn.controls.SettingsButton; +import com.skywire.skycoin.vpn.extensible.ListButtonBase; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +public class ServerListButton extends ListButtonBase { + public static final float APROX_HEIGHT_DP = 55; + + private static DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm a"); + + private BoxRowLayout mainLayout; + private ImageView imageFlag; + private ServerName serverName; + private TextView textDate; + private TextView textLocation; + private TextView textNote; + private TextView textPersonalNote; + private LinearLayout noteArea; + private LinearLayout personalNoteArea; + private SettingsButton buttonSettings; + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + private LinearLayout statsArea1; + private LinearLayout statsArea2; + private TextView textLatency; + private TextView textCongestion; + private TextView textHops; + private TextView textLatencyRating; + private TextView textCongestionRating; + */ + + private VpnServerForList server; + private ServerLists listType; + + public ServerListButton (Context context) { + super(context); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_server_list_item, this, true); + + mainLayout = this.findViewById (R.id.mainLayout); + imageFlag = this.findViewById (R.id.imageFlag); + serverName = this.findViewById (R.id.serverName); + textDate = this.findViewById (R.id.textDate); + textLocation = this.findViewById (R.id.textLocation); + textNote = this.findViewById (R.id.textNote); + textPersonalNote = this.findViewById (R.id.textPersonalNote); + noteArea = this.findViewById (R.id.noteArea); + personalNoteArea = this.findViewById (R.id.personalNoteArea); + buttonSettings = this.findViewById (R.id.buttonSettings); + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + statsArea1 = this.findViewById (R.id.statsArea1); + statsArea2 = this.findViewById (R.id.statsArea2); + textLatency = this.findViewById (R.id.textLatency); + textCongestion = this.findViewById (R.id.textCongestion); + textHops = this.findViewById (R.id.textHops); + textLatencyRating = this.findViewById (R.id.textLatencyRating); + textCongestionRating = this.findViewById (R.id.textCongestionRating); + */ + + imageFlag.setClipToOutline(true); + + buttonSettings.setClickEventListener(view -> showOptions()); + + setClickableBoxView(mainLayout); + } + + public void changeData(@NonNull VpnServerForList serverData, ServerLists listType) { + server = serverData; + this.listType = listType; + + imageFlag.setImageResource(HelperFunctions.getFlagResourceId(serverData.countryCode)); + serverName.setServer(serverData, listType, false); + + if (serverData.location != null && !serverData.location.trim().equals("")) { + String pk = serverData.pk; + if (pk.length() > 5) { + pk = pk.substring(0, 5); + } + textLocation.setText("(" + pk + ") " + serverData.location); + } else { + textLocation.setText(serverData.pk); + } + + if (serverData.note != null && serverData.note.trim() != "") { + noteArea.setVisibility(VISIBLE); + textNote.setText(serverData.note); + } else { + noteArea.setVisibility(GONE); + } + if (serverData.personalNote != null && serverData.personalNote.trim() != "") { + personalNoteArea.setVisibility(VISIBLE); + textPersonalNote.setText(serverData.personalNote); + } else { + personalNoteArea.setVisibility(GONE); + } + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + if (listType == ServerLists.Public) { + statsArea1.setVisibility(VISIBLE); + statsArea2.setVisibility(VISIBLE); + + textLatency.setText(HelperFunctions.getLatencyValue(serverData.latency)); + textCongestion.setText(HelperFunctions.zeroDecimalsFormatter.format(serverData.congestion) + "%"); + textHops.setText(serverData.hops + ""); + + textLatencyRating.setText(ServerRatings.getTextForRating(serverData.latencyRating)); + textLatencyRating.setTextColor(getRatingColor(serverData.latencyRating)); + textCongestionRating.setText(ServerRatings.getTextForRating(serverData.congestionRating)); + textCongestionRating.setTextColor(getRatingColor(serverData.congestionRating)); + + textCongestion.setTextColor(HelperFunctions.getCongestionNumberColor((int)serverData.congestion)); + textLatency.setTextColor(HelperFunctions.getLatencyNumberColor((int)serverData.latency)); + textHops.setTextColor(HelperFunctions.getHopsNumberColor((int)serverData.hops)); + } else { + statsArea1.setVisibility(GONE); + statsArea2.setVisibility(GONE); + } + */ + + if (listType == ServerLists.History) { + textDate.setVisibility(VISIBLE); + textDate.setText(dateFormat.format(serverData.lastUsed)); + } else { + textDate.setVisibility(GONE); + } + } + + public void setBoxRowType(BoxRowTypes type) { + mainLayout.setType(type); + } + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + private int getRatingColor(ServerRatings rating) { + int colorId = R.color.bronze; + + if (rating == ServerRatings.Gold) { + colorId = R.color.gold; + } else if (rating == ServerRatings.Silver) { + colorId = R.color.silver; + } + + return ContextCompat.getColor(getContext(), colorId); + } + */ + + private void showOptions() { + HelperFunctions.showServerOptions(getContext(), server, listType); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListOptionButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListOptionButton.java new file mode 100644 index 0000000000..be47576a00 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListOptionButton.java @@ -0,0 +1,53 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.extensible.ButtonBase; + +public class ServerListOptionButton extends ButtonBase { + + private BoxRowLayout mainLayout; + private TextView textIcon; + + public ServerListOptionButton(Context context) { + super(context); + } + public ServerListOptionButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + public ServerListOptionButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_server_list_option_button, this, true); + + mainLayout = this.findViewById (R.id.mainLayout); + textIcon = this.findViewById (R.id.textIcon); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.ServerListOptionButton, + 0, 0 + ); + + String content = attributes.getString(R.styleable.ServerListOptionButton_content); + if (content != null && content.trim() != "") { + textIcon.setText(content); + } + + attributes.recycle(); + } + + setClickableBoxView(mainLayout); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListOptions.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListOptions.java new file mode 100644 index 0000000000..89dbfa4329 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListOptions.java @@ -0,0 +1,121 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.recyclerview.widget.RecyclerView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.extensible.ClickWithIndexEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +public class ServerListOptions extends FrameLayout implements ClickEvent { + public static final int filterIndex = -1; + public static final int addIndex = -2; + public static final int sortIndex = -3; + public static final int showPublicIndex = -10; + public static final int showHistoryIndex = -11; + public static final int showFavoritesIndex = -12; + public static final int showBlockedIndex = -13; + + public ServerListOptions(Context context) { + super(context); + Initialize(context, null); + } + public ServerListOptions(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public ServerListOptions(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private BoxRowLayout tabsContainer; + private ServerListTopTab tabPublic; + private ServerListTopTab tabHistory; + private ServerListTopTab tabFavorites; + private ServerListTopTab tabBlocked; + private ServerListOptionButton buttonSort; + private ServerListOptionButton buttonFilter; + private ServerListOptionButton buttonAdd; + + private ClickWithIndexEvent clickListener; + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + View rootView = inflater.inflate(R.layout.view_server_list_options, this, true); + + tabsContainer = this.findViewById (R.id.tabsContainer); + tabPublic = this.findViewById (R.id.tabPublic); + tabHistory = this.findViewById (R.id.tabHistory); + tabFavorites = this.findViewById (R.id.tabFavorites); + tabBlocked = this.findViewById (R.id.tabBlocked); + buttonSort = this.findViewById (R.id.buttonSort); + buttonFilter = this.findViewById (R.id.buttonFilter); + buttonAdd = this.findViewById (R.id.buttonAdd); + + tabPublic.setClickEventListener(this); + tabHistory.setClickEventListener(this); + tabFavorites.setClickEventListener(this); + tabBlocked.setClickEventListener(this); + buttonSort.setClickEventListener(this); + buttonFilter.setClickEventListener(this); + buttonAdd.setClickEventListener(this); + + RecyclerView.LayoutParams params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + rootView.setLayoutParams(params); + + if (HelperFunctions.getWidthType(getContext()) == HelperFunctions.WidthTypes.SMALL) { + tabsContainer.setVisibility(GONE); + } + } + + public void setClickWithIndexEventListener(ClickWithIndexEvent listener) { + clickListener = listener; + } + + public void selectCorrectTab(ServerLists currentListType) { + tabPublic.changeState(false); + tabHistory.changeState(false); + tabFavorites.changeState(false); + tabBlocked.changeState(false); + + if (currentListType == ServerLists.Public) { + tabPublic.changeState(true); + } else if (currentListType == ServerLists.History) { + tabHistory.changeState(true); + } else if (currentListType == ServerLists.Favorites) { + tabFavorites.changeState(true); + } else if (currentListType == ServerLists.Blocked) { + tabBlocked.changeState(true); + } + } + + @Override + public void onClick(View view) { + if (clickListener != null) { + if (view.getId() == R.id.tabPublic) { + clickListener.onClickWithIndex(showPublicIndex, null); + } else if (view.getId() == R.id.tabHistory) { + clickListener.onClickWithIndex(showHistoryIndex, null); + } else if (view.getId() == R.id.tabFavorites) { + clickListener.onClickWithIndex(showFavoritesIndex, null); + } else if (view.getId() == R.id.tabBlocked) { + clickListener.onClickWithIndex(showBlockedIndex, null); + } else if (view.getId() == R.id.buttonSort) { + clickListener.onClickWithIndex(sortIndex, null); + } else if (view.getId() == R.id.buttonAdd) { + clickListener.onClickWithIndex(addIndex, null); + } else if (view.getId() == R.id.buttonFilter) { + clickListener.onClickWithIndex(filterIndex, null); + } + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTableHeader.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTableHeader.java new file mode 100644 index 0000000000..ef33a7c910 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTableHeader.java @@ -0,0 +1,45 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; + +public class ServerListTableHeader extends FrameLayout { + private TextView textDate; + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + // private LinearLayout statsArea; + + public ServerListTableHeader(Context context) { + super(context); + + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_server_list_table_header, this, true); + + textDate = this.findViewById (R.id.textDate); + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + // statsArea = this.findViewById (R.id.statsArea); + } + + public void setListType(ServerLists listType) { + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + if (listType == ServerLists.Public) { + statsArea.setVisibility(VISIBLE); + } else { + statsArea.setVisibility(GONE); + } + */ + + if (listType == ServerLists.History) { + textDate.setVisibility(VISIBLE); + } else { + textDate.setVisibility(GONE); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTableRow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTableRow.java new file mode 100644 index 0000000000..707081cd69 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTableRow.java @@ -0,0 +1,162 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.controls.ServerName; +import com.skywire.skycoin.vpn.controls.ServerNotesModalWindow; +import com.skywire.skycoin.vpn.controls.SettingsButton; +import com.skywire.skycoin.vpn.extensible.ListButtonBase; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +public class ServerListTableRow extends ListButtonBase { + public static final float APROX_HEIGHT_DP = 50; + + private static DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm a"); + + private BoxRowLayout mainLayout; + private ImageView imageFlag; + private ServerName serverName; + private TextView textDate; + private TextView textLocation; + private TextView textPk; + private SettingsButton buttonNote; + private SettingsButton buttonSettings; + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + private ImageView imageCongestionRating; + private ImageView imageLatencyRating; + private TextView textCongestion; + private TextView textLatency; + private TextView textHops; + private LinearLayout statsArea; + */ + + private VpnServerForList server; + private ServerLists listType; + + public ServerListTableRow(Context context) { + super(context); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_server_list_table_row, this, true); + + mainLayout = this.findViewById (R.id.mainLayout); + imageFlag = this.findViewById (R.id.imageFlag); + serverName = this.findViewById (R.id.serverName); + textDate = this.findViewById (R.id.textDate); + textLocation = this.findViewById (R.id.textLocation); + textPk = this.findViewById (R.id.textPk); + buttonNote = this.findViewById (R.id.buttonNote); + buttonSettings = this.findViewById (R.id.buttonSettings); + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + imageCongestionRating = this.findViewById (R.id.imageCongestionRating); + imageLatencyRating = this.findViewById (R.id.imageLatencyRating); + textCongestion = this.findViewById (R.id.textCongestion); + textLatency = this.findViewById (R.id.textLatency); + textHops = this.findViewById (R.id.textHops); + statsArea = this.findViewById (R.id.statsArea); + */ + + imageFlag.setClipToOutline(true); + + buttonNote.setClickEventListener(view -> showNotes()); + buttonSettings.setClickEventListener(view -> showOptions()); + + setClickableBoxView(mainLayout); + } + + public void changeData(@NonNull VpnServerForList serverData, ServerLists listType) { + server = serverData; + this.listType = listType; + + imageFlag.setImageResource(HelperFunctions.getFlagResourceId(serverData.countryCode)); + serverName.setServer(serverData, listType, false); + + if (serverData.location != null && serverData.location.trim().length() > 0) { + textLocation.setText(serverData.location); + } else { + textLocation.setText(R.string.tmp_select_server_unknown_location); + } + + textPk.setText(serverData.pk); + + if ((serverData.note == null || serverData.note.equals("")) && (serverData.personalNote == null || serverData.personalNote.equals(""))) { + buttonNote.setVisibility(GONE); + } else { + buttonNote.setVisibility(VISIBLE); + } + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + if (listType == ServerLists.Public) { + statsArea.setVisibility(VISIBLE); + + textCongestion.setText(HelperFunctions.zeroDecimalsFormatter.format(serverData.congestion) + "%"); + textLatency.setText(HelperFunctions.getLatencyValue(serverData.latency)); + textHops.setText(serverData.hops + ""); + + textCongestion.setTextColor(HelperFunctions.getCongestionNumberColor((int)serverData.congestion)); + textLatency.setTextColor(HelperFunctions.getLatencyNumberColor((int)serverData.latency)); + textHops.setTextColor(HelperFunctions.getHopsNumberColor((int)serverData.hops)); + + imageCongestionRating.setImageResource(getRatingResource(serverData.congestionRating)); + imageLatencyRating.setImageResource(getRatingResource(serverData.latencyRating)); + } else { + statsArea.setVisibility(GONE); + } + */ + + if (listType == ServerLists.History) { + textDate.setVisibility(VISIBLE); + textDate.setText(dateFormat.format(serverData.lastUsed)); + } else { + textDate.setVisibility(GONE); + } + } + + public void setBoxRowType(BoxRowTypes type) { + mainLayout.setType(type); + } + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + // TODO: if the fields are removed, the images should be removed too. + /* + private int getRatingResource(ServerRatings rating) { + if (rating == ServerRatings.Gold) { + return R.drawable.gold_rating; + } else if (rating == ServerRatings.Silver) { + return R.drawable.silver_rating; + } + + return R.drawable.bronze_rating; + } + */ + + private void showNotes() { + ServerNotesModalWindow modal = new ServerNotesModalWindow(getContext(), server); + modal.show(); + } + + private void showOptions() { + HelperFunctions.showServerOptions(getContext(), server, listType); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTopTab.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTopTab.java new file mode 100644 index 0000000000..8be2b43c74 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerListTopTab.java @@ -0,0 +1,94 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; + +public class ServerListTopTab extends ButtonBase implements View.OnTouchListener { + private FrameLayout mainLayout; + private View clickBackground; + private TextView text; + + private RippleDrawable rippleDrawable; + + public ServerListTopTab(Context context) { + super(context); + } + public ServerListTopTab(Context context, AttributeSet attrs) { + super(context, attrs); + } + public ServerListTopTab(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_server_list_top_tab, this, true); + + mainLayout = this.findViewById (R.id.mainLayout); + clickBackground = this.findViewById (R.id.clickBackground); + text = this.findViewById (R.id.text); + + rippleDrawable = (RippleDrawable) clickBackground.getBackground(); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.ServerListTopTab, + 0, 0 + ); + + int corner = attributes.getInteger(R.styleable.ServerListTopTab_position, 0); + if (corner != 0) { + if (corner == 1) { + mainLayout.setBackgroundResource(R.drawable.box_clip_area_left); + } else if (corner == 2) { + mainLayout.setBackgroundResource(R.drawable.box_clip_area_right); + } + + mainLayout.setClipToOutline(true); + } + + String txt = attributes.getString(R.styleable.ServerListTopTab_text); + if (txt != null && !txt.trim().equals("")) { + text.setText(txt); + } + + attributes.recycle(); + } + + clickBackground.setOnTouchListener(this); + setViewForCheckingClicks(clickBackground); + } + + public void changeState(boolean selected) { + if (selected) { + clickBackground.setBackgroundResource(R.color.tablet_selected_tab_background); + rippleDrawable = null; + this.setClickable(false); + } else { + clickBackground.setBackgroundResource(R.drawable.box_ripple); + rippleDrawable = (RippleDrawable) clickBackground.getBackground(); + this.setClickable(true); + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (rippleDrawable != null) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerLists.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerLists.java new file mode 100644 index 0000000000..48c6b2b807 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServerLists.java @@ -0,0 +1,8 @@ +package com.skywire.skycoin.vpn.activities.servers; + +public enum ServerLists { + Public, + History, + Favorites, + Blocked +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServersActivity.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServersActivity.java new file mode 100644 index 0000000000..df46ce0362 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/ServersActivity.java @@ -0,0 +1,437 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.preference.PreferenceManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.gson.Gson; +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.index.IndexPageAdapter; +import com.skywire.skycoin.vpn.controls.Tab; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.network.ApiClient; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.objects.ServerFlags; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +import java.util.ArrayList; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class ServersActivity extends Fragment implements VpnServersAdapter.VpnServerListEventListener, ClickEvent { + public static String ADDRESS_DATA_PARAM = "address"; + private static final String ACTIVE_TAB_KEY = "activeTab"; + + private Tab tabPublic; + private Tab tabHistory; + private Tab tabFavorites; + private Tab tabBlocked; + private RecyclerView recycler; + private ProgressBar loadingAnimation; + private TextView textNoResults; + private LinearLayout noResultsContainer; + private LinearLayout bottomTabsContainer; + private FrameLayout internalContainer; + private ImageView ImageBottomTabsShadow; + + private IndexPageAdapter.RequestTabListener requestTabListener; + private ServerLists listType = ServerLists.Public; + private VpnServersAdapter adapter; + private SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(App.getContext()); + + private Disposable serverSubscription; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + + return inflater.inflate(R.layout.activity_server_list, container, true); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + tabPublic = view.findViewById(R.id.tabPublic); + tabHistory = view.findViewById(R.id.tabHistory); + tabFavorites = view.findViewById(R.id.tabFavorites); + tabBlocked = view.findViewById(R.id.tabBlocked); + recycler = view.findViewById(R.id.recycler); + loadingAnimation = view.findViewById(R.id.loadingAnimation); + textNoResults = view.findViewById(R.id.textNoResults); + noResultsContainer = view.findViewById(R.id.noResultsContainer); + bottomTabsContainer = view.findViewById(R.id.bottomTabsContainer); + internalContainer = view.findViewById(R.id.internalContainer); + ImageBottomTabsShadow = view.findViewById(R.id.ImageBottomTabsShadow); + + tabPublic.setClickEventListener(this); + tabHistory.setClickEventListener(this); + tabFavorites.setClickEventListener(this); + tabBlocked.setClickEventListener(this); + + LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); + recycler.setLayoutManager(layoutManager); + + // This code retrieves the data from the server and populates the list with the recovered + // data, but is not used right now as the server is returning empty arrays. + // requestData() + + noResultsContainer.setVisibility(View.GONE); + loadingAnimation.setVisibility(View.VISIBLE); + + // Initialize the recycler. + adapter = new VpnServersAdapter(getContext()); + adapter.setVpnServerListEventListener(this); + adapter.setData(new ArrayList<>(), listType); + recycler.setAdapter(adapter); + + Gson gson = new Gson(); + String savedlistType = settings.getString(ACTIVE_TAB_KEY, null); + if (savedlistType != null) { + listType = gson.fromJson(savedlistType, ServerLists.class); + } + + showCorrectList(); + + if (HelperFunctions.getWidthType(getContext()) != HelperFunctions.WidthTypes.SMALL) { + bottomTabsContainer.setVisibility(View.GONE); + ImageBottomTabsShadow.setVisibility(View.GONE); + + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)internalContainer.getLayoutParams(); + params.bottomMargin = 0; + internalContainer.setLayoutParams(params); + } + } + + public void setRequestTabListener(IndexPageAdapter.RequestTabListener listener) { + requestTabListener = listener; + } + + @Override + public void tabChangeRequested(ServerLists newListType) { + if (newListType != listType) { + listType = newListType; + + finishChangingTab(); + } + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.tabPublic) { + listType = ServerLists.Public; + } else if (view.getId() == R.id.tabHistory) { + listType = ServerLists.History; + } else if (view.getId() == R.id.tabFavorites) { + listType = ServerLists.Favorites; + } else if (view.getId() == R.id.tabBlocked) { + listType = ServerLists.Blocked; + } + + finishChangingTab(); + } + + private void finishChangingTab() { + Gson gson = new Gson(); + String listTypeString = gson.toJson(listType); + settings.edit() + .putString(ACTIVE_TAB_KEY, listTypeString) + .apply(); + + showCorrectList(); + } + + private void showCorrectList() { + tabPublic.changeState(false); + tabHistory.changeState(false); + tabFavorites.changeState(false); + tabBlocked.changeState(false); + + if (listType == ServerLists.Public) { + tabPublic.changeState(true); + // Use test data, for now. + showTestServers(); + } else { + if (listType == ServerLists.History) { + tabHistory.changeState(true); + } else if (listType == ServerLists.Favorites) { + tabFavorites.changeState(true); + } else if (listType == ServerLists.Blocked) { + tabBlocked.changeState(true); + } + + requestLocalData(); + } + } + + private void requestData() { + if (serverSubscription != null) { + serverSubscription.dispose(); + } +/* + serverSubscription = ApiClient.getVpnServers().subscribe(response -> { + ArrayList list = new ArrayList<>(); + + for (LocalServerData server : response) { + list.add(convertLocalServerData(server)); + } + + + VpnServersAdapter adapter = new VpnServersAdapter(this, response.body()); + adapter.setVpnSelectedEventListener(this); + recycler.setAdapter(adapter); + + // TODO: addSavedData will remove all blocked servers, so it will have to be called + // every time the blocked servers list changes. + }, err -> { + this.requestData(); + }); + */ + } + + private void requestLocalData() { + if (serverSubscription != null) { + serverSubscription.dispose(); + } + + adapter.setData(new ArrayList<>(), listType); + noResultsContainer.setVisibility(View.GONE); + loadingAnimation.setVisibility(View.VISIBLE); + + Observable> request; + if (listType == ServerLists.History) { + request = VPNServersPersistentData.getInstance().history(); + } else if (listType == ServerLists.Favorites) { + request = VPNServersPersistentData.getInstance().favorites(); + } else { + request = VPNServersPersistentData.getInstance().blocked(); + } + + serverSubscription = request.subscribe(response -> { + ArrayList list = new ArrayList<>(); + + for (LocalServerData server : response) { + list.add(convertLocalServerData(server)); + } + + loadingAnimation.setVisibility(View.GONE); + + adapter.setData(list, listType); + }); + } + + public static VpnServerForList convertLocalServerData(LocalServerData server) { + if (server == null) { + return null; + } + + VpnServerForList converted = new VpnServerForList(); + + converted.countryCode = server.countryCode; + converted.name = server.name; + converted.customName = server.customName; + converted.location = server.location; + converted.pk = server.pk; + converted.note = server.note; + converted.personalNote = server.personalNote; + converted.lastUsed = server.lastUsed; + converted.inHistory = server.inHistory; + converted.flag = server.flag; + converted.enteredManually = server.enteredManually; + converted.hasPassword = server.password != null && !server.password.equals(""); + + return converted; + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + if (serverSubscription != null) { + serverSubscription.dispose(); + } + } + + @Override + public void onVpnServerSelected(VpnServerForList selectedServer) { + start(VPNServersPersistentData.getInstance().processFromList(selectedServer)); + } + + @Override + public void onManualEntered(LocalServerData server) { + start(server); + } + + @Override + public void listHasElements(boolean hasElements, boolean emptyBecauseFilters) { + if (hasElements || loadingAnimation.getVisibility() != View.GONE) { + noResultsContainer.setVisibility(View.GONE); + } else { + noResultsContainer.setVisibility(View.VISIBLE); + + if (emptyBecauseFilters) { + textNoResults.setText(R.string.tmp_select_server_empty_with_filter); + } else { + if (listType == ServerLists.History) { + textNoResults.setText(R.string.tmp_select_server_empty_history); + } else if (listType == ServerLists.Favorites) { + textNoResults.setText(R.string.tmp_select_server_empty_favorites); + } else if (listType == ServerLists.Blocked) { + textNoResults.setText(R.string.tmp_select_server_empty_blocked); + } else { + textNoResults.setText(R.string.tmp_select_server_empty_discovery); + } + } + } + } + + private void start(LocalServerData server) { + if (VPNCoordinator.getInstance().isServiceRunning()) { + HelperFunctions.showToast(getContext().getText(R.string.tmp_select_server_running_error).toString(), true); + return; + } + + boolean starting = HelperFunctions.prepareAndStartVpn(getActivity(), server); + + if (starting) { + if (requestTabListener != null) { + requestTabListener.onOpenStatusRequested(); + } + } + } + + private void showTestServers() { + ArrayList servers = new ArrayList<>(); + + VpnServerForList testServer = new VpnServerForList(); + testServer.lastUsed = new Date(); + testServer.countryCode = "au"; + testServer.name = "Server name"; + testServer.location = "Melbourne"; + testServer.pk = "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7"; + /* + testServer.congestion = 20; + testServer.congestionRating = ServerRatings.Gold; + testServer.latency = 123; + testServer.latencyRating = ServerRatings.Gold; + testServer.hops = 3; + */ + testServer.note = "Note"; + servers.add(testServer); + + testServer = new VpnServerForList(); + testServer.lastUsed = new Date(); + testServer.countryCode = "br"; + testServer.name = "Test server 14"; + testServer.location = "Rio de Janeiro"; + testServer.pk = "034ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7"; + /* + testServer.congestion = 20; + testServer.congestionRating = ServerRatings.Silver; + testServer.latency = 12345; + testServer.latencyRating = ServerRatings.Gold; + testServer.hops = 3; + */ + testServer.note = "Note"; + servers.add(testServer); + + testServer = new VpnServerForList(); + testServer.lastUsed = new Date(); + testServer.countryCode = "de"; + testServer.name = "Test server 20"; + testServer.location = "Berlin"; + testServer.pk = "044ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7"; + /* + testServer.congestion = 20; + testServer.congestionRating = ServerRatings.Gold; + testServer.latency = 123; + testServer.latencyRating = ServerRatings.Bronze; + testServer.hops = 7; + */ + servers.add(testServer); + + VPNServersPersistentData.getInstance().updateFromDiscovery(servers); + + if (serverSubscription != null) { + serverSubscription.dispose(); + } + + adapter.setData(new ArrayList<>(), listType); + noResultsContainer.setVisibility(View.GONE); + loadingAnimation.setVisibility(View.VISIBLE); + + serverSubscription = Observable.just(servers).delay(50, TimeUnit.MILLISECONDS).flatMap(serversList -> + VPNServersPersistentData.getInstance().history() + ).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(r -> { + loadingAnimation.setVisibility(View.GONE); + + ArrayList serversCopy = new ArrayList<>(servers); + + removeSavedData(serversCopy); + addSavedData(serversCopy); + adapter.setData(serversCopy, ServerLists.Public); + }); + + } + + private void addSavedData(ArrayList servers) { + ArrayList remove = new ArrayList(); + for (VpnServerForList server : servers) { + LocalServerData savedVersion = VPNServersPersistentData.getInstance().getSavedVersion(server.pk); + + if (savedVersion != null) { + server.customName = savedVersion.customName; + server.personalNote = savedVersion.personalNote; + server.inHistory = savedVersion.inHistory; + server.flag = savedVersion.flag; + server.enteredManually = savedVersion.enteredManually; + server.hasPassword = savedVersion.password != null && !savedVersion.password.equals(""); + } + + if (server.flag == ServerFlags.Blocked) { + remove.add(server); + } + } + + servers.removeAll(remove); + } + + private void removeSavedData(ArrayList servers) { + for (VpnServerForList server : servers) { + server.customName = null; + server.personalNote = null; + server.inHistory = false; + server.flag = ServerFlags.None; + server.enteredManually = false; + server.hasPassword = false; + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/VpnServerForList.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/VpnServerForList.java new file mode 100644 index 0000000000..e96eaed8dd --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/VpnServerForList.java @@ -0,0 +1,32 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import com.skywire.skycoin.vpn.objects.ServerFlags; + +// TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. +// import com.skywire.skycoin.vpn.objects.ServerRatings; + +import java.util.Date; + +public class VpnServerForList { + public String countryCode; + public String name; + public String customName; + public String location; + public String pk; + public String note; + public String personalNote; + public Date lastUsed; + public boolean inHistory; + public ServerFlags flag; + public boolean hasPassword; + public boolean enteredManually; + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + public double congestion; + public ServerRatings congestionRating; + public double latency; + public ServerRatings latencyRating; + public int hops; + */ +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/VpnServersAdapter.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/VpnServersAdapter.java new file mode 100644 index 0000000000..8fe13b7247 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/servers/VpnServersAdapter.java @@ -0,0 +1,559 @@ +package com.skywire.skycoin.vpn.activities.servers; + +import android.content.Context; +import android.content.res.Resources; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.ManualServerModalWindow; +import com.skywire.skycoin.vpn.controls.options.OptionsItem; +import com.skywire.skycoin.vpn.controls.options.OptionsModalWindow; +import com.skywire.skycoin.vpn.extensible.ClickWithIndexEvent; +import com.skywire.skycoin.vpn.extensible.ListViewHolder; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; + +public class VpnServersAdapter extends RecyclerView.Adapter> implements ClickWithIndexEvent { + public interface VpnServerListEventListener { + void onVpnServerSelected(VpnServerForList selectedServer); + void onManualEntered(LocalServerData server); + void listHasElements(boolean hasElements, boolean emptyBecauseFilters); + void tabChangeRequested(ServerLists newListType); + } + + public enum SortableColumns { + AUTOMATIC, + DATE, + COUNTRY, + NAME, + LOCATION, + PK, + /* + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + CONGESTION, + CONGESTION_RATING, + LATENCY, + LATENCY_RATING, + HOPS, + */ + NOTE; + + public static int getColumnNameId(SortableColumns column) { + if (column == SortableColumns.NAME) { + return R.string.tmp_select_server_name_label; + } else if (column == SortableColumns.DATE) { + return R.string.tmp_select_server_date_label; + } else if (column == SortableColumns.COUNTRY) { + return R.string.tmp_select_server_country_label; + } else if (column == SortableColumns.LOCATION) { + return R.string.tmp_select_server_location_label; + } else if (column == SortableColumns.PK) { + return R.string.tmp_select_server_public_key_label; + /* + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + } else if (column == SortableColumns.CONGESTION) { + return R.string.tmp_select_server_congestion_label; + } else if (column == SortableColumns.CONGESTION_RATING) { + return R.string.tmp_select_server_congestion_rating_label; + } else if (column == SortableColumns.LATENCY) { + return R.string.tmp_select_server_latency_label; + } else if (column == SortableColumns.LATENCY_RATING) { + return R.string.tmp_select_server_latency_rating_label; + } else if (column == SortableColumns.HOPS) { + return R.string.tmp_select_server_hops_label; + */ + } else { + return R.string.tmp_select_server_note_label; + } + } + } + + private Context context; + private List data; + private List filteredData; + private ServerLists listType = ServerLists.Public; + private VpnServerListEventListener listEventListener; + private boolean showingRows; + private int initialServerIndex; + + private ArrayList filters; + private ConditionsList conditionsView; + + private ArrayList sortBy; + private ArrayList sortInverse; + + private ArrayList premadeButtons = new ArrayList<>(); + private ArrayList premadeRows = new ArrayList<>(); + private int lastUsedPremadeButtonIdex = 0; + + private ServerListOptions listOptionsView; + private ServerListTableHeader tableHeader; + + public VpnServersAdapter(Context context) { + this.context = context; + + int screenHeightInDP = (int)(Resources.getSystem().getDisplayMetrics().heightPixels / context.getResources().getDisplayMetrics().density); + showingRows = HelperFunctions.getWidthType(context) != HelperFunctions.WidthTypes.SMALL; + + if (!showingRows) { + int aproxButtonsToFillScreen = (int)Math.ceil((screenHeightInDP / ServerListButton.APROX_HEIGHT_DP) * 1.3); + for (int i = 0; i < aproxButtonsToFillScreen; i++) { + premadeButtons.add(createNewServerButton()); + } + initialServerIndex = 2; + } else { + int aproxButtonsToFillScreen = (int)Math.ceil((screenHeightInDP / ServerListTableRow.APROX_HEIGHT_DP) * 1.3); + for (int i = 0; i < aproxButtonsToFillScreen; i++) { + premadeRows.add(createNewServerRow()); + } + initialServerIndex = 3; + } + } + + public void setData(List data, ServerLists listType) { + this.data = data; + this.listType = listType; + + if (listOptionsView != null) { + listOptionsView.selectCorrectTab(listType); + } + + if (tableHeader != null) { + tableHeader.setListType(listType); + } + + processData(); + } + + private void processData() { + if (filters == null) { + filters = new ArrayList<>(); + sortBy = new ArrayList<>(); + sortInverse = new ArrayList<>(); + + for (int i = 0; i < 4; i++) { + filters.add(null); + sortBy.add(SortableColumns.AUTOMATIC); + sortInverse.add(false); + } + } + + FilterModalWindow.Filters currentFilters = filters.get(getCurrentListTypeIntVal()); + + if (currentFilters == null) { + filteredData = data; + } else { + filteredData = new ArrayList<>(); + + for (VpnServerForList element : data) { + boolean valid = true; + + if (valid && currentFilters.countryCode != null && !currentFilters.countryCode.equals("")) { + String elementVal = element.countryCode != null ? element.countryCode.toUpperCase() : ""; + if (!elementVal.equals(currentFilters.countryCode.toUpperCase())) { + valid = false; + } + } + + if (valid && currentFilters.name != null && !currentFilters.name.equals("")) { + if (!HelperFunctions.getServerName(element, "").toUpperCase().contains(currentFilters.name.toUpperCase())) { + valid = false; + } + } + + if (valid && currentFilters.location != null && !currentFilters.location.equals("")) { + String elementVal = element.location != null ? element.location.toUpperCase() : ""; + if (!elementVal.contains(currentFilters.location.toUpperCase())) { + valid = false; + } + } + + if (valid && currentFilters.pk != null && !currentFilters.pk.equals("")) { + if (!element.pk.toUpperCase().contains(currentFilters.pk.toUpperCase())) { + valid = false; + } + } + + if (valid && currentFilters.note != null && !currentFilters.note.equals("")) { + String elementVal1 = element.note != null ? element.note.toUpperCase() : ""; + String elementVal2 = element.personalNote != null ? element.personalNote.toUpperCase() : ""; + String filterVal = currentFilters.note.toUpperCase(); + if (!elementVal1.contains(filterVal) && !elementVal2.contains(filterVal)) { + valid = false; + } + } + + if (valid) { + filteredData.add(element); + } + } + } + + if (listEventListener != null) { + if (data.size() == 0) { + listEventListener.listHasElements(false, false); + } else { + if (filteredData.size() == 0) { + listEventListener.listHasElements(false, true); + } else { + listEventListener.listHasElements(true, false); + } + } + } + + sortList(); + } + + private void sortList() { + if (conditionsView != null) { + conditionsView.setConditions(sortBy.get(getCurrentListTypeIntVal()), sortInverse.get(getCurrentListTypeIntVal()), filters.get(getCurrentListTypeIntVal())); + } + + Comparator comparator = (a, b) -> { + SortableColumns sortColumn = sortBy.get(getCurrentListTypeIntVal()); + + if (sortColumn == SortableColumns.AUTOMATIC) { + if (listType == ServerLists.History) { + sortColumn = SortableColumns.DATE; + } else { + sortColumn = SortableColumns.COUNTRY; + } + } + + int result = 0; + if (sortColumn == SortableColumns.DATE) { + result = (int)((b.lastUsed.getTime() - a.lastUsed.getTime()) / 1000); + } else if (sortColumn == SortableColumns.COUNTRY) { + result = a.countryCode.compareTo(b.countryCode); + } else if (sortColumn == SortableColumns.NAME) { + result = HelperFunctions.getServerName(a, "").compareTo(HelperFunctions.getServerName(b, "")); + } else if (sortColumn == SortableColumns.LOCATION) { + result = (a.location != null ? a.location : "").compareTo((b.location != null ? b.location : "")); + } else if (sortColumn == SortableColumns.PK) { + result = (a.pk != null ? a.pk : "").compareTo((b.pk != null ? b.pk : "")); + /* + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + } else if (sortColumn == SortableColumns.CONGESTION) { + result = (int)(a.congestion - b.congestion); + } else if (sortColumn == SortableColumns.CONGESTION_RATING) { + result = ServerRatings.getNumberForRating(b.congestionRating) - ServerRatings.getNumberForRating(a.congestionRating); + } else if (sortColumn == SortableColumns.LATENCY) { + result = (int)(a.latency - b.latency); + } else if (sortColumn == SortableColumns.LATENCY_RATING) { + result = ServerRatings.getNumberForRating(b.latencyRating) - ServerRatings.getNumberForRating(a.latencyRating); + } else if (sortColumn == SortableColumns.HOPS) { + result = (int)(a.hops - b.hops); + */ + } else if (sortColumn == SortableColumns.NOTE) { + String noteA = ((a.note != null ? a.note : "") + " " + (a.personalNote != null ? a.personalNote : "")).trim(); + String noteB = ((b.note != null ? b.note : "") + " " + (b.personalNote != null ? b.personalNote : "")).trim(); + if (noteA.equals("") && !noteB.equals("")) { + result = 1; + } else if (noteB.equals("") && !noteA.equals("")) { + result = -1; + } else { + result = noteA.compareTo(noteB); + } + } + + if (result == 0 && sortColumn != SortableColumns.NAME) { + result = HelperFunctions.getServerName(a, "").compareTo(HelperFunctions.getServerName(b, "")); + } + + if (result == 0 && sortColumn != SortableColumns.PK) { + result = (a.pk != null ? a.pk : "").compareTo((b.pk != null ? b.pk : "")); + } + + boolean mustSortInverse = sortInverse.get(getCurrentListTypeIntVal()); + + if (mustSortInverse) { + result *= -1; + } + + return result; + }; + + Collections.sort(filteredData, comparator); + + this.notifyDataSetChanged(); + } + + private int getCurrentListTypeIntVal() { + if (listType == ServerLists.Public) { + return 0; + } else if (listType == ServerLists.History) { + return 1; + } else if (listType == ServerLists.Favorites) { + return 2; + } + + return 3; + } + + public void setVpnServerListEventListener(VpnServerListEventListener listener) { + listEventListener = listener; + } + + @Override + public int getItemViewType(int position) { + if (position == 0) { + return 0; + } else if (position == 1) { + return 1; + } else if (position == 2 && showingRows) { + return 3; + } + + return 2; + } + + @NonNull + @Override + public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + if (viewType == 0) { + listOptionsView = new ServerListOptions(context); + listOptionsView.setClickWithIndexEventListener(this); + listOptionsView.selectCorrectTab(listType); + return new ListViewHolder<>(listOptionsView); + } else if (viewType == 1) { + conditionsView = new ConditionsList(context); + conditionsView.setConditions(sortBy.get(getCurrentListTypeIntVal()), sortInverse.get(getCurrentListTypeIntVal()), filters.get(getCurrentListTypeIntVal())); + + conditionsView.setClickEventListener(v -> { + if (conditionsView.showingFilters() && conditionsView.showingOrder()) { + ArrayList options = new ArrayList(); + + OptionsItem.SelectableOption option = new OptionsItem.SelectableOption(); + option.translatableLabelId = R.string.tmp_select_server_remove_filters_button; + options.add(option); + + option = new OptionsItem.SelectableOption(); + option.translatableLabelId = R.string.tmp_select_server_remove_custom_sorting_button; + options.add(option); + + option = new OptionsItem.SelectableOption(); + option.translatableLabelId = R.string.tmp_select_server_remove_both_button; + options.add(option); + + OptionsModalWindow modal = new OptionsModalWindow(context, null, options, (int selectedOption) -> { + if (selectedOption == 0 || selectedOption == 2) { + filters.set(getCurrentListTypeIntVal(), null); + } + if (selectedOption == 1 || selectedOption == 2) { + sortBy.set(getCurrentListTypeIntVal(), SortableColumns.AUTOMATIC); + sortInverse.set(getCurrentListTypeIntVal(), false); + } + + processData(); + }); + + modal.show(); + } else if (conditionsView.showingFilters()) { + filters.set(getCurrentListTypeIntVal(), null); + processData(); + } else if (conditionsView.showingOrder()) { + sortBy.set(getCurrentListTypeIntVal(), SortableColumns.AUTOMATIC); + sortInverse.set(getCurrentListTypeIntVal(), false); + processData(); + } + }); + + return new ListViewHolder<>(conditionsView); + } else if (viewType == 3) { + tableHeader = new ServerListTableHeader(context); + tableHeader.setListType(listType); + return new ListViewHolder<>(tableHeader); + } + + if (!showingRows) { + ServerListButton view; + if (lastUsedPremadeButtonIdex < premadeButtons.size()) { + view = premadeButtons.get(lastUsedPremadeButtonIdex); + lastUsedPremadeButtonIdex += 1; + } else { + view = createNewServerButton(); + } + + return new ListViewHolder<>(view); + } else { + ServerListTableRow view; + if (lastUsedPremadeButtonIdex < premadeRows.size()) { + view = premadeRows.get(lastUsedPremadeButtonIdex); + lastUsedPremadeButtonIdex += 1; + } else { + view = createNewServerRow(); + } + + return new ListViewHolder<>(view); + } + } + + private ServerListButton createNewServerButton() { + ServerListButton view = new ServerListButton(context); + view.setClickWithIndexEventListener(this); + return view; + } + + private ServerListTableRow createNewServerRow() { + ServerListTableRow view = new ServerListTableRow(context); + view.setClickWithIndexEventListener(this); + return view; + } + + @Override + public void onBindViewHolder(@NonNull ListViewHolder holder, int position) { + if (position >= initialServerIndex) { + position -= initialServerIndex; + + if (!showingRows) { + ((ServerListButton) holder.itemView).setIndex(position); + ((ServerListButton) holder.itemView).changeData(filteredData.get(position), listType); + + if (filteredData.size() == 1) { + ((ServerListButton) holder.itemView).setBoxRowType(BoxRowTypes.SINGLE); + } else if (position == 0) { + ((ServerListButton) holder.itemView).setBoxRowType(BoxRowTypes.TOP); + } else if (position == filteredData.size() - 1) { + ((ServerListButton) holder.itemView).setBoxRowType(BoxRowTypes.BOTTOM); + } else { + ((ServerListButton) holder.itemView).setBoxRowType(BoxRowTypes.MIDDLE); + } + } else { + ((ServerListTableRow) holder.itemView).setIndex(position); + ((ServerListTableRow) holder.itemView).changeData(filteredData.get(position), listType); + + if (position == filteredData.size() - 1) { + ((ServerListTableRow) holder.itemView).setBoxRowType(BoxRowTypes.BOTTOM); + } else { + ((ServerListTableRow) holder.itemView).setBoxRowType(BoxRowTypes.MIDDLE); + } + } + } + } + + @Override + public int getItemCount() { + if (!showingRows) { + return filteredData != null ? (filteredData.size() + 2) : 2; + } + + if (filteredData == null || filteredData.size() == 0) { + return 2; + } + return filteredData.size() + 3; + } + + @Override + public void onClickWithIndex(int index, Void data) { + if (listEventListener != null) { + if (index >= 0) { + listEventListener.onVpnServerSelected(this.filteredData.get(index)); + } else { + if (index <= ServerListOptions.showPublicIndex) { + if (index == ServerListOptions.showPublicIndex) { + listEventListener.tabChangeRequested(ServerLists.Public); + } else if (index == ServerListOptions.showHistoryIndex) { + listEventListener.tabChangeRequested(ServerLists.History); + } else if (index == ServerListOptions.showFavoritesIndex) { + listEventListener.tabChangeRequested(ServerLists.Favorites); + } else if (index == ServerListOptions.showBlockedIndex) { + listEventListener.tabChangeRequested(ServerLists.Blocked); + } + } else if (index == ServerListOptions.sortIndex) { + SortableColumns currentSortBy = sortBy.get(getCurrentListTypeIntVal()); + boolean currentSortInverse = sortInverse.get(getCurrentListTypeIntVal()); + + ArrayList optionValues = new ArrayList(); + if (listType == ServerLists.History) { + optionValues.add(SortableColumns.DATE); + } + optionValues.add(SortableColumns.NAME); + optionValues.add(SortableColumns.COUNTRY); + optionValues.add(SortableColumns.LOCATION); + optionValues.add(SortableColumns.PK); + /* + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + if (listType == ServerLists.Public) { + optionValues.add(SortableColumns.CONGESTION); + optionValues.add(SortableColumns.CONGESTION_RATING); + optionValues.add(SortableColumns.LATENCY); + optionValues.add(SortableColumns.LATENCY_RATING); + optionValues.add(SortableColumns.HOPS); + } + */ + optionValues.add(SortableColumns.NOTE); + + ArrayList options = new ArrayList(); + OptionsItem.SelectableOption option = new OptionsItem.SelectableOption(); + option.translatableLabelId = R.string.tmp_select_server_automatic_label; + if (currentSortBy == SortableColumns.AUTOMATIC) { + option.icon = "\ue876"; + } + options.add(option); + + for(int i = 0; i < optionValues.size(); i++) { + option = new OptionsItem.SelectableOption(); + option.translatableLabelId = SortableColumns.getColumnNameId(optionValues.get(i)); + if (optionValues.get(i) == currentSortBy && !currentSortInverse) { + option.icon = "\ue876"; + } + options.add(option); + + option = new OptionsItem.SelectableOption(); + option.label = context.getText(SortableColumns.getColumnNameId(optionValues.get(i))) + " " + context.getText(R.string.tmp_select_server_reversed_suffix); + if (optionValues.get(i) == currentSortBy && currentSortInverse) { + option.icon = "\ue876"; + } + options.add(option); + } + + OptionsModalWindow modal = new OptionsModalWindow(context, context.getString(R.string.tmp_select_server_sort_title), options, (int selectedOption) -> { + if (selectedOption == 0) { + sortBy.set(getCurrentListTypeIntVal(), SortableColumns.AUTOMATIC); + sortInverse.set(getCurrentListTypeIntVal(), false); + } else { + selectedOption -= 1; + sortBy.set(getCurrentListTypeIntVal(), optionValues.get((int)(selectedOption / 2))); + sortInverse.set(getCurrentListTypeIntVal(), selectedOption % 2 != 0); + } + + sortList(); + }); + + modal.show(); + } else if (index == ServerListOptions.addIndex) { + if (VPNCoordinator.getInstance().isServiceRunning()) { + HelperFunctions.showToast(context.getText(R.string.tmp_select_server_running_error).toString(), true); + return; + } + + ManualServerModalWindow modal = new ManualServerModalWindow(context, server -> listEventListener.onManualEntered(server)); + modal.show(); + } else if (index == ServerListOptions.filterIndex) { + HashSet countries = new HashSet<>(); + for (VpnServerForList element : this.data) { + countries.add(element.countryCode); + } + + FilterModalWindow modal = new FilterModalWindow(context, countries, filters.get(getCurrentListTypeIntVal()), newFilters -> { + filters.set(getCurrentListTypeIntVal(), newFilters); + processData(); + }); + modal.show(); + } + } + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/CustomDnsModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/CustomDnsModalWindow.java new file mode 100644 index 0000000000..9a5639faa4 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/CustomDnsModalWindow.java @@ -0,0 +1,108 @@ +package com.skywire.skycoin.vpn.activities.settings; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.ModalWindowButton; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; + +import java.util.regex.Matcher; + +import static androidx.core.util.PatternsCompat.IP_ADDRESS; + +public class CustomDnsModalWindow extends Dialog implements ClickEvent { + public interface Confirmed { + void confirmed(String newIp); + } + + private EditText editValue; + private ModalWindowButton buttonCancel; + private ModalWindowButton buttonConfirm; + + private Confirmed event; + + public CustomDnsModalWindow(Context ctx, Confirmed event) { + super(ctx); + + this.event = event; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_settings_dns_modal); + + editValue = findViewById(R.id.editValue); + buttonCancel = findViewById(R.id.buttonCancel); + buttonConfirm = findViewById(R.id.buttonConfirm); + + String currentServer = VPNGeneralPersistentData.getCustomDns(); + if (currentServer != null) { + editValue.setText(currentServer); + } + + editValue.setOnEditorActionListener((v, actionId, event) -> { + if ( + actionId == EditorInfo.IME_ACTION_DONE || + (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) + ) { + makeChange(); + + return true; + } + + return false; + }); + + editValue.setSelection(editValue.getText().length()); + + buttonCancel.setClickEventListener(this); + buttonConfirm.setClickEventListener(this); + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.buttonConfirm) { + makeChange(); + } else { + dismiss(); + } + } + + private void makeChange() { + boolean valid = false; + String ip = null; + + if (editValue.getText() == null || editValue.getText().toString().trim().length() == 0) { + valid = true; + } else { + ip = editValue.getText().toString().trim(); + Matcher matcher = IP_ADDRESS.matcher(ip); + if (matcher.matches()) { + valid = true; + } + } + + if (valid) { + if (event != null) { + event.confirmed(ip); + } + + dismiss(); + } else { + HelperFunctions.showToast(getContext().getString(R.string.tmp_dns_validation_error), true); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/SettingsActivity.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/SettingsActivity.java new file mode 100644 index 0000000000..e079385c54 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/SettingsActivity.java @@ -0,0 +1,196 @@ +package com.skywire.skycoin.vpn.activities.settings; + +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.skywire.skycoin.vpn.activities.apps.AppsActivity; +import com.skywire.skycoin.vpn.controls.options.OptionsItem; +import com.skywire.skycoin.vpn.controls.options.OptionsModalWindow; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +import java.util.ArrayList; +import java.util.HashSet; + +public class SettingsActivity extends Fragment implements ClickEvent { + private SettingsOption optionApps; + private SettingsOption optionShowIp; + private SettingsOption optionKillSwitch; + private SettingsOption optionResetAfterErrors; + private SettingsOption optionProtectBeforeConnecting; + private SettingsOption optionStartOnBoot; + private SettingsOption optionDataUnits; + private SettingsOption optionDns; + + // Units that must be used for displaying the data stats. + private Globals.DataUnits dataUnitsOption = VPNGeneralPersistentData.getDataUnits(); + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + + return inflater.inflate(R.layout.activity_settings, container, true); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + optionApps = view.findViewById(R.id.optionApps); + optionShowIp = view.findViewById(R.id.optionShowIp); + optionKillSwitch = view.findViewById(R.id.optionKillSwitch); + optionResetAfterErrors = view.findViewById(R.id.optionResetAfterErrors); + optionProtectBeforeConnecting = view.findViewById(R.id.optionProtectBeforeConnecting); + optionStartOnBoot = view.findViewById(R.id.optionStartOnBoot); + optionDataUnits = view.findViewById(R.id.optionDataUnits); + optionDns = view.findViewById(R.id.optionDns); + + optionShowIp.setChecked(VPNGeneralPersistentData.getShowIpActivated()); + optionKillSwitch.setChecked(VPNGeneralPersistentData.getKillSwitchActivated()); + optionResetAfterErrors.setChecked(VPNGeneralPersistentData.getMustRestartVpn()); + optionProtectBeforeConnecting.setChecked(VPNGeneralPersistentData.getProtectBeforeConnected()); + optionStartOnBoot.setChecked(VPNGeneralPersistentData.getStartOnBoot()); + + optionApps.setClickEventListener(this); + optionShowIp.setClickEventListener(this); + optionKillSwitch.setClickEventListener(this); + optionResetAfterErrors.setClickEventListener(this); + optionProtectBeforeConnecting.setClickEventListener(this); + optionStartOnBoot.setClickEventListener(this); + optionDataUnits.setClickEventListener(this); + optionDns.setClickEventListener(this); + + optionDataUnits.setDescription(getUnitsOptionText(dataUnitsOption), null); + + setDnsOptionText(VPNGeneralPersistentData.getCustomDns()); + } + + @Override + public void onResume() { + super.onResume(); + + Globals.AppFilteringModes appsMode = VPNGeneralPersistentData.getAppsSelectionMode(); + if (appsMode == Globals.AppFilteringModes.PROTECT_ALL) { + optionApps.setDescription(R.string.tmp_options_apps_description, null); + optionApps.setChecked(false); + optionApps.changeAlertIconVisibility(false); + } else { + HashSet selectedApps = HelperFunctions.filterAvailableApps(VPNGeneralPersistentData.getAppList(new HashSet<>())); + + if (appsMode == Globals.AppFilteringModes.PROTECT_SELECTED) { + optionApps.setDescription(R.string.tmp_options_apps_include_description, selectedApps.size() + ""); + } else if (appsMode == Globals.AppFilteringModes.IGNORE_SELECTED) { + optionApps.setDescription(R.string.tmp_options_apps_exclude_description, selectedApps.size() + ""); + } + + optionApps.setChecked(true); + optionApps.changeAlertIconVisibility(true); + } + } + + /** + * Gets the ID of the string for a data units selection. + */ + private int getUnitsOptionText(Globals.DataUnits units) { + if (units == Globals.DataUnits.OnlyBits) { + return R.string.tmp_options_data_units_only_bits; + } else if (units == Globals.DataUnits.OnlyBytes) { + return R.string.tmp_options_data_units_only_bytes; + } + + return R.string.tmp_options_data_units_bits_speed_and_bytes_volume; + } + + private void setDnsOptionText(String customIp) { + if (customIp == null || customIp.trim().length() == 0) { + optionDns.setDescription(R.string.tmp_options_dns_default, null); + optionDns.changeAlertIconVisibility(false); + } else { + optionDns.setDescription(R.string.tmp_options_dns_description, customIp); + optionDns.changeAlertIconVisibility(true); + } + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.optionDataUnits) { + ArrayList options = new ArrayList(); + Globals.DataUnits[] unitOptions = new Globals.DataUnits[3]; + unitOptions[0] = Globals.DataUnits.BitsSpeedAndBytesVolume; + unitOptions[1] = Globals.DataUnits.OnlyBytes; + unitOptions[2] = Globals.DataUnits.OnlyBits; + + for (Globals.DataUnits unitOption : unitOptions) { + OptionsItem.SelectableOption option = new OptionsItem.SelectableOption(); + option.icon = dataUnitsOption == unitOption ? "\ue876" : null; + option.translatableLabelId = getUnitsOptionText(unitOption); + options.add(option); + } + + OptionsModalWindow modal = new OptionsModalWindow(getContext(), null, options, (int selectedOption) -> { + dataUnitsOption = unitOptions[selectedOption]; + optionDataUnits.setDescription(getUnitsOptionText(dataUnitsOption), null); + VPNGeneralPersistentData.setDataUnits(dataUnitsOption); + }); + modal.show(); + + return; + } + + if (VPNCoordinator.getInstance().isServiceRunning()) { + HelperFunctions.showToast(getContext().getText(R.string.general_server_running_error).toString(), true); + + return; + } + + if (view.getId() == R.id.optionApps) { + Intent intent = new Intent(getContext(), AppsActivity.class); + startActivity(intent); + + return; + } + + if (view.getId() == R.id.optionDns) { + CustomDnsModalWindow modal = new CustomDnsModalWindow(getContext(), (String newIp) -> { + VPNGeneralPersistentData.setCustomDns(newIp); + setDnsOptionText(newIp); + + HelperFunctions.showToast(getContext().getString(R.string.tmp_dns_changes_made_confirmation), true); + }); + modal.show(); + } + + if (view.getId() == R.id.optionStartOnBoot && VPNServersPersistentData.getInstance().getCurrentServer() == null) { + HelperFunctions.showToast(getContext().getText(R.string.tmp_options_start_on_boot_without_server_error).toString(), true); + + return; + } + + ((SettingsOption)view).setChecked(!((SettingsOption)view).isChecked()); + + if (view.getId() == R.id.optionShowIp) { + VPNGeneralPersistentData.setShowIpActivated(((SettingsOption)view).isChecked()); + } else if (view.getId() == R.id.optionKillSwitch) { + VPNGeneralPersistentData.setKillSwitchActivated(((SettingsOption)view).isChecked()); + } else if (view.getId() == R.id.optionResetAfterErrors) { + VPNGeneralPersistentData.setMustRestartVpn(((SettingsOption)view).isChecked()); + } else if (view.getId() == R.id.optionProtectBeforeConnecting) { + VPNGeneralPersistentData.setProtectBeforeConnected(((SettingsOption)view).isChecked()); + } else { + VPNGeneralPersistentData.setStartOnBoot(((SettingsOption)view).isChecked()); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/SettingsOption.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/SettingsOption.java new file mode 100644 index 0000000000..8aaf37e966 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/settings/SettingsOption.java @@ -0,0 +1,106 @@ +package com.skywire.skycoin.vpn.activities.settings; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.CheckBox; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.extensible.ButtonBase; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; + +public class SettingsOption extends ButtonBase { + private BoxRowLayout mainLayout; + private TextView textAlertIcon; + private TextView textName; + private TextView textDescription; + private CheckBox checkSelected; + + public SettingsOption(Context context) { + super(context); + } + public SettingsOption(Context context, AttributeSet attrs) { + super(context, attrs); + } + public SettingsOption(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize(Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_settings_list_item, this, true); + + mainLayout = this.findViewById (R.id.mainLayout); + textAlertIcon = this.findViewById (R.id.textAlertIcon); + textName = this.findViewById (R.id.textName); + textDescription = this.findViewById (R.id.textDescription); + checkSelected = this.findViewById (R.id.checkSelected); + + int type = 1; + String name = ""; + String description = ""; + + if (attrs != null) { + TypedArray attributes = getContext().getTheme().obtainStyledAttributes( + attrs, + R.styleable.SettingsOption, + 0, 0 + ); + + type = attributes.getInteger(R.styleable.SettingsOption_box_row_type, 1); + name = attributes.getString(R.styleable.SettingsOption_title); + description = attributes.getString(R.styleable.SettingsOption_description); + + boolean hideCheckbox = attributes.getBoolean(R.styleable.SettingsOption_hide_checkbox, false); + if (hideCheckbox) { + checkSelected.setVisibility(GONE); + } + + attributes.recycle(); + } + + textName.setText(name); + textDescription.setText(description); + + if (type == 0) { + mainLayout.setType(BoxRowTypes.TOP); + } else if (type == 1) { + mainLayout.setType(BoxRowTypes.MIDDLE); + } else if (type == 2) { + mainLayout.setType(BoxRowTypes.BOTTOM); + } else if (type == 3) { + mainLayout.setType(BoxRowTypes.SINGLE); + } + + textAlertIcon.setVisibility(GONE); + + setClickableBoxView(mainLayout); + } + + public void setChecked(boolean checked) { + checkSelected.setChecked(checked); + } + public boolean isChecked() { + return checkSelected.isChecked(); + } + + public void setDescription(int resource, String param) { + if (param == null) { + textDescription.setText(resource); + } else { + textDescription.setText(String.format(getResources().getString(resource), param)); + } + } + + public void changeAlertIconVisibility(boolean visible) { + if (visible) { + textAlertIcon.setVisibility(VISIBLE); + } else { + textAlertIcon.setVisibility(GONE); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/MapBackground.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/MapBackground.java new file mode 100644 index 0000000000..142647ecd6 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/MapBackground.java @@ -0,0 +1,139 @@ +package com.skywire.skycoin.vpn.activities.start; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; + +import com.skywire.skycoin.vpn.R; + +public class MapBackground extends View { + public MapBackground(Context context) { + super(context); + Initialize(context, null); + } + public MapBackground(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public MapBackground(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private BitmapDrawable bitmapDrawable; + private float proportion = 1; + private Rect drawableArea = new Rect(0, 0,1, 1); + private int widthSize; + private boolean finished = false; + private ObjectAnimator animation; + + private void Initialize (Context context, AttributeSet attrs) { + Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.map_phones); + bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap); + bitmapDrawable.setAlpha(25); + + proportion = (float)bitmap.getWidth() / (float)bitmap.getHeight(); + } + + public void pauseAnimation() { + if (animation != null) { + animation.pause(); + } + } + + public void resumeAnimation() { + if (animation != null) { + animation.resume(); + } + } + + public void cancelAnimation() { + finished = true; + stopAnimation(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthSize != drawableArea.width() || heightSize != drawableArea.height()) { + setValues(widthSize, heightSize); + } + + setMeasuredDimension(drawableArea.width(), drawableArea.height()); + } + + @Override + protected void onDraw(Canvas canvas) { + bitmapDrawable.draw(canvas); + super.onDraw(canvas); + } + + private void setValues(int width, int height) { + if (finished) { + return; + } + + drawableArea = new Rect(0, 0, (int) (height * proportion), height); + bitmapDrawable.setBounds(drawableArea); + + stopAnimation(); + selectPosition(); + startAnimation(true); + } + + private void selectPosition() { + int max = drawableArea.width() - widthSize; + this.setTranslationX(-(int)Math.round(Math.random() * max)); + invalidate(); + } + + private void startAnimation(boolean appear) { + animation = ObjectAnimator.ofFloat(this, "alpha", appear ? 0 : 1, appear ? 1 : 0); + animation.setDuration(800); + animation.setInterpolator(appear ? new DecelerateInterpolator() : new AccelerateInterpolator()); + if (!appear) { + animation.setStartDelay(15000); + } + + animation.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { } + @Override + public void onAnimationCancel(Animator animation) { } + @Override + public void onAnimationRepeat(Animator animation) { } + + @Override + public void onAnimationEnd(Animator anim) { + stopAnimation(); + if (appear) { + startAnimation(false); + } else { + selectPosition(); + startAnimation(true); + } + } + }); + + animation.start(); + } + + private void stopAnimation() { + if (animation != null) { + animation.removeAllListeners(); + animation.cancel(); + animation = null; + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/StartActivity.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/StartActivity.java new file mode 100644 index 0000000000..36299e3b66 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/StartActivity.java @@ -0,0 +1,297 @@ +package com.skywire.skycoin.vpn.activities.start; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.index.IndexPageAdapter; +import com.skywire.skycoin.vpn.activities.start.connected.StartViewConnected; +import com.skywire.skycoin.vpn.activities.start.disconnected.StartViewDisconnected; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class StartActivity extends Fragment { + private enum SimpleVpnStates { + Unknown, + Running, + Stopped, + } + + private FrameLayout mainContainer; + private MapBackground background; + + private StartViewDisconnected viewDisconnected; + private StartViewConnected viewConnected; + + private SimpleVpnStates vpnState = SimpleVpnStates.Unknown; + private ObjectAnimator animation; + private ObjectAnimator positionAnimation; + private SimpleVpnStates animationDestination = SimpleVpnStates.Unknown; + + private IndexPageAdapter.RequestTabListener requestTabListener; + private Disposable serviceSubscription; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + + return inflater.inflate(R.layout.activity_start, container, true); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mainContainer = view.findViewById(R.id.mainContainer); + background = view.findViewById(R.id.background); + + if (!HelperFunctions.showBackgroundForVerticalScreen()) { + background.setVisibility(View.GONE); + } + } + + public void setRequestTabListener(IndexPageAdapter.RequestTabListener listener) { + requestTabListener = listener; + if (viewDisconnected != null) { + viewDisconnected.setRequestTabListener(listener); + } + } + + @Override + public void onStart() { + super.onStart(); + + serviceSubscription = VPNCoordinator.getInstance().getEventsObservable().subscribe(state -> { + if (state.state.val() < 10) { + if (vpnState == SimpleVpnStates.Unknown) { + vpnState = SimpleVpnStates.Stopped; + configureViewDisconnected(); + } else { + vpnState = SimpleVpnStates.Stopped; + startInitialAnimation(SimpleVpnStates.Stopped); + } + } else { + if (vpnState == SimpleVpnStates.Unknown) { + vpnState = SimpleVpnStates.Running; + configureViewConnected(); + } else { + vpnState = SimpleVpnStates.Running; + startInitialAnimation(SimpleVpnStates.Running); + } + } + }); + } + + private void configureViewDisconnected() { + if (viewDisconnected == null) { + if (viewConnected != null) { + mainContainer.removeView(viewConnected); + viewConnected.close(); + viewConnected = null; + } + + viewDisconnected = new StartViewDisconnected(getContext()); + viewDisconnected.setParentActivity(getActivity()); + if (requestTabListener != null) { + viewDisconnected.setRequestTabListener(requestTabListener); + } + + mainContainer.addView(viewDisconnected); + viewDisconnected.startAnimation(); + } + } + + private void configureViewConnected() { + if (viewConnected == null) { + if (viewDisconnected != null) { + mainContainer.removeView(viewDisconnected); + viewDisconnected.close(); + viewDisconnected = null; + } + + viewConnected = new StartViewConnected(getContext()); + mainContainer.addView(viewConnected); + } + } + + private void startInitialAnimation(SimpleVpnStates desiredDestination) { + if (animation != null || desiredDestination == SimpleVpnStates.Unknown) { + return; + } + if (desiredDestination == SimpleVpnStates.Running && viewConnected != null) { + return; + } + if (desiredDestination == SimpleVpnStates.Stopped && viewDisconnected != null) { + return; + } + + animationDestination = desiredDestination; + + View viewToAnimate; + if (desiredDestination == SimpleVpnStates.Running) { + viewToAnimate = viewDisconnected; + } else { + viewToAnimate = viewConnected; + } + + animate(viewToAnimate, true); + } + + private void startFinalAnimation() { + View viewToAnimate; + if (animationDestination == SimpleVpnStates.Running) { + configureViewConnected(); + viewToAnimate = viewConnected; + } else { + configureViewDisconnected(); + viewToAnimate = viewDisconnected; + } + + animate(viewToAnimate, false); + } + + private void animate(View viewToAnimate, boolean isInitialAnimation) { + if (animation != null) { + animation.cancel(); + } + if (positionAnimation != null) { + positionAnimation.cancel(); + } + + float initialPosition; + float finalPosition; + if (animationDestination == SimpleVpnStates.Running) { + if (isInitialAnimation) { + initialPosition = 0; + finalPosition = 20 * getContext().getResources().getDisplayMetrics().density; + } else { + initialPosition = -20 * getContext().getResources().getDisplayMetrics().density; + finalPosition = 0; + } + } else { + if (isInitialAnimation) { + initialPosition = 0; + finalPosition = -20 * getContext().getResources().getDisplayMetrics().density; + } else { + initialPosition = 20 * getContext().getResources().getDisplayMetrics().density; + finalPosition = 0; + } + } + + long duration = 200; + + positionAnimation = ObjectAnimator.ofFloat(viewToAnimate, "translationY", initialPosition, finalPosition); + positionAnimation.setDuration(duration); + positionAnimation.setInterpolator(isInitialAnimation ? new AccelerateInterpolator() : new DecelerateInterpolator()); + positionAnimation.start(); + + animation = ObjectAnimator.ofFloat(viewToAnimate, "alpha", isInitialAnimation ? 1 : 0, isInitialAnimation ? 0 : 1); + animation.setDuration(duration); + animation.setInterpolator(isInitialAnimation ? new AccelerateInterpolator() : new DecelerateInterpolator()); + + animation.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { } + @Override + public void onAnimationCancel(Animator animation) { } + @Override + public void onAnimationRepeat(Animator animation) { } + + @Override + public void onAnimationEnd(Animator animation) { + if (isInitialAnimation) { + Observable.just(1).delay(50, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(v -> startFinalAnimation()); + } else { + finishAnimations(); + animationDestination = SimpleVpnStates.Unknown; + + if (vpnState == SimpleVpnStates.Running && viewConnected == null) { + startInitialAnimation(SimpleVpnStates.Running); + } else if (vpnState == SimpleVpnStates.Stopped && viewDisconnected == null) { + startInitialAnimation(SimpleVpnStates.Stopped); + } + } + } + }); + + animation.start(); + } + + private void finishAnimations() { + animation.cancel(); + animation = null; + + positionAnimation.cancel(); + positionAnimation = null; + } + + @Override + public void onResume() { + super.onResume(); + + background.resumeAnimation(); + if (viewDisconnected != null) { + viewDisconnected.startAnimation(); + viewDisconnected.updateRightBar(); + } + if (viewConnected != null) { + viewConnected.continueUpdatingStats(); + viewConnected.updateRightBar(); + } + } + + @Override + public void onPause() { + super.onPause(); + + background.pauseAnimation(); + if (viewDisconnected != null) { + viewDisconnected.stopAnimation(); + } + if (viewConnected != null) { + viewConnected.pauseUpdatingStats(); + } + } + + @Override + public void onStop() { + super.onStop(); + serviceSubscription.dispose(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + background.cancelAnimation(); + + if (viewDisconnected != null) { + viewDisconnected.close(); + } + if (viewConnected != null) { + viewConnected.close(); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/StartViewRightPanel.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/StartViewRightPanel.java new file mode 100644 index 0000000000..f20ea4fa7e --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/StartViewRightPanel.java @@ -0,0 +1,329 @@ +package com.skywire.skycoin.vpn.activities.start; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.RelativeSizeSpan; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.apps.AppsActivity; +import com.skywire.skycoin.vpn.activities.servers.ServerLists; +import com.skywire.skycoin.vpn.activities.servers.ServersActivity; +import com.skywire.skycoin.vpn.controls.ClickableLinearLayout; +import com.skywire.skycoin.vpn.controls.ServerName; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.AlphaSpan; +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.helpers.MaterialFontSpan; +import com.skywire.skycoin.vpn.network.ApiClient; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +import java.io.Closeable; +import java.util.Date; +import java.util.HashSet; +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class StartViewRightPanel extends FrameLayout implements ClickEvent, Closeable { + public StartViewRightPanel(Context context) { + super(context); + Initialize(context, null); + } + public StartViewRightPanel(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public StartViewRightPanel(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private final int retryDelay = 20000; + + private TextView textWaitingIp; + private TextView textIp; + private TextView textWaitingCountry; + private TextView textCountry; + private TextView textRemotePk; + private TextView textLocalPk; + private TextView textAppProtection; + private ServerName serverName; + private ClickableLinearLayout ipClickableLayout; + private ClickableLinearLayout serverClickableLayout; + private ClickableLinearLayout remotePkClickableLayout; + private ClickableLinearLayout localPkClickableLayout; + private ClickableLinearLayout appProtectionClickableLayout; + private LinearLayout loadingIpContainer; + private LinearLayout ipContainer; + private LinearLayout countryContainer; + private LinearLayout bottomPartContainer; + private ProgressBar progressCountry; + + private LocalServerData currentServer; + + private String previousIp; + private String currentIp; + private String previousCountry; + private Date lastIpRefresDate; + + private Disposable serverSubscription; + private Disposable ipSubscription; + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_start_right_panel, this, true); + + textWaitingIp = findViewById(R.id.textWaitingIp); + textIp = findViewById(R.id.textIp); + textWaitingCountry = findViewById(R.id.textWaitingCountry); + textCountry = findViewById(R.id.textCountry); + textRemotePk = findViewById(R.id.textRemotePk); + textLocalPk = findViewById(R.id.textLocalPk); + textAppProtection = findViewById(R.id.textAppProtection); + serverName = findViewById(R.id.serverName); + ipClickableLayout = findViewById(R.id.ipClickableLayout); + serverClickableLayout = findViewById(R.id.serverClickableLayout); + remotePkClickableLayout = findViewById(R.id.remotePkClickableLayout); + localPkClickableLayout = findViewById(R.id.localPkClickableLayout); + appProtectionClickableLayout = findViewById(R.id.appProtectionClickableLayout); + loadingIpContainer = findViewById(R.id.loadingIpContainer); + ipContainer = findViewById(R.id.ipContainer); + countryContainer = findViewById(R.id.countryContainer); + bottomPartContainer = findViewById(R.id.bottomPartContainer); + progressCountry = findViewById(R.id.progressCountry); + + ipClickableLayout.setClickEventListener(this); + serverClickableLayout.setClickEventListener(this); + remotePkClickableLayout.setClickEventListener(this); + localPkClickableLayout.setClickEventListener(this); + appProtectionClickableLayout.setClickEventListener(this); + + localPkClickableLayout.setVisibility(View.GONE); + ipClickableLayout.setVisibility(View.GONE); + ipContainer.setVisibility(View.GONE); + countryContainer.setVisibility(View.GONE); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.StartViewRightPanel, + 0, 0 + ); + + if (attributes.getBoolean(R.styleable.StartViewRightPanel_hide_bottom_part, false)) { + bottomPartContainer.setVisibility(GONE); + } + + attributes.recycle(); + } + + if (!isInEditMode()) { + updateData(); + + if (!VPNGeneralPersistentData.getShowIpActivated()) { + textWaitingIp.setText(R.string.tmp_status_connected_ip_option_disabled); + textWaitingCountry.setText(R.string.tmp_status_connected_ip_option_disabled); + } + } + } + + public void updateData() { + if (serverSubscription == null) { + serverSubscription = VPNServersPersistentData.getInstance().getCurrentServerObservable().subscribe(server -> { + currentServer = server; + serverName.setServer(ServersActivity.convertLocalServerData(currentServer), ServerLists.History, true); + putTextWithIcon(textRemotePk, currentServer.pk, " \ue14d"); + }); + } + + Globals.AppFilteringModes selectedMode = VPNGeneralPersistentData.getAppsSelectionMode(); + if (selectedMode != Globals.AppFilteringModes.PROTECT_ALL) { + HashSet selectedApps = HelperFunctions.filterAvailableApps(VPNGeneralPersistentData.getAppList(new HashSet<>())); + + if (selectedApps.size() > 0) { + appProtectionClickableLayout.setVisibility(VISIBLE); + + String text; + if (selectedMode == Globals.AppFilteringModes.PROTECT_SELECTED) { + text = getContext().getString(R.string.tmp_status_connected_protecting_selected_apps); + } else { + text = getContext().getString(R.string.tmp_status_connected_ignoring_selected_apps); + } + + putTextWithIcon(textAppProtection, text, " \ue8f4"); + } else { + appProtectionClickableLayout.setVisibility(GONE); + } + } else { + appProtectionClickableLayout.setVisibility(GONE); + } + } + + public void putInWaitingForVpnState() { + cancelIpCheck(); + + ipClickableLayout.setVisibility(GONE); + loadingIpContainer.setVisibility(VISIBLE); + + textWaitingIp.setVisibility(VISIBLE); + textWaitingCountry.setVisibility(VISIBLE); + ipContainer.setVisibility(View.GONE); + countryContainer.setVisibility(View.GONE); + } + + public void refreshIpData() { + getIp(0); + } + + private void getIp(int delayMs) { + if (!VPNGeneralPersistentData.getShowIpActivated()) { + return; + } + + cancelIpCheck(); + + ipClickableLayout.setVisibility(GONE); + loadingIpContainer.setVisibility(VISIBLE); + + textWaitingIp.setVisibility(GONE); + textWaitingCountry.setVisibility(GONE); + progressCountry.setVisibility(VISIBLE); + ipContainer.setVisibility(View.VISIBLE); + countryContainer.setVisibility(View.VISIBLE); + textIp.setText("---"); + textCountry.setText("---"); + + ipSubscription = Observable.just(0).delay(delayMs, TimeUnit.MILLISECONDS).flatMap(v -> ApiClient.getCurrentIp()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(response -> { + if (response.body() != null) { + lastIpRefresDate = new Date(); + + ipClickableLayout.setVisibility(VISIBLE); + loadingIpContainer.setVisibility(GONE); + + currentIp = response.body().ip; + textIp.setText(currentIp); + + if (currentIp.equals(previousIp) && previousCountry != null) { + textCountry.setText(previousCountry); + progressCountry.setVisibility(GONE); + } else { + getIpCountry(0); + } + + previousIp = currentIp; + } else { + getIp(retryDelay); + } + }, err -> { + getIp(retryDelay); + }); + } + + private void getIpCountry(int delayMs) { + if (!VPNGeneralPersistentData.getShowIpActivated()) { + return; + } + + ipSubscription.dispose(); + + ipSubscription = Observable.just(0).delay(delayMs, TimeUnit.MILLISECONDS).flatMap(v -> ApiClient.getIpCountry(currentIp)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(response -> { + if (response.body() != null) { + progressCountry.setVisibility(GONE); + + String[] dataParts = response.body().split(";"); + if (dataParts.length == 4) { + textCountry.setText(dataParts[3]); + } else { + textCountry.setText(getContext().getText(R.string.general_unknown)); + } + + previousCountry = textCountry.getText().toString(); + } else { + getIpCountry(retryDelay); + } + }, err -> { + getIpCountry(retryDelay); + }); + } + + private void cancelIpCheck() { + if (ipSubscription != null) { + ipSubscription.dispose(); + } + } + + private void putTextWithIcon(TextView textView, String text, String iconText) { + MaterialFontSpan materialFontSpan = new MaterialFontSpan(getContext()); + RelativeSizeSpan relativeSizeSpan = new RelativeSizeSpan(0.75f); + AlphaSpan alphaSpan = new AlphaSpan(128); + + SpannableStringBuilder finalText = new SpannableStringBuilder(text.toString() + iconText); + finalText.setSpan(materialFontSpan, finalText.length() - iconText.length(), finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + finalText.setSpan(relativeSizeSpan, finalText.length() - iconText.length(), finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + finalText.setSpan(alphaSpan, finalText.length() - iconText.length(), finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + textView.setText(finalText); + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.ipClickableLayout) { + long msToWait = 10000; + long elapsedTime = (new Date()).getTime() - lastIpRefresDate.getTime(); + + if (elapsedTime < msToWait) { + HelperFunctions.showToast(String.format( + getContext().getText(R.string.tmp_status_connected_ip_refresh_time_warning).toString(), + HelperFunctions.zeroDecimalsFormatter.format(Math.ceil((msToWait - elapsedTime)) / 1000d) + ), true); + } else { + this.refreshIpData(); + } + } else if (view.getId() == R.id.serverClickableLayout) { + HelperFunctions.showServerOptions(getContext(), ServersActivity.convertLocalServerData(currentServer), ServerLists.History); + } else if (view.getId() == R.id.appProtectionClickableLayout) { + Intent intent = new Intent(getContext(), AppsActivity.class); + intent.putExtra(AppsActivity.READ_ONLY_EXTRA, true); + getContext().startActivity(intent); + } else { + String textToCopy = currentServer.pk; + + ClipboardManager clipboard = (ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clipData = ClipData.newPlainText("", textToCopy); + clipboard.setPrimaryClip(clipData); + HelperFunctions.showToast(getContext().getString(R.string.general_copied), true); + } + } + + @Override + public void close() { + if (serverSubscription != null) { + serverSubscription.dispose(); + } + cancelIpCheck(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/Chart.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/Chart.java new file mode 100644 index 0000000000..1c44894935 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/Chart.java @@ -0,0 +1,158 @@ +package com.skywire.skycoin.vpn.activities.start.connected; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; + +import java.io.Closeable; +import java.util.ArrayList; + +import io.reactivex.rxjava3.disposables.Disposable; + +public class Chart extends FrameLayout implements Closeable { + public Chart(Context context) { + super(context); + Initialize(context, null); + } + public Chart(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public Chart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private LineChart chart; + private FrameLayout chartContainer; + private TextView textMin; + private TextView textMid; + private TextView textMax; + + private Globals.DataUnits dataUnits = VPNGeneralPersistentData.getDataUnits(); + private ArrayList lastData; + private boolean showingMs; + + private Disposable dataUnitsSubscription; + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_start_chart, this, true); + + chart = findViewById(R.id.chart); + chartContainer = findViewById(R.id.chartContainer); + textMin = findViewById(R.id.textMin); + textMid = findViewById(R.id.textMid); + textMax = findViewById(R.id.textMax); + + chartContainer.setClipToOutline(true); + + chart.getDescription().setEnabled(false); + chart.getLegend().setEnabled(false); + chart.setDrawGridBackground(false); + chart.getXAxis().setEnabled(false); + chart.getAxisLeft().setEnabled(false); + chart.getAxisRight().setEnabled(false); + + chart.setViewPortOffsets(0f, 0f, 0f, 0f); + chart.getAxisLeft().setAxisMinimum(0); + chart.getAxisLeft().setSpaceTop(0); + chart.getAxisLeft().setSpaceBottom(0); + + chart.setScaleEnabled(false); + chart.setTouchEnabled(false); + + dataUnitsSubscription = VPNGeneralPersistentData.getDataUnitsObservable().subscribe(response -> { + dataUnits = response; + + if (lastData != null) { + setData(lastData, showingMs); + } + }); + } + + public void setData(ArrayList data, boolean showingMs) { + this.lastData = data; + this.showingMs = showingMs; + + ArrayList values = new ArrayList<>(); + + double max = 0; + for (int i = 0; i < data.size(); i++) { + double val = (float)data.get(i); + values.add(new Entry(i, (float)val)); + + if (val > max) { + max = val; + } + } + + if (max == 0) { + max = 1; + } + + double mid = max / 2; + + if (chart.getAxisLeft().getAxisMaximum() != max) { + chart.getAxisLeft().setAxisMaximum((float)max); + + if (showingMs) { + textMax.setText(HelperFunctions.getLatencyValue(max)); + textMid.setText(HelperFunctions.getLatencyValue(mid)); + textMin.setText(HelperFunctions.getLatencyValue(0)); + } else { + textMax.setText(HelperFunctions.computeDataAmountString(max, true, dataUnits != Globals.DataUnits.OnlyBytes)); + textMid.setText(HelperFunctions.computeDataAmountString(mid, true, dataUnits != Globals.DataUnits.OnlyBytes)); + textMin.setText(HelperFunctions.computeDataAmountString(0, true, dataUnits != Globals.DataUnits.OnlyBytes)); + } + } + + LineDataSet dataSet; + if (chart.getData() != null && chart.getData().getDataSetCount() > 0) { + dataSet = (LineDataSet) chart.getData().getDataSetByIndex(0); + dataSet.setValues(values); + dataSet.notifyDataSetChanged(); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + chart.invalidate(); + } else { + dataSet = new LineDataSet(values, ""); + dataSet.setDrawIcons(false); + dataSet.setDrawValues(false); + dataSet.setDrawCircleHole(false); + dataSet.setDrawCircles(false); + + dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER); + + dataSet.setColor(0x59000000); + dataSet.setLineWidth(0f); + + dataSet.setDrawFilled(true); + dataSet.setFillColor(0x00000000); + dataSet.setFillAlpha(255); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(dataSet); + LineData lineData = new LineData(dataSets); + + chart.setData(lineData); + } + } + + @Override + public void close() { + dataUnitsSubscription.dispose(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/StartViewConnected.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/StartViewConnected.java new file mode 100644 index 0000000000..d4129b09b2 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/StartViewConnected.java @@ -0,0 +1,509 @@ +package com.skywire.skycoin.vpn.activities.start.connected; + +import android.content.Context; +import android.content.Intent; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.apps.AppsActivity; +import com.skywire.skycoin.vpn.activities.servers.ServerLists; +import com.skywire.skycoin.vpn.activities.servers.ServersActivity; +import com.skywire.skycoin.vpn.activities.start.StartViewRightPanel; +import com.skywire.skycoin.vpn.controls.ConfirmationModalWindow; +import com.skywire.skycoin.vpn.controls.ServerName; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.ClickTimeManagement; +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.network.ApiClient; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNStates; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class StartViewConnected extends FrameLayout implements ClickEvent, Closeable { + public StartViewConnected(Context context) { + super(context); + Initialize(context, null); + } + public StartViewConnected(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public StartViewConnected(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private final int retryDelay = 20000; + + private TextView textTime; + private TextView textState; + private TextView textStateDescription; + private TextView textLastError; + private TextView textWaitingIp; + private TextView textWaitingCountry; + private TextView textIp; + private TextView textCountry; + private TextView textUploadSpeed; + private TextView textTotalUploaded; + private TextView textDownloadSpeed; + private TextView textTotalDownloaded; + private TextView textLatency; + private TextView textAppsProtectionMode; + private TextView textServerNote; + private TextView textStartedByTheSystem; + private ServerName serverName; + private ImageView imageStateLine; + private Chart downloadChart; + private Chart uploadChart; + private Chart latencyChart; + private LinearLayout leftContainer; + private LinearLayout ipDataContainer; + private LinearLayout ipContainer; + private LinearLayout countryContainer; + private FrameLayout appsContainer; + private LinearLayout appsInternalContainer; + private LinearLayout serverContainer; + private FrameLayout rightContainer; + private ProgressBar progressIp; + private ProgressBar progressCountry; + private StopButton buttonStop; + private StartViewRightPanel rightPanel; + + private String previousIp; + private String currentIp; + private String previousCountry; + private VPNCoordinator.ConnectionStats lastStats; + private boolean updateStats = true; + private Globals.DataUnits dataUnits = VPNGeneralPersistentData.getDataUnits(); + + private ClickTimeManagement appsButtonTimeManager = new ClickTimeManagement(); + private ClickTimeManagement serverButtonTimeManager = new ClickTimeManagement(); + + private Disposable serviceSubscription; + private Disposable serverSubscription; + private Disposable ipSubscription; + private Disposable statsSubscription; + private Disposable dataUnitsSubscription; + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_start_connected, this, true); + + textTime = findViewById(R.id.textTime); + textState = findViewById(R.id.textState); + textStateDescription = findViewById(R.id.textStateDescription); + textLastError = findViewById(R.id.textLastError); + textWaitingIp = findViewById(R.id.textWaitingIp); + textWaitingCountry = findViewById(R.id.textWaitingCountry); + textIp = findViewById(R.id.textIp); + textCountry = findViewById(R.id.textCountry); + textUploadSpeed = findViewById(R.id.textUploadSpeed); + textTotalUploaded = findViewById(R.id.textTotalUploaded); + textDownloadSpeed = findViewById(R.id.textDownloadSpeed); + textTotalDownloaded = findViewById(R.id.textTotalDownloaded); + textLatency = findViewById(R.id.textLatency); + textAppsProtectionMode = findViewById(R.id.textAppsProtectionMode); + textServerNote = findViewById(R.id.textServerNote); + textStartedByTheSystem = findViewById(R.id.textStartedByTheSystem); + serverName = this.findViewById (R.id.serverName); + imageStateLine = findViewById(R.id.imageStateLine); + imageStateLine = findViewById(R.id.imageStateLine); + downloadChart = findViewById(R.id.downloadChart); + uploadChart = findViewById(R.id.uploadChart); + latencyChart = findViewById(R.id.latencyChart); + leftContainer = findViewById(R.id.leftContainer); + ipDataContainer = findViewById(R.id.ipDataContainer); + ipContainer = findViewById(R.id.ipContainer); + countryContainer = findViewById(R.id.countryContainer); + appsContainer = findViewById(R.id.appsContainer); + appsInternalContainer = findViewById(R.id.appsInternalContainer); + serverContainer = findViewById(R.id.serverContainer); + rightContainer = findViewById(R.id.rightContainer); + progressIp = findViewById(R.id.progressIp); + progressCountry = findViewById(R.id.progressCountry); + buttonStop = findViewById(R.id.buttonStop); + rightPanel = findViewById(R.id.rightPanel); + + textLastError.setVisibility(GONE); + textStartedByTheSystem.setVisibility(GONE); + ipContainer.setVisibility(GONE); + countryContainer.setVisibility(GONE); + + if (HelperFunctions.getWidthType(getContext()) != HelperFunctions.WidthTypes.SMALL) { + float areaWidth = getContext().getResources().getDimension(R.dimen.tablet_status_area_width); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams((int)Math.round(areaWidth), LayoutParams.WRAP_CONTENT); + params.gravity = Gravity.CENTER_HORIZONTAL; + leftContainer.setLayoutParams(params); + + ipDataContainer.setVisibility(GONE); + appsContainer.setVisibility(GONE); + serverContainer.setVisibility(GONE); + + textLastError.setTextSize(TypedValue.COMPLEX_UNIT_PX, getContext().getResources().getDimension(R.dimen.small_text_size)); + } else { + rightContainer.setVisibility(GONE); + } + + Globals.AppFilteringModes selectedMode = VPNGeneralPersistentData.getAppsSelectionMode(); + if (selectedMode != Globals.AppFilteringModes.PROTECT_ALL) { + HashSet selectedApps = HelperFunctions.filterAvailableApps(VPNGeneralPersistentData.getAppList(new HashSet<>())); + + if (HelperFunctions.getWidthType(getContext()) == HelperFunctions.WidthTypes.SMALL) { + if (selectedApps.size() > 0) { + if (selectedMode == Globals.AppFilteringModes.PROTECT_SELECTED) { + textAppsProtectionMode.setText(R.string.tmp_status_connected_protecting_selected_apps); + } else { + textAppsProtectionMode.setText(R.string.tmp_status_connected_ignoring_selected_apps); + } + + appsInternalContainer.setOnClickListener((View v) -> { + if (appsButtonTimeManager.canClick()) { + appsButtonTimeManager.informClickMade(); + Intent intent = new Intent(getContext(), AppsActivity.class); + intent.putExtra(AppsActivity.READ_ONLY_EXTRA, true); + getContext().startActivity(intent); + } + }); + } else { + appsContainer.setVisibility(GONE); + } + } else { + appsContainer.setVisibility(GONE); + } + } else { + appsContainer.setVisibility(GONE); + } + + if (!VPNGeneralPersistentData.getShowIpActivated()) { + textWaitingIp.setText(R.string.tmp_status_connected_ip_option_disabled); + textWaitingCountry.setText(R.string.tmp_status_connected_ip_option_disabled); + } + + ArrayList emptyValues = new ArrayList<>(); + emptyValues.add(0L); + + VPNCoordinator.ConnectionStats emptyStats = new VPNCoordinator.ConnectionStats(); + emptyStats.downloadSpeedHistory = emptyValues; + emptyStats.uploadSpeedHistory = emptyValues; + emptyStats.latencyHistory = emptyValues; + emptyStats.currentDownloadSpeed = 0; + emptyStats.currentUploadSpeed = 0; + emptyStats.currentLatency = 0; + emptyStats.totalDownloadedData = 0; + emptyStats.totalUploadedData = 0; + updateDisplayedStats(emptyStats); + + downloadChart.setData(emptyValues, false); + uploadChart.setData(emptyValues, false); + latencyChart.setData(emptyValues, true); + + serverSubscription = VPNServersPersistentData.getInstance().getCurrentServerObservable().subscribe(server -> { + serverName.setServer(ServersActivity.convertLocalServerData(server), ServerLists.History, true); + + String note = HelperFunctions.getServerNote(server); + if (note != null) { + textServerNote.setText(note); + } else { + textServerNote.setText(server.pk); + } + }); + + if (HelperFunctions.getWidthType(getContext()) == HelperFunctions.WidthTypes.SMALL) { + serverContainer.setOnClickListener((View v) -> { + if (serverButtonTimeManager.canClick()) { + serverButtonTimeManager.informClickMade(); + Observable.just(1).delay(Globals.CLICK_DELAY_MS, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> { + HelperFunctions.showServerOptions( + getContext(), + ServersActivity.convertLocalServerData(VPNServersPersistentData.getInstance().getCurrentServer()), + ServerLists.History + ); + }); + } + }); + } + + buttonStop.setClickEventListener(this); + + serviceSubscription = VPNCoordinator.getInstance().getEventsObservable().subscribe( + state -> { + int mainText = VPNStates.getTitleForState(state.state); + if (mainText != -1) { + textState.setText(mainText); + } else { + textState.setText("---"); + } + + imageStateLine.setBackgroundResource(VPNStates.getColorForStateTitle(mainText)); + + int description = VPNStates.getDescriptionForState(state.state); + if (description != -1) { + textStateDescription.setText(description); + } else { + textStateDescription.setText("---"); + } + + buttonStop.setEnabled(true); + + if (state.startedByTheSystem) { + buttonStop.setEnabled(false); + textStartedByTheSystem.setVisibility(View.VISIBLE); + } else { + textStartedByTheSystem.setVisibility(View.GONE); + } + + if (state.stopRequested) { + buttonStop.setEnabled(false); + buttonStop.setBusyState(true); + } else { + buttonStop.setBusyState(false); + } + + if (state.state != VPNStates.CONNECTED) { + String lastError = VPNGeneralPersistentData.getLastError(null); + if (lastError != null) { + String start = getContext().getString(R.string.tmp_status_page_last_error); + textLastError.setText(start + " " + lastError); + textLastError.setVisibility(VISIBLE); + } else { + textLastError.setVisibility(GONE); + } + } else { + textLastError.setVisibility(GONE); + } + + if (VPNGeneralPersistentData.getShowIpActivated()) { + if (HelperFunctions.getWidthType(getContext()) == HelperFunctions.WidthTypes.SMALL) { + if (state.state == VPNStates.CONNECTED) { + if (ipContainer.getVisibility() == TextView.GONE) { + ipContainer.setVisibility(VISIBLE); + countryContainer.setVisibility(VISIBLE); + textWaitingIp.setVisibility(GONE); + textWaitingCountry.setVisibility(GONE); + + textIp.setText("---"); + textCountry.setText("---"); + + getIp(0); + } + } else { + if (ipContainer.getVisibility() == TextView.VISIBLE) { + ipContainer.setVisibility(GONE); + countryContainer.setVisibility(GONE); + textWaitingIp.setVisibility(VISIBLE); + textWaitingCountry.setVisibility(VISIBLE); + + cancelIpCheck(); + } + } + } else { + if (state.state == VPNStates.CONNECTED) { + rightPanel.refreshIpData(); + } else { + rightPanel.putInWaitingForVpnState(); + } + } + } + } + ); + + statsSubscription = VPNCoordinator.getInstance().getConnectionStats().subscribe(stats -> { + lastStats = stats; + if (updateStats) { + updateDisplayedStats(lastStats); + } + }); + + dataUnitsSubscription = VPNGeneralPersistentData.getDataUnitsObservable().subscribe(response -> { + dataUnits = response; + + if (lastStats != null && updateStats) { + updateDisplayedStats(lastStats); + } + }); + + updateTime(null); + } + + private void updateDisplayedStats(VPNCoordinator.ConnectionStats stats) { + if (stats != null) { + updateTime(stats.lastConnectionDate); + + downloadChart.setData(stats.downloadSpeedHistory, false); + uploadChart.setData(stats.uploadSpeedHistory, false); + latencyChart.setData(stats.latencyHistory, true); + + textDownloadSpeed.setText(HelperFunctions.computeDataAmountString(stats.currentDownloadSpeed, true, dataUnits != Globals.DataUnits.OnlyBytes)); + textUploadSpeed.setText(HelperFunctions.computeDataAmountString(stats.currentUploadSpeed, true, dataUnits != Globals.DataUnits.OnlyBytes)); + textLatency.setText(HelperFunctions.getLatencyValue(stats.currentLatency)); + + textTotalDownloaded.setText(String.format( + getContext().getText(R.string.tmp_status_connected_total_data).toString(), + HelperFunctions.computeDataAmountString(stats.totalDownloadedData, false, dataUnits == Globals.DataUnits.OnlyBits) + )); + + textTotalUploaded.setText(String.format( + getContext().getText(R.string.tmp_status_connected_total_data).toString(), + HelperFunctions.computeDataAmountString(stats.totalUploadedData, false, dataUnits == Globals.DataUnits.OnlyBits) + )); + } + } + + public void pauseUpdatingStats() { + updateStats = false; + } + + public void continueUpdatingStats() { + updateStats = true; + updateDisplayedStats(lastStats); + } + + public void updateRightBar() { + rightPanel.updateData(); + } + + private void updateTime(Date lastConnectionDate) { + if (lastConnectionDate == null) { + textTime.setText(R.string.tmp_status_connected_waiting); + } else { + long connectionMs = (new Date()).getTime() - lastConnectionDate.getTime(); + + String time = String.format("%02d", connectionMs / 3600000) + ":"; + time += String.format("%02d", (connectionMs / 60000) % 60) + ":"; + time += String.format("%02d", (connectionMs / 1000) % 60); + + textTime.setText(time); + } + } + + private void getIp(int delayMs) { + if (!VPNGeneralPersistentData.getShowIpActivated()) { + return; + } + + if (ipSubscription != null) { + ipSubscription.dispose(); + } + + progressIp.setVisibility(VISIBLE); + progressCountry.setVisibility(VISIBLE); + + this.ipSubscription = Observable.just(0).delay(delayMs, TimeUnit.MILLISECONDS).flatMap(v -> ApiClient.getCurrentIp()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(response -> { + if (response.body() != null) { + progressIp.setVisibility(GONE); + + currentIp = response.body().ip; + textIp.setText(currentIp); + + if (currentIp.equals(previousIp) && previousCountry != null) { + textCountry.setText(previousCountry); + progressCountry.setVisibility(GONE); + } else { + getIpCountry(0); + } + + previousIp = currentIp; + } else { + getIp(retryDelay); + } + }, err -> { + getIp(retryDelay); + }); + } + + private void getIpCountry(int delayMs) { + if (!VPNGeneralPersistentData.getShowIpActivated()) { + return; + } + + ipSubscription.dispose(); + + this.ipSubscription = Observable.just(0).delay(delayMs, TimeUnit.MILLISECONDS).flatMap(v -> ApiClient.getIpCountry(currentIp)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(response -> { + if (response.body() != null) { + progressCountry.setVisibility(GONE); + + String[] dataParts = response.body().split(";"); + if (dataParts.length == 4) { + textCountry.setText(dataParts[3]); + } else { + textCountry.setText(getContext().getText(R.string.general_unknown)); + } + + previousCountry = textCountry.getText().toString(); + } else { + getIpCountry(retryDelay); + } + }, err -> { + getIpCountry(retryDelay); + }); + } + + @Override + public void close() { + serverSubscription.dispose(); + serviceSubscription.dispose(); + statsSubscription.dispose(); + dataUnitsSubscription.dispose(); + rightPanel.close(); + downloadChart.close(); + uploadChart.close(); + latencyChart.close(); + cancelIpCheck(); + } + + private void cancelIpCheck() { + if (ipSubscription != null) { + ipSubscription.dispose(); + } + } + + @Override + public void onClick(View view) { + if (!VPNGeneralPersistentData.getKillSwitchActivated()) { + VPNCoordinator.getInstance().stopVPN(); + } else { + ConfirmationModalWindow confirmationModal = new ConfirmationModalWindow( + getContext(), + R.string.tmp_status_connected_disconnect_confirmation, + R.string.tmp_confirmation_yes, + R.string.tmp_confirmation_no, + () -> { + VPNCoordinator.getInstance().stopVPN(); + buttonStop.setEnabled(false); + } + ); + confirmationModal.show(); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/StopButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/StopButton.java new file mode 100644 index 0000000000..b956b2fd7c --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/connected/StopButton.java @@ -0,0 +1,89 @@ +package com.skywire.skycoin.vpn.activities.start.connected; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; + +public class StopButton extends ButtonBase implements View.OnTouchListener { + public StopButton(Context context) { + super(context); + } + public StopButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + public StopButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + private FrameLayout mainLayout; + private FrameLayout internalContainer; + private TextView textIcon; + private ProgressBar progressAnimation; + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_stop_button, this, true); + + mainLayout = this.findViewById(R.id.mainLayout); + internalContainer = this.findViewById(R.id.internalContainer); + textIcon = this.findViewById(R.id.textIcon); + progressAnimation = this.findViewById(R.id.progressAnimation); + + progressAnimation.setVisibility(GONE); + + internalContainer.setClipToOutline(true); + + setOnTouchListener(this); + setViewForCheckingClicks(this); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + mainLayout.setScaleX(0.98f); + mainLayout.setScaleY(0.98f); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP || event.getAction() == MotionEvent.ACTION_UP) { + mainLayout.setScaleX(1.0f); + mainLayout.setScaleY(1.0f); + } + + return false; + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + + if (enabled) { + setAlpha(1f); + } else { + setAlpha(0.5f); + } + } + + public void setBusyState(boolean busy) { + if (busy) { + if (!getBusyState()) { + progressAnimation.setVisibility(VISIBLE); + textIcon.setVisibility(GONE); + } + } else { + if (getBusyState()) { + progressAnimation.setVisibility(GONE); + textIcon.setVisibility(VISIBLE); + } + } + } + + public boolean getBusyState() { + return progressAnimation.getVisibility() == VISIBLE; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/CurrentServerButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/CurrentServerButton.java new file mode 100644 index 0000000000..af847828dc --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/CurrentServerButton.java @@ -0,0 +1,89 @@ +package com.skywire.skycoin.vpn.activities.start.disconnected; + +import android.content.Context; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.servers.ServerLists; +import com.skywire.skycoin.vpn.activities.servers.ServersActivity; +import com.skywire.skycoin.vpn.controls.ServerName; +import com.skywire.skycoin.vpn.extensible.ButtonBase; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.objects.LocalServerData; + +public class CurrentServerButton extends ButtonBase implements View.OnTouchListener { + public CurrentServerButton(Context context) { + super(context); + } + public CurrentServerButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + public CurrentServerButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + private FrameLayout mainContainer; + private FrameLayout internalContainer; + private LinearLayout serverContainer; + private ImageView imageFlag; + private ServerName serverName; + private TextView textBottom; + private TextView textNoServer; + + private RippleDrawable rippleDrawable; + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_current_server_button, this, true); + + mainContainer = this.findViewById (R.id.mainContainer); + internalContainer = this.findViewById (R.id.internalContainer); + serverContainer = this.findViewById (R.id.serverContainer); + imageFlag = this.findViewById (R.id.imageFlag); + serverName = this.findViewById (R.id.serverName); + textBottom = this.findViewById (R.id.textBottom); + textNoServer = this.findViewById (R.id.textNoServer); + + rippleDrawable = (RippleDrawable) internalContainer.getBackground(); + + mainContainer.setClipToOutline(true); + imageFlag.setClipToOutline(true); + + setOnTouchListener(this); + setViewForCheckingClicks(this); + } + + public void setData (LocalServerData currentServer) { + if (currentServer == null || currentServer.pk == null) { + textNoServer.setVisibility(VISIBLE); + serverContainer.setVisibility(GONE); + + return; + } + + serverContainer.setVisibility(VISIBLE); + textNoServer.setVisibility(GONE); + + serverName.setServer(ServersActivity.convertLocalServerData(currentServer), ServerLists.History, true); + textBottom.setText(currentServer.pk); + imageFlag.setImageResource(HelperFunctions.getFlagResourceId(currentServer.countryCode)); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (rippleDrawable != null) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/StartButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/StartButton.java new file mode 100644 index 0000000000..7ba05604e0 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/StartButton.java @@ -0,0 +1,95 @@ +package com.skywire.skycoin.vpn.activities.start.disconnected; + +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; + +public class StartButton extends ButtonBase implements Animator.AnimatorListener, View.OnTouchListener { + public StartButton(Context context) { + super(context); + } + public StartButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + public StartButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + private FrameLayout mainLayout; + private ImageView imageAnim; + private ImageView imageBackground; + + private AnimatorSet animSet; + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_start_button, this, true); + + mainLayout = this.findViewById(R.id.mainLayout); + imageAnim = this.findViewById(R.id.imageAnim); + imageBackground = this.findViewById(R.id.imageBackground); + + animSet = (AnimatorSet) AnimatorInflater.loadAnimator(getContext(), R.animator.anim_start_button); + animSet.setTarget(imageAnim); + + setOnTouchListener(this); + setViewForCheckingClicks(this); + } + + public void startAnimation() { + animSet.addListener(this); + animSet.start(); + } + + public void stopAnimation() { + animSet.removeAllListeners(); + animSet.cancel(); + } + + @Override + public void onAnimationStart(Animator animation) { } + @Override + public void onAnimationCancel(Animator animation) { } + @Override + public void onAnimationRepeat(Animator animation) { } + @Override + public void onAnimationEnd(Animator animation) { + animSet.start(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + mainLayout.setScaleX(0.9f); + mainLayout.setScaleY(0.9f); + imageBackground.setAlpha(1.0f); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP || event.getAction() == MotionEvent.ACTION_UP) { + mainLayout.setScaleX(1.0f); + mainLayout.setScaleY(1.0f); + imageBackground.setAlpha(0.7f); + } + + return false; + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + + if (enabled) { + setAlpha(1f); + } else { + setAlpha(0.5f); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/StartViewDisconnected.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/StartViewDisconnected.java new file mode 100644 index 0000000000..67d59299eb --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/activities/start/disconnected/StartViewDisconnected.java @@ -0,0 +1,156 @@ +package com.skywire.skycoin.vpn.activities.start.disconnected; + +import android.app.Activity; +import android.content.Context; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.index.IndexPageAdapter; +import com.skywire.skycoin.vpn.activities.servers.ServerLists; +import com.skywire.skycoin.vpn.activities.servers.ServersActivity; +import com.skywire.skycoin.vpn.activities.start.StartViewRightPanel; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +import java.io.Closeable; + +import io.reactivex.rxjava3.disposables.Disposable; + +public class StartViewDisconnected extends FrameLayout implements ClickEvent, Closeable { + public StartViewDisconnected(Context context) { + super(context); + Initialize(context, null); + } + public StartViewDisconnected(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public StartViewDisconnected(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private CurrentServerButton viewCurrentServerButton; + private StartButton startButton; + private TextView textServerNote; + private TextView textLastError; + private FrameLayout rightContainer; + private StartViewRightPanel rightPanel; + + private Activity parentActivity; + private IndexPageAdapter.RequestTabListener requestTabListener; + private Disposable currentServerSubscription; + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_start_disconnected, this, true); + + viewCurrentServerButton = findViewById(R.id.viewCurrentServerButton); + startButton = findViewById(R.id.startButton); + textServerNote = findViewById(R.id.textServerNote); + textLastError = findViewById(R.id.textLastError); + rightContainer = findViewById(R.id.rightContainer); + rightPanel = findViewById(R.id.rightPanel); + + viewCurrentServerButton.setClickEventListener(this); + startButton.setClickEventListener(this); + + currentServerSubscription = VPNServersPersistentData.getInstance().getCurrentServerObservable().subscribe(currentServer -> { + viewCurrentServerButton.setData(currentServer); + updateNote(currentServer); + }); + + setErrorMsg(VPNGeneralPersistentData.getLastError(null)); + + if (HelperFunctions.getWidthType(getContext()) == HelperFunctions.WidthTypes.SMALL) { + rightContainer.setVisibility(GONE); + } else { + textServerNote.setTextSize(TypedValue.COMPLEX_UNIT_PX, getContext().getResources().getDimension(R.dimen.small_text_size)); + textLastError.setTextSize(TypedValue.COMPLEX_UNIT_PX, getContext().getResources().getDimension(R.dimen.small_text_size)); + rightPanel.refreshIpData(); + } + } + + public void setRequestTabListener(IndexPageAdapter.RequestTabListener listener) { + requestTabListener = listener; + } + + public void setParentActivity(Activity activity) { + parentActivity = activity; + } + + public void startAnimation() { + startButton.startAnimation(); + } + + public void stopAnimation() { + startButton.stopAnimation(); + } + + public void updateRightBar() { + rightPanel.updateData(); + } + + public void setErrorMsg(String errorMsg) { + if (errorMsg != null) { + String start = getContext().getString(R.string.tmp_status_page_last_error); + textLastError.setText(start + " " + errorMsg); + textLastError.setVisibility(VISIBLE); + } else { + textLastError.setVisibility(GONE); + } + } + + private void updateNote(LocalServerData currentServer) { + if (currentServer == null) { + textServerNote.setVisibility(GONE); + + return; + } + + String note = HelperFunctions.getServerNote(currentServer); + + if (note != null) { + textServerNote.setText(note); + textServerNote.setVisibility(VISIBLE); + } else { + textServerNote.setVisibility(GONE); + } + } + + @Override + public void close() { + currentServerSubscription.dispose(); + rightPanel.close(); + stopAnimation(); + } + + @Override + public void onClick(View view) { + LocalServerData currentServer = VPNServersPersistentData.getInstance().getCurrentServer(); + if (currentServer != null) { + if (view.getId() == R.id.viewCurrentServerButton) { + HelperFunctions.showServerOptions(getContext(), ServersActivity.convertLocalServerData(currentServer), ServerLists.History); + } else { + if (parentActivity != null) { + boolean starting = HelperFunctions.prepareAndStartVpn(parentActivity, currentServer); + if (starting) { + startButton.setEnabled(false); + } + } + } + } else { + if (requestTabListener != null) { + requestTabListener.onOpenServerListRequested(); + } + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowBackground.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowBackground.java new file mode 100644 index 0000000000..9df7c76f0e --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowBackground.java @@ -0,0 +1,66 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewOutlineProvider; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; + +public class BoxRowBackground extends View { + public BoxRowBackground(Context context) { + super(context); + Initialize(context, null); + } + public BoxRowBackground(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public BoxRowBackground(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + BitmapDrawable bitmapDrawable; + + private void Initialize (Context context, AttributeSet attrs) { + setOutlineProvider(ViewOutlineProvider.BACKGROUND); + setClipToOutline(true); + + Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.box_pattern); + + bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap); + bitmapDrawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); + + setType(BoxRowTypes.TOP); + } + + @Override + protected void onDraw(Canvas canvas) { + bitmapDrawable.setBounds(new Rect(0, 0, canvas.getWidth(), canvas.getHeight())); + bitmapDrawable.draw(canvas); + + super.onDraw(canvas); + } + + public void setType(BoxRowTypes type) { + if (type == BoxRowTypes.TOP) { + setBackgroundResource(R.drawable.box_row_rounded_box_1); + } else if (type == BoxRowTypes.MIDDLE) { + setBackgroundResource(R.drawable.box_row_rounded_box_2); + } else if (type == BoxRowTypes.BOTTOM) { + setBackgroundResource(R.drawable.box_row_rounded_box_3); + } else { + setBackgroundResource(R.drawable.box_row_rounded_box_4); + } + + this.invalidate(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowLayout.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowLayout.java new file mode 100644 index 0000000000..868c713d29 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowLayout.java @@ -0,0 +1,219 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +public class BoxRowLayout extends FrameLayout implements ClickEvent { + public BoxRowLayout(Context context) { + super(context); + Initialize(context, null); + } + public BoxRowLayout(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public BoxRowLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private View baseBackground; + private BoxRowBackground background; + private BoxRowRipple ripple; + private View separator; + + private ClickEvent clickListener; + + private boolean addExtraPaddingForTablets = false; + private boolean ignoreMargins = false; + private boolean ignoreClicks = false; + private boolean hideSeparator = false; + + private int tabletExtraHorizontalPadding = 0; + private float horizontalPadding; + private float verticalPadding; + + private void Initialize (Context context, AttributeSet attrs) { + baseBackground = new View(context); + background = new BoxRowBackground(context); + ripple = new BoxRowRipple(context); + separator = new View(context); + + int type = 1; + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.BoxRowLayout, + 0, 0 + ); + + type = attributes.getInteger(R.styleable.BoxRowLayout_box_row_type, 1); + + addExtraPaddingForTablets = attributes.getBoolean(R.styleable.BoxRowLayout_add_extra_padding_for_tablets, false); + ignoreMargins = attributes.getBoolean(R.styleable.BoxRowLayout_ignore_margins, false); + ignoreClicks = attributes.getBoolean(R.styleable.BoxRowLayout_ignore_clicks, false); + hideSeparator = attributes.getBoolean(R.styleable.BoxRowLayout_hide_separator, false); + + setUseBigFastClickPrevention(attributes.getBoolean(R.styleable.BoxRowLayout_use_big_fast_click_prevention, true)); + + attributes.recycle(); + } + + horizontalPadding = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 10, + getResources().getDisplayMetrics() + ); + if (!ignoreMargins) { + horizontalPadding += getContext().getResources().getDimension(R.dimen.box_row_layout_horizontal_padding); + } + + verticalPadding = 0; + if (!ignoreMargins) { + verticalPadding += getContext().getResources().getDimension(R.dimen.box_row_layout_vertical_padding); + } + + if (addExtraPaddingForTablets) { + tabletExtraHorizontalPadding = HelperFunctions.getTabletExtraHorizontalPadding(getContext()); + } + + separator.setBackgroundResource(R.color.box_separator); + + if (type == 0) { + setType(BoxRowTypes.TOP); + } else if (type == 1) { + setType(BoxRowTypes.MIDDLE); + } else if (type == 2) { + setType(BoxRowTypes.BOTTOM); + } else if (type == 3) { + setType(BoxRowTypes.SINGLE); + } + + this.setClipToPadding(false); + + this.addView(baseBackground); + this.addView(background); + if (!ignoreClicks) { + ripple.setClickEventListener(this); + this.addView(ripple); + } + this.addView(separator); + + setClickable(false); + } + + public void setClickEventListener(ClickEvent listener) { + clickListener = listener; + } + + public void setUseBigFastClickPrevention(boolean useBigFastClickPrevention) { + ripple.setUseBigFastClickPrevention(useBigFastClickPrevention); + } + + public void setType(BoxRowTypes type) { + float bottomPaddingExtra = 0; + float topPaddingExtra = 0; + + if (type == BoxRowTypes.TOP) { + baseBackground.setBackgroundResource(R.drawable.background_box1); + + topPaddingExtra = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 10, + getResources().getDisplayMetrics() + ); + + separator.setVisibility(View.VISIBLE); + } else if (type == BoxRowTypes.MIDDLE) { + baseBackground.setBackgroundResource(R.drawable.background_box2); + separator.setVisibility(View.VISIBLE); + } else if (type == BoxRowTypes.BOTTOM) { + baseBackground.setBackgroundResource(R.drawable.background_box3); + + bottomPaddingExtra = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 15, + getResources().getDisplayMetrics() + ); + + separator.setVisibility(View.GONE); + } else if (type == BoxRowTypes.SINGLE) { + baseBackground.setBackgroundResource(R.drawable.background_box4); + + topPaddingExtra = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 10, + getResources().getDisplayMetrics() + ); + bottomPaddingExtra = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 15, + getResources().getDisplayMetrics() + ); + + separator.setVisibility(View.GONE); + } + + if (hideSeparator) { + separator.setVisibility(View.GONE); + } + + int finalLeftPadding = (int)horizontalPadding; + int finalTopPadding = (int)(verticalPadding + topPaddingExtra); + int finalRightPadding = (int)horizontalPadding; + int finalBottomPadding = (int)(verticalPadding + bottomPaddingExtra); + + this.setPadding(finalLeftPadding + tabletExtraHorizontalPadding, finalTopPadding, finalRightPadding + tabletExtraHorizontalPadding, finalBottomPadding); + + FrameLayout.LayoutParams backgroundLayoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + backgroundLayoutParams.leftMargin = -finalLeftPadding; + backgroundLayoutParams.rightMargin = -finalRightPadding; + if (finalTopPadding > 0) { + backgroundLayoutParams.topMargin = -finalTopPadding; + } + if (finalBottomPadding > 0) { + backgroundLayoutParams.bottomMargin = -finalBottomPadding; + } + + baseBackground.setLayoutParams(backgroundLayoutParams); + background.setLayoutParams(backgroundLayoutParams); + background.setType(type); + if (!ignoreClicks) { + ripple.setLayoutParams(backgroundLayoutParams); + ripple.setType(type); + } + + float separatorHeight = getContext().getResources().getDimension(R.dimen.box_row_layout_separator_height); + float separatorHorizontalMargin; + if (ignoreMargins) { + separatorHorizontalMargin = getContext().getResources().getDimension(R.dimen.box_row_layout_separator_combined_horizontal_margin); + } else { + separatorHorizontalMargin = getContext().getResources().getDimension(R.dimen.box_row_layout_separator_horizontal_margin); + } + + FrameLayout.LayoutParams separatorLayoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)Math.round(separatorHeight)); + separatorLayoutParams.gravity = Gravity.BOTTOM; + separatorLayoutParams.bottomMargin = -finalBottomPadding; + separatorLayoutParams.leftMargin = (int)separatorHorizontalMargin; + separatorLayoutParams.rightMargin = (int)separatorHorizontalMargin; + separator.setLayoutParams(separatorLayoutParams); + } + + @Override + public void onClick(View view) { + if (clickListener != null) { + clickListener.onClick(this); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowRipple.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowRipple.java new file mode 100644 index 0000000000..42ea085859 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/BoxRowRipple.java @@ -0,0 +1,68 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.widget.FrameLayout; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; +import com.skywire.skycoin.vpn.helpers.BoxRowTypes; + +public class BoxRowRipple extends ButtonBase implements View.OnTouchListener { + public BoxRowRipple(Context context) { + super(context); + } + public BoxRowRipple(Context context, AttributeSet attrs) { + super(context, attrs); + } + public BoxRowRipple(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + RippleDrawable rippleDrawable; + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + setOutlineProvider(ViewOutlineProvider.BACKGROUND); + setClipToOutline(true); + setClickable(true); + + View ripple = new View(context); + FrameLayout.LayoutParams rippleLayoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + ripple.setLayoutParams(rippleLayoutParams); + ripple.setBackgroundResource(R.drawable.box_ripple); + this.addView(ripple); + + rippleDrawable = (RippleDrawable) ripple.getBackground(); + + ripple.setOnTouchListener(this); + setViewForCheckingClicks(ripple); + + setType(BoxRowTypes.TOP); + } + + public void setType(BoxRowTypes type) { + if (type == BoxRowTypes.TOP) { + setBackgroundResource(R.drawable.box_row_rounded_box_1); + } else if (type == BoxRowTypes.MIDDLE) { + setBackgroundResource(R.drawable.box_row_rounded_box_2); + } else if (type == BoxRowTypes.BOTTOM) { + setBackgroundResource(R.drawable.box_row_rounded_box_3); + } else { + setBackgroundResource(R.drawable.box_row_rounded_box_4); + } + + this.invalidate(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ClickableLinearLayout.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ClickableLinearLayout.java new file mode 100644 index 0000000000..78939294e2 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ClickableLinearLayout.java @@ -0,0 +1,66 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.LinearLayout; + +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.ClickTimeManagement; +import com.skywire.skycoin.vpn.helpers.Globals; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class ClickableLinearLayout extends LinearLayout implements View.OnTouchListener, View.OnClickListener { + private ClickEvent clickListener; + private ClickTimeManagement buttonTimeManager = new ClickTimeManagement(); + + public ClickableLinearLayout(Context context) { + super(context); + Initialize(context, null); + } + public ClickableLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public ClickableLinearLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + protected void Initialize (Context context, AttributeSet attrs) { + setOnTouchListener(this); + setOnClickListener(this); + } + + public void setClickEventListener(ClickEvent listener) { + clickListener = listener; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + setAlpha(0.5f); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP || event.getAction() == MotionEvent.ACTION_UP) { + setAlpha(1f); + } + + return false; + } + + @Override + public void onClick(View view) { + if (clickListener != null && buttonTimeManager.canClick()) { + buttonTimeManager.informClickMade(); + Observable.just(1).delay(Globals.CLICK_DELAY_MS, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(v -> clickListener.onClick(this)); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ConfirmationModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ConfirmationModalWindow.java new file mode 100644 index 0000000000..cea138f401 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ConfirmationModalWindow.java @@ -0,0 +1,65 @@ +package com.skywire.skycoin.vpn.controls; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +public class ConfirmationModalWindow extends Dialog implements ClickEvent { + public interface Confirmed { + void confirmed(); + } + + private TextView text; + private ModalWindowButton buttonCancel; + private ModalWindowButton buttonConfirm; + + private int textResource; + private int confirmBtnResource; + private int cancelBtnResource; + private Confirmed event; + + public ConfirmationModalWindow(Context ctx, int textResource, int confirmBtnResource, int cancelBtnResource, Confirmed event) { + super(ctx); + + this.textResource = textResource; + this.confirmBtnResource = confirmBtnResource; + this.cancelBtnResource = cancelBtnResource; + this.event = event; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_confirmation_dialog); + + text = findViewById(R.id.text); + buttonCancel = findViewById(R.id.buttonCancel); + buttonConfirm = findViewById(R.id.buttonConfirm); + + text.setText(textResource); + buttonCancel.setText(cancelBtnResource); + buttonConfirm.setText(confirmBtnResource); + + buttonCancel.setClickEventListener(this); + buttonConfirm.setClickEventListener(this); + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.buttonConfirm && event != null) { + event.confirmed(); + } + + dismiss(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/EditServerValueModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/EditServerValueModalWindow.java new file mode 100644 index 0000000000..e11fa85e9a --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/EditServerValueModalWindow.java @@ -0,0 +1,122 @@ +package com.skywire.skycoin.vpn.controls; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; + +import com.google.android.material.textfield.TextInputLayout; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.servers.VpnServerForList; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +public class EditServerValueModalWindow extends Dialog implements ClickEvent { + private ModalBase modalBase; + private TextInputLayout editContainer; + private EditText editValue; + private ModalWindowButton buttonCancel; + private ModalWindowButton buttonConfirm; + + private boolean editingName; + private VpnServerForList server; + + public EditServerValueModalWindow(Context ctx, boolean editingName, VpnServerForList server) { + super(ctx); + + this.editingName = editingName; + this.server = server; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_edit_server_value_modal); + + modalBase = findViewById(R.id.modalBase); + editContainer = findViewById(R.id.editContainer); + editValue = findViewById(R.id.editValue); + buttonCancel = findViewById(R.id.buttonCancel); + buttonConfirm = findViewById(R.id.buttonConfirm); + + LocalServerData localServerData = VPNServersPersistentData.getInstance().processFromList(server); + if (editingName) { + modalBase.setTitle(R.string.tmp_edit_value_name_title); + editContainer.setHint(getContext().getText(R.string.tmp_edit_value_name_label)); + + if (localServerData.customName != null) { + editValue.setText(localServerData.customName); + } else { + editValue.setText(""); + } + } else { + modalBase.setTitle(R.string.tmp_edit_value_note_title); + editContainer.setHint(getContext().getText(R.string.tmp_edit_value_note_label)); + + if (localServerData.personalNote != null) { + editValue.setText(localServerData.personalNote); + } else { + editValue.setText(""); + } + } + + editValue.setOnEditorActionListener((v, actionId, event) -> { + if ( + actionId == EditorInfo.IME_ACTION_DONE || + (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) + ) { + makeChange(); + dismiss(); + + return true; + } + + return false; + }); + + editValue.setSelection(editValue.getText().length()); + + buttonCancel.setClickEventListener(this); + buttonConfirm.setClickEventListener(this); + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.buttonConfirm) { + makeChange(); + } + + dismiss(); + } + + private void makeChange() { + LocalServerData localServerData = VPNServersPersistentData.getInstance().processFromList(server); + + String newValue = editValue.getText().toString().trim(); + String currentValue = editingName ? localServerData.customName : localServerData.personalNote; + if (currentValue == null) { + currentValue = ""; + } + if (newValue.equals(currentValue)) { + return; + } + + if (editingName) { + localServerData.customName = newValue; + } else { + localServerData.personalNote = newValue; + } + VPNServersPersistentData.getInstance().updateServer(localServerData); + + HelperFunctions.showToast(getContext().getString(R.string.tmp_edit_value_changes_made_confirmation), true); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ManualServerModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ManualServerModalWindow.java new file mode 100644 index 0000000000..cab1ee98c2 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ManualServerModalWindow.java @@ -0,0 +1,154 @@ +package com.skywire.skycoin.vpn.controls; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.objects.ManualVpnServerData; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +import skywiremob.Skywiremob; + +public class ManualServerModalWindow extends Dialog implements ClickEvent, TextWatcher { + public interface Confirmed { + void confirmed(LocalServerData server); + } + + private EditText editPk; + private EditText editPassword; + private EditText editName; + private EditText editNote; + private ModalWindowButton buttonCancel; + private ModalWindowButton buttonConfirm; + + private Confirmed event; + private boolean hasError; + + public ManualServerModalWindow(Context ctx, Confirmed event) { + super(ctx); + + this.event = event; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_manual_server_modal); + + editPk = findViewById(R.id.editPk); + editPassword = findViewById(R.id.editPassword); + editName = findViewById(R.id.editName); + editNote = findViewById(R.id.editNote); + buttonCancel = findViewById(R.id.buttonCancel); + buttonConfirm = findViewById(R.id.buttonConfirm); + + editPk.addTextChangedListener(this); + + editPk.setImeOptions(EditorInfo.IME_ACTION_NEXT); + editName.setImeOptions(EditorInfo.IME_ACTION_NEXT); + editNote.setImeOptions(EditorInfo.IME_ACTION_DONE); + + editPk.setSelection(editName.getText().length()); + + editNote.setOnEditorActionListener((v, actionId, event) -> { + if ( + actionId == EditorInfo.IME_ACTION_DONE || + (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) + ) { + if (!hasError) { + process(); + dismiss(); + } + + return true; + } + + return false; + }); + + buttonCancel.setClickEventListener(this); + buttonConfirm.setClickEventListener(this); + + buttonConfirm.setEnabled(false); + hasError = true; + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } + @Override + public void afterTextChanged(Editable s) { } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + hasError = false; + if (editPk.getText().length() < 66) { + editPk.setError(getContext().getText(R.string.add_server_pk_length_error)); + hasError = true; + } else if (Skywiremob.isPKValid(editPk.getText().toString()).getCode() != Skywiremob.ErrCodeNoError) { + editPk.setError(getContext().getText(R.string.add_server_pk_invalid_error)); + hasError = true; + } + + if (hasError) { + buttonConfirm.setEnabled(false); + } else { + buttonConfirm.setEnabled(true); + } + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.buttonConfirm) { + process(); + } + + dismiss(); + } + + private void process() { + if (hasError) { + return; + } + + LocalServerData savedVersion = VPNServersPersistentData.getInstance().getSavedVersion(editPk.getText().toString().trim()); + + ManualVpnServerData serverData = new ManualVpnServerData(); + serverData.pk = editPk.getText().toString().trim(); + + String password = editPassword.getText().toString(); + if (password != null && !password.equals("")) { + serverData.password = password; + } + + if (editName.getText() != null && !editName.getText().toString().trim().equals("")) { + serverData.name = editName.getText().toString().trim(); + } else if (savedVersion != null && savedVersion.customName != null && !savedVersion.customName.equals("")) { + serverData.name = savedVersion.customName; + } + + if (editNote.getText() != null && !editNote.getText().toString().trim().equals("")) { + serverData.note = editNote.getText().toString().trim(); + } else if (savedVersion != null && savedVersion.personalNote != null && !savedVersion.personalNote.equals("")) { + serverData.note = savedVersion.personalNote; + } + + LocalServerData localServerData = VPNServersPersistentData.getInstance().processFromManual(serverData); + if (event != null) { + event.confirmed(localServerData); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ModalBase.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ModalBase.java new file mode 100644 index 0000000000..cfb280343a --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ModalBase.java @@ -0,0 +1,115 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; + +public class ModalBase extends FrameLayout { + public ModalBase(Context context) { + super(context); + Initialize(context, null); + } + public ModalBase(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public ModalBase(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private FrameLayout mainContainer; + private TextView textTitle; + private FrameLayout contentArea; + + private void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_modal_base, this, true); + + mainContainer = findViewById(R.id.mainContainer); + textTitle = findViewById(R.id.textTitle); + contentArea = findViewById(R.id.contentArea); + + mainContainer.setClipToOutline(true); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.ModalBase, + 0, 0 + ); + + String title = attributes.getString(R.styleable.ModalBase_title); + if (title != null) { + textTitle.setText(title); + } + + boolean removeInternalPadding = attributes.getBoolean(R.styleable.ModalBase_remove_internal_padding, false); + if (removeInternalPadding) { + contentArea.setPadding(0, 0, 0, 0); + } + + attributes.recycle(); + } + } + + public void setTitle(int resourceId) { + textTitle.setText(resourceId); + } + + public void setTitleString(String title) { + textTitle.setText(title); + } + + @Override + public void addView(View child) { + if (contentArea != null) { + contentArea.addView(child); + } else { + super.addView(child); + } + } + + @Override + public void addView(View child, int index) { + if (contentArea != null) { + contentArea.addView(child, index); + } else { + super.addView(child, index); + } + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + if (contentArea != null) { + contentArea.addView(child, params); + } else { + super.addView(child, params); + } + } + + @Override + public void addView(View child, int width, int height) { + if (contentArea != null) { + contentArea.addView(child, width, height); + } else { + super.addView(child, width, height); + } + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (contentArea != null) { + contentArea.addView(child, index, params); + } else { + super.addView(child, index, params); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ModalWindowButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ModalWindowButton.java new file mode 100644 index 0000000000..762fe9550a --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ModalWindowButton.java @@ -0,0 +1,93 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; + +public class ModalWindowButton extends ButtonBase implements View.OnTouchListener { + private FrameLayout mainContainer; + private FrameLayout effectContainer; + private TextView text; + + private RippleDrawable rippleDrawable; + + public ModalWindowButton(Context context) { + super(context); + } + public ModalWindowButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + public ModalWindowButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_modal_window_button, this, true); + + mainContainer = this.findViewById (R.id.mainContainer); + effectContainer = this.findViewById (R.id.effectContainer); + text = this.findViewById (R.id.text); + + mainContainer.setClipToOutline(true); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.ModalWindowButton, + 0, 0 + ); + + String textForButton = attributes.getString(R.styleable.ModalWindowButton_text); + if (textForButton != null) { + text.setText(textForButton); + } + + if (attributes.getBoolean(R.styleable.ModalWindowButton_use_secondary_color, false)) { + mainContainer.setBackgroundResource(R.drawable.modal_button_secondary_background); + effectContainer.setBackgroundResource(R.drawable.modal_button_secondary_ripple); + } + + attributes.recycle(); + } + + rippleDrawable = (RippleDrawable) effectContainer.getBackground(); + + setOnTouchListener(this); + setViewForCheckingClicks(this); + } + + public void setText(int resourceId) { + text.setText(resourceId); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (rippleDrawable != null) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + + return false; + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + + if (enabled) { + this.setAlpha(1); + } else { + this.setAlpha(0.35f); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/Select.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/Select.java new file mode 100644 index 0000000000..9f3f8b9494 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/Select.java @@ -0,0 +1,143 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.EditText; +import android.widget.FrameLayout; + +import androidx.core.content.ContextCompat; + +import com.google.android.material.textfield.TextInputLayout; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.options.OptionsItem; +import com.skywire.skycoin.vpn.controls.options.OptionsModalWindow; +import com.skywire.skycoin.vpn.helpers.ClickTimeManagement; + +import java.util.ArrayList; + +public class Select extends FrameLayout implements View.OnTouchListener, View.OnClickListener { + public static class SelectOption { + public String text; + public String value; + public Integer iconId; + } + + private TextInputLayout container; + private EditText edit; + private FrameLayout clickArea; + + private ArrayList options; + private int selectedIndex = 0; + private ClickTimeManagement buttonTimeManager = new ClickTimeManagement(); + + public Select(Context context) { + super(context); + Initialize(context, null); + } + public Select(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public Select(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_select, this, true); + + container = this.findViewById (R.id.container); + edit = this.findViewById (R.id.edit); + clickArea = this.findViewById (R.id.clickArea); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.Select, + 0, 0 + ); + + String hint = attributes.getString(R.styleable.Select_hint); + if (hint != null) { + this.container.setHint(hint); + } + + attributes.recycle(); + } + + clickArea.setOnTouchListener(this); + clickArea.setOnClickListener(this); + } + + public void setValues(ArrayList options, int selectedIndex) { + this.options = options; + this.selectedIndex = selectedIndex; + + updateContent(); + } + + private void updateContent() { + SelectOption currentOption = options.get(selectedIndex); + + Drawable leftDrawable = null; + if (currentOption.iconId != null) { + leftDrawable = ContextCompat.getDrawable(getContext(), currentOption.iconId); + leftDrawable.setBounds(0, 0, leftDrawable.getIntrinsicWidth(), leftDrawable.getIntrinsicHeight()); + } + Drawable[] drawables = edit.getCompoundDrawables(); + edit.setCompoundDrawables(leftDrawable, drawables[1], drawables[2], drawables[3]); + + if (currentOption.iconId != null) { + edit.setText(" " + currentOption.text); + } else { + edit.setText(currentOption.text); + } + } + + public String getSelectedValue() { + return options.get(selectedIndex).value; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + setAlpha(0.5f); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP || event.getAction() == MotionEvent.ACTION_UP) { + setAlpha(1f); + } + + return false; + } + + @Override + public void onClick(View view) { + if (!buttonTimeManager.canClick()) { + return; + } + + buttonTimeManager.informClickMade(); + + ArrayList optionsToShow = new ArrayList(); + + for (SelectOption option : options) { + OptionsItem.SelectableOption optionToShow = new OptionsItem.SelectableOption(); + optionToShow.drawableId = option.iconId; + optionToShow.label = option.text; + + optionsToShow.add(optionToShow); + } + + OptionsModalWindow modal = new OptionsModalWindow(getContext(), null, optionsToShow, (int selectedOption) -> { + selectedIndex = selectedOption; + updateContent(); + }); + + modal.show(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerInfoModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerInfoModalWindow.java new file mode 100644 index 0000000000..83c81e6fef --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerInfoModalWindow.java @@ -0,0 +1,276 @@ +package com.skywire.skycoin.vpn.controls; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.view.View; +import android.view.Window; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.core.content.res.ResourcesCompat; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.servers.ServerLists; +import com.skywire.skycoin.vpn.activities.servers.VpnServerForList; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.CountriesList; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.helpers.MaterialFontSpan; +import com.skywire.skycoin.vpn.objects.ServerFlags; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +public class ServerInfoModalWindow extends Dialog implements ClickEvent { + private ForegroundColorSpan lightColorSpan = + new ForegroundColorSpan(ResourcesCompat.getColor(getContext().getResources(), R.color.modal_window_light_text, null)); + private ForegroundColorSpan superLightColorSpan = + new ForegroundColorSpan(ResourcesCompat.getColor(getContext().getResources(), R.color.modal_window_super_light_text, null)); + private DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm a"); + + private TextView textName; + private TextView textCustomName; + private TextView textPk; + private TextView textNote; + private TextView textPersonalNote; + private TextView textLastTimeUsed; + + private TextView textCountry; + private TextView textCountryCode; + private TextView textLocation; + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + private LinearLayout connectivityContainer; + private TextView textCongestion; + private TextView textCongestionRating; + private TextView textLatency; + private TextView textLatencyRating; + private TextView textHops; + */ + + private LinearLayout specialContainer; + private TextView textIsCurrent; + private TextView textIsFavorite; + private TextView textBlocked; + private TextView textInHistory; + private TextView textEnteredManually; + private TextView textHasPassword; + + private ModalWindowButton buttonClose; + + private VpnServerForList server; + private ServerLists listType; + + public ServerInfoModalWindow(Context ctx, VpnServerForList server, ServerLists listType) { + super(ctx); + + this.server = server; + this.listType = listType; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_server_info_modal); + + textName = findViewById(R.id.textName); + textCustomName = findViewById(R.id.textCustomName); + textPk = findViewById(R.id.textPk); + textNote = findViewById(R.id.textNote); + textPersonalNote = findViewById(R.id.textPersonalNote); + textLastTimeUsed = findViewById(R.id.textLastTimeUsed); + + textCountry = findViewById(R.id.textCountry); + textCountryCode = findViewById(R.id.textCountryCode); + textLocation = findViewById(R.id.textLocation); + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + connectivityContainer = findViewById(R.id.connectivityContainer); + textCongestion = findViewById(R.id.textCongestion); + textCongestionRating = findViewById(R.id.textCongestionRating); + textLatency = findViewById(R.id.textLatency); + textLatencyRating = findViewById(R.id.textLatencyRating); + textHops = findViewById(R.id.textHops); + */ + + specialContainer = findViewById(R.id.specialContainer); + textIsCurrent = findViewById(R.id.textIsCurrent); + textIsFavorite = findViewById(R.id.textIsFavorite); + textBlocked = findViewById(R.id.textBlocked); + textInHistory = findViewById(R.id.textInHistory); + textEnteredManually = findViewById(R.id.textEnteredManually); + textHasPassword = findViewById(R.id.textHasPassword); + + buttonClose = findViewById(R.id.buttonClose); + + putValue(textName, R.string.server_info_name, server.name, null, null); + putValue(textCustomName, R.string.server_info_custom_name, server.customName, null, null); + putValue(textPk, R.string.server_info_pk, server.pk, null, null); + if ((server.note != null && !server.note.trim().equals("")) && (server.personalNote != null && !server.personalNote.trim().equals(""))) { + putValue(textNote, R.string.server_info_original_note, server.note, null, null); + putValue(textPersonalNote, R.string.server_info_personal_note, server.personalNote, null, null); + } else if (server.note != null && !server.note.trim().equals("")) { + putValue(textNote, R.string.server_info_note, server.note, null, null); + textPersonalNote.setVisibility(View.GONE); + } else if (server.personalNote != null && !server.personalNote.trim().equals("")) { + putValue(textPersonalNote, R.string.server_info_note, server.personalNote, null, null); + textNote.setVisibility(View.GONE); + } else { + putValue(textNote, R.string.server_info_note, null, null, null); + textPersonalNote.setVisibility(View.GONE); + } + if (server.inHistory) { + putValue(textLastTimeUsed, R.string.server_info_last_time_used, dateFormat.format(server.lastUsed), null, null); + } else { + textLastTimeUsed.setVisibility(View.GONE); + } + + putValue(textCountry, R.string.server_info_country, CountriesList.getCountryName(server.countryCode), null, null); + if (!server.countryCode.toUpperCase().equals("ZZ")) { + putValue(textCountryCode, R.string.server_info_country_code, server.countryCode.toUpperCase(), null, null); + } else { + textCountryCode.setVisibility(View.GONE); + } + putValue(textLocation, R.string.server_info_location, server.location, null, null); + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + if (listType == ServerLists.Public) { + putValue(textCongestion, R.string.server_info_congestion, + HelperFunctions.zeroDecimalsFormatter.format(server.congestion) + "%", null, null + ); + putValue(textCongestionRating, R.string.server_info_congestion_rating, + getContext().getText(ServerRatings.getTextForRating(server.congestionRating)).toString(), getRatingColor(server.congestionRating), null + ); + putValue(textLatency, R.string.server_info_latency, + HelperFunctions.getLatencyValue(server.latency), null, null + ); + putValue(textLatencyRating, R.string.server_info_latency_rating, + getContext().getText(ServerRatings.getTextForRating(server.latencyRating)).toString(), getRatingColor(server.latencyRating), null + ); + putValue(textHops, R.string.server_info_hops, + server.hops + "", null, null + ); + } else { + connectivityContainer.setVisibility(View.GONE); + } + */ + + boolean hasSpecialCondition = false; + boolean isTheCurrentServer = VPNServersPersistentData.getInstance().getCurrentServer() != null && + VPNServersPersistentData.getInstance().getCurrentServer().pk.toLowerCase().equals(server.pk.toLowerCase()); + + if (isTheCurrentServer) { + putValue(textIsCurrent, R.string.server_info_is_current, getBooleanString(true), null, "\ue876"); + hasSpecialCondition = true; + } else { + textIsCurrent.setVisibility(View.GONE); + } + if (server.flag == ServerFlags.Favorite) { + ForegroundColorSpan iconColor = new ForegroundColorSpan(ResourcesCompat.getColor(getContext().getResources(),R.color.yellow, null)); + putValue(textIsFavorite, R.string.server_info_is_favorite, getBooleanString(true), iconColor, "\ue838"); + hasSpecialCondition = true; + } else { + textIsFavorite.setVisibility(View.GONE); + } + if (server.flag == ServerFlags.Blocked) { + ForegroundColorSpan iconColor = new ForegroundColorSpan(ResourcesCompat.getColor(getContext().getResources(),R.color.red, null)); + putValue(textBlocked, R.string.server_info_is_blocked, getBooleanString(true), iconColor, "\ue14c"); + hasSpecialCondition = true; + } else { + textBlocked.setVisibility(View.GONE); + } + if (server.inHistory && !isTheCurrentServer) { + putValue(textInHistory, R.string.server_info_is_in_history, getBooleanString(true), null, "\ue889"); + hasSpecialCondition = true; + } else { + textInHistory.setVisibility(View.GONE); + } + if (server.enteredManually) { + putValue(textEnteredManually, R.string.server_info_entered_manually, getBooleanString(true), null, null); + hasSpecialCondition = true; + } else { + textEnteredManually.setVisibility(View.GONE); + } + if (server.enteredManually && server.hasPassword) { + putValue(textHasPassword, R.string.server_info_has_password, getBooleanString(true), null, "\ue899"); + hasSpecialCondition = true; + } else { + textHasPassword.setVisibility(View.GONE); + } + if (!hasSpecialCondition) { + specialContainer.setVisibility(View.GONE); + } + + buttonClose.setClickEventListener(this); + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void onClick(View view) { + dismiss(); + } + + private void putValue(TextView textView, int titleResurce, String value, ForegroundColorSpan valueColor, String icon) { + SpannableStringBuilder finalText = new SpannableStringBuilder(getContext().getString(titleResurce)); + finalText.setSpan(lightColorSpan, 0, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + finalText.append("\n"); + int initialValuePos = finalText.length(); + + if (value != null && !value.trim().equals("")) { + if (icon == null) { + finalText.append(value); + + if (valueColor != null) { + finalText.setSpan(valueColor, initialValuePos, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } else { + finalText.append(icon + " "); + finalText.setSpan(new MaterialFontSpan(getContext()), initialValuePos, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + finalText.setSpan(new RelativeSizeSpan(0.75f), initialValuePos, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + if (valueColor != null) { + finalText.setSpan(valueColor, initialValuePos, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + finalText.append(value); + } + } else { + finalText.append(getContext().getString(R.string.server_info_without_value)); + finalText.setSpan(superLightColorSpan, initialValuePos, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + textView.setText(finalText); + } + + private String getBooleanString(boolean value) { + if (value) { + return getContext().getText(R.string.general_yes).toString(); + } + + return getContext().getText(R.string.general_no).toString(); + } + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + private ForegroundColorSpan getRatingColor(ServerRatings rating) { + if (rating == ServerRatings.Gold) { + return new ForegroundColorSpan(ResourcesCompat.getColor(getContext().getResources(), R.color.gold, null)); + } else if (rating == ServerRatings.Silver) { + return new ForegroundColorSpan(ResourcesCompat.getColor(getContext().getResources(), R.color.silver, null)); + } + + return new ForegroundColorSpan(ResourcesCompat.getColor(getContext().getResources(), R.color.bronze, null)); + } + */ +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerName.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerName.java new file mode 100644 index 0000000000..eabbcf5992 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerName.java @@ -0,0 +1,151 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.core.content.res.ResourcesCompat; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.servers.ServerLists; +import com.skywire.skycoin.vpn.activities.servers.VpnServerForList; +import com.skywire.skycoin.vpn.helpers.AlphaSpan; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.helpers.MaterialFontSpan; +import com.skywire.skycoin.vpn.objects.ServerFlags; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +public class ServerName extends FrameLayout { + private TextView text; + + private String defaultName = ""; + private boolean showConfigIcon = false; + + public ServerName(Context context) { + super(context); + Initialize(context, null); + } + public ServerName(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public ServerName(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private void Initialize(Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_server_name, this, true); + + text = this.findViewById (R.id.text); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.ServerName, + 0, 0 + ); + + boolean centerText = attributes.getBoolean(R.styleable.ServerName_center_text, false); + if (centerText) { + text.setGravity(Gravity.CENTER_HORIZONTAL); + } + + String defaultName = attributes.getString(R.styleable.ServerName_default_name); + if (defaultName != null) { + this.defaultName = defaultName; + text.setText(defaultName); + } + + float textSize = attributes.getDimensionPixelSize(R.styleable.ServerName_text_size, -1); + if (textSize != -1) { + text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); + } + + showConfigIcon = attributes.getBoolean(R.styleable.ServerName_show_config_icon, false); + + attributes.recycle(); + } + } + + public void setServer(VpnServerForList server, ServerLists listType, boolean doNotMarkCurrent) { + if (server == null) { + text.setText(defaultName); + + return; + } + + MaterialFontSpan materialFontSpan = new MaterialFontSpan(getContext()); + RelativeSizeSpan relativeSizeSpan = new RelativeSizeSpan(0.75f); + + int initialicons = 0; + boolean isCurrentServer = VPNServersPersistentData.getInstance().getCurrentServer() != null && + server.pk.toLowerCase().equals(VPNServersPersistentData.getInstance().getCurrentServer().pk.toLowerCase()); + + SpannableStringBuilder finalText = new SpannableStringBuilder(""); + + if (isCurrentServer && !doNotMarkCurrent) { + finalText.append("\ue876 "); + initialicons += 1; + } + if (server.flag == ServerFlags.Blocked && listType != ServerLists.Blocked) { + finalText.append("\ue14c "); + finalText.setSpan(new ForegroundColorSpan( + ResourcesCompat.getColor(getResources(),R.color.red, null)), + initialicons * 2, + (initialicons * 2) + 2, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ); + initialicons += 1; + } + if (server.flag == ServerFlags.Favorite && listType != ServerLists.Favorites) { + finalText.append("\ue838 "); + finalText.setSpan(new ForegroundColorSpan( + ResourcesCompat.getColor(getResources(),R.color.yellow, null)), + initialicons * 2, + (initialicons * 2) + 2, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ); + initialicons += 1; + } + if (server.inHistory && listType != ServerLists.History && !isCurrentServer) { + finalText.append("\ue889 "); + initialicons += 1; + } + if (server.hasPassword) { + finalText.append("\ue899 "); + initialicons += 1; + } + + if (initialicons != 0) { + finalText.setSpan(materialFontSpan, 0, initialicons * 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + finalText.setSpan(relativeSizeSpan, 0, initialicons * 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + finalText.append(HelperFunctions.getServerName(server, defaultName)); + + if (showConfigIcon) { + finalText.append(" \ue8b8"); + + materialFontSpan = new MaterialFontSpan(getContext()); + relativeSizeSpan = new RelativeSizeSpan(0.75f); + AlphaSpan alphaSpan = new AlphaSpan(128); + + finalText.setSpan(materialFontSpan, finalText.length() - 2, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + finalText.setSpan(relativeSizeSpan, finalText.length() - 2, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + finalText.setSpan(alphaSpan, finalText.length() - 2, finalText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + text.setText(finalText); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerNotesModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerNotesModalWindow.java new file mode 100644 index 0000000000..92007f9096 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerNotesModalWindow.java @@ -0,0 +1,69 @@ +package com.skywire.skycoin.vpn.controls; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.servers.VpnServerForList; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +public class ServerNotesModalWindow extends Dialog implements ClickEvent { + private TextView textNoteTitle; + private TextView textNote; + private TextView textPersonalNoteTitle; + private TextView textPersonalNote; + + private ModalWindowButton buttonClose; + + private VpnServerForList server; + + public ServerNotesModalWindow(Context ctx, VpnServerForList server) { + super(ctx); + + this.server = server; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_server_notes_modal); + + textNoteTitle = findViewById(R.id.textNoteTitle); + textNote = findViewById(R.id.textNote); + textPersonalNoteTitle = findViewById(R.id.textPersonalNoteTitle); + textPersonalNote = findViewById(R.id.textPersonalNote); + buttonClose = findViewById(R.id.buttonClose); + + if ((server.note != null && !server.note.trim().equals("")) && (server.personalNote != null && !server.personalNote.trim().equals(""))) { + textNote.setText(server.note); + textPersonalNote.setText(server.personalNote); + } else { + textNoteTitle.setVisibility(View.GONE); + textPersonalNoteTitle.setVisibility(View.GONE); + textPersonalNote.setVisibility(View.GONE); + + if (server.note != null && !server.note.trim().equals("")) { + textNote.setText(server.note); + } else if (server.personalNote != null && !server.personalNote.trim().equals("")) { + textNote.setText(server.personalNote); + } else { + textNote.setVisibility(View.GONE); + } + } + + buttonClose.setClickEventListener(this); + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void onClick(View view) { + dismiss(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerPasswordModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerPasswordModalWindow.java new file mode 100644 index 0000000000..b2407652bd --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/ServerPasswordModalWindow.java @@ -0,0 +1,101 @@ +package com.skywire.skycoin.vpn.controls; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.servers.VpnServerForList; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +public class ServerPasswordModalWindow extends Dialog implements ClickEvent, TextWatcher { + private EditText editPassword; + private ModalWindowButton buttonCancel; + private ModalWindowButton buttonConfirm; + + private VpnServerForList server; + + public ServerPasswordModalWindow(Context ctx, VpnServerForList server) { + super(ctx); + + this.server = server; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_server_password_modal); + + editPassword = findViewById(R.id.editPassword); + buttonCancel = findViewById(R.id.buttonCancel); + buttonConfirm = findViewById(R.id.buttonConfirm); + + editPassword.setOnEditorActionListener((v, actionId, event) -> { + if ( + actionId == EditorInfo.IME_ACTION_DONE || + (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) + ) { + if (buttonConfirm.isEnabled()) { + makeChange(); + dismiss(); + } + + return true; + } + + return false; + }); + + editPassword.addTextChangedListener(this); + + buttonCancel.setClickEventListener(this); + buttonConfirm.setClickEventListener(this); + + buttonConfirm.setEnabled(false); + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } + @Override + public void afterTextChanged(Editable s) { } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (editPassword.getText() == null || editPassword.getText().toString().equals("")) { + buttonConfirm.setEnabled(false); + } else { + buttonConfirm.setEnabled(true); + } + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.buttonConfirm) { + makeChange(); + } + + dismiss(); + } + + private void makeChange() { + LocalServerData localServerData = VPNServersPersistentData.getInstance().processFromList(server); + + localServerData.password = editPassword.getText().toString(); + VPNServersPersistentData.getInstance().updateServer(localServerData); + + HelperFunctions.showToast(getContext().getString(R.string.server_password_changes_made_confirmation), true); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/SettingsButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/SettingsButton.java new file mode 100644 index 0000000000..b3a8735400 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/SettingsButton.java @@ -0,0 +1,63 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; + +public class SettingsButton extends ButtonBase implements View.OnTouchListener { + private TextView textIcon; + + public SettingsButton(Context context) { + super(context); + } + public SettingsButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + public SettingsButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_settings_button, this, true); + + textIcon = this.findViewById (R.id.textIcon); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.SettingsButton, + 0, 0 + ); + + boolean useNoteIcon = attributes.getBoolean(R.styleable.SettingsButton_use_note_icon, false); + if (useNoteIcon) { + textIcon.setText("\ue88f"); + } + + attributes.recycle(); + } + + setOnTouchListener(this); + setViewForCheckingClicks(this); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + textIcon.setAlpha(0.5f); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP || event.getAction() == MotionEvent.ACTION_UP) { + textIcon.setAlpha(1.0f); + } + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/Tab.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/Tab.java new file mode 100644 index 0000000000..28dba6efa2 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/Tab.java @@ -0,0 +1,96 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; + +public class Tab extends ButtonBase implements View.OnTouchListener { + private LinearLayout mainContainer; + private LinearLayout internalContainer; + private FrameLayout rightBorder; + private TextView textIcon; + private TextView textName; + + private RippleDrawable rippleDrawable; + + public Tab(Context context) { + super(context); + } + public Tab(Context context, AttributeSet attrs) { + super(context, attrs); + } + public Tab(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_tab, this, true); + + mainContainer = this.findViewById (R.id.mainContainer); + internalContainer = this.findViewById (R.id.internalContainer); + rightBorder = this.findViewById (R.id.rightBorder); + textIcon = this.findViewById (R.id.textIcon); + textName = this.findViewById (R.id.textName); + + rippleDrawable = (RippleDrawable) internalContainer.getBackground(); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.Tab, + 0, 0 + ); + + String iconText = attributes.getString(R.styleable.Tab_icon_text); + if (iconText != null) { + textIcon.setText(iconText); + } + + textName.setText(attributes.getString(R.styleable.Tab_lower_text)); + + if (!attributes.getBoolean(R.styleable.Tab_show_right_border, true)) { + rightBorder.setVisibility(GONE); + } + + attributes.recycle(); + } + + setOnTouchListener(this); + setViewForCheckingClicks(this); + } + + public void changeState(boolean selected) { + if (selected) { + mainContainer.setBackgroundResource(R.color.bar_selected); + internalContainer.setBackground(null); + rippleDrawable = null; + this.setClickable(false); + } else { + mainContainer.setBackgroundResource(R.color.bar_background); + internalContainer.setBackgroundResource(R.drawable.box_ripple); + rippleDrawable = (RippleDrawable) internalContainer.getBackground(); + this.setClickable(true); + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (rippleDrawable != null) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBar.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBar.java new file mode 100644 index 0000000000..cc8d16befe --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBar.java @@ -0,0 +1,119 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.extensible.ClickWithIndexEvent; + +import java.io.Closeable; + +public class TabletTopBar extends FrameLayout implements ClickEvent, Closeable { + public TabletTopBar(Context context) { + super(context); + Initialize(context, null); + } + public TabletTopBar(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public TabletTopBar(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + public static int statusTabIndex = 0; + public static int serversTabIndex = 1; + public static int settingsTabIndex = 2; + + private TabletTopBarTab tabStatus; + private TabletTopBarTab tabServers; + private TabletTopBarTab tabSettings; + private TabletTopBarStats stats; + + private ClickWithIndexEvent clickListener; + + private void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_tablet_top_bar, this, true); + + tabStatus = this.findViewById (R.id.tabStatus); + tabServers = this.findViewById (R.id.tabServers); + tabSettings = this.findViewById (R.id.tabSettings); + stats = this.findViewById (R.id.stats); + + stats.setVisibility(INVISIBLE); + + tabStatus.setClickEventListener(this); + tabServers.setClickEventListener(this); + tabSettings.setClickEventListener(this); + } + + public void onResume() { + if (stats.getVisibility() == VISIBLE) { + stats.onResume(); + } + } + + public void onPause() { + if (stats.getVisibility() == VISIBLE) { + stats.onPause(); + } + } + + public void setSelectedTab(int tabIndex) { + tabStatus.setSelected(false); + tabServers.setSelected(false); + tabSettings.setSelected(false); + + if (tabIndex == statusTabIndex) { + tabStatus.setSelected(true); + + if (stats.getVisibility() == VISIBLE) { + stats.setVisibility(INVISIBLE); + stats.onPause(); + } + } else if (tabIndex == serversTabIndex) { + tabServers.setSelected(true); + + if (stats.getVisibility() != VISIBLE) { + stats.setVisibility(VISIBLE); + stats.onResume(); + } + } else if (tabIndex == settingsTabIndex) { + tabSettings.setSelected(true); + + if (stats.getVisibility() != VISIBLE) { + stats.setVisibility(VISIBLE); + stats.onResume(); + } + } + } + + public void setClickWithIndexEventListener(ClickWithIndexEvent listener) { + clickListener = listener; + } + + @Override + public void onClick(View view) { + if (clickListener != null) { + if (view.getId() == R.id.tabStatus) { + clickListener.onClickWithIndex(statusTabIndex, null); + } else if (view.getId() == R.id.tabServers) { + clickListener.onClickWithIndex(serversTabIndex, null); + } else if (view.getId() == R.id.tabSettings) { + clickListener.onClickWithIndex(settingsTabIndex, null); + } + } + } + + @Override + public void close() { + stats.close(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBarStats.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBarStats.java new file mode 100644 index 0000000000..1b4503e73e --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBarStats.java @@ -0,0 +1,148 @@ +package com.skywire.skycoin.vpn.controls; + +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.core.content.ContextCompat; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNStates; + +import java.io.Closeable; + +import io.reactivex.rxjava3.disposables.Disposable; + +public class TabletTopBarStats extends FrameLayout implements Animator.AnimatorListener, Closeable { + private TextView textConnectionIconAnim; + private TextView textConnectionIcon; + private TextView textConnection; + private TextView textLatency; + private TextView textUploadSpeed; + private TextView textDownloadSpeed; + + private VPNStates currentState = VPNStates.OFF; + private VPNCoordinator.ConnectionStats currentStats = new VPNCoordinator.ConnectionStats(); + private Globals.DataUnits dataUnits = VPNGeneralPersistentData.getDataUnits(); + + private AnimatorSet animSet; + + private boolean animPaused = false; + private boolean closed = false; + private Disposable eventsSubscription; + private Disposable statsSubscription; + private Disposable dataUnitsSubscription; + + public TabletTopBarStats(Context context) { + super(context); + Initialize(context, null); + } + public TabletTopBarStats(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public TabletTopBarStats(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_tablet_top_bar_stats, this, true); + + textConnectionIconAnim = this.findViewById (R.id.textConnectionIconAnim); + textConnectionIcon = this.findViewById (R.id.textConnectionIcon); + textConnection = this.findViewById (R.id.textConnection); + textLatency = this.findViewById (R.id.textLatency); + textUploadSpeed = this.findViewById (R.id.textUploadSpeed); + textDownloadSpeed = this.findViewById (R.id.textDownloadSpeed); + + animSet = (AnimatorSet) AnimatorInflater.loadAnimator(getContext(), R.animator.anim_state); + animSet.setTarget(textConnectionIconAnim); + } + + public void onResume() { + if (!closed) { + animPaused = false; + animSet.addListener(this); + animSet.start(); + + updateData(); + + eventsSubscription = VPNCoordinator.getInstance().getEventsObservable().subscribe(response -> { + currentState = response.state; + updateData(); + }); + + statsSubscription = VPNCoordinator.getInstance().getConnectionStats().subscribe(stats -> { + currentStats = stats; + updateData(); + }); + + dataUnitsSubscription = VPNGeneralPersistentData.getDataUnitsObservable().subscribe(response -> { + dataUnits = response; + updateData(); + }); + } + } + + public void onPause() { + animPaused = true; + animSet.removeAllListeners(); + animSet.cancel(); + + eventsSubscription.dispose(); + statsSubscription.dispose(); + dataUnitsSubscription.dispose(); + } + + @Override + public void onAnimationStart(Animator animation) { } + @Override + public void onAnimationCancel(Animator animation) { } + @Override + public void onAnimationRepeat(Animator animation) { } + @Override + public void onAnimationEnd(Animator animation) { + if (!closed && !animPaused) { + animSet.start(); + } + } + + private void updateData() { + int stateText = VPNStates.getTitleForState(currentState); + if (stateText != -1) { + textConnection.setText(stateText); + } else { + textConnection.setText("---"); + } + + int stateColor = ContextCompat.getColor(getContext(), VPNStates.getColorForStateTitle(stateText)); + textConnectionIconAnim.setTextColor(stateColor); + textConnection.setTextColor(stateColor); + textConnectionIcon.setTextColor(stateColor); + + textLatency.setText(HelperFunctions.getLatencyValue(currentStats.currentLatency)); + textDownloadSpeed.setText(HelperFunctions.computeDataAmountString(currentStats.currentDownloadSpeed, true, dataUnits != Globals.DataUnits.OnlyBytes)); + textUploadSpeed.setText(HelperFunctions.computeDataAmountString(currentStats.currentUploadSpeed, true, dataUnits != Globals.DataUnits.OnlyBytes)); + } + + @Override + public void close() { + closed = true; + + if (eventsSubscription != null) { + eventsSubscription.dispose(); + statsSubscription.dispose(); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBarTab.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBarTab.java new file mode 100644 index 0000000000..19ef475e1b --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TabletTopBarTab.java @@ -0,0 +1,94 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; + +public class TabletTopBarTab extends ButtonBase implements View.OnTouchListener { + private FrameLayout mainContainer; + private LinearLayout internalContainer; + private TextView textIcon; + private TextView textLabel; + + private RippleDrawable rippleDrawable; + + public TabletTopBarTab(Context context) { + super(context); + } + public TabletTopBarTab(Context context, AttributeSet attrs) { + super(context, attrs); + } + public TabletTopBarTab(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_tablet_top_bar_tab, this, true); + + mainContainer = this.findViewById (R.id.mainContainer); + internalContainer = this.findViewById (R.id.internalContainer); + textIcon = this.findViewById (R.id.textIcon); + textLabel = this.findViewById (R.id.textLabel); + + mainContainer.setClipToOutline(true); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.TabletTopBarTab, + 0, 0 + ); + + String iconText = attributes.getString(R.styleable.TabletTopBarTab_icon_text); + if (iconText != null) { + textIcon.setText(iconText); + } + + textLabel.setText(attributes.getString(R.styleable.TabletTopBarTab_label)); + + attributes.recycle(); + } + + setOnTouchListener(this); + setViewForCheckingClicks(this); + + setSelected(false); + } + + public void setSelected(boolean selected) { + if (selected) { + textIcon.setAlpha(1f); + textLabel.setAlpha(1f); + internalContainer.setBackgroundResource(R.drawable.current_server_rounded_box); + rippleDrawable = null; + setClickable(false); + } else { + textIcon.setAlpha(0.5f); + textLabel.setAlpha(0.5f); + internalContainer.setBackgroundResource(R.drawable.current_server_ripple); + rippleDrawable = (RippleDrawable) internalContainer.getBackground(); + setClickable(true); + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (rippleDrawable != null) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopBar.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopBar.java new file mode 100644 index 0000000000..4938b3e218 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopBar.java @@ -0,0 +1,94 @@ +package com.skywire.skycoin.vpn.controls; + +import android.app.Activity; +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ClickEvent; +import com.skywire.skycoin.vpn.extensible.ClickWithIndexEvent; +import com.skywire.skycoin.vpn.helpers.UiMaterialIcons; + +public class TopBar extends LinearLayout implements ClickEvent { + public TopBar(Context context) { + super(context); + Initialize(context, null); + } + public TopBar(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public TopBar(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private TopBarButton buttonLeft; + private ImageView imageIcon; + private TextView textTitle; + + private ClickWithIndexEvent clickListener; + private boolean goBack = false; + + private void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_top_bar, this, true); + + buttonLeft = this.findViewById (R.id.buttonLeft); + imageIcon = this.findViewById (R.id.imageIcon); + textTitle = this.findViewById (R.id.textTitle); + + buttonLeft.setClickEventListener(this); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.TopBar, + 0, 0); + + String title = attributes.getString(R.styleable.TopBar_title); + if (title == null || title.trim() == "") { + textTitle.setVisibility(GONE); + } else { + imageIcon.setVisibility(GONE); + textTitle.setText(title); + } + + int leftButtonIcon = attributes.getInteger(R.styleable.TopBar_left_button_icon, -1); + if (leftButtonIcon == 0) { + buttonLeft.setIcon(UiMaterialIcons.MENU); + } else if (leftButtonIcon == 1) { + buttonLeft.setIcon(UiMaterialIcons.BACK); + goBack = true; + } else { + buttonLeft.setVisibility(GONE); + } + + attributes.recycle(); + } else { + textTitle.setVisibility(GONE); + buttonLeft.setVisibility(GONE); + } + } + + public void setClickWithIndexEventListener(ClickWithIndexEvent listener) { + clickListener = listener; + } + + @Override + public void onClick(View view) { + if (clickListener != null) { + clickListener.onClickWithIndex(0, null); + } + + if (goBack) { + ((Activity)getContext()).finish(); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopBarButton.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopBarButton.java new file mode 100644 index 0000000000..2b1af828b7 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopBarButton.java @@ -0,0 +1,60 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ButtonBase; +import com.skywire.skycoin.vpn.helpers.UiMaterialIcons; + +public class TopBarButton extends ButtonBase { + public TopBarButton(Context context) { + super(context); + } + public TopBarButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + public TopBarButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + private TextView textIcon; + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_top_bar_button, this, true); + + textIcon = this.findViewById (R.id.textIcon); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.TopBarButton, + 0, 0); + + if (attributes.getInteger(R.styleable.TopBarButton_material_icon, 0) == 0) { + textIcon.setText("\ue5d2"); + } else { + textIcon.setText("\ue5c4"); + } + + attributes.recycle(); + } else { + textIcon.setText("\ue5d2"); + } + + setViewForCheckingClicks(this); + } + + public void setIcon(UiMaterialIcons icon) { + if (icon == UiMaterialIcons.MENU) { + textIcon.setText("\ue5d2"); + } else { + textIcon.setText("\ue5c4"); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopTab.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopTab.java new file mode 100644 index 0000000000..03369d5cec --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/TopTab.java @@ -0,0 +1,22 @@ +package com.skywire.skycoin.vpn.controls; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; + +public class TopTab extends FrameLayout { + private TextView text; + + public TopTab(Context context, int textResource) { + super(context); + + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_top_tab, this, true); + + text = this.findViewById (R.id.text); + text.setText(textResource); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/options/OptionsItem.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/options/OptionsItem.java new file mode 100644 index 0000000000..ff8903cecf --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/options/OptionsItem.java @@ -0,0 +1,116 @@ +package com.skywire.skycoin.vpn.controls.options; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.extensible.ListButtonBase; + +public class OptionsItem extends ListButtonBase implements View.OnTouchListener { + public static class SelectableOption { + public String icon; + public Integer drawableId; + public String label; + public int translatableLabelId = -1; + public boolean disabled = false; + } + + private LinearLayout mainContainer; + private ImageView imageBitmap; + private TextView textIcon; + private TextView text; + + private RippleDrawable rippleDrawable; + + public OptionsItem(Context context) { + super(context); + } + public OptionsItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + public OptionsItem(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void Initialize (Context context, AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.view_options_item, this, true); + + mainContainer = this.findViewById (R.id.mainContainer); + imageBitmap = this.findViewById (R.id.imageBitmap); + textIcon = this.findViewById (R.id.textIcon); + text = this.findViewById (R.id.text); + + rippleDrawable = (RippleDrawable) mainContainer.getBackground(); + + setOnTouchListener(this); + + if (attrs != null) { + TypedArray attributes = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.OptionsItem, + 0, 0 + ); + + String iconText = attributes.getString(R.styleable.OptionsItem_icon_text); + if (iconText != null) { + textIcon.setText(iconText); + } + + text.setText(attributes.getString(R.styleable.OptionsItem_text)); + + attributes.recycle(); + } + + setViewForCheckingClicks(this); + } + + public void setParams(SelectableOption params) { + if (params.icon != null) { + textIcon.setText(params.icon); + textIcon.setVisibility(VISIBLE); + imageBitmap.setVisibility(GONE); + } else { + textIcon.setVisibility(GONE); + + if (params.drawableId != null) { + imageBitmap.setImageResource(params.drawableId); + imageBitmap.setVisibility(VISIBLE); + } else { + imageBitmap.setVisibility(GONE); + } + } + + if (params.translatableLabelId != -1) { + text.setText(params.translatableLabelId); + } else if (params.label != null) { + text.setText(params.label); + } + + if (params.disabled) { + this.setAlpha(0.5f); + this.setClickable(false); + } else { + this.setAlpha(1f); + this.setClickable(true); + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (rippleDrawable != null) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + + return false; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/options/OptionsModalWindow.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/options/OptionsModalWindow.java new file mode 100644 index 0000000000..0f7075fba6 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/controls/options/OptionsModalWindow.java @@ -0,0 +1,69 @@ +package com.skywire.skycoin.vpn.controls.options; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.Window; +import android.widget.LinearLayout; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.controls.ModalBase; +import com.skywire.skycoin.vpn.extensible.ClickWithIndexEvent; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +import java.util.ArrayList; + +public class OptionsModalWindow extends Dialog implements ClickWithIndexEvent { + public interface OptionSelected { + void optionSelected(int selectedIndex); + } + + private String title; + private ModalBase modalBase; + private LinearLayout container; + + private ArrayList options; + private OptionSelected event; + + public OptionsModalWindow(Context ctx, String title, ArrayList options, OptionSelected event) { + super(ctx); + + this.title = title; + this.options = options; + this.event = event; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.view_options); + + modalBase = findViewById(R.id.modalBase); + container = findViewById(R.id.container); + + if (title != null) { + modalBase.setTitleString(title); + } + + int i = 0; + for (OptionsItem.SelectableOption option : options) { + OptionsItem view = new OptionsItem(getContext()); + view.setParams(option); + view.setIndex(i++); + view.setClickWithIndexEventListener(this); + container.addView(view); + } + + HelperFunctions.configureModalWindow(this); + } + + @Override + public void onClickWithIndex(int index, Void data) { + if (event != null) { + event.optionSelected(index); + } + + dismiss(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ButtonBase.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ButtonBase.java new file mode 100644 index 0000000000..03c82af9db --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ButtonBase.java @@ -0,0 +1,71 @@ +package com.skywire.skycoin.vpn.extensible; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.RelativeLayout; + +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.helpers.ClickTimeManagement; +import com.skywire.skycoin.vpn.helpers.Globals; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public abstract class ButtonBase extends RelativeLayout implements View.OnClickListener { + public ButtonBase(Context context) { + super(context); + Initialize(context, null); + } + public ButtonBase(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public ButtonBase(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + private ClickEvent clickListener; + private ClickTimeManagement buttonTimeManager = new ClickTimeManagement(); + + abstract protected void Initialize (Context context, AttributeSet attrs); + + protected void setViewForCheckingClicks(View v) { + v.setOnClickListener(this); + } + + protected void setClickableBoxView(BoxRowLayout v) { + v.setClickEventListener(view -> { + if (clickListener != null) { + clickListener.onClick(this); + } + }); + } + + public void setUseBigFastClickPrevention(boolean useBigFastClickPrevention) { + if (useBigFastClickPrevention) { + buttonTimeManager.setDelay(ClickTimeManagement.normalFastClickPreventionDelay); + } else { + buttonTimeManager.setDelay(Globals.CLICK_DELAY_MS); + } + } + + public void setClickEventListener(ClickEvent listener) { + clickListener = listener; + } + + @Override + public void onClick(View view) { + if (clickListener != null && buttonTimeManager.canClick()) { + buttonTimeManager.informClickMade(); + Observable.just(1).delay(Globals.CLICK_DELAY_MS, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(v -> clickListener.onClick(this)); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ClickEvent.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ClickEvent.java new file mode 100644 index 0000000000..2de332ff98 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ClickEvent.java @@ -0,0 +1,7 @@ +package com.skywire.skycoin.vpn.extensible; + +import android.view.View; + +public interface ClickEvent { + void onClick(View view); +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ClickWithIndexEvent.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ClickWithIndexEvent.java new file mode 100644 index 0000000000..bbac776754 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ClickWithIndexEvent.java @@ -0,0 +1,5 @@ +package com.skywire.skycoin.vpn.extensible; + +public interface ClickWithIndexEvent { + void onClickWithIndex(int index, T data); +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ListButtonBase.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ListButtonBase.java new file mode 100644 index 0000000000..2bcfac3593 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ListButtonBase.java @@ -0,0 +1,81 @@ +package com.skywire.skycoin.vpn.extensible; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.RelativeLayout; + +import com.skywire.skycoin.vpn.controls.BoxRowLayout; +import com.skywire.skycoin.vpn.helpers.ClickTimeManagement; +import com.skywire.skycoin.vpn.helpers.Globals; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public abstract class ListButtonBase extends RelativeLayout implements View.OnClickListener { + public ListButtonBase(Context context) { + super(context); + Initialize(context, null); + } + public ListButtonBase(Context context, AttributeSet attrs) { + super(context, attrs); + Initialize(context, attrs); + } + public ListButtonBase(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + Initialize(context, attrs); + } + + protected DataType dataForEvent; + private int index; + private ClickWithIndexEvent clickListener; + private ClickTimeManagement buttonTimeManager = new ClickTimeManagement(); + + abstract protected void Initialize (Context context, AttributeSet attrs); + + protected void setViewForCheckingClicks(View v) { + v.setOnClickListener(this); + } + + protected void setClickableBoxView(BoxRowLayout v) { + v.setClickEventListener(view -> { + if (clickListener != null) { + clickListener.onClickWithIndex(index, dataForEvent); + } + }); + } + + public void setUseBigFastClickPrevention(boolean useBigFastClickPrevention) { + if (useBigFastClickPrevention) { + buttonTimeManager.setDelay(ClickTimeManagement.normalFastClickPreventionDelay); + } else { + buttonTimeManager.setDelay(Globals.CLICK_DELAY_MS); + } + } + + public void setIndex(int index) { + this.index = index; + } + + public int getIndex() { + return index; + } + + public void setClickWithIndexEventListener(ClickWithIndexEvent listener) { + clickListener = listener; + } + + @Override + public void onClick(View view) { + if (clickListener != null && buttonTimeManager.canClick()) { + buttonTimeManager.informClickMade(); + Observable.just(1).delay(Globals.CLICK_DELAY_MS, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(v -> clickListener.onClickWithIndex(index, dataForEvent)); + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ListViewHolder.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ListViewHolder.java new file mode 100644 index 0000000000..c3fe237e0a --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/extensible/ListViewHolder.java @@ -0,0 +1,11 @@ +package com.skywire.skycoin.vpn.extensible; + +import android.view.View; + +import androidx.recyclerview.widget.RecyclerView; + +public class ListViewHolder extends RecyclerView.ViewHolder { + public ListViewHolder(T v) { + super(v); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/AlphaSpan.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/AlphaSpan.java new file mode 100644 index 0000000000..9839df1fa6 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/AlphaSpan.java @@ -0,0 +1,24 @@ +package com.skywire.skycoin.vpn.helpers; + +import android.text.TextPaint; +import android.text.style.TypefaceSpan; + +public class AlphaSpan extends TypefaceSpan { + private int alpha; + + public AlphaSpan(int alpha) { + super(""); + + this.alpha = alpha; + } + + @Override + public void updateDrawState(TextPaint paint) { + paint.setAlpha(alpha); + } + + @Override + public void updateMeasureState(TextPaint paint) { + paint.setAlpha(alpha); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/BoxRowTypes.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/BoxRowTypes.java new file mode 100644 index 0000000000..b4b9339fad --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/BoxRowTypes.java @@ -0,0 +1,8 @@ +package com.skywire.skycoin.vpn.helpers; + +public enum BoxRowTypes { + TOP, + MIDDLE, + BOTTOM, + SINGLE, +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/ClickTimeManagement.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/ClickTimeManagement.java new file mode 100644 index 0000000000..a8ffd434ec --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/ClickTimeManagement.java @@ -0,0 +1,40 @@ +package com.skywire.skycoin.vpn.helpers; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public class ClickTimeManagement { + public static final int normalFastClickPreventionDelay = 700; + + private Disposable timeSubscription; + private int delay = normalFastClickPreventionDelay; + + public void setDelay(int delay) { + this.delay = delay; + } + + public void informClickMade() { + removeDelay(); + + timeSubscription = Observable.just(1).delay(delay, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(v -> timeSubscription = null); + } + + public boolean canClick() { + return timeSubscription == null; + } + + public void removeDelay() { + if (timeSubscription != null) { + timeSubscription.dispose(); + } + + timeSubscription = null; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/CountriesList.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/CountriesList.java new file mode 100644 index 0000000000..285b53ad4e --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/CountriesList.java @@ -0,0 +1,267 @@ +package com.skywire.skycoin.vpn.helpers; + +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.R; + +import java.util.HashMap; + +public class CountriesList { + private static HashMap countries = new HashMap() {{ + put("AF", "Afghanistan"); + put("AX", "Aland Islands"); + put("AL", "Albania"); + put("DZ", "Algeria"); + put("AS", "American Samoa"); + put("AD", "Andorra"); + put("AO", "Angola"); + put("AI", "Anguilla"); + put("AQ", "Antarctica"); + put("AG", "Antigua and Barbuda"); + put("AR", "Argentina"); + put("AM", "Armenia"); + put("AW", "Aruba"); + put("AU", "Australia"); + put("AT", "Austria"); + put("AZ", "Azerbaijan"); + put("BS", "Bahamas"); + put("BH", "Bahrain"); + put("BD", "Bangladesh"); + put("BB", "Barbados"); + put("BY", "Belarus"); + put("BE", "Belgium"); + put("BZ", "Belize"); + put("BJ", "Benin"); + put("BM", "Bermuda"); + put("BT", "Bhutan"); + put("BO", "Bolivia"); + put("BA", "Bosnia and Herzegovina"); + put("BW", "Botswana"); + put("BV", "Bouvet Island"); + put("BR", "Brazil"); + put("IO", "British Indian Ocean Territory"); + put("BN", "Brunei Darussalam"); + put("BG", "Bulgaria"); + put("BF", "Burkina Faso"); + put("BI", "Burundi"); + put("KH", "Cambodia"); + put("CM", "Cameroon"); + put("CA", "Canada"); + put("CV", "Cape Verde"); + put("KY", "Cayman Islands"); + put("CF", "Central African Republic"); + put("TD", "Chad"); + put("CL", "Chile"); + put("CN", "China"); + put("CX", "Christmas Island"); + put("CC", "Cocos (Keeling) Islands"); + put("CO", "Colombia"); + put("KM", "Comoros"); + put("CG", "Congo"); + put("CD", "Congo, Democratic Republic"); + put("CK", "Cook Islands"); + put("CR", "Costa Rica"); + put("CI", "Cote D'Ivoire"); + put("HR", "Croatia"); + put("CU", "Cuba"); + put("CY", "Cyprus"); + put("CZ", "Czech Republic"); + put("DK", "Denmark"); + put("DJ", "Djibouti"); + put("DM", "Dominica"); + put("DO", "Dominican Republic"); + put("EC", "Ecuador"); + put("EG", "Egypt"); + put("SV", "El Salvador"); + put("GQ", "Equatorial Guinea"); + put("ER", "Eritrea"); + put("EE", "Estonia"); + put("ET", "Ethiopia"); + put("FK", "Falkland Islands (Malvinas)"); + put("FO", "Faroe Islands"); + put("FJ", "Fiji"); + put("FI", "Finland"); + put("FR", "France"); + put("GF", "French Guiana"); + put("PF", "French Polynesia"); + put("TF", "French Southern Territories"); + put("GA", "Gabon"); + put("GM", "Gambia"); + put("GE", "Georgia"); + put("DE", "Germany"); + put("GH", "Ghana"); + put("GI", "Gibraltar"); + put("GR", "Greece"); + put("GL", "Greenland"); + put("GD", "Grenada"); + put("GP", "Guadeloupe"); + put("GU", "Guam"); + put("GT", "Guatemala"); + put("GG", "Guernsey"); + put("GN", "Guinea"); + put("GW", "Guinea-Bissau"); + put("GY", "Guyana"); + put("HT", "Haiti"); + put("HM", "Heard Island and Mcdonald Islands"); + put("VA", "Holy See (Vatican City State)"); + put("HN", "Honduras"); + put("HK", "Hong Kong"); + put("HU", "Hungary"); + put("IS", "Iceland"); + put("IN", "India"); + put("ID", "Indonesia"); + put("IR", "Iran"); + put("IQ", "Iraq"); + put("IE", "Ireland"); + put("IM", "Isle of Man"); + put("IL", "Israel"); + put("IT", "Italy"); + put("JM", "Jamaica"); + put("JP", "Japan"); + put("JE", "Jersey"); + put("JO", "Jordan"); + put("KZ", "Kazakhstan"); + put("KE", "Kenya"); + put("KI", "Kiribati"); + put("KP", "Korea (North)"); + put("KR", "Korea (South)"); + put("XK", "Kosovo"); + put("KW", "Kuwait"); + put("KG", "Kyrgyzstan"); + put("LA", "Laos"); + put("LV", "Latvia"); + put("LB", "Lebanon"); + put("LS", "Lesotho"); + put("LR", "Liberia"); + put("LY", "Libyan Arab Jamahiriya"); + put("LI", "Liechtenstein"); + put("LT", "Lithuania"); + put("LU", "Luxembourg"); + put("MO", "Macao"); + put("MK", "Macedonia"); + put("MG", "Madagascar"); + put("MW", "Malawi"); + put("MY", "Malaysia"); + put("MV", "Maldives"); + put("ML", "Mali"); + put("MT", "Malta"); + put("MH", "Marshall Islands"); + put("MQ", "Martinique"); + put("MR", "Mauritania"); + put("MU", "Mauritius"); + put("YT", "Mayotte"); + put("MX", "Mexico"); + put("FM", "Micronesia"); + put("MD", "Moldova"); + put("MC", "Monaco"); + put("MN", "Mongolia"); + put("MS", "Montserrat"); + put("MA", "Morocco"); + put("MZ", "Mozambique"); + put("MM", "Myanmar"); + put("NA", "Namibia"); + put("NR", "Nauru"); + put("NP", "Nepal"); + put("NL", "Netherlands"); + put("AN", "Netherlands Antilles"); + put("NC", "New Caledonia"); + put("NZ", "New Zealand"); + put("NI", "Nicaragua"); + put("NE", "Niger"); + put("NG", "Nigeria"); + put("NU", "Niue"); + put("NF", "Norfolk Island"); + put("MP", "Northern Mariana Islands"); + put("NO", "Norway"); + put("OM", "Oman"); + put("PK", "Pakistan"); + put("PW", "Palau"); + put("PS", "Palestinian Territory, Occupied"); + put("PA", "Panama"); + put("PG", "Papua New Guinea"); + put("PY", "Paraguay"); + put("PE", "Peru"); + put("PH", "Philippines"); + put("PN", "Pitcairn"); + put("PL", "Poland"); + put("PT", "Portugal"); + put("PR", "Puerto Rico"); + put("QA", "Qatar"); + put("RE", "Reunion"); + put("RO", "Romania"); + put("RU", "Russian Federation"); + put("RW", "Rwanda"); + put("SH", "Saint Helena"); + put("KN", "Saint Kitts and Nevis"); + put("LC", "Saint Lucia"); + put("PM", "Saint Pierre and Miquelon"); + put("VC", "Saint Vincent and the Grenadines"); + put("WS", "Samoa"); + put("SM", "San Marino"); + put("ST", "Sao Tome and Principe"); + put("SA", "Saudi Arabia"); + put("SN", "Senegal"); + put("RS", "Serbia"); + put("ME", "Montenegro"); + put("SC", "Seychelles"); + put("SL", "Sierra Leone"); + put("SG", "Singapore"); + put("SK", "Slovakia"); + put("SI", "Slovenia"); + put("SB", "Solomon Islands"); + put("SO", "Somalia"); + put("ZA", "South Africa"); + put("GS", "South Georgia and the South Sandwich Islands"); + put("ES", "Spain"); + put("LK", "Sri Lanka"); + put("SD", "Sudan"); + put("SR", "Suriname"); + put("SJ", "Svalbard and Jan Mayen"); + put("SZ", "Swaziland"); + put("SE", "Sweden"); + put("CH", "Switzerland"); + put("SY", "Syrian Arab Republic"); + put("TW", "Taiwan, Province of China"); + put("TJ", "Tajikistan"); + put("TZ", "Tanzania"); + put("TH", "Thailand"); + put("TL", "Timor-Leste"); + put("TG", "Togo"); + put("TK", "Tokelau"); + put("TO", "Tonga"); + put("TT", "Trinidad and Tobago"); + put("TN", "Tunisia"); + put("TR", "Turkey"); + put("TM", "Turkmenistan"); + put("TC", "Turks and Caicos Islands"); + put("TV", "Tuvalu"); + put("UG", "Uganda"); + put("UA", "Ukraine"); + put("AE", "United Arab Emirates"); + put("GB", "United Kingdom"); + put("US", "United States"); + put("UM", "United States Minor Outlying Islands"); + put("UY", "Uruguay"); + put("UZ", "Uzbekistan"); + put("VU", "Vanuatu"); + put("VE", "Venezuela"); + put("VN", "Viet Nam"); + put("VG", "Virgin Islands, British"); + put("VI", "Virgin Islands, U.S."); + put("WF", "Wallis and Futuna"); + put("EH", "Western Sahara"); + put("YE", "Yemen"); + put("ZM", "Zambia"); + put("ZW", "Zimbabwe"); + put("ZZ", "Unknown"); + }}; + + public static String getCountryName(String cuntryCode) { + cuntryCode = cuntryCode.toUpperCase(); + + if (!cuntryCode.equals("ZZ") && countries.containsKey(cuntryCode)) { + return countries.get(cuntryCode); + } + + return App.getContext().getText(R.string.general_unknown).toString(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/Globals.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/Globals.java new file mode 100644 index 0000000000..d1942445a6 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/Globals.java @@ -0,0 +1,70 @@ +package com.skywire.skycoin.vpn.helpers; + +import androidx.annotation.NonNull; + +/** + * Constant values used in various parts of the app. + */ +public class Globals { + /** + * Time to wait before sending a click event after the user clicks a button. This is for + * allowing the UI to show the click effect. + */ + public static final int CLICK_DELAY_MS = 150; + /** + * Address of the local Skywire node. + */ + public static final String LOCAL_VISOR_ADDRESS = "localhost"; + /** + * Port of the local Skywire node. + */ + public static final int LOCAL_VISOR_PORT = 7890; + + /** + * Addresses used for checking if the device has internet connectivity. Any number of + * addresses, but at least 1, can be used. Addresses will be checked sequentially and only + * until being able to connect with one. + */ + public static final String[] INTERNET_CHECKING_ADDRESSES = new String[]{"https://dmsg.discovery.skywire.skycoin.com", "https://www.skycoin.com"}; + + /** + * Options for how to show the VPN data transmission stats. + */ + public enum DataUnits { + BitsSpeedAndBytesVolume, + OnlyBytes, + OnlyBits, + } + + /** + * List with all the possible app selection modes. Each option has an associated string value. + */ + public enum AppFilteringModes { + /** + * All apps must be protected by the VPN service, no matter which apps have been selected + * by the user. + */ + PROTECT_ALL("PROTECT_ALL"), + /** + * Only the apps selected by the user must be protected by the VPN service. + */ + PROTECT_SELECTED("PROTECT_SELECTED"), + /** + * Apps selected by the user must NOT be protected by the VPN service. All other apps + * must be protected. + */ + IGNORE_SELECTED("IGNORE_SELECTED"); + + private final String val; + + AppFilteringModes(final String val) { + this.val = val; + } + + @NonNull + @Override + public String toString() { + return val; + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/HelperFunctions.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/HelperFunctions.java new file mode 100644 index 0000000000..21e3bcb783 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/HelperFunctions.java @@ -0,0 +1,662 @@ +package com.skywire.skycoin.vpn.helpers; + +import android.app.Activity; +import android.app.Dialog; +import android.app.PendingIntent; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Handler; +import android.os.Looper; +import android.util.TypedValue; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Toast; + +import androidx.core.content.ContextCompat; + +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.activities.main.MainActivity; +import com.skywire.skycoin.vpn.activities.servers.ServerLists; +import com.skywire.skycoin.vpn.activities.servers.VpnServerForList; +import com.skywire.skycoin.vpn.controls.ConfirmationModalWindow; +import com.skywire.skycoin.vpn.controls.EditServerValueModalWindow; +import com.skywire.skycoin.vpn.controls.ServerInfoModalWindow; +import com.skywire.skycoin.vpn.controls.ServerPasswordModalWindow; +import com.skywire.skycoin.vpn.controls.options.OptionsItem; +import com.skywire.skycoin.vpn.controls.options.OptionsModalWindow; +import com.skywire.skycoin.vpn.network.ApiClient; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.objects.ServerFlags; +import com.skywire.skycoin.vpn.vpn.VPNCoordinator; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNServersPersistentData; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import io.reactivex.rxjava3.core.Observable; +import skywiremob.Skywiremob; + +/** + * General helper functions for different parts of the app. + */ +public class HelperFunctions { + public enum WidthTypes { + SMALL, + BIG, + BIGGER, + } + + // Helpers for showing only a max number of decimals. + public static final DecimalFormat twoDecimalsFormatter = new DecimalFormat("#.##"); + public static final DecimalFormat oneDecimalsFormatter = new DecimalFormat("#.#"); + public static final DecimalFormat zeroDecimalsFormatter = new DecimalFormat("#"); + + // Last toast notification shown. + private static Toast lastToast; + + /** + * Displays debug information about an error in the console. It includes the several details. + * @param prefix Text to show before the error details. + * @param e Error. + */ + public static void logError(String prefix, Throwable e) { + // Print the basic error msgs. + StringBuilder errorMsg = new StringBuilder(prefix + ": " + e.getMessage() + "\n"); + errorMsg.append(e.toString()).append("\n"); + + // Print the stack. + StackTraceElement[] stackTrace = e.getStackTrace(); + for (StackTraceElement stackTraceElement : stackTrace) { + errorMsg.append(stackTraceElement.toString()).append("\n"); + } + + // Display in the console. + Skywiremob.printString(errorMsg.toString()); + } + + /** + * Displays an error msg in the console. + * @param prefix Text to show before the error msg. + * @param errorText Error msg. + */ + public static void logError(String prefix, String errorText) { + String errorMsg = prefix + ": " + errorText; + Skywiremob.printString(errorMsg); + } + + /** + * Shows a toast notification. Can be used from background threads. + * @param text Text for the notification. + * @param shortDuration If the duration of the notification must be short (true) or + * long (false). + */ + public static void showToast(String text, boolean shortDuration) { + // Run in the UI thread. + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(() -> { + // Close the previous notification. + if (lastToast != null) { + lastToast.cancel(); + } + + // Show the notification. + lastToast = Toast.makeText(App.getContext(), text, shortDuration ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG); + lastToast.show(); + }); + } + + /** + * Gets the list of the app launchers installed in the device. More than one entry may share + * the same package name. The current app is ignored. + */ + public static List getDeviceAppsList() { + Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + + String packageName = App.getContext().getPackageName(); + ArrayList response = new ArrayList<>(); + + // Get all the entries in the device which coincide with the intent. + for (ResolveInfo app : App.getContext().getPackageManager().queryIntentActivities( mainIntent, 0)) { + if (!app.activityInfo.packageName.equals(packageName)) { + response.add(app); + } + } + + return response; + } + + /** + * Filters a list of package names and returns only the ones which are from launchers + * currently installed in the device. The current app is ignored. + * @param apps List to filter. + * @return Filtered list. + */ + public static HashSet filterAvailableApps(HashSet apps) { + HashSet availableApps = new HashSet<>(); + for (ResolveInfo app : getDeviceAppsList()) { + availableApps.add(app.activityInfo.packageName); + } + + HashSet response = new HashSet<>(); + for (String app : apps) { + if (availableApps.contains(app)) { + response.add(app); + } + } + + return response; + } + + /** + * Closes the provided activity if the VPN service is running. If the activity is closed, + * a toast is shown. + * @param activity Activity to close. + * @return True if the activity was closed, false if not. + */ + public static boolean closeActivityIfServiceRunning(Activity activity) { + if (VPNCoordinator.getInstance().isServiceRunning()) { + HelperFunctions.showToast(App.getContext().getString(R.string.vpn_already_running_warning), true); + activity.finish(); + + return true; + } + + return false; + } + + /** + * Checks if there is connection via internet to at least one of the testing URLs set in the + * globals class. + * @param logError If true and there is an error checking the connection, the error will + * be logged. + * @return Observable which emits if there is connection or not. + */ + public static Observable checkInternetConnectivity(boolean logError) { + return checkInternetConnectivity(0, logError); + } + + /** + * Internal function for checking if there is internet connectivity, recursively. + * @param urlIndex Index of the testing URL to check. + * @param logError If the error, if any, must be logged at the end of the operation. + */ + private static Observable checkInternetConnectivity(int urlIndex, boolean logError) { + return ApiClient.checkConnection(Globals.INTERNET_CHECKING_ADDRESSES[urlIndex]) + // If there is a valid response, return true. + .map(response -> true) + .onErrorResumeNext(err -> { + // If there is an error and there are more testing URLs, continue to the next step. + if (urlIndex < Globals.INTERNET_CHECKING_ADDRESSES.length - 1) { + return checkInternetConnectivity(urlIndex + 1, logError); + } + + if (logError) { + HelperFunctions.logError("Checking network connectivity", err); + } + + return Observable.just(false); + }); + } + + /** + * Returns an intent for opening the app. + */ + public static PendingIntent getOpenAppPendingIntent() { + final Intent openAppIntent = new Intent(App.getContext(), MainActivity.class); + openAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + openAppIntent.setAction(Intent.ACTION_MAIN); + openAppIntent.addCategory(Intent.CATEGORY_LAUNCHER); + + return PendingIntent.getActivity(App.getContext(), 0, openAppIntent, PendingIntent.FLAG_UPDATE_CURRENT); + } + + /** + * Allows to convert a bytes value to KB, MB, GB, etc. It considers 1024, and not 1000, a K. + * @param bytes Amount of data to process, in bytes. + * @param calculatePerSecond If true, the result will have "/s" added at the end. + * @param useBits If the data must be shown in bits (true) or bytes (false). + */ + public static String computeDataAmountString(double bytes, boolean calculatePerSecond, boolean useBits) { + double current = (double)bytes; + + // Set the correct units. + String[] scales; + if (calculatePerSecond) { + if (useBits) { + scales = new String[]{" b/s", " Kb/s", " Mb/s", " Gb/s", " Tb/s", "Pb/s", "Eb/s", "Zb/s", "Yb/s"}; + } else { + scales = new String[]{" B/s", " KB/s", " MB/s", " GB/s", " TB/s", "PB/s", "EB/s", "ZB/s", "YB/s"}; + } + } else { + if (useBits) { + scales = new String[]{" b", " Kb", " Mb", " Gb", " Tb", "Pb", "Eb", "Zb", "Yb"}; + } else { + scales = new String[]{" B", " KB", " MB", " GB", " TB", "PB", "EB", "ZB", "YB"}; + } + } + + // Convert to bits, if needed. + if (useBits) { + current *= 8; + } + + // Divide the speed by 1024 until getting an appropriate scale to return. + for (int i = 0; i < scales.length - 1; i++) { + if (current < 1024) { + // Return decimals depending on how long the number is. + if (current < 10) { + return twoDecimalsFormatter.format(current) + scales[i]; + } else if (current < 100) { + return oneDecimalsFormatter.format(current) + scales[i]; + } + + return zeroDecimalsFormatter.format(current) + scales[i]; + } + + current /= 1024; + } + + return current + scales[scales.length - 1]; + } + + public static String getLatencyValue(double latency) { + String initialPart; + String lastPart; + + if (latency >= 1000) { + initialPart = oneDecimalsFormatter.format(latency / 1000); + lastPart = App.getContext().getString(R.string.general_seconds_abbreviation); + } else { + initialPart = oneDecimalsFormatter.format(latency); + lastPart = App.getContext().getString(R.string.general_milliseconds_abbreviation); + } + + return initialPart + lastPart; + } + + public static int getFlagResourceId(String countryCode) { + if (countryCode.toLowerCase() != "do") { + int flagResourceId = App.getContext().getResources().getIdentifier( + countryCode.toLowerCase(), + "drawable", + App.getContext().getPackageName() + ); + + if (flagResourceId != 0) { + return flagResourceId; + } else { + return R.drawable.zz; + } + } else { + return R.drawable.do_flag; + } + } + + // TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. + /* + public static int getCongestionNumberColor(int congestion) { + if (congestion < 60) { + return ContextCompat.getColor(App.getContext(), R.color.green); + } else if (congestion < 90) { + return ContextCompat.getColor(App.getContext(), R.color.yellow); + } + + return ContextCompat.getColor(App.getContext(), R.color.red); + } + + public static int getLatencyNumberColor(int latency) { + if (latency < 200) { + return ContextCompat.getColor(App.getContext(), R.color.green); + } else if (latency < 350) { + return ContextCompat.getColor(App.getContext(), R.color.yellow); + } + + return ContextCompat.getColor(App.getContext(), R.color.red); + } + + public static int getHopsNumberColor(int hops) { + if (hops < 5) { + return ContextCompat.getColor(App.getContext(), R.color.green); + } else if (hops < 9) { + return ContextCompat.getColor(App.getContext(), R.color.yellow); + } + + return ContextCompat.getColor(App.getContext(), R.color.red); + } + */ + public static void configureModalWindow(Dialog modal) { + Window window = modal.getWindow(); + window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + WidthTypes screenWidthType = getWidthType(modal.getContext()); + if (screenWidthType != WidthTypes.SMALL) { + int width = (int)TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 500, + modal.getContext().getResources().getDisplayMetrics() + ); + + WindowManager.LayoutParams params = window.getAttributes(); + params.width = width; + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + window.setAttributes(params); + } + } + + public static boolean showBackgroundForVerticalScreen() { + double proportion = (double)Resources.getSystem().getDisplayMetrics().widthPixels / (double)Resources.getSystem().getDisplayMetrics().heightPixels; + if (proportion > 1.1) { + return false; + } + + return true; + } + + public static WidthTypes getWidthType(Context ctx) { + int screenWidthInDP = (int)(Resources.getSystem().getDisplayMetrics().widthPixels / ctx.getResources().getDisplayMetrics().density); + + if (screenWidthInDP >= 1100) { + return WidthTypes.BIGGER; + } else if (screenWidthInDP >= 800) { + return WidthTypes.BIG; + } + + return WidthTypes.SMALL; + } + + public static int getTabletExtraHorizontalPadding(Context ctx) { + WidthTypes widthType = getWidthType(ctx); + + if (widthType == WidthTypes.BIGGER) { + return (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 100, + ctx.getResources().getDisplayMetrics() + ); + } else if (widthType == WidthTypes.BIG) { + return (int)TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 40, + ctx.getResources().getDisplayMetrics() + ); + } + + return 0; + } + + public static boolean prepareAndStartVpn(Activity requestingActivity, LocalServerData server) { + if (server.flag == ServerFlags.Blocked) { + HelperFunctions.showToast(requestingActivity.getString(R.string.general_starting_blocked_server_error) + server.pk, false); + + return false; + } + + long err = Skywiremob.isPKValid(server.pk).getCode(); + if (err != Skywiremob.ErrCodeNoError) { + HelperFunctions.showToast(requestingActivity.getString(R.string.vpn_coordinator_invalid_credentials_error) + server.pk, false); + return false; + } else { + Skywiremob.printString("PK is correct"); + } + + Globals.AppFilteringModes selectedMode = VPNGeneralPersistentData.getAppsSelectionMode(); + if (selectedMode != Globals.AppFilteringModes.PROTECT_ALL) { + HashSet selectedApps = HelperFunctions.filterAvailableApps(VPNGeneralPersistentData.getAppList(new HashSet<>())); + + if (selectedApps.size() == 0) { + if (selectedMode == Globals.AppFilteringModes.PROTECT_SELECTED) { + HelperFunctions.showToast(requestingActivity.getString(R.string.vpn_no_apps_to_protect_warning), false); + } else { + HelperFunctions.showToast(requestingActivity.getString(R.string.vpn_no_apps_to_ignore_warning), false); + } + } + } + + VPNCoordinator.getInstance().startVPN( + requestingActivity, + server + ); + + return true; + } + + public static String getServerName(VpnServerForList server, String defaultName) { + if ((server.name == null || server.name.trim().equals("")) && (server.customName == null || server.customName.trim().equals(""))) { + return defaultName; + } else if (server.name != null && !server.name.trim().equals("") && (server.customName == null || server.customName.trim().equals(""))) { + return server.name; + } else if (server.customName != null && !server.customName.trim().equals("") && (server.name == null || server.name.trim().equals(""))) { + return server.customName; + } + + return server.customName + " - " + server.name; + } + + public static String getServerNote(LocalServerData server) { + String note = ""; + if (server.note != null && !server.note.trim().equals("")) { + note = server.note; + } + if (server.personalNote != null && !server.personalNote.trim().equals("")) { + if (note.length() > 0) { + note += " - "; + } + note += server.personalNote; + } + + return note.length() > 0 ? note : null; + } + + public static void showServerOptions(Context ctx, VpnServerForList server, ServerLists listType) { + ArrayList options = new ArrayList(); + ArrayList optionCodes = new ArrayList(); + + OptionsItem.SelectableOption option = new OptionsItem.SelectableOption(); + option.icon = "\ue88e"; + option.translatableLabelId = R.string.tmp_server_options_view_info; + options.add(option); + optionCodes.add(10); + option = new OptionsItem.SelectableOption(); + option.icon = "\ue14d"; + option.translatableLabelId = R.string.tmp_server_options_copy_pk; + options.add(option); + optionCodes.add(11); + option = new OptionsItem.SelectableOption(); + option.icon = "\ue3c9"; + option.translatableLabelId = R.string.tmp_server_options_name; + options.add(option); + optionCodes.add(101); + option = new OptionsItem.SelectableOption(); + option.icon = "\ue8d2"; + option.translatableLabelId = R.string.tmp_server_options_note; + options.add(option); + optionCodes.add(102); + + if (server.hasPassword) { + option = new OptionsItem.SelectableOption(); + option.icon = "\ue898"; + option.translatableLabelId = R.string.tmp_server_options_remove_password; + options.add(option); + optionCodes.add(201); + + option = new OptionsItem.SelectableOption(); + option.icon = "\ue899"; + option.translatableLabelId = R.string.tmp_server_options_change_password; + options.add(option); + optionCodes.add(202); + } else { + if (server.enteredManually) { + option = new OptionsItem.SelectableOption(); + option.icon = "\ue899"; + option.translatableLabelId = R.string.tmp_server_options_add_password; + options.add(option); + optionCodes.add(202); + } + } + + if (server.flag != ServerFlags.Favorite) { + option = new OptionsItem.SelectableOption(); + option.icon = "\ue838"; + option.translatableLabelId = R.string.tmp_server_options_make_favorite; + options.add(option); + optionCodes.add(1); + } + + if (server.flag == ServerFlags.Favorite) { + option = new OptionsItem.SelectableOption(); + option.icon = "\ue83a"; + option.translatableLabelId = R.string.tmp_server_options_remove_from_favorites; + options.add(option); + optionCodes.add(-1); + } + + if (server.flag != ServerFlags.Blocked) { + option = new OptionsItem.SelectableOption(); + option.icon = "\ue925"; + option.translatableLabelId = R.string.tmp_server_options_block; + options.add(option); + optionCodes.add(2); + } + + if (server.flag == ServerFlags.Blocked) { + option = new OptionsItem.SelectableOption(); + option.icon = "\ue8dc"; + option.translatableLabelId = R.string.tmp_server_options_unblock; + options.add(option); + optionCodes.add(-2); + } + + if (server.inHistory) { + option = new OptionsItem.SelectableOption(); + option.icon = "\ue872"; + option.translatableLabelId = R.string.tmp_server_options_remove_from_history; + options.add(option); + optionCodes.add(-3); + } + + OptionsModalWindow modal = new OptionsModalWindow(ctx, null, options, (int selectedOption) -> { + LocalServerData savedVersion_ = VPNServersPersistentData.getInstance().getSavedVersion(server.pk); + if (savedVersion_ == null) { + savedVersion_ = VPNServersPersistentData.getInstance().processFromList(server); + } + + final LocalServerData savedVersion = savedVersion_; + + if (optionCodes.get(selectedOption) > 200) { + if (VPNCoordinator.getInstance().isServiceRunning() && VPNServersPersistentData.getInstance().getCurrentServer().pk.equals(savedVersion.pk)) { + HelperFunctions.showToast(App.getContext().getText(R.string.general_server_running_error).toString(), true); + return; + } + + if (optionCodes.get(selectedOption) == 201) { + ConfirmationModalWindow confirmationModal = new ConfirmationModalWindow( + ctx, + R.string.tmp_server_options_remove_password_confirmation, + R.string.tmp_confirmation_yes, + R.string.tmp_confirmation_no, + () -> { + VPNServersPersistentData.getInstance().removePassword(savedVersion.pk); + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_remove_password_done), true); + } + ); + confirmationModal.show(); + } else { + ServerPasswordModalWindow passwordModal = new ServerPasswordModalWindow( + ctx, + server + ); + passwordModal.show(); + } + } else if (optionCodes.get(selectedOption) > 100) { + EditServerValueModalWindow valueModal = new EditServerValueModalWindow( + ctx, + optionCodes.get(selectedOption) == 101, + server + ); + valueModal.show(); + } else if (optionCodes.get(selectedOption) == 10) { + ServerInfoModalWindow infoModal = new ServerInfoModalWindow(ctx, server, listType); + infoModal.show(); + } else if (optionCodes.get(selectedOption) == 11) { + ClipboardManager clipboard = (ClipboardManager)ctx.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clipData = ClipData.newPlainText("", server.pk); + clipboard.setPrimaryClip(clipData); + HelperFunctions.showToast(ctx.getString(R.string.general_copied), true); + } else if (optionCodes.get(selectedOption) == 1) { + if (server.flag != ServerFlags.Blocked) { + VPNServersPersistentData.getInstance().changeFlag(savedVersion, ServerFlags.Favorite); + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_make_favorite_done), true); + return; + } + + ConfirmationModalWindow confirmationModal = new ConfirmationModalWindow( + ctx, + R.string.tmp_server_options_make_favorite_from_blocked_confirmation, + R.string.tmp_confirmation_yes, + R.string.tmp_confirmation_no, + () -> { + VPNServersPersistentData.getInstance().changeFlag(savedVersion, ServerFlags.Favorite); + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_make_favorite_done), true); + } + ); + confirmationModal.show(); + } else if (optionCodes.get(selectedOption) == -1) { + VPNServersPersistentData.getInstance().changeFlag(savedVersion, ServerFlags.None); + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_remove_from_favorites_done), true); + } else if (optionCodes.get(selectedOption) == 2) { + if (VPNServersPersistentData.getInstance().getCurrentServer() != null && + VPNServersPersistentData.getInstance().getCurrentServer().pk.toLowerCase().equals(server.pk.toLowerCase()) + ) { + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_block_error), true); + return; + } + + if (server.flag != ServerFlags.Favorite) { + VPNServersPersistentData.getInstance().changeFlag(savedVersion, ServerFlags.Blocked); + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_block_done), true); + return; + } + + ConfirmationModalWindow confirmationModal = new ConfirmationModalWindow( + ctx, + R.string.tmp_server_options_block_favorite_confirmation, + R.string.tmp_confirmation_yes, + R.string.tmp_confirmation_no, + () -> { + VPNServersPersistentData.getInstance().changeFlag(savedVersion, ServerFlags.Blocked); + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_block_done), true); + } + ); + confirmationModal.show(); + } else if (optionCodes.get(selectedOption) == -2) { + VPNServersPersistentData.getInstance().changeFlag(savedVersion, ServerFlags.None); + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_unblock_done), true); + } else if (optionCodes.get(selectedOption) == -3) { + ConfirmationModalWindow confirmationModal = new ConfirmationModalWindow( + ctx, + R.string.tmp_server_options_remove_from_history_confirmation, + R.string.tmp_confirmation_yes, + R.string.tmp_confirmation_no, + () -> { + VPNServersPersistentData.getInstance().removeFromHistory(savedVersion.pk); + HelperFunctions.showToast(ctx.getString(R.string.tmp_server_options_remove_from_history_done), true); + } + ); + confirmationModal.show(); + } + }); + modal.show(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/MaterialFontSpan.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/MaterialFontSpan.java new file mode 100644 index 0000000000..f4d5f6ce16 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/MaterialFontSpan.java @@ -0,0 +1,32 @@ +package com.skywire.skycoin.vpn.helpers; + +import android.content.Context; +import android.graphics.Typeface; +import android.text.TextPaint; +import android.text.style.TypefaceSpan; + +import androidx.core.content.res.ResourcesCompat; + +import com.skywire.skycoin.vpn.R; + +public class MaterialFontSpan extends TypefaceSpan { + private static Typeface materialFont; + + public MaterialFontSpan(Context context) { + super(""); + + if (materialFont == null) { + materialFont = ResourcesCompat.getFont(context, R.font.material_font); + } + } + + @Override + public void updateDrawState(TextPaint paint) { + paint.setTypeface(materialFont); + } + + @Override + public void updateMeasureState(TextPaint paint) { + paint.setTypeface(materialFont); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/Notifications.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/Notifications.java new file mode 100644 index 0000000000..a0e7501dc3 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/Notifications.java @@ -0,0 +1,162 @@ +package com.skywire.skycoin.vpn.helpers; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; + +import androidx.core.app.NotificationCompat; + +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.vpn.VPNGeneralPersistentData; +import com.skywire.skycoin.vpn.vpn.VPNStates; + +import io.reactivex.rxjava3.disposables.Disposable; +import skywiremob.Skywiremob; + +/** + * Constant values and helper functions for showing notifications. + */ +public class Notifications { + /** + * ID of the notification channel for showing the VPN service status. + */ + public static final String NOTIFICATION_CHANNEL_ID = "SkywireVPN"; + /** + * ID of the notification channel for showing alerts and errors. + */ + public static final String ALERT_NOTIFICATION_CHANNEL_ID = "SkywireVPNAlerts"; + + /** + * ID of the VPN service status notification. + */ + public static final int SERVICE_STATUS_NOTIFICATION_ID = 1; + /** + * ID of the notification for informing about errors while trying to automatically start the + * VPN service during boot. + */ + public static final int AUTOSTART_ALERT_NOTIFICATION_ID = 10; + /** + * ID of the generic error notifications. + */ + public static final int ERROR_NOTIFICATION_ID = 50; + + /** + * Units used for showing the data transmission stats. + */ + private static Globals.DataUnits dataUnits = VPNGeneralPersistentData.getDataUnits(); + /** + * Subscription for updating the data transmission stats. + */ + private static Disposable dataUnitsSubscription; + + /** + * Closes all the alert and error notifications created by the app. Only notifications with + * the IDs defined in this class will be closed. + */ + public static void removeAllAlertNotifications() { + NotificationManager notificationManager = (NotificationManager) App.getContext().getSystemService(Context.NOTIFICATION_SERVICE); + + notificationManager.cancel(AUTOSTART_ALERT_NOTIFICATION_ID); + notificationManager.cancel(ERROR_NOTIFICATION_ID); + } + + /** + * Creates and shows an alert notification. + * @param ID Notification ID. Please use one of the IDs defined in this class. + * @param title Notification title. + * @param content Main notification text. + * @param contentIntent Intent for when the user presses the notification. + */ + public static void showAlertNotification(int ID, String title, String content, PendingIntent contentIntent) { + // Create the style for a multiline notification. It will be ignore if the OS does not + // support it. + NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle() + .setBigContentTitle(title) + .bigText(content); + + // Create the notification. + Notification notification = new NotificationCompat.Builder(App.getContext(), ALERT_NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_error) + .setContentTitle(title) + .setContentText(content) + .setStyle(bigTextStyle) + .setContentIntent(contentIntent) + .build(); + + // Show it. + NotificationManager notificationManager = (NotificationManager)App.getContext().getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(ID, notification); + } + + /** + * Creates a notification for displaying the current state of the VPN service. The notification + * is returned, not displayed. + * @param currentState Current state of the VPN service. + * @param protectionEnabled If the network protection has already been activated. + * @return The created notification. + */ + public static Notification createStatusNotification(VPNStates currentState, boolean protectionEnabled) { + // Start updating the data transmission stats, if needed. + if (dataUnitsSubscription == null) { + dataUnitsSubscription = VPNGeneralPersistentData.getDataUnitsObservable().subscribe(response -> { + dataUnits = response; + }); + } + + // The title is always "preparing", unless the state indicates the service is connected, + // disconnecting or restoring. For the state numeric values, check the emun documentation. + int title = R.string.vpn_service_state_preparing; + if (currentState == VPNStates.CONNECTED) { + title = VPNStates.getTitleForState(currentState); + } else { + if (currentState.val() >= VPNStates.DISCONNECTING.val()) { + title = R.string.vpn_service_state_finishing; + } else if (currentState.val() >= VPNStates.RESTORING_VPN.val() && currentState.val() < VPNStates.DISCONNECTING.val()) { + title = R.string.vpn_service_state_restoring; + } + } + + // Main text for the notification. + String text = App.getContext().getString(VPNStates.getDescriptionForState(currentState)); + // If connected, the connection stats are shown as the main text. + if (currentState == VPNStates.CONNECTED) { + text = "\u2191" + HelperFunctions.computeDataAmountString(Skywiremob.vpnBandwidthSent(), true, dataUnits != Globals.DataUnits.OnlyBytes); + text += " \u2193" + HelperFunctions.computeDataAmountString(Skywiremob.vpnBandwidthReceived(), true, dataUnits != Globals.DataUnits.OnlyBytes); + text += " \u2194" + HelperFunctions.getLatencyValue(Skywiremob.vpnLatency()); + } + + // The lines icon indicates that the service is disconnected and the network protection is + // not active. The filed icon indicates that the service is connected and working. The + // alert icon indicates that the network protection is active, but the VPN service is still + // not working. The error icon is used only if an error stopped the service. + int icon = R.drawable.ic_lines; + if (protectionEnabled) { + if (currentState == VPNStates.CONNECTED) { + icon = R.drawable.ic_filled; + } else { + icon = R.drawable.ic_alert; + } + } + if (currentState == VPNStates.ERROR || currentState == VPNStates.BLOCKING_ERROR) { + icon = R.drawable.ic_error; + } + + // Create the style for a multiline notification. It will be ignore if the OS does not + // support it. + NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle() + .bigText(text) + .setBigContentTitle(App.getContext().getString(title)); + + return new NotificationCompat.Builder(App.getContext(), NOTIFICATION_CHANNEL_ID) + .setSmallIcon(icon) + .setContentTitle(App.getContext().getString(title)) + .setContentText(text) + .setStyle(bigTextStyle) + .setContentIntent(HelperFunctions.getOpenAppPendingIntent()) + .setOnlyAlertOnce(true) + .setSound(null) + .build(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/UiMaterialIcons.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/UiMaterialIcons.java new file mode 100644 index 0000000000..e1883ed109 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/helpers/UiMaterialIcons.java @@ -0,0 +1,6 @@ +package com.skywire.skycoin.vpn.helpers; + +public enum UiMaterialIcons { + MENU, + BACK, +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/ApiClient.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/ApiClient.java new file mode 100644 index 0000000000..9b385f3b07 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/ApiClient.java @@ -0,0 +1,69 @@ +package com.skywire.skycoin.vpn.network; + +import com.skywire.skycoin.vpn.network.models.IpModel; +import com.skywire.skycoin.vpn.network.models.VpnServerModel; + +import java.util.List; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.schedulers.Schedulers; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; +import retrofit2.converter.scalars.ScalarsConverterFactory; +import retrofit2.http.GET; +import retrofit2.http.Query; +import retrofit2.http.Url; + +public class ApiClient { + + private interface ApiInterface { + @GET("services") + Observable>> getVpnServers(@Query("type") String type); + + @GET + Observable> checkConnection(@Url String url); + + @GET + Observable> checkCurrentIp(@Url String url); + } + + private interface RawTextApiInterface { + @GET + Observable> checkIpCountry(@Url String url); + } + + public static final String BASE_URL = "https://service.discovery.skycoin.com/api/"; + + private static final Retrofit retrofit = new Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava3CallAdapterFactory.createWithScheduler(Schedulers.io())) + .build(); + + private static final Retrofit rawTextRetrofit = new Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(ScalarsConverterFactory.create()) + .addCallAdapterFactory(RxJava3CallAdapterFactory.createWithScheduler(Schedulers.io())) + .build(); + + private static final ApiInterface apiService = retrofit.create(ApiInterface.class); + private static final RawTextApiInterface rawTextApiService = rawTextRetrofit.create(RawTextApiInterface.class); + + public static Observable>> getVpnServers() { + return apiService.getVpnServers("vpn"); + } + + public static Observable> checkConnection(String url) { + return apiService.checkConnection(url); + } + + public static Observable> getCurrentIp() { + return apiService.checkCurrentIp("https://api.ipify.org/?format=json"); + } + + public static Observable> getIpCountry(String ip) { + return rawTextApiService.checkIpCountry("https://ip2c.org/" + ip); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/GeoInfoModel.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/GeoInfoModel.java new file mode 100644 index 0000000000..d8c0671639 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/GeoInfoModel.java @@ -0,0 +1,8 @@ +package com.skywire.skycoin.vpn.network.models; + +public class GeoInfoModel { + public Double lat; + public Double lon; + public String country; + public String region; +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/IpModel.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/IpModel.java new file mode 100644 index 0000000000..e797c1fdf4 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/IpModel.java @@ -0,0 +1,5 @@ +package com.skywire.skycoin.vpn.network.models; + +public class IpModel { + public String ip; +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/VpnServerModel.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/VpnServerModel.java new file mode 100644 index 0000000000..b54869a887 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/network/models/VpnServerModel.java @@ -0,0 +1,6 @@ +package com.skywire.skycoin.vpn.network.models; + +public class VpnServerModel { + public String addr; + public GeoInfoModel geo; +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/LocalServerData.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/LocalServerData.java new file mode 100644 index 0000000000..518e1fc393 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/LocalServerData.java @@ -0,0 +1,18 @@ +package com.skywire.skycoin.vpn.objects; + +import java.util.Date; + +public class LocalServerData { + public String countryCode; + public String name; + public String customName; + public String pk; + public Date lastUsed; + public boolean inHistory; + public ServerFlags flag; + public String location; + public String note; + public String personalNote; + public String password; + public boolean enteredManually; +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ManualVpnServerData.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ManualVpnServerData.java new file mode 100644 index 0000000000..2255fbd223 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ManualVpnServerData.java @@ -0,0 +1,8 @@ +package com.skywire.skycoin.vpn.objects; + +public class ManualVpnServerData { + public String name; + public String password; + public String pk; + public String note; +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ServerFlags.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ServerFlags.java new file mode 100644 index 0000000000..3a2ea5fc6c --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ServerFlags.java @@ -0,0 +1,7 @@ +package com.skywire.skycoin.vpn.objects; + +public enum ServerFlags { + None, + Favorite, + Blocked +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ServerRatings.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ServerRatings.java new file mode 100644 index 0000000000..8d178160ae --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/objects/ServerRatings.java @@ -0,0 +1,38 @@ +package com.skywire.skycoin.vpn.objects; + +import com.skywire.skycoin.vpn.R; + +// TODO: for currently commented fields, must be deleted or reactivated depending on what happens to the fields. +//public enum ServerRatings { +// Gold, +// Silver, +// Bronze; +// +// /** +// * Allows to get the resource ID of the string corresponding to the rating. If no resource is +// * found for the rating, -1 is returned. +// */ +// public static int getTextForRating(ServerRatings rating) { +// if (rating == Gold) { +// return R.string.rating_gold; +// } else if (rating == Silver) { +// return R.string.rating_silver; +// } else if (rating == Bronze) { +// return R.string.rating_bronze; +// } +// +// return -1; +// } +// +// public static int getNumberForRating(ServerRatings rating) { +// if (rating == Gold) { +// return 2; +// } else if (rating == Silver) { +// return 1; +// } else if (rating == Bronze) { +// return 0; +// } +// +// return -1; +// } +//} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/SkywireVPNConnection.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/SkywireVPNConnection.java new file mode 100644 index 0000000000..fca375bd86 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/SkywireVPNConnection.java @@ -0,0 +1,312 @@ +package com.skywire.skycoin.vpn.vpn; + +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.DatagramChannel; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.ObservableEmitter; +import io.reactivex.rxjava3.core.ObservableOnSubscribe; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; +import skywiremob.Skywiremob; + +/** + * Class in charge of finishing starting the visor and connect it with the VPN work interface, + * to make the VPN functional. + */ +public class SkywireVPNConnection implements Closeable { + /** + * Object for controlling the local visor. + */ + private final VisorRunnable visorRunnable; + /** + * Current VPN work interface. + */ + private VPNWorkInterface vpnInterface; + /** + * Tunnel for communicating with the local visor. + */ + private DatagramChannel tunnel = null; + + /** + * Allows to know if any of the procedures for sending and receiving data finished. + */ + private boolean managerFinished = false; + /** + * Error message returned during the last call to the function for making the VPN connection + * work, if any. + */ + private String lastError = null; + /** + * Last error returned by a procedure for sending or receiving data in another thread, if any. + */ + private Throwable operationError = null; + /** + * Observable used by this instance to make the VPN connection work. + */ + private Observable observable; + + private Disposable sendingProcedureSubscription; + private Disposable receivingProcedureSubscription; + + public SkywireVPNConnection( + VisorRunnable visorRunnable, + VPNWorkInterface vpnInterface + ) { + this.visorRunnable = visorRunnable; + this.vpnInterface = vpnInterface; + } + + /** + * Stops all operations and frees the resources used by this instance. + */ + @Override + public void close() { + closeConnection(); + } + + /** + * Creates an observable with the procedure for finishing the visor initialization and + * connecting the VPN interface with it, which makes the whole VPN protection start working. + * @return Observable which emits the current state, using the constants defined in VPNStates. + * The observable is not expected to complete, just emit and return errors. + */ + public Observable getObservable() { + // A new observable is created only if needed. + if (observable == null) { + observable = Observable.create((ObservableOnSubscribe) emitter -> { + try { + Skywiremob.printString("Starting VPN connection"); + + if (VPNGeneralPersistentData.getMustRestartVpn()) { + // The code will restart the connection in case of problem, but only if + // the connection was established during the last attempt. + while (true) { + // Stop if the emitter is no longer valid. + if (emitter.isDisposed()) { return; } + + lastError = null; + + // Break if the attempt was not able to finish the connection. + if (!run(emitter)) { + break; + } + + // Retry after a small delay. + emitter.onNext(VPNStates.RESTORING_VPN); + if (emitter.isDisposed()) { + return; + } + Thread.sleep(2000); + } + } else { + // Try to make the connection one time only. + run(emitter); + } + + // Finish with an error. + if (lastError == null) { + HelperFunctions.logError("VPN connection", "The connection has been closed unexpectedly."); + if (emitter.isDisposed()) { return; } + emitter.onError(new Exception(App.getContext().getString(R.string.vpn_connection_finished_error))); + } else { + HelperFunctions.logError("VPN connection", lastError); + if (emitter.isDisposed()) { return; } + emitter.onError(new Exception(lastError)); + } + } catch (Exception e) { + HelperFunctions.logError("The VPN connection failed, exiting", e); + if (!emitter.isDisposed()) { + emitter.onError(e); + } + } + + // This should never happen, as an error should have been reported before. + if (emitter.isDisposed()) { return; } + emitter.onComplete(); + }); + } + + return observable; + } + + /** + * Finish the visor initialization and connects the VPN interface with it, establishing the + * VPN connection. It is expected to run indefinitely and return only in case of error. + * @return True if the connections was established before the function finished. + */ + private boolean run(ObservableEmitter parentEmitter) { + boolean connected = false; + + managerFinished = false; + + // Reset the error vars, to indicate that no errors have occurred during this execution of + // the function. + lastError = null; + operationError = null; + + // TODO: delete if the code for protecting the sockets is removed. + // String protectErrorMsg = App.getContext().getString(R.string.vpn_socket_protection_error); + + try { + // Finish the visor initialization. + visorRunnable.runVpnClient(parentEmitter); + + // Create a DatagramChannel for connecting with the local visor. + if (parentEmitter.isDisposed()) { return connected; } + tunnel = DatagramChannel.open(); + + // TODO: this code is used for protecting the sockets (make them bypass vpn protection) + // needed for configuration, to avoid infinite loops. This is not currently needed + // because there is an exception that covers the entire application. The code remains + // here as a precaution and should be removed in the future. + /* + // Protect the tunnel before connecting to avoid loopback. + if (parentEmitter.isDisposed()) { return connected; } + if (!service.protect(tunnel.socket())) { + HelperFunctions.logError(getTag(), "Cannot protect the app-visor socket"); + throw new IllegalStateException(protectErrorMsg); + } + while(true) { + if (parentEmitter.isDisposed()) { return connected; } + + int fd = (int) Skywiremob.nextDmsgSocket(); + if (fd == 0) { break; } + + Skywiremob.printString("PRINTING FD " + fd); + if (!service.protect(fd)) { + HelperFunctions.logError(getTag(), "Cannot protect the socket for " + fd); + throw new IllegalStateException(protectErrorMsg); + } + } + */ + + // Connect to the local visor. + if (parentEmitter.isDisposed()) { return connected; } + tunnel.connect(new InetSocketAddress(Globals.LOCAL_VISOR_ADDRESS, Globals.LOCAL_VISOR_PORT)); + + // Inform the local socket address to Skywiremob. + // NOTE: this function should work in old Android versions, but there is a bug, at + // least in Android API 17, which makes the port to always be 0, that is why the app + // requires Android API 21+ to run. Maybe creating the socket by hand would allow to + // support older versions. + if (parentEmitter.isDisposed()) { return connected; } + Skywiremob.setMobileAppAddr(tunnel.socket().getLocalSocketAddress().toString()); + + // Make the data operations synchronous. + tunnel.configureBlocking(true); + // Configure the virtual network interface. This activates the VPN protection in the + // OS, if it is being done for the first time. + if (parentEmitter.isDisposed()) { return connected; } + vpnInterface.configure(VPNWorkInterface.Modes.WORKING); + // Inform the connection. + if (parentEmitter.isDisposed()) { return connected; } + connected = true; + parentEmitter.onNext(VPNStates.CONNECTED); + + Skywiremob.printString("The VPN connection is forwarding packets on Android"); + + // Create an observable for sending data in another thread. + sendingProcedureSubscription = VPNDataManager.createObservable(vpnInterface, tunnel, true) + .subscribeOn(Schedulers.newThread()).subscribe( + val -> {}, + err -> { + synchronized (this) { + // Save the error, to use it below. + if (operationError == null) { + operationError = err; + } + } + + stopWaiting(); + }, + () -> stopWaiting() + ); + // Create an observable for receiving data in another thread. + receivingProcedureSubscription = VPNDataManager.createObservable(vpnInterface, tunnel, false) + .subscribeOn(Schedulers.newThread()).subscribe( + val -> {}, + err -> { + synchronized (this) { + // Save the error, to use it below. + if (operationError == null) { + operationError = err; + } + } + + stopWaiting(); + }, + () -> stopWaiting() + ); + + synchronized (this) { + // Stop the thread until receiving a signal. If the observable is disposed while + // the thread is still waiting, an error will be thrown and it will be caught below. + if (!managerFinished) { + this.wait(); + } + + // If an error was saved while the thread was waiting, throw it. + if (operationError != null) { + throw operationError; + } + } + } catch (Throwable e) { + // Report the error. + if (!parentEmitter.isDisposed()) { + HelperFunctions.logError("VPN connector work procedure", e); + lastError = e.getLocalizedMessage(); + } + } finally { + // CLose the connection. + closeConnection(); + } + + return connected; + } + + /** + * Reactivates the thread after being stopped in the run() function. + */ + private void stopWaiting() { + synchronized (this) { + managerFinished = true; + + try { + this.notify(); + } catch (Exception e) { } + } + } + + /** + * Closes any open connection, stops the VPN client and stops the the pending threads. + */ + private void closeConnection() { + if (sendingProcedureSubscription != null) { + sendingProcedureSubscription.dispose(); + } + if (receivingProcedureSubscription != null) { + receivingProcedureSubscription.dispose(); + } + + visorRunnable.stopVpnConnection(); + + if (tunnel != null) { + try { + tunnel.close(); + tunnel = null; + } catch (IOException e) { + HelperFunctions.logError("Unable to close tunnel used by the VPN connection", e); + } + } + + stopWaiting(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/SkywireVPNService.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/SkywireVPNService.java new file mode 100644 index 0000000000..ad5ebe9b9b --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/SkywireVPNService.java @@ -0,0 +1,508 @@ +package com.skywire.skycoin.vpn.vpn; + +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.net.VpnService; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.helpers.Notifications; +import com.skywire.skycoin.vpn.objects.ServerFlags; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; +import skywiremob.Skywiremob; + +/** + * Service in charge of making the VPN protection work, even if the UI is closed. + */ +public class SkywireVPNService extends VpnService { + /** + * Action that must be sent to the service for starting the VPN connection. If + * the connection has already been started, it continues running normally. + */ + public static final String ACTION_CONNECT = "com.skywire.android.vpn.START"; + /** + * Action that must be sent to the service for stopping the VPN connection. The procedure may + * take some time to complete, so the state events must be monitored. + */ + public static final String ACTION_DISCONNECT = "com.skywire.android.vpn.STOP"; + + /** + * Param returned by the service as part of the state updates, for including the error + * message, if the state includes one. + */ + public static final String ERROR_MSG_PARAM = "ErrorMsg"; + /** + * Param returned by the service as part of the state updates, for informing if the service is + * running because the OS requested it (true) or was started by the app itself (false). + */ + public static final String STARTED_BY_THE_SYSTEM_PARAM = "StartedByTheSystem"; + /** + * Param returned by the service as part of the state updates, for informing if it has received + * a request for completely stopping the service. The request may have not been made by + * the user. + */ + public static final String STOP_REQUESTED_PARAM = "StopRequested"; + + /** + * ID of the last instance of the service. This is needed because a new instance may be + * created by the OS while the previous one is still being destroyed and in those cases it is + * necessary to stop making some operations in the old instance. + */ + public static int lastInstanceID = 0; + /** + * ID of this object instance. If it is not equal to lastInstanceID, this is not the + * latest instance. + */ + public int instanceID = 0; + + /** + * Object for showing notifications. + */ + private final NotificationManager notificationManager = (NotificationManager) App.getContext().getSystemService(Context.NOTIFICATION_SERVICE); + + /** + * Instance for communicating with the VPN coordinator class. + */ + private Messenger messenger; + + /** + * Object in charge of performing the steps needed for making the VPN protection work. + */ + private VPNRunnable vpnRunnable; + /** + * Current VPN work interface. + */ + private VPNWorkInterface vpnInterface; + + /** + * Current state of the VPN protection. + */ + private VPNStates currentState = VPNStates.STARTING; + + /** + * If the service is running because the OS requested it (true) or was started by the app + * itself (false). + */ + private boolean startedByTheSystem = false; + /** + * If true, a condition that makes it not possible to start the service was detected, so + * the option for retrying the connection must be ignored. + */ + private boolean impossibleToStart = false; + /** + * If there was a request for completely stopping the service. + */ + private boolean stopRequested = false; + /** + * If the service has already been destroyed. The code may still be running cleaning procedures. + */ + private boolean serviceDestroyed = false; + + /** + * Msg of the last error detected by this instance. + */ + private String lastErrorMsg = ""; + + private Disposable updateNotificationSubscription; + private Disposable restartingSubscription; + private Disposable vpnRunnableSubscription; + + /** + * Informs the current state to the VPN coordinator, updates the state notification and shows + * toast notifications, if needed. It also updates the current state var. + */ + private void informNewState(VPNStates newState) { + // Cancel the operation if there is a newer instance of the service. + if (lastInstanceID != instanceID) { + return; + } + + // Create a new message for informing the VPN coordinator about the new state. + Message msg = Message.obtain(); + msg.what = newState.val(); + + // Add the additional data to the message. + Bundle dataBundle = new Bundle(); + dataBundle.putBoolean(STARTED_BY_THE_SYSTEM_PARAM, startedByTheSystem); + dataBundle.putBoolean(STOP_REQUESTED_PARAM, stopRequested); + + // Get the last error from vpnRunnable.getLastErrorMsg(). The lastErrorMsg must be used + // to avoid errors because vpnRunnable may be null. + lastErrorMsg = vpnRunnable != null ? vpnRunnable.getLastErrorMsg() : lastErrorMsg; + dataBundle.putString(ERROR_MSG_PARAM, lastErrorMsg); + + msg.setData(dataBundle); + + // Show toast notifications for certain states if the UI is not being shown. + if (!App.displayingUI() && currentState != newState) { + // Only if the service has not been destroyed. + if (!serviceDestroyed && (newState == VPNStates.CONNECTED || + newState == VPNStates.RESTORING_VPN || + newState == VPNStates.RESTORING_SERVICE || + newState == VPNStates.ERROR || + newState == VPNStates.BLOCKING_ERROR)) + { + HelperFunctions.showToast(getString(VPNStates.getDescriptionForState(newState)), false); + } + + // Even if the service has been destroyed. + if (newState == VPNStates.DISCONNECTED || newState == VPNStates.DISCONNECTING || newState == VPNStates.OFF) { + HelperFunctions.showToast(getString(VPNStates.getDescriptionForState(newState)), false); + } + } + + currentState = newState; + + // Send the message to the VPN coordinator. + try { + messenger.send(msg); + } catch (Exception e) { } + + // Update the notification. + updateForegroundNotification(); + + // Procedure for periodically updating the notification with the connection stats, if the + // VPN protection is active. + if (updateNotificationSubscription != null) { + updateNotificationSubscription.dispose(); + } + if (newState == VPNStates.CONNECTED) { + updateNotificationSubscription = Observable.interval(2000, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> updateForegroundNotification()); + } + } + + /** + * Function that must be called when there are changes in the state of the VPN protection. It + * processes the new state, makes some preparations and informs it. + */ + private void updateState(VPNStates newState) { + // State that will be reported at the end of the function. It may be modified. + VPNStates processedState = newState; + + // If the current state is for indicating an error and the new state is for indicating + // that the VPN protection is being disconnected, the current state is maintained, to + // avoid replacing the error indications, which is more useful than a generic indication + // about the service being stopped. This also prevents the code from "forgetting" that + // there was an error, which may be important later. + if (processedState.val() >= 200 && processedState.val() < 300 && currentState.val() >= 400 && currentState.val() <= 500) { + processedState = currentState; + } + + boolean failedBecausePassword = false; + // If the state indicates that vpnRunnable finished, remove the instance. + if (processedState.val() >= 300 && processedState.val() < 400) { + // Check if the process finished due to an error cause by a wrong password. This data is + // used if the protection has to be restarted. + if (vpnRunnable != null && vpnRunnable.getIfPasswordFailed()) { + failedBecausePassword = true; + } + vpnRunnable = null; + if (vpnRunnableSubscription != null) { + vpnRunnableSubscription.dispose(); + } + } + + // Only needed if the service is not forced to terminate. + if (!stopRequested && !serviceDestroyed) { + // If the new state is for informing about an error. + if (processedState.val() >= 400 && processedState.val() < 500) { + if (VPNGeneralPersistentData.getMustRestartVpn() && !impossibleToStart) { + // If the option for restarting the protection automatically is active, update + // the state. + processedState = VPNStates.RESTORING_SERVICE; + } else if (processedState == VPNStates.ERROR) { + // If the error was not a blocking one, which would mean that the network must + // remain blocked, indicate that the service must be closed after closing + // the VPN. + stopRequested = true; + } + } + + // If the service is being restored, hide the states about the connection being + // closed and restored. + if (currentState == VPNStates.RESTORING_SERVICE) { + // Restart the whole VPN connection after a small delay when receiving the state + // indicating that vpnRunnable finished. If the error was because the password was + // wrong, the delay is much longer. + if (processedState.val() >= 300 && processedState.val() < 400) { + int delay = failedBecausePassword ? 60000 : 1; + restartingSubscription = Observable.just(0).delay(delay, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> runVpn()); + } + + if (processedState.val() >= 150 && processedState.val() < 400) { + processedState = VPNStates.RESTORING_SERVICE; + } + } else { + // If the service is not being restored, close the whole service when receiving + // the state indicating that vpnRunnable finished. + if (processedState.val() >= 300 && processedState.val() < 400) { + processedState = currentState; + finishIfAppropriate(); + } + } + } else { + // Close the whole service when receiving the state indicating that + // vpnRunnable finished. + if (processedState.val() >= 300 && processedState.val() < 400) { + processedState = currentState; + finishIfAppropriate(); + } + } + + // Inform the new state to the VPN coordinator and update the notifications. + informNewState(processedState); + } + + /** + * Function called by the OS just after receiving an instruction for starting the service. + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // Update the ID of this instance, to make sure no old instance is considered newer than + // this one. + lastInstanceID += 1; + instanceID = lastInstanceID; + + if (intent != null && ACTION_DISCONNECT.equals(intent.getAction())) { + // If this function was called to stop the VPN protection. + + stopRequested = true; + + // Stop the connection. If it was already stopped, finish the service directly. + if (vpnRunnable != null) { + vpnRunnable.disconnect(); + } else { + finishIfAppropriate(); + } + + // Needed for informing the new value of the stopRequested var. + updateState(currentState); + } else { + // If the function was not called for stopping the VPN protection, it is considered + // that it was called for starting it. In this case, the instruction for starting the + // service may have been made by the OS or the app itself. if the ACTION_CONNECT action + // is not detected, it is considered that the request was made by the OS. + + // Get the object for communicating with the VPN coordinator. + if (messenger == null) { + messenger = VPNCoordinator.getInstance().getCommunicationMessenger(); + } + + if (vpnInterface == null) { + // Become a foreground service. Background services can be VPN services too, but + // they can be killed by background check before getting a chance to + // receive onRevoke(). + makeForeground(); + + vpnInterface = new VPNWorkInterface(this); + } + + // If the option for blocking the network while configuring the service is active or + // the request was made by the OS, the VPN work interface is configured, to block all + // network connections. The action is always made when the service is started by the OS + // because the OS will only stop the service after the user request it if the interface + // is configured (appears like a bug in the OS). + if (!vpnInterface.alreadyConfigured() && (VPNGeneralPersistentData.getProtectBeforeConnected() || intent == null || !ACTION_CONNECT.equals(intent.getAction()))) { + try { + vpnInterface.configure(VPNWorkInterface.Modes.BLOCKING); + } catch (Exception e) { + // Report the error and finish the service. + HelperFunctions.logError("Configuring VPN work interface before connecting", e); + lastErrorMsg = getString(R.string.vpn_service_network_protection_error); + updateState(VPNStates.ERROR); + finishIfAppropriate(); + + return START_NOT_STICKY; + } + + if (intent == null || !ACTION_CONNECT.equals(intent.getAction())) { + HelperFunctions.showToast(getString(R.string.vpn_service_network_unavailable_warning), false); + } + } + + // Update if the service was started by the OS and notify it in a state event. Note + // that this code updates the previous value if the service was originally started by + // the app, this is intended. + if (intent == null || !ACTION_CONNECT.equals(intent.getAction())) { + startedByTheSystem = true; + } + updateState(currentState); + + // Check if no server has been selected and if the selected server has been blocked. + String errorMsg = null; + if ( + VPNServersPersistentData.getInstance().getCurrentServer() == null || + VPNServersPersistentData.getInstance().getCurrentServer().pk == null || + VPNServersPersistentData.getInstance().getCurrentServer().pk.trim().equals("") + ) { + errorMsg = App.getContext().getText(R.string.skywiremob_error_no_server).toString(); + } else if (VPNServersPersistentData.getInstance().getCurrentServer().flag == ServerFlags.Blocked) { + errorMsg = App.getContext().getText(R.string.skywiremob_error_server_blocked).toString(); + } + + // If any of the previous conditions was found, put the service in error state. + if (errorMsg != null) { + HelperFunctions.logError("Starting VPN service", errorMsg); + lastErrorMsg = errorMsg; + impossibleToStart = true; + updateState(VPNStates.ERROR); + } else { + // Start the VPN protection. + runVpn(); + } + } + + return START_NOT_STICKY; + } + + /** + * Function called by the OS when the service is destroyed. + */ + @Override + public void onDestroy() { + Skywiremob.printString("VPN service destroyed."); + serviceDestroyed = true; + + // Stop the connection. If it was already stopped, finish the service directly. + if (vpnRunnable != null) { + vpnRunnable.disconnect(); + } else { + finishIfAppropriate(); + } + } + + /** + * Function called by the OS when the user revokes the permission for the VPN. + */ + @Override + public void onRevoke() { + super.onRevoke(); + Skywiremob.printString("onRevoke called"); + // Destroy the service. + this.stopSelf(); + } + + /** + * Starts the VPN protection, if it is not already active or starting. + */ + private void runVpn() { + if (vpnRunnable == null) { + vpnRunnable = new VPNRunnable(vpnInterface); + } + + if (vpnRunnableSubscription != null) { + vpnRunnableSubscription.dispose(); + } + + // Initialize the VPN. Also, get and process the state updates. + vpnRunnableSubscription = vpnRunnable.start().subscribe(state -> updateState(state)); + } + + /** + * Cleans the resources used by the service and stops it, but only if vpnRunnable + * already finished. + */ + private void finishIfAppropriate() { + if (vpnRunnable == null) { + if (vpnInterface == null || + !vpnInterface.alreadyConfigured() || + stopRequested || + serviceDestroyed || + currentState.val() < 400 || + currentState.val() >= 500 || + !VPNGeneralPersistentData.getKillSwitchActivated() + ) { + // Steps that must be performed only if there is no a newer instance of the service. + if (lastInstanceID == instanceID) { + // Clean the VPN interface (which stops blocking the network connections). + if (vpnInterface != null) { + vpnInterface.close(); + + // Create another interface and close it immediately to avoid a bug in + // older Android versions when the app is added to the ignore list. + vpnInterface = new VPNWorkInterface(this); + try { + vpnInterface.configure(VPNWorkInterface.Modes.DELETING); + } catch (Exception e) { } + vpnInterface.close(); + } + + // Remove the state notification. + notificationManager.cancel(Notifications.SERVICE_STATUS_NOTIFICATION_ID); + + // Report the new state after a delay, to avoid interferences with any new + // state reported by the code which called this function. + Observable.just(0).delay(100, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> updateState(VPNStates.OFF)); + + // If there was an error in the last execution, the UI is not being displayed + // and the kill switch is not active, show a notification informing that + // the VPN protection was terminated due to an error. + if (!App.displayingUI() && !VPNGeneralPersistentData.getKillSwitchActivated() && VPNGeneralPersistentData.getLastError(null) != null) { + Notifications.showAlertNotification( + Notifications.ERROR_NOTIFICATION_ID, + getString(R.string.general_app_name), + getString(R.string.general_connection_error), + HelperFunctions.getOpenAppPendingIntent() + ); + } + } + + // Remove the objects and close the subscriptions. + vpnInterface = null; + vpnRunnable = null; + if (vpnRunnableSubscription != null) { + vpnRunnableSubscription.dispose(); + } + if (restartingSubscription != null) { + restartingSubscription.dispose(); + } + + // Terminate the service. + stopForeground(true); + stopSelf(); + } + } + } + + /** + * Updates the state notification shown while the service is running in the foreground. + */ + private void updateForegroundNotification() { + if (!serviceDestroyed) { + notificationManager.notify( + Notifications.SERVICE_STATUS_NOTIFICATION_ID, + Notifications.createStatusNotification(currentState, vpnInterface != null && vpnInterface.alreadyConfigured()) + ); + } + } + + /** + * Converts the service into a foreground service, to prevent it to be destroyed by the OS. + */ + private void makeForeground() { + startForeground( + Notifications.SERVICE_STATUS_NOTIFICATION_ID, + Notifications.createStatusNotification(currentState, vpnInterface != null && vpnInterface.alreadyConfigured()) + ); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNCoordinator.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNCoordinator.java new file mode 100644 index 0000000000..5689830d4a --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNCoordinator.java @@ -0,0 +1,315 @@ +package com.skywire.skycoin.vpn.vpn; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.net.VpnService; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; + +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.helpers.Notifications; +import com.skywire.skycoin.vpn.objects.LocalServerData; + +import java.util.ArrayList; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.BehaviorSubject; +import skywiremob.Skywiremob; + +import static android.app.Activity.RESULT_OK; + +/** + * Class for communication between the app UI and the VPN service. It is accessed via a singleton. + */ +public class VPNCoordinator implements Handler.Callback { + public static class ConnectionStats { + public Date lastConnectionDate = null; + public long currentDownloadSpeed = 0; + public long currentUploadSpeed = 0; + public long currentLatency = 0; + public long totalDownloadedData = 0; + public long totalUploadedData = 0; + public ArrayList downloadSpeedHistory = new ArrayList<>(); + public ArrayList uploadSpeedHistory = new ArrayList<>(); + public ArrayList latencyHistory = new ArrayList<>(); + + public ConnectionStats() { + for (int i = 0; i < 10; i++) { + downloadSpeedHistory.add(0L); + uploadSpeedHistory.add(0L); + latencyHistory.add(0L); + } + } + } + + /** + * Value the onActivityResult function will get after asking the user for permission. + */ + public static final int VPN_PREPARATION_REQUEST_CODE = 10100; + + /** + * Singleton instance. + */ + private static final VPNCoordinator instance = new VPNCoordinator(); + /** + * Gets the singleton for using the class. + */ + public static VPNCoordinator getInstance() { return instance; } + + private Disposable updateStatsSubscription; + + private ConnectionStats connectionStats = new ConnectionStats(); + + /** + * App context. + */ + private final Context ctx = App.getContext(); + + /** + * Handler used for receiving messages from the VPN service. + */ + private final Handler serviceCommunicationHandler; + /** + * Subject for sending events via RxJava, indicating the current state of the VPN service. + */ + private final BehaviorSubject eventsSubject = BehaviorSubject.create(); + + private final BehaviorSubject connectionStatsSubject = BehaviorSubject.create(); + + private VPNCoordinator() { + serviceCommunicationHandler = new Handler(this); + + // Add a default current state. + eventsSubject.onNext(new VPNStates.StateInfo(VPNStates.OFF, false, false)); + } + + public Observable getConnectionStats() { + return connectionStatsSubject.hide(); + } + + /** + * Handles the messages received from the VPN service. + */ + @Override + public boolean handleMessage(Message msg) { + // Save the error as the one which made the last execution of the VPN service fail. + // Must be done before sending the event. + String errorMsg = msg.getData().getString(SkywireVPNService.ERROR_MSG_PARAM); + if (errorMsg != null && !errorMsg.equals("") && !errorMsg.equals(VPNGeneralPersistentData.getLastError(null))) { + VPNGeneralPersistentData.setLastError(errorMsg); + } + + if (updateStatsSubscription == null) { + continuallyUpdateStats(); + } + + if (VPNStates.valueOf(msg.what) == VPNStates.CONNECTED) { + // Erase the error which made not possible to connect the last time. + VPNGeneralPersistentData.removeLastError(); + + if (connectionStats.lastConnectionDate == null) { + connectionStats.lastConnectionDate = new Date(); + } + } else { + if (VPNStates.valueOf(msg.what) == VPNStates.DISCONNECTED || VPNStates.valueOf(msg.what) == VPNStates.OFF) { + if (updateStatsSubscription != null) { + updateStatsSubscription.dispose(); + updateStatsSubscription = null; + } + + connectionStats = new ConnectionStats(); + connectionStatsSubject.onNext(connectionStats); + } else { + connectionStats.lastConnectionDate = null; + } + } + + // Create the state object with the params returned by the VPN service. + VPNStates.StateInfo state = new VPNStates.StateInfo( + VPNStates.valueOf(msg.what), + msg.getData().getBoolean(SkywireVPNService.STARTED_BY_THE_SYSTEM_PARAM), + msg.getData().getBoolean(SkywireVPNService.STOP_REQUESTED_PARAM) + ); + + // Inform the new state. + eventsSubject.onNext(state); + + return true; + } + + private void continuallyUpdateStats() { + if (updateStatsSubscription != null) { + updateStatsSubscription.dispose(); + } + + sendStats(); + + updateStatsSubscription = Observable.interval(1000L, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> { + sendStats(); + }); + } + + private void sendStats() { + connectionStats.currentDownloadSpeed = Skywiremob.vpnBandwidthReceived(); + connectionStats.downloadSpeedHistory.remove(0); + connectionStats.downloadSpeedHistory.add(connectionStats.currentDownloadSpeed); + + connectionStats.currentUploadSpeed = Skywiremob.vpnBandwidthSent(); + connectionStats.uploadSpeedHistory.remove(0); + connectionStats.uploadSpeedHistory.add(connectionStats.currentUploadSpeed); + + connectionStats.currentLatency = Skywiremob.vpnLatency(); + connectionStats.latencyHistory.remove(0); + connectionStats.latencyHistory.add(connectionStats.currentLatency); + + connectionStatsSubject.onNext(connectionStats); + } + + /** + * Allows to know if the VPN service is currently running. + */ + public boolean isServiceRunning() { + ActivityManager manager = (ActivityManager) App.getContext().getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + // Check if any of the running services is the VPN service. + if (SkywireVPNService.class.getName().equals(service.service.getClassName())) { + return true; + } + } + return false; + } + + /** + * Returns an observable that emits every time the state of the VPN service changes. The + * observable does not emit errors and never completes. + */ + public Observable getEventsObservable() { + return eventsSubject.hide(); + } + + /** + * Makes the preparations and starts the VPN service. If it is already running, nothing happens. + * @param requestingActivity Activity requesting the service to be started. Please note + * that the onActivityResult function of that activity may be called with the value of + * VPN_PREPARATION_REQUEST_CODE as the first param. In that case the activity must call the + * onActivityResult function of this instance with all the params, to be able to process + * permission requests + * @param server Data about the remote visor. + */ + public void startVPN(Activity requestingActivity, LocalServerData server) { + if (!isServiceRunning()) { + // Save the remote visor and password. + VPNServersPersistentData.getInstance().modifyCurrentServer(server); + VPNServersPersistentData.getInstance().updateHistory(); + + // As the service will be started again, erase the error which made it fail the last + // time it ran, to indicate that no error has stopped the current instance. + VPNGeneralPersistentData.removeLastError(); + + eventsSubject.onNext(new VPNStates.StateInfo(VPNStates.STARTING, false, false)); + + // Get the permission request intent from the OS. + Intent intent = VpnService.prepare(requestingActivity); + if (intent != null) { + // Ask for permission before continuing. + requestingActivity.startActivityForResult(intent, VPN_PREPARATION_REQUEST_CODE); + } else { + starVpnServiceIfNeeded(); + } + } + } + + /** + * Function for starting the VPN service after boot. If the service is already running, + * nothing happens. + */ + public void activateAutostart() { + if (!isServiceRunning()) { + // Check if permission is needed. If it is, fail. + Intent intent = VpnService.prepare(ctx); + if (intent != null) { + HelperFunctions.showToast(ctx.getString(R.string.general_autostart_failed_error), false); + + String errorMsg = ctx.getString(R.string.general_no_permissions_error); + VPNGeneralPersistentData.setLastError(errorMsg); + + Notifications.showAlertNotification( + Notifications.AUTOSTART_ALERT_NOTIFICATION_ID, + ctx.getString(R.string.general_app_name), + errorMsg, + HelperFunctions.getOpenAppPendingIntent() + ); + + return; + } + + // As the service will be started again, erase the error which made it fail the last + // time it ran, to indicate that no error has stopped the current instance. + VPNGeneralPersistentData.removeLastError(); + + starVpnServiceIfNeeded(); + } + } + + /** + * Asks the VPN service to stop. It will not be stopped immediately, the state change events + * must be checked for knowing when it is really stopped. + */ + public void stopVPN() { + ctx.startService(getServiceIntent().setAction(SkywireVPNService.ACTION_DISCONNECT)); + } + + /** + * Must be called by the activity used for calling startVPN, if the same function is called + * in the activity and the value of VPN_PREPARATION_REQUEST_CODE was received as request. + * The same params received in the activity must be provided. + */ + public void onActivityResult(int request, int result, Intent data) { + if (request == VPN_PREPARATION_REQUEST_CODE) { + if (result == RESULT_OK) { + starVpnServiceIfNeeded(); + } else { + eventsSubject.onNext(new VPNStates.StateInfo(VPNStates.OFF, false, true)); + } + } + } + + /** + * Starts the VPN service if it is not already running. + */ + private void starVpnServiceIfNeeded() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + ctx.startForegroundService(getServiceIntent().setAction(SkywireVPNService.ACTION_CONNECT)); + } else { + ctx.startService(getServiceIntent().setAction(SkywireVPNService.ACTION_CONNECT)); + } + } + + /** + * Gets the VPN service intent, without action. + */ + private Intent getServiceIntent() { + return new Intent(ctx, SkywireVPNService.class); + } + + /** + * Gets a Messenger object for communicating with this instance. + */ + public Messenger getCommunicationMessenger() { + return new Messenger(serviceCommunicationHandler); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNDataManager.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNDataManager.java new file mode 100644 index 0000000000..9ebee7bff6 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNDataManager.java @@ -0,0 +1,85 @@ +package com.skywire.skycoin.vpn.vpn; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InterruptedIOException; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.ObservableOnSubscribe; + +/** + * Helper class for creating an observable for sending or getting data to or from the visor. + */ +public class VPNDataManager { + /** + * Creates an observable for sending or getting data to or from the visor. + * @param vpnInterface Interface currently used for the VPN connection. + * @param tunnel Socket for communicating with the visor. + * @param forSending True if the observable will be used for sending the data from the OS to the + * visor, false if it is for sending the data from the visor to the OS. + */ + static public Observable createObservable(VPNWorkInterface vpnInterface, DatagramChannel tunnel, boolean forSending) { + return Observable.create((ObservableOnSubscribe) emitter -> { + // Streams for receiving and sending packages. + final FileInputStream in; + final FileOutputStream out; + // Only the stream needed is initialized. + if (forSending) { + in = vpnInterface.getInputStream(); + out = null; + } else { + in = null; + out = vpnInterface.getOutputStream(); + } + + ByteBuffer packet = ByteBuffer.allocate(Short.MAX_VALUE); + + // Get or send data while the emitter is still valid. + while(!emitter.isDisposed()) { + try { + if (forSending) { + // Read the outgoing packet from the input stream. The operation must block + // blocks the thread. + int length = in.read(packet.array()); + if (length > 0) { + // Write the outgoing packet to the tunnel. + packet.limit(length); + tunnel.write(packet); + packet.clear(); + } + } + + if (!forSending) { + // Read the incoming packet from the visor socket. The operation must block + // blocks the thread. + int length = tunnel.read(packet); + if (length > 0) { + // Ignore control messages, which start with zero. + if (packet.get(0) != 0) { + // Write the incoming packet to the output stream. + out.write(packet.array(), 0, length); + } + packet.clear(); + } + } + } catch (InterruptedIOException e) { + // This error is thrown if there is a timeout while waiting data from the socket. + // It is ignored so that the loop repeats itself to wait for data again. + } catch (Exception e) { + // Emit the error only if the emitter is still valid. + if (!emitter.isDisposed()) { + emitter.onError(e); + return; + } + + break; + } + } + + // Indicate the observable finished. + emitter.onComplete(); + }); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNGeneralPersistentData.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNGeneralPersistentData.java new file mode 100644 index 0000000000..e15b58c19e --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNGeneralPersistentData.java @@ -0,0 +1,238 @@ +package com.skywire.skycoin.vpn.vpn; + +import android.content.SharedPreferences; + +import androidx.preference.PreferenceManager; + +import com.google.gson.Gson; +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.helpers.Globals; + +import java.util.HashSet; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.subjects.BehaviorSubject; + +/** + * Helper class for saving and getting general data related to the VPN to and from the + * persistent storage. + */ +public class VPNGeneralPersistentData { + // Keys for persistent storage. + private static final String LAST_ERROR = "lastError"; + private static final String DATA_UNITS = "dataUnits"; + private static final String CUSTOM_DNS = "customDns"; + private static final String APPS_SELECTION_MODE = "appsMode"; + private static final String APPS_LIST = "appsList"; + private static final String SHOW_IP = "showIp"; + private static final String KILL_SWITCH = "killSwitch"; + private static final String RESTART_VPN = "restartVpn"; + private static final String START_ON_BOOT = "startOnBoot"; + private static final String PROTECT_BEFORE_CONNECTED = "protectBeforeConnected"; + + private static final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(App.getContext()); + + private static BehaviorSubject dataUnitsSubject; + + ///////////////////////////////////////////////////////////// + // Setters. + ///////////////////////////////////////////////////////////// + + /** + * Saves the message of the error which caused the VPN service to fail the last time it + * ran, if any. + */ + public static void setLastError(String val) { + settings.edit().putString(LAST_ERROR, val).apply(); + } + + /** + * Saves the data units that must be shown in the UI. + */ + public static void setDataUnits(Globals.DataUnits val) { + Gson gson = new Gson(); + String valString = gson.toJson(val); + settings.edit().putString(DATA_UNITS, valString).apply(); + + // Inform the change. + if (dataUnitsSubject != null) { + dataUnitsSubject.onNext(val); + } + } + + /** + * Saves the IP of the custom DNS server. + */ + public static void setCustomDns(String val) { + settings.edit().putString(CUSTOM_DNS, val).apply(); + } + + /** + * Saves the mode the VPN service must use to protect or ignore the apps selected by the user. + */ + public static void setAppsSelectionMode(Globals.AppFilteringModes val) { + settings.edit().putString(APPS_SELECTION_MODE, val.toString()).apply(); + } + + /** + * Saves the list with the package names of all apps selected by the user in the app list. + */ + public static void setAppList(HashSet val) { + settings.edit().putStringSet(APPS_LIST, val).apply(); + } + + /** + * Sets if the functionality for showing the IP must be active. + */ + public static void setShowIpActivated(boolean val) { + settings.edit().putBoolean(SHOW_IP, val).apply(); + } + + /** + * Sets if the kill switch functionality must be active. + */ + public static void setKillSwitchActivated(boolean val) { + settings.edit().putBoolean(KILL_SWITCH, val).apply(); + } + + /** + * Sets if the VPN connection must be automatically restarted if there is an error. + */ + public static void setMustRestartVpn(boolean val) { + settings.edit().putBoolean(RESTART_VPN, val).apply(); + } + + /** + * Sets if the VPN protection must be activated as soon as possible after booting the OS. + */ + public static void setStartOnBoot(boolean val) { + settings.edit().putBoolean(START_ON_BOOT, val).apply(); + } + + /** + * Sets if the network protection must be activated just after starting the VPN service, which + * would disable the internet connectivity for the rest of the apps while configuring the visor. + */ + public static void setProtectBeforeConnected(boolean val) { + settings.edit().putBoolean(PROTECT_BEFORE_CONNECTED, val).apply(); + } + + ///////////////////////////////////////////////////////////// + // Getters. + ///////////////////////////////////////////////////////////// + + /** + * Gets the message of the error which caused the VPN service to fail the last time it + * ran, if any. + * @param defaultValue Value to return if no saved data is found. + */ + public static String getLastError(String defaultValue) { + return settings.getString(LAST_ERROR, defaultValue); + } + + /** + * Returns the data units that must be shown in the UI. If the user has not changed + * the setting, it returns DataUnits.BitsSpeedAndBytesVolume by default. + */ + public static Globals.DataUnits getDataUnits() { + Gson gson = new Gson(); + String savedVal = settings.getString(DATA_UNITS, null); + if (savedVal != null) { + return gson.fromJson(savedVal, Globals.DataUnits.class); + } + + return Globals.DataUnits.BitsSpeedAndBytesVolume; + } + + /** + * Emits every time the data units that must be shown in the UI are changed. It emits the most + * recent value immediately after subscription. + */ + public static Observable getDataUnitsObservable() { + if (dataUnitsSubject == null) { + dataUnitsSubject = BehaviorSubject.create(); + dataUnitsSubject.onNext(getDataUnits()); + } + + return dataUnitsSubject.hide(); + } + + /** + * Gets the IP of the custom DNS server. + */ + public static String getCustomDns() { + return settings.getString(CUSTOM_DNS, null); + } + + /** + * Gets the mode the VPN service must use to protect or ignore the apps selected by the user. + */ + public static Globals.AppFilteringModes getAppsSelectionMode() { + String savedValue = settings.getString(APPS_SELECTION_MODE, null); + + if (savedValue == null || savedValue.equals(Globals.AppFilteringModes.PROTECT_ALL.toString())) { + return Globals.AppFilteringModes.PROTECT_ALL; + } else if (savedValue.equals(Globals.AppFilteringModes.PROTECT_SELECTED.toString())) { + return Globals.AppFilteringModes.PROTECT_SELECTED; + } else if (savedValue.equals(Globals.AppFilteringModes.IGNORE_SELECTED.toString())) { + return Globals.AppFilteringModes.IGNORE_SELECTED; + } + + return Globals.AppFilteringModes.PROTECT_ALL; + } + + /** + * Gets the list with the package names of all apps selected by the user in the app list. + * @param defaultValue Value to return if no saved data is found. + */ + public static HashSet getAppList(HashSet defaultValue) { + return new HashSet<>(settings.getStringSet(APPS_LIST, defaultValue)); + } + + /** + * Gets if the functionality for showing the IP must be active. + */ + public static boolean getShowIpActivated() { + return settings.getBoolean(SHOW_IP, true); + } + + /** + * Gets if the kill switch functionality must be active. + */ + public static boolean getKillSwitchActivated() { + return settings.getBoolean(KILL_SWITCH, true); + } + + /** + * Gets if the VPN connection must be automatically restarted if there is an error. + */ + public static boolean getMustRestartVpn() { + return settings.getBoolean(RESTART_VPN, true); + } + + /** + * Gets if the VPN protection must be activated as soon as possible after booting the OS. + */ + public static boolean getStartOnBoot() { + return settings.getBoolean(START_ON_BOOT, false); + } + + /** + * Gets if the network protection must be activated just after starting the VPN service, which + * would disable the internet connectivity for the rest of the apps while configuring the visor. + */ + public static boolean getProtectBeforeConnected() { + return settings.getBoolean(PROTECT_BEFORE_CONNECTED, true); + } + + ///////////////////////////////////////////////////////////// + // Other operations. + ///////////////////////////////////////////////////////////// + + /** + * Removes the message of the error which caused the VPN service to fail the last time it ran. + */ + public static void removeLastError() { + settings.edit().remove(LAST_ERROR).apply(); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNRunnable.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNRunnable.java new file mode 100644 index 0000000000..8eab908b09 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNRunnable.java @@ -0,0 +1,354 @@ +package com.skywire.skycoin.vpn.vpn; + +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.R; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.ObservableOnSubscribe; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.BehaviorSubject; +import skywiremob.Skywiremob; + +/** + * Class for configuring most of the VPN protection. After creating an instance, the start method + * can be used to start a series of steps for configuring the local visor and creating the VPN + * connection. Each instance can be used one time only, so a new instance must be created for + * starting the VPN protection again. + */ +public class VPNRunnable { + /** + * Current VPN work interface. + */ + private final VPNWorkInterface vpnInterface; + /** + * Object for controlling the local visor. + */ + private VisorRunnable visor; + /** + * Object for connecting the visor with the VPN work interface, to make the VPN functional. + */ + private SkywireVPNConnection vpnConnection; + + /** + * If the procedure to wait for the visor to be available already finished. + */ + private boolean waitAvailableFinished = false; + /** + * If the procedure to wait for having network connectivity already finished. + */ + private boolean waitNetworkFinished = false; + + /** + * If the disconnection procedure already started. + */ + private boolean disconnectionStarted = false; + /** + * Counts how many consecutive times the visor was detected as shut down while disconnecting. + */ + private int disconnectionVerifications = 0; + + /** + * Subject for informing about the state of the VPN protection. + */ + private final BehaviorSubject eventsSubject = BehaviorSubject.create(); + /** + * Subject for informing about the state of the VPN protection. + */ + private Observable eventsObservable; + + /** + * Msg string of the last error detected by this instance. + */ + private String lastErrorMsg; + + private Disposable waitingSubscription; + private Disposable visorTimeoutSubscription; + + /** + * Constructor. + * @param vpnInterface VPN work interface to use. This class will only configure it when + * stabilising the connection, so it will have to be configured before + * using this constructor if the network must be blocked before that. + * Also, this class will not unblock the network after disconnecting, that + * will have to be done by external code. + */ + public VPNRunnable(VPNWorkInterface vpnInterface) { + eventsSubject.onNext(VPNStates.OFF); + this.vpnInterface = vpnInterface; + } + + /** + * Starts the initialization procedure for the VPN protection, if it has not already + * been started. + * @return Observable for knowing the current state of the VPN protection. The operation is not + * started by the subscription, it starts just for calling the function, so there is no need + * for observing in another thread. + */ + public Observable start() { + if (eventsObservable == null) { + // Prepare for sending events. + eventsSubject.onNext(VPNStates.STARTING); + eventsObservable = eventsSubject.hide(); + } + + // Go to the first step. + waitForVisorToBeAvailableIfNeeded(); + + return eventsObservable; + } + + /** + * Allows to know if the initialization failed because the server refused the password. + */ + public boolean getIfPasswordFailed() { + return visor != null ? visor.getIfPasswordFailed() : false; + } + + /** + * Waits for the visor to be totally stopped. After that, goes to the next step for + * starting the VPN protection. If this step was already finished, the function does nothing. + */ + private void waitForVisorToBeAvailableIfNeeded() { + if (!waitAvailableFinished) { + // Avoid having multiple simultaneous procedures. + if (waitingSubscription != null) { + waitingSubscription.dispose(); + } + + // Check if the local visor is not running. If true, continue to the next step. + if (!Skywiremob.isVisorStarting() && !Skywiremob.isVisorRunning()) { + waitAvailableFinished = true; + checkInternetConnectionIfNeeded(true); + } else { + // Update the state. + if (eventsSubject.getValue() != VPNStates.WAITING_PREVIOUS_INSTANCE_STOP) { + Skywiremob.printString("WAITING FOR THE PREVIOUS INSTANCE TO BE FULLY STOPPED"); + eventsSubject.onNext(VPNStates.WAITING_PREVIOUS_INSTANCE_STOP); + } + + // Retry after a delay. + waitingSubscription = Observable.just(0).delay(1000, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> waitForVisorToBeAvailableIfNeeded()); + } + } + } + + /** + * Waits until there is connection via internet to at least one of the testing URLs set in the + * globals class. After that, goes to the next step for starting the VPN protection. If this + * step was already finished, the function does nothing. + * @param firstTry True if the function is not being called automatically by the function + * itself, to retry the operation. + */ + private void checkInternetConnectionIfNeeded(boolean firstTry) { + if (!waitNetworkFinished) { + Skywiremob.printString("CHECKING CONNECTION"); + + // Update the state. + if (firstTry) { + eventsSubject.onNext(VPNStates.CHECKING_CONNECTIVITY); + } + + // Avoid having multiple simultaneous procedures. + if (waitingSubscription != null) { + waitingSubscription.dispose(); + } + + // Check if there is connection. + waitingSubscription = HelperFunctions.checkInternetConnectivity(firstTry) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(hasInternetConnection -> { + if (hasInternetConnection) { + // Go to the next step. + waitNetworkFinished = true; + startVisorIfNeeded(); + } else { + eventsSubject.onNext(VPNStates.WAITING_FOR_CONNECTIVITY); + waitingSubscription.dispose(); + + // Retry after a delay. + waitingSubscription = Observable.just(0).delay(1000, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> checkInternetConnectionIfNeeded(false)); + } + }); + } + } + + /** + * Starts the local visor. After that, goes to the next step for starting the VPN protection. + * If this step was already started, the function does nothing. + */ + private void startVisorIfNeeded() { + if (visor == null) { + Skywiremob.printString("STARTING VISOR"); + + // Create the instance for managing the local visor. + visor = new VisorRunnable(); + + if (waitingSubscription != null) { + waitingSubscription.dispose(); + } + + // Start the local visor and listen to the state changes. + waitingSubscription = visor.runVisor() + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(state -> { + eventsSubject.onNext(state); + + // Create an observable which stops the operation if there is no progress after + // some time. The observable is reset after each state change. + if (visorTimeoutSubscription != null) { + visorTimeoutSubscription.dispose(); + } + visorTimeoutSubscription = Observable.just(0).delay(45000, TimeUnit.MILLISECONDS) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> { + // Cancel the operation. + HelperFunctions.logError("VPN service", "Timeout preparing the visor."); + putInErrorState(App.getContext().getString(R.string.vpn_timeout_error)); + }); + }, err -> { + // Report the error. + if (visorTimeoutSubscription != null) { + visorTimeoutSubscription.dispose(); + } + putInErrorState(err.getLocalizedMessage()); + }, () -> { + // Go to the next step. + visorTimeoutSubscription.dispose(); + startConnection(); + }); + } + } + + /** + * Starts the VPN connection, which finishes making the VPN protection functional. + */ + private void startConnection() { + if (vpnConnection == null) { + // Create the instance for managing the connection. + vpnConnection = new SkywireVPNConnection(visor, vpnInterface); + + waitingSubscription.dispose(); + + // Make the connection work. Also, check the state changes. + waitingSubscription = vpnConnection.getObservable() + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + val -> { + // Inform the state changes. + eventsSubject.onNext(val); + }, err -> { + // Close the connection (this does not means that the network + // will be unblocked) and inform about the error. + putInErrorState(err.getLocalizedMessage()); + }, () -> { + // This event is not expected, but it would mean that the vpn connection + // is not longer active. + HelperFunctions.logError("VPN connection ended unexpectedly", "VPN connection ended unexpectedly"); + disconnect(); + } + ); + } + } + + /** + * Reverts all the steps made by this class, which means closing the connection and stopping + * the visor. If the network connections were blocked, that does not change, as this function + * does not make changes to the VPN work interface. Calling this function again after the + * first call does nothing. + */ + public void disconnect() { + if (!disconnectionStarted) { + disconnectionStarted = true; + + Skywiremob.printString("DISCONNECTING VPN RUNNABLE"); + + // Inform the new state. + eventsSubject.onNext(VPNStates.DISCONNECTING); + + // Remove the subscriptions and close the vpn connection. + if (waitingSubscription != null) { + waitingSubscription.dispose(); + } + if (visorTimeoutSubscription != null) { + visorTimeoutSubscription.dispose(); + } + if (this.vpnConnection != null) { + this.vpnConnection.close(); + } + + // Stop the visor in another thread. + Observable.create((ObservableOnSubscribe) emitter -> { + if (visor != null) { + visor.startStoppingVisor(); + } + emitter.onComplete(); + }).subscribeOn(Schedulers.newThread()).subscribe(val -> {}); + + // Wait until the visor is completely stopped. 2 consecutive checks must be passed, + // to avoid a very unlikely but possible race condition. + Observable.timer(100, TimeUnit.MILLISECONDS).repeatUntil(() -> { + if (!Skywiremob.isVisorStarting() && !Skywiremob.isVisorRunning()) { + if (disconnectionVerifications == 2) { + return true; + } else { + disconnectionVerifications += 1; + } + } else { + if (disconnectionVerifications != 0) { + if (visor != null) { + visor.startStoppingVisor(); + } + } + + disconnectionVerifications = 0; + } + + return false; + }) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(val -> {}, err -> {}, () -> eventsSubject.onNext(VPNStates.DISCONNECTED)); + } + } + + /** + * Informs about an error and closes the VPN connection. + * @param errorMsg Msg string of the error. + */ + private void putInErrorState(String errorMsg) { + lastErrorMsg = errorMsg; + + // If the network is already blocked and the kill switch is active, inform that the + // current error will close the VPN connection but the network will still be blocked until + // the user stops the service manually. That behavior is not managed by this class. + if (!vpnInterface.alreadyConfigured() || !VPNGeneralPersistentData.getKillSwitchActivated()) { + eventsSubject.onNext(VPNStates.ERROR); + } else { + eventsSubject.onNext(VPNStates.BLOCKING_ERROR); + } + + disconnect(); + } + + /** + * Returns the msg of the last error detected by the current instance. + */ + public String getLastErrorMsg() { + return lastErrorMsg; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNServersPersistentData.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNServersPersistentData.java new file mode 100644 index 0000000000..3aaf0d4fc9 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNServersPersistentData.java @@ -0,0 +1,322 @@ +package com.skywire.skycoin.vpn.vpn; + +import android.content.SharedPreferences; + +import androidx.preference.PreferenceManager; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.activities.servers.VpnServerForList; +import com.skywire.skycoin.vpn.objects.LocalServerData; +import com.skywire.skycoin.vpn.objects.ManualVpnServerData; +import com.skywire.skycoin.vpn.objects.ServerFlags; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.subjects.ReplaySubject; + +/** + * Helper class for saving and getting data related to the VPN servers to and from the + * persistent storage. + */ +public class VPNServersPersistentData { + /** + * Singleton instance. + */ + private static final VPNServersPersistentData instance = new VPNServersPersistentData(); + /** + * Gets the singleton for using the class. + */ + public static VPNServersPersistentData getInstance() { return instance; } + + private final int maxHistoryElements = 30; + + // Keys for persistent storage. + private final String CURRENT_SERVER_PK = "serverPK"; + private final String SERVER_LIST = "serverList"; + + private SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(App.getContext()); + + private String currentServerPk; + private HashMap serversMap; + + private ReplaySubject currentServerSubject = ReplaySubject.createWithSize(1); + private ReplaySubject> historySubject = ReplaySubject.createWithSize(1); + private ReplaySubject> favoritesSubject = ReplaySubject.createWithSize(1); + private ReplaySubject> blockedSubject = ReplaySubject.createWithSize(1); + + private VPNServersPersistentData() { + currentServerPk = settings.getString(CURRENT_SERVER_PK, ""); + + String serversList = settings.getString(SERVER_LIST, null); + if (serversList != null) { + Gson gson = new Gson(); + Type mapType = new TypeToken>() {}.getType(); + serversMap = gson.fromJson(serversList, mapType); + + LocalServerData currentServer = this.serversMap.get(currentServerPk); + this.currentServerSubject.onNext(currentServer != null ? currentServer : new LocalServerData()); + } else { + serversMap = new HashMap<>(); + this.currentServerSubject.onNext(new LocalServerData()); + } + + this.launchListEvents(); + } + + public LocalServerData getCurrentServer() { + return serversMap.get(this.currentServerPk); + } + + public Observable getCurrentServerObservable() { + return currentServerSubject.hide(); + } + + public Observable> history() { + return this.historySubject.hide(); + } + + public Observable> favorites() { + return this.favoritesSubject.hide(); + } + + public Observable> blocked() { + return this.blockedSubject.hide(); + } + + public LocalServerData getSavedVersion(String pk) { + return this.serversMap.get(pk); + } + + public void updateFromDiscovery(ArrayList serverList) { + for (VpnServerForList server : serverList) { + if (this.serversMap.containsKey(server.pk)) { + LocalServerData savedServer = this.serversMap.get(server.pk); + + savedServer.countryCode = server.countryCode; + savedServer.name = server.name; + savedServer.location = server.location; + savedServer.note = server.note; + } + } + + this.saveData(); + } + + public void updateServer(LocalServerData server) { + this.serversMap.put(server.pk, server); + this.cleanServers(); + this.saveData(); + } + + public LocalServerData processFromList(VpnServerForList newServer) { + LocalServerData retrievedServer = this.serversMap.get(newServer.pk); + if (retrievedServer != null) { + retrievedServer.countryCode = newServer.countryCode; + retrievedServer.name = newServer.name; + retrievedServer.location = newServer.location; + retrievedServer.note = newServer.note; + + this.saveData(); + + return retrievedServer; + } + + LocalServerData response = new LocalServerData(); + response.countryCode = newServer.countryCode; + response.name = newServer.name; + response.customName = null; + response.pk = newServer.pk; + response.lastUsed = new Date(0); + response.inHistory = false; + response.flag = ServerFlags.None; + response.location = newServer.location; + response.personalNote = null; + response.note = newServer.note; + response.enteredManually = false; + response.password = null; + + return response; + } + + public LocalServerData processFromManual(ManualVpnServerData newServer) { + LocalServerData retrievedServer = this.serversMap.get(newServer.pk); + if (retrievedServer != null) { + retrievedServer.password = newServer.password; + retrievedServer.customName = newServer.name; + retrievedServer.personalNote = newServer.note; + retrievedServer.enteredManually = true; + + this.saveData(); + + return retrievedServer; + } + + LocalServerData response = new LocalServerData(); + response.countryCode = "zz"; + response.name = null; + response.customName = newServer.name; + response.pk = newServer.pk; + response.lastUsed = new Date(0); + response.inHistory = false; + response.flag = ServerFlags.None; + response.location = null; + response.personalNote = newServer.note; + response.note = null; + response.enteredManually = true; + response.password = newServer.password; + + return response; + } + + public void changeFlag(LocalServerData server, ServerFlags flag) { + LocalServerData retrievedServer = this.serversMap.get(server.pk); + if (retrievedServer != null) { + server = retrievedServer; + } + + if (server.flag == flag) { + return; + } + server.flag = flag; + + if (!this.serversMap.containsKey(server.pk)) { + this.serversMap.put(server.pk, server); + } + + this.cleanServers(); + this.saveData(); + } + + public void removePassword(String pk) { + LocalServerData retrievedServer = this.serversMap.get(pk); + if (retrievedServer == null || retrievedServer.password == null || retrievedServer.password.equals("")) { + return; + } + + retrievedServer.password = null; + this.cleanServers(); + this.saveData(); + } + + public void removeFromHistory(String pk) { + LocalServerData retrievedServer = this.serversMap.get(pk); + if (retrievedServer == null || !retrievedServer.inHistory) { + return; + } + + retrievedServer.inHistory = false; + this.cleanServers(); + this.saveData(); + } + + public void modifyCurrentServer(LocalServerData newServer) { + if (!this.serversMap.containsKey(newServer.pk)) { + this.serversMap.put(newServer.pk, newServer); + } + + this.currentServerPk = newServer.pk; + + LocalServerData currentServer = this.serversMap.get(currentServerPk); + this.currentServerSubject.onNext(currentServer); + + this.cleanServers(); + this.saveData(); + } + + public void updateHistory() { + LocalServerData currentServer = this.serversMap.get(currentServerPk); + // This should not happen. + if (currentServer == null) { + return; + } + + currentServer.lastUsed = new Date(); + currentServer.inHistory = true; + + // Make a list with the servers in the history and sort it by usage date. + ArrayList historyList = new ArrayList(); + for (LocalServerData server : serversMap.values()) { + if (server.inHistory) { + historyList.add(server); + } + } + Comparator comparator = (a, b) -> (int)((b.lastUsed.getTime() - a.lastUsed.getTime()) / 1000); + Collections.sort(historyList, comparator); + + // Remove from the history the old servers. + int historyElementsFound = 0; + for (LocalServerData server : historyList) { + if (historyElementsFound < this.maxHistoryElements) { + historyElementsFound += 1; + } else { + server.inHistory = false; + } + } + + this.cleanServers(); + this.saveData(); + } + + private void cleanServers() { + ArrayList unneeded = new ArrayList(); + for (LocalServerData server : serversMap.values()) { + if ( + !server.inHistory && + server.flag == ServerFlags.None && + !server.pk.equals(this.currentServerPk) && + (server.customName == null || server.customName.equals("")) && + (server.personalNote == null || server.personalNote.equals("")) + ) { + unneeded.add(server.pk); + } + } + + for (String pk : unneeded) { + this.serversMap.remove(pk); + } + } + + private void saveData() { + Gson gson = new Gson(); + String servers = gson.toJson(serversMap); + + settings + .edit() + .putString(SERVER_LIST, servers) + .putString(CURRENT_SERVER_PK, currentServerPk) + .apply(); + + this.launchListEvents(); + } + + private void launchListEvents() { + ArrayList history = new ArrayList(); + ArrayList favorites = new ArrayList(); + ArrayList blocked = new ArrayList(); + + for (LocalServerData server : serversMap.values()) { + if (server.inHistory) { + history.add(server); + } + if (server.flag == ServerFlags.Favorite) { + favorites.add(server); + } + if (server.flag == ServerFlags.Blocked) { + blocked.add(server); + } + } + + this.historySubject.onNext(history); + this.favoritesSubject.onNext(favorites); + this.blockedSubject.onNext(blocked); + this.currentServerSubject.onNext(currentServerSubject.getValue()); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNStates.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNStates.java new file mode 100644 index 0000000000..dd50fbc1c3 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNStates.java @@ -0,0 +1,267 @@ +package com.skywire.skycoin.vpn.vpn; + +import com.skywire.skycoin.vpn.R; + +import java.util.HashMap; + +/** + * Helper class with the possible states of the VPN service. + * + * The states are numeric constants, similar to how http status codes work, to be able to identify + * state groups just by numeric ranges. The ranges are: + * + * State < 10: the service is not running. + * + * 10 =< State < 100: The VPN connection is being prepared. + * + * 100 =< State < 150: The VPN connection has been made and the internet connectivity should + * be protected and working. + * + * 150 =< State < 200: Temporal errors with the VPN connection. + * + * 200 =< State < 300: Closing the VPN connection/service. + * + * 300 =< State < 400: VPN connection/service closed. + * + * State >= 400 : An error occurred. + */ +public enum VPNStates { + /** + * The service is off. + */ + OFF(1), + /** + * Starting the service. + */ + STARTING(10), + /** + * Waiting for the visor to be completely stopped before starting it again. + */ + WAITING_PREVIOUS_INSTANCE_STOP(12), + /** + * Checking for the first time if the device has internet connectivity. + */ + CHECKING_CONNECTIVITY(15), + /** + * No internet connectivity was found and the service is checking again periodically. + */ + WAITING_FOR_CONNECTIVITY(16), + /** + * Starting the Skywire visor. + */ + PREPARING_VISOR(20), + /** + * Starting the VPN client, which is part of Skywiremob and running as part of the visor. + */ + PREPARING_VPN_CLIENT(30), + /** + * Making final preparations for the VPN client, like performing the handshake and start serving. + */ + FINAL_PREPARATIONS_FOR_VISOR(35), + /** + * The visor and VPN client are ready. Preparations may be needed in the app side. + */ + VISOR_READY(40), + /** + * The VPN connection has been fully established and secure internet connectivity should + * be available. + */ + CONNECTED(100), + /** + * There was an error with the VPN connection and it is being restored automatically. + */ + RESTORING_VPN(150), + /** + * There was an error and the whole VPN service is being restored automatically. + */ + RESTORING_SERVICE(155), + /** + * The VPN service is being stopped. + */ + DISCONNECTING(200), + /** + * The VPN service has been stopped. + */ + DISCONNECTED(300), + /** + * There has been an error, the VPN connection is not available and the service is + * being stopped. + */ + ERROR(400), + /** + * There has been and error and the VPN connection is not available. The network will remain + * blocked until the user stops the service manually. + */ + BLOCKING_ERROR(410); + + /** + * Allows to easily get the value related to an specific number. + */ + private static HashMap numericValues; + + // Initializes the enum and saves the value. + private final int val; + VPNStates(int val) { + this.val = val; + } + + /** + * Gets the associated numeric value. + */ + public int val() { + return val; + } + + /** + * Class with details about the state of the VPN service. + */ + public static class StateInfo { + /** + * Current state of the service. + */ + public final VPNStates state; + /** + * If the service was started by the OS, which means that the OS is responsible for + * stopping it. + */ + public final boolean startedByTheSystem; + /** + * If the user already requested the service to be stopped. + */ + public final boolean stopRequested; + + public StateInfo(VPNStates state, boolean startedByTheSystem, boolean stopRequested) { + this.state = state; + this.startedByTheSystem = startedByTheSystem; + this.stopRequested = stopRequested; + } + } + + /** + * Allows to get the resource ID of the string with the title for a state of the + * VPN service. If no resource is found for the state, -1 is returned. + */ + public static int getTitleForState(VPNStates state) { + if (state == OFF) { + return R.string.vpn_state_disconnected; + } else if (state == STARTING) { + return R.string.vpn_state_connecting; + } else if (state == WAITING_PREVIOUS_INSTANCE_STOP) { + return R.string.vpn_state_connecting; + } else if (state == CHECKING_CONNECTIVITY) { + return R.string.vpn_state_connecting; + } else if (state == WAITING_FOR_CONNECTIVITY) { + return R.string.vpn_state_connecting; + } else if (state == PREPARING_VISOR) { + return R.string.vpn_state_connecting; + } else if (state == PREPARING_VPN_CLIENT) { + return R.string.vpn_state_connecting; + } else if (state == FINAL_PREPARATIONS_FOR_VISOR) { + return R.string.vpn_state_connecting; + } else if (state == VISOR_READY) { + return R.string.vpn_state_connecting; + } else if (state == CONNECTED) { + return R.string.vpn_state_connected; + } else if (state == RESTORING_VPN) { + return R.string.vpn_state_restarting; + } else if (state == RESTORING_SERVICE) { + return R.string.vpn_state_restarting; + } else if (state == DISCONNECTING) { + return R.string.vpn_state_disconnecting; + } else if (state == DISCONNECTED) { + return R.string.vpn_state_disconnected; + } else if (state == ERROR) { + return R.string.vpn_state_error; + } else if (state == BLOCKING_ERROR) { + return R.string.vpn_state_error; + } + + return -1; + } + + /** + * Allows to get the resource ID of the color for the title of a state of the + * VPN service. If no resource is found for the title, red is returned. + */ + public static int getColorForStateTitle(int titleResource) { + if (titleResource == R.string.vpn_state_disconnected) { + return R.color.red; + } else if (titleResource == R.string.vpn_state_connecting) { + return R.color.yellow; + } else if (titleResource == R.string.vpn_state_connected) { + return R.color.green; + } else if (titleResource == R.string.vpn_state_restarting) { + return R.color.yellow; + } else if (titleResource == R.string.vpn_state_disconnecting) { + return R.color.yellow; + } else if (titleResource == R.string.vpn_state_error) { + return R.color.red; + } + + return R.color.red; + } + + /** + * Allows to get the resource ID of the string with the description of a state of the + * VPN service. If no resource is found for the state, -1 is returned. + */ + public static int getDescriptionForState(VPNStates state) { + if (state == OFF) { + return R.string.vpn_state_details_off; + } else if (state == STARTING) { + return R.string.vpn_state_details_initializing; + } else if (state == WAITING_PREVIOUS_INSTANCE_STOP) { + return R.string.vpn_state_details_waiting_previous_instance_stop; + } else if (state == CHECKING_CONNECTIVITY) { + return R.string.vpn_state_details_checking_connectivity; + } else if (state == WAITING_FOR_CONNECTIVITY) { + return R.string.vpn_state_details_waiting_connectivity; + } else if (state == PREPARING_VISOR) { + return R.string.vpn_state_details_starting_visor; + } else if (state == PREPARING_VPN_CLIENT) { + return R.string.vpn_state_details_starting_vpn_app; + } else if (state == FINAL_PREPARATIONS_FOR_VISOR) { + return R.string.vpn_state_details_additional_visor_initializations; + } else if (state == VISOR_READY) { + return R.string.vpn_state_details_connecting; + } else if (state == CONNECTED) { + return R.string.vpn_state_details_connected; + } else if (state == RESTORING_VPN) { + return R.string.vpn_state_details_restoring; + } else if (state == RESTORING_SERVICE) { + return R.string.vpn_state_details_restoring_service; + } else if (state == DISCONNECTING) { + return R.string.vpn_state_details_disconnecting; + } else if (state == DISCONNECTED) { + return R.string.vpn_state_details_disconnected; + } else if (state == ERROR) { + return R.string.vpn_state_details_error; + } else if (state == BLOCKING_ERROR) { + return R.string.vpn_state_details_blocking_error; + } + + return -1; + } + + /** + * Allows to get the value associated with a numeric value. If there is no value for the + * provided number, the OFF state is returned. + * @param value Value to check. + */ + public static VPNStates valueOf(int value) { + // Initialize the map for getting the values, if needed. + if (numericValues == null) { + numericValues = new HashMap<>(); + + for (VPNStates v : VPNStates.values()) { + numericValues.put(v.val(), v); + } + } + + if (!numericValues.containsKey(value)) { + return OFF; + } + + return numericValues.get(value); + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNWorkInterface.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNWorkInterface.java new file mode 100644 index 0000000000..d611cb0dcb --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VPNWorkInterface.java @@ -0,0 +1,242 @@ +package com.skywire.skycoin.vpn.vpn; + +import android.net.VpnService; +import android.os.ParcelFileDescriptor; + +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.helpers.Globals; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.R; + +import java.io.Closeable; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashSet; + +import skywiremob.Skywiremob; + +/** + * Object used for starting the VPN protection and sending/receiving data. After created, to start + * the VPN protection the object must be configured. + */ +public class VPNWorkInterface implements Closeable { + /** + * Modes in which the VPN interface can be configured. + */ + public enum Modes { + /** + * Used just for blocking the network connectivity before configuring the visor, to avoid + * data leaks. + */ + BLOCKING, + /** + * Normal mode for sending and receiving data using the VPN protection. + */ + WORKING, + /** + * Mode used just for configuring a VPN interface and closing it immediately after that, to + * force the OS to disable the VPN protection, due to a bug in old Android versions. + */ + DELETING, + } + + /** + * Current VPN service instance. + */ + private final VpnService service; + /** + * Current VPN communication object, created by the system. + */ + private ParcelFileDescriptor vpnInterface = null; + /** + * Input stream to be used with the current communication object created by the system. + */ + private FileInputStream inStream = null; + /** + * Output stream to be used with the current communication object created by the system. + */ + private FileOutputStream outStream = null; + + public VPNWorkInterface(VpnService service) { + this.service = service; + } + + /** + * Terminates the VPN protections and cleans the used resources. + */ + @Override + public void close() { + if (vpnInterface != null) { + try { + vpnInterface.close(); + vpnInterface = null; + } catch (IOException e) { + HelperFunctions.logError("Unable to close interface", e); + } + + cleanInputStream(); + cleanOutputStream(); + } + } + + /** + * Checks if the interface has already been configured for the first time. + */ + public boolean alreadyConfigured() { + return vpnInterface != null; + } + + /** + * Configures and activates the VPN interface. After calling this function the OS starts + * routing the data using the interface, so all network connections will be blocked if the VPN + * is not working properly. This method can be called several times, which allows to restore + * the connection in case of errors or change the mode. + * @param mode Mode in which the VPN interface will be configured. + */ + public void configure(Modes mode) throws Exception { + // Save a reference to the current interface, if any, to close it after creating the + // new one, to avoid leaking data while the new interface is created. + ParcelFileDescriptor oldVpnInterface = null; + if (vpnInterface != null) { + oldVpnInterface = vpnInterface; + } + + // Create and configure a builder. + VpnService.Builder builder = service.new Builder(); + builder.setMtu((short)Skywiremob.getMTU()); + if (mode == Modes.WORKING) { + Skywiremob.printString("TUN IP: " + Skywiremob.tunip()); + // Get the address from the visor. + builder.addAddress(Skywiremob.tunip(), (int) Skywiremob.getTUNIPPrefix()); + } else { + // Use an address for blocking all connections. + builder.addAddress("8.8.8.8", 32); + } + + // Use the custom DNS server, if any. + String dnsServer = VPNGeneralPersistentData.getCustomDns(); + if (dnsServer != null && dnsServer.trim().length() > 0) { + builder.addDnsServer(dnsServer.trim()); + } + + builder.addRoute("0.0.0.0", 0); + // This makes the streams created with the interface synchronous, so that the data can be + // read blocking an independent thread in an efficient way. + builder.setBlocking(true); + + // Allows to know if there was an error allowing or disallowing apps. + boolean errorIgnoringApps = false; + + if (mode == Modes.WORKING || mode == Modes.BLOCKING) { + String upperCaseAppPackage = App.getContext().getPackageName().toUpperCase(); + Globals.AppFilteringModes appsSelectionMode = VPNGeneralPersistentData.getAppsSelectionMode(); + + if (appsSelectionMode != Globals.AppFilteringModes.PROTECT_ALL) { + // Get the package name of all the apps selected by the user which are + // currently installed. + for (String packageName : HelperFunctions.filterAvailableApps(VPNGeneralPersistentData.getAppList(new HashSet<>()))) { + try { + if (appsSelectionMode == Globals.AppFilteringModes.PROTECT_SELECTED) { + // Protect all selected apps, but ignore this app. + if (!upperCaseAppPackage.equals(packageName.toUpperCase())) { + builder.addAllowedApplication(packageName); + } + } else { + // Avoid protecting the selected apps, but ignore this app. + if (!upperCaseAppPackage.equals(packageName.toUpperCase())) { + builder.addDisallowedApplication(packageName); + } + } + } catch (Exception e) { + errorIgnoringApps = true; + HelperFunctions.logError("Unable to add " + packageName + " to the VPN service", e); + break; + } + } + } + + // Make the VPN protection ignore this app, as free access is needed for configuring + // the visor, specially in case of errors, when it is needed to restart components. + if (!errorIgnoringApps) { + try { + if (appsSelectionMode != Globals.AppFilteringModes.PROTECT_SELECTED) { + builder.addDisallowedApplication(App.getContext().getPackageName()); + } + } catch (Exception e) { + errorIgnoringApps = true; + HelperFunctions.logError("Unable to add VPN app rule to the VPN service", e); + } + } + } else { + // Block this app only, to be able to avoid a bug in old Android versions. + builder.addAllowedApplication(App.getContext().getPackageName()); + } + + if (errorIgnoringApps) { + throw new Exception(App.getContext().getString(R.string.vpn_service_configuring_app_rules_error)); + } + + // Create the new interface using the builder. + builder.setConfigureIntent(HelperFunctions.getOpenAppPendingIntent()); + synchronized (service) { + vpnInterface = builder.establish(); + } + Skywiremob.printString("New interface: " + vpnInterface); + + // Close the previous interface and streams, if any. + if (oldVpnInterface != null) { + oldVpnInterface.close(); + } + cleanInputStream(); + cleanOutputStream(); + } + + /** + * Gets the input stream for reading the packages from the system that must be sent using the + * VPN. NOTE: if the interface is closed or configured again, the stream is closed. + */ + public FileInputStream getInputStream() { + if (inStream == null) { + inStream = new FileInputStream(vpnInterface.getFileDescriptor()); + } + return inStream; + } + + /** + * Gets the output stream that must be used for sending to the system the packages received via + * the VPN. NOTE: if the interface is closed or configured again, the stream is closed. + */ + public FileOutputStream getOutputStream() { + if (outStream == null) { + outStream = new FileOutputStream(vpnInterface.getFileDescriptor()); + } + return outStream; + } + + /** + * Cleans and removes the current input stream, if any. + */ + private void cleanInputStream() { + if (inStream != null) { + try { + inStream.close(); + } catch (Exception e) { } + + inStream = null; + } + } + + /** + * Cleans and removes the current output stream, if any. + */ + private void cleanOutputStream() { + if (outStream != null) { + try { + outStream.close(); + } catch (Exception e) { } + + outStream = null; + } + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VisorRunnable.java b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VisorRunnable.java new file mode 100644 index 0000000000..a5614e5337 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/java/com/skywire/skycoin/vpn/vpn/VisorRunnable.java @@ -0,0 +1,218 @@ +package com.skywire.skycoin.vpn.vpn; + +import com.skywire.skycoin.vpn.App; +import com.skywire.skycoin.vpn.R; +import com.skywire.skycoin.vpn.helpers.HelperFunctions; +import com.skywire.skycoin.vpn.objects.LocalServerData; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.ObservableEmitter; +import io.reactivex.rxjava3.core.ObservableOnSubscribe; +import skywiremob.Skywiremob; + +/** + * Allows to easily control the starting and stopping procedures of the the visor and VPN client + * included in Skywiremob. + */ +public class VisorRunnable { + /** + * If Skywiremob.prepareVPNClient has already been called without errors. + */ + private boolean vpnClientStarted = false; + /** + * If Skywiremob.startListeningUDP() has already been called without errors. + */ + private boolean listeningUdp = false; + /** + * If true, the initialization failed because the server refused the password. + */ + private boolean passwordFailed = false; + + /** + * Allows to know if the initialization failed because the server refused the password. + */ + public boolean getIfPasswordFailed() { + return passwordFailed; + } + + /** + * Starts stopping the visor. It returns before the visor has been completely stopped. + */ + public void startStoppingVisor() { + skywiremob.Error err = Skywiremob.stopVisor(); + if (err.getCode() != Skywiremob.ErrCodeNoError) { + Skywiremob.printString(gerErrorMsg(err)); + HelperFunctions.showToast(gerErrorMsg(err), false); + } + Skywiremob.printString("Visor stopped"); + } + + /** + * Stops the VPN client without stopping the visor. + */ + public void stopVpnConnection() { + if (vpnClientStarted) { + Skywiremob.stopVPNClient(); + vpnClientStarted = false; + } + if (listeningUdp) { + Skywiremob.stopListeningUDP(); + listeningUdp = false; + } + Skywiremob.printString("VPN connection stopped"); + } + + /** + * Starts the Skywire visor. + * @return Observable that will emit the current state of the process, as variables defined in + * VPNStates, and will complete after starting the visor. + */ + public Observable runVisor() { + return Observable.create((ObservableOnSubscribe) emitter -> { + if (emitter.isDisposed()) { return; } + emitter.onNext(VPNStates.PREPARING_VISOR); + + // Start the visor if the emitter is still valid. + if (emitter.isDisposed()) { return; } + skywiremob.Error err = Skywiremob.prepareVisor(); + if (err.getCode() != Skywiremob.ErrCodeNoError) { + HelperFunctions.logError("Visor startup procedure, code " + err.getCode(), gerErrorMsg(err)); + if (emitter.isDisposed()) { return; } + emitter.onError(new Exception(gerErrorMsg(err))); + return; + } + + // Block the thread while the visor is starting. + err = Skywiremob.waitVisorReady(); + if (err.getCode() != Skywiremob.ErrCodeNoError) { + HelperFunctions.logError("Visor startup procedure, code " + err.getCode(), gerErrorMsg(err)); + if (emitter.isDisposed()) { return; } + emitter.onError(new Exception(gerErrorMsg(err))); + return; + } + + // Finish. + Skywiremob.printString("Prepared visor"); + if (emitter.isDisposed()) { return; } + emitter.onNext(VPNStates.VISOR_READY); + emitter.onComplete(); + }); + } + + /** + * Starts the VPN client. This function was made to be used inside an observable which emits + * the state of the VPN service. + * @param parentEmitter Emitter of the observable from which this function was called, to be + * able to emit the state changes. + */ + public void runVpnClient(ObservableEmitter parentEmitter) throws Exception { + passwordFailed = false; + + // Update the state. + if (parentEmitter.isDisposed()) { return; } + parentEmitter.onNext(VPNStates.PREPARING_VPN_CLIENT); + + // Prepare the VPN client with the last saved public key and password. + if (parentEmitter.isDisposed()) { return; } + LocalServerData currentServer = VPNServersPersistentData.getInstance().getCurrentServer(); + String savedPk = currentServer != null ? currentServer.pk : ""; + String savedPassword = currentServer != null && currentServer.password != null ? currentServer.password : ""; + skywiremob.Error err = Skywiremob.prepareVPNClient(savedPk, savedPassword); + if (err.getCode() != Skywiremob.ErrCodeNoError) { + throw new Exception(gerErrorMsg(err)); + } + vpnClientStarted = true; + Skywiremob.printString("Prepared VPN client"); + if (parentEmitter.isDisposed()) { return; } + parentEmitter.onNext(VPNStates.FINAL_PREPARATIONS_FOR_VISOR); + + // Perform the handshake. + if (parentEmitter.isDisposed()) { return; } + err = Skywiremob.shakeHands(); + if (err.getCode() != Skywiremob.ErrCodeNoError) { + // Check if the server refused the password. + if (err.getCode() == Skywiremob.ErrCodeHandshakeFailed && err.getError().toUpperCase().contains("4 (Forbidden)".toUpperCase())) { + passwordFailed = true; + } + throw new Exception(gerErrorMsg(err)); + } + + // Start listening. + if (parentEmitter.isDisposed()) { return; } + err = Skywiremob.startListeningUDP(); + listeningUdp = true; + if (err.getCode() != Skywiremob.ErrCodeNoError) { + throw new Exception(gerErrorMsg(err)); + } + + // Start serving. + if (parentEmitter.isDisposed()) { return; } + err = Skywiremob.serveVPN(); + if (err.getCode() != Skywiremob.ErrCodeNoError) { + throw new Exception(gerErrorMsg(err)); + } + } + + /** + * Gets the error string for an specific error returned by Skywiremob. + */ + private static String gerErrorMsg(skywiremob.Error error) { + int resource = -1; + + if (error.getCode() == Skywiremob.ErrCodeInvalidPK) { + resource = R.string.skywiremob_error_invalid_pk; + } else if (error.getCode() == Skywiremob.ErrCodeInvalidVisorConfig) { + resource = R.string.skywiremob_error_invalid_visor_config; + } else if (error.getCode() == Skywiremob.ErrCodeInvalidAddrResolverURL) { + resource = R.string.skywiremob_error_invalid_addr_resolver_url; + } else if (error.getCode() == Skywiremob.ErrCodeSTCPInitFailed) { + resource = R.string.skywiremob_error_stcp_init_failed; + } else if (error.getCode() == Skywiremob.ErrCodeSTCPRInitFailed) { + resource = R.string.skywiremob_error_stcpr_init_failed; + } else if (error.getCode() == Skywiremob.ErrCodeSUDPHInitFailed) { + resource = R.string.skywiremob_error_sudph_init_failed; + } else if (error.getCode() == Skywiremob.ErrCodeDmsgListenFailed) { + resource = R.string.skywiremob_error_dmsg_listen_failed; + } else if (error.getCode() == Skywiremob.ErrCodeTpDiscUnavailable) { + resource = R.string.skywiremob_error_tp_disc_unavailable; + } else if (error.getCode() == Skywiremob.ErrCodeFailedToStartRouter) { + resource = R.string.skywiremob_error_failed_to_start_router; + } else if (error.getCode() == Skywiremob.ErrCodeFailedToSetupHVGateway) { + resource = R.string.skywiremob_error_failed_to_setup_hv_gateway; + } else if (error.getCode() == Skywiremob.ErrCodeVisorNotRunning) { + resource = R.string.skywiremob_error_visor_not_running; + } else if (error.getCode() == Skywiremob.ErrCodeInvalidRemotePK) { + resource = R.string.skywiremob_error_invalid_remote_pk; + } else if (error.getCode() == Skywiremob.ErrCodeFailedToSaveTransport) { + resource = R.string.skywiremob_error_failed_to_save_transport; + } else if (error.getCode() == Skywiremob.ErrCodeVPNServerUnavailable) { + resource = R.string.skywiremob_error_vpn_server_unavailable; + } else if (error.getCode() == Skywiremob.ErrCodeVPNClientNotRunning) { + resource = R.string.skywiremob_error_vpn_client_not_running; + } else if (error.getCode() == Skywiremob.ErrCodeHandshakeFailed) { + if (error.getError().toUpperCase().contains("4 (Forbidden)".toUpperCase())) { + resource = R.string.skywiremob_error_wrong_password; + } else { + resource = R.string.skywiremob_error_handshake_failed; + } + } else if (error.getCode() == Skywiremob.ErrCodeInvalidAddr) { + resource = R.string.skywiremob_error_invalid_addr; + } else if (error.getCode() == Skywiremob.ErrCodeAlreadyListeningUDP) { + resource = R.string.skywiremob_error_already_listening_udp; + } else if (error.getCode() == Skywiremob.ErrCodeUDPListenFailed) { + resource = R.string.skywiremob_error_udp_listen_failed; + } + + String response; + if (resource != -1) { + response = App.getContext().getString(resource); + } else { + response = error.getError(); + if (response == null || response.trim().equals("")) { + response = App.getContext().getString(R.string.skywiremob_error_unknown); + } + } + + return response; + } +} diff --git a/cmd/skywirevisormobile/android/app/src/main/res/animator/anim_start_button.xml b/cmd/skywirevisormobile/android/app/src/main/res/animator/anim_start_button.xml new file mode 100644 index 0000000000..582fa05dd9 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/animator/anim_start_button.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/animator/anim_state.xml b/cmd/skywirevisormobile/android/app/src/main/res/animator/anim_state.xml new file mode 100644 index 0000000000..103fd50373 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/animator/anim_state.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/bronze_rating.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/bronze_rating.png new file mode 100644 index 0000000000000000000000000000000000000000..69db4edda45e8b349fb7c5369cc7ea4edb82a3a8 GIT binary patch literal 1950 zcmaJ?dsGu=9u1MjfU6*;R9-p`O5jS}-QcxQ$ww7jvPN>-aQFhLm`5x!q`+MDU zW|uT^)oiZ-FA|9~J3dY%Bh~=dOYtDSCsrqP5{o|`E60BEprn;5EC&VRwTe7c zhALDA8#~ZQ5@|-LIz^7lB?){bre!Ex7=~G^C)gxXWRzL2P-;*d$U*beIsxeT_!0=H zRRS=LErBI^A)2p_D>R_Vg^4N3LXDEA0;5&{k!C(2pha;7VAgKX8Tn=bIH}7g_O5FP z1STQ4Mgabjlw2YOgqQ&Z*bFvZ2{RFZ!($+9HiyGp1~6fS1;Ge}gwqiopT*+CF!1UD ziD(8@E?*`Rzluei1Ykan>-i95GMN~ra0X_`gAg8%2f<8;$)pnqy0JiqE6jAAF=R?X zgc_9wwH{YvI>4o<$iX(@0+7h`mlCx4*Rnd}t2Pl0gUkv&gfL)NNmD?HPz8<|QZQ`8R2QZB7>*h9F+Cs@PIe8T zrKxo)%w$|T$&pC-@j4@}&?(V)kpLuA7;3eO&y5grnd}%Qlf~j9NNfz3%MRypnc*=k z7BiL|flP5lm~xXA)!|cI)vw&}X}K;tX!S&75o%Dsi>ky1Obbkw%~wyK3p}mfE3Rt# zT##wG5RnYz8t!j~J#|Gm$hCcKUE=WC{HTs_yn(Pb+~W3Lk02Kf0xn{TK4>>)3N;SLC%uDye+#{C9Vl}yi=ZL_jdc# z+`nz7!%3#hr~G)z(>~We)H8H<>-b)2*pbaoar;-sHq)70ji&5q%*hz${nM=;S7wbB zjgN0DGl^gL+DhW0T6Num=%<4En(Wxv9UFsIwOX$6Up}dD z_?1zH4ewRkG9#4C>T*hBQM_fSJ)sk|x3U_x74i1vv2!Y>idbZx7nfu*}y#{b+Azg6Q8RUF|EzGAQ_l&RS zTd8SjWfkI;4{23mYqx)M_U=}TgZq;!tWXVk9Qx1{jr$+}a!smVZ^=TbX#HX!DKK#zmHPIY3U6@cvu~|4ETP|9W>8fD zRo16RmU`@7-O(}kPBNJ>v#pc&D*ybxd)?IZ8WY@tw^Ovz9lzFS1 z)UF#rzN`7bxJPaB;_iIbb^S-B4aemp=Pqu@#4Iu9D2*^*R^Klw9e%&)L(Q?5Ih0xd z`~#}}qPMqUKAcxNC~#}M{787HvGx&v`0Kl2zT&2y>&Zu)`=Y%j9+n6FyLLR#8`{pK z<|Hz{J}(vr?vVx8?@xF$BtCS%;mYM*NOqri4#Jrr|L~@uwkFGvK}p(oirbzpa-UV4 z)Y#ZqB?4Q{m-#Fl@&DV#U|ao-ZRfnfkfQ!YC(_6lj`pzLTO)VW=oeGdGcv5t2TF!F z!^jKa9Jeh&_sZ*cjx7GxP1G>mVy6v&i;pK zi#`CKdCWZXLVbJW)t=F(hg(@m#V5XCFQS!?ZLiu2Jw7mR%zJ=**vDDAy`dOOAcWp~G$7g;} zRo2U_@>@&C-oJS|x0vDc$AM>FAaBM)kDAcq&D6e|mZAW2dsM~Rs3rGgMHBAxNri4= UaDtrfbp0{oV-rOuW3nwj0|&|js{jB1 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/gold_rating.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/gold_rating.png new file mode 100644 index 0000000000000000000000000000000000000000..59c43cd7134fb8259bbb7e1f81c6851da67b6de3 GIT binary patch literal 2314 zcmaJ@X;@PE9>ygrmvZUJw2jbAQ+7x}$OR-Ru|g2Xv_d#48XSUS0ydL5X_H$zWm(!L zrK!23WTsAKYDwkfnyKS*wWiYQwwYQ}4K~@mA8z+N&pH3)dEfW9y&ukjP5$f5jBSl! zFqj$Bm(JF$w$tw%Bi*-fz2EP;#Rl>Tf&##BD3&h+U|vEn41hDm{0M*z@P*2qT7UwB z890bIK~NCOk1PPiDE>4Cr4UPXY#5B2HW6ir3^ zlT;9E6PyOh05}mvL<%rCEZmiZ!V-zDt~eJs4ud72F<3MfkHnJ51Ogd@fq#7vx@aa5*4uO;_B@kbMl*sL76zG6l zAQMR;5h#IAEAqp@7>J6{W%^GEV(B+oiTrDubPYo*_);_$g_$mC2FPOlf2dge4K0V* zz<=`npTcsEQVO8ifEF z{BAB-pYL+fx@6GP!~L&e&s^yoG`;<1UESfE`2mT}@iLvYo7;{Bz+kgfOu83G(R=s^ z^B{-2C~450!<|nM|7`D2Vs9%|ANZt~y2Y9?V$5r-j?WmjyXV zSgd^KG)2yoC&LG%l6RHL#1{8D${p3*Dch;I+^So8OS#8S{oIMKphCOK%aJ`SXkII5 z;h7b-@uU<)-z!i~p`u~G{bhfe88SigTbuDzy~O(P9astNY+1|J!iv%?ZDCjW@g3xI zBHUXk*&^iag=a&SNp+2P@V-=|=GkLcuX9%+QtCqstBZ>sbOzr#zb*J2Mr&E%a>>>_ z-7Lhd=lrF(8;G^7O#W_M{~FgAK+4NRd3g7LjPzUV)>P-_pNbnieJ4S_yBu|s5O`z>d0_fZT(1P z5*&=|ER%jh6@*&pT_3h__cL5TWUJ?T7A%g>a;PErbPFqgnegNC_qq3>W3u*L%?eXB zKN<5fF5m6y)~K5>Ql;4w zsfcrTCsn^f9g#yER*L?*ujjaz^P`p z&#b_VmKbO}gd3epu)R%p4-ECeP&J|r|7!5;yXW;}-tT!w;xh*}EB4H1obdSN&*BxK zZ!~!=)pPRirVkH=@p=LYMv=2Inc9Mv>q;vI10pbfMw~yIxv^yx)FttjXOA5}uYI%p z$?TPzcLqJTTGVzZZP%f3gK?wucSIq5@Hd9)?VkLh!1A9?sq<(Z7rdwT`OY#Cyh>Xz zD?FT%VRlg);alv`7Sy`B-^F)xl)9@mtMh)-(tz=+CCU0Jp%)T|if(%j63oRW&z(I9?~)e=o~`8{?h4M08b$oTdlcR8)(*asUgcrZmD!Po zPLJLsjS7#{JVbx}_$P>3-UK`Fn z-}16{-#G5V)|<(Pc6L_PKCM~Bo1#SG8p5+rw(L&adCB1vt7G9R0lN71-XgCp zA8&}#YE!DCM1pMKuNV3T6YA@9?IdW#IN@(GG`u#?7Z)?T zA%1YoY@$(e%+$U6?%S-1KJ^EWYYA6J?sN5jN;Oo?A0mb1{4v<=z0m0Lem0!hYu|b> z&Ck00>Bi3PYUG*TT!X{2V1V_y<0wa?fW;$%W> z2nJaAA6U9DB2fkw{sn}@pHQ)P;GD)O1Hsby9{RrT_qgv~Hyh8kGdmfMtn z6(-4)cuHBzK!d;$}<(wbsa@2%7%ezT2;?$2-Pap5<;a?PS>mY6T_(0Qm#(y zv4?${a-C}~gRbBvcJKlgS;wLK0y{=XJTNa?aaZS3S*f?+I;(T3uH-_N3`{Qfuggx4 zn1hn#(zNV9!FR>y>u=qvv$HU3{={{$4)1xz1HK_6{!Zn{V$t kypidAyx63p=Fj!pUGD7H+TBlkmtT_yRj)PlvugL%AN3_BV*mgE literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/silver_rating.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-hdpi/silver_rating.png new file mode 100644 index 0000000000000000000000000000000000000000..89a0a368f4f3392cabc0422fada3d982b430f463 GIT binary patch literal 1905 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box4.9.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box4.9.png new file mode 100644 index 0000000000000000000000000000000000000000..ced82888af9086a0a5f9b1b199f165d4e53f5098 GIT binary patch literal 11278 zcmaL7cT`i~)-Id`Lg=7?L1_UMDbl5d-lTUBlrB|zFQJ8wf)oV-5dugFp(@flhzQc8 zNH5X?K_mj98u<8~_nh~Bf82BT7`v=B=JU+C=9**gz2;oc4fND$C~r{$000_I4OOG7 zJL>ApA}71LQfWMYd3C$#uV(IV?B(PiWasM$P;&5k>Il>Huyb}aa

@@fmQG1pr96 zpP88Zo9pVp?Y%q%?f#Jw4EFH8LIVJ@3c=oX_HK^;u&0jB&phS0_F6l+V9y-nxXi?L zg>}7E99^DiAbcH-5qc)}2se9a2QCG9m~1foionCs-wqb+;qK`N50>Nl7cczk`JZkf zF4(_B{N3cZ{>LbDT?3ekm#-sCTu@xVURYEFCMhi_A}%f|DS96!DlGCqNLWNjL`*MJm-~ObXyD@I@8##>jgb)rD(% z`uW>=+B<5h%5hz>2tIq}0GALKQ<9Jtl@gbh5*HCsQZ}2;MUPZj`RcPPzeKrdK@I+{;Dwzb& z{mKhTzB3)ZT!CqIF2&|A=2^Y^X7wEV;+WIWfcbf$2a%IZ-r)|CjVHsd>v=Q`df%eq zQuzLWa9VHFT@8xsk0pv`5=oV7?{+u1Ys2ooEpTVkPR~&B4bfOFmvatuaz>n?k1acK zh^fxAsXePHxpuSZqQEmWrW&zlyuAD;g|Z~0+u@H*kML(ORB!Nu@=fdeac@ilrOKKm7ugFd{w!N(H|m!-8X0{CRI<#9AOEh zrYR&}&O%~Gum|2COGm;nPz`d2YOR+3M?W=dkX^^#J>j#Q?_>Dp!&t+LL{ml9kWUrB zQHXHX^n1~%EEOcD0IInyFk}x5Q*FH+Qv>oZyzOq9;R8Z(C`te}(44F{!>EJh$Kq&( zQ?zJ=)X6K5j^}Q1V`M91A@Y#=gSZih29#dNl&7TsxEimiu0ZdlO;p}y$S`>%PZcb& z6@NP9^hU^xAXc7F(_$wtMtt)K%+HVN8*1TuM=NSZe-A(l$)^>pL{_^dHu9@|>7s(z zhEuQ1!l>A~4#oSkWWuT=-W>+lXpb7;KRIQCq}=mI!LkhAr*N50){BM6zwH~nl1ud; zn|y73oIDF>v)|^V6g?3*m8axRn&wiyug2QHIHDIC>gVCS`?(^wKR?WWZS-<<@14h< zI|Z3ALl|q~5`cE&;M&wgR9Vc+2r|XE%io=0K7_ZqznguZ?PWr^za~3N6TZ;t$sg?2 zM9q*#!YJx+l&Or{B~R)QSH=(t^VX+_Fr3s=Ou@Q zb}iyw%Ixl*Okq(A7eDKkJ@4ZCpdj-bGY*;lw2-w^z=NooqteE7q8B?!q0yubX;zy z$_GaSb)^)yE08^uD3W0A`fuImUN*smHWpBrn*s^A!XZo6J*eq(P7`^5%}bO^DLC7?4U~ zxD@!^(Ac{Bqxtv9kyHP@yug#Mi()}{0MQ`%vp)Uy!^q1Wh@PWu-)%tc^)QL4q^+GG z;-ru`$KSC_1cVei8jiJdHkN6v6_smj8h@8>^-knn)-^cz9g_s#IBR{@8aSQbu9>5_fqmF z0hIt^7mfQFZ%6lE+Wj1GQGe)uvM3OehNBK0#{j>nw4^A^#E5r44!&ympu>v2=2= zjC!I#IC}%4D@ZKTfZ+iTz9)!$P%u1#^B(gMWl!0MJUAZJKAXi#w}G?W20qw)hUKqFZ{GoJf@Upu*ze1c6nesv}Pv#kCt?Onut=3LO+Rdnf%0&2+bPMSaQ*`4i&$qf)zR)|^3(^<8&iY>OPQOe1K2@C5L-$N6 zWnnDO9^aIr)}m#Jj*Tt>@FKTAtP*((z&v#xe@=a1o6}CnT1!2y)h%`v8fYjuMu5{bjD6R04tB?dg*_u56Dl$e6O3(x0k6Y${w}U#ucKga2bOoUN z5Ly9cprX>9h@Xh-MHH@N&*zx-0zqu1(hqg-kflVI8kdA!KKb}^P3yRX-WVp5cr8lU zED{q>svM@WD*FC6X@*#aBq^uC1^+pnkn3Vx@kIgqPlCnXrSrF&-r^v;&x*|8qu)N6 zt$ZH<(WqMI%R-{}&*jL?TmhByr^Y#RV*NHtD+T<})dJyju(b(XPaNIDyW$TkUQy+x z-=^HuuG;p3SH}_Yf9SJ+Nz>DpKV!FyG^Bt4Xa}EzG&o~@69mmBhNZXM$0nytAEoXO zdxgwjKf&HIW_h*>Fm_&sVaRW}AtH}RI_oly-k2-Q&cj2W>_IE!7YB%aD2vrjtI2H#zY;O1WDL!Schl#ps42zUSO90la_1&vcGd+ZE z0fSTn6#i^?@8UFsOg?Y!rOxTQunsrR2pJ7Oz+#b3NZXggUm05gYs2+x0?^A8jFptI z(&U58f(c-PU2nk#%ntVI*tJjVp;;T-5~}BUl!>q}3>l z75Lqu2IE24N4FS8EmAeMqvPFUoxzdEyA+k(cBbRpqjMV7k}qQs**3GC078+rvPw9+ zyl;><{;h=*eeiW+=6btcC8ftM;Y9>{MTD4NlD9Ld^XOl6nTKeu1>kuWb|imEWF23* z!UR(paAfe8SP^zHWTaiJW&w-m)Elj5auz9BmRS43l*N7N2P+c+%2E3<&q7=PsuXVJ zpVT%$h3wZ)HVds^?Tz>%3UGcQgp~|3c{@Y7hTQN)VAEK=v68(jnVz6ao(1&nXU8+S zPnKxXvf=)TDD|lkNQ-dfXm6>u>b=cLN(a`Y+BO?_Ah(I)rZ87Pvg-G1JYe>%SLbYjAbajppijihXEmpdYYy9Ka>;(@=oz7lZ$t zK*Ot2u@iVz)zR3>wR);+=B)nQD?IM34%NEu1ca`y05e6#{p9D2H-`K@-`*t8oK(BT zR7nYYUrm; zoh+-DZd1ZQ^je=wph|+j_ST7gT;RgcuP;q|R!n$wPM>SSa0M-#&71K7)VMU zsKUm?sE;SX4ZtbUZvrkCG6#0~%b@8j74qMVs-LzES8E@#G<89poTMrCLZ!OUAa?OO zc>RP@KTusMsmKLgKyn1t0E(osCbGuzDLyCt0}*$cVd}b1u1P}w56urLOl4SUTYlp{ zxHRKMKi&6FaiZVi;_=GUWiM8?C-op~5Z1UVi92D{gJ?wu&IjeT8G#RUIsL!}Y8}C# zs7m?$Gasaozd={m=J6EGBHI1J+GT+F=jE3ec4nY>BWC9{jVx|CXL5v$jm*=8yAju; zrqw@ILgnOod4d2nS-k1-SXd;Tq7WO!z?K8V+9MVkNrlGQE+Y)1y9T2a)HPMs%~s zWIv&mtqhdd61HV*KvV@NCjcXx*dY2b8tU zPLvZED55DxBY1*>8gu6{E6vIYQ89;KdF*g~KIjOlzjCiXmM-7JQUXA%IjJt-F{g-3 zO1=XNC8FhUrDyw^vXy!IE7u;;NQG@G(CQ2un-Kh!;^k6HA*sf# zm@!k4&UR~7oUOm1>Pxz>AQ%21W4wrg!l(Au{pJ^XTe(=4oNcw+-$*61TV=!J78g<| zWv^n8`Q^h!N}a}es%CpzX5LslTUcqBa%J=+wI2ZUAo=@Wt^)L4Ry?PH5Enfy02fVZ zzbt1~=SBrnPWAYv^bl#p{dM|18<|eJ0F9t!*VS5$bdK@B>RwFBJRQ=ce-rj8$BtGp z11$mKu`ltrH8#YjpGGJ-y?4-2Fb+XN}6snW>$bwD0jDbzPI`K*LcaFX0(OTKY%qqyasckcN%9e-jjLH?)NP;7{oeT3u;W5c;`+ROYJ@jR}Qcf$`) z#H=HF57J$E1MKOP!y{^nl0tjZiUC5cS?t#*Q7rO)!0`^=1=u0JN~JZnY9Yr{8q*5q52R zKlt6a;YukV5Bnm-c3ttamPX9=62!?uvkCRITZ^sc100l523lZ=qGn|p?Kg65zPWK* zU&Z1@oVz9N`d$TMX0%rrr&Fc5D-2^S$f2bF%~O}Vo5XaDzc4xtW6OT+HV1w0L!yEi zf#fySh9$Xdc1s0$VV2vYWr3~ai+soHo@P@Mry=qy+gBgMX?H!h(ef$RgoO(LSG)m{ z+f<1GIe9pqKQ2M&&7;#Yi1XKycJ3TqKZsjHjtcaNYTSoNytcTnScl8`D{F?=8OyP) ziz>G@>0gI^0w#{44lZ?YP)Lk@aj)obX`@gSQE#L)Bgri32l^)q*DB2^PnJk?Q)>R} zvzve0_*Q(zhT+Jjmy`NzBr1u+A1D;x#ur|ojGJQ!^z<%_t(LqKN$;1Gi6)ZG)(gKd z`;S}IMRuE;w8J>FumuRXpBu?2>0)KV{`Xy`L}q17G#t?UfclY2?itu+$(cugj7n^mVzu~B{*j|puY_S}|+y8Z~t>n!6JE=#gA(a%zpICW)&GuXE5 zNsf9XoCKSLeGHVLZy}$`QNxEXf^Ff3*r->_5W=gl$G!8v8n#~s+7MV}$W{9WLYrm_ z(F0QfDrK&AVvTUT`=Ara^}TPr1$xA-mqk0PC4+#$+v81~1}UH1)gOC7+hzr%>UCy6)Xnui6!d_H= zc)?Dj-tp{?wdp)Y>k!AKf9u2e#^KyU^rMNumNoRUDfYYQo&50XEdG1dvO({c1kLN* zTA8=gt|LECjYUv^srgN!1&L!y0wjC^b{5_hu~f*0xZnDzH5T~O)g2x{wxy(HCm$6L z^ygUjwwTmqvpl{Hcv!HVpl_dQ=(!u}Z@fYzLKe;%Jnrt~^*)Up`D`7c&!y{Yy`G7f%hmiAxi|ruIgAS9PnyJ{H z7~o`}Rsf*XO|s2yBL6|k1Ie$!o!-gLJgveRF|jL$rypZg%)t8LFKTJXr(Oq`Tu}V} zd}~0hg#JiIOY8Mzj4{2HlYh1mRAaN@<8R#pdA>zElG+#2KcbwCjB+KTvq7H)%|JguilgipP{ z$QPU`aFiKDw5DYWq0(LOiAVDI?Y{Cr703MZgLq<4sR}nEP^^LEDtvuCdayg(-Fk(`E-9dO$qM5?p=^X+2f7R`})vuWBzuh zP@Y%uBhS^sI6b1G9x-FDY*HW0F>@KSmy3RX?~zi#{bdnW?k)GtA8G`5?x2+(JTR80 zV%dCnPYRS$8%Z-ba5pXk%!VoOcJ80tV=%N8Yj0ZdaTrqTfCHGAbw#%z$H#SQ81600 z?g|Xz-9Sl%U-$9^DGPZ+(mb}V#N0oEgZdze-kWdZ6)hFxMi}EGg+=e)hd(6sATW0o z(=^b!jOR3I9)+xd95p-kk-=15j^*plppNL255-T&=$+`;2P(ZhpJr0fl37LQluj2% z#64EHc~C(@UuGvPtn0*Dy`!8vWZ6BDV_`NWmSZW{tdL!ymLf3DRyRvS8KL%vcQ$Z> z%2=@pZ6n`;66xdyOrW#TFi6^trGUhi&dT;sRR!)e+Y+}|XuI}Gq?H$`S*W`0@Zjr^ z5ks=B(C1b01iey`kf!{Rzc@hbUI6`5dR=4=aze?OB=xPuyJo4UnQ|0=TA)P@W zwkVKWVf1c&PkDaK08Rhy-b;p#j%CT<6*r`=Pzqw|{zWyeEZ&52N{J(3$)-`xGZ zI<8$$8YjcI1&w_xP<}}dXAC~6u+jV@jGTcuP<4Sg33TC#@3*f*w`w^mNa*+rYH&;- z8i)9P58wWpu&Ct8n9Gm%IzQ&=emDcK=Uj&$P@T^Mik-~M3ut@V(muW`)K7hCj2%^0 zGz7|;`MDX(tzyBr?#Qfn`x`;;y1si#$TEW2wTO4lELpyH8$)RTaA-7{GBhphhS^re ztBRE#(tP7)hAHDHGOfP}YZA?C60IAp{3a8DCET{cLK=y0XQ**m^1mklFdjBG6ML~< z?mAg&cT4uv(iiBtW~8%m`^QwsJfjuNQfSTc3nr{D1lbFyKq~})30d~$T7w^T= z$UB_rj@i{4cW2_tmAmcz5b>}W!}c)-uA9lWXP?eI={;JA#e*E!_S|0C=@Br;lLM9v zU2jx-#I4djER!idOi|;#7w4f9RHe*VW`n`FuP(>pmV@n7%$bI%zLvD=qV^_nCQN=O z^)8s_eeI2_tlyp0e50nQw<4R<#mGt0vaXvZX)*MC9XEED{A(Y~m3CeI2A1cYEwIQs zcz*XP=ATT!pnP5!E*iUfpmON0_xsS_HMR1ZT(dTF5VId>GXslu3IfO;kW&oteN}P+ zrH2k}p>+f$5?KA=_1~tnsR4dqx(arqHhSFc8q{}?^LnX#5r-e9z`BuP`j+v_FOxb| z6LW(ytcOz(wh>l9Y7NGP8mV?lUwWM4=Pnb&hk~=iz_J*o+kl@8|O6KnviM+4J_N zC72=>fB$1xGC2ts$@?2xP~&gFauU5914_0>GOkWkdve|1e!7k5mJlZ3{XG!EY)z+M zi%V9h74M27srgh5UL`NDO0aAColVO16D{%Vupd#K&xfXWUT#0BnNLXo3M=xV^Eq#T ziRvv0voR^!^kgI``WSM3+|3~?%h+_cDm)c@u-0y+tx(0aUm`CA3Asw3MC~L9UggMw z>~~`;mW604lkdz4b2=&iPO%0 zrP977DsZLo__Lc3dCb113bI?!P;F+0ggtU&M&hrPwcN}Yn?vZq5bfVQs|7KZeFh~g zxcK+X)j|-gO)2d5FiG{1NZPQWd^!epEs2Nl{?+cdJ@~O%vBPd3F-p zz<3r0eNR24Gx0>&5B2_k-R5N;y zCS9k6J^WA~*s+3R`JHWg%IT>IdZn8_Nk+p@Z_Mm^+=G0a{ZrwiYvlMg$(s`%vRUcDF=uS$iw;cn_O@`N*O zH_9%OgyoN3KBG)=u+QrdOmr!|k)Ndz&BJSc%oJ&iw@^%bsYE?>%(Q8&NIN z?}bh;BKCTQensEF>$z2gu5Q?u%~$zp0*sPw99w?~-Vks6Ww8`A#k!6nlanEBe}9`H zmPjdB!C97gw+lc{ssYpix%|5}5`Y^-`x|bEcKMGv67gwq%&NTxDF*ol)dn2~BL++N ztfd2Q4ceGV8hA}FUW|Z)i7z|un-ew%I7WV``MxWoVwithrwE#BKJ4P4AP- zuN-pWyWMVHRyx0{e@J%x%?ZfO;MVi-3-oa;6_Jy98TQrMNO4y$v^A?OMXhFn$;Yy# zNjTCSyYlmU@Q~L9y-YnDAAOcn9VXQWKg-S0WtH9YH0}KLy5B4L0Yuqk4|>Ts*VTM`n#*T$t9y;~IBC zC|X$tBtf!s!(*6^II8Z2ypf9SIKaoP%=k^dN7r1owUqz?IOMr(b`}y0FUmEnaQ-4iXZ$)-C^4$4$Sfy}h}v zKBP1gIUOjyd^>1SA#}Y;p(BH*_%B{)p*fFS0|NZvh~4 ziFmBSpD&?H$AuMMZRZz#>w|sYM%flWSV=L`S9+`58AmO2Q5nB1cWPhC#>CZsc8Yxl zeNA4!PUfoP4ZjUCFN5W+2_?o=5|lEcq7Ai3-!qXd;^$sX#D2QUK;9>6Ij)ZLE$9#H@Sg*L!mEz9fhvLvLVONib1gkxQw(MO z-sH|&N!qYa%)b7SaFEep`bI@^LE5jsHNxp8f}6N#bJ_WOFLFIx;AM}`V1JIKok52l z(}}YBi7!CmP@yAY>x;~uMQn9{e@cW$?-Xe&v+9k9w4!;b*atz$o85?w$0VS|`=Ao6 z?JsWcnaSwYsDYDBGX-cQuJNL;fvjZ)luseQO@bQPqoTp9G0PH;V>m8xrjqTu_ui}^ z>0|HkHGQ9hw^oqwy?6nA68gk@jOKkrB`y;<-qSGORU-Fj^G%V=-g!>RRLpdl{L%8o z6vweV@jJwV{0@WnVU2Lty@BOwx-f2lo5TTCWaK_H((e={f5LJOBiAf4`!ha~>Lq_U zX)jQ{w#{ru9riXBbMa}LC}r16;qZ0MDs;Y`N>i1!ek6~E{p>SjKJvgha?EjZh|J18 zN?$bc@rirs^!m9E;07ZeM2(i*UaXmr zY^h!-tiIxUm;t>To$Sd@l2_#M=qN|a1#PBLdhX7>P`7@YXEms>s@4w?TuVz*3_)`O z(t?hxLHS;dnKTI4sNnB%0l~c*rAD5uyjKC&$?L57R{sb^mLAWbh;~`U>Xrb1!hsZi zC43az(RCZ|4Kz^7j<~Ewos~3)Myz5tJv*?A!?quj9ZyGYqo9$;nWIN35k!3idCCl+ zKv)q=$qud+4P!-eMvs8IdXC3xel-Ni3Wu*(M~a3M2#u0Q)_=HNZoIfPu$UWr!SGZT z%DL)4)HgR^w(-X-pUztTC^qV&6|QX%YP1q;0*tfoHk{eI1&@}D8fB9&*ZlkT$BZ3I?y4z!qeTF$+55q@ zPqsR)(3?ZMr1A$EDu#w&X^zp&omtg>N}Eh+3)1}qsYoGZXb&*qc!SGL1nA`exYBZD zJ?*|pZO?=nWkk*ZK6Bsbq`OHEjvAg?x5jlfKWvkja3dKbE8!1rVX@!Z7FAXUNGsl< z)a$tx71Rvk;XjKQ2XfnK-kR^4crw6O{$@-C{D;&i+MOjG{f6CGIKtXMi6E$6$3l9f zv?$pA%7gNr>{PW8rjN|l)S?@IXVZhE*#?ofGj?Gec411nAlwQSYtbBD6f!z}Om%;y z>61b5d0?+l(4et~%AI>qyDpawE2g+2|2geuUF9` z-8)nZ8)!ra4vVqw&7S%o7dq?^Z*{e8gL|=GytZi}yn`>jmaZUkvf4l?UWC;Y3KjpD zGRF}$sJl{89dDXB?3`G>KywmSh<;U(i(bU@nS*OFG264dJFS+0!rq{2!w=v7Y{a&w z_cXDLy7Jxz1gK>IeB4R@Exo=@l*hiAET9VX5BlsU@^N zHpl|T+6UR-poUR#P~|Ay6;`>h*DrGKuUYp*(%S;y$?nra*`S(#cIaxV>8VyL+eZI? DiNZI# literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box5.9.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/background_box5.9.png new file mode 100644 index 0000000000000000000000000000000000000000..6d1f223273bc59387fa4893833ce349571fc09cb GIT binary patch literal 10298 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/box_pattern.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/box_pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..1a838ed167b047cbaf73f46f337d8262566dbe6f GIT binary patch literal 989 zcmaJ=KTOn67_Y$)Bq6$x;Gcw-5{V+$_LXaTZK#+2J843Z9G7r|gQb1PHPF7=KD@%D zZVo!=3*hILxc1`{(H}(+gV~a$r5gnj+Y`v5AX^CPUNkkI`V?eTW`@U!wgJ@jV5Wj z^Qw;{&MPon9wn5`FtgP>@%%MJzzSLoV~zc}^O^-=ps~x6E4Yb?+Tq+*ik7w(8vfRr zuLNxMDwxex>L5bI19`L_XKJpoUB42$cx42RQI9x95$ z3nDLyC5k9zn=$e7Qk+fnJy3Z~9c6eU^=Us^6MZtR-SL46AXTGU*x!gyOdLyNsf7!| zKsD@|WZSYKik1MOZ5XyttB8ss%_w#mN=BdS#2N8oAN9H6Ay+?^tC}hD2u>RqulKvR z*v15BZJYqp>~;%WS`Om?ce2Yxd3$P68s0`hEyWS&)<+GG&;X%M6Of@OnnqWD#j-8k z5=~3iWx=HS_qo9lx%ar@f4Mvr!xzo{r`dfIouJ})FuL?|F#IT{6Q9!2wyxYArO(23 zEWMF``u^z6Faw6n&%gGn+oNCMm*@V#?$i0*Q_h8#A4Vt6Pd(l?pJ4Xg$!Ge-omU^_ s${6AvJZx0n|G56GvQzytHa<3eVvyl(oSvKt?zD;r=hzF@3*+X!zcYC>!vFvP literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/logo_vpn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/logo_vpn.png new file mode 100644 index 0000000000000000000000000000000000000000..22000faeb18b552f2b30e4de4e0d6b59199d0cf0 GIT binary patch literal 3840 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/red_btn.9.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/red_btn.9.png new file mode 100644 index 0000000000000000000000000000000000000000..ffb342a495c6802cf0be0731a63f9cad75059389 GIT binary patch literal 7024 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/select_arrow.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/select_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..7a15f38a0c8438f38f485320cae51d545b7e5a69 GIT binary patch literal 1179 zcmbtUU2M}<6gD7!+K~Z;O#_5xTo54Du^s0}HZe4f6Pk^b5F(mVrp2*uQq$Vk*f+%O z%cN*XV-M4iK!CQQk!P6L1QG&7i-c5_NmE~t7}^UjrA!m7?17gFah)cGhlv*~+4tU~ z^L^*L=f|fqefxavo7xG2@TGd?EM7ajchw5~J^cH3{dn1k5_xn8mQYo(0TI(+5s)cE zDT6Ffw9(_!AW9I+KGSn~luz#$RcQDX&&Tf?7Df|9bgyeE>M%fL5tMaPqMqNHph#Vl zsQyrzO=hoK@0{(mL;1Hg`?S8NzBU0 zW-NS?s3C+bkzuOUs=vznp6Zv3Fv{xD6Bp?Hq(7t?FflAcD>iw8(h8Q7v+3ziw)<>g@8;+f)l| z{RGgGHZ;gaenkB(4GaCDK^5z;T6AKV4XdRS|;3O-gdFQ@nLa5fQhHV#_hl3+KFd)be^Q-Sk8TP`ONMQ zYuy_X`)VhDo7sM&{oJJ=e!5DnJ$0SlwrTS~ZT!d|57%#|>AA`CpK$J%FTQPiaUlln z6)yks>)OopzeoS;oGsk`@v*qHf9Ap86WyPVq`M?mdo)`p{Brl{%C?Hpv3+vs^N&0J z%Ab3%jdQOqQ$9Gjs;zQztM7~NkM(AseN&%X9rc}_8fVslCDiiNy>jC0+|~&*eRR*@ zuKLL2o#d}i_;SmPcTkdJqFNmJ4FH|v9!=Kk3~D9_&=pCA6J n(^ubd{_~et5@)X8FTYw>AgX+L;?|#b(EDAa5`FT|J%!q9`(llN literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/start_btn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xhdpi/start_btn.png new file mode 100644 index 0000000000000000000000000000000000000000..0c92d539d62a7bfded96887bfaadeae39f5cf1a3 GIT binary patch literal 19755 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ab.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ab.png new file mode 100644 index 0000000000000000000000000000000000000000..82ffb169a69a36583652d6d445f6b5458487e56f GIT binary patch literal 846 zcmV-U1F`&xP)001}$0{{R3f+qF10004uP)t-s$T2Ep zWi$YpKFBgF$uup_M?uwNUdc5t%sx2HLOkG#hVjeE&r3zgGc3J%tAZOLp*eAKp>`8)nHrgy}Q(1Sn|!x^3KiJZfVLoG~}9><(-+- zl8E2(=h0M7?!Ud!QA^1*F5;4o_TS$3;o#hTdE=Fmd0t%+OIw=cJ+2T2%Db)Y4c|`t9ua=H=s+k>;SB=AfPJy0_6&P5SKY{rLFz z;osYOb=hxf-GF@1PDuIa=lt^W)LvQ5L_YT2+{`~Y$~ZFo_4UXxE8vWX{{8*zxwriD z^WA=V$~rUDU02m%Tg^&CcX&~t122enbRr2r%yM=Cxx>}X-Gd7pdY+!lWSa#6Ufw>w ze#io3n8oZ5(aISR7!=GK5(;96Ns(t(c!V?9ES^Xwr>N){P9T#vR*ao2vkc?nAu*8P z77=%2gfrs%mPL(KWSo8dR%0D6EI-@qh*r z(?la4&_rTdb)p^^HERIPGNT?~(54<>V4@xvHERIPqGp<)W?CIBCq~P!(ei85EIOJ6 Y0OITn76QG{MF0Q*07*qoM6N<$f*d%easU7T literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ad.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ad.png new file mode 100644 index 0000000000000000000000000000000000000000..7b12b7214d5c7dd4d6c2142661806cb16a756230 GIT binary patch literal 3061 zcmV001}$1^@s6wfF^v000ZTNklf3ss?imjET_Po!i$Ez+NXHTq3sxY<4zlSGCqQz@CH5r?5Cq6Mmt34n ze9XZ?5G2sSK&${(n7~Lx!3h1De6+o~#uuel?EK{lhIFc0|9 zh3@IDs{g$AulN2{C2-R57_HUnnScA|Kek5>U-lnYAEOIo7r%zeJLtzb7sZBcfVBbE z23Q+lZGg1_)~9S~DkQPs{+9&CIY?q5P32Pu%lkAtUtaK;`{5MUDE1ubuw`e0$h|-< zTo1TL6^rLNE}yH@zga;m;ruC0e6+*#mIUQF zY%a7HLav-?AX$X9jw|OZ%}s4gE(`~XJXKU1InKGy0$7fj#-iMqL#kYUg>r@f-iB{I13NWo+tD7LvY@C5i(tbn!#O{D%!Z@zA2kh6<&? zT8i4m!7$C(_Dqkxhx&{Lnz$d}G7p=9bv|uV(&n`U{niSN#RPdwj`r9Hezv*DAARlH z9DDW?lmg!qJWq&IOVI37ZRC_ImiJGY_a2T3jJbO#yUocC90?FJKCdMwN&ak zL95Rwv3Q;k_=1Q~-CrX9$~1A^d-6PMQ*(uFn>zTO;Cq4y)W+@A>kZDPKK1E@bmSpP z2?vM>)jrpFGcMcbhDCYP9MjxRxMGcV^~~pIa8*y(C16=xBw;mJxIf{db{!LqPsubM4Q+NxpoZ z#uvfmkY9iiAPSTe-%|og6>XttD=Ahdr9i}?6(lvy@`(o39U;L#EmN8>cTF5D!BHu# z@!IhZDVH4neLd(}oo#>mYfS&@8N_>^U4QWk%YU2VwdNSU4=Q@diDkgSh*|AJ7+S2gl>^}smE`>{tq17bq$

}M`v)Uu?=px$$Y#ea&+a1>41DD=@`{5=dML}-2 z1+08wiZoQ~7py&7KjPr-Ii~80YFXiX56<26`(z8#jPrBy-Ym)G-Xno@I}|;rmKDvK zCf^n^+7zr`&T7_IiNc&=?C`W;jH48e6UOOTyr~wZKaU-iappMa8tDRR*4zMc9#M?shmzAY@g&e!&_gC5AEh9vI{BeL;_XxBi7!iS{+|H_wqRYdUYgjQAnL zb3dYdl5di&n z-X`4Dr2Xa`$(3nTbP}7Th*D%ri+GJD!w=5jvh<<)&T|I>o>%ZzDYt~3b(}gZEN|mG z-~ST7`RaF>st(Z_N@J4EmX=uf))FoU+|38Rn=LQG@*=L~-^y(Z8gqpRcn_zIKXYS& z&;eQrabmF!?7&bt-6vYlU>U0!#T6P=p;6VusYMCc=PGoYKV4U*q$!1(>lbRsilOs) zFflBigVoQ0u0wJfdQ*@dgNq+RVgqFCRi!|Y3+U7$)~#Q#L>&8fenO*~bKz=>Ez@nZ zhQ><@>3EDTLhU#V{u}&F;7u3p+Hn|NDqJNzq{#Y$Nj3fqRNjEN2ReuPFOR)2gVN6w zj|J?SA~%qx4s$bMYDYl2HNbr^m}to}QLvm-ym|7MIDcg?-L;5syzw{e+Oo#+Ww$ z!97Y91)!*+UD*Zcmtg6yL3KfwAbPq0Ys=T6^mO6T04{NqS2LoIACL0zm?Rp9h`l}W zM;c%Qo}Gf;2?#5YcR-gRTLJP13R4(^E>8kjfj{E4M&JXLp|cCEnuI6P5=jw zz>TX=t$=(C(f<3eswmors+2NI{mdF(?$$F{j+YMofZzSfcQGa=2nsdekrdY((ESc< zIss7tdk;YFpt_^rtGiW)u&T*^u1=tZ?$FXYzm5sM9If&4v3D?LNE9lp}0Ea4Y~FfjX{0)xe>txv-pWF7}jdeUV4`Kdp9ccSlstt*vLy>hj{V=P5^!8=+hpd)!cw*EeB) zA>sVFoWV1&;(>o12FD@20P*Gmq?NM@u^!jXO>yI*z2|gI`~K1>@yG@tS)vF7OXwH- zleuy5beZhO1A+;bJQD?Lb8C_9vmF9o2z(F}4p$r-5`~+ISZxD4AO#o#2cXjdrpM~R zvQP?y;c|uCxc^fVEYd2$^PpANj1r3j0$)g*9%-NmVzXYsTA^A_F&3(&f^<)R5L2%x zv=*X3#tLe&Gp%*u335ljBS{_YuAx>@)Fb`C5hP*;>40q_{Gl0>UV^e6p0$tZ7DxqutG^S2XQTj^_z!<_H79Scmomnee8?M8R5K zohCOSTE+AShH|7huq$AgIC^Ua-xqu>H0zqB)toeUtaNj7Q}E3EQjXRT1;X0U5~q%X zyF<=jNhpO1YhivVr&iWfNiZpP9}XsZ{75E!9Yw30+13Jkw_RhlwZP_?w)^Vq@4D&fo|3WhAOg+^ zd8&!~K7*b|o`dfTQ`I|%*LnksF^(XFN+TnzWN61#>rd>FLzn#r$l3_KRn32XZyJ@B3cky4eeNhlinXDozOB!(S2{!` zJKnCp^BYjy-MRETDD&js-H|(I74GsZrNH+cjh10{_YglSNLmTRiA5;dlk zRL06WFs)(xzL=>^8FN2^v>!4vJ7E8jK7Qa>FXpk1tWq;PcYK9B6N1ncO6xi@k9B6A zSA^zzw2iwv8(?jKwE@-!Sf5ezn8=yCpUT{m-pBs{Do!@)V^^}G00000NkvXXu0mjf Dl|<_{ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ae.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ae.png new file mode 100644 index 0000000000000000000000000000000000000000..9e316d90583c4bbd4b7beae835dc143513c75bee GIT binary patch literal 234 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 IM6N<$g554}0RR91 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ai.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ai.png new file mode 100644 index 0000000000000000000000000000000000000000..611293b9769f82c768fdd364df07394128fd68b0 GIT binary patch literal 3142 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/al.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/al.png new file mode 100644 index 0000000000000000000000000000000000000000..59b2b8d3fada3644c7511cc652386c3f422dbd1a GIT binary patch literal 1872 zcmWkvdsK~y9zEat{e9=0&imDg)FjbcO-fIdQhK0NBaIT3iA;~V@+hV^x8jD9(F;vv z^q>?iQ*urcBh~c4Wf&>ra^-oMn?Ls6Ywfl6TEE{vd*`hT@McH_$>Z@DUmwrlS@oNR z0}jn{mdVTo9uL&7^bhsAXT+Z{;@>vpy4CDWLvB>dA2;HUY57B1Zp?^(%aH5Uu>Bft zmjThGX0NK*17dPe%UxEnr4q`CfHg2^fk6Q@x{<@KsMvfFxt77=IV_Y$tuWXK0T<)& zAqjO?%jq=S1tn82r}ISQ&vKdtf#npEFD4^d?2Lj=;Nw~ZBmiLcyaok5qU9G$NC5;$ zAh1qOo1>5t2G1)P0SM3_P${KO%IR-d>}vw%gI_Wrk7t9!zpB~U_izB1i-0j4c3ei? zG326I%$b1QNaP10v5AkbW3fOQor8cJ2!t~z0`L}L&_9Gkp_nX}Qo~w~0f7_&UM-_F z2#5l_b`=vsqlO4L4}q*PxI#*KkO&0uG7X3&1RT%D=OR#+0dYga6^ThH1oo)eCM6S0 zqpKOz0f*Hv=t?4|<+LdZB@6I{7*r*rgJ?8@!Pc`F2JmcgI8}%jh)FpNcB@)rHaKQ2>?O}sFYE099AZwaztdd0kPA7*e@ncP{@x$3?RUlLKH9<#bN`7 z++GpsPoe%462_oEipcK;_Fdy+^fgU&;tV^V6Li2Pm4AJ+0OshD3B zOe}|8R5D@+D6*WGwkCGN<3GfrL$ZUbWUKBv0XywAhC z=_1*|!WFLmPzd3XWaE-Txm5L^Z$96;zNMupEg7Cn+m^b1Wed+LYGS;{c}Zu-vF`L- z`+~mSuCg4rTQ>%lYAvtpv|$D2#)Deh8%|5+1Y76Z%o(-&vFP3I14RW~XrPb(@&LkT zMT-AGw9YnnKq!&+4fW~z`_g7Cv@X+BLB5~=3cp8t?rmFOF10&#YH;zX`T#IvY# zR9)-BviPPL^)scSJjUPcXnDk1rec()N1fdFblg1q*OHc!fHqB4zxA`{Q-$ml)*EAX zWvK&b;twBrJQ69W5ge~tzT@k1+YJ5SraK!4v*{y_esto{bVUH)dE=I;?e{WD=jO8i zd(Tp@UrSKO-OScx@MOb-ebMvpcZ9mcpJ-WXdV6Q2ab-ze!u`12K%||d+#*`19(zUtv37PNe z-!KW?_M085jyw)+_ucvOe1BeXgJ*e9XKjR&kCFM9qv)<>Z+wd5-@$+OSgEr-%kAb3 zy6(C&bm;R~>%jN!)|-1x3ffYhmwlIF7uH!C-*h-_li%~d#zLCc#2znq84q%LX#Tc9 zec0;JgCUR3oILxf>w|CFZhIdv!+Xw-XR4l94_kj{r3+u>s7nZ}h}3&E1XZL4pf4YS zCj)FYt^Qyd>yiD*KlojE)S{%1xukct`eWY0ZJ~9V>rQFkRGhvLllP?g?#i^N!%s7U z?uB?YJ}t=!oQSRZbiJ*zt^NMh#7VJJ<1yykfAWXLuSYMu+FZEvc7Sfi^UtJZE%h}| zk&yY>{f(11l5L;D$gYm+J(qiDUWK^%U5b?I#{%aJEojy6+fp}?<-Rz^6e@WCu4$EP z)6Mbic0PgOR+}CcN4N}q$gK4I7<4-M@1(iWUai01?ds}3s}6k=ROZLnHcOXYJ{0v1{JqJFj2ryz@a{?Y-sJ zxq-LwjlYe&ym;Y7x`B;ZoOndns*6d{(=!(Ro} zwoTR^+j2{#TPALj{jlTyy$2h95~j;_dP{N{Am{P&kHqyZ%4EN6C(qX_!1Jtobk_d? Dy!M~M literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/am.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/am.png new file mode 100644 index 0000000000000000000000000000000000000000..750da0651ea35c9672c43c6355ed2ada5d64c907 GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIzopr074dO1poj5 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ao.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ao.png new file mode 100644 index 0000000000000000000000000000000000000000..5161bbedde482e536b9c6945ad6c52c5d2a00e4a GIT binary patch literal 1307 zcmV+$1?2jPP)001}$1^@s6wfF^v000EyNkl62L6o%iK&M8 zlzk~EAV`tT$X294kX8XfDwLL)R-+;aQh5AvFR_ubk1%xZk9RWnn|tnjd2-Hs&i4rk zr<;=;1P9B(a_OO>uMzm`U|=VrU*&| zy9F;eSXZl~;E3Rq1ywFMDtN~J9%JQT2^tE<3f2maTUe(BO9h<;!vq5bH5@D>kD!hq zT`)^Vq>x~$pg|RTy@H{FQoFZQFd*7ItCh{BJex6N ztXEaFH`(qh6-=_RQ3b1qV1j+L>1(I=`0HfibPMa1XzPX^L1RInQmG}|?s%=3Vbx&VCMBy%RAt(;*7N2}5h71rbn@5z< zhi`I!Z;Uv&UCjQ}OyeEfi*Gj?FrmYucMs7bSq$kfe%K|ZO&0Ci{Of0JQpA)DQCuK) zZZPJblPw1IGv`;U(ZTCA8P+VxtOEg&IZGvT&$I0R{e#$8Q8V5?6nk!MsBN7h#tajmEikWq)+{zNSha<9rwRnj zbf3FI^ndN1n|@@&x>B0;>qf*|h{%T(HP40)mV@QwU^!S0mV@PBIap3N(mxIGs~ibw R#lipp002ovPDHLkV1k;mV^jbD literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/aq.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/aq.png new file mode 100644 index 0000000000000000000000000000000000000000..efa0c26927a160703abd1c61acdf1415aaf2a3c7 GIT binary patch literal 1048 zcmV+z1n2vSP)001}$0{{R3f+qF10005$P)t-sI(^PN zea<_6&OCn3K!MO#kkxXc+)#|vMupKofX_jK&|sF=uF31j+wt)A`{eET)Z_HR*YL^P z@_?(~+2-~9{r>;||L^qstHX`I?agV0!!)k1>MXq(ye`26zs{EoEamb&GL zvEe*^&xf$#rp4)ktl;MF`HQpSPKwh+gV40i?6l78=>G$61 z_fU(|K7Y^t|Nrjv`@7QaaiQE*kJa}1{cWGyaG~4j@%roX`h%_D&ffF+`~Lp^|B|=l z@%H?Dso#63-pAVUMTF6xz~@bh)4I{_`~CjS-t$O@(n*KX#@X@U>i6&U`(l{buFC7& z==MyB)3D3y(&6+=h|>A{{gk=ne5l@Cl-J|z_+yyZg|6ZF`~CX+{+qq#Oo`H4lh*C? z`n=QbdZ*sjkY#v*Psk{POnvhOgm7gwaQZ(UG>~jkM#Hx#eb>*>t1a z&EE3u^ZU%*^4jP2PbXFR9M4fU>F6XVAudgCTf|)%)-jX&cVsW zOa-I3d3gEw1q6kJStvG(RYX(_48$cQrKDvjFo{W42x^p=n5Z0|yaEMgDJqGf0A&?b zHFYx0($GXTOH@l#PFqKpSnYcHNE`!0F$^GTWK67CCZ=Wxj=8897GOcFS(aATHjE4m zwu+$OvcqPSm_4!4De2(o#OBQ40`w}Qq$^gl+}w#Y%R@!Z)63h3g^_{7*Bh%B#r*t< zP6q*jVq!tTJ|-bnp;(O)3kxSYE1O4v?Dq2%!v!28iB42e(OzPBfgc<3i9IIP53gB# z;c>(#clLPv4ogThP9oN<swk$t!LYO+B-T8 zy1K1001}$1^@s6wfF^v000LXNklAU<*ZR5n7;7N=xK2z`!sUX3jbLw$}1t>XR?^fis=S{=e@_ zK3V&}*ZQrc*y|iceW;Rxpuj4y3akRFz$&l`f�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 eu15b=!T$gfO4gf9rf|*x0000$$yb2Bd_xtyM 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 YZM0|Z|LQ$QfNo>(boFyt=akR{0Jiy>$N&HU literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/az.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/az.png new file mode 100644 index 0000000000000000000000000000000000000000..9673ab5a7c3c12cef2c7464f498167a87b131d94 GIT binary patch literal 1556 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bd.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bd.png new file mode 100644 index 0000000000000000000000000000000000000000..705ef7e35ee9edd969301e5434f58eac46cfc87b GIT binary patch literal 538 zcmV+#0_FXQP)001}$0{{R3f+qF10002tP)t-s3S1Hm zTM`di5))YxGEEX)JrRX15TYUwt{xDfA`pZv5L`VGF-;O3RT5u35wRQ);|dS=0uTHE z5B35N;tCJ19S~hS5gb(#5m^#cKoP(f5cC5N^8*jP7!XlG5fNGv9#j&HD-i4k59 z60#f+tsW2*SP}pL040vk4gdfE^hrcPR9M69mf2FmKoCVCVTnk9G>RyMRfI4Ej61u^ z>i_?cRXkCeOwr*|yv+H4Q$0&}-$+EEP@IgYrbQ7RJr++S4dWtpnbwapnVD?Pq8<6G z>%vWVQ}On$M0=F&N;RaaR&UTBn)l&KYdu)>53Ah?p>n!{d=Nd8t#bPV^2K^AGF9Hw zGX>Ci@p!J+Hwxt4;g$7~4|WYlEZ5j336m+uHJi&^pI;o;w~)ESf}vWjNJ^jalIxA! zwbc%H{n&EX#66D-c}C>jBmV^SuOfR0+1tpO1D%D)84cYX(47X|wa^Rz%_i8)gw1ja ch2kK70j$VM+jOYXuK)l507*qoM6N<$f+M`d;Q#;t literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/be.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/be.png new file mode 100644 index 0000000000000000000000000000000000000000..35c2ba83ee238bb941b7c9a462241797b18476ac GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIfMWk|@_d#sxO{PoGLXYk666>B2MEsp58V#rGbVYvyRh_U+zbSAI14-?i-9h? z4#JF18nY{af|{N#jv*e$lM5V~dKPjBq)cmL;N)3+%57?|mUO7us+^FUD>enDUI}Ak zFi~3GKkJ|49iS%F64!{5l*E!$tK_0oAjM#0U}&OiV4-VZ8Dd~$WnyS$2xOaB85m?+ zdt5-#kei>9nO2EggH+mw9H0hCkPX54X(i=}MX3zs<>h*rdD+Fui3O>8`9|UhV?@1tzoSI zQ>NG%#LfVw-huQo{%geE5`;!rP6Ag4JVOkAg4u5Du43@x*VO_`P}z&MMXW9&uo#qNSQo>d zg}6r;5n*Hs-#vmI3$}N0suF8$-x_*p{MW z46j!3V>9eZ(KL*CDY84^QVG{e9*VoP#qZh~S`00Ld);YLVQErFj%^ z;a-J89+nnxy#=kK=$k;y16+NG{z~xT-5zMcR++)o=53^rsGF7u}>YsWO?YpXq1i;c5_` zm2XeuQ7I~S$|{}7>_2E*j^}B)nqgH{NnQQDtT7AA=!5li+r~?Jd1VznTBKL;th`Xh zziMi(#*8SFPc^N%o~>7uC@JF%oBB-koFH7zAW(`1||D*C03bww|e>8qprg?UG$ zKgI8Jqf~mttdFDU&h1+%;&x(RL)Y_e$CKtZ8By#Dt9`s<>PLo%R+IENi{qv*2qv{s zBXnAy;vcG?8%A>qwyG`X)Nu~A#)O;p5anU@n^`YU8CU#Ms>+i_nQ)1^6Gi;Rg3E5U zGt$CdvdBB%Ig6f|5MSYWS54xb=b#+E>G>7!!<=@(*=t@0nQr4_-;c}Ik~-gybmX?W zjD!cLWpBL~pGEJF;8tV|5849s*5 zOsotHzOOSDM$wR)pOTqYiCaV4=kE1D4U!-mg7ec#$`gxH8OqDc^)mCai<1)zQuXqS V(r3T3kpe1W@O1TaS?83{1OOOgYjFSo literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bh.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bh.png new file mode 100644 index 0000000000000000000000000000000000000000..aba79a4b2288ba5f2b9d75982fe4735cb73994a2 GIT binary patch literal 450 zcmV;z0X_bSP)001}$0{{R3f+qF10002bP)t-s|NsC0 z{{H>$@Aa;;>|SZ*BQoL!7vTUA{`dI%+1vD?r|eo~;s+Q0`T6_a;Pt7m?P6`_DL3K? z8u`!C?s9zN4#I^qf&{p{`Zq^sp5GvWsr z{q64brK{^%WaT0;``zF5rmXB=Y2_v~;s_Z2`uh9e;`OYu?PYJ~DmeMj)9-V9?s9we zs<7rOIrOBernrmXB)W#uC=>|bi-CN=;4{r~*@|NH#?@bUJwy5a~K0000H7J^U|gkheF2|-c7h*`{vIp_WF$!B3>iDTh8^DkhEdp|{yiFcuJ zM2*IRrhF7nBsDdK*^=o-I%8%rTd%pCWka@{e8DQ(rSG@&a>cFI5cjG#v}OzR$9Bi- z;!Nnh{$MzQnHW!|{tRehzMu)3_?d|%#)Pr**BBEUZwoWA+aI7NjwhO+37Vh@nxF}q spb47z4--O&v+N?fI=8!PJ_7&o2~>)H36qWV-~a#s07*qoM6N<$f&b%7 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bi.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bi.png new file mode 100644 index 0000000000000000000000000000000000000000..7260b4c39cdf9b9f4c67fa643a8de857fed8dff9 GIT binary patch literal 1799 zcmV+i2l)7jP)001}$1^@s6wfF^v000KgNkl)vTVymrIr8TA) zYipyWQn4XEuozzysiLSTD+(%#f~>qmVPRQVc4trL>}FJyWoPa#!s>jRo5}3#z4x5M zJ$LS%xfcVbO#^!N1Plf}c}vts*2#dH8er#6VALoeBt#eH`9wXa3-yt8lE0Bdl7Ql3 zV8sexzyMt&Ch9;vs0;Ox!y}dwb~})j1(o$gUT42zi;Ik(2 z5;yUaM+cVkUo0gY4j?xdm^TmT(6Kh2p}#Fm^8_AuwM)p83GBr(yPP9QH2m_HwgjPy+=EX*TD zLIRMQ3Ybj5!-t+RPD6v|$a8+r>--&m&wRehX8puLJj5js)PZ_X7wSWu++s0>GI~~Uxj-%PtEN;z;3SiSFV91bX*6iOOShEHwD-*VbJ0J72 z9G0h8j`grU)+<;x`I0yJQ->F|c}XlK^(*Lygz^LwHW&J5P1>{3$cy~c=%7k0B}ElPVex~-xMsTsrz>y-Y%UuT^OoW8=kMV1 zgx+WkHQa`Qc{>H43mo|<6!+kG)YZ_TBC&8Ks#+_A z$4soBIEW{Z`~oVLl9oJJB7D1NtFXGP!Z!}q%lbQmSHQ($lTWNpL{|pu5192jQ!F<9 z19bmimm8?q&_5F|oe29%%b5ZIj4c-pk+t9&1{<*Oow}!WUdYTg-iT^5&bZD!h#(ey< zFcvQ3vwT%CMx>nj@+FajX6WE+P%&HQ}eM`MCH&7^#_2yj81>7+}STIx*`Jp z!lEl)g=w4rbdy(MUHOt#Rdk1!torMF6|e7K`NEK-B(a4d%-59*L-Zv_DwiDTi`g_R zX498Rs_b33Oj0Q4o4Np3v*q-q-6}%gG@x%s3CwmBeWQ~~vGgr;D#X$^XR6qosc-vL px$RfqP_AJ^x&E&TRR61j{{bIsbi+MpD%Jo1002ovPDHLkV1kU&b&vo6 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bj.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bj.png new file mode 100644 index 0000000000000000000000000000000000000000..211a163b693fcfebfee70f4157b24f25e3c2fcde GIT binary patch literal 398 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIX?)FK#IZ0 zz|ch3z(Uu+GQ_~h%EZvhz(Ci)1V~P)W79;@kei>9nO2Eg!&BvbQ-K;JK{f>ErERK(!v>gTe~DWM4fKGAj? literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bl.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bl.png new file mode 100644 index 0000000000000000000000000000000000000000..34c9a878705325445afc976cb83b5ac82d563661 GIT binary patch literal 7828 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bo.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bo.png new file mode 100644 index 0000000000000000000000000000000000000000..0143a672c1211b61a3542d961293cf6738fbdae1 GIT binary patch literal 396 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIL8%FzC^e4mIPLD#*#w$;iaB@JK^LVCv#iFIM?< zN-Q{#;KO5(;GxW>0#fDXpUUv%@9Z}#!Ii-$G6GcOAeoAIqC2kE*mG?~rYLEok5S*V@Ql40p%1~Zj hu9umYU7Va)kgAtols@~NjTBH3gQu&X%Q~loCIH!(aUlQz literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bq.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bq.png new file mode 100644 index 0000000000000000000000000000000000000000..2be4c18cf9d55c35844fc60cd68f1e9b9af758c0 GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI6D!U%rX*Aos5h;nMWF00#g^C zVq0j$pm1z?#Lu1WQ-EfumbgZgq$HN4S|t~y0x1R~149#C0}EXP%Mb%2D-%O2VnC}Q!>*kaclTvydoZ`K@wy`aDG}zd16s2LwR|*US?i)adKios$PCk U`s{Z$Qb0uvp00i_>zopr0E#4UX#fBK literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/br.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/br.png new file mode 100644 index 0000000000000000000000000000000000000000..4e338c132291d25109c21a290e800f3861826aa3 GIT binary patch literal 2578 zcmV+t3hniYP)001}$1^@s6wfF^v000TuNklRES~;SYlf@V$^6fc~tUPqGXo@6r~L_ zL+`>M(uDvDBE|xuC}!7gin`DKIeYKx4ojPvZXKp#?-aE2^C#J(} z(oALs!3>reEHhY5Y!KP7;Av*C^x50Nj`%3q8j9ip@JJZf;Xsph6mPOqPDW7SAl$q8 z2mJfaEBNuoYxsNq5KM}gB3?IxMK&VD0qakVLiv?mcvja%0Hv{~^zY&ydc!MujPSbO zEm-U=(<9$S1cltg+OCa2YHI9lCkqF{UTNE|Ue+#PjSO{wk9s72xYh&Db%4~+*y}3D z`!=On!GgT8(}OkmJ7D@ZoYK=G< zqZ^*;_{>lAT1welaEX~9uJclZH7-@F|@Uwn+fAQzm?vckPEP=P00L^v4>l*UHQK8epL&)ub5rj5+W8++oL#dpu^9$p1D(Sp4NMf~?%o+@qB}tVnLaOwXL@pmx_rivq z+c3^m0UKAP_-pMFK_Mik3K(uOz?@ecz}0gD^pe@jB(XkKTMEh5W?->YaTZdgpFvXP zQQZ=#a&vtA@uXshF`m4rAvjh$!Eqc(53CSBX-kC9At)jpWR82#hU-Z)g?{%L{NYJQw@qX$VNlLqK{4 zRE0VC_N)RoAKpR!?LypmQj64TH53=(5tNgLeJ66@wlM{}j^<#|#w5MTJ|^1UpfmnL z_jUThn^-7;%*u306<OUqecH5lZh=!2kR)q}OC3`a(JsnWb3y*(njGx&Yg;ZjOQpFHs z66)>aBW}Q9NfhNDRV$`bIqQ8lfIKw?QTR_sc`=(+z-{PymCs4DAk^!b^yoxl7?(f`*}OqnK;Z%(tq zwJPHqbB2J$p|saO5A)V0=zZT90>pwIPhK2}4TlmDU7Cf2Ybj7wBxBFXbl5IdnG`I> z;nLNfLZLJQOXFQLqb4D$q`zq0nkEdl>gGm7N*8TP5&$%O{@sNt%wLzFm9+BnvsT4o zk0KT^mr`)NEDXoevoO;;is*y!VfcX9e)#tTFimCKJPaEe4+A!mmtDA`TklCt@bwjt zJ%p0C6u#uLCLUh}7Kr!jdY{dFCD!=HBdjD@cv(zwK8_v_z{Bf3j0(NAsiW~uh@&*> zz-UetL(hRaI|+KJgWRdUspq&P8b^q1PW6avL?fD->&FRrkCHkA#oje-X|ztjMv|Ag zZ6?6-&fvkbNBH$oFnv*7O_tOplTsdRa!;tRm5HC*#SR_;n6oMlW! zw3yf@xhY}i9!WYMp-Fzz=i9N&CslYredBI{0w1hT)LI26exQWyLX{*=w%acU_8&%JHdV}} zoBeUOYOv(( z4SQHq!pg5PozUbq4q<*ThbGG;4owDo3RTgS+fIr1No~;>GYOIzH(Bn7#}e8#+M0h1 z%6`&=x_B0Q;>?w?;=P>bINz}%;IMOA5hEVkxax2InPSFhR9l%FfqzRMiAo}G&+1-j zRa@CgViT}ov(VWqPUI;mZDsZ*CfhqsTVPa}eftkA9EiMy6#WJFYxN8)f}{^DEZz@q z1e9+;z6e>N|5n5bu*AS(pZ0;pVsW&lW+WxRa(0tIfvu+!M>v(G#m5-}{(me9y_fl%&e)l$NDRhhGfg^1PSIG?(k= zCRhx)+rnZ9+rP-ezAG3ETSeRMpZ+E*M{}uMC!K#w?^MXpl_O1O(=&0bNyqf-H!%_QDR;~2|D-UpPFq6Y8Gv` zWmx{Ei?$v9w>viV`=>8x2G0RhCH?W95nC^v`iD+0)&(X&{`~+k2WYP!2fS3jH{>ce or~{tM{NqeBSZ1)yV7*NCUv>HHWQ@%=UH||907*qoM6N<$f<0#Lq5uE@ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bs.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bs.png new file mode 100644 index 0000000000000000000000000000000000000000..731bfc6fbbc6b2e1ea8dc795d6d159f1418d3bce GIT binary patch literal 1245 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 m{(r(c%0ztm#Z|k1l=VLe(suAVm5Dn50000p zmi+(!|LLnQYGKP6ET(669s2k0|BqX@ZdqI3G&Q|tX7+yN%Aa@c+;w;VcJboxH*fxZ z|Ni~z)o(w3-+A&;HFQ~O`+=|Df4k&vw$0eUHfi^2_mskzrCFu#kLIsP>ptbot-Ixd6ZhKl zOy0Ybx9r;M(BTw)KZCbzp@8PKXzxCyYZ+^zCugd-8)lnbU+WsfPN&PEET Oh{4m<&t;ucLK6UKg#e2H literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bw.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/bw.png new file mode 100644 index 0000000000000000000000000000000000000000..cde0a5f545a4731e76572ee184abd343b93b7a8a GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI;u=wsl30>zm0Xkxq!^403{7+mEOZSlLkx_p zObo3I4Rj4mtPBhyuCQ%K(U6;;l9^VCTZ3ljK5n1}NstY}`DrEPiAAXl<>lpinR(g8 c$%zH2dih1^v)|cB0TnTLy85}Sb4q9e0Nqh=0{{R3 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/by.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/by.png new file mode 100644 index 0000000000000000000000000000000000000000..4f3ffd8859956a87d76479ca7468299d8c30a955 GIT binary patch literal 1504 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 P00000NkvXXu0mjfi8)X0 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ca.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ca.png new file mode 100644 index 0000000000000000000000000000000000000000..3790ae51751e2c910d71d7a3d148e87b537b6d3f GIT binary patch literal 1044 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cd.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cd.png new file mode 100644 index 0000000000000000000000000000000000000000..0433ff7e6e4f63e08a7cf81ccf50138baa6b5b81 GIT binary patch literal 2653 zcmV-j3ZnIiP)001}$1^@s6wfF^v000UkNklt%hE-)u2w<_5yL|; z8jWc}Di9tu0U-tm5C|cJ5SYAq$V)(80YZQzv?_kx-?`iw6O+tj?o7-C{y2XPWafVN z{Lb0?+k1EDFcG-Cv~|VAZ{V2rIc6<;4X4T+IKSP5x>6HPrCRXWLt*G}StM=!B>Egi zCVz}gdA~zVMF`GUm~eKz83)H(Q5tTC^Loc+v96x<6(Zsak-BOOPL_66yQy}o3CCxJ zV0(lOMK?I$yiQv_i4_)m2-igYH|UY$b4>a;2K%=4McsBY>dH*0T5Q3dN5fETc8lb* zSRE&QiOFeG5I$pf&|TlFlUQBeti+a01C?0ZUBePH+ca|bzMGH#tR9Uj7jkACn=Z)RDadtpv`DBH zi#0Gl2PaCqsW0YY@W$QN76Z$e^hr42?BfnB7YK60yhqNXSbQ5lnq1rH`tjr%Oi7PY zyz63dm)T+uHu-;k7=^Y8g6%>NB)D=ZpvYXrG*f?QX-&myVSFevRY zeGlR@#v^O}BUqg?yxv-q^Dqt;^>PWtb*4NF8#n&`JO7p|ea({?o|w@pJCEVafcNr{ zw&p28&aQr2%@#9`#)hDxzYWgL#zZQU2>!rWYkjeYZZsb-9a?cBLcH_wH93aTO zFUUDBB3IG_Ntxr&Z&s=2n6SAIuZsLiiK4Df!60Sji|UZ2^0Z-JnxCMucz(X4@J_W0 zeMU}jfAIZv$jE*|UDGb)q6N8M*o`B1n;l>LU69MwkULu*igg?QpaSP>y}g*4`_Lk{ z0KF^TJ`)zsR_R1TG5LVpZA`33BrWv`xt=pV!)(bj4;SC6Ay+KOrC6|Qkj*%9p?2&W zCCGguyR=+Gj>pP`h3~*L^;pZ&vF1J;^uby=@E*d@4_7wnUGdAf+fr7%s1j=m=(}Nxk8YdO=cX96y*B&0lD6G9DXAdHF<92m{AN#Sml4$ zBXGIg1r^oDXZ#gMi*Iowmn6tN;0NTag4}39F2jvn#g+)Xk{XYj{efI-iNznwAI%@H z5^y3X553_$|z#`;Yx26qhd80_iDF$iQZ z$e_?LauS>}NM*397e^SxGMMf3eKn={f*jKYrVUIVlp7()F|A;F!8F4Va+2mS-C^3p z^oMB>(;=osOpm_Tx@1d=mrOjFfHD!)kz<0&M77u`a*{AJab^OoBge#=2{se$R!7cP zv1nn?%Aln|YeONIYgU#Atq()Ub#^EVhE@zM8NHH1tA>t7o9YAegd9yVnq!qZa!!*n z&uF3fFu7gN zuT|-XB_Ni96mr{4szk(6kzwQwJMvsg-pl^$5ov|6$(&GNKDj^*kcvnpS+jBOaXJ8Y_~&holm zLSxyT4s6G^j$C6)ucQz;HbSr=!c!xvr^Ut$HfXR>Lr0Dc9IP#}p(AKp!WD997S&qg z7vzS9sg@tM{-{2PqTZ?{h^;|a1QT+NE$)D_a6cf|-=>mGB%lY43e&9 zkIRrqI&$NzsvS{Bj%|u;S8P~k41TeEEnb=&s-cyQtvYgScxB@&8(>?rm$c1_#g=Hc zMzckltAw~{1T{6fuCze7k!luSuIrgfzYjYfyW3e2Mz2s-9 z6><{6b8wmi)H-q;tmc5VH!Vb$%;E?>NAdN*k_R}O(%NlcX<+>y{i+YHE*a0{00000 LNkvXXu0mjf6k`X? literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cf.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cf.png new file mode 100644 index 0000000000000000000000000000000000000000..3913150b92711fa2def3a4ad5cb83171a7a3bcf7 GIT binary patch literal 611 zcmV-p0-XJcP)001}$0{{R3f+qF100033P)t-sGBW?3 z7~UomqZg4)3nHihmrnrW^Z@d6000300|Wel0DkrW2@VeNNJzH=0{w;n zdH(Y-$eSL!R@(l$A`UVF1#Kb%Q{{WShY6=SV2M72gBixINbg8LU zudh(Oy+Zc(3GD0>xVT5MvQ4b4Q+auY5fSbL1p7utxys5l{rv*?_y;H`*CZs{rKMW> z`UQV~fD#hzJUqkj@DJG7CmkK)Zf=qH_XywLAp83S^YaWuM7szG_u}FmaBz=`igksB zdLJL)nwn)gI>k>-TYa|NsC0d3gYu0ES5b|9Joa000N;^dkTO z01I?dPE-E={`>v>{`>v>{w9|yV*mgF1xZ9fR9M69(pOIcK^TSMLFu#TipbCwS3$AT zEPzxiU_*re|64>76262SO=d2*_};5c_TDl>(rgd2?m7U)zxvK8Dq`JDk znNi8xnu%{;Z_KQ_rjdJWmD`FQZH z53xLdtT$jIVvX=ve8I-V8sjlrY_X+jZNj|7^5U`Z!TiMXj!^3qa9MNyg&c|002ovPDHLkV1gC-B$)sJ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cg.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cg.png new file mode 100644 index 0000000000000000000000000000000000000000..8e30e9d7e039f4bf644c454d26f3ba3af223ce8e GIT binary patch literal 1122 zcmZ{jYfRHu7{;F>CRjpDwlYvc#@sZ*{Nuk+D6L#tXrbjcTDl0DEZhphK(1REW&_!V zQll zVG8pg8W1Ij3s^HomOxtAa#AS@Kqa2bVi8uQc{ z2H6eGEEZNkhCl*Aj35liAWaU6GUioOI9UmCvTDWcQac@NDp91D)1*(WH%|1<=1LmS ztCUGKX+K_>)b90B@jm}W)El!e>_BNiuP1YG;P~l*`HPkA%(L0U62G^ukImie{bO^b zbvrPuSnn7EPkt6yt=z0A0v+H|xbK0Y+AwX#CL`E~x8rpKA7 zn!Y0z#xydhvA)?_sQU0r$;VZfHB6()sXF$W-Jad?bEEotPRE~(YJ*$c#<*Ks|0|va<4?WYhN`(dpUbsOQ^TOy+@&aGoe39m za~xN+V_frjALo$ai5J`5`zW?k8t{YR++Mc3?{RXc)L}GC3EA%NTBHAB4y~;6R@!|N ze_w1@y>s^a>WrFa>CZ1pRwLD|wm9Ex$0JQ+)wSP(V|e?YcvZ8`7wS*57EInoYgFRu zZEK6|)J{futzX#F#OvzU&G5RKJfpmA5q#UwK*=`Y3h3VB*ZPG*HxoL#1+f*_VJ zVAwE*6=~QYv49l|f*4jHX4wPzW$Wef--4pzoc!F6{(r$1fr(ZLwyd9!TAW*<%PiAE ir_%-IAAG+&JF`R^SX`FZycoWf20;C{r~^}|HUKr+tKs4hyNeHuz%+-{?;`5_we!m@8AD_|MCCt-{0CM|6jfW8c>z? z%LquNd%8G=L>zv5&64kc0*^~z8HaboyZRd;5}LjNkF;jR@2U`u5Mxp-@z=5q{;+kAgZk>$!;C$dU=z3*-LRp8&t$9>Ja`}5kOe~NGPBR2fd fIVUZ=(7udODMVy*LFuG;pp^`su6{1-oD!Mn^>h|+&AfGYG+uem-EV1J?ki%Kv z5m^j$+;tFUbkdkz0Tk5qba4#vIG$YK$keltLm*{Z8v`fL;!|!@d$puP%~s`v++49K zF!f3p8-t0`^8Q)>9Pa=%sg}4#l%yn}eU1IrKtBP$a_DcptHiBg+PlT2Kn;>08-nxGO3D+9QW?t2%k?tzvWt@w3sUv+ Wi_&MmvylQSV(@hJb6Mw<&;$TIZ*#@~ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ck.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ck.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb42801cb4727a271b72a51ba6a0e9d6a5606fb GIT binary patch literal 2986 zcmV;b3sv-qP)001}$1^@s6wfF^v000YdNklOr$v< zidS+-Dn`D9l+K6$_x!Kdn@MlI@wR&Rd49j^S3SMY{oMEezV7S(U;pcVjUxK@N7Aul z$jQtEuauMwtX>_2#*Js8Oc^T}8Ce!;lO}m1Eiw|Sh7EDn$Ve=ta%Ch21!3K~V3aMZ zTRU&OF%7e4?!wK0Lx||x7w3$P5!SLLqSv^i>j%y{wNb1VQl$!1jT#|#(IRBT#tLAW znb|mfItoLFu0!R@HpLEBg$mZ_)$0fB^E!+e=S8^KqzOW6)Iju@F-TFVkeHB+Zrzra zJXkRvUbxbu2SO@U!jKZt?35G%Eio|_n>U|CyLR(YuH1ONgGJA6(qtBvE!c+$ zPajt6#_Az+I#qfYb1!$M6=5N&CR)bMa1 zI~zGUIf#mihrRtS)TuK~Z(vocHW?P9JaEb<5K$AXalTe9s2ViD9VaJb+`bLv#mLIa zMo35uMvQRB>#t8Nd9c*6!WL5}dqd?DfXLzI2(4BPVa=N(&eau}_wUQ>rl)7&@ZqcI z*KZ{%R-E|4U>O^a$A=#-!uItC5xv3%VXayrv}#pE4jF>SCr{?a&bxOXVe#Vqc>C>{ zI>*kx1S}c{^xk_;aCi9?QEN9Kyh8^WFavGkp+m??PnSVFc#w=$tAfy?g<~!Vx`0)t zj3wTB%N}!-0Djrq58PPzneeu45%1}Vti(iVKQ%Q2{{AX_{PFiFUw%R<0Vxky8mm-k z5(W)igP`q)5Ix-<7wXo<`8suQ%gzpIH*U!C$j;6|cz7(vkKY0llWz(Ii<`utfvc4^ z4=Zgt;6lB6Puo`8YSX+&IW9EiDs1K4-$h+Bmil^BKyA9 zr;lFR4l}nvr#j3hG1ry(Dz$QVOU86Ju>QP)U1STn^#UqtvGaR3Vgct5@UtfB};H-cuNT%ELsh zV5O&Lih7^{>+y*bqH09+>xWy@rs0w561HzUg<-?iY3;{npZ!=$QlNZpGUaIA+))zd z`}ZHK$EkP(NhVVjNqv+=J8F%GB;KyBhqZ#`=XU{1mh2Z5LE2Hg>G7#k0yNbIst!~i zwyzHqHG--{sZDqC-rx)}L1v;X%a6-MNuZhZGWlg<9c^bXiT0pDYb5b5ERlZgHEU53 zm`8G1@nU|-Jd^pRnV;%ofP6PiuV7IQ>Ct1^^8@nodO9F8GfAxT z4oFu$OFa==MP0~uBI?x>Rf6gSRSHgot^?7gu; z;sEVSg++tqD@CyD-1+&XQWWuzzf)bH%241EU%Nq;c$+p(1xnf)BOs=xGo){oD^HRz zGIFFl0s_LYckg)&8RCX_-kFUWHNFu~V;?^M{HLOUMP=dKxfpd2R%V)#?wxt_Bq^rU z8#R7{NWWiMY!E3}U$QdQ)_(n5M19T3$a=bYxVj>&Su>#_k>=)5`RvCOn{B9Cb+Xn$ zu>JY-f5GnEp;)ovAbR&+E}p+`-S5cE%#uxplT*LGy#Tav<1qvUU4^6LUY#Z)*SKv9 z8%R#ZLtkGh7O-f*;z5Mx4k;!yX)?P&Y4>HSJCt6i?^EX|jUWw4y>v--Ox6Qf7q~oQ zE;_ZEneX#8Yfi<~sXmB^h?8*~J$fCrYT3&Fh7Vtlt5@%enVGrDJ=PIex#GoVY;Ao| zz4{d0z@kKwa`7Uf$Bvcq)5XS(5#ziN{$7XCyVr6lQx$h9jI+xM5-UlpD9Pk0JdBkl zR-BGjtcrH!20kg9O^~p`e({-Bt>&VAduMoipGI6<5{@5_l-SF6_UZGJNaYWVdhLe2zGSrsP*(91C9L2H$3#vE8^1_O$;e0$1`c|! zJvoD?{ZC+L{eNJ4+1g?|Dw*QA#~#d?vsdSW13OH<6&QF0p`ka$&IFu>@36^eQNcVH ztc45z15eKrGU(Q=zmxl8$9lrTVxts?SzhBi^TtZvYtXb~I{Mw?UOEHbrWS zyA{RSP|&8$yrPlP z(rhfY?$~h_(b4e|d#tUuNxAGrIT*b$I5%oJUVQ%h@rcJ-ib8Nr>z+y1Awfz~% z$>~C#S+Rcq{ds!JA2myS@ZhyLfBu#b+1s}h@ZER&@^CN%S%OSwu3Wi`n3(&Lx)jGd zNK4$^k0L24O|qJL^`;x%GNcFf?D>Ndh!>!jLX=4?i{m{^=jP7!llJLVT!sb+mX!oy zbpW>1DXVx9BYW2{dG5H+r$Gh)mYRtAN}OuibhaFkDz4Z_`P$UfL1;<4cFu+c76UIy zCwUNcFkjzL8BB4#YsQS-2n)L{vGJ2nzR#1*lyb0`=kag~PulRJ)KXmMw%D$n-A-Zi zly#_uF)4paQ4AnnQHI&E;|zZLEgTy*922FXxF*KY(NErS?AUctES}ZT3?#sh8RIDo zo>DWF(Be2c$t6c)W?*T#IiKQ*f$%KOFm)#~>ej92qGe0{dKb^&H)}RWCV`WYzcOne zU~$H6Y`n2;+i3&`Uqkoq%k9o@9QW zVZWGB*RrzOg82Bya*A>G>^)jEJp%!&VZ#|%zy5dG)~RJN5UcY;tC#jzO3P&Kk&*WV zFcyZnBn=iU3Uy@EUAr#9(xt!V2H$tz{kBLFcwVZ|r_YblHbHZC{zWzt0+s(6Rs&N? zIWPjmyp6R#509ft{3_b_dbml@D_IsJ9U&DVduOdtTe;65!O|=p9vz^Z!WhN@v;IlX zV-BgUhQOSXUP!>XS=_w&K)yM0>Qt0ag~By_0|ZO05KKW};AJUXoIH60AAPh~z?eRL zr>F+pE!(!8FENeel3qwDnwmSGXAxGV`a?DgdZ%u+LjwhiJd4$5mawRx5-55Vzrb+s zUJ}lpjTV6TSxUwsAvcxaorIMu0|h7+K4#7GRT5?|iB-CtdoWP27SUl`i_@% literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cl.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cl.png new file mode 100644 index 0000000000000000000000000000000000000000..11eeb917a2b8e3ff76a527755705eb74ac66c284 GIT binary patch literal 631 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!3-pu)V^+KU|%s0A*-fMKDm79rKr%=ks+%) zTMtj3a3UpsLwEa;DHBgbhp#Cu+}TjGFC}3^Vea;6lTNN&cDbo;e|hn)_^7ox8C#O$ zH)N-8$xPimssDI<)Vip!)g7&eGJU2Lht0fy^!)F~_ipKm-q9C(Z72KQN&c+{!|D}R zPMv&m{o1QnFTZ^J`2FY4-@kwV-M#b9!Tt9S9e8l~(1S-0Km7Ug_r;6P$BsTacI4sF zBM;xd|Ni;YkM(P>J-Gk=*|Sgg?!9~Y^7EbBZ(qIo^7-?Rw{O4x{Q2kCuRk}gzdm*1 z$&Vku4;_4P=JeBjd+x1SdFAGf*TBHJ)wCiCNSTxb`33(Y1MH1Ccn+xlp{I*uNW|gf zgaf=1GiP`*@hnc`71|sTu_9eeTl(vnB=d(0rbW(KmKJlxsQdPvrgpBhjUPUJ`p6v- z#mU9V&HXtzMQnY?*50*q_ck{#p1iqx?Fs?ccaMZhN*kEW%MG~>dwbkD%#|}`d5g-? z#Pfg=P|WAg0pGbC+U&R#mkTT=4XgNf(Yj>g7?f}RZrXH+Fz nSrfR~?W~k(Hk4tN#>g;h$>oVJ=6^B*dX~Y{)z4*}Q$iB}>Op4X literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cm.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cm.png new file mode 100644 index 0000000000000000000000000000000000000000..6a2b9b38ff271c396e504a581447c78ec63979db GIT binary patch literal 763 zcmWmAeNYPk9LDkAQ!6zsmzR*76Bljc^0JxNTdhU4WtDdJ58=vPH_q$fIMQ|JWp$TQ zD({n0;$%pNL)*&MMlX{tai%D3trt!6R_2fA`~3eoBa2_pVg@mZh!wLTD$)IM?g@N6 z-OUVj)(~N@OtMK#MFH$XtQud`5!L(C@KcNrLeC^EgX%!-o2hX=^;e=}3z-omtiw4T(HhcP?er%=N*8qvXC2AwZ8uckj;`1uZ|Fskz)trvYdNaF(- z>qloQ>AXodfrg*qu|M9%Qg;@yTX~dVa_|uIRA${CKwreo6Nynto7IdG2-jC`hk=BEnR?w?Jw>_y% zGlSju;+Uw2O`EklQeI$2aZ$Brpqw9^vCUGIpoumI@l@=z>ise^H-7Q%%3}R%Z5&VI za(OW-QVVnMonBaAnmlD^er{==BC$oOTzcQKBrg6+jx^$2h(>Ty7$eCRDZ~XiQt4%p zLLo{@lF4IL2Q&{39&nDZ{Bp9Tk_e{Rw1|7BYL~L#ZU|QyUN-xRYBj3cvlP~wcE{Y4 zl^v~(7xO#sjSe|Qa_;9j!iY8L4DX7U=PSJ$&m^1=6^J5}=d!hR^A<|C#-0n;s0Cp> z&MKd><)Imgn(bXeqj2|kRe2NlHAAM}D2bJ)CP}TOCT?L0pa0Xcyn4is$8VDuO~&CQ zbD&2-z`B*SwMV8F>x%~zvRe%P!_zg7v(zKW=`)hOIZ7jEA3JObN0{JOk-ncZdzWW+ i2`|0vmf}WQK=?#DF(|*icCl0XA2voDA9Xh}CG#I$KS5Ri literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cn.png new file mode 100644 index 0000000000000000000000000000000000000000..1e39d2ca7c46484aebb0f9187ed5ed7f68ca99c8 GIT binary patch literal 782 zcmV+p1M&QcP)001}$0{{R3f+qF100048P)t-s-YF2? zDiGc)5aBrw-z^a4RSe@p4(xOZ^P2|vw*%!*4c{*h-!Tv7R}B5q0sh(m^r8jjPz~l- z4C!SH;4=^Jgb4Pk1^U1P=UfZnI1liR2lSr?-!2f~G!Ni55AA#j___o8#scF)4&N*g z=U5E=)dA{i3g}-8>TL?;Q4Ho(4Dg8u|J?xp-T?ER2J3PP{LlgCTnzD#2l12#@|Fhs z$O8P%0rHs!@sS7UVGH)H1pe6o{LKRCW((jo5AJ>l{LKOIiwEI44&y`)_N)ZzX$t6F z3+G!5|J(rizXRVe58^-$@|Xtnqy^+h4eDqL?t=*Tvjp&r2mR3j;yn)GIuHHO0s6%P z>uw6@Vhj1b1OCwFK^g2u(DB!vg%w0_kT9{nG*d*8$=_4gdfEQS+vO0004PNklU1brn%E`^kx1*9- zwgrXKT8vaNOGHCotcY5EWMpC$VW1jdWEcgbU=)mkQ7{Td!6+aI0D-|N+>_3a3IG5A M07*qoM6N<$f~7@M7XSbN literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/co.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/co.png new file mode 100644 index 0000000000000000000000000000000000000000..447f8714e9d2056cf33707c298259f23245aa3a2 GIT binary patch literal 377 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIKmV#jMBhqJ&VvKZ)?>mbbNq%pe!C}{5K z;uzv_JUKyvb#a174^xiA1PO%$3tV^t5)9PXBzm-@y-vk+F!J#%Jkr3>W|HJ^XvL<7 z%Xt`#|Jk-p;cx#7G(xq+HKHUXu_V=o(mt7#LZZ7+RTH>Kd3> z85nr7`OZVpkei>9nO2EggGKaLC7=dLkPX54X(i=}MX3zs<>h*rdD+Fui3O>8`9C&D1= d@kJ2^hCPw@j_rBgYXLNw!PC{xWt~$(699UWLU8~9 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cu.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cu.png new file mode 100644 index 0000000000000000000000000000000000000000..160bb489ba133b6bde253b8da8ff45cdbbcbf698 GIT binary patch literal 1439 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 M07*qoM6N<$f`WtZ!~g&Q literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cw.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/cw.png new file mode 100644 index 0000000000000000000000000000000000000000..2365ec8454f5e5746fa07f78b625de21b2b25169 GIT binary patch literal 653 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 LNkvXXu0mjf?&5f7 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/de.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/de.png new file mode 100644 index 0000000000000000000000000000000000000000..db3f7db47d18f7cf7e0cff0e98789d801111168d GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI)M;clJQx~6NTWG|f zaBO+R&zenl&`0yRj2YzWRzD=AMbN@XZ7FW1Y=%Pvk%EJ)SMFG`>N&PEET Oh{4m<&t;ucLK6THrDhQT literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dj.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dj.png new file mode 100644 index 0000000000000000000000000000000000000000..3ff60b0827365f38c9af38b857f243b80581c427 GIT binary patch literal 1150 zcmWlXc~H}L5XFB43o#U|NU?&$B}YJzDOO7>7RW#;1!9mWF>NP~)-gra(%yU+CVHoZZC6pS-m7@HIkidi=Y5?ib>F-1da&hJ^3rZq5;>Jr!uYswLl83=ZNf8w z*;#06f~_s+?gln{UAtDdc*jNP0&2BD#gCedTGy=A%9zeVXTIt+eUeV#5!N(vrmV(U z;PhHGenv{WExYX`a2i*QMM@)g@^;EKtJN}wqrh=Y$vZ7QRkcucKyYAGIZBmMD;Fvp z1P)rAhO6X8NFr7>E9DF2_5%A6<;Y1%IG%@J(JYtE-?HP|4J$bm3FYSeO^$+dLUIB> zYP_sjDxEK}<=YM^2Fca(h|R(= z*nF)i%`(K~{IQgYj(hu_xRY@Cl8}n10NdxB!n030NA0&6Qpq+WV77J^x_V2teUIJx zN14 zs}}5cU*yFE-+8cnH0k&9_e){#qEn`jVV>JT*CWgSHYxjpyu7@gS|m;!YVn8)`YBzp zK0zQ{cb9a=ADN=1FL^hUD;XE2=o_4fdv&2%?UjeQZe8hd%u5^5?8`0{QB{{u?QQdn zoer-~`q9<9mfInWxu5oz=xu+|v!8WFIaZ6a9viZ>sl?M1k4L|_{`Pd3QJVTsmzN9~ zMzZ#MAe*nQgc`8T#iC;vM;s+KXYWdn8toX=LRD&ya%G{EXDA2#6SH4*If2D!ZWRgA| OSa2XY;4$gDy#E1x5R*s% literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dk.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dk.png new file mode 100644 index 0000000000000000000000000000000000000000..6eea46fbca5e5769d9c6d5c088baeb0c19a875ea GIT binary patch literal 257 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 Hu0mjf3!309 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dz.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/dz.png new file mode 100644 index 0000000000000000000000000000000000000000..85e5bf86bc77d5885377813684c123d89fe1d2ae GIT binary patch literal 1530 zcmV001}$0{{R3f+qF10008_P)t-s0KXG| z---YK{{+1g4Ym~-t`;Dv7m&n${^jWY@bUlp`w6)eETI@^|S7`dGvi|Ju z1HBV8n;D`|Cgve8@*p<#Qe^wO!T>~ zg^_$}BIqD5ok)BCi%^-Ny&Xm{!#FYFyJvPCFQi5-v7 zhyVTl|Nj2|?(qA!zV=*f_iK9o>g-~CAKfS|aB?6LvK9XC@%f#n`I4Rf_V?5-EKQ0W z1iTdg{QUgI%lymGqfaN?C@oBj90I-*{npz2(AE9q=KuHjON<=r9xrWlAOyS<{_XDe zVsiVnyroVju1F{mvlW2fiU0cg_=}eFBRMUg7~3c<=OHhEzHR>T^82{I^D996xxi$4 zAG$y(5wsP4#&h;xZ}m`O`H!0Z^7H-N-}FRR`@F)MRweNoFnenv0lpLb#>?|DM)N8^ z^;2Z|kel{laq=56@*p?+zQqo<6-kX8`J1KkAvfP9E-s%L{`2(rZG7?@GWm{~{L0VR zD=j0Z7yaGg^hHA{`L0%{{Ol^DfyJ2{ov%8Rwa9CBK_gy^CUX-Lsj!6IsC@Vf75^Wa)9$FKJ`jm z_=J%9rLKUiUjFXy_jH1-NGO5ZhW+5<^+;MviyUlqAp5()^fgKOl%D?R>Nc7g)+{al z@$>x2&iv2R`k<=+`1pQkB2I}Mj?stqUT^nzgnelu<3>#Tw7dMz)BW1r{o3C6g_8Gk zf&T053b+*WFGKS$MDr{`HJcgsSZVvf$M734_FHTEuD1T~@GhVjWqKc)RVBMVDb6x1 z)-5gcI!^UVUHO`%{`B=4trq^~=>Pls0002(T~y`(00O>AL_t(o!|j$$NK{c2$Ip)} z%gIVo$UrMlq9h~QNG_rlPe>4>P#0+;<`ih6DSn^@Wu+t*em0bZq~!;xAX$wqB1;Pw zf{P|Wa*-5fHM-Kkhm2f6qCHI|go@^09oZ|Aw^z-Il#& znQ*orUa&#|M|c2CddP|ZmRxoM0GlT)(?0m+SOtN=Ob9CvfTXg3l6ziudX~+h+ zn@^q+tG0Z*k@@Hx<5Ws4Rf4QaQA4;@g|*SNO?Qg9WUmp8=;4-?-ll)i{r|XW|D5hz zBg0Wwn=ul_&Rn3?>K?1j$dU`Iaq*u0dlfnLy&Z}%R?j+OOkPR3A|fIoB>aFs`$sa?H>~f7KT(6wk^-;=I)AYk=w2g5Q~EPQ`inhPEu| zZ|H5Ud2_{pbO(R3?J7}Yt-bB1FDgVthOX-pcu$8aOFnjW8ri?l>|ArS8`QK*Pm@wD zG!GiJ@Xtw?T9}hInx)-JwW-wTQ4-d#(W5bmB@|kfdPmYq=lZ?Ff8@=HLyA3ZB-`H8 z;wJ0JwfD5>402zo_F6xirPI-C+or;Rg{GqO@yYB_p8=*045qEk03oY=d@h{%U|Fp8 zXf}CQFycC!6rE-7vWGL1U%LN%%~4~KH(db1-pq1S0EB-fzJDSzz}p$q9AyqPx# literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ec.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ec.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae8eecacd8a932a2e89039e9eacbf96c79572e1 GIT binary patch literal 4071 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 Z{U4<-)eA{J`p5tP002ovPDHLkV1kOlv)KRu literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ee.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ee.png new file mode 100644 index 0000000000000000000000000000000000000000..2a4b9ae044d1c3a99bc689ffd5fed4228eaacee0 GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI6D!U%rX*Aos5h;nMWF00#g^CVq0j$ zpm1z?#Lu1WQ-EfumbgZgq$HN4S|t~y0x1R~149#C0}EXP%Mb%2D-%N?(ls!#GBDWY z%$$y*AvZrIGp!Q0hU0R(Zv!<*f@}!RPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw{mw=T PsEEPS)z4*}Q$iB}>YZk0 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/eg.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/eg.png new file mode 100644 index 0000000000000000000000000000000000000000..c3a6e9802ca355d3b833a3d85b1fc93272bff5a9 GIT binary patch literal 914 zcmV;D18w|?P)001}$0{{R3f+qF10005zP)t-s{{aF2 zm6iUKkNb{#{gH_wmc}>8|$g#rpQ%|Ni~`{Ph0*`}+6d{rmF!_~iQc;QROE z`u5%X`RM)o@A>uF_wdR5`s@1k-{sO;;>}3t*KhFUpYGw4@#m-U=A`lGrtRN~?A?Up z&P@6A*8BM7;L1GR#3tRuDd5RF+rb>|;Em|kYuv&i;K@1M!z10qDEINr-^exH!zA9t zEZDmS?&6gH|NZCHXW6_A-NYx|!z1?Y!{5g=-o+~3#xB{t58J>Q+`=OC?78^z(cZ-^ z9FS0Uib3O{QBO{`~jy&;S4a z{Q2wj>$d3DY4q&7`SjQL^V0eB*z)MC^Xs+q>aq9m%J}os{rmC${rUX*?fm-e{rm9# z{PzC+_x=3!{Q2ns007K0pAG;302OpnPE-E+`uh6%`u_g@{{H^{{{H^{{{H-u4eA{L z00BlxL_t(o!($X>pc-HtHEYzYQL{$P8meY7P|++#!YY8^H&JGZ5oH$RFRGZu!2XY! z@&6C%n8n5ThxO}!ip}C+1$&U;12Zeb**`zlJzvX@{y^2d?!l0M^d z38zc?izqQG9+tem9(bC-&N!W-H1RVQX4(hA1&NH^6qr?Z>PYb=uB#RjU*F4iR!sRs ziMy(&{3^breasHzu%6PRHnkL()$+ZshLQ1q*T;6o#)eNb-VksXI}rsTjQ&EvED001}$1^@s6wfF^v000ERNkl-0S0aGHt^&j0`i))ojd7Yo^WVuDaca^X`w+?M~fx@3w1t&pki*aABO^;dfvA zo%8#CxsrsPD3c-Kupy#LwZwD@w~Y}wNHj^Tk{D!zM8-nmhQuz3arR>w84C$R;)KL( ziT*ZD_gJC)E%BbjFdHZ`77{**FC->OIBlrzu|kjKZxRb7Qf#d5u|l~bu}NZtjTIRS ziGaj663 zmo|yd5}{R!1y-mg`&nX+m0P7)VRdYk)=Q+@Skc8NvES{cbjA!yii;^JDX~QE2o@4J z9N_W5wrz0j8dey04l9(@R9Lzc8XIG~^7a7KUw|J@!m;B}R|j5ioMTDgazS1mR98bF z(6P_zO(6JT%#WRHZRA~<{)g26b)3MD%m%FE&E)!wbv=Z9@O;Q^2Nm`jk8 z13PxW<;!3g7T8%GGhhG|7Q&e`J^ONo0lW7?`tb0*#(1Er>h|3f48r-wnB)EbSQ0p$ zFnKcU+Xp^hbSrH<2a~6S?~$AWpH_7K*fn2*b#K9y77Jj7l92%$Hb8T8??+cca`4&rgra6$&4>MMA0DtJ;M z+YQ5IetJ@2R-TF2$CL8jzzUs|TeftL<@G{-euwKFuzu6+2S9BN6utzR4ihWW35(}J z{h64&?C(C7%LUV>bzg%80w=OyyRXoe%S00B>s?V>ujVF}Zy^T)!UON-ti7f`afp z+-_L^f%)8JQM8?}TPM28iF0ps4nEHBR)lNOe`s+TiE?+#j(gx`k>qxek`>GgLY-2>k kdT4oK*G@gcvJs-sKVsz`o|i~JS^xk507*qoM6N<$g4~&7#{d8T literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/er.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/er.png new file mode 100644 index 0000000000000000000000000000000000000000..d1bba3ae7c3213206906f6b7f87f06a2326f71e3 GIT binary patch literal 3413 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/es.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/es.png new file mode 100644 index 0000000000000000000000000000000000000000..338a347000602ac0d12a8a86d7bb1bfa85d591ac GIT binary patch literal 3364 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 B5q$sv literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fi.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fi.png new file mode 100644 index 0000000000000000000000000000000000000000..a8dacd0ab3b86b6cd75c9e258caabe8147eb9196 GIT binary patch literal 484 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fk.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fk.png new file mode 100644 index 0000000000000000000000000000000000000000..9c75d7abe2f0a738b3e5b649f836489e544ed054 GIT binary patch literal 5415 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 Rna}_L002ovPDHLkV1i!PMyUV* literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fm.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fm.png new file mode 100644 index 0000000000000000000000000000000000000000..7731a55da44e70cbfb16129e0d12cdd5ce3cd40d GIT binary patch literal 930 zcmV;T16}-yP)001}$0{{R3f+qF10004-P)t-sXP4G! zm)3Ki*>RrPv&-vmoY;%7;Og=DhppdenAWz<>;C@#u*vFfoY$9)gR0%D#_06-{POnur^DxPo!EDx+T!c? zbfDUmw&MBw{qFPn%G>eq_51hv{g1QZX_?o))9uva^NO$EYns=MvEbL_^tjLLZ=Kk5 zpxJey+G?5Cc%$32%#}We5Ksz@cF64=xLbOcA?sMqT7F`-0$@JoxJ45*YAL+ z-Gr;&(ctsL*6*9Ta!{GPq#`~3d$_WY8y;)$={ zw9D(+<@Nsm|NH&^ugK~B{r=eG^lX~fk+k8#)$Zi&_@u$+tHtQ!?DxOZ?vAqIsKn@o ztlp!*=I8GCoV(=5*zn8S@zvw>>+$*b`u&Tq;FY)HfvMe(vf-4r;*hi9z|`)@+3>s3 z?U}jcd86Ct@A>5H_@BMyo4e#}o7a7&+``uH_WAw3((S#{?V!Hp0001Fj8BjN00F{D zL_t(o!((6=1%nzeGEvVgW)@benZ?G=K`o;=xwv^4sbCH-AHRU0kgy1k0KcdhrEU_J zkQ4?1DQOu_$^%|j4rr9Tf+977OGyN1ma+;}f?ickT|-k#n;J&x=<4Yk7#bOKQo$?} zK2soR#>Y#I3YGNgb3ahZkLoCrl0mL^1~vf{EPLY0lJojrrOk^}Di=>U}H z5pg8KEGJE8J{MOBH=GLGJv_a<<$Zh!r*acNf8l_@AW_`ek2BamBtSScj1_-QFbI!` z42lvKj*h_>vIbnS!ouA0ae^H9RK+JGCaD2SE6HShrGkA*AW%&}YFfGhUYFTBWP}KZ zaI04r&xCmWxrrEc1#=H+vcBDtl@;tXfK> zN+_U=DxR#V1(q;%4%9G8tX>mnmU=@Yw@=0b2Avn9=!;#kaTO-oc4_Nho~Yo|CS z85tSvtGKy1fizV@zg(S-YRQ$UikWI=aVm{A3I{m=02&cKG~y+@^Z)<=07*qoM6N<$ Ef@ZiAX8-^I literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fo.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/fo.png new file mode 100644 index 0000000000000000000000000000000000000000..9c05b1e2660e2c26aadfea48aa4be3fde59b1f95 GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!3-pu)V^*9QfUD`A+G=b|9|-6vr^Df28-$Z z=KX85?XGKRT+z_A_ zZ?9O3H3jgvUOaGlbw`cMPtFye<(I0i_Yj=6^20pFH%i)_9buE&4hu5)Jr^+gsTs8; za|^M)4Q#Pl`MO&8mZ?lzs}EKU}s%zbhhd=H^YZ{Z%?x4 w{cU|}S$%%)hdZI?r9bV-{TB)`EamTaXdM}fywC@M}p9_HU>_f#i!h+_G(FonytzSxw&FfVCt1HHU>GL}eU1IrKtBP$a_Doc5!lIL8@MUQTpt6Hc~)E44$rjF6*2U Fng9xfX@dX& literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ga.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ga.png new file mode 100644 index 0000000000000000000000000000000000000000..999588ad3e0c49b91d79f3c546c42e84d9017a4b GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIpGEJF;8tW1oo49s*5 zOsotHWM*~9gP2NC6cwc)I$ztaD0e0sz2>Yfk_G literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gb.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gb.png new file mode 100644 index 0000000000000000000000000000000000000000..ffebad31497a1692ca29c8f25d1f1cd3bc6619af GIT binary patch literal 1651 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gf.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gf.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d0b2504c009ca8ad4bd0badceb3c1db3234068 GIT binary patch literal 1345 zcmY+CdsLEl7{{?X74HkEfLvY_14RkBxN5Gn*~!eZ8kxD1)~Q2Hyd39FW?IUzoJ||v zN+`<0E($uvY=(I$OH7TtkQed>YMNP&YMPf=3c6vZ&bB{(&*%AmpXWT!AHPQ!#NCDj zAOHq~Ap^Jj2V1$*`qr;pYK>`b@2g<2CFw!CLbevw;^`MW8e7o**W5Vu;59&~S1;s9#^G*+n??S6Y%-SZ5uhjk7Q&i-y z6#H!Uq#l&qBT_}ox+csfsr%4VRJ3x{lpefXLv#@{>zmPj1@7Sr$_k<9>;N3d^t24Z z=N1#rN}(s1$pN!C=rbJiAlx>cb12l^?F37~b`IjvI|wA;tY^BPivbNmr`Y9mvRMzG zYXk79Z2t&G8=<&0j?Cbg``|CX#a`Gz znQaD#*Wss2Py;05fFp6l57$p3S_a^663_+=H0VYc7uwCX0dqRwMF4Kt3qMtaG8^Eg z8ic7DG1~&pwu10XPwQXI4lJH1)x2_r6+a#i=I%AmTP*XIg#~L^1`cg)aP|!95b9W& zj*3y6%%>&=K3b8o-JFnIKz^T0nK}mSn;KPed(KaFOz@-a+5}-EjQLGcrqNqEsahA> zdRN-h9N(zIN^0A3F{`41;dZ(s&V?yW#jcB~#VS42fGcM9-GrA^lzc3}|4pyTW zm38w>G@XdNrKBh5;F0S`N|%q`aCaijZG`0vMPc_vRHLyW@X}ZfJkH17M6MySAHk(w@d__&2u(4Sb!7IHaKi4tL zR|tM`5sGOk8OfZT@j+vJ#r|UVIF)Qo^n*+Cq|n!V#hiGUAGkJ0ZF6?&TEgDJJuTJ~ OgavNp`rq>7rTqh}WT$5U literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gg.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gg.png new file mode 100644 index 0000000000000000000000000000000000000000..8f10041e37dea30ad101d4bdf91413464e7b6ed9 GIT binary patch literal 373 zcmV-*0gC>KP)001}$0{{R3f+qF10001NP)t-s|NsB| z;^pfvNaztQ=oBsPQzh|!A@P49>oh6#tsD8=7Wv&4>oY0sQzrS>7wt(W_|_Nnk)HE` zkm?~j=^ZTTA}i}UDD#pY>MAPiJSh0i81$eW>MSbg7A);eCiu`7`P&xuwi@-Z8vp&9!v5NlxN0!<}YX}(XTeOwC#hl;^kX(dR zv25o(wuL^=_gb#!U!L(j_vpR?Ou|P94vzn0v|j;@bo6dRzB%T-H001}$1^@s6wfF^v000UwNklhi-R>eNS$ZTBE0umg!es%E9K>A zed}$sl$PR?ZjO%re}Og9+lOmqWw`qBMGUpKqx(`BE}uDr zuJac#*wKM*de(LRJo@YFIar}R&%l_F2%|0u=7M=JrcXmzf)2*iG*sr#g_+gUbI;ZX zF*ZDm(f)p1r7^nMcx`Qbyv`3#Z;TwCd0G+35<=5 zpg$aj<<3GFvu48c;5L{F=Hfz9GAgoW!2A7&V3?f;eMTngUV16!t{r=@f&|u_JeY_L z7w*u(K-UT~b}bWTNK8WG&wkF^_lN4?+xT4$2m{EZGNUl(iemIsSM$YRZ)kvH-h5be ziJXB|bY<^iSN0yp(n=b?;pP3>2Wzyq7j=(3$|uYwZq`W?08qVW{RUhQg_J%`ufEEk zvjsaDIKo)DlE$ccY8P&fjYY@5ejT+XJNP&T0gl>Oopa?X^!Wo{jsV|D!Pt;tZ+OB`S5UbsI23Aj#k5KmP@KifMYm+DpVR zdC%Szq6G1!$%bvj6gRn*ofd8viljNbaA)Uox3H>Q%B+*@qA#C28`mrr zj1CQ<^%rl!MLMgR0C9^x9*TDk!5CJ@o`p6)z{kIQ?i^gC)2hUH?CMF3vR1l1dwzvP zDr?VTqPD=IFWyYslxj-ZwxvZd(PGUaJ2?bEWg=s7ZA9R>idUlL?O(#aaFN2oDv<%L zf{EpVDXeTG(OF-55bk^L`iQa0 zE$-cdF)ry+;+Z=o4S{(J;3g^IRt0HwJC__on2+46I(_Ob?VS_u)yC?Ak3Pnef~LS~ zBst)wEr@Hv!vCcaSlm8udj=To8{HJ_-6{YlwbXQkc0a8#X3=lxN*5x`tK}{HTTulc zb(QeT`_XptJ?I6nCgDO-0$9W*TAnBvDOPoyK8^5C_VPNJ5v_tv()|nxE_oGn&e$5>>gc3toN9!<~xlmin`jb~Lc z&@ULq01rL(cue<*F$JrY0=z*LcbHj3EMe~FRk=!V*DgeeSw;f4S_n^%h;Js97-hyq zEnOq;{E8nzPmG5$38>>E({%nV4`A%Z4UK|z@Q|X~1BBgHI>D3=SDGbD_=pI*2odlx%5)1HDCzjksF={oG|>%}4} z62{ZIbg#P2ThMy^HTVfER(9&f5A*b@80HcIoAnFr+lQ8t9m+e@r?7^E@x7vaUh}PQ zW4O0hgJ6-CX?l8(a%QC$3wH^(Gzk#ohPj$FR+!|2U9d!MkLjNVu11hqTV@pb7!N+OWD+EQH>=6a=ARb6ETdjM7>`$)Bey(N?w-rf_dt9mQ$aoW+QO3 zW}vIW!2dpD|75fIvVXO2J6J=sn-5dKty{SYwJTO4NIN9e+sRO!2XfC@- zV_Dg$XD)$~6fN5)^qH(|pYRSl0UKQYP1LVjkIBQS^9y z+WM3Azxj=gj-b2t4U7)=YHez9u>K>kMmjp6CA-nyhAZyF7-|bcYjc}YzqAONv7%*Y z-2OGRoSKiur2a|B39PJCH1AAA%f>qpOixDRhD3x*(%{cbjf3@> zHa!I`B|1cY^c9L(BuZzbK}++~7FcZan>P*N*E8T*lnJfPO>2a{) zV8sHY%nUg0odx@*JOp0Pje|9r*$Z-Ddf_fu_Af-uyK!bsYL4s~xU{MehAoRw^V>MH zCI@TQOjMKCqEYjm_;J XZo8gEcb@2N00000NkvXXu0mjfb$|&- literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gl.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gl.png new file mode 100644 index 0000000000000000000000000000000000000000..acfe5fd5179b28eea4720e2c85d6bdecab7a7a80 GIT binary patch literal 890 zcmV-=1BLvFP)001}$0{{R3f+qF10004)P)t-s|NsC0 z{r&y%@%!Q7`P0+*%gg!9%=*>U{O0EV_4WV${`};^eD}S*{q61g+S>1cfaNVM;0Orc007?s0pSh~=RH00o16UU>GGGC zQzU_PV<9 zk&)_KTjee;?Q(Md_xJqg=k9fN;~*gMkdWaG4feOU@|v3d^z`5i4C`QE`Owh+`}^Po z1mYJL=t@fPiHY{RyZYAF{Oaoa;^OtNu8oD<-yb4~r@GFfg$Yv0D+lF|oVrbNfm$wiClQUf2E~;CG$#>^u_$ zJw3gT3}N9Bkx|hxv2me7#`uK9q~sJ+YFc_mW>yHCAv-76jE_W1UVedADy*=`if>89 zB{oe|X<0dbRZ&@`fvT46P)Mq&<)-TDMJVQ|;XPSagB>cF8=JVO<`xO6wK}=1wssJb zc}8aI5J{G~ljG5Lxj;;2AAel8m2A0tm@Q8)nS$8Ie97>BiiLqeR%^&jvxb?!Ueh-v9sr literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gm.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gm.png new file mode 100644 index 0000000000000000000000000000000000000000..154552a59491c463c94d608bbf2403163f36f0ec GIT binary patch literal 424 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI(TAQv$vdAqxC zWnB0Y2IO!SctjQhU3ndZ8J#p{R{#ZlJzX3_JdP(9D6&}?^k_+kw#7?MRG1k=hh2AgH=mhBT7;dOH!?pi&B9UgOP!uiLQZ#u7PEUfsvJop_Qqbu7Qb_fx&)1 z+j0~Qx%nxXX_dG&oD*(64b&hBvLQG>t)x7$D3zhSyj(9cFS|H7u^?41zbJk7I~ysW OA_h-aKbLh*2~7Z>VSNz* literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gn.png new file mode 100644 index 0000000000000000000000000000000000000000..ecc7aed83cbdedf354ae51ecd4598ae84abd538f GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIUO_QmvAUQh^kMk%6I!u7QQFfn|t+k(G&om9eF+fr*uY zLBeW%brcP``6-!cmAEwsew#iUs6i5BLvVgtNqJ&XDnogBxn5>oc5!lIL8@MUQTpt6 RHc~)E44$rjF6*2UngCjqZZ-e_ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gp.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gp.png new file mode 100644 index 0000000000000000000000000000000000000000..75d7f46e863c9250d49bbf803d3216c9818d131f GIT binary patch literal 3437 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 P00000NkvXXu0mjf?=`VH literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gq.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gq.png new file mode 100644 index 0000000000000000000000000000000000000000..f7bf7ac3c5be9aba10bf7a03595c16cfb6d05c40 GIT binary patch literal 1321 zcmV+^1=jkBP)001}$0{{R3f+qF10008MP)t-sWvf4K zvp<-o=I!$H_xttr`0(`j?)Lfe^!M%e`t+Zm=Jf3I{`K|!^z`!S>F3|x;@Q~kQzrlfO0>V3qG0vRB72tfO9{X3RP$1RB78vfOJZLbw8L3N`Q7se|AcLcT{QI zN`H7imkdgOc}jnJN`HGwetT4D-AjIaKbH+letk=ReougeSZd;0YvrJ-@b&in|NsB} z{rmj<`ts_<@8!4Z+L7znf9v0?>(pxT>(cGto%#0c{{H=+sqp>%{O;eK>e+|s*M#fU zbnWA_@8hiT>dNllp6=$s>d;&2->>Z1f%x_5^!EMd*_zte(%RY7-rnBc-QC*Q)8E*- z_w?qTsPOdl{r~>{@9*x$#>UCX$-u3EQ%^ra6ma?AT5q7ACDhqpk~(B*7*7U#>d9K!@j)2ytJ;ViE?DV!M(S^x5351 zzq`1SesRmo%F4>h{{H^@`ue=Lw1#b5$j8V0{QTP5+U@P_@9*!jsibpGHo(2R_V)JM z+uQE$?$OcF>FMd5iG5=*52~J)+1c60$H(>c_0G=Ds-T*TcWJ1fnds=~+}zyF&CUD! z`{3ZPp#lyqH&(F`&($cQ3uCK4JprD|orKP^UzSq~+!otF-sj0oaz3S@f z`1ttq^YiiX@uQ=oo}Qk>#Ki3E?DX{X{r&w~Y2sOB-jz&_l}L%(B_!h^7+PiCl}CtL zW!;uXhubA1mPdw{M~0V1h1?}0m_~%$B_dj6-Izv$+$17eWZaoXgPBEwnni(IWZc{& zA(};i-6SEKMSxsn+M7gwoJ4*A005iEYcc=;02OpnPE-B-{{H^{{{H^{{{H^{{{H^{ z{{8%km~tlo00IR`L_t(o!>!l5ixW{4$MN$`X5#h1PFP3~QV1eyKv;vtf+F|;u@>x- z!rn@-viDE05-hZKRhB`}bs!|>!M|eq2JT8#6YNJiOD9RmLk1rww5KH6SuW2@!XeaSyE{JXKVuu+@wSj9}0Y5ew)^k;z8a_HU|I!5SlA5Qd_fFC2~e5 z4ARzqh#l`tu*!~cwKf*Aq{fFaRw<0Fdy838dwS~b$l5caJ_hzy%!c_j{pZcHTJ$ueoJ&w=X?B5AY-3&utkl+s&NnVR*L>V9Q)g*Aur-{yPhY zt@gV>zqLtp7%Yemgkem1e7W-Q-{ZK!F>cloW9<@{~@ zQp%W{u`M;RHtl2d*s2p-QzNUMiR`JNRnHC-D4`_MNHQIzH(w*eH>I~|!%1`~;R}%x fC4B9MN|gKshcTA8n)I5`00000NkvXXu0mjf`+NUo literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gr.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gr.png new file mode 100644 index 0000000000000000000000000000000000000000..be17af8219f7267a574b60f7a064d73c7eee816b GIT binary patch literal 486 zcmV@P)001}$0{{R3f+qF10002DP)t-s2W9gL zXY+}+`M}uxcdPksrTCP+`p@D03~BTcZ1fRq^oO+h7;y9xZS)jv^vvM>_L^#0D_ z{mtP0(c=BnE|No7-`o`S+-|PL6yZVf{`uF<(NRIZW$otde{pRrgsmlAL$NO%j_*j_t17q`n zvH1&V^tRIc{Qdt2W%Ih!{QCU=0002y04<6D000?uQchF;{rmd+`TYC+{Qmy_{rvv^ z{{H^_`}z9)QZ~D{0002RNkl=~ti#r3 zE0$e0M+d)Odw)>*R&`6Qbwp0#^XH3Lt~Zws?#LthzC5vbPoGZTHf!sZFVIO^PO(I% z|Bs5TGExao;ax>jtxg9Gr1=T7+G6Q+dvsv(Nkq%%11=vh_~@2mtItQ@V~ni<9~kn{ c_ZVO63%g3P{OCc|oB#j-07*qoM6N<$g8JAFp#T5? literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gs.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gs.png new file mode 100644 index 0000000000000000000000000000000000000000..73ebc9f07c76e70db1899529d8396805fbafb575 GIT binary patch literal 5719 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gu.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gu.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb2617c51643f2d9543f29f7f14082b63892282 GIT binary patch literal 2915 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 N002ovPDHLkV1i34k>daW literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gw.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/gw.png new file mode 100644 index 0000000000000000000000000000000000000000..387962a6b8003bf7d1446cfac82471aece18838e GIT binary patch literal 1080 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hn.png new file mode 100644 index 0000000000000000000000000000000000000000..5778bc45fa41ce3dd54ca87a06819146f10f1b3e GIT binary patch literal 811 zcmV+`1JwM9P)001}$0{{R3f+qF10004!P)t-s2z$;7 ze9oTB?EnA&^ZEVQ>i6UD`RMcd$KmvDvEumq{{H^|jKb)n&h7U4{leb!uhj2mt>Gk! z)0@ic-R=13^ZT~g@Tk%5HB{r>;|5rEJRe$Pyx-1GST`uzU+{QhUI;SGMzki_Zz z{{Q^_{(`*ba0%j{F5-PP&$l*a1n^!r+;-j&DdfV<{!v*Xd{_TTRL=kogc{r=G%Bp{^arcF_G2~f6x?x&^D9Thrj3V_WYH`>JNX#)?)iSY<&nhcIF;B|rQJiD z+Ek?7Podm*x8$bK?cD76uGH_S(C+W|{H4$BGm_T;003BA0?_~f0aQsuK~z}7V_+Bs zqhJ(_f>FT0NIk&7L_NU3Og(_jEEZOR+Su5MG>e0ii-1wwJiNr1#m6roC?qT(g2yCL zF>wh=DQOvkX35IQD=;W3DdRCqMO96mQA1OUuphNGb#(Rg(F6@p9ARi=Y;0mmM0lB* zTUc75d(qkkS*xv`y@Mkc5oQ@WX>mKdAd9%VxVd|HdLf+V?PF@-%STi^q60twfIx*H z!C-RIm#hk!fR?H;gJcMb@mfTh6>NhRuH2zv;SrI@B2m%Am?di)6C39ng5oqj@%V&9 zM0D`E#3ykk$8Zr&t0}2z3h5b{DDKJvVuV|>Epl@6@(T(H`%$Z?n4zRp8($zRhchsi zu@Dhn+T|6MRmylHjHS9pzBY>pv$*Q&SsL)iKU-r{b~7>V;x>Z>5FU^P3_DY9OtT`X p2N+tY2SzK;Q7{Td!6=|E005>6dy&z8yHx-H002ovPDHLkV1m>r(*ghh literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hr.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hr.png new file mode 100644 index 0000000000000000000000000000000000000000..8026133719d9b3b630e3fc1744cdfd1f2d5e954d GIT binary patch literal 2233 zcmV;q2uAmbP)001}$1^@s6wfF^v000PpNklz}1}vj=aKuUz3sj`@y=aV*r-5A4&{N!I2Yy z;Z9b>dAGH2J(3Tme>m={X$tNWKov6#kY=?3=jL00^O1lJIxOx~6_naK;?~UBq%!0L z_tdlywLA>tCkI1AeJdgjy>Wfc5@c=N1)c0tjL0m(_^cB6?q#9e$Q-#w4zOAfhH-L% zn6DEo04yy_L9Eiy#s#wfZNnu)9G3J%oO5u;jHBnVh;;(dff2Z3YKb51H^WB71M4Kd zM5M%GBu+F$mWd;DlCv>2H6EX)=RqNr1+}zz=!K*~W=b$de7*@{<2PaC=bK@o7mTxJ zE0Ml21kMRLP{}SqP&6AHCDow|>z?Kee0?Yz;zv(I??gN%<@^NQlrzXQS_!VCGI)|o zxFM;AQb{c&X_&w+${leT)1Vu_6M=CH@on-vNP9*@Th$K6a_xucf%+tKPUxd zVjOu1OY`<4>asHy9?!tZRcojhG$di2TH_08l8|jdIO=K?;90r_qteg9ak)QSB&;xZ z!fL2Y@PPaTZ^$b7V{wQFwtcS)W7cN4>@$Z~q9v5vci?*uMfjSkVV1&DOjB46aTR}z zjX8-iS%nC{=!W`QMVJ)Cz@C{!=A72hgjMY10R=LHX8x1_X1NU*`3Es7s~8gk(_kp= z0w2UhLqE8{aWeR8oe3(wjK3(v7ktLw3ODIeNgn;2L&w8gTqk8*+)1kCwo7z9;M? zY%zzlqAF=cIVm6fBPbnXGR|OXA`?<+nHZUJ7E*>`SgKZv0<7zn5vrKxqE}Bsy?)>!#$FS2Qw3ki*Ld9@?9vLD8p!C4P-o$p{BJB41;a3 zCqR5mLa=?ycZk>>hsa$&U{_QucJ4_)h(iR{n+9QpQ5fdwheDGP0gL11*mtD@#i!5V zT6iR`nSX`*dUFPKxQoV=k_2-8=?+-}Xlhy!R>U2{-t7l)%E1feb4^f5%qEv#8B>(N zBj%JVD<^R3gS4|E2uf9Gs2LuX3*40B-ef_(b-d;4W_x<2SJP8hkDDn{FKjdJm zx9tg<-PQ~mtoFRq)Y@R@AqEj#y-%K^$@S~Og(ZSZM6d*vMSbahRn2rAblj;x@9WnB zy?RBJ`uj1pdivwJV`v}O=kXND0h(uS`xvhXtZc)y+{B;w(6TQq!W zNvWQQUo|gta^J!pkSeWUS?*Y^Wx><1*_}EO+oMfdUcW(h)m;>pBgKhV`ezYHdS%K_NOh9CUKIlwKDV;lYB1 zsL`I$ucpm@LNTxM@^F97JfWD@?cx3Fe05yCMw!)N1*_R}EowBh`gMQa0{l{3g09+H zO8iP|+PFz5=D|V+ez|b*T}*W~8rS*=$B=#A5SKr!VDXPGw9gtSEGHV)fUcgFRy5ME z23lFIX!Q#~oyk%nQ%VgMmUy0=f>-(Z0_Eis5F1c$whZ;=0yS9M;6-ZcyO>jXWM-`s z>|-p@LtuJU>`7G6SMd;2?|#p9tyQ~Uq-ShKZ#m;~)=gcyO9j zS67z+7WWUr`pvwYaA$|RR z)an^fRn=L=A-FkX!RwQ7A2%lf1 zu=r9kPeks9y{NaaB6F)+$U`;xZ}|XLcSD0vOiN%e?m2nF)hV8;Ri?7M1EX4Z7 z>LvC$)dv|f9{V0i!X5_HXL0K<5E&SURPA1 z_vth0ApLs>-^t-pkeV-C1uG&O(=Aw{8|Z%(SbXv4jf@Ek_NxygIWik%+YX}A(F+_q zXH?m{;Es0yN|`y>9h{B@Hf)St#Ky;b-j53FeSN}ULz;X5G%VQAw`OCe6&osFurYq| zN35j3Dy$(P8UhWAhDF1oVbQQ?(GX}@G%Okx4U2~Lx7U9FF`-)4rF{FH00000NkvXX Hu0mjfHPtG0 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ht.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ht.png new file mode 100644 index 0000000000000000000000000000000000000000..7a0601c50fbad56acb14e79b7a169852fa1392da GIT binary patch literal 369 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIQ0&v!+txsesU*lR`0rnl-aDB<9%GWXy9-Ny#?3$=hqJ&VvKZ*1>mbbNq%pe! zD5&M>;uzv_JUKv#O~ar^OFGnSRn8JkWg!kBW)_K_PDZxHr`)FYYDs6FV(2vu+p)pJ zD-@_pwZt`|BqgyV)hf9t6-Y4{85o-A8d&HWScVuFS(zAG85-&um{=JY{9DWm@-#w2 zZhlH;S|x4`{r|S^2WpT6*$|wcR#Ki=l*&+EUaps!mtCBkSdglhUz9%kosASw5re0z KpUXO@geCwZ=3|5a literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hu.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/hu.png new file mode 100644 index 0000000000000000000000000000000000000000..d87ba073a8e985bacfce2cffdb897eda704a1e54 GIT binary patch literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIIU$mzQ(A?XnItZC zFmm!dJ<`w=n7a5B+d?CT%To=1RXGU%1sbJV;u=wsl30>zm0Xkxq!^403{7+mEOZSl zLkx_pObo0{4Rj4mtPBi{W0HXmLDG<$pOTqYiCcpcj05MN0BVo~*$|wcR#Ki=l*&+EUaps!mtCBkSdglhUz9%k SosASw5re0zpUXO@geCw$R&4tK literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ie.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ie.png new file mode 100644 index 0000000000000000000000000000000000000000..49aef004dfca10e9b27593c41077e87d9665a247 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIt?bVVFHCvSva&yI|z|<>YYz%Tf%Ok2f=dA{6 zQY~?fC`m~yNwrEYN(E93Mh1o^x&{`y29_ZPMph<nC}Q!>*k zach|J@_Ro}gCxj?;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1STJYD@<);T3K F0RW`&YVZI6 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/il.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/il.png new file mode 100644 index 0000000000000000000000000000000000000000..5b033849cd8d0dd4a852a9e937c5a7918fb1cd1c GIT binary patch literal 1111 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 Y0V3x5Di3;rjQ{`u07*qoM6N<$f>-5?c>n+a literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/in.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/in.png new file mode 100644 index 0000000000000000000000000000000000000000..03300d8bef63a19297553f73766a3dc8d3642850 GIT binary patch literal 2250 zcmV;*2sQVKP)001}$1^@s6wfF^v000P)Nkl2`VUvH`R>d+_ug-Lzx(|-&w0-K zVN>sE17rMu!^VJMz%pPNunbrRECZGS!GLAJGGH073|I!NyZr-9GRob5jLZ-15&iBs z1TC5fzlFcocYfz~?l*m(VO{n30n(N{iVbsK!t|Gy;;E@$z|PSZBOP~Rl(Rn`a`e;h z+|Kq%7?+q*}B=;jbc+PwSW2bsyn1dgF{z@2L_c(=#^P=GT!7=O%FT|pC$@s;q zvfc9(e*M?|81Aq~kLP(j-*msjqADU6Ka1z5uf*sn0dQD!L;-Esa}H9x4o*@sJ&tvH=|6&~Bu@e>Ds zorm*rUW>tM4V{gl9?c=M5a9kM9&`4__!*J-V9OZ<94SGSsY6vnK!b`JkS_<~%LPy` zuGF+4`J4%SU9M_FZt-;mId5eGURZb-k5BQ@c{#r&?!yNCW(@VnDllQZTR6tN7>pg_ zM+w<_ zTr6+VeNVm?uJdw!OWcPIhWORm-UGK!;xKwz5T2R2A4$1&$S!Kc(bOshR$14Moe_m- zY464g-)vRWCq5bSegmKF%v6vO0xCJzgnj{)C-5=~uA#W170-*h9(E1Z`CH}up?(c1 zEU7Mhck&8;Dxl`9h(}s}y?POEIw89beuplh|I0jIs?`Wi^l!(KTg2bDG=L2FChIw zgL+PVQ>Wf==?{7}U;(sM$--%M}IeZD*zbjDF zC*XLQ=j7QM^|wb}3eoHEdR7aIs^E>#Tej`Hpng*&kznu95(LLwR@2@Tn1{Jy*i^`S zn^F~!r*{V2x29vMC~C&1aaxW`yw2cqbsG*QRH&!rUv5I5xnI3&>>Cky^pBx>J*$Pa zRn+v*l)b7db~`Gf&paTd*oVVOmH6A5M6CHXMsci%{?+L{cH&px9Jr_|qEFFVf<#p@snzOB?`}*H zXi3@$kBc9rNK{~4U?X73@>r^lr5PenxPAZWa)gV=#bwrN{-Td@6)<0}$Lm@hEVg@> z_hZzr{=P94-MwZtV7C8rm#)KG{Accp1ck8CKM&EOCc;d~Gif=Uk!C<>gvtgmZ&iYp zJ}!^wV|g;~tM0LSJzm#pVR7w8 z5G(!GO5!Omn_|Pj&VKEjE3}La$QB`-sqQNf{qu(7>tSU?|XppScf(r4h zyprp>e6m_tgJn3vF%a);NI_3;zm^Q!J0k>3hh?Q@D4&mr;S&r25g1iQV8_#Hv}9@W zJa)B|A-(KcbEkIK!7?1_Y{g|bXz&lq5{JrQ$;3!%=Vz+wcv>~05-T)Uy(NR?#YHm# zQHcac0}nY?s_L5~{uU~Ei`&AaNdzCetmVkkrolfY@x*EkelR-Fc(?4$M`2krnl(D~ zYp`TTW4>Z)e|vp0KHhrvc61OwW2|7Tvv$j8cz7F^=6QSfbu!eQ?k z41ei*j1B}vbx=_?4IMCDzlB`~FRE(T5U3=oE2p?oQ-A1xRY+Abvqi?0<5x2xbzaWT z>kRe1*es8oi=iH$otOis^|LTynm3*kb#Xn&vMH;*fwKaRS*fVJMSQ41VK5flxY?uo z`P`i^DvhN}Wpe2=`R!=fy7=h4oZk}nVKZ^(1F*#St%4>4}}1dM)V6-G@B z(0^@BlI0L%1yeiMkIYRBrF@)B1E-Q$j`X`}lF;p54Ak>E59hPw@3$zd!SUP{dp!P* z8*Hb0;XwyKjD0-UFsSOzQumN5)i1}p=X0n318z%s^v Y0KQ!SsCtnCQUCw|07*qoM6N<$f)`~_jsO4v literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/io.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/io.png new file mode 100644 index 0000000000000000000000000000000000000000..dc0ac4f873eb44a2efe8e15660a2ae80da3f1d2e GIT binary patch literal 6741 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/iq.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/iq.png new file mode 100644 index 0000000000000000000000000000000000000000..fbac7587fca1d35d7d7fd978807a7aa95411dd3f GIT binary patch literal 1703 zcmV;Y23YxtP)001}$1^@s6wfF^v000JVNkl3X zxC%Q?2Eiac4+X3{rNy{ia_500xK(@`YY*?hJK8Q-7PNtZv$cv#OZc9OEQ9D+sW&s3 z!RqfQl)u}`U#8cIdkgOmtk8=m@awrh{xwi3*=ZQP)*COlI-u)3SMl6%Upd8C*8P?l_8s;*X!*AOD8TI z?>`5wKThUk6D@ijy$koP zLG3Z^5OY1AzdIlb-f&!JoQ{u1Olks}d36_s2)6n>`nNXI`XD1O%Q{$;kUB`Qi0e5S zaG2DKlf-8v^QI|C9txf9S=+)7pysfa@IMmB^ez(8^4u^R`_6=#T&K>RRgi;rGoA3J zIPk@kD>id0@(zj$GlDg8&2+pjiW8TX$lrPI@Wo5R-^YdISOlK>4b`2SVy`Mc?Mul` z&l~>J6uyR3rR1hiq^lt&QNlqhwCK6O4IO6XS`o4l?R^GthoW^OKL;%*e}RCAU=t=CzH}1KtGqaOr`gV^_H`3fb!vdtqQaE> z%SEHG>F6GDs8W^OavAt%hcv^w!vV%;jS-S3W+_d?d;3znG5RBBHJLtLe=z#E@i~`L z;>0~O(QBa_x(OA$IP5)C>0b{`CUoW8J?0N*RVdcM%px=2cw{%TsfuGgX!_UXpd0IW zbmR&a-NSTsqDpGnE)VvH+nQ;{st{NXlT5)<44vrT^xJNDOqH904oc5an=Vz_YtwuN z{#Ca&*buhsAF#y7eZ3FrxOYHuR;m%Kx5b&r^oi*;TpVyLCemcjN5#*d%o>cx2Q)Cr zP1jD^yb$TaPAoi2FDi39_6&QBaswH2uwdQ2I#_l=E~al=0=L!EjOMy{|60rq{K0sQ zHLw&`oV|7lbwy#6+|`}mWj4@()GIRn0vc;SXOIYBlDPtw$NE{Q@A)ZP2`$=FtEs(_ zTa=H@$M&)$BNHxK4h- zSUnXrU0d?K?YtdvGA;^Z*ZBzf?6wOmT2YCA;*}AtQB8OTJ&SBWk(hdIIldoH3#m5u z>cN2ehXtB2tI_z+jBJ?r%K~(n>%z(*Dc2hL0ehkO$`FHc^NBt2Il7tM&XUL1DZe7gE z*eD8Dd+f)EOHZ`OO+H4fCN`bA8jXR=#_`$B-!?Sr?&2HtGb7J~9L*gfDGid@f<5KE zgAEnN{M^vyEIDEZ9g?I&hwiu3!)WdbSYIt3Ee;!N@hmcLns3esi71jMpP}6Jr-@|6 zhbmrp=N8XyEd(R{4sGXa35@3jyQwKS`hLUBN&$zM)p?FH@59^zwubL#awua6?b|PO zo5gFBMm=AZH`~xb%FKKtRd~YS6%$wvRAmBgl-~?Y7KTXApphdp-?ST;A%dF>%jr0!?_8f3V;H}la%$74ll z#orC72}C7cMsL9oYO^$*XNxv&xXT}8< zM1Pu&LobB0iL2ki*cTcN{kOCXdqaXC!IEG} xuq0R#EEy6636=y)f+fL{V9AgmNU$m){sxA9hKRWKkX8Ty002ovPDHLkV1gMsEyVx; literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ir.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ir.png new file mode 100644 index 0000000000000000000000000000000000000000..c172ad9d809e1fb5bb419c36283c21984e271fe9 GIT binary patch literal 2316 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 KMNUMnLSTY0-}&_b literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/jm.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/jm.png new file mode 100644 index 0000000000000000000000000000000000000000..e8070276608c4692f7163f4cd70184efee5c42c1 GIT binary patch literal 1307 zcmZ`%dos?sw1q&Ueo}=R5a=pRcFBuDLD%eQz(O zKfyi()@rK}GsAYOmLT;A##RPUl{HTltU+kYP%rIS1h||B z7IDS0?XCbR%Wq2nYpx5bdnl7*X(1qlq6uzSpq7t;Rzy}}o(PkjIA4p!1^9LkElt=^ zjDj>wDDZbHVk)60!aN-W`{G3vzI9=$6QWzNEC5MJB!f(bGZpeYeCfd5CNPSjo)0q< zoZ;ag1qR!2;5zih(9y#FE$EZt!Y%Cg!J3t*%f;6&v?;K$7^cQJ#l?6NhT6cr4g)c? zwZPtt=arc5L}4AQ3$c0yYO|qiM~4Ee5-eJP6LA=8#BdvUQWz2zP3-l;vkF|Q!?Hq< zNpQ5ojkCDd49^l!79cYgZ`+V0g_b4)H{oeHK6j!>2HT5putjwix|^}N6dL(hI3Gu& zF?t*C+QF}akr-MU*vZ7>YbcRn`9+Y0Adz5i1Lmc4w8qR=n(PQ z5N{?UV0TORc}}1O!{oSgkx}0oX}t8+vDc#piVXR_2bzZV8|qh-MD-s%G?v+)l`!lc zA$jpkCP}9CM+jFUyQ8k3lbx+2&d#T(ucWOx#>>gcojlb=eUt9K-c$c<(Qw#@3n%sn&EDzi z)uqf1?(vJ%)8=S6EBqt{cJ_X3-E*;k|H|UR2O;c09wAZ9H)s!48EKn&^1$r}`WX;UuQ6H@@+Zx*S zzSZtFTWnr*I6OG^aE9CRl0LV`W!}>w(yi)2YNYkmnc~rj@Tl zZ#z(8$ClWaX7Me)spW|;zp7c(XUr4q9Jg4Yx;b?J2SttF_^CYkdi60Ug?v|~rcwD; z^%&c_y}r^`lky`&(LBX|Ms?=7k&0yk_Xw51LFqi08m8#~Ww4;xq5LbOKYIMH2BLyC ziLBj;VZn(Umrx#uAgrQ0I??EBY4mjg^i?iSjxLVQG`f=uoqi!eXMl1qrHbc z07pqwM4WX-TM@*uwf174B_qIEjolj`IRqpDJnI%9zzlQ%Qosr51Y&^KKo<}T7yuc- z1*}@D-y+Zr>;tX?@xTor0pJ5mfC-QTi9ipa00clU&<7-2Ljeom4DB0qCw~p4XrHTr2a3C))a(1TRU}|orxjEXnku(}=Y^3pVs;{TgQsVI_ zD2SSxh|MOIir&1T$VjqSh|i~t3^E$YY^K>+YH1;B<#yb0b?@^7EtI zT*}WUfqoXJ(RMksm|13pMO213YDQB$OVolSzOMINd2z*wU*0O$Pq%0$w>{F9U{Yjr`qpSIN4V{S=<*FWD@B{k zUmQ|OWKqF^?Rw!8?q>~mN^>ejSLgG}YP`351_ZS0-O7tBen8@qPz~hp=35&vY_rs&NJ#Q zw_4*0zC0yvy&d}Eh~a+T^Tg+Ewv#~%*Sb6c%%{B)?u*i9%wv7ytm%S(dXK%|eNs|z zP&M$%WPED3J@rz@k$2Zp(@wFg8XhZi^K;VPuAhGNz%b~(RF)cDST{12qxyI%^j((B zdFkvfS=;%(OGJi+t(iAP4kktVQRCyNyU9~K)ge1%cgGWj_I2N=;%27y{&DY&QBZ9j z*YA|rn8);A$pt^9)*aaL-pZd(Zb!?x=834^+qB$9pRkku`ogxwdwYr;>fC+SynLWE z?D4GsOTv|Vgr{!(@wo2Xfm+V-!k|vg)QOm<{>qLQESC+rM;nLJOa6akNu)I5hfh8) F{tr*ezHI;i literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/jp.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/jp.png new file mode 100644 index 0000000000000000000000000000000000000000..b432601becdb2a47119357e24590c49991074266 GIT binary patch literal 495 zcmV001}$0{{R3f+qF100024P)t-s|NsB~ z`uh9k=Jw0W^0Be+p`q=Wng9L$`r_j6qod+lTG}fs))Erb2M5yw1JeQm|Nj2!hlkrN zEYt=D(*OYe`1tXxtlTv<)C2_l@9*e+ebx~X>3x0t?Cj-dXXR*U{qpkWZEgPh`|FO5 z)d>mp!^7Ab8vXI{;ZIM~0Ri!_u+gwAqE&Al-*(D_a005>dDGUGr0Q^ZrK~z}7?bc}yf-n$dR5H`GP*Rn9roF z<%&MHTFOvC7-d*d7)V(0nzCBQ;?yRg?2?VJm2NrKHWjvZ9>?-_qSXFCybgOo>v-Z? zr=y^CzHqI}SmjeK*UZz1xHhRzP;OoPr^=neqg lO`tmybeF^Km@3tOb_axPT7g1jt9bwb002ovPDHLkV1m}Y1QP%N literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ke.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ke.png new file mode 100644 index 0000000000000000000000000000000000000000..c2f2a4a7a4cc108f2aa51a61449628966c44cbc1 GIT binary patch literal 2158 zcmV-!2$A=RP)001}$1^@s6wfF^v000OzNkl_!4Zt2)+icL5SmyOjkcA9vWf8Ty$^Zu%@Eq=#K-B&ymS7XfxB?$H{bo= z@4F)si9|5yzm@@l0hR%l0hR%l0oG5DSS)7rwC0yerHljtYrud3aCCHhYM{KmyP-8m2h%$`q?8kBqRhXl?oDxL^z%`d-iPP=jX%9%Bt^3Apl&=<*=2R{>M4p z-QA(pY7rF`B`kvgtf0nfaqz##AE3bi1J_8Xtrm?mqC4TSELJ^Eo>^L!;54 zy1E(;4i3VCMSZKJqy){)%~-Q$4NOhBks~)U!cs?9l*Z4+$%q$ldR8L#_=UmG(i##G z_i(VawZ(x02hiEsiNeA{$Ye5M!J__>nwpBPt}Zk-HZmU8<1+)KMp!<~4P{}m___!N5vNN)l$C6&V?crluy)r>d$7Lxv22 zi~xJdVK|QYhU47J$@s3O4rd9hZ}L9E&CfL`501izo_>sv(K+a;T3cJo%yIADJ^1+e z2osi#jSWtmIDz)|b{sl%2!j*~JZEEvBR-)h_lrPn-iK(teTRkBbn7-w#m~bD(zemf z3kqWy%*@QNbLURnxN!q{d3i85Hy0)>x)r^=ypWifi0tfac-jueuCe|MsPf<`XwaTS zYr_w)I$B$Ce%VTB(XgrfnISr8|q zevQVudJZgd8sO@-_t-uZY|+f$VkvmiLA(YJgB+Og+-pL z+u!H(E1ft6HJVcN*CRp~`N^BQPcAZ$`__+bpc+yWisU%op_{YYEBp!>w(Z+>Fn) zQ}GHbhtzzuw4qt~QQt65TjiXW@Xw2vSy&CnD^MO1&FMqO_>!@9CZ^cq6(DR_tA>w; zgdD+xP0X;Llro%ll51Vto{KZn=HU9)99&qnR&P8le<&!L`Dc(sRj{z=7D^cG$ku~u z5@ou5Do6@Z5gNl3(iNexdhOdrd&68RWokuQcncO5&BDX1e#O8t5=)TgI=0t-6+ux% zk-fI9bMavAyyzS=?1t+3o?u{=5WjhL;Gpi(hiJtxH@!ZUF*DIn{4s9rEyQV(d+D|( zIgMm*ASihH0SFS-XQW{Eu&`w7tR=l{qq@v(=z7$Ku7|CtS^th+`=X&E*ztY}Yu-;p z-e4W}4rS7Yo;SO|-dQKtnv6RiA0pXb2cw(2_Ug@{bQsQ#A4j58_e2#8EP53)=84Ek z)P?0g88`B2P56*e{nj0Q!Wt`A;By~=!a7Rcn3r79=Qce5o8RI4>MQu};zgX(Nl2+w zjxOwYnyYDqx(!Pwgd=#+GpxZU_hRrgx+@kQ*cUbpfj-wCMsXgBy= zI~DKO%18eAClo*a`im8*1#gvh1K?;5$6`Ys(<-6J_vhr-eYc3`M*}?`MLk~ zH#qh}0=IY$e;$9oc$ow1QCk}>y`^9BXria%PEBq9+`-pBs0W{_xnF~WZ?1&aZxZ+0 zo$K`+Se*|aqI$DhFa0QfbqTKBtp?xgrV=^kID&6^yg3BBg94v`6_3W+Iz3odcjR(l z9S8`*vn%b56y1vvgVx+=n_l5Kd23#+{yS2k^BL1~Gy=S_pv z1_kuITVX=2Qo?gWirIL<1UGGBl7!20&o8B|Pq9>++VGHy6z zZ%ZIam*AD6SD@OjVqOaxmcQB`>SJoiGvw^w%zrOMQtU{Sg+=4$krD>hrR6EOv_2iB z#M6>v++eZJi0yBmX^#SJ0i3g(g$v6p!wg$Wwjd%mLa%M$8e;~^$;8DtJ6nk_VrD`U z5sPJ0T_GoJPxhsLwP4Qzq#R6P{7;au+&8-;^^;TvT5s(%dX(`aa5yjw#R0*XF>5Hy z-s&BO%_bY001}$1^@s6wfF^v000RNNklaxmJ?VSAZ8#C2(MTuIjEY{eE(p42+ zp^U>;*hHuC;hK;-Jd9uJ%``$7-LtpOmkanpkx{ra-gbPt=JQ=cwnY zd7ApF16qP#T~EkY^Sz3X9$1VI+5{U0UX`thPo$~&p2b?aBTgNUD$r_etomM%tM#@R z4ZSW)XM3YH`tA&^wZz!xSB}T3`zd+$*mZ5bdY_r6*;^Bhp$$c)_IRYg8h&%8=}$v( zsTOaKvvFeFroNo4rN6`(VB511IzJw%y-km{PDf~KIzsEMF`EB%ycT{JZ=Xfu02R<+ zA)|UEz^aO@Fny`ZDN}7)xu(C7tnm-0Y49~!+USnf>6=otvmCCS6|XMLhik1NR%d>e zpqXzaY3|2~T6LJTJrkjwRj++++Iju_SfozAB~vHgmZ{}qK6O1lUp4W5wLiQt=rJR> zV1-wz<-Wx__1RPn-H@f$2L|-$^vx++b%5DB=dqJ#EP1a!x6GJ_*dzY+d1dzf(f6cl zb0A84TOO@7#puj06YR0)X?dn;(_c%rajo)(D6_Ep`hqgmWtZ8B<8(9MNz&k}vuxCM zH@sT>W1N8m`(O*>xH%MMBY18k(gc6(gXt#ZXdG-HH3#J*@t_Wdu*(a`I&H2%>vjlM7401c@z#0k-?p*Liim@{fP8`%m5 zAm+H_@g%>c`olu)Xb)A#w8wK`6cvYA4My^vxgRB(?rjc6Y58wHGZ98Tm@y(O7$b<# z5-xE$gqx^38VU=hU|-6DD{-{G@-HtC4`z$>!@n~YFl7URXnfK>}_H+X%v=5I@|lVc4tabwz@ zC+~nZ`-4PV(NKMGNCX-kgCQJjA;@G0-@z1E-vAV8o)nM3w>_xHzJFKHFKe(6;m)fA z?3ws@nsqqKPmZ=6g5_BJ!HF}99gbkr1rhF!M;DlAbK*Q_Y@6>La82!B1nU=fg09*qG=Mhiym?|L-yt_%yRY#|sSmgX>w++g70bL+QRsMx zF>Z$rZB0hla|E6d#1zmY7_|7OI9t;o{8LVy0i2~j$CkXhARysRU2wqc9`vGv7`!rU4Wi1{j0Q!vX>& zU@(e3PtUVm7GUu?oOATO={B0DK9j26XXV;zr|ltymf6-jVl?E`Al_T}eY{h+GmL@N zQ6ZGzS(U&jM3~J1Bg2?%_D5OWVe$ZVJfnzpkN(7RKg4mcm(@UZ51LUcs~qlf@#6kuWV=o4!dN;4p`eu0UM z82kA7+u}|98ApOGuUjE^Sc+i)A}bzTl zQg4Xw;E4oQa2?PHsVg-;V*pM_xMY!sOEOlJY}iAvv^d}x0u6Cj7j7z zI^^6>6V>AU4q+6>3L&R}ljPAl zgq&3jI>wbAGT4Du!P=v9k<}9b4ACc8aAE(K(Skr9KmKEMHxS2k>?WsKi%6TU9eoR tT(DfQT(I231001}$0{{R3f+qF10007ZP)t-s8vt`O z0A^VL6va{v_2=ae`{qFAZadYPk5#|I5@MC59zP|8c zW9ShV@^^XX3J>TK7V&X%`MMAbwprPp;A?FPe>_0*Fp`!cL)b^pF z>^?!}0}1X@RP~&k>_SB50te|KCG&-b_`SXR+uQlQz3Ct%?om_maB}#$x$j*!}A2_`165FEZ*T zD)f+%`_j_&o15w_F!!vi_NArnO;70@AnsOK_`AIM#Kii}&icv8{q61g%F6h=yY5z4 z=ocI7H#+vDr1!6{>_0;25f<`ydile{__Vb8(b4#_viZiw<^>D!Y;XMG;r!p<`N6^b z<>me9>G5lB<^&4#i;efLuJ^F8_o%7xYHa2O3hFH~^_ZFd@$vY#xBAe~{`dFzwzup$ zJ?l3-^M!}~=;-^^)%n1{{qOJn=jZshxc>0){p;)NEi&j57x=Zc__(`t|NHyyQB>_kNd4^W{`U6mNK5+6%MJk&+}-~2^6y$*_p!3^a&`OF*89`c_p7V-t*-gM!1~F`_`JR77aZsn8t4`q=L`}6 z001pQZQ=j`10zX9K~z}7?Uwml(qJ6G%}UJFqRW{FwIWl)%t9=wOsQseF6> z%L3GdwMHHwh^EeI;Z;_2A6nUAhk0~Ze^MKgtY2{S_>&Kx@jUPM!?w@!KJQNp2=L>_ z&x~Z&^bruqf!TBBa(OHuXli)D^MW}%7R03$GCwqo_x~+kI7=2RY;n6Y!>k6pW= z>^8UidhUF2Ns;)%MUJh~OP94eeM(t*#TA-LgAr3)RbWQElF^E5gibFurC%@Q)T*k6 zpc=f^oN1|}vD)fu?OJ|ZL0YCU6Af8ikjtR%jvloAcHJG0XWy&I18}TM z+o{3rxS`=LB_&9sU1c!fcvf5YJq*S2?Dx603}zkPglb8rHPwN@l4^uVO`#yfVYT+; zcAH92oe1UJ!c{eRA+D9eP6T$6DjiH9^&r&)I~*j^eYX~^u{3jSJ)m-n@rP0orpEa6 z&-o~cNXn6`F&hBbWD@W(ny(;xIkifkg!RbcTC(L*00b!z;_Jm?LafvCA&{adKBazY zd{&J-e<7T@74ovr3BiS({d!0bG0>}mXlGe}XCVwZWv@b}YVlsb85kI-vzct@z$73+ zc?6~J)zM4eTb}n;!Mpb=8VhCuR37vM$uhu%S(x?xhXQY{!H=K17(QD~NS6nTR>ZPg zll8M}Fh>u1X?+=LlrWe96Cm+G5+uv$eVJWD<4~HH*4N<)Mn)B5!ydkU|G}~`#psB8 z4D-?w{B#pVUmnJPjkAn(@Atp0!0BUJzfJu3@iQa&17hZpMUoK=2><{907*qoM6N<$ Eg7bPrM*si- literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ki.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ki.png new file mode 100644 index 0000000000000000000000000000000000000000..b7a89ba5930b101f16443e91c4794fe71066f386 GIT binary patch literal 4026 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/km.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/km.png new file mode 100644 index 0000000000000000000000000000000000000000..0b64f430808ca4d5bd344eec3dd20afa672dade4 GIT binary patch literal 1664 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 Q1^@s607*qoM6N<$g11j>x&QzG literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kz.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/kz.png new file mode 100644 index 0000000000000000000000000000000000000000..0d1377e48f2273136be41f94d6c38b0663600a73 GIT binary patch literal 3588 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lc.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lc.png new file mode 100644 index 0000000000000000000000000000000000000000..971bc37738a0f4ddd50de8b1cd4af2ef07d0c7a1 GIT binary patch literal 1792 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lk.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lk.png new file mode 100644 index 0000000000000000000000000000000000000000..08f7d6a7e3cc29ac0802369ec7ebbc046439245d GIT binary patch literal 1848 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 PP!WTttDnm{r-UW|3^8W% literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lu.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lu.png new file mode 100644 index 0000000000000000000000000000000000000000..2b28a35d37c137a90d8d360fa9c4b791f7503aef GIT binary patch literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIYw#2j@wIC%09+AaBhg}C@ zMkkHg6+l5VPZ!4!kK@S(YHSh)JzCPCW*k!mIaxXxnRxV$Fti7zE!C8<`)MX5lF!N|bSMAyJV*T6Ewz{twP z(8|bC*TBTez`*&iOFN2&-29Zxv`X9>jOP8Z25OK5*$|wcR#Ki=l*&+EUaps!mtCBk aSdglhUz9%kosASw5re0zpUXO@geCxtFmLMs literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lv.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/lv.png new file mode 100644 index 0000000000000000000000000000000000000000..a6ef25feb7883b1fd1f5547b4c5cb1696329014d GIT binary patch literal 360 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIpGEJF;8tW1oo3=DJ)On_v)=Uy!o4Y~O#nQ4`{HSmeB zP6KL?1lbUrpH@mmtT}V`<;yxP!WTttDnm{r-UW|3k6_W literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ly.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ly.png new file mode 100644 index 0000000000000000000000000000000000000000..2fedcc1dd486c7c2aa8efa4385ec9dd1b3c844bd GIT binary patch literal 469 zcmV;`0V@89P)001}$0{{R3f+qF10002bP)t-s=KvGR z01+Jk0ssI21_lNd6%`#F9S;u=R#sN1sHo1)&d0~ctE;PogoH&!MGXxNB_$=Psj2kz z^wrhXbaZqvF)eLc=7S^WMpJDH8mF(7h79f;o;#C z5fP1zjsO4u@9*#I?Ck35>Q7Hke0+S%%gX};1A&2o#Kgphhld0d2_K3*Bc4VgyUVBm z006;BL_t(o!|l{r4uUWkM$u{kXGH~35v}4ZiWAm(-v63RG_nBrOo-`yYF|#$ru_*K zi9~-{3>S&xA{3Xf@RlSivIMab$y7R%&E*T>EwxxGYkI|~*6P#@XDJP<*?Jqyq-`UN z)v>K^&);eN0cFEcKx;g4rp({X-1$PYmjSJnvsS-r=56?Pw?D9e){&pQ&+4pSt}f@+ zO`;L7kY_~RJ@QQ;-zxMELH{;%=7@R!GwY6vJaCaGE@25vSkbXwV+K|3xORUE00000 LNkvXXu0mjfbS1^= literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ma.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ma.png new file mode 100644 index 0000000000000000000000000000000000000000..f89c2e001142170a8e61a645afb08520863aa1d1 GIT binary patch literal 1588 zcmV-42Fv-0P)001}$0{{R3f+qF10007rP)t-s>i`q# z02A8=6~zu2t`;0>E+^*#6zKsIa4aS)L@>_^7uW|DH$W~CPBfnzAFmc17ECh>P&HpN zDCPnbuN53|EG7(4H8?;n4o@_o8y+1>GD|rtMLR5|86FHzHGn1~*9R6FOEb9=8*MEn zaw{c1J}uk@6{;5<6iqbH3Kw=NCDjNPN;)eqLol=y8{h;L*#;Kb2Nu=`7S;$B)Cd>T z2^Z1{7tsk9-USt~6&%S77|0A5wG$h*6C1)08OjV8%M2LH3mD7`7~}&KPB<$wLN75w zFEK+eFhegcL@+HxFe^ncDn&3UMKCBvF(*bbsTdtLK`v7^Dk4ZRBSNvF@Gl` zxDp#|Ehf7W8kHX);sX@a2o`TFCKyXI6HPQCNHP~pGpHCHha@5rPBaxwG#E@X8A~%8 zN;4cvG7V2P5Kc5rI4iyo8k`&-AW1U45E`Tz9;X-`tQQ@z6&$}18dfzb4^A|DC?x6u z6HqrQcPS+~KrUi2DCYqbXfG$T6dOl7EZPPZd?+LjPc%q6Ea(9fMmsEhCnO|5gJi9D!>mJJw7d=8XlP)AcZ6%bt)y{1QpN;7qk-_ zXD=sGH!3beFs2zD#SIyhA0RqEE?P7x=m8XEFefiVFTxHPrWhTJA|WzDFI6@w9Z52R zB_nPvCO?e}M01YdONKRt^0V+{M5{Ux`X%(V~BocLcI#7(AqzAJI z8bSpdz=Vjgi=h(A7#6OG;Q44|)KIy0*l^>Bk40T>Kcb**tda zxNcZD$P#+(&$#i46DB5wPnyh3NuHXL%2ivNk*#Y<&Y;sQSpH$|}%;)}EV9U(NnB^T@2t!eEiBefoV3@5coMYF`E!8>5@6LJi z=|7Jwp==Tf;DTazv&m#65|g6KD=MuErP4(a@%rrY#nL5JK)}+dzncHDG0UAm=?X1H zMXdx@Rd=rrS+UxyQbJX)S?h$u-}=|xShJ2yX#EDgD=z=5-xJb-+DLg_Jz&^q;c>|M z6s3`&Y})L;r6hT4&)T*IY~Qid2{v_CB)`Meyt{yG!0tVJ_cit`HPxeGf3vdcKsL?S z@GS>zz`(@jVP6(2V!B)&S`I@_L-tTI|3Si>Xmq7Gv5-)2rJfT-;mb z94D4NF+gapPj=)a6RTwww@75CREg$xU>_}ztB>Mzyrv53uyJS3I-v4B&cZ)R7?r0t zbmz38DSk~^=SlvUhzoYuqE415Hu!DqM#jYulXrx-NGh}AA z8^#uD*-aK3ZV92PUIlo#I@=0KoN8~IyY4KeNio(p339|;81BUe5s#II4!>Iuk-@cQ2{<^*2YjSzE>WApxwYSO3 m!t;9L>gCSy@&Cu>zlKjv3CXd6+QV7^0000VodUOcVX$zxEToKa29w(76aXI9fTR3G-g)-1vNcg978;gCnrd- zE=~~XVajosAfa$zfeTMSf`J;FM30tqsM)HVC7R3(?}aWeJz zm0Xkxq!^403{7+mEOZSlLkx_pObo3I&2KMcLn}~& wB*=!~{Irtt#G+J&^73-M%)IR4(^b literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/md.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/md.png new file mode 100644 index 0000000000000000000000000000000000000000..65fbb0b1af13c36198e85ec579852dcd763933ba GIT binary patch literal 3024 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 FV1kJd_#*%S literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mf.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mf.png new file mode 100644 index 0000000000000000000000000000000000000000..332a8e3fcdae3ca3d97bd15487b962286f95da5f GIT binary patch literal 2458 zcmV;L31#+)P)001}$1^@s6wfF^v000SLNkl^UycUfd3DHFGL?ckHG3%V4jO zzc$f|5}8ih&sf>ac`!!*VFHfhNl7{B+PIU*Og}P0=@-^di<3w>m75olTP)^{Ustuc1Xfnfbl%knC=d-HC+mb?+xAQso&!KvI&k7_Lk4Xam69j}c^B*G9o zYrYZIz$wOqRMxQFTlS1PEr$;4F|!UCr#j)y$lqVe8(|GbOP!9_#U>HO^1qA;t+g>7@J+ig@`LG^qDU)+5Paj79!7|hXc|Nt;g+y8-&k_%Qk4v zorZ^bYtUAjjh?n<3W)+^m?v7+irMZR`wd=8lfK3QczFSP_5glP7&NFf=TW;iK7KK&+ zz7_Ho+u%QD=D2TRg{q~Wpls!8+?&4uO|wl=wQMCSZSB$c!F)WNX^IDPEm2PYubOKH zG&DSaPljrlltvVG>kU%NzF~<($h2DlEB`Oy7{3v_;@q%S6oeyrH8>g?3-@yYa7ih` z@uCKNn_CNyP!Ze{vhibRB6{rX0fnN^A(pMb$11;eG&n8$hE-IA2i6O*J<538{e@{g?HE=q;f)xB6HI*EVfSHM+t3w{>^;O*^=(^sy+Iin0tqBMwd3UM(x z9pO9of#1Ksx9$zJ^jfB*6NSSDgV3^XSP}_(Y!<;MA_WHu%HfoG7pLOWa6302O-)T` zZf-_heLb!wBvXKCIFeb8BRSPb*}4mGbsf^)k`*_y%5M%5*&J)$Fk!W}qGy4%0Lvx0 z03kP1aU({A($Z2C78atoxR{z+gzW5WM9}j-Nf~g?t3?{kEI2z4eXFGC;?zDD8Tq^w zoI$|S=_;VnTm#Ft1C^C%pZ77mk_+)&at^MCh0&TXLT+v@{QdnA7#N64mo6bID+|HF z!MILgy5v?P=BNj_&6~j!hTgA)PW;#DxTYE$F^2Hk)C?kCvVkp~u@u8COrF@l(Fb#- zWn>%$77xV4pw89~Zuxa^OUS_Gpdf^XhQia+6HZP}GG=x9`F|vjWYQ9|AXTqO~m(r4Qai zOSe5$TiNGkp|W1auHKPIUYp^|4_i=A!%}MtQPysT`|@wVvos5~Zbk8@>)}f4(1|b+ z;u8{tj7myMqQiLuZI%+Dz^A072oWiY#j(U}oDyX~Hg!4>8w;(j3WaUfXpnEDnWcFS zl#Xf_mDE6Ht+>XHnL3OiJqwFr?xR#n3z>Ae@U9AH&!T7iZxOxQ5!bF>M@(8KZe-D} zn3{oWw=xl$mL(jAr{_SFosVc*;IH`jp}=7cxJi?Q1|jdE4WQK=tzCzNn)vkU=+tmW zP8Be}How3Pdq~g1($|zniXF(4%z;dK41C)|V9OTh#*atK(&8QR?tkE4B3=vU}w$(?Cb<( z;o<^Zz6`#+3_4XA&8E*$(lH%PU4I7Ak0OG0EFYeDkIb|UF%0ecgT?b&$d%utLN*I` zJ515qy$U^QZ`vKxshSiVqXXn}&|wQWe_q%U(fI%z(a>uuq0-z$2hGZQxebatW}vFW z25L>Z@anl;uHg=Q zQf0(Yr~=Wcm`CTYF%*>k(lL;z-=cNMoQ4HzJEwwhV<`|5;hIM2+h~#Rq!|_`>;kkp zmcJe^CexQ)HB9XW1MBIfRu@wcdnv3%RK<58Q%^>NVmgYYW~h~$Ld#5`=V#OGa-j2< z2R-hsPg0qhJLHka{L;*TwHqp|$6F!2o`gK2Fe+)*T7=y4&9TAD>|Kh04*INI$> zs@vI>TjC5~yBC2q*rnGlB(|PRTQWkl`+vcbbf`JUA99&D=lb&_4${klrPr>wQp&E_ z6iZ$mY1+L^SQ;(M2glTrsf&Y0rgkq2R$IG@+y8wI^NaWV`4JE4MPYFqkD_u3Yx{Q* zd3Boa=m9lUSbFV(qH4*MMOQ|Lb}tI6RjT6lx@9xtMw50!g{9Z7piIIp{UT=cX*U#D zY7OBpMbwZ<3#oRa8PY(oS|mN(p5s}}xH*2ji4jy^uV{GWm9&vXf4f0mnRdsjmD6T};F~X2(gfU?J Y7erH+eQPjo5C8xG07*qoM6N<$f?+qBLI3~& literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mg.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mg.png new file mode 100644 index 0000000000000000000000000000000000000000..9eb2cf69c357898541ec9c6eeb1e9cf6cfd63460 GIT binary patch literal 415 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIqJ$RQ`{Zc`cCQC<*cl{sRU2tRYfB zF~%fscNgAoTN1wlIh+L^k;OnKUk71ECym(^KtXd)7sn8f2s4}~rxtk7)0gX^Cag8WR zNi0dVN-jzTQVd20h9Jw0dB*=!~{Irtt#G+J&^73-M%)IR4bP0l+XkKpIC(d literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mh.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mh.png new file mode 100644 index 0000000000000000000000000000000000000000..1d17d4d1b97eb34720ae0013b71e396415c76005 GIT binary patch literal 3230 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 Q7ytkO07*qoM6N<$f{|@9mjD0& literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mk.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mk.png new file mode 100644 index 0000000000000000000000000000000000000000..da1b9abcbb0ee255792bf76a0e5ac9907b7fc252 GIT binary patch literal 2129 zcmYjRc{J4P8-M7!i7QzuN|BJHa%DZr#n{G3BGS+`$kMc+h#{%TmL$8PlC5M-Lza{n zOG4=)r72h8W}7i%w(o3y=AQFA=f3CjKF>d&_xU`}=Y7w6!_~z>PG*Y?0LY;oZQWKg zW_6oMuUU;m9aa;7B+AwKn4PE}L~lXV2ckD1B*XGWShR%2L%@s$(EuBfz>0 zw8Ox?3*1~lieL!~+(KYRi!J430M!;4{y?<>IUQ<2)ki9Ol&(N z2%B@bXm0yhtYUBFHSMj$Xw0j~;>`@p^lG#6mS0s{}cGT@{G zCllDoKtlubA}pT){xje|1x_|BhXU^r@Ed@e4V)}sC#=3EpbTbGSCeLhn~VE`c+Jnx zuSO1!FA$1ELIHvxJb^$c)^CIUSM^H)*%HQy3y!SMW^Bbh{ z6RsI+KDCmRxGqs?-(owZ*b#=cNvW2Qw6RLp*?^MN)JWC{u91<_Yg}yq_%bSpRM^Si zO$hyFIx*08=J$nd*mQ#n%%3l4+hTon)N$(mZEEKGOt)KzKHN}JP*BLs%v2vH+I1+# z{hE(Xvp2#A1e$Evd^DfHN|EVRV+_AXQi&-Th21Kt3PP5mqE6&> zc1e90W_HjJebO`iUU^B@*zvE#(ViT`9OZX@lhLemv#F&<#*G=n+xFDeRwXwy%3)P^ zE66q-Gjie^8GCEhy=Y$=-;`a}@TqdDOiRB2dnPaBd6V_n!r$uCTbLRgH9-{Ii=ipb9=4qs`im?b>4a!N|{KF@L!R9b#$ z92(T)$r1fEuDfbz_m-5-_#JG@2vJiuQSskM?tC)uAg%3I>}AGnOf0pJsU(b|dh(ev z=-gmWt@5jO?ptpKnVQ^2%({2#%yc zt8q3;h2T$+Kr5#AYa}&O_0a1g>?k_1qw`unYN93fe8huTQ^TR+B59PK+NQ&UK{YRz7{Od8VK!cGU4iT1%~&GfC002)h^So^V!HQ~RWa zs?7HnKd`=+qD`tV?VT8wPEDc+Y6zaDAsKTyews4512!sU!8IX%dIz4COja3>tFN1@ zC&g=ZZ_Q>BuT_U+`FR8lPvTWZ22JYDKf-7nb@Ea?72%5^@M#H@A)aAi+XQk)Gl!yF zA5HMsUb1Cgj%2C-ael+7XMb_M^h~7bOH-S7UZe#yX*`fqMzq6wR^N4(?e|GJwKlM- z@eGqc-TLv|mz!IcFYSGjW`|$+D0iFuoccERdv&XC(zIxe%)9bW_k&TY8?@~4i_5Av zzgW~8q-dDPN!k&t9oxqCcW?AnDfsC;T2SBTfMMC^4yc3_5^r0lFT8T?GYJs-up{nJ z*feU*$du!{(^n!bMum*?cPO4U<~O_}MmQ`p2GWz$TEN_ONIgwXZAN%iqBxw> z__wei^I^4_265~%SJM#wfru6=uwSrSeX9G6~l`L z=hk`~;xCq@RJ3~;HX09*vqHZJ)^JP34m`^=HM_h%q1SLz6k|u& z(dgJE`KsuB(R0Jxjgi?s241*YD;3mNjK77`kEDuuqD@quuaf@kuXr!ZFKw=;s*jW( zkvz{HC{i*GE=g1zIdS$_mt?J7xXC+gwu#RVMq%q?qgs4&o|W#VF{U};nS6NY-=iyf z=^qkCXR`h1Wa)VM(jo4-@u|j_LnER1+@9E)UMZYDisY`>+ezu}8*S<|`z?Nt%JKc^ zL<`p`rTT=)Tl@D0SaQQ7_M}c2j3pP(S{)c&W2R4bHX7MkW}cfJ*O5%IW`36*aJv{Y ztJR^8-l`7 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ml.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ml.png new file mode 100644 index 0000000000000000000000000000000000000000..a87c0abdc11f66bf431859004a990faf3ff2dc79 GIT binary patch literal 377 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIO-!Y4x-BK#Hj($S?TszuuPK%RnAulDE4HyI5k!YaoZSz$3C4 z=(Ot~%;=;sy8FMGa;&D7Vz=^5n7)OH8v^EA#p2er!ruJ${hnlU*3AwpqQ()?q zFgAu7`9BZLt_D5=YEmt6jVMV;EJ?LWE=mPb3`PcqCb|X|x(1dZ21ZsU2397Px&|gz z1_oJqYIZ0Za`RI%(<*UmkT#yz1k@l2vLQG>t)x7$D3zhSyj(9cFS|H7u^?41zbJk7 SI~ysWA_h-aKbLh*2~7Y7E^LMX literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mm.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mm.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f9032c8a0f6ac4c8a19af4cefe9f8cc936d158 GIT binary patch literal 935 zcmV;Y16cftP)001}$0{{R3f+qF10004fP)t-s{>uRV z%L4w>C;rbB|LU3k*h2sM<^Si0|Ns8}&lCUj$p8QU{@6bM{P6zE0srQL{?8Hr^Tqzx zJOBLdxWW>-!xH4-gg3G>L$^QA?aef@GhoAC@cHmHvNV*|l>YwyKej&0>&rW}I&jHv zhth|L(ubMYnezJaHnTNh!(hqk$ou{K{{Q|+x<{hiq4N6jI%y`}M-B!O=yXCw2 z{rN$+K#|pv>-g(8vo>nRY0>V{{r>$;yG*O!tM&W!JheN4&w=Cg<8;b%hS7!m{`@<% zI=<$g?($IqgSQ{N(2T^7Hnfr|wZ- z@^^y&`}_a?{`}qG@pFFbFF@-pKKQe_{`va&y1(vDTo-UK z`uhL<{rb_=@MLi6EIs$Iw*K|@_o}e%L{sW1IqgGI{NLjI9h{uy^sl`v5wjC1H&(4(Dle=gv3T-MbW3N-6&+k}`>CR(VCaXjWxarD$8M zrmMx;q*Ml(R3xjW7PU3vP<8b*==wTQtcFGajSZq$P0awBn*?;W@I~HA1E5>wyq#1V zU)nnW03GeTom3}Zx)cBaMHg=;uchqn0RZ%LD}QaN)JR3GD&Fet>mOi6GnlppHSBVK zU#|d5tJ52dtl&HBqCv0I3fkk)@CdjwGCU;oyhcr9Mh?k1W*QZK_R8@Ib3w{HF|I7? z9w(=!3sBQjlf}-&jAeE%k2E)HnIV|3`2}kZWnGvjo7F{|J@X2TcH1K9+-sLI|IE6i zC4WQ9jttAOO#U8MoLR@^Tp_kr*Rrg&RZeT2BO9Ap*5(FRJGE6fE_a&nY@o01(^{9WV!>{U87Y{AAW)7yyorPfi(zIXyW(0)X%# zkrg=ua2}1NKV#8z0A~>*D|CU&t9at)I}yLS#6^h2N?haS_U@ilxxc%;!F7VbdU$+# ze#xr6JU=}?5LmBo?;knEkN3A%0xS8MxBQtTny+G0vP#xpT3-T2SVMhyQE>nO002ov JPDHLkV1kYT_qPB5 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mn.png new file mode 100644 index 0000000000000000000000000000000000000000..e8f9d9d1a39d5f0e9d6f1e35ee4b2904bc3305ae GIT binary patch literal 865 zcmV-n1D^beP)001}$0{{R3f+qF10004!P)t-s))g1K zBrqI*#sQAs*BBSq85i0p71tXU-8U2DR}brU3-+l6`L+b*To2Y37u_@y_pb%{x&z`* z5ZE0S}5a?+Q>T?X{UJmA94%i?T?~4iV zi3#q63ha9e+%Oc@7Z>425!fFV;!6?fZVcyT4eD|X>1_<)L=o8`7VLZr-a8W485ZC| z67Y@*`M3o7y94y02IX20+bb0Dk_guv7TF~g+bk5_I1}n|4BIUf+A0<8ehT)g1@@~2 z^q&XZGZXKM3EM6d*d7+#G8E-o59VJE*#q6YDm2azs0HU{ z4cQ_U@Q(@dm z0#OtP@Fz1R@v^oklk7qbLp3reB_<`?WJ#8?tL!0J=MUGZU-?0E&vBeH-#_4U-n;jm zdmlnSq)Ji!eH2ybw~;N`k}cViE!q0N7C|aiP*#n)R!u@#n!0)nWN8~3o1pI4+|sH9 zvUGZbvCV`*EOR?WSvsr`7Ve~q>FNfsa8GZah3X%$4Fa5NXxQ$cN18?%0E;k>jXTF& zZjjou)(LyJM_M28B*!_d$UQYp)AWp&@U?QBnyAp4Ew;~(0&~In1wQ0MiyDdwN2FMI zDZ0Gk^scT2q+5Y?K@ehsu(2uKBDruWTientq>Jw)cB4!L16btV{y{QzhyiMkj*h95 z)alt0gcY)!yXa)>0>U!4I}FBjqaMPF1~XgP%k(mY#a=o1>zf1#V6nH%T{f%rGy}YQ z+kLK_L{ze6dU!17NxG%m)|Rr92dKUE>A6tIz4%#4R)*(aO~qCF_Wq&%#7Jr6`7>38 rGdeDcgG6cD#!It0FNzHQ>;Kmm7@l-y+=ACx00000NkvXXu0mjf%omEG literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mo.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mo.png new file mode 100644 index 0000000000000000000000000000000000000000..f7ba1c3c1dc38a0be1ed3a2cf37fe6199f367eb9 GIT binary patch literal 1673 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mq.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mq.png new file mode 100644 index 0000000000000000000000000000000000000000..b723c009ea6751d64376bf4b74aefadba57470ec GIT binary patch literal 2714 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 U+8{8~9smFU07*qoM6N<$f?(E2;Q#;t literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mr.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mr.png new file mode 100644 index 0000000000000000000000000000000000000000..285f7374027592eb76a62a915b358801225ae553 GIT binary patch literal 1122 zcmV-o1fBbdP)001}$0{{R3f+qF100060P)t-sEu2d- zo=H8TMNOzckiaM~ol1JRFxlGcC7|LFj_&>2>%JY20hUamSfphrojL13>rE}TnausFQX7}VJh+1m}{-wOQb0s7|x z?&AjVYp zqeRWu5DRjWNXphvOH9Q5S`>fr}csy;EENu$RhWCP8tdW*ioPmctvb}%5R<_t!_yT!qDINp6VccaGoDFeuQ<5R8f>#Q z_~!ypsXm~_B8I&yN~b`ByDazS1CPKcN~b~O-wEg72sEBaywMoX*AYjiLd(_@*V+z0 zqeOqYF8k;K{pbO^&=|PS8J@)>;NAG40v}04K~z}7 zV_+BsqhJ(_g5d`knW$$LGYc!#%wl8bpq5dbT--clY2@Wa;qvhd2nq>{hzf~`O9-L{ zq9j4Hq@ZmcGp-IpzEo~iSuCAWGfd~i~8li|Y=o*_4G|SXX z8dbnt-2!NorJfb4u#mM4L9=Yl>`+6DLDn8h=?c^;x^3OE?Y#TsFFB}Mu~Xw5|_Or%Yce= z%4M+?TC5e7IY8MeX(EdXt9X#jBGvviGH5a~wRP1XsT^lUV#^)#dT@5mZfI;m4q>aB ztY)w+A}y}O7hZpiwXiMgh?P0N?Owc2Hc{TL1t607*qoM6N<$f;Mvg82|tP literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ms.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ms.png new file mode 100644 index 0000000000000000000000000000000000000000..94d50aa75c9bc355b3c66229583dfe3fa5b75f13 GIT binary patch literal 3535 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 JPDHLkV1f|X!?OSY literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mt.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mt.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b0ce0b6f38c044ff9fc0fd9fb12530c9079c16 GIT binary patch literal 806 zcmV+>1KIqEP)001}$0{{R3f+qF10004=P)t-s|NsBy ze}B#Z0Q~Uo`sd#L^6==jlf9T@x|wXhmtgF_qTsQOp__`IpP!?dgXX)NqMVDMprE9g zg#7mQ-LsaYo{^@VjN-eZ{P*|r;o-cXps%QSW zoSdblrJI|ZzObdwqju1uaptdx{`&g;^YY7|Yn_~xpPreYo|>knrJ)F}Rw40087@#C-q& z0YgbdK~z}7?bg*&!$25@;o!Q!k6Uor1&XFA-ogfL@s<{McX!vzW;a(5J7H$u!|&jk zot-2TNitGYw2>rX*|K6{_2Qhc;u8{CNy$!FDXEB+mhOm^0jx}68MCsobGVh8mv7rD zC@d;QvW!Yf%gQZV6_vcI>Y7@N39GKY!KF`)rY%=fa|^4rO>tvD2vm>U-ocm7u5Pnd zPp{X<>gxweRH{)>@cDl>3M8QN3UtV3?eTQyt@9z0EJv=@=i|^Newq9P}SnnTBSf5|tte;=; kpCc?`2}@YQ5|%U8ANTcKoK^E~xBvhE07*qoM6N<$f;wf_HUIzs literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mu.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mu.png new file mode 100644 index 0000000000000000000000000000000000000000..755dce20858d08902d70289d8f4269b08e6a5d14 GIT binary patch literal 400 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI08-nxG qO3D+9QW?t2%k?tzvWt@w3sUv+i_&MmvylQSV(@hJb6Mw<&;$Tt>TJsZ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mv.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mv.png new file mode 100644 index 0000000000000000000000000000000000000000..31eac72d2201c5cb57f7e68bc706ebe48435eb32 GIT binary patch literal 979 zcmV;^11$WBP)001}$0{{R3f+qF10005nP)t-sw-f=j z76Pav38p0qsv!tZdO1FeLO_W@Eu2d-pHM-iTvV=XXSjWMzl(##m5Ilih{u_Ez>H9> zX)m2kHK0;fuWpIRn!De{-0tD-_we}q`TYL<=k)5V*tlo6eLbXEE}Tp`p;d3YguLL! z?e_2e{{8>||Lpefq|~ufu52=%PcWTMUbA(+;K%j)_xt_)#^laowRkt7Q|a~W_WSpu z)37#g?zxyEvg$TCsB2?cGYM zWVYSG{{R1b!H#6Mdh+@7%I4BMqgXMXPW%1)fy0wUr(fUjRT# z{{Q}y%%Z2(v~#_OFrH4g-N9kBcQKw%yx+!Iv2mx>v-bP=W3_pU$(;QC{C2;JXt;ft z&!;Y&OGv3=;_>Fg{{H^D-o#w8bKLLY`u+Q&)34d>-aVsORIhEs>D}+*fy9$Oq*~7C)r7^BOsi#p!;fPBG^hsKxy z006ekI+_3g0kla(K~z}7?bp>-f#r(={XzX0rs!lHCWsftU`{wfV)6qS{uy{fIK@Jk~dG%hU*4O5R%3)C^lKt=wvZ?;N3>Q%N1L)!D_Z#BSK?>E)Kb z54JQP($S#fkFJfAL9EsJc3)JAN@5p4qFqG+?tZYmU^07Gqd>nILB{7xkP}i z`2{|(#U=dRUS46=>Y5q0);ClPYuLo|#MU;mEIZ+NHrd@{*uJ#{wuJ5j*8MW;A)J|n zqhli**~zIH&R=Ke7ZxHS%jMOzfMz*OT&!uBh)8#Hd)JKSLc`sCSIC1-)w*wbe0q*R zGp1S``11M|ROIJ%D*a8__bb+S{uRFS4`Z)CUhk%2p(2j3=C}X=002ovPDHLkV1hTm BAvXX3 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mw.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mw.png new file mode 100644 index 0000000000000000000000000000000000000000..d649d1144da1bab109626c695117ee109deefbcb GIT binary patch literal 1215 zcmYk6Sx{4V6vpp=y-RX)6BZS$QvunRvP4A%YgqyjNZA|}u|%XINZlaTh0+p(G-YUu zpdwJU0yaWaWYbn^MHEHB-4s-4)mp3QIHEpu#!j2ZKAiJ?-*3+2H^-c;ixUM`fngX? z8y}Nu*97~7bI87pUT;n?44j>um>z47!0s7F1{lIH5&)QCc)-~2{4kt`Kpv073H(M* z7EriLq#BBhGLp)oc#8ZnoCE<27&ipg33#VSHVOEMMAz`RN6BV!_@t68;IT!bjS5;M zV5>wIb7;Ga_+mI6fjI=uB(P0lSMk^&;3fqvmy^$Bq(!274zCyRE|KK$_+uV-E7?K; z?-A)N0=*EJfxtqFszvgpoK(ok*K)E?K{pE6qM&9uSJgyQ+iHsbRXqS@xh(jV^ z7P08GlFjGwat{5Vpes0>#G=gtUc#X|k?s|#Nk*C#Gz3Ex0P_f(PvH9mx+CC?Kmdke z7$&o5tBf3%*jyg}tYkAd6hUC8lC2e~mZB*TaDbr<;|RgK2*gpej6>-x`c_U_CAvYt z`xVp!0ScJI65S%;wLDg1C;%o1Lmi7e5m?HhQWm1u)V z6Ij%sp#3UV4wy&+l>pR>G=`#E4%{IH-7V5jcsvb)c8UFvMHf|Uhr~W};%h{*R=^Gr zs0n=O#JzUn^0R#1%I95wHfx%_HqqLc57L+Pt<+D)Q})&_T{1H4;^N|i-YICvycf|^ z{1zIc&bl8A_@mz2D=5It*E7(=-}~4BOOy3r%b}yaG5hM(U7dy;!<*Nlyt1h>DLwJg z&|qM|5$izkrGDF$vEJ(gBj+zJ%G&i?(xrjXzTTS&{deOo?un@$F8Jpy>OtF$&zqy7 z`tS_X+oI940fw`Y9`20=joMH@FI!sd;K&hJGc45)Ez8CdbBaT|Exwz}x-apC-N4Y+bz$JirgV#Ka%f0!d(kH6a@P|r zKCeD?%&m!+e);}XsczjOgrZA!$7TI%GCO+f>LQw>O_im2EjD$<6FL$W8fy-zI3G7w za;R@GU_75{#+P0p^1@jsf)o>lwppNP@M>SB&Y>5cyZQ%HX% literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mx.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/mx.png new file mode 100644 index 0000000000000000000000000000000000000000..fdb43b6deae56e071fdcce4af698752700a8337c GIT binary patch literal 2131 zcmV-Z2(0&sP)001}$1^@s6wfF^v000OYNkl53rl41^kGFdFrk8{zVM$mLmV_l?Nmvq=ge74~SQ3_mC1FWe z64s}I)%>D~ih{!wr$$k7B#nleGTPhTNLar;@87-SY*d;95O-GBUmdk^o?*4jeBo;Y@HGAGr|nwSkg5w&O%itp5w z%VX*ge5a`qY@$J&r78Dn%IWOv{B*GHJiLQ-;Cc#*^0}C^mq_!eco?YTHf0EMqhSR7 zIG$azWJJtR$InoWPytPDpoT)Iv_0!)bGzjH|A6&qtkb1unB(w0d-sHr+JD2`%V3Byzr^)h8;U?8T7Xg43jJ;a?JBXYjB5<}eb?zu{^9ot}fG>8j7g+gFu?XZ!2dl+;|tHo}siL|0bK|B`U~ zWhD7;A=Q5?E{1Z{GhJb5)g*GE9!V<*v#Ma5GN+@wR z6sybi+aUza8AV>GBTt2VdQf+V%y2JuuAR?57h!yO(<=Oa6dLY>F8-0v97;i`M0b8;a^Vqd!7_LH91(=S(TNqG9 zC?|bY!6(ay>$hu2_Vge(AznB{KjHkL6fR^YQgJSyq|Fw{4b}0S zp@IJ_EoJbFGgi!kah!?^;(FOtF6L$`v#_Y}0M<)Jb6{}a?wwh>4!Z2jkEZUau*G+@ zBhQvI!gmBCJx5~bHwCW=YGeetQF*(PBbSa)Tvg2DXN{Df%_P=#3h8bJ6r_f*+inr5 zwhPG?W~;ntUrLW3;nDS4Y8z{D*k>bb4(c2p(5L%i4HA}YnewH$r|oTJ4SKA7x*eY^MQAam4asILtDos+m(dqrr+50swFAunw^ zQMplA?Y3fV>{|4K^cdhZfd0Py8RRjDscsYa!s9dg%6l=^Z8%oZmdJc$Xl&|3qTO4}|%vM;&)MJ3kP1 zGK}HD!{WL_#E|LRF@-z;axK3^t=ej{X{KJl01_#k9p zk32}f7iO)d<|Ya+XAux+flG=V>yp>7KHZA=)4y=y%u%9#3CGiY8?h1L9M3<--l8;S z$IoJX*k5qVawaP$hLWmtG`@PoyASVr{-1yE-*#}V<^p#6ZP1O-WklczbQEK;N#D$D z`44Q}vYd76eq>{!Ez_f?qvof^nBXxe@)fi+zZ8x*EuY?1SaVx5)xvkj%W{>WpDfH# zj;^bSUA2NOHWmbAgizH`#fhpDlsA;|qV>i9-Ryj_m#i;^A@f#6InVD4JKmMcJQ0pS z(v7rqeJ^22SQ3_mC1FWe5|)G|VM$mLmV_l?Nmvq=g!MbDe*@O6L(7dsQy~BV002ov JPDHLkV1kbe9`XPH literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/my.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/my.png new file mode 100644 index 0000000000000000000000000000000000000000..db64b56c50a43d9f4093944d8b7a1a31aff6177c GIT binary patch literal 964 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ne.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ne.png new file mode 100644 index 0000000000000000000000000000000000000000..959afa7fdbeda1fa4264316b579db7e94f7259c2 GIT binary patch literal 512 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 SXCnnv#Ng@b=d#Wzp$P!*_HDxe literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/no.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/no.png new file mode 100644 index 0000000000000000000000000000000000000000..326de62d12a3984ae3f67c2540fec11befdd8365 GIT binary patch literal 604 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!3-pu)V^*9QY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIJA-8!97SkCmr%zse`SZbpw=67f+uMIVe*DX^W4G<>zF)icDdX`?W%iLfTznaB*rb@`N^v;99l{?w(X4#~z|M&0Tnd`46t-ADj(xi9G zmi_(q?cdLzUr(L7=jZq5?c3k4UfuKcy`iV~`{m2K9v(pFO#Z-g0!T3?dAqylp1tL0 z3*>MXctjQhy?-5q8J#p{R{#Z9c)B=-L>zv5#Z&m8gFxHEzC@*sDUJe1gslJn|E~Ec z;aJGbX)7On*n53lmET?8CC^P;GPXUj-LLEN;Ntg#O|0BvYofwdTz{z*wqE|0+r+pz z8eu~2+a?-(O)^|`8;25+kiIcMEp;>%b+zQsHph-d{v;E zRZCnWN>UO_QmvAUQh^kMk%6I!u7QQFfn|t+k(G&|m9deofr*uYL2sVGV-yX!`6-!c zmAEzhF(yEr+qAXP8FD1G)j8!4b722WQ%mvv4F FO#mcT{5b#s literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/np.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/np.png new file mode 100644 index 0000000000000000000000000000000000000000..c9916676d933658f270703601e911adb89cf0db1 GIT binary patch literal 2191 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pa.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pa.png new file mode 100644 index 0000000000000000000000000000000000000000..fbafa66e2c8138ba259917c158f4f72912779d0b GIT binary patch literal 975 zcmV;=12FuFP)001}$0{{R3f+qF10006IP)t-s|NsB` z-QDSAWawF0_{+=P5D?n{0RH~}{r&#u=l8#(rpSy{;T_Wb+%{%2>+8ymFL)bZov z_9`m50|Ts;mE5nd8Y{wudhr@#56R#?(X^j{{H#e+W!0d{O<1OVq*H>;Pt=1+ZGn_ zqoe=*{pxjf*Z~3MR8;==_xtDP-!L%P007(@8~D@H{qgbjy}k3Wu=B32@~y4%tE=pS zg4qcP*aHLTYHISRsQ&i$@uZ~RE-u*z2iXM$*a8CC0|VIv1l=Pe@067I)YRr)UfK%_ z1}P?9Ubk9i~Q{D{`K|psj1*GG5h4?`rO>;X=(Py$Nv2M>UMVh^78%g z@Bjb+x~1!N0004cNklvJr+f3JVNL#{A7$~ zE8n-WSOHSbtOAnGtRi@KWCej&M^+i|u6v7_Yu~}Gq)_o`%HGFLZWKzWJQy`wU0j8M^JWgs zRyRL!TXW!9tzM2wVp1PGMy*yPe&$J1xQ$(_74ZCdpVbQXxZAY@xU+`#bGX&Rrf>OH zc54_mTv;Qq=E@p_RY%r1z=|7d3Shx%&HV0`WD!V8dCUK|W&z}VNFfkB&l_?aum1Z` zl@`LZtzL@|m!w*lFxP&v@mi7^E|=|nT>XltTb~n6jM}ZG_ey}1_Lpg^HJNFL!7aE7 x4_K{jpkEr^+&P}JT6;u(ADCSC=c;dIjc*Wlkbxz<2G{@q002ovPDHLkV1f|Y7l;4= literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pe.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pe.png new file mode 100644 index 0000000000000000000000000000000000000000..744008621333d302a369a19a873329c3d3c1d412 GIT binary patch literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI?BdRuxg19^-|-tI1JiD^4(fgH{PkH})6yRL&U zqm#z$3ZS5dr;B5V$MNIf%#wQ+u_fL)~&y8Qw5> zCA4?M)C2XXmbgZgq$HN4S|t~y0x1R~149#C0}EXP%Mb%2D-$Ct10dJL%D|wTZKfrP zhTQy=%(P0}8h%A9wgNRsf@}!RPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw{mw=TsEEPS L)z4*}Q$iB}tO0Gu literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pf.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pf.png new file mode 100644 index 0000000000000000000000000000000000000000..000c41d143f0d2dbf65a2003d3042e4f0e0786dc GIT binary patch literal 2097 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 Hu0mjfUuh$7 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pk.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pk.png new file mode 100644 index 0000000000000000000000000000000000000000..6637d24d61f213f848bbf109e1998c57e3e2be9d GIT binary patch literal 988 zcmV<210(#2P)001}$0{{R3f+qF10005+P)t-s|NsC0 z|NefbeGFL(4O$EpUlc-qK|Fao4q6R3cQ>HEpN6c48)F&}Tn}83TiNB=u*t9=W*!t? z6AxPsW|n2@@af;_-#U0XA!s0XqIVKq5e-=kPlr$D?&kgd{e`TB9b_EG*~g5rj2>nl zKzu)gs)9LqI4Eo<%G=81?c_CeG!|eLz|_F?`1Q!y$La9tsKcoI{rs}avJP7fDQzfn zo^j*s z-RRxR+{?w+#mn2u+vnSeu7?+47A|ltsKck>>fy=S$)&-hJ$pPMXCR`#qR`*a@%8cG z>fpiDz=o}cA7&pjbTUDGKtz8-0002Zb6V;E00F2;L_t(o!((7T1&mD0WCMm#vqsGt zHEX1r#X>c+SlQS)sACofCl@ylb=<|v$1fm=;0aM+mavGZm^gwfAqjM%uoPKlNz2H} z$qOU+3W`d~DynK^nWe75ps9tVR$E6`Pv1bB9J7Q~4H=9OL1b)VYR14|&PG;vS@1Jh zS|MW8+Q!zBfx*t6gB-IQ92p$>kP@1cvkQZ*tDCzt>1KI&GB9{KqbTW0tA;PKMJ-vPX6Kqp`CQc%^9-2Hw7g^SFsxK?~^<#m^v_QB?@zZArQ&QKm%;cLj zJ9CbY-Q0QeRfH&RPw*~SxM*>*MIj4Cb?s>TYt*b!vqsHgW(ENJ#AP5r)N-!?0000< KMNUMnLSTZI)9u0l literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pl.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pl.png new file mode 100644 index 0000000000000000000000000000000000000000..ec7a4954b4ed819a29ca4448ff32a07453e7df44 GIT binary patch literal 359 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

2g1dNqtyIZyf!+ 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pn.png new file mode 100644 index 0000000000000000000000000000000000000000..7c344ea73465299d3f99106c239bb2e23adfadb4 GIT binary patch literal 5116 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 S9V=JZzk*h+FC0@{ZTk;Na}jj_ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pt.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pt.png new file mode 100644 index 0000000000000000000000000000000000000000..38d60e9c1776edcc8472127196ece61c99ac2dc9 GIT binary patch literal 2539 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 B1cd+q literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pw.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/pw.png new file mode 100644 index 0000000000000000000000000000000000000000..cec7edebe0233c6f894d9267e70d08c927d6b5ec GIT binary patch literal 876 zcmV-y1C#uTP)001}$0{{R3f+qF10005GP)t-sO0CvQ zt<_Sm%v`d-Wwf?wwXtWlv|qEmRj|oSuG3qx!GXM$uE=1}(KzGQBJ9}~@7faW*%jv3 z9oN$^xyw?HzlLPAw@$9oV6(fQ#Bto!Dfrw7|K0%n-2~{^9JI<=Z?~#VuF_Gj&5XZ@ z-PI`l-2?vK0jI`jTe8AgvBa^-T=v`x&Cxq-wys>V!NJW+-PI{_xTm|!Q2gBk+te#) zwX&eZaQfW_#?M1nvBz_`rR~`knZkKYt<+Jk&cDq};?^T#w7T)y5t_qy($Y3mu*y`h z%hJ*{@7fbYs%0+>DU^6yO{Ia52wayO|H{W zug|c_UewYvUb4T;&^+hY9d)^+X0^4&&P4m&1>V&sd%B!VuGCnu#iGS->)99e+Y7zS zPPfZd{oMld+YZXmK7+iIQm@TUuF-0?vB%Ft;?^SY+7b8M3i#X!=hz(3(Ko2ZXLh-w zQ?ScVuF!Y6p?tcVdAgrrv%3HQ0Gtof8UO$RZb?KzR9M4fU>F6XU=+{_FfuW-u(Gjp za8hg#7dHNjHqc$XEs|Ffk?7C`B`K>_*9`Sy+;2mX);(E?{nBOOjD` z_AY3GAbgm!XnJVzPO~6tO~NMyaLUil~rtv4CH|fh3bkLgqv#X>KiC%Q8YH$G`F<2 z1-93Abar+3P}4t$EX<9OHZd0000001}$0{{R3f+qF10006IP)t-s;5(>&(HPs_07%Az`wuI#l+jdy579B+r7Hc#KghC z!0qkrzrMcO$i?Q8gxo+l-$xkVM-kjM7~4BI=9Prn$HnsU^1i;lR71-^#|}S~}iB7TA!0tFoS_yQcH?^V{az($dA^ znTp^`7U5bs-p9ts$jI*Q?!?2x=a78iObgG8bzg;CKX5~Kg_oh3)3C79ld_n9r+nCx ze&0U}<%oR6!^7Fx+1Ja+;$So1R5FT`dNF1)e}J$~IKOsVfK@)UjEBW!mu9ZGo#9+F z;8io%$;jWuz~D?7-)>rFi)2%KT5DRoj&ftruNA0@Ms8ZGe4vPnuZiP%T;4qy-oe1% z#K7TC8R2tWeVltobxdbj#&}(Ss*FaJcwJ#p!fBOqk+hKFbzR>?8Q03l-%vB$KQy?v zrz~bHbcB>-QnyPr!&ynNZGw?JfIYjwtKCF2-b*s$e0U$@IoB2z;f;*M#J7;Lke#))5if#l^wDzuLOG+_bdd;NU$a z$^ZZW0DSGy00004bW%=J{Qmy@{!Qas0002hNkl&?!dKuux7lGx&&NXXp1y$?R+zPAKvAJbN zwzYW*tf@ui5-S{7)>$%R&>Q~st8pF literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/qa.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/qa.png new file mode 100644 index 0000000000000000000000000000000000000000..3dd855689ef8d2581f9b05712f3114dc84c4271f GIT binary patch literal 416 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!3-pu)V^*9QilV4LR|j?!9O4Zf$yKbU)gbC zN&4(M&7y3Mm^6m)-#>r9zW4I<%3}+YW@K~5{`vjq-QzbGHlA6QJ-11(ESD!fjWPWB z^(UJf)|7~+{r&Uz)60)H_Fmagy`s~)u23jBohjno2>Ob*&MNde*Jm-@J)ku$=~09-#>kKY0KFax$~P1%KrZT z^ZfeL&5djR{r&gv@82I^zTez`b!EZACjGKp?l_=7e80sB0I3aW$aly< zr1jxv_Gzj!*w|;zcbJgy?cc0)g-lk*2Oer{5-xW|ZmzQlHT_$B`L`k`rwZqSdt!5M zo|gTPxnEJ&bETiT+ZL6jVsV0NljpwPxohe-hCA2)ho4`(_{f`$_nEe92&XTrshX}K zEZ!iKmei0WY?g?Q{%<#vQrWb``7%R{`1_CS@^exRPrrCG1L#HuPgg&ebxsLQ070A2 A)Bpeg literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/re.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/re.png new file mode 100644 index 0000000000000000000000000000000000000000..ff4cd7cb3ba6dcab32427fe373e3a7f8dc9567a4 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIEamTaXdM}fywC@M}p9_HU>_f#i!h+_G(FonytzSxw&FfVCt1HHU>GL}eU1IrKtBP$aFD-&~F0~0F)1FOK;WE2g#`6-!c zmAEx{^E|x|)F276Aviy+q&%@Gm7%=6TrV>(yEr+qAXP8FD1G)j8!4b722WQ%mvv4F FO#r+%Xwv`y literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ro.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ro.png new file mode 100644 index 0000000000000000000000000000000000000000..70b8505fec5e12bd274a03406fb65d163b393fac GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIE zOpna@7!2q9zsGP-Q2;1;{rS5(AjMJ=As)w*3mlnx7IFxrOlxD{mZECNUbg0>?oRFI!0HtpeEH4*NBpo#FA92>_#kObKfoS#-wo>-L1P+nfHmzkGcoSayYs+V7s UKKq@G6i^X^r>mdKI;Vst0HhXgWB>pF literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/rs.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/rs.png new file mode 100644 index 0000000000000000000000000000000000000000..73c0531399eb32e02bbb1425ae8729ec3075baef GIT binary patch literal 2661 zcmV-r3YztaP)001}$1^@s6wfF^v000UsNkl|Sn+P^xZNmCgz_N}duW0NhtRDdiNXIhlftakGZ^HUf zu^9QBYM1Dw?k5_Sx;tg(K&1@T3Pvd|xFNlogWX~ov;{~~Y`cvUMqury5S9>_LIJaF z4EMfi(z+mzJ7jUl)?6?+^9b$Aal+y-GAN-`33)vsd*^-RaubmxpAJ~IScC`mp?43U zCdWvxo*?_yY22OL#?Yvh8DH@byZa1uug8fF%uD#Yqdu7nOd zT}x0nLcDU4W}%6v9U2^>HTO3DmI}51N|4F=^Fks1s zV;tXNB7v$Iva&_hQgmOSDwe336Gvy*-SjD^>%UFCV>^@ zo`*IDB^``~USH$;ib_$i^sb^dCqK@tP$=GP8Eq=N^uP_v5oxl73haF z2e(9w6a~o=5ESNNf;bFG_&To-+yIlQ~KcI2AMXz=dz2kH2`r=Dmd+i@- zzxof1{@SlH_`m^9oqC=6=!a12(0g-;?Z5F=`WG%T|EFKWWElp5S%)fi+nQxvmn}c{ zC58_^KTbz4uai<}=hLCTM+d zmhkPjNv}J|c^BdPxPuLHYZ*EE?AmB&qrxi8&ft{GXssE0?peIOd+;NPe=b1$TSC0J zf>*0D@YEAX&m%s+fIRL{xa#9+Mdk354DG+4B#9}EZ$oVz+n}%n6sD#~^Nc*t$qE4z zU;PSVbOZsolM}d&0SwTXnj*_Gg2_o_qXB?>&t4{e`4{OW3D$y<5^rqFx`idZsmFi+ zRu_*H6+dxkhRkC4s8`rK;``MT17Xmu%HvN8|=8YS~j)SRJiCZmp{pPPD zMu*AroG6OubUQQ`n|$Z&GGS&mSi55^CP{FWi!p{Q&&aZr(nAMHzW03=zWrUWmeRrf z3_gB{#lL%nbm=-aPVj#09@O?p!YBd@jw6s#VxxHD_J)q*5Cj22Kpe0A(Pp=W>UJsY z+DV~Yrl34_?$}AFJoNN5$-)BR$|~(n8!0712z4d2dw=Vbg@`M$_?H~3v0f$#eWrErG^ z!Gg`!MlU+-qsnDuwTe^cDpb+!D60~r5P45#*pWlB#zg9->9&ZwJ4>?@{CHkg5x;kSw`vM zhZq`Ivj{TqainDA!3QzkQP@PdFm%L&5}CFR|@$XqY5^lA&=^C^}}Xnk{1 zkZZ1d{co5L0;D6U&d#voh37f<7k@>XrRdcb@!$X>zwq-2Kq(Mf`}mbEtH@JWh4pFm7bp1N@r=CES%5*2jS$XAEf|+UZJSXY*S-Ef$1jzT#-N1&M{qm)x@)lh^7p=Y%ZrO9qxScIi&Tod*W;sa ze3QJ=q2C^-E_dV3El_^uGla9#oV#!yjvk@%`oH4LFQTS*)9jrix^#lYZ|6*W;YFlU zOzqpnA3yg6aF5yM?~=Ur16tEf96d>OWf(sAi1L?yhQiZ_ zafE}(GrE8M4Wbi2gguk27S9u8Ey|0N$O8v)Wx&|XKE^)({04OJQc~SBL-W|Hbi1!o zesYC@(8a&9gn0ivs$8b=@IzE9RZ682m3p1};U|$v3h$hvw9um-IQR#z&|Uf#y_0WH z-8;K+1-^b@KlQPl=m^3^2d60+J(z=Q5d(EH*Ck0}w9bjb5L>MxiUm~FvgLp#J?|2A z1SW#vdmp5*eRAWPQg5)q#F3vS{N{%wjUMIM6uDI+?$ZoB^;rhCjv<7D5CUy9RH}r} zK1RI!3UV|@?h=Hnf=XwcZ7=*3j^}M$SO~%JGtbaH^&SiV@*N@@)Aux@<6>%6!Z5;d z1VT7iYu5Z&y-s|_AzlEM9Q^AQw*S)0RQB!NxKoFZ$E~FzlVAQaL$i;dR~1AKN{-Xm zxtqZE@jM^bb@5z}zz-Oj-H(ath*%&xWhOuW5~DA^h>+|1X8Mlxt^VjJyMFh#alhVR z`S?FmdFmjdR6;7bb`t*2p=%A^_%_P5X*gSE+siLA`oiy%00000NkvXXu0mjfTB$3o literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ru.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ru.png new file mode 100644 index 0000000000000000000000000000000000000000..9e86166a3cbb0f903e74b40552d2c8a4c50cb3a1 GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIiQD7@CC)9U2eLkVr6Cl@lUaI%TH-vy6mOCnF&cJ@pEVU6rdTZC9V-ADTyViR>?)FK#IZ0z|ch3z(Uu+GQ_~h%EZ9R#8TJ5 z#LB=RD^JZ1MMG|WN@iLmZVl4L^O}GfBtbR==ckpFCl;kLl$V$5W#(lUCnpx9>g5-u U&wghk1ysb~>FVdQ&MBb@0K_L}#Q*>R literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/rw.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/rw.png new file mode 100644 index 0000000000000000000000000000000000000000..ff0476fa847fa4c0892d4ab231001a1db1995dca GIT binary patch literal 1005 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 X$9@kY$l7i!00000NkvXXu0mjfVU>H} literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sb.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sb.png new file mode 100644 index 0000000000000000000000000000000000000000..e92ecffe823a10afebe8b4d9632ab823c4765a35 GIT binary patch literal 1788 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 U!ED@R1ONa407*qoM6N<$f|HgKQvd(} literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sd.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sd.png new file mode 100644 index 0000000000000000000000000000000000000000..6fea21812ad8a7727c7c76a6844c731b0daeb628 GIT binary patch literal 832 zcmWlWX-Ja+0EXY~vv!n(`E4Ci2I174mc&ZB@Jz?N($&7tsm!#bs3;=O&I#Vqp<6ob zN1mxOajAKQNmv<~9axs-RumG5L4sWYzYO$w;4d2+3vnX|yfS4j;AVK{dWUww-1=WI_S2>*IoHT8Ls`7-WVp7S|I9~L5|`1x>G+8f)v0@KEqO<`iX}wofQQT_ zsYX@nb~?9M?W@(XYE)Hq-lmT!((zf7p`EZWujjwKS@`o*H#^!iw`K#=**a;qW+kms zUO#hN`hjSvjqlu?(osJYalUu|O^wXGUtjwobSQl3-O16ScmtK)r;VyG>QneJQBTc} zOFctpf{dj@rI`k~u-W-s>ZiP(@0VzOP*^Ojf3;dspU|%S@%F)1*TX*><|)pV=8@J( X=bKehqlj)8{x4P<5*d6yP^JC{Qgc3* literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/se.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/se.png new file mode 100644 index 0000000000000000000000000000000000000000..0bc7c93cedc5405ed194b74468bcc8c33f3a8ea6 GIT binary patch literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!3-pu)V^*9Ql$YtA+8Kr%k!tk{XN6@?+nAC zts2~U3l44pk&Ic(@}|TC**tmk8MBs8S?qdft2$84<3oaz7rXZ^^m=w!V9qj!=}TM= zZq?w)2dY^HGzzE%Najw7hZqPXXD@R+woMhNktur_#3G>0lm8Vr0V#=+Aiv;WfBygf z_2c*7|Nnvb4-ozO{r4Y`t*w!{1gNIm)5S3);_%xmmVC_)0xlQR%+t0W%we8!^v?gT zzO+SrQ5_5}f43|V7ZaSEYHf90xw31eyxUxPjlvBH2b)-5AGy}7`A6!t`0}0^do}DIWu*SfJ+|tVn@pXkm`=ooyriu@ckN#D>+IU>plC4T QAkYa6p00i_>zopr09-7YS^xk5 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sg.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sg.png new file mode 100644 index 0000000000000000000000000000000000000000..1e3e8a0d6bc049fb780fc7aba8b8c76f8e39e085 GIT binary patch literal 917 zcmV;G18V$001}$0{{R3f+qF100054P)t-s-v9vL z0s`Lz1m6Jx;uaR@M@Q~^d-I;2^rxrsjEv_$Ki~ue;S&?=Yisz*%Kr59|Nj2{^78bi zrr!et3*#Rj=R7?4#l`yA*x(5X<~%&_f`aHsNa7e6 z;Sv)4?d|DMP~Zjz>RDO*=H~E=i|lG@@{^O`2M6OJA@;Sk_Pe|4R8-;@7yRhx<}@_v zO-||u+F){Ca zeD8mM^sB4?{r&mT(dIil_O!J5%gg3AHuIdE|NQ*xU0v>adi12E?}LN>`ugfsRpl=) z;SCMo1qJ0WFy$>R;S3D&m6h#mZ1Iha=RrZ?5D?%92V0s8|Onq>}6%- zCnxJ%T;(h*^|G@4^YiIWPX7D*`q|m&N=oD`EcnOA4@JdkS?Us&kkYL9wDe`=8iSFZGHHOSs7wsLs=7vNR?>G^3k|Y$ z7K9j#khQ*{5neKMR#Pq^q`3ulqSc0wv8~;X5!a!kvJ9PIIh?qwyCoD&gNp7oalph<%6x%)lu_mV=&9r02mVC!~95N?GEN%v@**O@u*Jn>z z{(x^jNOOz}VDbH_kjciVSAUTQY$-7)yu1>>WVfzTT5IcIZJ6M0bIY;q_2Zq!p4~mV zUq)~rtb-8TS>=aE#~cnf@Fx*W7wohGu(NY$@#69d@-gPKYuY9)H&H^O9zEj5Z)5N+ zS|YZ9j&}NY_YZ`q9({uRRc%ktEa`j+b3$NDK;eGXzP-m7AvDhT@mUz!5hU5~sxRRR reG6BJ2v?#dTB0Rdq9t0Q^>?fvzJo%_mD_7~00000NkvXXu0mjfpCR6I literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sh.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sh.png new file mode 100644 index 0000000000000000000000000000000000000000..670eb8eab117fcd21e6d7487bb8e2a620ec17b3e GIT binary patch literal 4273 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sj.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sj.png new file mode 100644 index 0000000000000000000000000000000000000000..51905cc110f93df4cb71acba65bacccb9d7d916b GIT binary patch literal 597 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!3-pu)V^*9QY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI;|L@;7AHF|( z{nb5xi*wGVC$GL3#IDkdS;gl(m(g+tqviA|t1nMjaY-k7rB>vM{uLL$U%B${*RS8N zU;lpf>S02{-!EV82M6Ev@_G;%`S$&YHITzu z;1O92^!s%XW^~e+T>%ta;_2cT5^?zLm6Lo;0X(e_4~2$YS#4pk>VeJP|G#&BICkWc zE3?Wji~rkBZhx)#sB)j--ntKY1}14Pz0IuLVmc9P*Gw>U$*85bk+<5b<)1l$D8TsXDw)1+w%K^>bkg#=Q+{SOg&660}j{x1PTH+c}l9E`G zYL#4+3Zxi}3=BpGEJF;8tV|58jE!{-OsotHv?Zm(Q8eV{r(~v8;?@x5viLPn xgCxj?;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq=1STJYD@<);T3K0RUkm_`3iA literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sk.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sk.png new file mode 100644 index 0000000000000000000000000000000000000000..8d860bced32dcbba14891331015811737596ca0c GIT binary patch literal 1422 zcmV;91#$X`P)001}$1^@s6wfF^v000G5NklFv-@EU<_&#$E zsF%%`gG!)csaPtOilt(ySSo>vrDCaADwc|+V%hx8-d?zS7iwz$-*k761FZITFsU3+>9A;{`@$=YHL#vgs`5lFlcCiwl*b+iAYCB>*aJhIDUMB zVR?B$Rh3fCVnME6ZCkksiRIz~xw%TY$B&VV7mp9Dqep+kTF#!W;pv^0p|R%7QN|p- z9;wqIO(w|6QNE$0L&4JNlo*XjKR=|a>!4UN`#5D~t=Lkpv5tdQ8;`ZjoS90))tVu( zNKAzCa>&kxtSsf;+WK!d{1BXxBt_ugIwMoQ@EfoIQ@SS-poHc_!; zWF&I!T4ZprlCUr+Emg3(yOCSAAXltV5*Z0eNl1VH35KQBBBxD5y0|FosQ$RRLQ#=| z_2>~YFc7KH3`DO-PMU;tcAh|3uCB_Ueexuvrw`=t;ei3Zrw1}KAuVkn`}V=PbIOij z!eC9F48_GGTeMg#$W^OUEEVf|9diEsm$7nk;QDnaC>Tg#A#`*MoK}>Sy!^bf61j5a zi=XG@ggkZ1zOlT$zon+41sM_Xa!c^@L%O*k4Tgbuc)-Pr3f7Y+$gr>%pBs(JivPc> zcz1VtuU=#0#xwSdWi;+0FSib9HY>B4wLPB6q_m#?r9()FZF|Fc^XRH=Bs4VLez7!~ zIFgbsA#-z)CX-EAmb?PGy8h(pgGclRFS89RDvH|b`}p{z*e{k4W+Ebf=8w7tMj759+Q z(MXMkJ|Soq21mGF@@*i zpF_b}vv_{1mUZh+zL8MF!qRHvNlCekEG*(LHxJH;*(?+5aTnW|EWY9$@fo3F9WG+W z;9XgmStvFVBtFJd#4uHS$!rnLP7%sujRCoJD>tj|J`c^-Nx5DX;dnp)5T-nkxxZ=D}H`y zZ!DA$W*oHQ8-|lPvr;L$(L!4CMGP*lHxO$yX2QdNqQ17BPu8D6uZd;2^%=`JWQutV zCr32_)U4V-cYPC|ZaRftA3tKKMGJqRvZR^L?+$RZ^W7W@ISa9y@?~J*@r~78!}9w4A&%)r5r{#Mx=^ZmD&QBfx(z2lic|tF8$d z8;hJe)o!3(A4>>jujucOJa~}yTTT4VIty1UyQ(&%IL$7nW`x%a>^9D>~3R^H4dXG^eP|(`b#WBR=cyfUvn}tD-mUO5Y$5cU1mQF?{o`pvm8Uj-npIWil zr&D6Vfdn5Og9Hy{HWjEUMuvk6<^_Iwzc&PEjB1H%L`h0wNvc(HQ7VvPFfuSS(KWEp zHLwgZFtRc+vNABxH825^^`3jRP&DM`r(~v8;?}??!a5D8K@wy`aDG}zd16s2LwR|* gUS?i)adKios$PCk`s{Z$Qb0uvp00i_>zopr0BC-5)&Kwi literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sm.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sm.png new file mode 100644 index 0000000000000000000000000000000000000000..06a6bda12c0c2591637bd3e45784e4e8d4ab61be GIT binary patch literal 2843 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sn.png new file mode 100644 index 0000000000000000000000000000000000000000..8b813b3a0ad303bd3da83a19cfa77dc8e4f67304 GIT binary patch literal 694 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 z zj)XnX5#p4Z#YhZ6QZP;jgHnv-AW$!2`Mvd`dbc4yJKaOV+I|-X#2FmaYcG#Ux~Hs* zJscIw4vo{JKIIftx|~bbkV{*1+0UDj1sSX@Woh#@q~T0MYUtq>PKC^!XJJ&x_Hwq+ z_leDwM7bRCxIJ|4p40U4GTz$me;9RhHtrmmnC-OBDmyCt#|>L)ZT*K@3{0LJYBB1l zzS)j?mw3A{ZPf4Ym>MHjSO+=9O6%rOTTqhDZwALIW!=UeJBaNql75TFa>mY{;-OG|2kfEma|^NaAU6quX0j z;|^HgBAg!^D(A+pD==FBhMQo&yQpjKOwY@HJ7wok;-buWxmclhN7Oy&Gcjnn(3fyq z_|p6Ph9@KGW|fhri+sTAn@T8Oe7$Fz&I@Vw!_>xS;Ud3%p{f_*-?cyKAHUpiztMPo zjDv5{sby0)7Z=ak)wP?BEiFx|7P97CZO=9cBDq&XZRm4GRjw~M)WwrsZXGw`6A7n% zS*C3t{z$Xeez;*aN${ts*wT07#JWzYdu)j$<*Ks5iuqM$b7&)rN!%{WFE6lPH{HXF zKD(IHWUdOI%I+6AluESXyvzjM1CCDdfvU8w)EccbY5qLPP;%PAEsyv!Ilb@0ZTIr; zd9NrMI{c3rjxlk}lv3r^99eX&qAh`5*4KE=WcY-{x~hVGax}%!VkWn*-+6=cO~!wqq7^>? literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sv.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/sv.png new file mode 100644 index 0000000000000000000000000000000000000000..d6f48971d8b7860e43bebeab76a50e4d79199e17 GIT binary patch literal 2893 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/td.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/td.png new file mode 100644 index 0000000000000000000000000000000000000000..991f9b1e04adbe395e4db7bcd2289876f80e9a22 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIqF39(VPs z;Q#*&|F1Iq7iahnlwx4mRlNTikYY^oc6VVHOYC?JvL>2=ba~*^koit`w00lKY zT^vI^jwdHLFgYFLND!LV#=yz5_>|k!UM=ZRvsF1EH&<*5OuZ7u#vtdjJff;|-fEyG z)e_f;l9a@fRIB8oR3OD*WMF8bYha;kU>RayWMyJxWnijnU}9xppj$Yp7ezyEeoAIq zC2kEdI$DN64U!-mg7ec#$`gxH8OqDc^)mCai<1)zQuXqS(r3T3kpe1W@O1TaS?83{ F1OVyEWI6x< literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tf.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tf.png new file mode 100644 index 0000000000000000000000000000000000000000..88df1bf7b635e11cda8fec730711f5da32ae6c35 GIT binary patch literal 1084 zcmV-C1jGA@P)001}$0{{R3f+qF10005SP)t-s03($k zKce5{^8f$;{p9BFL{jZ3Iem7q0VI_nKcnwOQtc`_bB4u$k;=`~>e}D%+0^FLld98+ zplo)y8#xZ%K3T82!{SwJxF|)Y1Sgl+-S4@>;)a*a3M-j5 zQmn$t<@@{n+u-n5Zn^dL`&@Fn0VI}wkjd@u_p!X+3M`ouG@a}2_ocDfFifdxf5Adr zuyBOL7B-%AhsC6@*$ge3fs)D{JfR6InFS}8C`F~&-tY4C`crGT0V9Fo9s zGo8D{GSmZuDRWtsMQWHo1CiF116R@RIKRh^_!{H3oV*MU9srv z_8K{$L0hn_x7;d6rW7=t*4*y*`24}j#gE}FEy;P&_XfRf4sC61R9M69*4J0s zU>L{oU;5Vet)-=QNHHKNnzzP9Z4s<-&{joT7ZUg03kQO@H|{dCC6szqP=>pS9+83 zRw?COOxF8<_5qKfRKv#y8B(=a#x(vxX+|xU$;^}cc(X}0luiN8VzpW`GBY!*R;z^r zXu2xOmX)nM35?~XC63xxaFDSGt7WrIM^yMq;g$4O}xin_WQS5Y;+*^cv zE2XfPxSYj~m@PbOV(Y~hTYLgzg0IEf+=NCE4K|fyA-6i)+B*csgpT$$XREAf?mN4>dwO+@ z>3Vy*yE?VZeP928dC)sF%-HadchEf0->2Rh8D-uvo*ySi-atO06-RlT5d83h+)RU zA`sO0VO?650OQJv5s;RbG#zSnZGB^tis7(GH#gSTRy7<=Hf(M0>{9IQf$r{XZyB@< zeq{gP@CfkvfTP2M{fL&A^c001}$0{{R3f+qF10004HP)t-s)&c;O zFaQ{Z02PM-*Z=^OE&vpS01<`&-XGz2r+v?1O{+?d|r*$KpgpwSIN4i56EspV5s-6JFV-rm^-1?E~>{`vX)<>l;zgz=`P z-6A5{1_tq>qW=5)^}fF1KR@VfZ025G?TCo|0N^t-<4a5F zZEcq?03C+_8HoVd8UUU{05FdLE|CEC!~pKf0N~aD-`4=W$p8QV0HQ+YsQ>^07fD1x zR9M4fWFQ0>85tRf1B@)AW{sLfz$_NV-_$aTjgj#OwanrKYW;#D!b`SU0zk9gqKJr) zYnC|BD8{EKLNcVA#U}Yon(-4q<2PD+0>es^@$pxPc3|A?VW7k;1{X%g%OF~hk#RdkVGnkh@p-UWEL#~UHp_?c41@HC zuMCC@ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/th.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/th.png new file mode 100644 index 0000000000000000000000000000000000000000..03fd23b0efe0282e432ed0668547f3f67bde3a20 GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^-au@@!VDzcuSskJQtAOdA+FCE8J{ySd`L|E|M&0z z|Nn2@`yg$+ltFq{Lg9hGfB$`7zWg~eNXz4kouxo3(9^{+#N&8!f&!NTm(b!@_QHkB zdSy~|Tv~72KbrDa)$iu~iw|5`_jd+=>$XU>cNhM-V$;FtC+Y>3zp-9?IzzH4YU^4h lx2xulnx^M&S)}4G$sk_D`#~gm@f)Dw44$rjF6*2UngCw6N|pcs literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tj.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tj.png new file mode 100644 index 0000000000000000000000000000000000000000..5c3b062de10fcb5c0c721b1bc0cdeaa942d78efe GIT binary patch literal 986 zcmWlY4O0^Y0EQO`b#g$R9IWz42#~Lg`@4 zWfLrj9vLLCIbhcE~oa0_Z-82aE{$bnQyhD=aFE}VxDaD(Sz3lu^Dq=N_CfLZvmXJ)|1 zAbIlB2wy|AMeF+pnTtj1V4OHome!Z85Zcr$oGPrWiYwnKey`kuUf3IT*f(o9OPPJG zP9yivkI+=@uiaat*VlaD-nMA7tW>0vYb9d6{!G88huk|LAwE62xa150aHH@6Lin$0n$bzNia zFZU~>jvreyrAoTpI{JxIuT{Kr%gUt{RbE?Pm!>;DVkc~rItC;*xGHqfVFBAFn&kB= fd-t}+7aawH&}%Llue`nYpDT!yB#S?bQfU4MapYfP literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tk.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tk.png new file mode 100644 index 0000000000000000000000000000000000000000..d6f69805e30128456eac24341385f84de6e35cc6 GIT binary patch literal 2043 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 Z=l_%8+Kis9lsW(a002ovPDHLkV1gI>BEJ9t literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tm.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tm.png new file mode 100644 index 0000000000000000000000000000000000000000..bf7186fd1aea4077c7d52265bf2cadb3f81fa02d GIT binary patch literal 3089 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tn.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tn.png new file mode 100644 index 0000000000000000000000000000000000000000..5b83512b684fbfc959287055b70389436742f161 GIT binary patch literal 1354 zcmV-Q1-1H#P)001}$0{{R3f+qF10007xP)t-s=KvGu z0u<;CAL<@2>n%L&GeYYwKI$Pc=no<10u|^EAnig>^of%Bzr_69-~H+JmB z;rhkN^^=?KOj+p^CFcPY=m{L|PFwi4y#Da<|Nj2}|Ns8<_4&ic@Lp>5mY)9g_V=l< z>nl6!EIj$W!vFmI`p?qsK27T^ey4 z6D9YqwEW}c^oo=0H%93ZBj*Jd=K~h$7AWmQQS^_P`o_xYATjcEe*XFS|NQ;=yus`^ zN9P0==K&My8!Y|n?f&@r|NZ^-lA7oT8tW)H{M+96vbX9VFzq`^`NPQn`}^~Khx^de z{`U9ywYv75rRWA2{`U6pU1{kRCi%L*{_*neOj_zCHS9S^`o_!fWN`iM?(8x_{Nm*L z%+UV#`2P9&?^R;(V{Y?@Yv|n4t8AkMo6&?^9p=LfJh0~PE#N%3xZ{`mOqKu+lyD)DG^{pIKMjFjjG8R-xr_ouG?=;`f0PU#dT z^^uwRz{TnvF8}@h`_a_+w7U4UyZh7D>@-9A$jtPOmG`Z){p{}fy1?&MVdx7U=nEbA zw!8F)knKWI=nx_32^;AcDeERS=^85l001z-foK2#0(VJ7K~z}7V_+BsqhJ(_Ou)#* z%)-jX&cR8sNnG4KynOrufp9|)5X=zox#J?$-&DT8V5eUek7aa9{|!A7{tIJ9vl)X;{y*fK}}MP;tT`n4%Y%Q zEsbSmBO;yQ;U5)EqFG|PKy5Lxz<`d62Znk=VkA7B2_=zeRx&84Q^bHiO!Wq`($d{B zWSuiIvw-aE91_jS1$t2?59qA?0+3q@Rcwkxic3mEL7XxY%_;{upaK*Jm9nx~Rn;{N zoSZhbbzo1{H;`nMcq33xQ!|j&A}80{*4_aU?96}!SQlxjLcAMfRwt0v6Vuz*@0Je| zoG=k;)+EyW*bme*c?ys*RS=Yorh!U`=`$d4*=j?YSrdTS+Ng{Xm?Sbm+BIf&Oqey> zZ4TJ1xg>_Yr2tTq?mS?ko$mr-El^mPwWxbBC;@x7k!aQe2cWKrOMr=DX*iG-vP=k+ zp=R}Z(v~9Xs10{2A+dt3pbHm zPH=AC0xBp%wnj6EZ`&TfV``MlV-8 z4Aw1un+a literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/to.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/to.png new file mode 100644 index 0000000000000000000000000000000000000000..3b861fc9cd423d80bc9a0f300283fcb8bac0c417 GIT binary patch literal 391 zcmV;20eJq2P)001}$0{{R3f+qF10001@P)t-s|NsB~ z`ufyRP{9BI{r2|ku(0T&qVK-G{{8*==H|;PD#8N;{rUO%<>ksJC)7?({rLF)`T6FU znBH}D+iGgX5)#TJB;9gy?6R`|{{H;+_0d2;+;DLH_4U+GPsS4y#Ssz01_s}HdjI|X z_T1d`(9rI?yU8FR&_6%&%F6lV-p&D`|t1l_xIRgVc1_^$Q~X5 z002Ghr`-Sm0H8@kK~z}7?bOK*!XOldVcIGpIMmrXRqH(B^#0H0W$S{5h8Q*HU)`L= zw<0iRDF`E%*bi8xGUvP^yjWGKlnQHJta^inW(+IWYPCC5w?{*NV9sGu*cy%B=E?tA z(^*Q*6%7k>rCD)nxmr`3Ee*T9IS+WNpS6xBDdjovVu_2^`kH*lA1y519Id;h$J5P< l`~?dZELgB$!GZ-VzZXDDJ7RQ3QicEk002ovPDHLkV1l_v%Ao)N literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tr.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tr.png new file mode 100644 index 0000000000000000000000000000000000000000..31f5edff17e15b871299d75689ab9ed7e31fc935 GIT binary patch literal 1244 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 N002ovPDHLkV1f{n4F&)J literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tv.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tv.png new file mode 100644 index 0000000000000000000000000000000000000000..131956f6d4a0ad2a436c0e9c633567bf59d32ede GIT binary patch literal 3300 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 M07*qoM6N<$g0^=|Q~&?~ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tz.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/tz.png new file mode 100644 index 0000000000000000000000000000000000000000..73180f0004b7a45fcb3f66d8948cf11eece44626 GIT binary patch literal 1997 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 b);jeK#DPknl{GW^00000NkvXXu0mjfVr$}- literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/um.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/um.png new file mode 100644 index 0000000000000000000000000000000000000000..e092541afdc5037ccaa1611e22ba3fd31742f5e7 GIT binary patch literal 2400 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 SD*V6z0000001}$1^@s6wfF^v000KiNkl)!G zBsclY@0|Jm&N&70(efzd3UXQhe_2@?5&LzrES-KY@G4|!l*>AQ1XeH3@Ma=$dP-iF zpq|=6>?`L84Uo;hA(X$8W%0XLmTw$Hj+F4mx0A*WX>8LNyo12%`_FgG!-(Ndyy|W^ zrvhiK*bK5PYkLZ_PrDGvreDW_il}VDuXGaFy~M!?>WS+R^*v`kSMVmb5sa0P+8Z;b z@5RLVVZx7h^>E)~``eybWM#O`!#IuC$r2xcG?9qfR>FD-slP@b_M;D7hBLgGIH)7) zPZQS5i1AItassjx-#LLXv6nOnF8CD3i@161?%v0lEenC_8j$ruj5?H8s##X+D+W#TE$=}Vo zcFk#7LVXQ)Y&+iMi}>b2(%8j!|BN|t6~>YGqji50QU4a9eLt?b3Tw0j`yVUto8QJa z?!&Bq6G!_ndf#%KQ(a_faQ0Uq2@R64NtPw^LsnoP#~XPJf3$=@-ibH4gV6gIp1B1_ zS&n(6h;`@+jFHb^s+VF^ix`7TF)BCV=pVr~i zPZ8_T<+!!AgjR`2FA*va5LQcwu`Y!2DoJEcKTi5Fr6=Fw3wQ5rdkPoNJ_&(+1lf3$ z$Sf1Pok;UeOyx@aa3K+e{~}BmV2&)qseFl8mI%y;5Xx3WV;4f*OC&~Sr2pw^4ikhQ zw>^c6#fB^mrgD?=GRDx|*h8PgJbnYVvhsqW&LU(7rutFr=4!0QI;69g$R8w$nrGIp zYr|AKbvakRm2p!eN~-hBJ zjFDAnCqIQcbQjH$9SHNCmeYOP%1UAvX&uLHzJjAXjn()*wz>veyAH=#iES*!^A^u4 z%MTZ#AA1khftBb7uEywDgFUnXcjA{w?M(uAm?X02Q*W4 Xx#kae08r$)jcj6hh zW7IxKm|a|!YcIp9-HLDBg*4XTsZSBf(fQd(XNixmzlArk6Z0(f7|LZyv3+Ju|0a2tiokn=NS;PCo<&x> zkd;o{%68H-jrqK4q|a`zIHTLp`rn7$ya`v^jE*q4eK?tv*N;3?uYCL~fbTE2AI( z%G9*8ZpPDIAQB}4r<1_xMl}9Fv;VW`W9vy1cYZd~=Z;W}Qy<)bF#bfA`ULhN{N@HC ztBlZoj#v8?M&Bj4#=S&x6yJD+z$_DbeLzMeD>P3%fl+Lo*<3AckfYH8aqUC{Je_EuqOABMAkX` zVFO70?{l*E`=6gkpML_BAF}3SN>;ro$Z3tj$G-dmIjvEc6JfAj$R*^ma#^{oxx`PN z?001}$0{{R3f+qF10004lP)t-sl&sX2 zuGN~c)|#=`m#)>GwAj4MKwb_-d)T_ST)7|Z`!rzy#)wsyx z&)V#!y4#ts*3#YWs=eKot<|Wz-0<`HsJz_0%;oj?`^D1e)ZXs#_4=>D-a0(`n1L2tiRr*x!a_;+Vb`K(Aw;^#o_w<{jtN~qPN`uwT9+^M|Xo3hu$(&xm|=)}?I&e`nY>GS99_3Q8V{{R2T z)#<^{=IHMBvBKcM&*tRn^y%*Q)!y#?{r>Uv`Q7C4$JFVrz~7*@*`Kx8rMlafqQkLB zDB`V~@Y&C^MiVQI005}~0002*X-qW$00Fs4L_t(o!|jvnQxZ`a#-CkI7{v;&hP)x{ zw%`(^n2HKUxkTm#%`(lTG%KxC>|z(Zbn##JgX0+WT=Sp*E3mJ*NtwXP>=cNu({rwTDq@)ARieVVo8O2=ckUE2V7rh&u`nV#)~yoJ z?W~$Nyc4ETw>dJ)4B##UA^{i`Y1h3m>UEkDaesUwDcs4asxQ}4Ovff&P15Bbqv@XJ z+Gk1+44&cCHR;FmgZs1DENxjdKW`j@UF XT8Oq=ZXpq?00000NkvXXu0mjfPO>Z! literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/va.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/va.png new file mode 100644 index 0000000000000000000000000000000000000000..48eae4583377602246f06fe9898ad7cec792f1b3 GIT binary patch literal 2127 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 DfLoer literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vi.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vi.png new file mode 100644 index 0000000000000000000000000000000000000000..e7eb8f7fb8137ca61a8a7088b71291daa0ee90db GIT binary patch literal 4928 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vu.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/vu.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d648ffab499154f4cdae02085b2a137c72f140 GIT binary patch literal 1535 zcmV001}$1^@s6wfF^v000HXNklZ!y?IhxL^TjQNW1+e*kzr(qRE$ zObj?{7I@|i`2M|Xl=bw0`}c#HncxT=I3M6ifNhZsOG|t_xMmG__b%vk3NWV=ymJR! zxe^>{1#JMo2Y5NsVF92{2WDk~`}TpaUb#kDZ!dW4m}rJs50(Ku0`RX$hNVSOz&Ufk z3m3o-9|V{i&ER(c|A=&0TE>h4H*N$UJ#sAoZZw1Y0p5soSOBQkgHxt}hYy3VUwhOH zhO=h)1WQY7EVy_vc>Oxq-|ty7{)}{3+5(W83U1p5K6~bxnms*WLqm9LhTpKX7>(fc z>ENkT;M=#6p&4HdOUuZS0;jzlbU4CWGlmRHo0{qA;Eo;O%a`G+83BQ%#bg4@%S9{t z`rK$33$c0g z&{RX)(ti3>fH@q}bN2QIPu#**Zh9=_Do-ICZKN{K=ES zLx;e#XTPjtfhh1XCLIlmfd+H%WKhqFi<6Pn)dgO<1n$`b?%WBsw1}t{70KRwXXiiE z1Evn91QRDLoxpU(U>hdS|?bUyCIilN6Ug5 z(y+qvxcQd~##oIUKW{u|G|XVdsfv&Q6>wPIwv|naj`2D1%&W=c?BlaT22wy_`EtX; zlwe}|vUHX-mxLUofWh*1D=gS`;D3ST?Ix?H1KHKtL9GMB3d@(<_o1W%!v@RS!H`hY zfq=vEdgSPH9XJqLIuKA;-VU>cx();cmM_O7l`znOA;I$H0B$(xz*ocacCl4QCzj3zoY_=sxLynpcyjhPw{<1N zsOihooh8jBY824FU}=y28Wp{oI4#jxSYPOjBpvVzmZE@`aTaIx>TE~p&!y4PD$?_Gl4YFd7Z%YJ9%_}<>{y@THe_t0Mlj{bS@=&hF3r_7SSFJ1{5&o%H_ z`S9uIzt3KL2#kAY;r=PB^izEHhoJa(mL6|)tiO~u{(Jx7MDL1olU9G;wCm^gJwG?? z_%?a=*S=}rrq2Bk5c{ov#`lFQ-kCVR)wBKDKH=Y&uWxm1zD=3)@B5E$6KB3NuzzRh z@NN41e?NZy`~2lgWA|HK+jpieKbEik`}W=EqT09G*6*#ozBcvzefs=UO8%ca_kh09 zD|wg&q!^RD-CdjnuG@YAaySb-B8!2ccO8Tooit`w00qB$x;TbJ9DaM{IA4>4Ktp0l z$(tM1?^#)-Z`}U9e~RjEc7w*8Y0d3Fclzy1xw%MVPk=X3CCR+%0z*xPtE?6>vhJkyPV<&m;+ z+pnkdwkcn$cqB3J+_N1QzL+n~=ALtYnO^qGnZ>egJEI<_?QXtX*8F;E<_?{SzUBuu zeXp>Xb)5NKF8321i@Ew0dt*Nq*)^_V{oda7{(T{PCHK7fe`nY{JimtFy2OM1{2zQ+ zD#HH0o6zyJe7md8eslLTpX#`e>by6PI`gRqPsvQH#I0eT&cA6u x4U!-mg7ec#$`gxH8OqDc^)mCai<1)zQuXqS(r3T3kpe1W@O1TaS?83{1OSFAPF4T_ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ws.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ws.png new file mode 100644 index 0000000000000000000000000000000000000000..04adf0673664e6a029a33cf6d2f06bfaf2638be5 GIT binary patch literal 512 zcmV+b0{{JqP)001}$0{{R3f+qF10002$P)t-s003(@ z095b*2LAv69v*xL2X8huilU;)R8*W25_Sj(Z+?EX`1t$0yx1Kbd`L)@eSNe91Z@HW zYykml4i0k-4RR0=bUQnZE-r=x18oHbZXX|geSNbaAbtS>Y)40w%*^4wzS%W3ikFwe z@bLDTnZy$lcCW9|5fOD{WTnEw+h}N~BqV?)CV>bDa2p$YA|ii5L6LB9tQ;JBIy#Ln zFNQ5Gg{rE}Mn;nu7+=V&ItE># zFaTqMmKsq3qO!&y9EzREl3pxX&cd5e`)*sSFZ~gW4+n#kUHx7!%_RyI(_7P zx?DcbuAJ75BlicNCE001}$0{{R3f+qF10007TP)t-sBub?w zOr|AErB`&iH(9MGO{PI&u_sKXEm5hhyWL@ZzB^s7p0C#}QmHUhs)n1*x5MG@^7+!* z?WnigSarH4OQtGXe<%4C1PG*_!7N~J?&vcb&e|NsB=_WFI4$}CW* zTzR}DOQtngteC3R^Y!}b?e>9}%P38zskq$Q;PBt&@>X-YN@}&Z#NsqqtoHZ&z{}+` zSF1o_uw#C|Wq`m+X|$MMQI>~oFHoO3RhpWKYtN=ou9RV7YlksWo-R+ICrqPCUy{L|SJ0+V$DvfK zl46~UX@q`tN?wv=Y=z3BQ+s%NNL`bdh-{{hWhzagFHoRuae=;^S00S5{?= zx0qeBmS35PYov~5VQPmqQ=DaOg}t0v&!temomfp^kv>?MLtB=jjc2=?TDO^8pp0lS zQJ*qVo~@H%dU$%LkYvW8RbXm|RAh~bfO9QRpiyFv#GqEGkz*@PqL+wld3bxxq*BYH zQ?Hd^x|&-%R+=YEq(NGikArZ)o>;M#Ut(*9V{C?~kz`?Ohbc{?je>Eck7kmEZ^)ul zqmE`cRhw63jLoD`xtUyufO9QRp_YeiG*g|5fO9=pnZ%z~MO&4|pjA0ln*aa+kVzIN z0008HNkl?eLY|V zB^KaMgju1C3}ML5508k9ijF}Gh%g4mP%_eNY+QVNLSj;K3N9S&*&Lntw-wyqvwR(fSa zV-uMM73Va!)R!Zfm0sRjK!!PO?HwVVT_^^DKxPsdW_9=U_NAp`fc^<}6J<#?YtrQM zbSz*>#nk?3)2EYWT*!=>>Da-PS+nQNCCy>;=BMKVv#r~<%aWVDcc2(nzH?Idu3B;no!xt2#_Zjv z@rvz~6|5U)0ZnRMS_RBD-3uwKicZw0m!CWZE}Z@5wozu5Y-#!F0tPC9GihfVsbmz> wjtokJ9Fdb2RL-fQmLHSO&1Dz`qkudB02$U4#1|fuN&o-=07*qoM6N<$f~LcN-v9sr literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ye.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/ye.png new file mode 100644 index 0000000000000000000000000000000000000000..5325964fe71d538575a756eba93d630a16f273ba GIT binary patch literal 375 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/za.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/za.png new file mode 100644 index 0000000000000000000000000000000000000000..e329e2e186fe639890ebafbe931c805c6ddc90ef GIT binary patch literal 2027 zcmV001}$1^@s6wfF^v000NFNkl-U#`JQpCM`1>Sh0z<>aOg!pUtP(x%9 zIM7VUUcjFzONDRGHZaxJ#UC#FtExgwNkS!i1Nyqb1|mul7c3RTh{3Bphz21`sC7)~@YS=C|uC z`73Y?!P{>lus@AJkC(Fcia@@(UCWcthX#z zt{PAHsz(W}H&Iq8l|3kxg=kur1!u*I_=2aw-dGFFTUk>R;+i#x)28`vSr?QEr7S<) zsH{yY+a#4mXi}#BNBHh|p8Ahnw6*(;QCUd|;-W=56WnEPEBf?*JjL`2)l$A?mOQf=3ZOZ0}2M?@* zs-`a(H!2gjem%n#D+DGd|KBs9vU-&T5;5;)scf@RSxfm?zD_E;cE`<}Xi;_>M(#=A zTk7^gd(Th16F9Ji0QFMVD3!e~m0fLR zWjf1pSN3W@%089K7D#1dt*C4`%5o|D#T)`N`&&}2)bmJxp%{eu5N z(~FN0_|8OAiCm!Gt8sy9*|LOP>Y-5tD#Q}k6XxvUG98c?54 zxbQlnu@BEdAgyZp0xWezqcqK(MOfY@!mgf`hB%7i}f_CjDu;f{14yCh+ZkPfhB=wQ6`STxs8htEZ-x7U1jG zpQhpAwLBoeS0(jRmeO?FPYKJ}AT@nvxXKq9g_Ct(<+I+a{O)#xrtC^o|lUG?v)2%-tY+tHT zlNvvIQ);?y7{-s9yvC1|CWqr3BqV$ZAtC;v$t`N#^mHdrY!U zYI^v?C>W=4826X`4CA)9GqrA_GiLa)CbP26I>v|{2PVP|Z_m;^=AEg=yqWP`R3|K3 z_7#MN9u_#JHEAg8!V_TX{RCKPs;%u}$;n?s zSlCF=G=#Fo*kbUOj~;=2#}7(PJqArSTN9l*b0ldRWLe|0@8VCDzCn+7oRXS^ho)o4 zy4XVxeKP_z4Wg`RyBD+Uf93Xc!Fl1m`OV2`qqAn!jZjTLT$nq+*I7FP`2>0&ZjbZ~wi)h8rRZ<3GoX z!UdLvoELUr(rN0H5=CTziZ4(HW*k|e)%mH;UgYYNwvT;yUOqn0b5B!#?&c7Agd7OT z+LF25ui1XQ85H1WY01oGnAMh_vtyI;Fv=!k-9$7IQO}wRiY-_jtaK405ihaK#foa8 zJ;Zo;%gNhiWH?xSkbOn8>$zgUXUAjZupYt5iaH`2$UH!AIlUqkBFkz>bhDwFofZlm zd|>93hO+_=Ez9;3SIhn)dQ}YSxM^V0hEL#XDW8k9+ORt*sN#g3rw6%iC*97<1H4|x z>RL*iw3KmLWWd0np1Wqo%-l0kr6yY9H5XCMno=Y$*drvk+ceqs2LO(F>ro2H}u>Pcu>v{74)eX zDCNsNoZHRWcNsM??cir86IOgiEG8BmBDtCp6UR#F6B#vgRn4%#BL}}$@=GOyT5gGa zX2NYHtBOVSL^hHjvC)Rpz(*Q-RD7XkNXM9oyGFWp(zA>1BHD~>v$I5EaV@JV$+6)z zFk$18iE%5}G@LJ{dl#PFWLSvxkSpR9&|4TbAr(`jLu(|>L24OIC8V3^*h9KVl!vGW zl4_{2FkqoY;Ec#dBk>0*7RlDLv5dqTB0NO8QTn4Ek|j#ANEffvqHvMWz|tlX+_H|$ zd3~uN7gB;(#s|%=J5V4VS7Evr___b4L5LzQ#b`P|%#|7XeeFQO(Vr>L-89+LZCW@Zo~E;I+C| zS;2gTqI>F*6o35G&Ble-UXC44no}A7W1jA0^EU@p#MgZr7B;5Yf^kjh-G8^9X!~=m zJGAxhT!r9V9d{?WP(Ja`JI}6%wO*a+Jih0*`X{o@G5sBNZO`_O&#~P~e?07LE?d+t zrCIWnirdNag1XNrmDSm*kNYP=@-*5{zxTY+HS}Utz9Bqd*w|R`{B~An&V{+=qm#9V zf)-8*UhlCB7cV|Pox5(j|2B*u*PXQOXyJ&r-K7nmw^XocQkU2L yweLn?m(bFYYW%LJ@PB4}GAZ0X^VLI7?PCq=^hsyb-u0>e0m<5wn|W@7O8O7&;a8sk literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/zw.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxhdpi/zw.png new file mode 100644 index 0000000000000000000000000000000000000000..9cf6b5eaa8714eb3f587ac614b19745435438d20 GIT binary patch literal 2165 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 C9@_2z literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_error.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_error.png new file mode 100644 index 0000000000000000000000000000000000000000..fd994a23eeb57a81ea6def8f5a642ad3d82f2f84 GIT binary patch literal 2651 zcmbVOdpJ~iADUgsl+Bcp%bO!i@j}L9QTn z>kV)j3^pxV#0gP_u-DM|G6|MDse@HZsJ0%B6bb0) zU@{xemV1GbBA++~7!>En;m1YssRFdea^x~K4Kg4BRa~T6B9<=p5Elq4W!n^ds2)OxDKM0~e|9vf2(h90D@0IHxMHS^zfA&4RlZC* z8hV2jhy*mAyBnVe3P>0pneUF_yOGEkE>TFuxVeD<8Nl8B^qHl0;6Mz&Rl}CgE zCh;%=0f~>H;(5@6LKcz)?tB8DKtg}P3;y5y;UH4D$!7Vl=9xNzI&xC{IttL{>o9>* zXo3{bsHD0LU4g-jxBD_zaMT?G8=@lZS+o6g7gEOX<2xxN2V6Zb}{Z~yI$69b#Y+{dw8YTj1(H5IbwEe z?}+=-rRPp|)#XlGu;Fg?lizPuz8uUU)^^^Q_YB$8OiPBLH8YS721zi&w}r8tR&q@E z0>r_*QG=qdS{=9NsK+Z!vW@VjWwWG1g*c_rMgj zFw!jG=g4~V^G+?JS0lLXFFb83S;-)3f*wJf+)T=Vt1tR7<@go!oH;SL~?^#H(rn2+L+Lhpn@HiS%naCE=PhwQ&Dr~3*&Q9fgtlL5Gkmh@U?aR4wk|UncJyqv zMSR}lfgJ0PTl*iZyU`5j8`>^xx4!VfKs^1vvHJxx!z!~PQxSGYSn9YqBW)XedHc0- zTX~?^z%PkYcK}6yVvyh+T;Ymy3F`UFK5RGEFn%%ZdoL|-l|`kb4Y+K6w=lVB$TX$k z_4u1-n$_iKP2M94`t2Q`z}Z1@Rimq~%hn{<+PS$ZbFj}Nl`YCqz2@lQtVQO@R$FZ}LZpE%ZG_?+--WiXpD}#a>3NFr zHeGXT=#?DJ&0}d|g`H+gS{w1y@Tl{uMf=HHV~^FCy&GSAdT=}Xu)?X^JaN~&Usq@u z5Y^fMA*ndI&gk7GV@!`=?7Q}GMohxvz*SCJ%u+JEdA*sB ztbn(%gclY__}9dTg5 zqEN(e(nHUI^bp63U3%XuaW}48E~)8Xh$ucY7nbJqOT)ELgkITEw=c0fZkiOo^JQ!Y zx4x=1_i&)9{p2$myR+CD7z&oWD4|uZ`7sJrP~3QB{Z$9+9qsvj``Z6-r?sR&mx$_gmBv zRt9e9{^X1B%@#gIL*5gkxva{;nUV(tRQK|Jizo+Qf8wDNIo+-)`JI{hebLpzie+0E zu$`qn7HgNLJJO3D?-4F^e&JkGcckTY6_2>mS8t%doOImP?QBS1<{hJ&{7zS0QAO@? zn(kii0HMpGiq__ovf6&pk=ca}alWk|V%cM;-nSg)k>jq3u70fBqZy+Gx+j--LQLxb z`c}%l3}<*n8}TW^rFV>{+wrI}NGrYThVOkHvI;T%+Cs9KulgLfbx_W@?+`hJa))CN z1>iCsJ*~q>oFCToSH$mwXOdhCi*ZOe*5x_E!kK`n z**d13cqtDP8a{HQ19KDRxy(wDdYs=`-9ou6Pl=su3f%5vW zn+GSq7=}sjfETeE@a?_>{_LWRk?z_}v&re|KT(@;#Ql}en%);)L%%8`|2ulja{VKV z%IIln*1yP_;8!)NnwW-4pU$UE)zdTydMhKxa3Fm75blXkN7ttdUl r3QjU%!VsR`hL&)_{|bl!8Qd5)oMD6z^4HFt{N3|q`7wWDgzx?v;(0qv literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_filled.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..b7bcc6b595bf687deb8439d94196fbf698338c47 GIT binary patch literal 3543 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_lines.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable-xxxhdpi/ic_lines.png new file mode 100644 index 0000000000000000000000000000000000000000..2b8a9282a1f0bb9b8b306026b54263e8a9c8f4dd GIT binary patch literal 3787 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 XnZ*GMlu!Nk&+*&ZhFX<39UuJ{fj>mJ literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_background_pattern_tiling.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_background_pattern_tiling.xml new file mode 100644 index 0000000000..4708111a52 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_background_pattern_tiling.xml @@ -0,0 +1,4 @@ + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_clip_area_left.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_clip_area_left.xml new file mode 100644 index 0000000000..e9d2a8c109 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_clip_area_left.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_clip_area_right.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_clip_area_right.xml new file mode 100644 index 0000000000..ff8e69ee69 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_clip_area_right.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_ripple.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_ripple.xml new file mode 100644 index 0000000000..a5a4603ff4 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_ripple.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_1.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_1.xml new file mode 100644 index 0000000000..cd88329f33 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_2.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_2.xml new file mode 100644 index 0000000000..31d96f34f2 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_2.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_3.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_3.xml new file mode 100644 index 0000000000..1f2be538e7 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_3.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_4.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_4.xml new file mode 100644 index 0000000000..3df744babb --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_4.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_5.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_5.xml new file mode 100644 index 0000000000..5ef7f10567 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/box_row_rounded_box_5.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/clear_box_ripple.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/clear_box_ripple.xml new file mode 100644 index 0000000000..cdac0f21d2 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/clear_box_ripple.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/current_server_ripple.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/current_server_ripple.xml new file mode 100644 index 0000000000..85b80d9bff --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/current_server_ripple.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/current_server_rounded_box.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/current_server_rounded_box.xml new file mode 100644 index 0000000000..2ba99e7d1a --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/current_server_rounded_box.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/flag_rounded_box.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/flag_rounded_box.xml new file mode 100644 index 0000000000..4c6bbe75e2 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/flag_rounded_box.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_1.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_1.xml new file mode 100644 index 0000000000..71083003f0 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_1.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_2.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_2.xml new file mode 100644 index 0000000000..26018e3aac --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_2.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_3.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_3.xml new file mode 100644 index 0000000000..e86d1fe085 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_3.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_4.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_4.xml new file mode 100644 index 0000000000..5ef7f10567 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/internal_box_row_rounded_box_4.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/map.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable/map.png new file mode 100644 index 0000000000000000000000000000000000000000..1218ddbc949d6f5de6aca47356991e244decf29e GIT binary patch literal 16857 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_background_pattern_tiling.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_background_pattern_tiling.xml new file mode 100644 index 0000000000..e31574bdfb --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_background_pattern_tiling.xml @@ -0,0 +1,4 @@ + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_primary_background.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_primary_background.xml new file mode 100644 index 0000000000..14d5e6874c --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_primary_background.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_primary_ripple.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_primary_ripple.xml new file mode 100644 index 0000000000..142ef10978 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_primary_ripple.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_secondary_background.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_secondary_background.xml new file mode 100644 index 0000000000..2973e01b92 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_secondary_background.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_secondary_ripple.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_secondary_ripple.xml new file mode 100644 index 0000000000..0eb4edfc30 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_button_secondary_ripple.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_internal_area.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_internal_area.xml new file mode 100644 index 0000000000..4f9212b84b --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/modal_internal_area.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/red_button_pattern_tiling.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/red_button_pattern_tiling.xml new file mode 100644 index 0000000000..4708111a52 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/red_button_pattern_tiling.xml @@ -0,0 +1,4 @@ + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/stop_btn_internal_area.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/stop_btn_internal_area.xml new file mode 100644 index 0000000000..571fe729c2 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/stop_btn_internal_area.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/tablet_tab_border.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/tablet_tab_border.xml new file mode 100644 index 0000000000..2574f6f64b --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/tablet_tab_border.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/time_rounded_box.xml b/cmd/skywirevisormobile/android/app/src/main/res/drawable/time_rounded_box.xml new file mode 100644 index 0000000000..3f4b675875 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/drawable/time_rounded_box.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/drawable/top_bar_shadow.png b/cmd/skywirevisormobile/android/app/src/main/res/drawable/top_bar_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..5706222b35e0616c25e906e67978c60a82b2afed GIT binary patch literal 979 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%0D=4-1II4$y1WQr6D85bgn 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�r00I3o-*5nql4iHu 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

1=DUOAl49ohB{*R4L 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/font/skycoin_font.otf b/cmd/skywirevisormobile/android/app/src/main/res/font/skycoin_font.otf new file mode 100644 index 0000000000000000000000000000000000000000..e3e80f0e48b29fe264995cebf363cf3ab078b911 GIT binary patch literal 67292 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 VXQ$2o*2VwU1$rib31|An{{cZc+K>PM literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/font/skycoin_font_bold.otf b/cmd/skywirevisormobile/android/app/src/main/res/font/skycoin_font_bold.otf new file mode 100644 index 0000000000000000000000000000000000000000..9ef702074416cda7fdce85c237546d0fc865f50c GIT binary patch literal 74840 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 literal 0 HcmV?d00001 diff --git a/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_app_list.xml b/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_app_list.xml new file mode 100644 index 0000000000..7c02a596ca --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_app_list.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_index.xml b/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_index.xml new file mode 100644 index 0000000000..a909194a88 --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_index.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + diff --git a/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_main.xml b/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000..e69786e05c --- /dev/null +++ b/cmd/skywirevisormobile/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,125 @@ + + + + + + + + + + +