From d2409b386dd66a73df7793be19a6149352cc4ae6 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Wed, 13 Mar 2024 09:41:33 -0500 Subject: [PATCH 01/53] change app subcommand names back to full app name --- cmd/skywire-deployment/README.md | 418 +++++++++++------------------- cmd/skywire-deployment/skywire.go | 10 +- cmd/skywire/skywire.go | 10 +- 3 files changed, 164 insertions(+), 274 deletions(-) diff --git a/cmd/skywire-deployment/README.md b/cmd/skywire-deployment/README.md index ea23e0db42..07c976b8f6 100644 --- a/cmd/skywire-deployment/README.md +++ b/cmd/skywire-deployment/README.md @@ -108,12 +108,12 @@ A tree representation of the skywire subcommands │ ├──kg │ ├──lc │ ├──nv - │ ├─┬swe + │ ├─┬se │ │ ├──visor │ │ ├──dmsg │ │ └──setup │ ├──sd - │ ├──mn + │ ├──nwmon │ ├──pvm │ ├──ssm │ └──vpnm @@ -135,7 +135,7 @@ A tree representation of the skywire subcommands │ ├──curl │ ├─┬web │ │ └──gen-keys - │ ├─┬proxy + │ ├─┬socks │ │ ├──server │ │ └──client │ └──mon @@ -146,8 +146,8 @@ A tree representation of the skywire subcommands │ ├──skysocks │ └──skychat ├──tree - ├──doc - └── + └──doc + ``` @@ -160,7 +160,9 @@ A tree representation of the skywire subcommands └─┐├┴┐└┬┘││││├┬┘├┤───└┐┌┘│└─┐│ │├┬┘ └─┘┴ ┴ ┴ └┴┘┴┴└─└─┘ └┘ ┴└─┘└─┘┴└─ - Flags: + + +Flags: -c, --config string config file to use (default): skywire-config.json -C, --confarg string supply config as argument -b, --browser open hypervisor ui in default web browser @@ -180,9 +182,6 @@ A tree representation of the skywire subcommands └─┐├┴┐└┬┘││││├┬┘├┤───│ │ │ └─┘┴ ┴ ┴ └┴┘┴┴└─└─┘ └─┘┴─┘┴ -Usage: - skywire cli - Available Commands: config Generate or update a skywire config dmsgpty Interact with remote visors @@ -249,9 +248,6 @@ Global Flags: ``` Generate or update the config file used by skywire-visor. -Usage: - skywire cli config - Available Commands: gen Generate a config file gen-keys generate public / secret keypair @@ -266,11 +262,12 @@ Available Commands: ``` Generate a config file - Config defaults file may also be specified with + Config defaults file may also be specified with: SKYENV=/path/to/skywire.conf skywire-cli config gen + print the SKYENV file template with: + skywire-cli config gen -q + -Usage: - skywire cli config gen [flags] Flags: -a, --url string services conf url @@ -312,7 +309,7 @@ Flags: -z, --public publicize visor in service discovery --stcpr int set tcp transport listening port - 0 for random --sudph int set udp transport listening port - 0 for random - --binpath string set bin_path + --binpath string set bin_path for visor vative apps --proxyclientpk string set server public key for proxy client --startproxyclient autostart proxy client --noproxyserver disable autostart of proxy server @@ -325,7 +322,7 @@ Flags: --secure string change secure mode status of vpn server --netifc string VPN Server network interface (detected: eno1) --nofetch do not fetch the services from the service conf url - -S, --svcconf string fallback service configuration file (default "services-config.json") + -S, --svcconf string fallback service configuration file (default "services-config.json") --nodefaults do not use hardcoded defaults for production / test services --version string custom version testing override --all show all flags @@ -339,12 +336,13 @@ Flags: $ skywire cli config gen -bpirxn { "version": "v1.3.18", - "sk": "9d144c6d03595a2a3e8eb5377fe82bf0ec918cce79e0165119662a69d4c73fd6", - "pk": "02f10962a6e241dd686bae18730b28d5e13c9dd0d9929f6c7712d80e7e515ec295", + "sk": "eab215b4851fb14cbcb856a0b763923bb0d21dde0ede41eeb7ff176327fe760a", + "pk": "03603bdd732230acfbbeaf769a92487b469176ff84d5cce1041bf36963cbbc1d69", "dmsg": { "discovery": "http://dmsgd.skywire.skycoin.com", "sessions_count": 2, - "servers": [] + "servers": [], + "servers_type": "all" }, "dmsgpty": { "dmsg_port": 22, @@ -474,8 +472,8 @@ $ skywire cli config gen -bpirxn "db_path": "/opt/skywire/users.db", "enable_auth": true, "cookies": { - "hash_key": "e9535bb8d96b3e116cbc30ed527b0073224e943f1ef9c30a5250132cc782e0e87aa64a97878b625d04359836f37950a21b0feb3abd13e323c4d74202161c955d", - "block_key": "a16855cc04e3e5c5f1b31c9b5fb9b4d1031a3732553787b8f2d382710187436f", + "hash_key": "19a47254be4a7d9ce7664d20b4271bb402434eadfbb6c94dd59922d5cbf89ce3c03f1d54c320ca624fa44e8d85ad0b1df2a84acf607ef1ef7ea63bce99a50c53", + "block_key": "09df61d626fbda1632c91604620ca94c926125a109c4cf2f3d9bb608bd24b904", "expires_duration": 43200000000000, "path": "/", "domain": "" @@ -494,8 +492,7 @@ $ skywire cli config gen -bpirxn ``` generate public / secret keypair -Usage: - skywire cli config gen-keys + ``` @@ -505,8 +502,7 @@ Usage: ``` check a skywire public key -Usage: - skywire cli config check-pk + ``` @@ -516,9 +512,6 @@ Usage: ``` Update a config file -Usage: - skywire cli config update [flags] - Available Commands: dmsghttp update dmsghttp-config.json file from config bootstrap service svc update services-config.json file from config bootstrap service @@ -547,8 +540,7 @@ Flags: ``` update dmsghttp-config.json file from config bootstrap service -Usage: - skywire cli config update dmsghttp [flags] + Flags: -p, --path string path of dmsghttp-config file, default is for pkg installation (default "/opt/skywire/dmsghttp-config.json") @@ -566,8 +558,7 @@ Global Flags: ``` update services-config.json file from config bootstrap service -Usage: - skywire cli config update svc [flags] + Flags: -p, --path string path of services-config file, default is for pkg installation (default "/opt/skywire/services-config.json") @@ -585,8 +576,7 @@ Global Flags: ``` update hypervisor config -Usage: - skywire cli config update hv [flags] + Flags: -+, --add-pks string public keys of hypervisors that should be added to this visor @@ -605,8 +595,7 @@ Global Flags: ``` update skysocks-client config -Usage: - skywire cli config update sc [flags] + Flags: -+, --add-server string add skysocks server address to skysock-client @@ -625,8 +614,7 @@ Global Flags: ``` update skysocks-server config -Usage: - skywire cli config update ss [flags] + Flags: -s, --passwd string add passcode to skysocks server @@ -645,8 +633,7 @@ Global Flags: ``` update vpn-client config -Usage: - skywire cli config update vpnc [flags] + Flags: -x, --killsw string change killswitch status of vpn-client @@ -667,8 +654,7 @@ Global Flags: ``` update vpn-server config -Usage: - skywire cli config update vpns [flags] + Flags: -s, --passwd string add passcode to vpn-server @@ -690,9 +676,6 @@ Global Flags: ``` Interact with remote visors -Usage: - skywire cli dmsgpty - Available Commands: ui Open dmsgpty UI in default browser url Show dmsgpty UI URL @@ -707,8 +690,7 @@ Available Commands: ``` Open dmsgpty UI in default browser -Usage: - skywire cli dmsgpty ui [flags] + Flags: -i, --input string read from specified config file @@ -723,8 +705,7 @@ Flags: ``` Show dmsgpty UI URL -Usage: - skywire cli dmsgpty url [flags] + Flags: -i, --input string read from specified config file @@ -739,8 +720,7 @@ Flags: ``` List connected visors -Usage: - skywire cli dmsgpty list [flags] + Flags: --rpc string RPC server address (default "localhost:3435") @@ -753,8 +733,7 @@ Flags: ``` Start dmsgpty session -Usage: - skywire cli dmsgpty start [flags] + Flags: -p, --port string port of remote visor dmsgpty (default "22") @@ -768,9 +747,6 @@ Flags: ``` Query the Skywire Visor -Usage: - skywire cli visor [flags] - Available Commands: app App settings hv Hypervisor @@ -798,9 +774,6 @@ Flags: App settings -Usage: - skywire cli visor app [flags] - Available Commands: ls List apps start Launch app @@ -822,8 +795,7 @@ Global Flags: List apps -Usage: - skywire cli visor app ls [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -837,8 +809,7 @@ Global Flags: Launch app -Usage: - skywire cli visor app start [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -852,8 +823,7 @@ Global Flags: Halt app -Usage: - skywire cli visor app stop [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -867,8 +837,7 @@ Global Flags: Register app -Usage: - skywire cli visor app register [flags] + Flags: -a, --appname string name of the app @@ -886,8 +855,7 @@ Global Flags: Deregister app -Usage: - skywire cli visor app deregister [flags] + Flags: -k, --procKey string proc key of the app to deregister @@ -907,8 +875,7 @@ Global Flags: "beginning" is a special timestamp to fetch all the logs -Usage: - skywire cli visor app log [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -921,9 +888,6 @@ Global Flags: ``` App args -Usage: - skywire cli visor app arg [flags] - Available Commands: autostart Set app autostart killswitch Set app killswitch @@ -942,9 +906,6 @@ Global Flags: ``` App args -Usage: - skywire cli visor app arg [flags] - Available Commands: autostart Set app autostart killswitch Set app killswitch @@ -963,9 +924,6 @@ Global Flags: ``` App args -Usage: - skywire cli visor app arg [flags] - Available Commands: autostart Set app autostart killswitch Set app killswitch @@ -984,9 +942,6 @@ Global Flags: ``` App args -Usage: - skywire cli visor app arg [flags] - Available Commands: autostart Set app autostart killswitch Set app killswitch @@ -1005,9 +960,6 @@ Global Flags: ``` App args -Usage: - skywire cli visor app arg [flags] - Available Commands: autostart Set app autostart killswitch Set app killswitch @@ -1026,9 +978,6 @@ Global Flags: ``` App args -Usage: - skywire cli visor app arg [flags] - Available Commands: autostart Set app autostart killswitch Set app killswitch @@ -1053,9 +1002,6 @@ Global Flags: View remote hypervisor public key -Usage: - skywire cli visor hv [flags] - Available Commands: ui open Hypervisor UI in default browser cpk Public key of remote hypervisor(s) set in config @@ -1073,8 +1019,7 @@ Global Flags: open Hypervisor UI in default browser -Usage: - skywire cli visor hv ui [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1088,8 +1033,7 @@ Global Flags: Public key of remote hypervisor(s) set in config -Usage: - skywire cli visor hv cpk [flags] + Flags: -w, --http serve public key via http @@ -1107,8 +1051,7 @@ Global Flags: ``` Public key of remote hypervisor(s) which are currently connected to -Usage: - skywire cli visor hv pk [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1122,8 +1065,7 @@ Global Flags: Public key of the visor -Usage: - skywire cli visor pk [flags] + Flags: -w, --http serve public key via http @@ -1143,8 +1085,7 @@ Global Flags: Summary of visor info -Usage: - skywire cli visor info [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1158,8 +1099,7 @@ Global Flags: Version and build info -Usage: - skywire cli visor ver [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1173,8 +1113,7 @@ Global Flags: List of all ports used by visor services and apps -Usage: - skywire cli visor ports [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1188,8 +1127,7 @@ Global Flags: IP information of network -Usage: - skywire cli visor ip [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1203,8 +1141,7 @@ Global Flags: Creates a route with the provided pk as a hop and returns latency on the conn -Usage: - skywire cli visor ping [flags] + Flags: -s, --size int Size of packet, in KB, default is 2KB (default 2) @@ -1222,8 +1159,7 @@ Global Flags: Creates a route with public visors as a hop and returns latency on the conn -Usage: - skywire cli visor test [flags] + Flags: -c, --count int Count of Public Visors for using in test. (default 2) @@ -1241,8 +1177,7 @@ Global Flags: ``` start visor -Usage: - skywire cli visor start [flags] + Flags: -s, --src 'go run' external commands from the skywire sources @@ -1258,8 +1193,7 @@ Global Flags: ``` reload visor -Usage: - skywire cli visor reload [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1273,8 +1207,7 @@ Global Flags: Stop a running visor -Usage: - skywire cli visor halt [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1288,9 +1221,6 @@ Global Flags: View and set routing rules -Usage: - skywire cli visor route [flags] - Available Commands: ls-rules List routing rules rule Return routing rule by route ID key @@ -1309,8 +1239,7 @@ Global Flags: List routing rules -Usage: - skywire cli visor route ls-rules [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1324,8 +1253,7 @@ Global Flags: Return routing rule by route ID key -Usage: - skywire cli visor route rule [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1339,8 +1267,7 @@ Global Flags: Remove routing rule -Usage: - skywire cli visor route rm-rule [flags] + Flags: -a, --all remove all routing rules @@ -1357,9 +1284,6 @@ Global Flags: Add routing rule -Usage: - skywire cli visor route add-rule ( app | fwd | intfwd ) [flags] - Available Commands: app Add app/consume routing rule fwd Add forward routing rule @@ -1380,9 +1304,6 @@ Global Flags: Add routing rule -Usage: - skywire cli visor route add-rule ( app | fwd | intfwd ) [flags] - Available Commands: app Add app/consume routing rule fwd Add forward routing rule @@ -1403,9 +1324,6 @@ Global Flags: Add routing rule -Usage: - skywire cli visor route add-rule ( app | fwd | intfwd ) [flags] - Available Commands: app Add app/consume routing rule fwd Add forward routing rule @@ -1426,9 +1344,6 @@ Global Flags: Add routing rule -Usage: - skywire cli visor route add-rule ( app | fwd | intfwd ) [flags] - Available Commands: app Add app/consume routing rule fwd Add forward routing rule @@ -1457,9 +1372,6 @@ Global Flags: Types: stcp stcpr sudph dmsg -Usage: - skywire cli visor tp [flags] - Available Commands: type Transport types used by the local visor ls Available transports @@ -1480,8 +1392,7 @@ Global Flags: Transport types used by the local visor -Usage: - skywire cli visor tp type + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1497,8 +1408,7 @@ Global Flags: displays transports of the local visor -Usage: - skywire cli visor tp ls [flags] + Flags: -t, --types strings show transport(s) type(s) comma-separated @@ -1517,8 +1427,7 @@ Global Flags: Transport summary by id -Usage: - skywire cli visor tp id (-i) + Flags: -i, --id string transport ID @@ -1539,8 +1448,7 @@ Global Flags: the visor will attempt to establish a transport in the following order: skywire-tcp, stcpr, sudph, dmsg -Usage: - skywire cli visor tp add (-p) + Flags: -r, --rpk string remote public key. @@ -1559,8 +1467,7 @@ Global Flags: Remove transport(s) by id -Usage: - skywire cli visor tp rm ( -a || -i ) + Flags: -a, --all remove all transports @@ -1578,8 +1485,7 @@ Global Flags: Discover remote transport(s) by ID or public key -Usage: - skywire cli visor tp disc (--id= || --pk=) + Flags: -i, --id string obtain transport of given ID @@ -1596,9 +1502,6 @@ Global Flags: ``` VPN client -Usage: - skywire cli vpn [flags] - Available Commands: start start the vpn for stop stop the vpnclient @@ -1618,8 +1521,7 @@ Flags: ``` start the vpn for -Usage: - skywire cli vpn start [flags] + Flags: -k, --pk string server public key @@ -1636,8 +1538,7 @@ Global Flags: ``` stop the vpnclient -Usage: - skywire cli vpn stop [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1650,8 +1551,7 @@ Global Flags: ``` vpn client status -Usage: - skywire cli vpn status [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -1668,8 +1568,7 @@ http://sd.skycoin.com/api/services?type=vpn&country=US Set cache file location to "" to avoid using cache files -Usage: - skywire cli vpn list [flags] + Flags: -m, --cfa int update cache files if older than n minutes (default 5) @@ -1697,8 +1596,7 @@ Global Flags: ``` Open VPN UI in default browser -Usage: - skywire cli vpn ui [flags] + Flags: -c, --config string config path @@ -1715,8 +1613,7 @@ Global Flags: ``` Show VPN UI URL -Usage: - skywire cli vpn url [flags] + Flags: -c, --config string config path @@ -1739,8 +1636,7 @@ Check local visor daily uptime percent with: skywire-cli ut -k $(skywire-cli visor pk)n Set cache file location to "" to avoid using cache files -Usage: - skywire cli ut [flags] + Flags: -m, --cfa int update cache files if older than n minutes (default 5) @@ -1760,8 +1656,7 @@ Flags: Control skyforwarding forward local ports over skywire -Usage: - skywire cli fwd [flags] + Flags: -d, --deregister deregister local port of the external (http) app @@ -1776,8 +1671,7 @@ Flags: ``` connect or disconnect from remote ports -Usage: - skywire cli rev [flags] + Flags: -l, --ls list configured connections @@ -1793,16 +1687,9 @@ Flags: ``` - reward address setting + skycoin reward address set to: - Sets the skycoin reward address for the visor. - The config is written to the root of the default local directory - this config is served via dmsghttp along with transport logs - and the system hardware survey for automating reward distribution - -Usage: - skywire cli reward
|| [flags] Flags: --all show all flags @@ -1817,11 +1704,10 @@ Flags: Collect surveys: skywire-cli log Fetch uptimes: skywire-cli ut > ut.txt -Usage: - skywire cli rewards [flags] + Flags: - -d, --date string date for which to calculate reward (default "2024-02-26") + -d, --date string date for which to calculate reward (default "2024-03-12") -k, --pk string check reward for pubkey -n, --noarch string disallowed architectures, comma separated (default "amd64") -y, --year int yearly total rewards (default 408000) @@ -1840,8 +1726,7 @@ Flags: ``` print the system survey -Usage: - skywire cli survey + Flags: -s, --sha generate checksum of system survey @@ -1860,8 +1745,7 @@ unknown command "survey" for "skywire" Query the Route Finder Assumes the local visor public key as an argument if only one argument is given -Usage: - skywire cli rtfind | [flags] + Flags: -n, --min uint16 minimum hops (default 1) @@ -1882,8 +1766,7 @@ http://tpd.skywire.skycoin.com/all-transports Set cache file location to "" to avoid using cache files -Usage: - skywire cli rtree [flags] + Flags: -m, --cfa int update cache files if older than n minutes (default 5) @@ -1905,9 +1788,6 @@ Flags: ``` Query remote DMSG Discovery -Usage: - skywire cli mdisc - Available Commands: entry Fetch an entry servers Fetch available servers @@ -1920,8 +1800,7 @@ Available Commands: ``` Fetch an entry -Usage: - skywire cli mdisc entry [flags] + Flags: -a, --addr string DMSG discovery server address @@ -1935,8 +1814,7 @@ Flags: ``` Fetch available servers -Usage: - skywire cli mdisc servers [flags] + Flags: --addr string address of DMSG discovery server @@ -1950,8 +1828,7 @@ Flags: ``` Generate completion script -Usage: - skywire cli completion [bash|zsh|fish|powershell] + ``` @@ -1963,8 +1840,7 @@ Fetch health, survey, and transport logging from visors which are online in the http://ut.skywire.skycoin.com/uptimes?v=v2 http://ut.skywire.skycoin.com/uptimes?v=v2&visors=;; -Usage: - skywire cli log [flags] + Flags: -e, --env string deployment to get uptimes from (default "prod") @@ -1995,14 +1871,9 @@ Flags: ``` Skysocks client -Usage: - skywire cli proxy [flags] - Available Commands: start start the proxy client stop stop the proxy client -stop the default instance with: - stop --name skysocks-client status proxy client status list List servers @@ -2017,15 +1888,12 @@ Flags: ``` start the proxy client -Usage: - skywire cli proxy start [flags] + Flags: - -a, --addr string address of proxy for use - -p, --http-proxy string starting http-proxy based on skysocks - -n, --name string name of skysocks client - -k, --pk string server public key - -t, --timeout int starting timeout value in second (default 20) + -a, --addr string address of proxy for use + -n, --name string name of skysocks client + -k, --pk string server public key Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -2037,11 +1905,8 @@ Global Flags: ``` stop the proxy client -stop the default instance with: - stop --name skysocks-client -Usage: - skywire cli proxy stop [flags] + Flags: --all stop all skysocks client @@ -2058,8 +1923,7 @@ Global Flags: ``` proxy client status -Usage: - skywire cli proxy status [flags] + Global Flags: --rpc string RPC server address (default "localhost:3435") @@ -2076,8 +1940,7 @@ http://sd.skycoin.com/api/services?type=proxy&country=US Set cache file location to "" to avoid using cache files -Usage: - skywire cli proxy list [flags] + Flags: -m, --cfa int update cache files if older than n minutes (default 5) @@ -2105,8 +1968,7 @@ Global Flags: ``` subcommand tree -Usage: - skywire cli tree + ``` @@ -2124,8 +1986,7 @@ generate markdown docs cat cmd/skywire-cli/README1.md | gh-md-toc -Usage: - skywire cli doc + ``` @@ -2148,9 +2009,9 @@ Available Commands: kg skywire keys generator, prints pub-key and sec-key lc Liveness checker of the deployment. nv Node Visualizer Server for skywire - swe skywire environment generator + se skywire environment generator sd Service discovery server - mn Network monitor for skywire VPN and Visor. + nwmon Network monitor for skywire VPN and Visor. pvm Public Visor monitor. ssm Skysocks monitor. vpnm VPN monitor. @@ -2167,6 +2028,14 @@ Available Commands: └─┘└─┘ ┴ └─┘┴ ┘└┘└─┘─┴┘└─┘ + +Flags: + -m, --metrics string address to bind metrics API to + -i, --stdin read config from STDIN + --syslog string syslog server address. E.g. localhost:514 + --tag string logging tag (default "setup_node") + + ``` #### svc tpd @@ -2176,6 +2045,10 @@ Available Commands: ┌┬┐┬─┐┌─┐┌┐┌┌─┐┌─┐┌─┐┬─┐┌┬┐ ┌┬┐┬┌─┐┌─┐┌─┐┬ ┬┌─┐┬─┐┬ ┬ │ ├┬┘├─┤│││└─┐├─┘│ │├┬┘ │───│││└─┐│ │ │└┐┌┘├┤ ├┬┘└┬┘ ┴ ┴└─┴ ┴┘└┘└─┘┴ └─┘┴└─ ┴ ─┴┘┴└─┘└─┘└─┘ └┘ └─┘┴└─ ┴ +----- depends: redis, postgresql and initial DB setup ----- +sudo -iu postgres createdb tpd +keys-gen | tee tpd-config.json +PG_USER="postgres" PG_DATABASE="tpd" PG_PASSWORD="" transport-discovery --sk $(tail -n1 tpd-config.json) @@ -2226,7 +2099,14 @@ Flags: ├─┤ ││ ││├┬┘├┤ └─┐└─┐───├┬┘├┤ └─┐│ ││ └┐┌┘├┤ ├┬┘ ┴ ┴─┴┘─┴┘┴└─└─┘└─┘└─┘ ┴└─└─┘└─┘└─┘┴─┘└┘ └─┘┴└─ +depends: redis + +Note: the specified port must be accessible from the internet ip address or port forwarded for udp +skywire cli config gen-keys > ar-config.json +skywire svc ar --addr ":9093" --redis "redis://localhost:6379" --sk $(tail -n1 ar-config.json) +Usage: + skywire svc ar Flags: -a, --addr string address to bind to (default ":9093") @@ -2255,6 +2135,10 @@ Flags: ┬─┐┌─┐┬ ┬┌┬┐┌─┐ ┌─┐┬┌┐┌┌┬┐┌─┐┬─┐ ├┬┘│ ││ │ │ ├┤───├┤ ││││ ││├┤ ├┬┘ ┴└─└─┘└─┘ ┴ └─┘ └ ┴┘└┘─┴┘└─┘┴└─ +----- depends: postgres and initial db setup ----- +sudo -iu postgres createdb rf +skywire cli config gen-keys | tee rf-config.json +PG_USER="postgres" PG_DATABASE="rf" PG_PASSWORD="" route-finder --addr ":9092" --sk $(tail -n1 rf-config.json) @@ -2356,7 +2240,7 @@ Flags: ``` -#### svc swe +#### svc se ``` @@ -2378,7 +2262,7 @@ Flags: ``` -##### svc swe visor +##### svc se visor ``` Generate config for skywire-visor @@ -2388,7 +2272,7 @@ Generate config for skywire-visor ``` -##### svc swe dmsg +##### svc se dmsg ``` Generate config for dmsg-server @@ -2398,7 +2282,7 @@ Generate config for dmsg-server ``` -##### svc swe setup +##### svc se setup ``` Generate config for setup node @@ -2415,16 +2299,18 @@ Generate config for setup node ┌─┐┌─┐┬─┐┬ ┬┬┌─┐┌─┐ ┌┬┐┬┌─┐┌─┐┌─┐┬ ┬┌─┐┬─┐┬ ┬ └─┐├┤ ├┬┘└┐┌┘││ ├┤───│││└─┐│ │ │└┐┌┘├┤ ├┬┘└┬┘ └─┘└─┘┴└─ └┘ ┴└─┘└─┘ ─┴┘┴└─┘└─┘└─┘ └┘ └─┘┴└─ ┴ +----- depends: redis, postgresql and initial DB setup ----- +sudo -iu postgres createdb sd +keys-gen | tee sd-config.json +PG_USER="postgres" PG_DATABASE="sd" PG_PASSWORD="" service-discovery --sk $(tail -n1 sd-config.json) Flags: -a, --addr string address to bind to (default ":9098") -g, --api-key string geo API key - -d, --dmsg-disc string url of dmsg-discovery default: - http://dmsgd.skywire.skycoin.com - --dmsgPort uint16 dmsg port value - (default 80) + -d, --dmsg-disc string url of dmsg-discovery (default "http://dmsgd.skywire.skycoin.com") + --dmsgPort uint16 dmsg port value (default 80) -m, --metrics string address to bind metrics API to -o, --pg-host string host of postgres (default "localhost") -p, --pg-port string port of postgres (default "5432") @@ -2438,7 +2324,7 @@ Flags: ``` -#### svc mn +#### svc nwmon ``` @@ -2540,8 +2426,8 @@ Available Commands: http DMSG http file server curl DMSG curl utility web DMSG resolving proxy & browser client - proxy - mon DMSG monitor of DMSG discoery. + socks DMSG socks5 proxy server & client + mon DMSG monitor of DMSG discovery entries. ``` @@ -2569,7 +2455,7 @@ Available Commands: ┌┬┐┌┬┐┌─┐┌─┐┌─┐┌┬┐┬ ┬ ┌─┐┬ ┬ │││││└─┐│ ┬├─┘ │ └┬┘───│ │ │ ─┴┘┴ ┴└─┘└─┘┴ ┴ ┴ └─┘┴─┘┴ - DMSG pseudoterminal command line interface +DMSG pseudoterminal command line interface Available Commands: whitelist lists all whitelisted public keys @@ -2627,7 +2513,7 @@ removes public key(s) from the whitelist ┌┬┐┌┬┐┌─┐┌─┐┌─┐┌┬┐┬ ┬ ┬ ┬┌─┐┌─┐┌┬┐ │││││└─┐│ ┬├─┘ │ └┬┘───├─┤│ │└─┐ │ ─┴┘┴ ┴└─┘└─┘┴ ┴ ┴ ┴ ┴└─┘└─┘ ┴ - DMSG host for pseudoterminal command line interface +DMSG host for pseudoterminal command line interface Available Commands: confgen generates config file @@ -2688,15 +2574,17 @@ Flags: ┌┬┐┌┬┐┌─┐┌─┐ ┌┬┐┬┌─┐┌─┐┌─┐┬ ┬┌─┐┬─┐┬ ┬ │││││└─┐│ ┬───│││└─┐│ │ │└┐┌┘├┤ ├┬┘└┬┘ ─┴┘┴ ┴└─┘└─┘ ─┴┘┴└─┘└─┘└─┘ └┘ └─┘┴└─ ┴ - DMSG Discovery Server +DMSG Discovery Server +----- depends: redis ----- +skywire cli config gen-keys > dmsgd-config.json +skywire dmsg disc --sk $(tail -n1 dmsgd-config.json) Flags: -a, --addr string address to bind to (default ":9090") --auth string auth passphrase as simple auth for official dmsg servers registration - --dmsgPort uint16 dmsg port value - (default 80) + --dmsgPort uint16 dmsg port value (default 80) --enable-load-testing enable load testing --entry-timeout duration discovery entry timeout (default 3m0s) -m, --metrics string address to serve metrics API from @@ -2722,7 +2610,9 @@ Flags: ┌┬┐┌┬┐┌─┐┌─┐ ┌─┐┌─┐┬─┐┬ ┬┌─┐┬─┐ ││││││└─┐│ ┬ ─ └─┐├┤ ├┬┘└┐┌┘├┤ ├┬┘ ─┴┘┴ ┴└─┘└─┘ └─┘└─┘┴└─ └┘ └─┘┴└─ - DMSG Server +DMSG Server +skywire dmsg server config gen -o dmsg-config.json +skywire dmsg server start dmsg-config.json Available Commands: config Generate a dmsg-server config @@ -2784,7 +2674,7 @@ Flags: ┌┬┐┌┬┐┌─┐┌─┐┬ ┬┌┬┐┌┬┐┌─┐ │││││└─┐│ ┬├─┤ │ │ ├─┘ ─┴┘┴ ┴└─┘└─┘┴ ┴ ┴ ┴ ┴ - DMSG http file server +DMSG http file server @@ -2808,7 +2698,7 @@ Flags: ┌┬┐┌┬┐┌─┐┌─┐┌─┐┬ ┬┬─┐┬ │││││└─┐│ ┬│ │ │├┬┘│ ─┴┘┴ ┴└─┘└─┘└─┘└─┘┴└─┴─┘ - DMSG curl utility +DMSG curl utility @@ -2837,7 +2727,7 @@ Flags: ┌┬┐┌┬┐┌─┐┌─┐┬ ┬┌─┐┌┐ │││││└─┐│ ┬│││├┤ ├┴┐ ─┴┘┴ ┴└─┘└─┘└┴┘└─┘└─┘ - DMSG resolving proxy & browser client - access websites over dmsg +DMSG resolving proxy & browser client - access websites over dmsg Available Commands: gen-keys generate public / secret keypair @@ -2869,20 +2759,26 @@ generate public / secret keypair ``` -#### dmsg proxy +#### dmsg socks ``` + + ┌┬┐┌┬┐┌─┐┌─┐ ┌─┐┌─┐┌─┐┬┌─┌─┐ + │││││└─┐│ ┬───└─┐│ ││ ├┴┐└─┐ + ─┴┘┴ ┴└─┘└─┘ └─┘└─┘└─┘┴ ┴└─┘ +DMSG socks5 proxy server & client + Available Commands: - server dmsg proxy server - client socks5 proxy to connect to socks5 server over dmsg + server dmsg socks5 proxy server + client socks5 proxy client for dmsg socks5 proxy server ``` -##### dmsg proxy server +##### dmsg socks server ``` -dmsg proxy server +dmsg socks5 proxy server @@ -2897,10 +2793,10 @@ Flags: ``` -##### dmsg proxy client +##### dmsg socks client ``` -socks5 proxy to connect to socks5 server over dmsg +socks5 proxy client for dmsg socks5 proxy server @@ -2932,7 +2828,7 @@ Flags: -c, --config string config file location. (default "dmsg-monitor.json") -d, --dmsg-url string url to dmsg data. -l, --loglvl string set log level one of: info, error, warn, debug, trace, panic (default "info") - -s, --sleep-deregistration duration Sleep time for derigstration process in minutes (default 10ns) + -s, --sleep-deregistration duration Sleep time for derigstration process in minutes (default 60ns) --syslog string syslog server address. E.g. localhost:514 --tag string logging tag (default "dmsg_monitor") -u, --ut-url string url to uptime tracker visor data. @@ -3075,10 +2971,4 @@ generate markdown docs -``` - -### - -``` - ``` diff --git a/cmd/skywire-deployment/skywire.go b/cmd/skywire-deployment/skywire.go index 6f67fcd121..a191baad99 100644 --- a/cmd/skywire-deployment/skywire.go +++ b/cmd/skywire-deployment/skywire.go @@ -134,11 +134,11 @@ func init() { sn.RootCmd.Use = "sn" scli.RootCmd.Use = "cli" visor.RootCmd.Use = "visor" - vpns.RootCmd.Use = "vpns" - vpnc.RootCmd.Use = "vpnc" - ssc.RootCmd.Use = "ssc" - ss.RootCmd.Use = "ss" - sc.RootCmd.Use = "sc" + vpns.RootCmd.Use = "vpn-server" + vpnc.RootCmd.Use = "vpn-client" + ssc.RootCmd.Use = "skysocks-client" + ss.RootCmd.Use = "skysocks" + sc.RootCmd.Use = "skychat" var helpflag bool RootCmd.SetUsageTemplate(help) diff --git a/cmd/skywire/skywire.go b/cmd/skywire/skywire.go index 49826e2d8f..1997ad0f2d 100644 --- a/cmd/skywire/skywire.go +++ b/cmd/skywire/skywire.go @@ -42,11 +42,11 @@ func init() { visor.RootCmd.Use = "visor" cli.RootCmd.Use = "cli" sn.RootCmd.Use = "sn" - vpns.RootCmd.Use = "vpns" - vpnc.RootCmd.Use = "vpnc" - ssc.RootCmd.Use = "ssc" - ss.RootCmd.Use = "ss" - sc.RootCmd.Use = "sc" + vpns.RootCmd.Use = "vpn-server" + vpnc.RootCmd.Use = "vpn-client" + ssc.RootCmd.Use = "skysocks-client" + ss.RootCmd.Use = "skysocks" + sc.RootCmd.Use = "skychat" var helpflag bool RootCmd.SetUsageTemplate(help) From 9777a154f14128c236f15f8afa3de3c502dae451 Mon Sep 17 00:00:00 2001 From: Moses Narrow <36607567+0pcom@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:58:34 -0500 Subject: [PATCH 02/53] remove unneeded file ; increment minimum version requirement (#1766) --- mainnet_rules.md | 26 ++++++++++------------ skywire.conf | 56 ------------------------------------------------ 2 files changed, 11 insertions(+), 71 deletions(-) delete mode 100644 skywire.conf diff --git a/mainnet_rules.md b/mainnet_rules.md index 73d7aa95c2..579bf077ff 100644 --- a/mainnet_rules.md +++ b/mainnet_rules.md @@ -45,19 +45,17 @@ A total of up to ~1114.754 Skycoin are distributed daily in leap-years. To obtain Skycoin rewards for running skywire, the following requirements must be met. The update deadlines specify the version of software required as of (i.e. on or before) the specified date in order to maintain reward eligibility: -* **Minimum skywire version v1.3.14** - Cutoff January 1st 2024 - -* **Minimum skywire version v1.3.15** - Cutoff February 1st 2024 - * **Minimum skywire version v1.3.17** - Cutoff March 1st 2024 -* The visor must be an **ARM architecture SBC running on approved [hardware](#hardware)** +* **Minimum skywire version v1.3.19** - Cutoff April 1st 2024 + +* The visor must be an **ARM or RISC architecture SBC running on approved [hardware](#hardware)** * Visors must be running on **[the skywire production deployment](https://conf.skywire.skycoin.com)** with a config that is updated on every version. * **Only 1 (one) visor per machine** -* **Up to 8 (eight) visors may each receive 1 reward share per location (ip address)** +* **Up to 8 (eight) visors may each receive 1 (one) reward share per location (ip address)** * **75% uptime per day** minimum is required to be eligible to receive rewards @@ -99,19 +97,17 @@ skywire-cli -v skywire-visor -v ``` +**Reward eligibility after 3-1-2024 requires Skywire v1.3.17** +Requirement established 2-1-2024 -**Reward eligibility after 2-1-2024 requires Skywire v1.3.15** - -Requirement established 12-18-2023 - -Rewards Cutoff date for updating 2-1-2024 +Rewards Cutoff date for updating 3-1-2024 -**Reward eligibility after 3-1-2024 requires Skywire v1.3.17** +**Reward eligibility after 4-1-2024 requires Skywire v1.3.19** -Requirement established 2-1-2023 +Requirement established 3-14-2024 -Rewards Cutoff date for updating 3-1-2024 +Rewards Cutoff date for updating 4-1-2024 ### Deployment @@ -156,7 +152,7 @@ Add your skycoin address there and run `skywire-autoconfig` on linux (assumes yo If this file does not exist for you, it can be created with `skywire-cli config gen -q | tee /etc/skywire.conf` -If you do this, please uncomment `PKGENV=true` before saving the file +**If you do this, please uncomment `PKGENV=true` before saving the file** ### Connection to DMSG network - Survey & transport log collection diff --git a/skywire.conf b/skywire.conf deleted file mode 100644 index ca79c8be78..0000000000 --- a/skywire.conf +++ /dev/null @@ -1,56 +0,0 @@ -#PKGENV="false" -#USRENV="false" -#TESTENV="false" -#BESTPROTO="false" -#DMSGHTTP="false" -SVCCONFADDR=("http://conf.magnetosphere.net") -#SERVEVPN="false" -#SK="" -#DMSGDISCADDR=("") -#DMSGSESSCT="1" -#DMSGSERVERS=("" -#DMSGPTYCLIPORT="22" -#DMSGPTYCLINW="unix" -#DMSGPTYCLISOCK="/tmp/dmsgpty.sock" -#DMSGPTYCLIWLPKS=("") -#STCPTABLEPKS=("") -#STCPLISTEN=":7777" -#TPDISCADDR=("") -#AR=("") -DISABLEPUBLICAUTOCONN="true" -#TPSETUPNODES=("") -#TPLOGSDIR="./local/transport_logs" -#TPLOGROTATE="168h0m0s" -#RSETUPNODES=("") -#RF=("") -#RFTIMEOUT="10s" -#MINHOPS="0" -#UTADDR=("") -#SDADDR=("") -#VPNCLIENTARGS=("-dns" "1.1.1.1") -#VPNCLIENTPORT="43" -#VPNCLIENTAUTOSTART="false" -#VPNSERVERPORT="44" -#VPNSERVERAUTOSTART="false" -#SKYCHATARGS=("-addr" ":8001") -#SKYCHATPORT="1" -#SKYCHATAUTOSTART="false" -#SKYSOCKSAUTOSTART="true" -#SKYSOCKSPORT="3" -#SKYSOCKSCLIENTAUTOSTART="false" -#SKYSOCKSCLIENTPORT="13" -#APPLAUNCHERSERVERADDR="localhost:5505" -#APPSBINPATH="./apps", -#DISPLAYNODEIP="false" -#ISHYPERVISOR=false -HYPERVISORPKS=("027087fe40d97f7f0be4a0dc768462ddbb371d4b9e7679d4f11f117d757b9856ed") -#RPCADDR="localhost:3435" -LOGLVL="debug" -#LOCALPATH="./local", -#DMSGHTTPSERVERPATH="./local/custom" -#STUNSERVERS=("") -#SHUTDOWNTIMEOUT="10s" -#ISPUBLIC="false" -#PERSISTENTTPTABLE=("") -#VERSION="" -#OUTPUT="./skywire-config.json" From f8f4b328a5062608a296e3a5285f5c794b0ce4be Mon Sep 17 00:00:00 2001 From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com> Date: Fri, 15 Mar 2024 20:12:47 -0400 Subject: [PATCH 03/53] Android VPN client (#1419) --- 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 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 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 zcmbVRcTiK?x=%=G3B8E{A<_&OAc4?}frMTRJ*Xf7g0v8-AV>!h1gT09AyjD!2#TO0 zNK-lpSWtSC4k}HQhv%Mi-zF9k-V5-Lofk6NO0IPvM-u$>t zJbs@sF&uxz#XpebO9p5;Qys~00}AO1*_=#rzUK3mtPTKxuDV$g z{fQ>Vs!mjj4CxO>CWzvFObr02p9}IPIeC!%;f~}hZeAK98_!>fz}=iRL~ImHP$u3u zva6eZurJvn*woS~*uzQ1S>&81Ts=tjm;r_CPl5+gJiYu>gEU0`;;VYx|1&Ks0{;u* z@1Y^`FQtek1UQcBONJ}RC`db@6&bXGf|8OP5-x{AV`NciS+u+~T16FuQAMHP ze?KC}(tMp=RL$|af6F=^X^6P``+KX($_54o$^^>GP<^k+qE%E>WKnXma&pqg2x-4- zUjC#YX)iy~e>mXDeonq_-u`Y>FZdshBu8q1zlO-MrvKUk#oNT>-;BNd{x;OH$z+2_ z-m+*Jlq`kvXJ3Dz{rt_z|E(P$lQZ5&=n8-v5hYw6%nDrnq4v<6;&{v@8Jsja-9$0s-XWx7xkp~ap-z|*9hEV z=)Q5s2#ZB+26I-qaf>mT&P9qFWsx0`-3{)wER_Sw-}zq)mJ$x7X(btbN`UCRdGE0Y z4pUPRCsjCVrm-y?5mJn7Ou0aBRN+LKvX`J5DrHl{6OIUbiRP491<`zyXhh98)@Zyc zpV49=7snf*=|_37f{m&TB~CbHF{-f5tTx$oR5B#0gD!SqcnSm> zUpj%ZCSaNM6X}vz`5!DfZ#J_7M-Bx*QXrP94`)y*00;umU83p)WXyhb8W3$AAm&}; z;ZV$07v&hK4;o0pYPanUPnN~#fNmR5x^FwAVh#3<%}2Hc5&A2$#8XqJU(X($0-5-gv#6Y) zlzGwVb_Wtiis}2r5iYM9LN8~cQuFu@E zH(P)4L%pSXRQQE=(9rsU^d*z-T`z|pyXOVh!w=;fcJs*af(aWshun&XWqEvAJj8^- zpO?R=9JN$7Ts;apG4+0hy&*VGG+>qQAg=7uHi@Zd`-Q0Of0dN8W7zfyEgI6__s+*7Gtm9%HTPDu9(hTTy zE2XrEVUIpHZjLX?{QjZXBKk`!YRkK#EK^x-SC3r=iYSak z&Rl9+w%B;#v;UG++!j0%Yaw9hlOz2EAy;mMkhIBFezxy0&C`)8sP<;3n99pgcqkj z*8U)ouEWet@QXHTd0iAe1Uu9ep)`l%W;9(>YIYHDF!H_%?Myx$ohQP@rr7pV?fLEt zPnF@^*4lvj3IgqBzpR8m)6+X9)2mNgn9GycO^QE_m~TfZZCu)uJZ+w~jWU2ZZIkSY zyuRRQC5UoD)uGC#nT%Ef<|4^%Q7UU0~N^lcV$ z-*5BXmUUJXfRzTeG*rMx9PST)X1M7wU@F7{;-7drv0nV!`}?W9s&OLwp`?`Awd^>7 z6o)C^m%Envk1)o)U@0GDIXDPmZN3328`I?)K@lyaXN)$KyY4U9M?dW`t zh;ncMv*|RFHzh)zO?~WO*TEO#9Mu7Vd};*JtBWZ=yEF>Yqwm|67sXrz>@r4_zj(i3 z#Q<|HtcIoBz4fb2B=WUZlwm4RNQgN%<&!_y#H4M%WMdekp^3?9PHc9^YCJ8@)5-)x zXL)>SV7OKQh(;&GkZa+32?`OwRP@ihc4vcCB%7r_^JB0O`-RXl5<|Miblh@uq+1!K zXoCuJ-}R3?=L}{%PdDdcVyMlXnCMt2{={GyZ_>|@xH#9@;s`9+VyqA?9UO}CSc%`5 zGjzvgA)X9pV}&R{sFlMcM$!tnZ$jATuW6}^*)gc0$j`ic!toQ1$}_34bx>gOipdRx1`8o->O zQCft2xsfXJ!;M%=?w2j#1}K89QjwwLElqCtS*qVO(B-l0L+-L-VXo`^5QxaFo^9Q> z$Et$j5mC4KHJOr5er>TuTS&Qnn2m7Z@U`BL0v0{tFfE#5dsQ~EOIDE@G zAyd0<{^jCMhhBbT}s9iAx0$J7FxLK`<#suee*k39ask zq`n$~*DGM$+I%YAwXPuV*IB^%50CvW*Gm}q+PBuoGpDLV1S{hzHX;bGukFR@Y z4Z{e&D(IV=t1O)?dSo;_(a+=Q1zjE$Kiz&;ZIoZ!jg-hz(x`|vEgzcuuA9FX%gI&4 zO%X!Xl|w~@&Gzm`@DYlO?{8jguf>-qcH39rCPWu(9r+yZ^+y?al^&+}=2DSh)5mLHc|;A0peeU;vB;hwelkAMb9 zwBqP=zkPZW;>t@>Oy}kx)9j5wf4v`a-bLh76nk~8{BFT2J?|Jbh09Fa`hA&9S>$cCnoipE zGEp?p1jF5$+doZNL!;u}bJ-@1iJknF`FR2A!`d}+hq?22vKy)e-;lGQ$ZyWZIIH^3 z)|lI(0a1rZG-rthC8~uKX`lZ(ww?2H{G^%{EL1O%s{WbB0X1NXnvWau{pEl7@llRY z#UmBA; z@lU^C=(%5qWD~0@SCc!GL5$Bh|CVjJu(@JSI=IOPCx3}aA~|f=+GciZH-}cK7x1=s zMmHz36_0l-Z0OU2+{U%=1_d>9{a5%e(B~w-@H4Qfzp*kBBvzwxy>NuSsic}Y-|!Tv z7y?@P#Eh>nej%(~39WiC?U2}##fBd`4fjXzp9PKtCM+rwN9kh%fHRTnD)lf|B5OBW z!e5T_!;KjK}YYr`_K z0*V;-a*7zr=cM`EwRMfT3FUDY9YduwnCU8tAPbv{8@3QU3@urnpz&75Zz`>I_Jq?$ zxjdE(@cWeUy<}Q_WYdl~=GRYDRDEX|3p?w(at_Wjk@UI{zM>|UcEKM^iDKE1J8vOL z4Z;MWD3{A`Ya-C`AFH`PPsV!oZ65ebm7ds9EOjk(36kZxQ5`eh|By}QpeQjiz}TLT z^|*8|+oWi}@(Z@a#;o;m1}$womGEGyjFqR)dcm+#sjM07Ue1az<|OA}>7g4;e#@@~ ztNX+|IYA)%ae%OytBC~*CgrqY)FTI=1gDVoiD`cCIY^OB_TgZAlxXBI5czdkjz2x- zOGToBs1&YtW+VT0c zwIrAIKtw(^!O%>Ko0M~55DT}N$(V9AP6H=E=Yrr|e7|AezyZ?-M&OE7?dI1kgXfY`}R88w7+ot*&@+b2#_uz4^F6(jq@97v(Ot&KpL!^_NQI zc00gj+i)OhS6Ek*oRwC3v`=CYjC4VohH(H;9OX>yo@u_$YL=1Xj2~i^UJ&K~)}Diq z%4NvhpbK(n^Dwt6B*phHA84|4?znMkOaY)7&(N>0%(qjOZI_*oXAOf();uTjTcn8;hR(Fy+ z`*$CgwFPf0$bUdg{CoW%!Cf>m{dCpBmDwkuyl|`JKyN49l15eY**Q|m+X08$>WQlM zuWpYj)roy4x+1)>40`m*^jr`g2kcM_*GBHSxX}it^x`D1=wEhHigX*i%-ZKOukd{f z579!`Mf<|smD*;oZ$g8aDs4Ec`hQv?rbq+Seg z-Rsl9USEeYHso1Hw8VZYt+^l|9vj9%6_t5pc`MxbEj2*?opv9}J;8<$ z?vQiVQhnyrz3cfT$Z@^q!j3_SC6`qew680x@J!0W;x?x-G>?yI zWcV`|T`TuIJKCTv0+g#t7n%{i|G9PI9H+PIlury-qo73mVS((a)$k)J>+sAR6Z+V% z)2+;9rbgG+9kEA#^=X7gAqB)WhsGkAZ?cDrhf|D3%Gy#ElBU<67(8|k(mAvI?x&Kb z`TO+?su*i;aL^mJ6T`zfHMu=v@-#p(yKV0Z;0JYwdc6<`rB52eKRI|#I5=}Vvd!%A z?%d&2cLQHs_{`&s6kOHY8sAM6pd4U^6^TlFBz0zflARj9I(}>a2{zo)kbAzE;{_^5 z`0ZDU)lyr?d(H613e9^^n40qK9V<&odp3@)WP@_-TaeM(((i9wyVc>TW|@W&dg=7m z?3x#x_IQJZzdS#ogCNw-(>S53Jj_AV&ntMh=K@^#~P7gmx zf$Dh;!s=n`zESvhEcz$!dt2N7ejMnV!EPM$Px#<`9n;q+WmCF6uBamy|CLb z{`Ul4W>|N=TkG&8cVf5A+N$PZ+Q+e;jp&a?SAq8Rtee=EMRw2X`a3h5^!NhU*CrnU z8{OgO?_q{My_GS{~4+^)zcgbY^>c3m9GrQqV=Q1B~8tW8W zV9v&#(qDScr+X3@q^9|GuwpBeyo!YIUG9@&;NzD(KD#?@V2~U`!kxipOdA-9;=zt& zbHdL1=H&oD27V#Je8T)}d~1igL~eLmlQIaEd(MKBhUA$wT2-GMW`ge4oD}AJzqDkC zHXZ$LB}PmDJ2>=JI08%M%2>#SIr(m#cLW(t-PFA~*qYq^mE~xAtO`&|IqA;;_OeBcp-m_^w@4TcgDL$~ zE%7qL)~rGkU?&VkvWD<3~bOEGt`H0^Q@VRx?utP zs&QxA2E;Sl=51O7HhFi5nZyC|44H}~{V;g_`qVV%yJD_lDfKl3)EU-;*gOrK7WcBH zr}CQ==DTRE=+tHwDL~3Lk_%Z(AJ1IbN>jALnMhH;yuNkgFw`3Oszax&hiIJUw;k(} z^U*IY%~IDY9#SdpDp;g=XDXV;NY6kki+RygN>^khz|6AN;VFo81?p~J5p&hvq%95> zwjL5nU6*Qzf|Eq=D!;1=HO@w#RX`YirsPVzK7xF*?p;rSLt=7KW9fy^^E5egTV?r@ z*d%#*YqF0(AxcKeE!WHh|1Eu{t$0r{ZJ5xatb+lI#&j(AC>PWzWM8POPU8|ca3@9H z_+$-YVvOH>_TCI28UTj@h7s`$e7!)H(Jj+*RK_d|7;8;FS;_8X1$*dAp!$ zT+o3H#tP@tJHfydHwJvFYr!Y3Wn|Z5oYazycwi|AQ9di?WOkym+to^>WA6J-6|n#K z2rL+p+{DuH!kxa897PaNOS6g@CHJL@$c#djmB!u15$CFw_6pOgD56a7>&Hbmrd$>x zHz$}E`c%TYU5#;xIuu7*i@vLxf&pw&SI!0v&~^uT!m;-wh80k^&FIgRfm}Wvm15JN zQOGkEjhzfbWgt98;`mm%aT-RCvW}=?WVKTq!M1+=BA6-aj^fvjPU0^+Y=O^=I9L=6 sd?*^96w;ytSX?pI(RAXRKBDIb?BZH(luUYw|M>@Ipks=Er0o#(Uu-}+RR910 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 zcmV<61t0o}P)001}$0{{R3f+qF10008$P)t-s%U@fq z2LO*?AYFL?`R(oe^z`n<#nyOu{Pp$q-QDD*r2qc@>btwyfPnn=_W0=N)N*pN008K- zv+l>o(Pn1BFfi%3xc&M0`t9w`TU)|3H0!;+iQ~2iQ z{`>pkot@Z!f6ZH4_Tu8zb93|9*vLvs=B=&y?Cj25T-}e4@XpT1M@QFM>}-tEN1$3;ceb#?jb>h|E^$3{lPJ3HW)m-po4>9)4YPENu# zHTB=$^xWLOC@AKutMb*={rC6Wii*x%UErFU$Vo}{+}zPEM6 zhU>n*+lYwbpP%#B*VlY}!7?)Eudl;7Iqt~FydomC2?@|;Wx5<3wF(N&{+Y z%T!dh3=Fpr5V;o@=B%vu<>j{z54#*3>9@DsjEuJu61^oQwhj)(K0dxFDYOR%y(T8c zL`1d?4Y(B*xD*uPprF)mZ@eHN({66%sj0ab7_q{rdXNSXk$=vE`_!`|4=dP~q#>VTryVZ7f+lh(s(b4qU+QvdchwFn5%WMsM<85QLB=B}>HRaM}dnwQ6L zmA`2C>FAfpasU7T0TVaj00004bW%=J{{H^_R75M#0007`Nkle15AQjg^L?J@1t09iqb#HV0K#hk05O`S z{QE@$G6fcs;GkLMm=WS_CO+{~Cq$2F*8T<2K;CTf^eX<0z&lE_cBKI>HGssc#jUq# zR;d*8-=-LqPmW2sjb_!n$@0s9(M>T?B-l%{&Ur=-`8IghieF}T3&v?y1AzH28=nV@ z9fhu8nzaqPfrm_n0st=3EOiM0phd?3W@1j5fmSWv$zv~aJW&_TY5}fS0RZAmQZ>!m zlOIh5aA&;+rjN5UD|fRRje2-gq1fPS;&}<}p>dn#()oL(I9No?iNfR0p3dlxS*uN_ zublZxvmA9z752Gy(cVg)>3bE;vRI8R0R!O7uQ#HEX1PyXt1ayRU~jm5IAxMpAH|OH zTLnGt`{pC7o0^Gdud75`v?{^yfI4Z<^0CCNwYxg-5E0Q3QVu4d$@q??!%?Ad*cIM=fNDflr#vi<<}3n>KYMb5VX0000001}$1^@s6wfF^v000a8Nkltbh8T0E~|=~=brP;jWu=e{NeoBrki@Ji$0ulsvq9v z_r33T6h#UJ7JD>PSRhznSzuXUSzuXUSzyII{W2DNG*kThyCGQY(M+LuKM9LHqFErA z87wQwo?M*FbAL$XC-xb<_F<-Ymw%T(xB9Weva(JRFcyAZz;0g+F*%)-H4Kxj)p0C7 znAFM|it7gmN~vS7Up0$%s>R>S=ly74$zJk`Z93aeYEe}WkX2X2*2_+u$~eXNopA=n z1~{F08i$Jxm_|&T%|6TFq$BhUjgVN>jKlE~alX~#4oeOxTengoa;vCl(=s_V$O>8DWEDY;a9iT6F;C$cP()cRDGomob0S1l>p6!ymKC%wD_yM3kNdXFWn zIV)2+5~`=U+r+WtZ?HXQ%g)H1#8t(SRFlN2bE~*ibcw8nEVf+UB6-_Z^7MK5W+=J_xEnWAqg7y3dX?Vx#=o;!};_d`3eJzBlLs{f#G}~x+2M4>iKy}@((BW)^|d7|Eeuz0SH21Uh6`~QuyL_rkH;SN zdF^BFs{drc$M0|>{{|jP5B6W*PmV5!jK&PktIo6b{91-5#yA^mWTtc5Tp4`jq`LlU(@r%s=WF9l;JAApZtL(j!XFLv$cF^^B#*G7xD9Duk!Xg({cR$ zlUTnq1GnS5NytgS?W&uonDJHdXxcS=Bjvy=Dy6ueS;Bf|X$pRc^|&XvWAA6r$3Y*X z?bM3g(?8lzxW5w{SIyzg#lOPkOBd`MZ251y-;$gZ!}+5Ja5?PE8_QnjE&Dfld*M$w z?J$`q00GwL%4FAHbQ`86a~ClQ|BCGKl>uok+gDbQ8pm+MDhK_K@2 z_QY1iBELq4OU0=bor~iw%}#< zd%6*GF^H9~zCc3AIa+%SA{1A6uMm~BeXyO<<_4@?3d{@Ev;{Ztjxo@Et6Suy*veSE zv%CqDq(O6CHW`=KQE_Ds`nVV9D*b|~JEL^>bz}XhHPhdn&U5e0u*BwVezEj5 zR=KR=;CHSZSihP-zAzJ~|9TzeWq<1QmH1|zCB8bIiuMX2MGjxmi2Iujtd}K)9jn%` zA!q|jPcIc!E2l9Bb#5@qpe2+>yh%sGbjcLxuDwKGYX-*VLO$7R&w|Yh*zLX@$N9hE zKi+tXXWxAaduK_5ojlIUXQyHNv;y1bp5g2D_MCKH$GYRoMIPLEVI!Y~eMWJEN!;IT zV9j?f4>g=rLqcnYx4OxVf}kn@&lzHFddv!C3NF>PrJ@ zHs~=lR}kf6&1v7aaQ%E5?k+QN-?V}?QV7~SW5r+WKH%8qKhx2qlSHl#k2DWa8Jl{W z7#h99{Otwi1Iyu9C8Lw$v<kqAFuLXID$C?}g+LxbXaQ}?GtqED+& zqjRW})S7fQ94TZ_Vv-A zN}=Fd2x%$VlB~#}L^YSxgx^t`JC~d|M~sq3c6D`e`}S>a-MYoSd-ue(Mx}iB>BUd+ z%=F}9(M2|eZepiTiTS`fdby76;oI@e_2qQ-Y1T=VuWzjHA*|8SQO3r`XlrW|U%b1= z#MsCXnGxS%s3~T!x12`RTNJBYNRB^DdHzz`+ms9q4GCcK`FHNz`Cm`{9%ZdkRK=A( zD@oKQ;t}3tKCn)N>&e&W3oTUGQYb{|*qyP5uw+1ek~ejCcZ>I~o85G^)zjTphqkJe zsJeUtGj~v-v171p7A46ur2>$I$z&3+WN5O730Trk$6$x(T*zxi*@I6;w3rVpcPR|- z-M>ddRf6!ae{O}SQyO>HxCDhzy{mP4qfimX|8w$W8_fLjACq(v>l=iDpIjXcH^1$tihkK%X1vAp3I!HXx(GfmyXywhi} zU+_zuR=tl-Tg_S5J(T6<2rsQ{uSDIfW>d%}+!Ng?Zq_o-oTP&+3bU(?=zH{{p(iWr zKvf{alf!?nhPT>jF>3fW_FI1C^b2PE*^22K{|$xDNffFs6s3bGN(NBm8hPTRJ99pI zh3V@R{CvyL2vvqsQmSHde2nqC_47>s&H=0R3=}DeJaNRHr&iBq+Qw-p zwkVjfVFt5YW^pj=AXE3IxH){2T~WJmxa7d0q(f5fa^<^-dh=TD{IHN#Y74}FUKWfs zT{Y->bPuay&tMOEHF@|Z`*J+)I1#E?;`0*;yb;8acwY|0p2G8*7jfBf(s2*mBK8rG z5+BqqVO=;1J?Mj5xsRo zzkvr29*FWkGBJs!s!H;@IvOOsB@5R3_wS2*Bp)A=DxXqQNkK^oMyYB}NT12anVFQ-Hxgdo zOtztoO?9ojq%mNd62i`?okHxU)}@NpOtYzjU1w^{yR9snJV@}ms+Shi8MOAdQr%Te zKz;zL{a1@MoqusO#);cfWxGv~v5kxRHaZ7~#F8ejy%SGeJ4L2WcE#@FvNn$^Wmj45 zya!`S>>O+T2!dGYb2^L)O8OyOLK# zviZ_x)CRQ}ux`7uja^rFiQz-dY}x)7wsu<{2KW~q8n8TOVtIDQ0?Pu+0?Pu+0?Pv7(d~Z#g=$?aloyss 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 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 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 zcmV;80&@L{P)001}$0{{R3f+qF10003^P)t-s04sk2 zEr1R;h88}GZ-}=AFM>s6qRrOkA4QJ?E`cLRkuX!2n5xV7`1@>ywJ%ba8bOQ!EPqL8 zq{YwT>+bdV`TPI>|MT_v=sY%0xW>L$KC$_|N8s=mZ!=aL5+%?#Qy&N z`uhC$`TJ6Es1rJf!OP&Tyw+oVvC7lsm8Z%CE`cgelxu{w5;}+@NRV=hxEVi-2r`2P zFoFj#f_;;|89hAT7pv4$JivcWvCrp#1v(fhW`~Lp_+TrbAdan>UhqAxe^7QylY^D`FiObaG z`~3Z6f3ZShpz!kd&DG{xcCC@4$no>|2r+}7u+Jq*kqI(`Tz9Ry#oQ%Ik~&+Q4K{`= zPLv@=kRL;G+R!2S=oA0|05-OxSO5S3`$F5t0Y)b3nZ?Y)N;R|C*g2?T z4ks5kHxDl#H@|=&SsZdo}oc?Cu4#G;b2imDoQLS9`%Q%hS% zmnvrI=^F?d8X17u#wL`RWeReV8Q5K%=KNGkXcm@MR5MG++Qya&#+ca&2?^PYNjL}z zIXY2jmaemltE!uroV%)rrx&FT^Y-!e6B85j4+x}&H-mzKW`%?@QX}NU!o_&qA|k00 zi&4=rv2pQ|2~;pj#yl}eFgYbPjS6O&re}am%*-0Cv_`=wAQEI#4>06V4>06X4~&{c nqFL08M{1_8Q7{Td!LS4XokciA001}$1^@s6wfF^v000V6NkldkJmIk$pgtTKxgvOdxh^U}m%CT{jgQ)-!i?=obPlT^EM7Ib>_fw0pP_Z&h-wYXYoiMPJ}E)? zUIn^^R}hx4NS1Y7W6;v~gK8_wPN8ntmn6fBOjmrdUyhy;>I%Zr$+9QO@`Jh6R+cP7 zhV?Sb6>*7jIF$peOjvqZ4h?~A;K*tR%PCTg%rq&GtpT=`D$F6=3|M+uy09!i)U@ns z1Z*Z%*Ryl30vWAA@1K!iP>iY+Sc6Q1 zrI+P^4QMfELY0uEG+K>}R88mm<+KFnk4UjX%{1dH1DKfnj$7~T3mQI#E zNS5RM(I%ww%hH8_B_EU_H&Y|Ge3IQw%2|tcDul|)I-;qpJo^4DszA|U>15f!V+Cv% zjjHs(I7_YAt-wzi61>hXF!eLk5g(BJp#9!*`WcJBDv;$yw3t1y(#x^~Np8pn74}jf zeR8${P~jDyEE9gtGO+Zr+=(XR=2Zz<+J|XE>G!8f0=scSBKTZK0@gL$O%F~~Sb{9= zHIfGEYsaNUV6ks~Mct7-?gITCwNEa@3DIHcW%<@7w3zjga06qr z5T5`z(=R2VcU4ZEYQS1GdXUc;l7yHUY$L@ALvqxC<5;0yZz)*XRkM2>nv9)KvK&(S zKudt!Dc6LMsUAHn6W-<9WWaZ*#s5eWxpY)247{^RGH($)JCR{^q5B^@EGq$t$DbeQ z{(NuOXiYA&C@j4!2d{$dLhlkrJ5+15oPF}MTuXg-dF9ge?@maCPSrO`EgG!Rn^kzE zHF{3*+$qx!$q=wZDL}Fhvyj1;CCfVfH>1T&AK`{PtOmv@gbI3{T{fiRi#Nof(wn@- zF4R2vvDCS$a%eW>ob}gOO;EKj{aQ9*NQHdsLwWKg@Ez6Gxtj+ zuFxFFM$W@@4`a@=tO={0Lp*{5{P6s&v_gQ`Z2v}^yGOFUbxPxD?~#n!8x6?~<33jj zIf|=u=`KxrH}?OxvlgrxoetogF)Q)#xVl21IAQaNz}wUexFK+5@aVz~;9UYHBY)wI zb4urI#9;Vq7itP4NU|*Z-HCEUQw-p^z)`^DW??N@ZTjxOg`M8|3VD%fJSg_HwX{ja zx`HYgzEOq$p4E6>HdV<*N zYuxw{ygDZ>QZdJAb2^FeCK-w!nYKT93m?AyIZ9n-EMW6ykIL7+CZA35YgiQ4YAe86 zG&i7#ym;1M{iNbaF5#e~2p%6C8kn*xh9*6Mc|KyxR7-%_Zv(Y;i zdOF;lAadj=EoN}KcZ#S2usX<&z%S%7?%aEh+?V-CJ#`13ld@31Ym#u?uK#viKQPp! z@rEBHe7sFrq_T96+6}Fp1lW>?grXXJTbDG1Zn**9rI+#X!haN%seJ-AhfFj1I3}=A z&}6?dWbC~71Y^QhrR77ZSg`7nZ#C+95Vc5pO?#)5xULwo7QY^Kt1zs|RAo7(%#-I* zci3;0`3=pafOv9NtPm; zH^+*Xh2jRskWJJuY@K6EzJBKf)K^BJRlpeWWmz7qnw=AcQ%r#&*fP zObNbC@Wj+fL1^4c zd*})D^ExF|ni%Kt4sJ(<`{;652MVqpBg5c3eGyhK@I_?MEG!G~htI@dxC~y0H=N=G z37dRBmZc&Rt>*v5d|4WTRl7?fmPB5~t)Fv{^E?j^9_8Zt&8HYfsoY#p?ImfDj6RL@ zGj}mz{zW(rJSJo`LsAv9ifcQ6f##E@qwR{p;=;u458x`={bC4a9tcz{f5; zC^vULBuKP8KdM#A=J}e}lx*xuzJ>b_b8#Z;9u|jxD~w6jj4y&Ldv8ZGpBX00(qK2N z-!&PZt-A&pb;GG7$+2_3GU}IT=9~ie37MtF4VE#>&OhV|I;+yHTUEv1JO`5Hh#<6H zGTb~^yon~gQUs%|(J#m3ISYG{3_s=?BImaLvE8m;^d%hIOGZO96%R001}$1^@s6wfF^v000MMNklhNdri6OMub~QW18mb2!z-KzTHf`#r&*N$U7i4t}p+0Hls;98&xX z^c?{#IZMVd{HJl;3#&2{C^}XrD9VKtk;M)JIcsP^*Xbh%%T?SW>A%r%5w*jMtp190 zk<8C%jtvxNegKTS>0T$KTJ-ZN&QLJcye8es>0FMGo~JqABbPrwhA?`#bYQEdj37lk z2i%)i1B5^s_sUo#tm-9W!W~~Aj}}}&k~i*=pSiL?M}Q24@8#&eu3V>i70CFAKp(F$ zs_V(bJ+N$MApJuJ|L-8(A^#Vt2xku-*QRE%UXKXtF-F`or4qI$#X5}B=(0*m8jNrYRw0XJsD7|JxSD+dI){bso0$6Sx}r%II(aQpYxqVf@xg?kY2ODe$4)| zNnugnmiHr7>+gG}j7VrFtMCW?A}gEQpNx+I!%FH5ej=rRMd2Y?sXL`Mx!q#;7SQ#0 z#8%`H2k$xYPD1@go%h9Rm1J}4>Y8TPfvs&xBlifDeHa&pqQUiY-srO1a6_FsM(z-3 zJ4v_Z&Va8M;M>S^IqS0Pd18~$Q(H8Zwn|h~<^rZL*(Vxz$)1s$Sw~R1x8)9{IhMpw zO6t5%J=`^7VUf^F&dJ>jBXtUyGYHht8U@w8m(^x%Ij~HXCZwCvQ7tyzfIW$Ou=wg? zR8%{pZg!p2(RCG;{((#ou4dhOin3H&up)W|_9yR`&S$qlU#3TCRjG1eX$!R&a%u>+ zYqrBwWx@{a4s<`<9dY?_$S%*Wfk+FrB+DNJvKEt@`pS}{o>E+jkOU>KA`jXEEqWa3 zfvuXYD6^JfZ{l8z2p%CmS1c@k&{>yeq3y42u{?S?bcH(f59*JFQ42Bb^f2-84^$`B zVV7>#AW*voN>`+VPlykeUR#R3$NS<|;VrCB?4?*nl8QGpK&Y`w0vC zU-!q%$eHMKtPgf4?#804i;!IWWa&K3l!lczR$^b$KJ+@;3x1J)SaNlVa6`V&f~W;B zlpB;Cv)J0<_rkFyVGDNYcH!imlfwF!442>=;fu-VCZl6OM=ZOx42Mz<38hups$jKS z|94(iFUA83ymku%UKpc8`c1~)q28T}dYz28MYbU4^SsA}k`p}1kZA+m3z zArhMtHe=G+N#cI|KWD;+v9SfzS+V)C7#T7Wy^r?Bn9wl@ObbMOemp|%hKPWC(Qr}3 zHCENcGZTekiYtrj%u!NRf(d6PU|iTZ5&L+q^I7LbITDc_fuy1&;m)kkZil;x*BE0= z%PUG*VFi`~WR+!!1#-+{h45fi<(P92krg4J^7p(@R)pih|8pr4b}tOq^R6RYA6}b4 zc%H6@y9&4F_>@tSfn0NLlPJn#KeBT0=DKUj`6*Zvvj*EV+c58+dBTsu{W@)Af)T2X3*M81!<`e28l*KWO69vb001}$0{{R3f+qF10004`P)t-sR8*hO z&*c66{{R2~0s?R;DTbe)$@BC0003_b3w2&zq}A2u0ReC*D2AS%$X#8e)YRuFD21J! z$Xr~b)6?cBCxx7x$MW*{TU(=>o5u{^SXiLY(Bvc}gP54b2?=ypSD=@d#PIO09l>@BxC?T%K#2A09c;@ zBjEr85CCUp06sAQSfBtRX8=CT01Yw#SD^qR5ddds06onB4FdpeGXPhk03qT40{{R3 zv{Za@0004uNkl>9`@baC(J z>d>J>MInnzL#g;e2~t~P)Vy!#P-vx<^iA^I5AfsRa_+h3AV8i&0J+Jk37BHC6!4In zEQu=LF6q_+0n=GjT*O)BDOSm#wQlrXeOqOB? zMkZ_JXwO=TCi9WWlG<>I$x^g<7?~`o!*IElyTDSkf}zQhI&rZ?w8fTUk78)Dq|OFs zm@LI!@X=&Rovor&v@(`rA3m5Y=@58tvK0Fp|E-R)RsxrGRDvpJr8*RzyFaS&=&6ac zOhv#i~t9lZDz}aT441mMP!ygE$yx{p*(9H z!(%3gm0mMjX>9t}I<9b+$zi1frlwi054V{d)&Oo-4fU7RuQJZ$um%-4_v{KM%+?C^ z#~Reh*9-OAItjjHa#({G+^-$#mvsuCGC8cbz&q3K{$_fYc_{q^10+`ZWO_ay00000 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 zcmV+?71-*DP)001}$1^@s6wfF^v000#3NklG(NMEXN;!UqKTq05kv%oioF0zM>+yhq)YGp0+-&T_bRmecG^PKI7;fpWM z!qX`QH++0izHAu^XUstD)~!gk4aA(crJt*vmnubXyn(XMKgSI#D-3saFd8 zsQl&|6utQ->UQivUuGsA-oMYFb#(MGE&JpXRW|Z38rIaQV)*K-b8xdwz!eAA3Hy_Uv)On{P_KFj#Zv%0fcS3njscXgqL$eRq5f zufgWc7Z}+8IxMO&pMI(aGs8$UZ~{=hVFRdX((~=8C_Ee-WF7D6xeHg-reRdmuFA+@u%Q9;2_6!uu5O2znD>|u`Bz}k2Uk-LLUlkC z>iGDX$yKaafleQW@#Bw%4D71|>JeC@GJn1zG*p98;~$B--+yNh zM14u8j}IR7_p=8Y7#KoGND+Sc!GJx`KZ8XliEY~~5bsL{LQ@;13m4+@yYJ$vloSTZ zXGRC2w)O`5JT=i*3(^FzMoneyS|fyd5>qiYW4?rfXgYWh1Er;7rgHUa7gSY4;pLSx zAuI!f7?_+VChi@G>Wv#mOnlus+>VIAgS&Sb{NCQXaCgrl&R+Y~@J$oRiWNGTNH%RU zgOg(#Ze{1A>F{Axu3U)*AtCgor7>WmPPngca9mi_d~e_E#tk=j)NJ02%GIlJT}ul? z*RL~eqDNQP9T*zMu)&$AA4`{NVA`}-z$9sS@GR-YK+)$~L%f*Wj8g5V% z8-;aw-aOo}uy`SD$Ll)c?Ke+h$r25iS*OClCjF&~xqUiTtU!W*!2r`^-Ao_xSpQ9P%7Kwuqa@tIO^EqLTJ znM^-(2|rjALpG!qyWA=;TQz&^-hU#8eX6ljV3Nb4lYru%u%-}MZjg`SD;?+~AyURf1a{d^ZdmKq8R1M&E#6Sh!U69pxJBo{&p&`<7d^O6X4snA zI=Eg;!>aS?cwI3QGtOjUszTi8-^SuwjD<yOUPUkINN-XS21v(fEHzP__2@xkESdq(@AamnV5p@9y*&fS%4&FKA8c$= zFl&|+e)`D-a)Bd0i<;THsu?)xl#1fwViFH_qJ^~A-rmk$({n*V0ZGWOp{=crovHTK z)zvY0^nB&Y6*M$9LDb=KSo;YqA>nag(Xz{ULW96fn@kZMUB;qxdeGdk)7KZ3t5&fj zyPibz^$|&EsJ}5Gtjfw(7Nw6g@BsZeIiS^9nj2Pq|2?jAxbSpJ#<$<<;jOo%#_h}N zub*ZC>BfzwkPWPT?7?;rQ%XuoW?&kL7B)9GlR)<#hRFc-_V(gF86YYT$Zu1j_wL-e zGj@#v)Z%7fn!;ag?&DNLc#qTb<%aLSuK*>bVDiJABY`?;lxh%7d05@)v(M0MWQs6O zDlrXXHu4kYhH`QN;N!EwrAzgsgCi*tjpo};Oqf3^o-+rH$Bv^gI2lKexIeSVM+Z+P z;Bi=+P1A5GE1Q9$LN$m2dH82?{-{uZgM)(%(&)7YGN39X2YwXRUQQME{Nem;STkoz zutHlxLM8dZk+fvEyBjy{?NR>47p(5ouy-%Yg5n{4+6V8xEB9O_8u}Sp)7R07Bqnit z#3V__@8svBL0Fjiy7J}A(QIi8u5%KLp2y1tfBxAF(g8J(!CI-Gj_8CG=96-$){@_B zZEaDHk2BwRoTeoiAVwy(%8GS!AG{oT1Nn*cU>SJw+cvj&LkqKbYhgq~pg(b8% zZuE}%mww^|smT=0nuUt5zecmI6FeOfS#mpC9r#!3Su=%ALh-If2^~={Bi+OPr#zH8RudWQiu<%s;Xj|MQ2C)n)Ri z2TkG<$O}%v0Z|v`SN_wo@_X;eG4pI}>|mxq&HOHTxGN`5qIm9HW}eM@28i-bW!gj` z)9+EOm-K%e)=Uyho%2ayFsMdR&C1QqMQUm)n+>V9QK7FB6BC&vkqIV^%+wTEa(X7# z8l^wh<{pnKi0m5&7MGh#2oH>C6S4K4xH#1O{4-06tJbVRqlYgnO`=&6J6-_0 zApu@qbDSCn7L|8M7;)S&l>wq))Ho?vZ=41}t2FE*KIwNdYyM!Ejuphm*^nlC!={G0&AbB+tdA};XqeF04vPV(X8F}D{6hiaqes=KKxLL!Fbv0hNBCtcI2VD zyYGx?c`q6L+AUkadF^9Xb)f5*5`Is>k_ZIPNe+0VqOn*r4Ibf1a0^SsVvQv5NXLTb zR1o=$KRE}3M=}CaWfO7IAs(9zqw%3yGB)akfai3`$bGpHbKLD-iTwx0f#oWy0rPpQ zVfxzNVZHt!?9PWWCp_vWo<|NIbcd%d7iM$5f$7X;Ow1Rog_mU{(FTj>Zii|xKff(J zJhGU1Qt`5ig5r-7cw_1@Y~5xF@h4#&vj)$OZ^6U=E9RZhfQY_11T-}lD zV=_(r-zwkInUPf)A8m^Yx%ThW)JH0o5@Ryjr)|GlLd>^jED?TJBL_>gVlY=^1Ey?W z1fJg(W9s(LFzvU`z{9r~GlZ7n;}dJ~iP#3bCiDfn#=}o!=O+Xl`TTaGvAdUJiP+D0 zUrZTOq;E>1j1*Na5{JPx=Ckd>XikBeZR)zG29$f7jKypbgemi>rGqx`t zfk0r)Kk^-PIL63J3`c2IA*`c3@X1L6b=RkuvTY%z@qdc-%0h_9O~loXHsm#T&Y1eV<&izNy)rV+luE#mGWB7H~R}j`ef%u{{v?a%* zLQDkZ7adW3DFTVf;ZWMX0wM?2LC!%FO>NDO<*vPh!*I@Nhkn9U96stf$u*0ZVQ>Gv zC)Y~wki==7CB?X=EQ=h;-6#k#LwkKGu9YUC#Ct#LLxs_p&BfKy0#tjMBi;T43M2H; z(|H3=9<=Gfp$9lJ*)@v>sG~92FbOm2I_@I1{x%wK-9>L_2dZM-Q0BcCRgtQw%ZkF) z{3sN9ilfw17&YOtXuO<`@)&JY1@A|7oGEIuV$j?)XReP#T?Y6`6JuuAS!%C5XU`_BCa4REWbsI|91!o zQbl}pAc}oXqSlWWH4%GZ?T3u z>%IJ2VZ^kid(K0Dky;iDHQ|7cu@q2s8Wzc2*V{JUsg zWm&jT8ASyRXe!D?mWMIwvJ-Kutp&sP?;|xa0rpz*2nvfvADIMNo9Yl|AcxEVXY_S< z;YM34IChqB(pN)#Xduc0tx;gBh^+I6QNq1|`}YP>-_U_&%g+99z@n*-o?aA&NTOYx z8HY<@-iQnEKwS(6d10QoXs&_F>G8OCcK|K*)d)Q=gH%5!+_-iH-8b72=V6UlJ6%Mn z??s~gR>aC|W>OL1&VpN-%uk#bPcm3k{{DAO6qU6?BdiiX8>c|h+#j*_`pD7Uhjb%x zWQKYZEoy^rbUIYz6>$8FKDe>js4l&XoJc<;TB#vHi4SqIyfBj3izBC$p<(KblB%}x zH-t=9STwQTxX~CP;e|NiU&|I6rYmINJKZF3l@B2ItN>EX6`*Vrh$RxP*d?Zpl?QdO zNX!k#bzP9=V}}@(-MFO2hh#ND{3z?pR;}qGMMP{V$r8-jmeKzSSerJP;9_6|(H<`H z3QF;V#bs83m@1bJDFqE&Qr!W^LmQxZY!8G^s^FC58Eg~N1@Cb!C`gDQRF01T-H9}n z9Z*qF#OrcN_=jl`a>|>K?&peNCkF@$+Dx#OeG-A9n|^Jr!%!F#gxU}%6uJ`)OD)E7 z!$M3U+VPoOAOclHk)iTCinMpZSNs>GstF)QNdSS8TT!YfgbV^RUsC{9iegwG7miQ$ z@)4L_k1Iu4s10>SyxwUzTX0>#M-i^f<&LUsu8@j-o@A4*;I5UD7FSQBMr6xCoC ziI?aq^#(a7gsO=$SOr=FD5hruCr5)HB|3u0I4cCrGxAuh5RT3EWk{`RM_EcVN)p0R z7w(EI0|~gPh(YXx%Q(BGCkd?O%QX=kl!*B7Afz}OBhum=oMlDfC;l7qiH~Zms)S|w zb@02CLywz^iXa=5+sdNE>^Lr)9z%(hB+9)F;ld4rkW(SfCtgGI&D-d1C`VD47cP3c zA%Ww7aP^}w-ntC#dOBFU#$fWnqKmnPh9S6=k&d2@b|i!aAuZ(+VuC{vDkFf{6F(tB zPa5_0ZRjIO@cjo5@qn&A_jTjWtybLWXvJV}7arUnV%3U%5`#4~+(dCs7H*biqdbbF zMxo(IOGt<1{$Ie^{5M$he+?^B?h}9bngr%2E5kvIpBG{rjZu=BjNTj9k)50XPjdr= zOYTk)YXC5M|SdPmW;F~;yvw9Xm=Lw`l?8di3G>d7LFH; zU~i-gj)ejI>@Fb0!xjD>esFUPhK*$?!a_1}t+@rQEv<-($b$8SAUIlb;cepxFMV~` zOYFvZzEzN0|2a;7|1tD8E`lNPS2i*vUbE4FpPUHMEKjxvWYYMo&p%g(r#%N@TH-J~ zxd$#Jw+pr=fE+F1?`Q>Uy|W}aR)sSGV4`>$_L}ktwa`JJsU}>s<-yTWfQzaGoaGO} zTW%lJcCN$u-RmH~`5(~S_BCV)EbYz9a6xDdti*+3t}c%#Lj`cmEwFmE-sE!dRJ$a^ z-C(354>L(o#F?og%S-`WrNi(rH$_$hFf%thN{|We!8@#6GC2dOcC9 znIxeDot0edgJ7?vfY=Lah%(av*Tww5|uksPNC?12Wwh~;;Y@n~rft;Kdd|mwzV`qR!Z3&nh-3b?S2NteO z$~cQ|6WFl9hyV+Li>*Ht6*yS2;@sF@NGQyW8%-cC<_ZISe>jockmKYJA1C6Y9KGRb z=gs_;kscS4r`^EIYlg*(RhU1bnoFI&l%zWxEPUWBiWmwwt ztd(vGkM2RGTbt-#0;$lwUUa(@O~jwoKHaE7XTR~=pM)ivSL*+U|1aaq{{fAqyQ%|A 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 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 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 zcmV+b2mknqP)001}$1^@s6wfF^v000KZNklKaO&bEUT`R&$W1OqDYT_@=R8l#rCgn~l=eLd0p7Mx-~Kq~ ze=g61>VtnA4k$JxQ!5A*SPCo!mYb>QD@dil8j|UPfM>@78kGWTP@53}EQz25F?%X+CzI<7STSzUX+!U z1&hTZt~WF^VBfxdXm4-FG!p;GVF3;+*eUGnJy)(=5#Ho7hr@xRM~|YSq5=^bV7^fn zs{sp(3(x7~HFmol2M!z%-qhXh-o1-s$Bqebb0}1!f&&y5dr7*prMZ9qzVIT4+k~4# zkVCVnsY!sE6B(eejNODiFE0P_^_D7IyZW^XJbG`276*e3X=wh}XFiLcqY9M8ZBb zLTb|f{rh1unFf6Rg9i_A=+Gfi;m;!7iU|o2SYlR1-ofQSSy@?Uwj0P8(!qlVQCnLp zv@XpsGX0|0VU48^%VhuqVo>GfASSb`n&$DOGib`MW91I4l{TNmP3HuaE z|6B%evB_nDyUj}ic5VU2NA-SveSN*~My~kTv*JVj6qc)~Wbfhvl*<8ki;n?zeGF_~ z4QyWz=-irT*101`j^N(Cdt!ev-OK%=*I;qGlA`ZwkSi-IdpxeEQX+E$@X|s+OCZ*- z06u&}{@mT}+_{6}$B*+uVlZgb`XMat>!P$Wl(=c;Q0kmM(DhAk0&l-Y9_Vam!YRbQ zSO+YBp{Mt7rOzSEwHeo2DUp5#i~G3@XUOmv=IYg}T{l2?u!4Ymww6NFt=JrAl=}5d zYR~HzE?f`^LgU<&4b}J&tf?gG+}H77kNZ3x^to)&9N_!S)tQkzI|IHj0HyT z93=5LES@ZIKgTW0;lqbfU0p4%k0Ej1u?bj`HhAkILiI)9^LM3|v2lXn%?(Z%v-Psj z9o4WHLMnZbF(lyJ*X88o2xy`7!M3geRxXnuhGLu*H?9QUTMh(iG1V(oETQU41BuA zfjnBDcZl4NEsY9TqRBPL#fu70;ko1zZ0MuUds^cJ!HHsV(!V%$@`UIY=XE;}H!5K9 ziM=?nCI1+W6>QCrnSFw|$)r^<_NrX%=CatQ#e;T!(0RFlRMg#EM0KAvW5x zS&BzHtCsu?bA54-#NzX?T!(03tAf$I6Y*mkz0$W$jxiy|_A|Y2r6?gLhWR)w7FMAd z>c>F!4yYcV_VJCL&ZOj~pZ!R#+^oX?hz}^}hiPe-Nj3c-0(spz%+r z>V8FPq(!trJ_{=`q64v(T%cwb6f-#M| z5vNW>sePhv!D_UJ<9GWvbk7~#;HY{*8XTJH2>`1GhG|uyNoDXo&oVo7C`TH6&W14= iEC0!=z*1n1xcvt`R7D?RJPLIH0000001}$1^@s6wfF^v000HVNklU|0-`VSQ*=X(_)VDZY_m{ae;0yh4e#7#o#O7}oo;IrSk54xPq{VlBQtauQjY zbqwoWS)cSAnW{S|Ez)7{u1olG;{%436#tsf+lS!`Xw(JNw;sLse!a#@Nqm8KXp$bqO!AX~QFAXWl?w?gbR) zSD;99f*-EAJ1)2A-X?h0@X{8Pq4by%%H&W)&i8{n+z*PSld*nX1oq@4qNp$jhYxBo zEjSFpK?C9IBSVHt4c*a$$Ul$@ReB6!71JP}I~sGseCT{U4&^COUaCgM=AZQki>rv^ zy4CRabAnjh32rW3F?7gBe8hb_LDZ)+h75MV7=Kq-ShR(=ObByx6L?7aL+T+$F9#d+ z>1~Dn{cQMf_c@8I>AW+xsAizNM8j8Zi^ob{^9mFD9! z4KBm%;O5#DPEOV^H8qBTfdN%jFT!dABO^oD+L~kVAX`Y>?djgG{O7%--Qhmmo{ztu z2vw(4G#T7)`B>aHURkcDVJD?Z>GoDE4#Aa}(l=MW!i43UH8uX)Q?cZI4-8&;;ZDj^wj}A0+9IDmrJoT>oXMO2lFUP7fTR2Z2*oJPshpQGjO{;2DLTwa6}!< zqsqgFVOREe0-wNVTgEsz_UZ^58w;8dm(Vgqht!lOdVrO5pQ>dM_T_#-4;h4sfzH^! zXCiLaMWg=uLR|hK0#`51$F~(>Xt=us)m3w8DG`X%--O}b?ZvowUXGgY<)}J68+AWM z;l{NnLY#tGGY1i(EAsZv!uHG%T8mY-d@OEZsikkewq^nU&=;yBaEqY0N*Ob{H?)Nz zc=Ay3CZ)RR)VHkp;rPpgWpA!?_spN?f<@8ps5vLc`S0e@-;(26?E?ICC6V4M@9Ia- z;@x!Rh7B?N?$Lqxa!)Qqd|0(1ed56{qgV$nkA?6L#rtYm&*kD@ZQpYT-Rws`C<4M zDmNh<*gq9{d(P_DyP=Az>r7=lkHbN5$Es7aaqMU)HR;L7-W~weCV#9?@j)`pdfd#X zOdf^wR9|e(7)w2GBJvMT!71uxSE$EvXdG(&^^_L7J0W=APJu?PBa{i46xf%4ZRtXb zP74bYn3%MJaVtX@8XCZmR`3Q*!d+`cziZRR2xexjVP(~pW!(vzri(xS=7KX*J7#71~SPYAWVKFR*#jqF_!(w4r42xkg jEQZCfSQr+=`hW5}b2nR>xtjOh00000NkvXXu0mjf9y8ue 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 zcmV;>3orDEP)001}$1^@s6wfF^v000Y@NklBCa7Znj^% z0U7WQEOyUg&wtK*`~SX~`DO$Uzar^iY{@PM$G`XY4E7e)PPO4LD8c-9;`jwc?POmR zHev&;4X`%A+5l?VgsySOR!2!sM_lTEbUj(NTVqzRRrm-PZkS)096m12q+SK z8}vX>si-)vz|bY#=>RMC*$a%8EvS_RQvMo%|WRoYl z&nn#_rKnDxH;&~gmD^1k8ssgIXTkkvuwIqqm%7OB{2k&}mY9}+W}fpOb=^0&=fm+6 zDN3_ju&k_@e5haC)RpzCS$T7sr$(01Re`D~1dV=9zMIAmR(}wJq5#8S^hkWSMrHDC zLbnNkswmhk{k{~td?mu%_z)4zKnRErB$&u2@Yg3;lF{TWrWW7_?cVynV1Bxf!jk&S z*2PY+4ngi4VDD~W*H@6Tji%#!WU>u}xQp5@e0-r78JL8jL`rbH0JlF*V_j;N3^8Pt zbJt7sCQZ@_gNxG@`g$}fRh4|-YI(V!B_R@pZM!R+n77f?7*c|sSmfb7^?Op=j$pB% zapiiEO_>-mQ|01Jl}(0iBAwn{Lm+n@IL*)H3en$UMWEl0!>}bC)XWV&~ifNi!F3bK}t!}3Gk&#%V}4a za+elWDk`CGf(nXIDHe64wC~X@$AN0yXTI1XYHA3e+VoHiwPQea9IT{e(33Q=A{t>R zDK}g)$p|Oj9AN%tjV;4<^8F6UxMaDc@Xk9qbP)pyx~ejMIZrk_$>@lS=Lr@H8q-rT zCMJ>yKS_5YB%LtYBd03J#q{vaZS zX?W;yg@*0Zumen8!D~5;9ABnqvaLsl3VW!XOpbF*Fs|3vm39{%#db5g^-aS$-HA zJ7Dy`!Rdn5_aSu-cY*a7m}!V_2j>uk3iO--<@cd9c3VDiE1&q~ zKp{HG!H$4sG@)m@{eIWn=pHxXARaTkEH*bHi;EjiAk`< z8U!m;U}g+_6JkZE#z1fKaqk3LZQ7FhFOAI;|_ug0q%-I?LTxVD{Hi}5K!-h8xKS8 z1Vrax+cD;cuaQ`>cq+&du1dVCL^(3>Hv|`Saw`{^F79X3qrc$GJ*V!QS=Hg@WBWMr z+TSt!+Pf?*Zl^jt#QxZy0UzEmd_)XM{of7ZKhr;ht7~PRWi;E`mAeEU^!pJWsr6tJ zpxrj`hSFeZag3$KeSl&%S?RW%)sV z{MyUZYAWZ?NAP{Pa@AmYS>>I#f0Ga1d--!8RWp1X&!)QAxvAq9TUlNNK6(LW_CfL{ zms6)XH#A8-^x0WTVxb|CfTSyZh38)Q4yp7wisDnPLa8K}pUorX1f^mY&u^e8Efm!u z*LNNvJQnBowrO{l!I#XmXqF>{fzz>-^cTO}2~~)dVCyNG))WoZXV8h!?Y7+k&$WqL zSEwx=K-X-2NqCSM{7f=w5h1u6q7ABRU^Du+eAdFRCJFpbb0>3-}2U3g}2}QE1<#*x?l0mtrRXz)DGAN(sO&(8p6TPouY#Y%u{ zcXns5GudldIQfS>I{JTXi!4zLT>@QlxvPp4I){5_8A*MQmh&_-#b;44aGfsZ=6VRi zCPPC|t4Ye`2-V6U3$tG#+dV~o_!XXb>_6GFW0Kh`n}Bw;#=0BKPU&>Zj(UF`0xMLZ zk|SA8BexfLFDOzp94ZNqUa1mQ16s=-R^%KbiPJpsh@|cv=B=ZD#Nfyg=0?U@m`ii& z(_G!XJ~2+$5(T=irn7%H(yE8`lTX zWDI9g#i_u~FV#4HvO-T^65Wp@1Et*?u};BC20F=Rj5*Q5@jU)y(C6u{5aWO0>A50{ zFO?WNDA81yn9^t#0(y)S^rp^%Z)3L{ZcghQI#^;nS7++`QMO)dVfRA+ANaiVVu5S3 zEoLY4?GaF{Ec?Za{S}Y@%s&9f59Qc{bMnP4 zHpUO2@jA4kAifS}3My5>&yQwt%LBN9PpQ$~RQ3Fj`BDRu6er#sV)BMg;%TTI2D1## zzd-34Y|956e)2{r>NVNvlQbROzOOXl{mKL3p1n$_T!6#xKz|*U5}*vjLP7AOBmLAD zx1b1tqZAm(DF^|nxQ=30u>BUAm*v<`hq*AWV|*P7Sx`TQ*q=h{N3bp9$!AuoVPm1b zUcrL1b4ji=b|Dm%_yK4ffUpZRt1ZN;3jO&yBDRQ?o1kx?Oww$hnlW{iTslg2c$~nT zBhz0Z7LyPIPlvPs9We84w_(@|Nn9!7`|EPULSZ4;!4{#n{lPqFe*ujdxLSq%8Axc7 zXAV`dohpT8h1Y*Fh$Xbu#kM5$q9l_phYwC+nxHD+yI}tw6sJMk56OKHF1I&qAM^rD zAv$KpcMdF}LZly9{=}TKAhi?rIjcvFRz0X_1P1y;UU*@aGiPkd^Mh!yB9A{l$LNSn zG;;fb8G3*B&JBuNZcLxrSqc7?9Fx4qPB!oJASw(F_;jZW{LhbV_J6s-$k6@0mQL^D ztSPWQE3BC0tIt+Qcd_O|V+}^8hcy`+=ioQM+5l?z}konur|QzO#DC9i@!-U S)6@3=0000001}$1^@s6wfF^v000g8NklC;T>89ecB3^yv%z&o4dO(JM5b@jV1qr#v1k{iSCN z7B|a$c{q#_QO_JKf24!u9y|~~S7zf@etZvD-QD1g)L5LWor%BA$$;LwPY+lR4gRK) zH5A62L>OB$alK(OZtlv1xnK;8qB!V-`}csQ#-RSN7L9>jp99a~@i5*T1^XkuE=hvD zJp&#_B)ei9jIq&waIgmSdwf*mXXYy-V6PnqPhA$Q_mW{`4u$tb9;|tzU=+l{dvrXk z#RQHwuJ`Tc-um(PUg+~2f)&*t_8W8~ZOC&3mRQp0!7y`U5ZILoy??*QUr+ba-XS*;ENx#7%$iIX zALhc`l?A;v3&saI&=w^@n>4&z!uP&28Xmf|o;avmU?T%8g5z151Z(z4bTrW_qWgDy zQ`*o0uy$s`ccuW|)&kfk^VO@TB_E#SdC;wp zl(ldSI?m+7lwE%CI`WE<91Saf7>u}q(4z;yO;;3)vYJTfG6hcz)CUPd<$ zf$=uy!_Ua)+n)`SuBP+5MqwB zKktI?*aYSNo@L4KZ%>aWJuM--I-ibyYSRD@;6XGLmsaS_%7X^Tdy(V`sn9wZQVu9u>N zuOdp-H6(o>-S5sfrRXFO9$Ks(AEm&#%TrQLVxS0qkd4l(#p*gAUEki#O`v7lOOs*E z85x0K#STP(MC_&ME3TrobbMNb&d;4i`00|0Lmrr4BY?g=S?W5)Uk7qvP@_o@2!8Bx z04E3OA~t*7zZk154n78mPF~Y_)!CT9$Au~o_{F+-KUQ&Egn`A0#UebMTq2U^=mg~s z9almNpk>|ussy+Bw=L1J_x(wAty z)V)m}>mc`u+smfN26;~rti!qR)8Y&Pt6$&9vMlSh1b8V3eY`;rZ`MmUkee6KWY@)= ze)+Z(U2XYFFWt*^8r{W1%aet6kfZcvW8tS8i)#s3dNskKtOyHJgDx6iwEZPKC;3hQ zQ=swp1Z4TXXMHNX>&JDUQ$AI&WD(5i{PomBhp2Jr0^$xqE?jn8qL?fyJp95x!Z%dz z_O=ZCbfOgc2^Z|PLbSJ*;@hLI;Kx&wV4W?3dCG;GCrV%*ro0eA%0<}Rn0x08^P0G^ z0B+GWJ><>cNebJkBO(thb50_oAa{~r31@gY1So&V1;Y!IzpV|aitIhqN*50P1+T8D z!@LdK@Xn?x%ve_o*UEa7mG8s6joYz+|C_pI7iwyj!TfLn3`RF&$_R84l)GSEFL9#p z#gGmYHEJz;r$7=n5(|@8aT&1&qcV*`k||KkK-}10 zh}YKc#I*8Sc>(%EM8)m~~RMkY}GXE2o z4R3(5DsrM&v&sy34*{u*31{_+G@V7lwbxKPZO>4$TM|{enr246otYyLN ztJ6_lwGNjL&cu70H{#mS>FQdmtq3bBEAal7jo4Ye6g#Sy;pOr@_-gkQ=)p)DwI3^u$LS* zk7*56de=plved3?#me<$Q^dMF^Zu$aVpDIG+KcU*GP z9S>bX+(SgC@R)>m(MGaZ{d^gwSJWv!I#}~ItWX_GEHUsYT}w36l`>RSuSDUhdVJ9| zQ{{;AeU(q1b407Sp(uA5N~#bv$s{DJh{&pg6zBvfsUBGC#yL@ut{@yDfXO0cp|ZH} zBL8*zWw=%~VC1rsSiGqcKQ$HO(}o#1zIPt}**FomjuG(BO7M@iX_&iyJ5Dvc<$Rx; zAr>eg$tKAB3S>#%>HboA&?#*HuU6UwvKdk+R2v||l-BF5&QQ@dqKkh9txHNHQoqs|t!(CIQez5(V~3Qz;urq^+oq|Q zyP+DJt5@U4*2$=^TZEY#YO%X^sp6-)olDWMdogabOu~k(t8sMid^L}f3bF~DPd4Ii znnU8(xnbJx(Pw<(mC})el(sRYav&9jg=3r+7hO^;LO4qnDDGm?UCdS&E*vRE%h3{? zZ7fFfN2NGO^!UJB#PuV9PI zdC7@B&g+#95EFDM`=~OIbbs1^>@)u%g4f$!X97U$~mK~eLrL<>Wio1(uzrNZ_mb0!=8gyn7E!cZ!|3K`a|_^ z`Aj-46{)H~C`2Ew80b3)a7`wDZZ5#36UF%MR3VImxp1#YL4ej5)@G6qTC^^AS-exZ z2)X~IC8b!nX$vkNnuUw?v#@E#eEi&ZK;*aY-D9I+@5+LabbtNZO`r^^d4&jChJawp z;$InoAk!eRG)vS!!4&DC&q;*7B$>+H$^TSAP)f%HvVF=32QXTxal!?or3juj7rr|_ z1&6o2g&z`zMLt;4VU}{QSIWru^bFl4@v3pE!u{ut0=N0z?TSP4KMWCc&AyG}Vl4^NfcXB@ks5CJq4_9TK_Gt;w z7bn5j>_kLqbVzg7T9tyYk4#0))}{E00`}s8*=XJOx;lvy`C!pC3@%C~wygzsOEh9_ z65`vXCM9JSvAW(Avl&C-ld?{z@fxnM3LpZkTC}7tGG3yEQv9|{or>-$AO}w6DWR%V zJ->UiW<1*8NK&Ps$OlUfsFAQmK!%$Ro&&i~9lh5hs;tru`^4!FhPg}*hsgH?I1e)c z?NPc(qAR6?89Lu(E+Ie@$Ww9{rT6o4K1;eiBxWfs3mKaSxzo~~@AZi5XjOq@PVGxl z7&Ec8Eq?k9*ARl629@DB=>xCpt%ofGp&&Ji_l@rD8fLNyygjahG~n;ov< z2hJ6!TA3FA6dxprxYNp#%SrQsR%dQZgO|CV(q0l7843Nucbn5-yq643IzmtR-XiiG zydr3cpzFrL*p{JO&u++3T1*ap+;rVSbJ`yJK(BR;z$;lDj8 zWKEI&>`p{HLKg|6yGQ7H9?twP9?t9;Jo5QS002ovPDHLk 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 zcmV;A2ypj_P)001}$1^@s6wfF^v000P9Nkl z+gdSBqc!o0jj0y`f<{n5kV{w)7P$&6-@e~-7k4=pL>l)G`=9v^Fth)e{eR~7-uwK2 z@4<|X7h~eYa9q2Vg6e7|3|He9W6T4lM((h)3&OT-=a8RYVxXc6))Y1Y@3Hq`*bR;F$kH!cjPPQ{_TywX6bE-ZEa!%XoxvxPmIiSBRs!pG+*?%#jHtE|QVt1c|c zEuPb?3qEF}(cO3j*#gEA%p&3p?8&L_~bcHRG89R%5xz7l0$I2i{j{#?-0X@ZEPw?M5?R3YI#g z8NqBIY*m^O988+=w9RWqi(oxp0E}>gYlagWFEnH9*f4zcRom8#7Q@oij4fO<29su3 z`NG@#OC%?^P0e^^SelyQ%0>yz7&0UjhYnq7bDGgAu(UPfZIxztV(#31xOFS7&1goe z!cyk~v%qqdX4u*UAw2vW$mP%40W4!<#F?1j$3cT|+sX=wefl8Q$mn^R(2Or@HNyi_ zrbOV*f?cZPPvk7m$sZ%p%t2ATGn9VqOG8ScJmC}WkJZ>Cv!^08ZrHdGP>F(}OK5JmE z;m|5GGLSTSv;cM2&JKCIb|Il#H=RTCD%XrZFdMEJj$AXA!OM#@BS$itp#>|Udv`J1 zvevA@y%8gzIC~Z~RaFh%s4OjoeD7Y|8$KL`$B!d->sA4{Iht{XYla)w3~R0#g9nG; z;K3_UD9R-^!SgDi;Z>EN5Ba`*P<;J$qc_%6R*GsOpj2ha4h}D^8IfEw9N1LII(fpy zB@!_)`mGuDRzs?;!suvGQDUCbaS z$;qgxsL-0+m1Sipi;cx&Pfx^m?b>QG`#m#5DAx=-m1cbU=?Of3tUsEeQ%Y&VzGG{P zR3|571qC5z(ld$UiGRW&4^|)t{K1Kn&Aouhs}tNzNHtM(W2L#%Um@7 z#X8r^*#^@|nvlcT&tc8xqeBNz%$~g)0Rewk zUZR=tS7|6jGTawX-M$chy&vV1w^FCu<^FXHR_=h_4Y9v74U>rjjqcO7nWPuk3Z z^)Oz5Nt43y;6bjKh<~a`u0=z@HnINP#21LJ!o!Doh8}!Vz%~kC3&n0vN{YOl!lIs) zN)O`9&4os2YK8ju8ywj0*)IgJ>C?9(HC5jJU{UuW)!7;GojT!1dwa+uBN5-ZGw$^3 zhZILgtp*?g+rS2h9{h|MJMidHe*1uR+tN~068$VbcMe&rS0g((7?s)CNSQekMW;?7 z=d;fQEHe1`jvZ@(trx%+;e!u$A}uYy?ZbL7VFL1Y?7+jxlTmv8I;s^4RLS|!QYulI zl_g%Vb0Z=|zbk+Leo<+f6-J%WZXB4S;}=LzFKFAa5_KF2(xDG;_r6_Z4Xvr&z?wgbw!$+8wyXHX!2%yp{A}w<|m)v z(Y$$LKGOt7Ckfv$`4~=4yG7rMvGiRD#S$y8g-LeHq zBS#7^YrN-ff7^ENz8l1qN!ZhPr?Supwfzb^iSQ$jK>^CM@zL zdSKds$UksEXFr50ieP20UynGxiqcBj+%_`-TcrZFV8I{pfJB=Y{BPR*A;+S<#d_x+nhDIG4TX#YTeRo^a&r~Zghku&v>iqhHo+*q zcoFj5yK9ZT^yW?RH@!d7=^8Trmb7=Sa$qY|z+7FUIIv;~cOCeUq=O-M2M!d~R(#|PS%iHwZ`ra0i&~AGPB>o&Fvua@`|Mii+Wbnc~FCUFlo~BJ{Dg!Z_8v&p3!<4 zV18_v0Osy~NLoA6x(>Fr_CBHJ2^(J(Fb|KzdI{|Ra!m47PCona?;001}$0{{R3f+qF10005AP)t-s03mQ@ zTS)z}4*#+Z{;>{>ePD5APiI?52q<(GG<*Xja)FV!D@KS8EqEO}fC?&heU7#(M~D$H zdc(`zTX3TcEO&m7w>wpl0U~gBiL=4U-7H6lE=h{$>++YS#ek5w&C}x>IDZ8ua|I=H zJ5-VF@b&uo{Lt3rhnBqa^!TQ-%?2iO2`P0KHGLU3erbHGHBXKvLWRuI;{N{r_4fJy z|Nr~^{rUR*{r&!UinBCMjc+bZ-)8ggn@!a9->+SSveyj#3 zbO9l7EJumI$J=Rrs|F@?BtV36gs|}P_t)L&XM3t@eXFv()ZgRn_xSs_z}GZSjbe7E zn5D(Z(Bb&``{(QOnWo0_^Y`WG@KI=;1SE1*2Pbrnoxm_li{9hy&eY?Tqr~0f>?uTtWO%5euFDcIdXS&NN@1B4GkbQ3vTJ^< zJXMhbBXKfKj4w)y0002K4f_oM00B!$L_t(o!((6=1*2dTjDk@x3Pu4<0VDMQ0~7TC z12gpiRn5Y5Dhms_e#8tyRyH8PPEOdP01i$D1}<(M1_oX}iW3OGfFOgAun2>wm^j5| zNk~db%gD-!$}1>RW{{FH1A~gHu)LbOhNhM_gN`m2`DW=!>Khmu8Jn1znOj&|S=-oB zV3wVgy@R8ZDF|3NyEwSIQQ}E=4^InIC{XqCrYPWbe0=?2X88vga4M5+lwx2|aEK`! zn8}6)2$5?RZ&-LlWE9M(=ok?}UONhW=@t+RGb=8hi=qUUkeFnV3^6JtH7z}Z5~DJ+ zvU765Mj1!s<%bp&l5bW~afwc8WLbGsMWuNaKUZ~04Q2T#%&E4nzQM4ushRScq@}S) zC%d(*885o3mdMPe*K*8PD4001}$1^@s6wfF^v000iENklTH2m@=lsvW%$s@l{@!=L`z?<}PMnBb z4-Zu4<)QvjUarE03;q~8b{7l{SAsF;cF~$O7f@YYEv_NovIPN*Jy&U8eksncs;b6{ z6&}#jbLLtBiDU)z^*L1&xxm=i1tun+2$`CG0yDE!LY+FThPnA_SXyqt zs#WI^6P1qg*jUInZ${YQ!FV!Y07ClqMM$4Mh!`^l>3jBo(Le&t%@H$e7VP z7BHGra&jK_?eoIWpryl#XcFCqpR`wt-e_17UaFsQkv zM1m)lmPlE*4y8|?pt_<0jL6Xk0$Qg|h@LS6c^5A>urwNETwFGG?Dz?tQ z-MW2>ci%mMTV7$vkBmj;!Gj1NJ{(d*!&*oK2O{OO&rli?f@+mY!+;e%_7@+J*#+q7 z)j%^dL-h3N$UT1^s@z;HXqA;!h>Xm@rcFQM#TPfW3fl8v1)8=$EP}LP!MFJ3Rygvb zx^szPX}9P8Fy#DD>=!hf^}ECQ9Z+>l$jUc12R-u*KyTzC? z>0cNl+TH+Cqt?M9NbkLO3U_WtpddC8Sx1jTHhQ!cq>%pok-TP2JxJBa%q+y|(>|Cu zagW#-G`~wb1pPtFiFSt1Kfi=0QV#7`A0#eair^PssD)-?f=F9i?(8|gxaQ}X^xU1wRTCxmj>1FIPhJ1q&GWQxI z$U?7K%diI0I(+!yX?Wj_gd#o}Stm{)!p24$5_d7=YU)EuODn+fpiC>Ck# z1A_L_OPjE9)Qq#MrUOI({4#X=z%}N=lTt#hvN=`QO08 zVokGPNsSq|WDZEeUwi6tOK#d{5hSwWMT^eho_7=q6H~+uj2Jgg8xoI~l3ZL+5)hyT zNiHwIv19izX3W=k76&a+5ZBZR1kK87Gq!BG0vU&vchd`Tj*bZG+O>9)d|+hin*-iEVQ7}PLp}~FqA1N zTF?p#O5y1#g`M32vBX;oi@tZ|N)R4Ah(=MeT=-9N30jbN?3ncN$0&aMSPK#b*KTgN zF>>V2)@3BGzPeRd4lOzIRpjJpckJJPJ^J-qkG_5XjNZLJN3ULg5_gkcX>A`mbO&j% zaui&GSQB;KTm>87F zWFj>v2@DhiE`Ic=W&>#AP{X>zjB%I^gbN#m_t|)?x1*yZ=Q=Uv zkLb-l;67o6kh%FX?A|dJyG-|{8kSU_rzds>jV8U#kw5oC$*wY_gLW2-ru3K`BKeirpC*$oQ)CRYt8j2;EkT9-)dm3f<=(RUX>u` z_XZ**iFY(W_%Mmc?OO6XzcNGNr(XN*0-QQJ0(XD5hW8CC$YXi|#hsCp)D!n_TEUyY z?{WGyj34_pM(WMQeP-0cNiu}LCPDlHLlpjOCi2ZNYe|ctwa&8mMIwHukHVWghb!!i zQol}!p3W0qgO*+V7Bj`m>|GweIg6!t<-!n@=XDXF)KV2#2#Awy=fl9j8H3nD++dwr zHc37%y0K*eolvdl48=7w$VTXEa?XGIG9)X?+;0rUkEX~wYpRvQHCMmzkl+>OsPRcX zx-Z7XbHCMsb&p%tfc}d#=e)$>dNH#WUn{7q9`Za)p}1lya?h6D*fzl;KSg&Z$XrjD zcu<#?|AsHq#~W;>282cDrMPq58dZfB$V%)ftSaQuK=kSDq&a6In*|?c(Bf+acheKD zg2qeZuA6iIM;v^6Xd4?0Beq-$1X&-PJTd}f$2el&S2jqE?u|ERI*88=*hkpF zhBg}A|0g71MNOH4j?$wkGcnb21@w5{_0IfRSp1>A*!c*KG2i|EFn2aST4E;xR9j0I z_)htKhmDoW?2J;Cj#4Gpz4f~=itln=%$EpfUpKa*XrA65p*IiSTiwL@9X4K`Ua^uA zWi{2FNTsEu3Lz@usYU%mwWuOWQmHE2C51@kx#3UD9E+Ka2t3Vr%45ivCZ#@u`2S99 z9Ii48?cA^p2bICqb7-m9)T#S1ZQ21*I-#uG-rfx}X1HPI%tM$p>#(Rpz4@j)=FD-2 zgToQbn|Dk!B_BM95w)ep4oYyT8mUgEiyuA|QYIy7m2K)K(tbSHyw0J0%lhH~8-x?A zpMdDcBn_$#&pN2Wk^BAk;@q^IJCXj?S4iKrOLTImN>Qn-fUj>HRW3@#pUlJQ*X?&DD%(r^hA`MohU<7fshn7WC-NjwxK*e9)*fBQQ2s1J&TUS z#2nF{s>dDGm6f9Uk+x%p=qgL~^=msgmX=6$bwyEVID!Kc@yRED7p14>&R?iGCsW;L z@#3?hAwlL?|DZKpaP=x;X3rLlTJ->fd-X!1v$LpF78ex5%PSmjy>(1qFN?mDcykCqZ3T zd;5c;RZXSX`bTwnIf{LKk+66%f_wI??I+TMC3EJW;QDna^A(~BNt*zba2xAXHwKHW ziE_@wC59;RF%wPsD@|8&(UA8ZI_A zq70rUi^qu*_r>otXyyDMpeyM4)7JKuAcT{ajr8%(FqGrVK;XClxzupp?L8H(T6OplR zA5Yg_tA$4W&`Fb!F&P|KbaTT3KnH+afb1@)X=DQ{_lcE5OmC z-lG5Zye4f+U^O>%Xeq3^(5Uq%dY=*L&xDrg<_6iwks4^UaYZ=HdkX968Wy`now!77 zvHbt^-MdlFKhNe%^~{-nV%)f0&1-l6m%*yvW#72*2hrxE$EWH&?kJkX)GSs%o+LFf z0aK@H_NTo8;tl zlEb37t&&IfoB3cQ&sF-@JH`2=O`9&Yc?={&LuU?c16*A%a%jn@P%6c^s;X3|7OFr+ zY7(;C-4HopELiD{p~!PLMD78mQQjeal-(VH>XdhYw1tod%)&p855@TdR_&sPKiHsJ zzEJ#j+3nYnf5hmyD)SyG&L;)mv~9PskS@4c!^d+B@@4Z-8uvEJ6C9yReiv1#3u>+@ zUjWq;d%g^#_)65NS<=DM!P3Ff!P3Ff!O}?wO9x8_O9x8_O9x9Q9V{Iz9V{Iz9jyOT Z{R<4+@j{eNX|w001}$1^@s6wfF^v000X1Nkl?SN zF}i7a*N{f)N|IiLN)OVzbI$kf-`-`yDd(KB3a7QUb)0kl`~UxaetZ9(`!rj#CKLq) zC6GUP0xVw+v~CT#PG`sh4?YVG9jW%t5 zkev;fn1~1L-5UrEg}hRVD_1fwe7IYk!0H2w<{dI*10o|+AeYI2=xDGT0xesL z=gG?AplofAqHWt>8wV60?4MSx%wTF7h}_&F;Ne4H@nZ3yy?OybK@3eaz`@4c0c2&xJejg~D}V8H@^CU7B;moK(t zt5!gd9>9SE99Vq(J&YOSp%9oluxOr1mg9#Xe2|`=2RvrNOC&&>Hb9pyfUhr5 zUV)^fY<%<0)@OjJ0*gS69lHtFuipolF-KS!(666RjD-d8=n+>KDXv9}X0$4N% z!jvi7k(`_Zke9Tx1GB7vjt=1C!+@3J_U#-P8hSkirYtP_{)7ozn3sLPKuLjP$ACV4 zghEI$OhKe5QWz<&>42&QR!yO9++ac#F~jfd3}|ZuojL(tUO-tHQd4s=ZQ2ellrper z&PkKD;nuBe0V*T}(A5=+AcZi+kb(>ix4*E>n>4KI?egW99>T5^v(Uob9cbSk=+FW1 z^aM&vk&%%PW8+=;B>$`nz|6mpj%kTQ7L2V6^4t&;K_9 zyuuN-!5?M+n1gsHZ)~&K4-4~PW)aq?0js89uffRD(uP5c`AP}bxw92KtoNg2z6q~n z$cGL^?6w1#Hq~3bE_sNw}3I+(-L z^E66J*-E*0512bwG&sKgnpZa!!lG2@`|rX0IACbVLy7XTN*2hUX92zC>l4AN^5ssQ zEaByKj%77$JKbR`>&G7jfX_YyE?ug98`ArUiNM5(;``rz3nV467`BoH^pV_ay-~1q zbgbaxbCC;(^mJhQbRqbN5x|uzD0))HOj0dX2ljqZ5z8BslYz;T#dWk2Z`@#}Op3jG zFQIE!$(sSIOBZYS`bKfVcr-p`iV*no&w!cC)POEc_o3&lp;pgMw91#kB5y$+$FaiUC2WtCB*1SsbA z<;+M^tDOX{8Lz0;H#FtdQC_Cb$_5rL6z%%KgMo_|A!GLq3rj%1e)dYmQERP3uy~=L z8lXh~(slhYcuNA1y;6eP_M2e0#1~5!?^6)`zEBCZ!ykWK=MT)N0K}b7-3^$ z$kNggaVoLa0-Cx9L#Rwm+fJIBC$a!)-|OiCr%pl6fKkDZ=6-(b(8>-!7==807f6Q= zL-}MQ-0}P!2mPW{E>*X0Z;rFhzG7i8d%V>;fN#rMw;|o&^ywr4iVkQP7zj|aW9 zhYLij?o}K)($!rn>?0jWp|ptF_|(ond6EOWcroRL)l9Dq1~}rH!!}Xd*Vo4prvPrh zRBe8c?D4^8;cMfE2={#m*%_|*#12h7Xpl3`pTEr?j33Y-&63fbHxHF}GZ7RN%LRIs zhuJ8`3J8csMgAkOpiFd)sI7nWC_f{1=~7yiM}791IIjb^D{(@B#2(vi_Ni4-pp!gC zEa#*In?fZjL3izH{o;2=jdDj+RH^{QG|4qepiE3qnR%ZJpiHn>Us%<_FQq@_`D`9X zN71dO_I^kRKk^h4lfeZ0yT3@MdAvPOVF-)yW={9Uo?Yje*|AU=79BsRsRSxTD?DdG zrB~`EueUw~m;STi+G;d5bTfkEioGy1`wtgD*(l%AG6c>Ot@s?(W5d{0kVuZ8dv_b9 z^Ur0=0^w*K1ZTaud=8gBGvH(q$e(lO%-v6`oRor9p9HKn`qTkxGJV6hduSkNU}<1! zU}<1!U}=U1f(DibmIjsvR+Ac(Bvb3Y{y#!^3t&-k&(kAY(h z?)rsb>UUdtQIDz?>YaCZLt)Y35h@Ku`h_E9-A2gAjYFdSW}Mi05>{4+`D)X^dgjQ1 zB|KK`<@I?qZ;tZm#_+KVan0@S(4@ zRi6{%zO^=eaIpEcXZQEnvp#ydeROvCwR_j+_?WK^bsrsVKUkap+`REqMCkWfGk&gL z`@z)k*ZzH9o9e&TRQ|bs?Z=`8zt5chU}O2g*7Adu*_WKmU)#6-+_L%a<437IcPWXN7=7iCkC{(ah%@3UwAm^=H|t{uM) z9r&?q$$Mk{FZnsYc5M4|?dqRJf zX#0|z_50|Nua#x*4fQ^S1peB)=lj&jUz-}fWTbxda{K7z{=v@b=cWyx!h%28TYsNA z>F34`pF)E^1^9jn5BVG&@qO~dzb{`tFxI?cq4`c%vmshtu}Lj2S)E~mDlm>tKH6{r zNHHdPySp?B?OOK_$l)yTh%5$1{dEv#bkdkz0aT#j>Eaj?aX2~Q085Y0nH0trPMw}B z2Hg{<_BJ-oZJylSIJLHIy~9 z)z=?bv|@=*<&ExDD^{({=E(L6mind>b<1j3)i1U-w_|E*YRg1srl#d3W~SyQXD4T* zzgKwr!lg6voR^-|x;(FUL2`%Y#oe*w+b@)9Cz4j(vlOw4Jb{7$FCcMdBn z=`LR6ykbIoQc~@sN5V^lCr{Bd@7A_dHSKLQv{epn58CXrY0)Of%67%$C+wyhZT@8Q znR`L1OJZOlr?P6=)K^)Dx#vyTRMd5eMQz0j748I6?f$ts2MoB}4r)lSE@rS^u}Y0Q z!FbiOZGAcoSJ(Vv+jL+>0yJ`MF4U!-m sg7ec#$`gxH8OqDc^)mCai<1)zQuXqS(r3T3kpj8H)78&qol`;+0C!Eu?*IS* 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 zcmV-T3$pZyP)001}$1^@s6wfF^v000XVNkl9sW1W=EpZHl1BA5ozq4p7u;inxK@ z25J9kY9vS#r*@1qv1KD^|>%#17$iTauma^JPnHEghLsDMCkvgM&q z+S;aiYkd)2y2OeL*p-SXhwqaCbrX&M!}r_u=S9XHkOi;*eV?x?cN21k3E7v(`R>Bf z)@S?@C0lyy(FKbui+Iiop?Js7P46VG=brrlEW>1%g(tFVA$=g{;h;c$Y&(lBmr3?p_Yit0xQ!SL)bFJ3yu z(Sx5N5v{>>1+(s9YW-n+I%INbp1t@qckjBFP03A2B%bS1S$P9dzDe2bVd>`cgpz$U z_uPXJ8*t3BOIdPDqj=5=ilWodd4N!?i<#kn!V)tKnl>>aJT~|wl}d?^?!1>ow1&A% zh1tb4t+gsG4Kb!u6Fm0Hw|U|Ei~RoH-=U~FYzv367EaRE{|N$#%@p&K2s{>Vo}sS& z0Ah{A)(op`r7@Q-qX(O4>^h91Xp}^ptKLUQDmgA(e2fXR#20?^5xU#@sR`EraARto zW#tJ5$M@3P@$2+;Z0FI>{wr^dUE#!2U!X4$rt#2c$cOHuEwmFwQHeJUF#h`EgyU^g z$~g>wd}CpS6J2QizlCF)`24F=fU(6fjz9f*LefWzc9c6d1?Y$g>ca_yq9LV+<(}d3 zJARErnNfbbJWn**LRVcUQN`rG?qA{ROh3m?eg{)5^W8uBF1Pt_BM_-4lDrKwbB)aG zB^o*oA{2FFV3mtm>{0>U7a^ReM@osA9if!HNH#yiv@7ZCF&NDsWpZJdp!yu4A(<;BnO|{<)$T8Sk&cC9n;QoUp&+D{IgSl~ze4>NVsWEodPvhiDIOhFKi(wwPFvs!cDgL-a zK{G;Vnnr5sJVqc%Akw%&BC9YJ0*RQHhM(^pFez@BYSrvC1k7+!Ppi? z@(SPTIKwbRZOP~fiu&E_ysob-phtD1SES;j3zG%l! zZ${N9=BIINbK_v?z9@lcE2KMdL%f@{*Y*}}PF&`PXH_c74U)!nB9w8p zSMeeW($%p`b(9LXvpn^;oF6&B4)=%LeV`o=lkQ+EW1&`#ee@U+e+18SaP1tKnOEu9 z@w*!b%NK}~Y}t#Io5HazR6Rh<2(vTT&eNBl!qPpy`pJjc*KmrHbApDt=g0D7v2)-af;r{164ZzYmxX{b@j6g&v) z2s~Wrv9;|cyLx)D5lk#iFlH~Xy!;w_uAQd4={BVF2u52`wYAHh3Mu89RQ~0bu486K zk&2(jj{T&q91nc&0iHbf1U-pP`a8Ds(2o^L%w+Q%hW|`$;=I5mkOv_5F?O4H`b^d zmMg4DWJ!Fu(nrzEGdK1U&HYDkJ(W33v7$LRj>9uU&k!}@^8EM<;){nrB}?`smGqDC zWPyMdMaH+`wSAfgPk&SF=}gIIhMo}>uYze9n7Tw0L7C5`SzemNHfKn-?ng1|DOc7g zpY_o9N^6!WmF2KVSQVt47wQ{ly;*Ok7@gZAVtNXLS!Neq{_V9N$kFswku$sGp5_Oo zs`^;na8+=p%ABA18*y&(jCgA{C+8MRBCK@bQ<@kY@8aQq&&s&>7DjoIoZO5(+K#H* zYgSM!*0x$%;&-R;vqV@-GAHiA#fK{^RNNdhxu?0F`wB%lLsV?#&c-M48}+Dq5Z5W; zR93i<`Xd%9rwNHp`s05|edI25HG!cRguHQTxkyC@$fzHtD4I9&?v9|OwxPKz_`DQN z;y=iHn<#2MC|V6c-zHijAIEizgq0pVuZ-upc;y^YdU(>N*0&!`3!$lDbS*|eZ$YC- z$Qz-Ci@4GtE$*PS#!%}JS?`insBzDsc^NcJ5E?1vE^K9WD@;m> zDJef)n%H&*>9{DWAL-htMgUb?%_;=kacaaBgsh;;Wm3xBc&J!PJL^>pw2nK{f}+~R zjBFw4ox?9O*h<~cF2J=Io~WQ`2A->@)ylsrRu!v? rRmG}eRa+ISidDs`VpXv|pzXf_BpA=#IS001}$1^@s6wfF^v000SZNklVv8kv7)wuic%{$ZgtmDw{=?|tF~0jsH{{$aZsy( z?V^Z?A_@pap1~M-C?qIvioE0{v>*scNC=SU{oQ-N`%BQ(>hQAe41xS+&Lo7JJNbR) ze9rHjYo!;6n0BYXv5F~kVTswK=@j5-Pk4+}+i&t_MIDNqCPB9MTb~)+%)Rk)az6DaRupTm#$vMs8ZyDt*M~jwy z!7^#7r&`7fN&AvjmeaB?SpU`%(R=1W0UY{f) zrY6>Uo&{FlDaM18SF)WOcMLl%2M+5Yvo-~%Id+0kzPp&W!0L~d1_N(MP9drf{yr?U z{6(<3iBT;ps@TrWZbUI{Xlwb)VLjARq2x4vfsAtY65ax0<+D0@Nq@kms z9<0Ie=*tF%H;B@SYT240p=HmoG#b-f$f&y@t-K6b#bn%*Oh%sXa%6g}MaG_Wrq{AC z2Q+6-L__vUwBEi6b!#()M1e8PBQ2|H+0Jdd%w9{Yp4tKU`vW_60B6pCIkgr1!52U_ zy@ZbCuR-NH8j1sNK(YJ}AnQj0?p7dve+G9U2nY-Wwr&N=%AU|t#c96_W|VeL+|bJ` z3ajQ_JLD{Kz>hXF@sq6`Di(i;l4T#^?!5VEn)Ws-mMlZLlMCu+&qL$nx6v@e7Nzw6 zni)1gUES07WT=)YH;Br4tyyZ>Gc17s66d9`3;7q^h3nyZ#Rsbe7vYst3D3wx_yh!F zUuqr>7Tm?5>`EMsOu&&V5}b|}LGA1Us8l^Rv24v4p`8WC$45-FIDo!=R1LY`vyKLsX91q z;5kM)cPVEUundMW=yXwFSs74X4*A^o;h&lhA5j*V@ z+rXhixcS*O>@Tdx{;()SMMu+aLV&QaFo?xsENZY;#2Z|fu9hHdFr%#)}q@<+K7OK04s-gwnsODLkb*Dqm!|Qq>zLAundDa}@ z+BMv7k)XKY9o+A5p=x*aI4qRcDATy_FeLhO$HuzEbC3u!*!KO_p zTDcYnZr#D*OYzjoBvUaL3Wcbxtwn2VD{^ylArgsj_3Bkb$Hl`-D8{z}F%%Of0m;eG zQT_5;?NO&(PcuvR6et7LESTBuYM@$&Rke-CaFh8MKwwQ(!-2@#~PM(DN_1`1j zbr&KoL?S*d1M%W)gs0p2L-EER!HpRM1O$N5RH0Vkj9jS= zIyHjFwBu1)-Nvria+Qo6?ArAN%ecC<*CB3RfMU6EgT7!pC@@;gjhn~r-N2ePfSnz5 zW5+=;ej?Z@)_}9KNm+P$f>wX7{zUq(4_h+2rGY}9m`)9-6d0;E}4gR zy}{ymJb9MREh%AwX> zgS_(qs+A5XYMYGmHV0^RnWjh2a;1*j>z~W~#+3!P*BE8tsvFDhCJy^8n&ovp8&#*q(H3D(K!Nj zve9T#Y=c^xOj`vx^ahsyURX;eEr~Qw?fL`j=TDc5C@eRcK?~`L`4$w~ak#6Rgd&*@ zs+3bHkUvocr_$?|)9ZZcH8RupWC|_k;hV$!+Qy8v8z`)YqmWTgLhex*EBiK^VSD_6i@3OkK#LWcG<=&=C9qe!0PWZYIiBQhD>xEBHI1GU`gAwocqyi=G7S? z{E&n6ykHr%D=L?Bo+m7wp5-Hws>y^!5kphE=LM@(uHkkaxyAhIoe+M= zLwZ(N9LJ-eRLVO2Q$Svv7&Lr94HTA9yNmHvWc@RW_IK9AXYE^Tt2PEW`X%_8z43-Xew~AVb!@2~E mc6~*7LN#M*GA%IrjQ;}0iIOoC9gGtI0000001}$0{{R3f+qF10003vP)t-s{{R60 zYHI)f{{R2~^!xPP^4<3Q_S^B>%j(N?$8`Gr`qS;xZ^du;{P_O={)^FzD5)sm^Wc}& zmm{Ym+3?x?{`~v>`|tVhMzuxsP`OYAn+2ZOp7#6pn%A0KzFb$lS53H0 z4W11EnE)!PDpI;qQ@T@V!)VUz&Y;<#UA|pUxK9wD5GSZ7Wx{31>B!{tt1RX9<*MDP37iQrtTAcBX(gy7 z-}2vO!DVB>W8LxHvf#2ju{^ipw{FF5x#PL6->&ib@wno+#^}bw=fi4hY5)KL+d#9Q z0003LNklK}MF^S2Cn_c;E+Hw!#l+Awl001}$1^@s6wfF^v000RINklR{c+Cu z&iTI2M4HzJUdG9Uhrq+)VezndSUfBq77u}k^?YDuzP$&$jFZWg;}gNlIGNy_33wSN z9@bc3^?z;(UdGAfvl$}rGEOEvR#te?>LzuPekBs&m!wt~7pUFcp>}qL+RhHTm!OSI zFAP=}Nl9SVT38@y{CN0{8wYnYGlb5Xh2wpF_}hsSIRE9B_)4R}k*!zWb#4dgiTG$dSQ_P(^7uT*`!)V)`8#nNkP6u6dG_+#z zs|8C%dWYO?GFg&9V1a2t`3T9`gQ)ZdM5ffhKjs67UH!07D#Z-~VeEGO>Qx-swhgMO zQ-2UEd+xAUT#6A2VQFg0xtimY5QGt=l!QverGAXUA1^_6FAQlp!W5JhTOUP zS;K1gvP91k0S+u1S^G&JgbLs$FoWrk0M6o)+Zzc;h~J8=sEwRr)9O&L@NERmU5ar+ zOL9{Iyu7?{`0!!ezkh#BR`&1jzJqS90zE%5AF*w!JOQ>)+nSdL;IDuF6Z&u8#?=cK z@Vi~RpbQFv`dPwKPZU8r#{${_{Zf4th#+c=o2NGtvo|9w z`6Coa-+?T>5mLn`m>a$fezOB$I_R!`ettM~=nzhxJc)}JFXHs+(>Q~Az5CE zym`wILh&jmec~|Gp0%{JM1FogR;^lvhK2?d7Z+pu_U$MuE91nD**SKN-LrS^ z-bWVp9|EV#&49{Z#I>t^mIa!o5~#>P{JyCP-yA)Pqg7Q@O5zk3xWoFdmPl$0h@}SalO@;>v8ab) z@rOZJuwVfi8ygW87RG&GWMm|E?AQSr{dY@C%OIDhpuD{N5iAzF;wkyuv}qIC+S)ic z>^-%$wVc>7JIAh-mX>l>JAAuL9fgWI47K$@J3Ga8==7ZhO^gg3UelqnHQX6=8G_a6 zJ^_6zh4`Xa0PV8z_(hTvTJmhMH{S-Tr6PR(o`AbHT;&l0IE;G|)WeS!Q%_@VZvNPw zS&ki*E^Kd0Zou4*?T0?=;o-r#*{xf*INiB(2e-*8nAkBp*Wcfdn>TOb?%lh3gZ{<8 zoPfH>Pj9;)>bE}?q9)ZDRf(?Hx5NgA-yLAEKUWB~y=Y`uzgZ!~?vlwUEJ{UmP6bj6 zN+Dmm8H)>7Li?UA2W+@TgJo}TZx9yS>w_hcNKjK#!$Dy{hQng#7%Y~^8iJ)FSo+${ zbhucE>J4*{xTFHn`8CKaq?ouV0qPB6^yV0V#Z>1o2~ja+P(+tdPu0NnSP@xDgrwEu zL+5FF&6T<7I{H|N9ygpZW5yG1SYKcN$PHP*-$q$@O3LGI$m&FeLV=ANH*&64QBlFY zhrwfF$LyRTH)Oe&mfTQFJ@QDdw#Cf{i&CnwaPdAa?+eXs#X?ewbT#(aI*jawESRtI zl0ueJ%jG^{IW5SDDd!X}D@C21#Na3b8$oaw?D|Iw@C=Shr9xI#7T1#%6g(c_y}i9T z7*8AES?<(DG0#v|-(hWww0TRhD7zUE3MIm6pH2yK7?omS@mo+Uo;Wfr_Q6H&vk?`y zg&r#Bvgty&ajEnP_fqMqXYXcJAEC z!5UA+_UhHEp;RiLI(6vD$Uqlm>4w_7&)NoW`NTp&<(540=U7C>kxJjrtSfrqDkyF2 z4EKgg0+KOP26^N`CMju$BIo71~+U3jWrIbTwWbRZs=?x@Po|lxk4XN=J z+&70(lKhr;6c=7b&9j=wJQvTFLrxFNs2qtPtE!T?kEwp#Ou#(MhL<~|i&++nRmYAU z8!>1bDtCHwa*PJW)GT&Ffo~%60&}@Iok#_K{DPIJv3D^(DXn(&K-QcLq|-~P36%CJ z3T{?N`wc0ijTI|-m<6&pcICHvAwx8yNksQYMMYu#`t@9lV`;(7^hS}m?$zpi(9`H8_l>J`5sZ*yOO;{Y?z5CIG#cMwmCLyb{C$YUN{r0AEX{}U>p7iwBZz^o$&SY?sm8`Cw z)JWP*zxR<|nn_lET7C@E_xys7F)R$>mlE;ovz`wue);1VmPzspYsatvmtV5YuY>1d o@vwMUJS-j-4}pip!+O#BA9wcGd0?DSAOHXW07*qoM6N<$g1mx001}$1^@s6wfF^v000fGNklNE1U< z=>ZWz#eyJ3QJR8+2m*^Nuq?~}&CJ5;5>d>4t{MkLgSLQeO-nsYAx4jCcPe=XP zvuG8IL0@9A1Yu#9F>2IiC@U{QuU_9gRqEny@gWtEk{ouU<@X4lYV;+PJ2|m)Tvk&T#-9!6q~*4lO~}yIvSG3Mh301u^Ew(S(rG{iOu-)f<>+6laJkz5}JZr zj_Yv6!~_|-x+tDM9}QVqboSW&#Kq-8L!$>RYl*!-E=HWj&6TT=*{2UOd-q1kvSnzz zejR9SWjA^Et^ixN#=y+%2Vwz>x(ya}^N}MrgMT0axA^|JHg+tVH2Pj05(0@($gVFE zwczB*JWQFgr^}(?xrknT(4ckLvm**cLBs{#eHWM2)sbs!i%L&VwA9wJ8*XWl;Nr!b z`22IvKe@tRfn{j88Y`C{L3Vf|issE@mP9S;&V~&Tm6o!JY9%Tzu7ZO@2#k!@^c*Ca z+CK1jOYqJ+dl7Ra1tmLnu~b{ni>#%Ek6bc`);GAK@Q0d`;mYN!w1z);!6JLDc2`#N}Ul2&0z@nlvNmB=J zw+^vn+_(<0M2y75Yp}CBz+y^gu-x2|G3P&gTnLwsjklIAmBz-l08V4$wrw#mH-CI= z>~Wczevi&%W3vIPSO1QJvsZ9u{d(kBTeDb3t*kxGps$)5A#=~pU{zKKP<|p7`5%19 z(v<5{rl2+|O1A1!OU=kA!R*;y#QGdNy&r1DJqj^D{~n&Cf`WSlMgv-#n<2V&3r+d? zXeum(`C|@*LsNZ8&%w&j(Lr~Ku4?U|n3R-oSxc+i&hM>j15M?HJqJr} za4)#LR0jM5>mOK(2#e-V-K9rY!uFVzuBzH?=jUr!_AD&gW#}%^Tug2?)YHQ~H@9v( zf7DJtXsY$#YFJiQi>Ha!X&n+HR$w=ml`*v!+nwfF9bPR0!NX%o&ebYuWr^+6?js~5 zy=U{RAwxExGtrV~`SKuE2e!XxsY9V$pFA14!-u1I&K%rJJr7}hQ>U;5f(A4-iDl<# z%*nycg$t28Vg&LgOh8rO5u}~W!K_(_U~9Xn)BB+jYro#mJ}&j(pqw^@6c_2sLoP_b_xvaPIG;+a2vI`V^~ zuw~ORHrv!Sp4N4s7BOPP&*1aV5KC*T3B_X6r=_9r}IR7gvP)CS$^cow8Q*v-Gnl z*qJlkk(O4(Hb>fHSe1_taz~DowzuDY8%2SkaM=|DE32P+*!3O}-6$|NcEI}e$52{Y zE$t+eAEM%7lsY)D0n>I$vAsPqB2K~n8^1qoVmxc7RJJ*>ITCm7)XJ_&i|mq>E0Jkv zh)e?m++6l8(j$_waDfkWbyxH}EZQbcO)WrkbBlDA>*`qXdv(khHp_VvC!*NzcO2Lo z$AawRX8Eu7;JMtz7(adoA|f)-(9kTk=B6eH;^T2+>Qrfan=B$FuI>o-Ifd6=+uE}& z8kx(rv_N?25{f?moE5)WW@aeayakEDDfr+6SE#8ieQvuu)M~!|+7G$8We?+IWhL(J z-i>TaOBT-xKKcm3oA}UBUn+MT-FcckdG~YCqpfo$wldwli}G#TkY#SpKox%W8LoxJ z!}0qN^y}yFx9l5Ii?z014_DVD+`nHho4|(5OqQO=x(=MLWXI4LTXr)C4@$ziU$~uxop(MQe>eeoA6i}-R3c84VpCBZv>LNi&gXxr! z8Ez$Cz{ZWoyKL(8T<^cXkF}|3o0&ER%Ztj9xBn0hSib{}N6MWy-q?ZKd=E&@Xb`DE zbV?H~DeCNVSJaZKhVlpv)Wm4uezb=C-!BbALR7pwtRXt{Va@pzRzyP#?J;!7`e)uK zpmD^);ztZKUqjCI8LX8*l}dFMY^{DI*17hX)LB@Vk)4Hf3 z$DN0!L}k=Oa!`4U1Ifj~1Xf&!is6M!{<@||VTn$1QRO)d0=KcKJu(1-fL9>cfPruE2E&qr-@aCad5+nha3Vb$x#=f3k`96p%KCt8Q|Q{I=Hr< zEG9mxs7|y}Ojs?axu_1;A(k~9B9AvA@tTNwm$|6kAT%Z>m zBXQ{j7ZLON!q2WhPOj~ZhIkI@qgAB;qiCz)xN+N1b6vU`vH(s}hWcHXxId~xOi2EvhMHI9QI&d01@k z`}7|C%OcvT_|4@QBv&SM3aXg^;2-FXb(5{&H*zSP##mwHYzyQ>@}2;g13}()Oq#Sy zabQu+{?Zdoc`G^{cuu@3h95SE!GVF$aT^RZ4=X5pSz}SK5gL+}AAj6AJ>;eZqksSJ z6bqJ(&3Y7PkRX3n>#hm^XhMjBI_6=V)O`GP7nrlvEpz?9(T&x=U`3=G_PgB@>n4%xCFkNo~>#z;ezmN9E zgHs(`ktCz1<4(Z9V6|exqN`$XKqRrQ@p1!qJ*EdkpFuG5vcTv=rm*m~fYD*gHdxlM zB=uxLg32RSrAn-71BrtkYzImqVM*)6?I_LiCBfNH7OxhD^hJN-di}gDVdG^6Q}2Q3 zOJNZ=>_yyA%g+jlF%N6TmMfDH5*YP_Z8rshMKkR+s{$arzMf?C>JXBZ?tS8BLx~F- z5bHAG4}=+kW$fDq%ijiEVqJle+HLMP2zeJm*_RZa=~;)DRrJvV_uo!HlxGi73J;DU z1~4L4rAOkF9sy>^C!c)>GdENYuz}5C3#7!VLvp@9%5%Lj^P>Y#+5J`^SX6!cu7#g> zGy%2@b*Hru9;t<|ju_!zN6awJ-xQ+*%`r7_0M>;XAUmFi<_iN+an&0O7I?8u-)DY` z=4BUs3dhYg62co>iPadB_@#_{B#o#_;^6K{E(Bx+Y)K&nbLJZ;z7mRAvpk;o6+wlD zMWwC19BWnw;&yHbD~jdPinKmxzOfvq|46`FZ#g}~7pW8!R(sH=+xwB>i4YdP(bf3&Yl5BiO?!|MSCaiYEHh&-{7bNc!Up4tpoCc68U=?*9;0`>wg3-ijwa;PMZw g=lx9SKYG0W3wJG!lZ4w=MgRZ+07*qoM6N<$f*G~bYybcN 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 zzo0LIFu>FK&Ex0xW+#dD4({(5?!0w+-#z#2OYYalfkZIhHC!`$F|j|23*Lp>eIpy|8m2INtZg{sI!-nawR0uqGh zBlmibOdAi$jz6OdAQSfXu$C?&vR}6)We;aRTH|$6ow!kom;SVWUO!uE@ZN>HSOsTA zE-m~Hq%Q|*1~HV>Xbs0reH@W#Yh{o+9JRq~=bzNq(bKk|sA>Q7(o5N|e_g#KZ#dye z$X^u=TuHAMpB!CV8~vpE#K_v($R}hm(E;NxWCjYPW*PV_c?Sj|U>-E$Md7A}Wi6by zKdXwOIXx`fmv&XxcZ~$LrT3(_1*(6o@~Ev1!BBF|$o9ktVoT>nM*Q2ly0-bN-!7F9 zV~^XmltbooW`4YW5>RA&DpKo&S_fiU1c8V`cmX?|%(-zpAp|oG4eh-3*2lwlkzm~w z{!*$h77ovEYis)tTaK#Mqg!sh_02Ep?_RuTeAmuGEPpaTzb)1sWBjVcW8ESNW3kj# zN3n=-ozM%MG(_jY$}_|Ot3>s{Ge{=~`d!2pV3C+*SaYzBEq+rY3((ggmAE6hP*fJ7 zwVsu2-|4~cZhaG8*X%oeTo^&=^uBAhes^$%o6|q58A0QM%u!CW3#B%!W_J*aSwea0 ztmUbJ)B6x&b3a~xH*h*2*Zj!H&V8rDzUo)PzQ5zHs_5%d+qVF&ACJoy4gJdazD9j? zmx(1zlafP1f1s2BSciOii5U;QPw~M`!hsdr2o)uQ6^21A^|n3HkvEhtUSw4YqImEE zfgRPrzh9BBHZ=se`1>{Fkf{dB17dQe<~|dhoWx&*e^+d@x~9*NEv8xHAqiGz&^mY< zS<6))Re0Yy1j*FHVy$NludjiTUFlJmz?PaVRs?YHB-+PCOg&mmmYSwW>G3vy@j!3y zfueu5+%P3ojHUfsUDsXb+PZ)63$+5&;`=R^*3xhbrCRMqYYmm4#2RS*hXy(feD!=Y zF5ChtW!i1v2YPj68+d?4W32Z^;71PCcP7-+;#R0jV!&?%nvyCAlKV=e^QV8hGxVy~ zotm~^(Nlp=sTh8`2~dM@!wx3Ozkp~p4~3d`2Bs*mJc_0vu?znCR2bB46&Ed0saoU) zV0vL_d$@X0owK`FbY5JzrBYtTNojg3ENmZg1$#|(?)O#G;P=Bhbx1H8bQ(1l2<>Q5 zTRerT0cwB|6rfN9xe@SJCwW2u)e76N%%67mSm=0m%-!l}8E}L`p;Nx;Y2UWCU8AGB z+Fow5h^j69Esi$lSoU~m%x(An#!%?YQ0UFRojZNoj`HNA7*`W(kbTonZ$@_f54yVo zr1}DK!d&%+5DoTyZejuATe-d@^<)?alLK9&BMZYC`0oC}cYFBKU-ceW+K5NA zH6~IU{c-3$J7?LpH;p95&1Sn!7E?-Qbj>dyo1-zHALxS{W>}pMj=~;E4pFnI?^X>LIeck#;_e_6b zu9C(n=juI1lqSo2VB}Qv2ltDE)D6^1%mr7P(A_XaE25Prwd7{*B zPhX9vFbY|wE*!S5+rz_0uJK!C}mO90Qd-rxU}zdBITPa z)ZSp_GE4L3>3!o{Qi;C8oHFXWIF5eYW!oj9c_(bMa3@!UQEOL($E{m(!vTWQ5=>N7 z6t-R@R03IhBAkq;eUYxQ?3gH{z~mo?F73k?Q;qxwj>^c77zkx87_)PT@4MxtxLRA{64InH4{f{S7yR*!_uO^(A*2R8 zM|g43;;-yN}$(!8#YQ}OXt1yFGHu9A{dsg=TPV?d; zpsAe~+5qYmY49y_^%X>(FCw<8E*{h@L_C0&Fm~8Pu`c1SPAlM5m=m)7+}AQ+HPn+Q zSCx;R z+tc}}EAl5yY}TM5{7dr=+(sk`Hy5&qF<9}nz{x=KDB)th!gz-?@TphTd5mrXAtAOPg(mPf+Ocpo3QaC|(do`% zS`WGbFObOm`?i1|*{{OB+WWIUwbXg%vfApa{*RGQNBdQ_@tv=;)8D^!=tD#O{R=Ha zcf5Ib>lhLfSScFI02UY{&R%Ea`lZhOC|cI!+Fw~e*YN8n{QDh9xnlbpggX>6?s9l> z3EVYgyvQKH7)(bwsQ$aV&3pLzrFgb7hYT?Xy1lXL!*H~!4A100;ElTmma_4cN@v?r zyR-GQ$TB)GOGC_({tMiM6GXewODo~0J>#&V{(i^NvUaQrQ`=TaX z$ySgMa)}Ny8Xo^91~o^U;2`uY4s32xh88H5xCnp*=b|vm@f)c9kqLV?J2izJmpb)C z`sjxugDt(PyGtpj@^@smPHvrv+0{IFGf=;7|Qgx+uAcdxUZ6}0H(E$sE&RSscOR}QaXGV#AyKuA)2~m z3J3xFUah3&-WbpJ234>t>AxmEv;E-bYMneL*|Bd*&1K_H1be&sg56_dhr_zJ>0gOI z5f*5lp}Pr%1=zVP)=uf8DBvAwz!gFiL>S5EFOuMB0mKSV2z0Bte11Xx&Z8Oee%0X# z)F#oE)`}Q2=%N$=5Y2go5DnmD6dPBUn6;sz5G)MU3QpUxz>E8QrIK%-GUiK=FRUmG z-U*{H^q0~M6%5WT7afh1yI?ir5W1!(t~eT%>TRf)zr0-LjjC{I1;w%&muLD+HXtii zUz!DE>hUF)T@~IGyzRy%(aIGrM{wH&NXC_`mp$DQgolz{2pUU=S z)0}GQ?yz@sw@jJU*V2e&^T*SO%%)2Y+&8eTeyH-IHxTf?SUG=|vgq0K($#cmd;x|U z2~J`TXt((B5we49mBz<@cX{U5cI5-Jld}OMaWG+S+@*qd8b@9|vaxHI5kMj{BY}f= z-f0{@j7w@Udjr!lh?NWE1kZrmWq;)GA|JAZewm(JE?SQUo27eA{=~a-Q@K|xc{%P6 z!}_<|eKk=B@{U2@YbLJ=1E1PZ%a}5{m<99;cnH(86p3Jp(3H82MEgJz zU|560TG#=Zjl>CJu?+(XyJvyR-?URmfUm)gu z(6M1Ub~$3c_O_X>?tCt^vwb)`J=S?m>z0v9TlH^MXDAkEZ~Gsum}(|?HlZq-%n_TI z!!T-ojM0NQ$9@1>Tw9|=Nk5x&PH!2W4g2GN_Aoj?+2h@WAaqc639`YmgqW)7LPQC@>;79j2pr15A({SQrTf8tnitd<5S}f;##un6RgZ zOOofiV?DnNa|FVJ&&``Ad{5PyX(Hc%X;#EhVTyHgwa->EJnc4nVE*|RS? z{julu&OPW%OOB#90H+jPsX|s3Fng$GgIz;;N-hVr z`nv5tSsJ+Z4)on`<7^iDq+>a(6nYsGLB=P&lqn5A|F;u~JK7iL)c(Yt|K4^1Yq~3; z?)}bq;*K^hYp3HtV(-6ezfi#HPIT2ehDZthCi*prfct8$fb{vAMmqXb2Ktga$CW^T z8=yzA*34_hZcPmTT%}lcAfY~%$fFKHJlLt0x)Ds#(VlrNzuz}M@7oVC-*$Zux{Xf6 zHeb|%*}%1MQGE-05fD(YWm(q_HbmpH=uuRhAPPl_?kskZN*fD%Euj_@ABoKb^10&H zZ9_(GtJ)Gebj@4xH$7fB`mrUfa(rJsm-t9LkXJe1SSV-YqR)lre51y^d&)m5v);t4 zUxj^skdzx!4&7ID5~;eHyT1C>r*5uGP#s)1~XDlLt=SyMLE@hz{R@Q!4D9zvB_y7EAlr8SvJ=19Ub*%yd$ID{#Z6%&ffsnn-DSuE_=Me zXc8iCcqSa3^T*u-1MV0>yFNst;3s^={CyCHjHO7&U4+N(>fq_&=x5{p_V(G?_I9v& zFM3N$yA+Qas-8}3HGzCMf~Z4RFOWWBj>^k{eSWrHn?JJ-v|mT)%&!RA0@0EGH1vYV zoVDuw+Ny--EUyuuL4cry?3Co#rVpqLMcBtm%|TC<9%?NR&dJ|l2Ab-ZUD(j3EH5ua zQOYDzrJmi}D|z>4w(l-h*S0@KulhlREP--wbPYrch2f-<_vpY59JBtwCvCQh-G+&z zA@vNM%Ro!TQpfxoY&ctMB-&5InVzfAneY{X+==kt9ytkVsB>_;`9Zm~(dG#TJ#F9% z-<2#Y8<}3i=uNp&QN!pzpxbsN_9PtGjW7+1X@X6e7$XWlT7+#*R5h$gJjY@47=U)x z#|Qij<57TNaS4NfIDPr2-`8M&uONWv@{V7|q1q5B`tzboEE4dOPCj{sb};Fi&R= ztw5$KBDtw$he6mzSYI6ute|GdqX6L!{I&8qN1GiQoy#uI@>2t``FC|+K%t)Mw+;W! z#Q*+B&O#4vZp{1QO>U zI8g>HJunahpxWrlynVPgdE8n3a_@-Ua2|(97x>!Z=10yA90@1Q``!~iGH{>S)WOWY z3L-&uj~=^lg1Nl?`T!Odr>7UEf2OHz8Adgh&0e3)PH%3rHky_Jl1!XcKOw#frh)2X zg&5xv|XAV z_INwpU>oc8J+nFZ4VL(yG$AG+Scbx45#kh#Y1^1*&I;(pL>5YqbtG|!*5@de7FfPT zsj|B=cFQ<`;~yZQImY$#ZcllGhkB z+VxlOzYuTh@pg2?@z&E8U&y6)>_{O~Aq2c;*{04pEcW!pH=eC7e?=mr*2ci4v!{?5 zXks6RnIh?ih(rYE7agPo6&PvQb)q%oiIx|}_yhEnO-<7j8;wSZ z>tgI%N2a$TiE)P~4}Z;Y@@aqkUS}=&>HD@`SC|iapj3Ojz<6C4hvSB^x-rj43j}+teE9y#&Bu<-A3X|5(?p?3vU(WsIax&s z1!1OwD6>{l7iftXzAq@_H3czoU~qg;eIytYbu)O*PB|jp9Xe;X|6JgcgR%JFV7#s6 zq#uX^a9?^$YfE|B7|w6$>?rggO~d5UO>}m9HKdNcjKUQclBDv!R>9&rQ$U7q&&AL79+LIAe}k)wcIWu`1g`>S;UWvNW; zu2`lcewBNGJeAM)o%kVcphZ;9Jph&GUNf0&; zS@w*Nh1CP`C=gHzlQ87V`_#nfH67hAg?l^R<3Unt6?()UQ_HZ~AWuc`o#Ec_F9$lF zZUdWi-yQcy+dj78^{je2u#_wYE%i?ZnIA*0N8OHzwRD>>l#!x?_^7Y3nNYH+nd-bW z^SAE|CqtLC;yGV$#T0FTo}VzIHz$tM02$f6l{t)gfBcr6^p`(1ust-r4i1_oQYQNYH+sh=x5EM=0$VKDiMjM5d{Esee)W$ zcvJHwJ}I$Ums}_)ArGRaQya78fq*-E8QDu8q=HZ{;x%1~A&V1m=xzio*dtb-t zb8^%AWT#*uQTlV?YzS-oow6MX@4tOu?cvP3^sj$w>n$^_^Vgj|cSK*K=>i^h-k@B7 zQH+C|FEViZ{;(R{bfx*$nOlCdaWyCI-lkr++i>r+J6%W1CAc2Y3&Iysq+W-G3#r$0 z@L|H_;oK_FzU~IC<}8!k^g2O1va`b|Ep|M75Ay=dJjk1>Y|M-5+>1o14~~U`18w%P zLeLqGWVUVVak`woSa&kv^Y)~=x_aZW9$#0ayZ1ov(N6i%LA&j#yQi5w^?zRTM8DgA z^O!f%nhi&mkGJ=`L*=t($T)lACqt~BUnJl$flXI@0qL?I_Qws?8`{#S+ zF3BqJa{|iU3mgq81$vcR8ok$YU=0K2{R4D4i%X0U11esU<)<(Kau$hBOkr+e9=ann zfY}<-VnKr+7?kkOHOXLsOZBZvD*oz$h=g(Xyb)CNM zuEbsHK;o{1&huYIivNc`nm98a-&wi)So+wpzT@dS-~J|M3;DLl3CNLJmuD@$Z8Zt! zlOk9OyqM&9wn(~F-6oR5-Ich&Mj@&5Z$q-G>UIyIwKQ5qi;xXK!oG>w(+)|g-v;kJ zW*iTS|H8z$BVxl-$%+RK!H3K7>D>JQrpZ=njzBAcRPbwG>pw9w`45LwY%noa+<)Xu zPtf0;?Cw>c!kzqdq8pqV1qb>(C)98IlD_KsujNsaO+9|7`ZqX#?4iTG2P6JaD%d-U zn>*=AhaNgSJe2P4nK{waFPe+9i1f5}xzq7fJefu@)b?IkTl0A5cyvy0y-kdd&sB4V!>Wp{d0Vt^LaaP=E%zL~XG z%(B71ADeE*zyuS@4qenqkb^czqs_*7D?iF*U0~p4&JzM14qGi-%J5b0paOdRypUxbr*YX`l#?Lb zy-8&g`ufBVaU%@p24}KTkXQhF5YyV?6N2YR7yP5dg%HaHMr1u-?fI9)1o>rXCiI`Q zzLaFbe0YmHZ9qNk6f7M?+Z`=fPkxmrhWl~ZBduQctg8qFD}&Qsn#YildX_Z5@xx$^TZtt8)DOkS-$Vjp4X(KWA6 zMb%lX#-dQu?L7ozI*}*%gQ^cCPVXy~3Uj`P@Jh)Cb<39VeW%U&k~vpMCil!0(mg28 zD@PzkS%o!RSmaW;9X&937(fgLiU~-Qt9jj` zDBco$Rj4eiK>jL8?mb48@#Gc}x#3J$lzDCaX1xseCjJcaUv*_6fI5Km zdc|j$y6?N7!KFGhkMe6 zIjl#^!8mO>8sp+tbQUARm^~-x?nCq&Oma`FE=ddP{Fa=hwKJ|DYyH$jJ*{Bw5-C!v zmTOvgq*6+bM1L#GoXDEE{>>CDiopU?5__UTjyNG;LR8ky$-gouflw}ZR={8n3H#Ob zGwE>pM>KcY3l+#(74!i;B{)g6q1dg8ci)Yu!S^z9@a~Tym=f=N-Gzs;x{lM37G}s5 zSBf{%$#t1Nwvk|$(TFTEbV)+pIvdh6oAlpCZ+|~Jhpv(0QyWHzWVM0~w58W=tW<=0 zMd$}>K{3jXQQW9sHxYHf_DWF)q75Rt4n`Iu7g+HXEJCdy<#xEbN*68kDVny30R6|> zB>(C}uFi9!7RL!<>Z$M}kC@BLD7jJd^T+Bo&%TOP0{&9MU5Rhn{3XRJ*+UGjud_43 z-)l6Bw9sO6n~%Rsv;t+)Y)w@$J+UknvjQ7b0IgGV=m1ytY zkN;N*`o@TQU|skp#DjBBCOQZ-!@{E!UOmZ2lj;;)OCC$z{M1v)r~ZKmT;{ zY5dB>gx69GRRqWoePL9+nb^#+F_se)!8j-vsV|ZzQ;9^JU8Kxtw+j4Hnq%x%5hCBf zXrjbtn}Kjn5Y#UfN4Fs994D{^*q}ZN@fu9EHx|G0m0}WAA4!WPf)7Qe@~kgj{lB;8 zx8oa`)q1H@?I4-~HO9hkAW@47sm6}}+R-6d@fSiJ9ifh|{@T%BQ#wh*wfNW(LT^(c zr5g{%9Jk6@C{lM#tP(g3BnxI4?Vg7@KzW^g6;>}t5SWvW`H%K*?LX>|rMvt3y76%y zabcKNDy-kF!`4}~A2IEpr43VTROcj5_c8`k7QileKq6oal}k?ArUaQ^4!;el!$lZU~bgGxeW^wpDP4( zTiH^Dt<#a~#&=}1JI1e@+_m?bo~@&MioM(RjEwBrmb+<&MduBhzDBo5qBFP%X}uc= z5Rr&b(jJWlc|m5!%1NF(wxR4>N2kI@I%c#EwHmRs5uO^|YF`+?E;7BfkV@^{H7U(L zYz*%ljimEEkw_0}&yMaKHtg!gJ==PVdq%hRBxYuA%JF6{nF^wsSbUaEUsW|hImasp z!qBupT(A$|Ud^Q^D#0&0XdW>!BEOKpOhh&G@rTC(#PfzzJ-4@al1{<5bfxlD5wmYN z){`2ZL6(-l9j!V|cm*9-vQ4Qkg5ya361qGK@yh|#ag#v;U#2e?d<8HbIv_^feP8Mg zLt7R6w}vZIsgZ%5t%mdc12b-;^``B$gTMEN)O~QA=lA28!CxBBOdZspKqT~%*A%-8 z9FH0>Gexl$+$0Jt>?9e`X*uAJEBTTAzRF^S4K^#|j`FZDj48G8OzOuGPTDC?~ z1~-T`LrODAvTr1$U0WmX!5Ogt&XBCNxUJW1u>c+c4k#GDGK`ay3YmgNQaS=g;a%vE zk^IsUv|eLlX{l0?h4w>3ybXe2nR)7fAAHs%JC;?Y#3Br8mStd4C6JobaEY7rgiyR^ z<;d^L5x5!hT^U;9p-7;KC+7hsx}u>BD^}7d!NkB=l1VrOu%c9rlwKP`YMMM8$2;P& zd@Po~P8v~Np&k^7$)G#?I}(Y$_P)di^Le$nxg9A9|3b^tXy+;=#d9NvXg z18+HD&fAYe*grKAFRlS->x-s_k)+_n%)I0H5gELaG{A9&@>fI=NW85rnrmz3;(Q5J z1y?GV1{L%tBr{NSfz}nLBY=6N9_q8QAw#TPMwQ`Z%&ro%>6TP`X>M+{RGL%Jcrjt% zG)|v$eIeKbg7u$P>OWbt&a|9zG!l@*!`B#=1!qw?wR9~mhPoH_Zg?MW6X1Gj$WBn-8_HZMR8RvT}i6^n2taTK`V{5^eQW%@QIkK9v@` zHC-aF#iLBX-7f?CJ3U}s^!>}YLeVHbL~>I_uzWaJMYC8acu0IxbiuWrzD_u3(SX|y zxLgMuTVhk4vu!bl;~s}&yr<(>$2HzStk}^}jD^~c9B?=exK?=dvCgSj!26PriM5_& zPk8v*){*GYvG8yhe{b6|JiKo7;mC4yq;+5@k7GT_&*11VKKB{5%oGh-vX%6MS_YGU zWH@_jLb+f|P(Bnm%U6?MJ+vqyDM>qB6_yU+Sap4A$uf4({Kqn^_zsb38phM^iS;}` z6o<(R$@PR~AQ$^u?NtE>1A8hnQ9F16GzqsfgrW!8-%C}`X53;1}UB+A1kx66175tEp4&7JU_lV)0K)u zeO*3ZG?Mb&@{&O1o6_a~cGqyq*VX0YiMJ@PxL|C`ov+n~HM>m|>L$7)vk~(KAVEQ` zG(6Qn4fAk&X(}sFDGBFjsI#4nP*4-OAgCF*Oc=^nA$Yk$OS4lxa*0Y?{RbX1QM063 zspZ-!VV198zLPs@)@2b%Qp`TIK?R354Hkn-TqK!9;kR`vTad?$`ATJ-1s*^+*VaT} zyTl6rqg<7ZK-i|7vqX^)&M5>QGADytK<&wfl>mn-QidZiAjA@3MDCa-@mFS*D4St! zFi9;0q(kh31WvTTLm#2biq_V($9t1AwbFXAcQ!`y8IiZ%lAJ+xJ|?F0{a@B1VrrDK zROM@G@<#@`P}|bNPP@2L*7!Ze!p4|R0b3LEQ3fFn^|2LK%37i^iA6p)GGjoTGiFB4 z@xf5@Gq(ZmhoQ%&7M(oOx_^x2g2wi@j_4wXlIhH>K?jroAa-mvKUlY7nWZTe^Q9Vw z2WhB|BN!E0ihIK~gTuX+#!tgE$bf;a#6R^fSZZ$DLEB9Y6F6K{U;=l$fLyxWA%I5( zc*gK!AqO@+3Fk8$C>CjH!$-K!gyGY|k6~z}Zh9h6$pX2^z?fXiDf3{|uELI%xWm`t z%DY-x+u}}pSlOdWMIH9A-5K|~~Ror4M<$bNgPy;TJqErTug_EtwvOG}U4({69Um&$GrINbKu<=c0+cB++sw1dQ! zYvKGBD0vAaX|3A2_65rlM^ZK@4xZPV$R#NP)+-gNQ$)~y60VgTH^Xidp*TX6sh%Xr zVozFyu4|h}a5h(%dzI6WHD94aSEx`zT!ZVvg1Jy`WFU&=!I1%JD<(P8Gm;QJtWcB? z+!g}@X`|#T?15OC;Y5`|;QYQ?-HV@->vkwOPjgE@6+!jTS-l{81@BcDsF(m>yW z^wS2#4x(D6nnI*nJ|BMivDcvpKLU{&z^4k|QvHIOK82rZq?tb@(tJhBrxPO1W{W2> z6kNgF37rA5W;;gnH4u5+31m&==tkynrb!E`rG5h$BOU`Ro|We*gip`K(k4b3PWy*{r#wBsb}Mh(n&Cxd%>XP_?i z?g`uge3yd0CQ_6Pe$;hF8jN3C@qr&h;VW3&-cln}`$E^5ZB1H)DxbL~v8PhK*sMpO z2P`dEDl;fTe_{&i!-U}!^6NnTzJe-uc-Bg&i;DteNbXeHfHfTi%i~2Ib0WP+Zd_f( z_Xa1SPSpGs>`g}{oTmYYwi&NqE}%`zTC z7uI921}9z$NNL+{(rIKvB&G=AS=zL$rj~ipS~DcvvT9`Qj7PCz2tt8ks+9*uGG=S5 znHhP2W#+VrQ*WD024is7?+dlJ!@(e)ZCR`AjGbt2KM~tmSp+a(>U7|;O~wKcH)KQi z{T`ps+m(&GJL}6tM-M^sre8Hzn4500SYGQJ8;JFU>LCf zXr{4gn^P}?9=W-YEr=z`l>t~$2O5Cnx}(1+iL_r`OfR~!lRJ(cefUVI!=)2#Z^uEu ztMACeN006Z7my*{4n)^bUl$~FwsBSPT%be3ajDhja+>3U%|dHza9a}HXpP&F?uPzC zA{H)=xSd9erx5mm6VBrm^cOrWhSNQw)YZRw8L~yb*t!VCr6mDwFm$%sW7DRf zbJ1RghfPdsTyJXp*WqAxB(W!w%x(iACm>cbH{?oc6?8x9PbLHqE0vDSigpNfq6~)d zINH(aId!s~D<Q!j`})#j`{9I}**n zG4ba?-I&R&yO2Xhs3L5>QRp4;XT3r$4CXY+KA27ytiM+;byPz6B6!g()xXBhwU>J> zfnfV>XVrJrv)H3OPZ!a``Yq-NK@eRRD1s>{i|UDoAMU&Dwg&-?uNd*$ZtHvaVfE~@ z&nC{Cc{e2w{(JYCGl^%P6*Af{rY#mjK^774N2Y!fG{8yDiOUs8=1JZG^3=cl*elN% z7<%CJ*C$Ogc|A%iq<;D>Cs-vfuQr~!lf3`V5AD-m?&}y}p2LC9oIo-tq}IdqXe2aZ z@)Wvb?j;egm-cXkWmZVb+NiE?f?z$0TeJ8FvvGlD$$sq4TiZyvm) zT7q>U*u8?U^1<#Bh8!+7upWfd?o+3H>IA`67+i1HZ=UKpsy_QpopPtaOynGQTFur@ z`v3dwH+Yp)g(fDWmmdI9x(rK7ZDH^j@Rd9&;^vL8xopBy7)wS07R5jKB3I-^odSHt z8c@qBL9fPXiSrUWD<+Vxq0Tin#Re`yz+v4@eTRWld_c&)N7h_BZ5`kLqd2Z zH$yn8=E6A|;GhL=ax>h3L{(6QLZM>JUqSk=ZcI&4p)!n5vxj^UuDYl-%kNcMEt z5SnQkZ3ht>h4gv5kkDozj%w%xI@Jr4Cm9$ucr2XE9$;$lr0F}pwpL|>)Fe+qNBubH zb1Sg&xFmg~yAM5k-Su2E%?62@lSd73y8F81_Ve45dp*N%8BTVL42*P43xg?BriOEb z*JHQsO>V~-Zy&}9fk4L#mjA@-HVJKk2#)6_H(G)PU&FW=!V(bXHSJ)O0aO`B?MV^J zNa+?+KOEvQwKP}A7v=`8yY;$lpe0T**{&GMm^#+FZ(r-Nsj-=vF&GH(Zv!cuK<=@r zepg@#OUkY#GJ>}rGD={PSuQAuUax7NtLM*$=kpa^2NiFSmf;9k#zm_e0<(n;y4;{c z!w|aYjG(Ux=XLM^JI)GZ%@vU3q z3*xjqtua_4LvSHNL&TGrXbiNj0Gn(WLyStM=gzI4J6BRBaqkLVEb&?f6^CU=|01Xn z^Qd6Z6Iezp62XL=`p2j+g{QZMgl*6*Ug$Xn7K?U5+oJIV>TCg38_#nyA_KDuv+o7G z4(d^%23x@Ncp0D&p?Y*eor;i#z`WIm2qcF%8BC~E!N>rMA@;rk>#;n7dYL&Ud5jY? zPm5K)x=~tP#bY(gxYVW#Fqds=L#?jBv9NLB@*1oFRtj}oGY{jauYMst>Rs?(=>~IU z#)M%c@0*0Aw@Yk|HF8TS4x%fvO<;%gmXbPO4n52ckYn-8pyk(rC#~DuR2duyGscKl zpNBvKFGK^5BI|fQ976_9aWCOglS#FL@3;o5D_E9z2ULRP2{;?zA;O_+9)I}lvY6~J zWlI?76%4dBeF?{bBwdvZAkCC8YB1{`MKxzFP_cPpZ@9R02vBKI+FDXYVA!zA{J-42 zd7L9>eJ89(N0sios&uu^ZmCSZSFjDb5TMAsZodpm7QYJFSk3GH8|h7e9{jV? zn_|Ssfp_wOk|UY(QyM7})OXwt%mX}xeb*m$t%O9Ihu!b;TBlvnleBs7;8CVVe9!n@ zcZWo`e^&Imk~Zh-qfNfub^>U5MaM0v7Qi^(ngQ*2;JnpeBvs)bl(J0+F3ht~2Tyi6BNCo*}&efCjb|Q!=N5koUrBeK8$YB#rGvXNuFAa@ulBD zc_xsTxOW`wJt^uZmrUy&W8foFoy+?V-EqeuL`CpNCOL-Uece1$1A(3q-peln$N}f0 z^iBof1A+`nNip!E$Zoi%ATO)2J7VhciO_*|`rgr)Dy1gm|F%|MTX0XH641@Nery3V zwA@oUu##?9YNMH%1r^jGdwqiO+30|=bSNkbauYFF{D(zg`mW|j)1Bs%)19q7d|k6Z zT1IDYlnw@S0N~UZh+YrUK|l1IkWTNuQ>X6JQ$WxIsB_c%qtW-@)WMRF z;MxQ2<0+mr&`sGAz~k8QSugD0UnOyzamn1+?`=gfuMdPT+dx`7oa^eO73S}m5LM|z zHK$Owhcv_fx(1gB0_BYtz(tn`y4WLOf~!uXxhO=d5b@OSHl>TKv$3%OJ*ZDqbH_c1 zg3|TrKSRFHk1cJ);;rtuhyK)t>jlwOxt-5y1Kog~7fHEX`_La0Is)rysF=ZzVbkBx zD8uY!1t7BM69w87fXG=-YKYN2`RV461YA0%xRjf&}|zwzfATtmSqsC=KyzlF+W2k z8KBE>has4-{&!sJjPg|e)JHq1REIVwUq{E^!PEJ_cIL>DGk6(#+$ApUONhY&b&ila zC6$O8!>r8h@_O|uU@ZSs0yixd6(cKF5uigu3jTLnqWDYRg^Ci(IfX>g`oSc7W71Lc ztJUi|DbJ+rRsSR!nJ7(M_m)&_dfvG}<@D?`F{QHL%~^{H!TG^t|H-6d)*n7|B(V_k zRvONUz?ma^%JY#ZO1J<2z*S^&K*A%6p8?_#0-9tEqCVt)UF)COp5fAJHo4o1p_qFc z@}dNlI2>O=L!CHwVq8%nU*@`@Cys-wm@**V=`LhlGi^MZjx$s1M{b=4QB@=K2c~T$ z)o9Sb!|`=;)0cIx7{rk+XBf8ykYn34FxV(rnIuyVL#ED8jDG7@YU|?G4O-m9faHwJ zuSz1r5+14t!z{104IdGlMB9U95e7`RBA*d!uVmUnuqKUWve5_+csX|FRPEHR-PcF` z!um?K9Ta@gNXAM1T1T7BU8go3NM>I@vSmQu&7u5zj#+-LkGwK!U$BZIikuGU{$fHv zSRh79dJPQIlo&zNVgvOzLrRz>xqEjKufrT0)lO-VYvOuIu+=@u#GXBgSb+8x>-Cb!1z6H)njT3vnKkR9%UkX|JR|aR*uG^5R=M*0p~J8 zR6Er|a+jjy0=$^e^(XkwikzNPZ9FS@}Ev3``(K#$HraN?KR54=-o4Yj`!Y zSv8ww9!aW2e3CDscCuOY8>W{>?T{gN6RCTKr-gD;^~4_BmFjbh;6-af>K^j7p<%;Y z)PvBu0?DPvTH3`8?pne}4H3=|1+11D1gaN7C#VvzWu#at)iPH6A&F8DN)7^h_0*g4 zndr*cp4a<)HoNTW|2$jkuY66(y69vhYjL5W*8_qkzW%M3#vw9VebWXv;B_^-^I%khMU!zzDEqpJKnxew9lCVJu{f0*;6EI8r&RTPo{-KdJ=7jR35+xOnU0 z;(f>>XtnNXHmO<5yXeTRi_A^O-gHJI(UnecLZ{OmB6JXV>PCpFIX)#1pr_o}nJBb& zU9IM^N+3+7VyQTo9`Prmqk(8qmeQjjNX1-4D%u=Y_qIHV>Zl8i_x zQE*$`;P_^I%ADo6GbT;J4^K<3iyqt$}X#QUt`Sbce$9=ZS&A1%N8EyIOf99y>#w~gF+2CxHA#l=at8%(S;l~;z)l8ss2R{KOOk%*1kt$5fmNntT%)cyc+GHuX% zZP~2DC2*Nh0@~|#2%b}m4h+C}5%ryzI07ni>ao3V_yJyL_p*t-{YSY@P=CYT$9{m< z*}eTg*_%)~vO#D2V`O-su2s{hlKz^PtmF9~Yd?%zv z!t++vqn=~^&e~d!%BJDF*nn)^0Y>}@NKz?s_lcUfT z`3BUn8sbDt0t&MlW;!NK9Thd_T_-!@(W#SnIWJYOy|#*a^Vi=a9+^6Mw|Hslh`1l8 z-{NfD({k>o((}%NJJ3Xsm|@=F3fE8ie%^Wab)(+Yb$2^2(ZkTyzvRrGxJSH{b^g3~ z&xuhnKbjYRUca^nM7#+bi)k>?HGOmLAV!gKgRAP8twFut)c+eeZtiozbDeVaFF;$N zc$toRsr}N*OYrYdj2$7$=06xs`TUW;d;18kD$sUY5Si9w8G-?yoHnHJ4!CWUxS?6r z45HHv@gB}sfW(I5x<;6#2N0AnZe^C;!t)w`cHcg4iLfD<`F*aNuyLW08=Y&6ex`il zd}|e$l&Lsab_5Icb+-#{N3S=t99ijGf zY-)56oPPFf@n5P{>F{o3eGN%(SZ@^1-&{OWJ#@G#4I%IAFj|Vd0idi}%3Qlrd4$A4 z#GP42(xL!{2JQypAP~<3tkECH1aWxI4P`UeSa)~f-UfaOCZ0ZM_1S$kFLT)(Hcv^s zA!Se9AeKC~Xh?SF+~J7bCV0FKn>`YCX8L2!q5fmUe6O89_ACwCoo&*M=)7kI8U3Lw@}>>NPa*>oyy6wIbgJT(aNxj z!05?4A8epu-5NR0#o|YF8llHrUBYz;`FM~TLtYy1<7~VO;1%#A&YH8rK0QBQs8kBs z?6BYeyHr8A1w(c95V|6C=kqgfn;I#c$qq-xBNupK;&<~Bz>s!L$W8fzO!UU7Lb1G?*f_ z4~o9TTsR#>QQ4vB@#xT#+3R`S#ByC-%4(XH{UDoQ@klP1iawr*9-j{Ro@8wv(bxai z`mrpGh!RY+8^FFv4utn?H`O!WClPF3VnsCaIg7^ZNMWa+KyXsRck|V z3%AlJg@Z`+KnxDa?*LZBv|@`&ni`zZ^<(p|jV|qHZ2wa9we#uYGQ2Q_Bf-a?vRa>d zJb2{cd~y27oGPRRb?(Sew3x4CRKMS^W-5koO0Go((oTa@HMrpm1%+b_?nAMO3jBR_ z)Qhe+MA9LU!MZ7ta5&<0-DK+U1_9i7x}3sZ^trx}$|P?c3k)BM9+1mVyL`VH3TJ$O zIp*p_BqUO_0-*y7aaxFEP}gy*`;tRYwm+4jica{;C?wk3O_} zf=MHhELyINhLS1Rg2-pu?}ZW?Kv%;XmoF{s`|SH5Lm{Ypu>Emi-0s?I&nE$eP0-dZXkv^EJSddUQyL+%!7z*^1(xQK zy({lX2tgbgG#T#?<*rcBrtk?v2GFfM0zF z6F{!~ET9?>4uFPDfEpPA6%YwjB1PJU{zAx)C{b2pEVO#}2c-YdePWDg+a2upl5gyx z>DxLtwZ-9fmDI5(dg!64GNFGc`Nk$&(NzPy z4=lFqlV|(C{o7~H{@@2^(~qGz*0=PJFZ|%9A$%kPLL-lPk!@Xb$iOzycvNp5IG?$hzMqu z5Yk-dfnW7%Z(AJ9GdrSWFb=|sRrXZ~XGO~d;RP9-NP$5GY)m|7l>w`MHK;hen?h|< zYG{)Nqa@s=N&lGNGjx9};YKq^D%ApkWVWbr0p+_;VA^BS>72Ki7?rMF;?-JI^6Mo2 znYU>IP^s{@u5;a-bcHK~f}4_q?qb+7N6Y1lWsQ3}fs>Th2I_-=4ni9|oo*HGV2}Gf zNo+U&>#KtJ72PhZmfJ9wGf>_(ay?d|5|i7P*bOW%OLyLRN#=zC+N-N!6a>HpO+XWP z4v<-ZuVi9ZjX}K9^FmQrsoX-0ybfrRUVn@7Yc}-S5DXH#L?z+NM(CUykV|!TA-980 zQI9G_Z^=lyQdX>Hx-LVPqQ-x*%1j9=e#U}?+lgg_)aYUOB}}D%qQ?1K=~7p-nrQi zInGEAfVv6LfSRZv&a0yV!4a~zF*&)xKB4u0#N5Alax{2HCAt!=++kee`K?P}Qpt^s zHg`xerr5j&zrvZV-<;E!yZ@s*+T}}=lb3LT29V~X?AOdOj35P%sF~7eU=$!}4K{T) z{Rf%fPZ#DU%eQ1AOOu&@(9(Z^|DT(?EBp_c$>Y)Nd0gixuJb$Wlje08#T(%FyvTQn z_$&Jz{O0%5zqj=(whLFr|KYi*vGdvJ@rmp|UOp-26QN$Bh@Ls6nld;i`qONm5`vxQ zK_HA-Mfx=bNAR|G`joKecTVKsCGk5m;hKo1Zk{;4rR13&iw2l$gQ6BFI9xAq%lCA& zwj031O*y3SQve2P4bfL2n6i&i%}d`$dF9Kr{70~cx&)u#?D8$kID#Jv_P@x>s3L4v z?E!scnbeRj@&?SmpV2h&%)^4)BP0@n$1Oa3#^2`WzE@1h>S~)+WC_1W6oGk7M2mEd zXc01JAX)?-#Qu+&q?<8pzz+#h^+l{em~&E$*&(4W3rsL|kS%NwZocUz1YA^3!=L|W z+*t=tq?D}mf0LYe{@qE|sZ`2;#Z(b(TUjhzVe$TdN>;d!aCZ_nP_aVm5Ek?#L(a5m z9yc#eGsKk$1PNos;mYXlbPpn!pw(%iiRfk)p6+;5FI^(yBv@9V;ll<1(qvXAU|*~XC;Khfpe5A*FJ-|pqx*YRzK|BA!4 z9?m&PEny}iZ_NO;G(2Z8Ad)Pg|3VLwnSt;IFC7VYL^hv!erf6XV=(e9FEa}#pKxM< zfa(;K4CRBjBRB@TMx-7DYKe3~yhtx(sC2lFuweS9TcZZzJhrR4Dw+TxPHVfWOU*RR z&NegEv<;>wL()VPDcIdM)%Wk&9mxf&t$*R+x+j?#N_k!*#R^s5-1<%SLiI9bxYhqi zask$DwCb6(2~My08eg>#ljiQesaoLk*#Ys~xQr)ffA1u$~A`z-Tl=(?jqDTM1(RqJc*^Cvt@D;vsO zl8OruKlID~oU#N_v9_{w^qnKx${Iv-T^ioNJw;fuAt@1W#K3Y!r7atkfGE_qVr(pA zOI+d6xfTeXYc14(KvFJ@5gd1$?9%{cSS=!?&T?C{EDqSg&j}FEp-3>{apdv3e2=kr ziXz372;wj5#Tq|^v6wQto}~rz0S^sEISQILs+y(~0}uqlE{giWOY5Mz6KtO4{sq(F z-2-uT0-&f467}sQx&{L>6b{fnq)>oTBjux|KK9tj$6%BZN+(M~Sa`?S$uY>*2y&(4 z9l_-kMsY&86UV`xHP8gQup2goERV*e5OFX=a?mMgT+HoHuq((Q4e(?UMK!fC)3;9A zB71IXYNVLW7E!0NMpIC8c^}rYRLzvEth8OpW_y}8HPxA#lHeWSE&{K&lLaD~Um>IH za~f9992R*6;*c5|mcv|s#GTNJQHosvCSP@-6;iQhVM?T`E zY*zBR>NFB;p3$<|M|e|`V+h+?cKNb$R(gJG^v`Fr>)AGCgt?H-?fseT@>-TZWk+`e zENj{_TN-?jrUrVr^&r%XGzooiA5efEgAe@ueJYUA8C z${5`CWz_Wp=cQ5BHYXJyNMa*-{HGl>&GeE6AMUcYbd}j&&pzXIWoZ!rPFZQ`RX&A; znJyu&{HQV4I$Yop04jLF;P6ub4b3|M22Y@BOIHUN9a^&B;n|-ATDGkMC36P8y)bF&_i0ywg?wIkmhq8%YqAlUK&gb2Vv3Y z_@cubOa~@kTF<({m^)eDc$@I4&AFD0m8xg66m!F$6SyO-n!hkYWlTQihGhT-zX2IR z*RpnUSoSdFfst%>b=wr=|pXaW9eFMIbvG7z(j!pCvQ(n53(XM!bjAuy6nCaABY zxiqwvo9p!|1dvBao;arS#IJ5iPUTl%>t7{7WE*$PZbIkBUad&7joHI)4B#U({3gHD zVUwC-znZ5bM8m(%0KuYDM2u+TaH-H7>~L6j0BcMEnWsrU+v13Nyb4sKPbp^ zKk*}yWUR|NV?Xr+hG4F(S{A8X8<&uE^alwbu>9Zja2m(}grJ}N!AxNYl3#XH9=$4f znh9d0Sn*Va0fKqOC%KHYLrdcN)RO~Cz@t2LWy&O^H6MvOSqZ90R(f$8(cVr`RjR$f zvp6rOsJfarNZkN4myM`PBvd{(q7!!FJ38G0=zsO)NNsX!aK1m>Ho7g<(*_^XFd+Mo zzU)_=o>z~l>Hwzd%_gs`*X^LCuWDs=F15*+40TyYM;#?En?!S2AK=rcYR6nYxTegR zF;|uJ;#^C<0z%rl08X%J7ZWHi`P7EDEN zC$^d<0v%&DC}~)8jwb0Vq3zS81eln+Ml%a@2e%Tey&U?&HZUlaMDPTI>s~O813O2? z%!Ph$#^&=Jn@j)G7LN1`h3a;9_$msWE(8rKbXz2L^VT~J%WV5PzQY&*3vP{I#{yny zAcF|%g{(m^rt5X)BfomQtYau{SVGRdB16)xAXl|PZ4HpD@Z~F@q-iG5t?)Z3E5QyC z-_4bRaKQlxNO@svs(_JOIJsqk-zH(KLdF}=tq3J46ZMsm@+TbK_Qiy}VUBTtUD6PD zBIPR$bx?1oe$vyH{aaYM(|hsimU(kl!AHpoO#}`jekBnESh?Lxa5Xt(2{wxXS>(#} zcyM~JELC#iVWO$I_&a4wZRg@co!Kd1uD)7&{1cAt%1h5jxP9?=ptHX8S03mfIX123 z207lo@cNv+`o$+JhPm)u8@|fYo9-ddV$AE+mmV%g-6P-QL0#OARs$LvavnDbvh`Io zKU!_#)mep~@PgLW*74H7H|fV4aNUuOmzpwB-VT0mc=UPl4E&3f$Uyq=V#Bbicq*`FT_gSY5SP2W)kSuR=hyc?Vts znT9}Nk7h_RrELw?(eCj(sl?*e)*;+u$0`m~((dT@dXb-6U!(7_vAo^pc5K$urV;4D zRXooWQRSF5&RZ#;Mx=rk6ePk!TBRtw`_$^-bJmFC)P234kU+=C=E4Hq298ul6)sw> zPIAbSx|B=F{bQU}m{Afu2a;UgdNL#tVspOfahcZ9Wmc--N&OJ$ZHoYR5kZ;= zX^1EJ^=6ZzO7QJfI)u%DpI88oqNH?GqldJE=u%#SM~`funJLXiaDQf@8UiRR(C#49 z^a2u2+X%d3Qj0FTGGAFk9ds1!U#s;03-`gBwHUpK*rck4byR0Vpenjv$PF`8+`;p0 zFq5-0>=S8ZnyY<=JuPjW;@v-^%MXt~$T*4QHOs@@kA6o+P-TH()J^;mOI&ZInUb z0mB9)@etfKSf&}OxFgAS9WkBgqT0WpX$+n7sBi~bsgg2H@h3EtOFa5ck3~tr$N;Y< z;R9ir8Bau^W|-$FTG-S!bCnryISWt!ma|`P@JDe(dMFWcwd!?>a#K}>GRo&mRBJNa z`^ znSHgE^FT6MBz}V=Ic$s39clhHkI89VM=9P#hw~P?)zO4}6Yj3z1cNkqj>2 zB}$a`S%_|~a9hr05DAsZ$v3CM+8ht>>`KyHVODh0gSf@n!ki@4BR(HHp@)2uGSbA- zcP&&XYJuzq%%p_CIv(R?EI;BMAm->4Zt(a{amaz73f9RYmDs58p}D-P^wB8&p>0Y< zJd>}+i}y$#%_9?~;fjj9?GwBG=S%^phfQT|T58s|SwY##A8P8A$%QnT$0Gi{W_5~!*QMl*rhjw3|o3CWX^RckZ zDKzLchJpSTC>@C5Y@@=I0s>}j1z~Qusu(mE@W4z#mh-yYW3~q<`^R0>=W~j=Hf|$H!CV%Ek z?kf~&vlVmOLHF=hGo4rSAESWjt+o+7Kh^|N8}1UNDB2l`ibY8jmj$>)mPu9FK;gJA ze>U;EEba6C_trnze0g89)lxs3_-umN@!`Lp{%<@LL8G@>8zQxwY4>{h|akiAAi$Ly$%(~yMzvu zXCHsl4d*Dli|FS%q6Hg}{vW`!5-k+*v;t+hiTKa|N)4o7!B*U;*5)0chzr&#kFTcm z0|%l|5vPPN=Eb}u;HOkOy?})`g4HOoyN1-IdIRaF^agT*Z3*|aQbQDGXI2MuSeb*} zuRVP~b5hR}tKH_X3s$S!CVFjlr$e-P?PuQH?>>F(z3jQv9{o%1U;sbz+HFpUAo$O` znXYF=%B&qPT$rx77yYaS!Qr&oz4&DFSSi)ril5*Aw01v^P-Yw54d2Iat?bQzt{wY7 z`^(9_{G8|zs67h~pYaKz!)dp9MH_xDB2`epk8qtmzyZDIV;2;|1*oAAUxgv!>+83b z=RQjHOz`@>+eRB7RgpDph;-`^>CA#3VATx-6H|u{dgrekb`Vd9?Mwub#-A5E;)6m* zxL0fnuf^*g7FBiy8s)0%Bd)622+VDm5*@l_svlhL|0BB5g_i-xni2YC-i#v4DAkRu zFT65d#CRw0A>21Vql}c=?RIF>+}vv;Uad>Wg_O{(T|9{%jK7XO-m8}6PX>@MN+SY@ zcXWa9vC?s%+f_g9o|_QP2O+8Q(tR=H%VRD~=ITVCA{9z8yQd-s9FcuHSz4275r*P_^Gy2QN}4lhBg(#G zbhPQdCw*Tfjbfh0aqx-Fk@k>EOfifoL20rdB>0+Cve`vy4)7lGe!B4&f6<_5R1jJ& z_m|u4Ort#aO0YuW6u}0)Ox1$ksq(#z7%?Oy03H$;p;O=wxxg221D>BiT{-lc(WunP z%lkC@M0w0OWn!~1QZWSPOT16xrbGh{+9-hhk~lUC`YE$6%Gp1|sX3&Yc@5{gn8eafl?#!+yl9n)2aRi(-CfZZC8TQNHb~gEWb65hxLm5U<$! zfWrY27D{=s-hkLo%$sh-L7@Y=T3t_96aM+xS)erZ3T;zWRH1H^Nj~X!zh5}uN=;1x z4=$%9785RZ!om&^!0h4Q*5Xs>a|!apItHJI@>R$3la2*`; zDcazy6p+JKD~b@lW@Mry?7JS1J1U=i&1+7|ZZwvOy5o0u?vA^^)|`FU)Abt)C*^0} zSz|+A{ZC*C*FJo_R%OQo&x2m@gXx7(Xkps!c6cB3I^6cTYp$8IpF21@da(boQVMTx zS-$0#Wos#@x17-jD?s#srr3}n5I2Mf9~HFTmmT+`^2tLOft3&DJy#y-2Pl1UlpUm7 z-|-4#!=i7=Tom{Sl!@>X(T4bjoB>39K|ggAC}w=v2*dA0A`jK(p#wJ)te0wjyUw!F z&g}A!Eqac$in9>)=9{VhfmBnCm)cL*Ci1^|XaAAS2QBPn}y*z2B5NB$+v-j$xsM0}GjJC4E<@N?M--6G&{ zPX+`3n(jZA{&YGXpLF?#N9l)8fNY!KMoRLw>vOS!?xqidJ+ygQ-HO2PdNWIGUd=Q= z;AT@Ciz#8P2Oo9rcmmF!YFb5V5Wmw`PRw$avDsP?iRBt5N zfpOQ=(TVSmAarKdauUM;&Bk*TL|H*}G|-E5y?nqb3217Eut(JN8BmmwHS9pJK241R z#%ZS4fD`h(QzH7RD%u@_O|lnj^OaPBxqT3ltTsVSvnP3OM)M;{>(8?8QmvB7{@8+U zA<&M8JmLBHzMHs$d_4p(Cd`kh3oO6{<`%t9n=>5F?s2oXBQqe8e^YVN?Uie%#RdvB zcs!^&@ZA|yCQ01HIiH?-D07W~$SuWHb>!E`EF-$1!qRo$tVGLF)va z4wpjRBnZ)B5r9`}q|sLJS3bM^a4(rWI(zWceQI*{k)Ip==x3K_Tb+HMN*>)6Bx@EO zea|R{XRfsq!z3ME*D`9vt{WvVxS5^qGZf82?I}wc-H(xuP^JLb0k9|f>BbgenUx8vyZ&&O?w?lZmKVP=Pgx-s=}jJQ<2}$r~!h0 zmeoz;L}#hiY@$RN5=)j=-ouyp#g!$k+0uyMp>Ua5)r;Y2bjvE(C)uJ560oJc=LiXT z9U+p88;}pS>F$^> z;ch$>_C5Bz_oMP+V$Y;3PwqMM=BPgrzWwk-sWdS*3=Hky)~lD5zMj z?;9Hatj>p0+>9Dbb_Vfx)n_<%DlzA=2znZfJ9^O^Gxrp0DjU^nvfUy4z_GTX<~ zn%ReVwUz$Art#IZQ=y;Y_2BvR`Ak|kofiAQLf?*kiRU=;-|LD7*$OtW=rDvaq-&!9 z2Wm+G`@s;Za%8Z`)nJJ$2=1R5U8#gl-E}HdSs9Jp{J_n@pf%)u*6XGkR}Q1X)vXc! zH94J@@vT%o-~TK71Kyw;wcmr^rz#g6>lBIdTT@5{f;kn^t(9hO@lNPN_PoSRL#%t| z9_#h%g`$#0JjVO=`M3GA*WEV0-Kwuwg63!r8 z3HkO-J_ahM{BmPp+Rj0^oI3|WNWO|ew83s+>8u-)2zVC_gqL`FoUa{|k%wQoNShvC zEWO6^0+$E|u3%P<0z7P9fnBdJ|I1u~R-=tRYO%+q>ZBH z6iIXR28DRuYD#fW@{$o785Pwv;;ucK>?kKQs?!(sM6=h9AK9NM?@P>&ryMSqOma^A zB`)sl!nZ3ht$S{ZCWk8-uM+m#L*Y~^E9YY4Bd}6s`PZ;wX2K!a79b--KZXL$`6A#j zw7nPzcobO|f2Y0L1>GmGhY`AGm`G1-v|)AZbZ{6rLDtd%Iz=Ky0un1_H%wX%5X(1= z^EnQeU|SIm8@MTMj{!h1k2JsqVB&zS6NX??kVFBlD6YlBgbJf%IpXVo_n0qI)-LK> zI`Y+4OKLWoa;x=E!QjLCpEKxG%No8D@g37xhqee>#^?U$rf-`bnXXpfiT6G3@H@lq zMK}KVj@{1n)h2g2Y+1f|rCQQe=_TStNxQ2=r_mE!1L+32)_}CVW?Sp5QHCZbH-s64 z@owF60GLOAkgu8)HYk`XKx z9|H)}NP&nak^JN7@$`e`a*ti;x7ZE;<~)4aq(h z4)R}N`-0(}ze+V!>=YF&1dVW=$-I`dn#@m>-iO9gRACVXr^=aIjg3*640T~;{>)ALX0G#nq}OA$si3Bze8zmm zTKVFcH{P^w>Hzt)U%V!pRGSbl-V0&tKHR|%NQGrH-#{BMly^Z*&<$B$3Oa4pnWNb8 zJG525!+O(OS4r_E%N-3%z`#%xW&}lN0B;J-ze%5nHtIuT%S)=JNesxhjIIjf{3f1H27 z^-OG}#3tmzqr(bJoB((bZOY&VT2K*J@}$*7S}vG-w+Tp1h^HA@kP>BTwm~M=Zm+%B zJzi~7)!k?0j5Q~{>lwHE8G1%4eA_Cokb}=aTPm(F_*i6$9ZbwO$|jf(Y0@_HVa?CR zL%)tIaNppMe)+yW4Nh)~WeGkRA}3AH@+r|zY=eF;BmT7S@MGPbfdUPD=&hP;tXZr$wV=^7nsGH|H1aDG8$&^JK{t>HT*2JdD9}w>w zsB-}c3I#$<^;Q9~tnqAmlBTd&qVfyjiMeoiZX!HUx>B|3mulI1JzLA|r&56@FDr^I zK)TnU()Exz6$ibnz^HXC?hJc#p6+A3z&aGNTr~;`gnWXA0_xuIT?l!iBGbt_h^ENZ zZf9UPEC4uyTP(qNLvMuIkqkhHS?nL>&6Mm07e&=gF;gIL{;pogaGq+7@reZm;lz+vr6GF)C2>#_59+8xO_rKy>P7uHnB4Di@~J~4`R!8V zD~yo1OlwaLh;4X`qTJXDRVglrv5C`z895Ob}LVs^%UgFf;P zN{!A*_+YDY!lqYkrh|DvKiuK;y*hh1k&5Q`4i}5%y1uu(cX)Izk&2Dy%KK+$_m@h? z%c9+89ox5mRy|gqg1}=J)um&q`VB|cAq|H{L-c;FR0unALib1R>`Pge&Gx@y7a8(a z6eif&u531oCvRd+x{zo^7)!}eLFupv4}%i}j4?#&A|XBij!~?;n17h@GH6wLAiMSYz-0UmKqe)!O8yOaCNn5-y4i<_V&}E|#Ol=78!XlX(HM`-K z^o{UDPVR*e-blQTE57W8<#hAMXknA|UstWnPv~_@T?k@P+1I(k6a}&0Z8=~$27=GC zNgG7ZF^gCXSaT441&Uq-$HA@P9H$bMm|?EPW8!(094re#rWB1D%Dp)x5n>@EI)0Lz zZ*9?Lw<|%}7PpBu5%F2J!)f}b1SSH(pdtogBn^szUq@9|IqYw0JV;4zjhp#o`jfwk zph9>!m+him4#>9q1-rwBi-@+-jDD3sutHa%0;>>XktsOo77`vdJ+)n!*v_2gXYSr=SuNwn=WL8eAmAK@j5cSSCNda=j;L4}imJ z8~(%c3gVd{8v|EYy0>vF_!a9r`StQnZ6Gl0o2@cB8BxSsyVK>?aUb74q-oAGj|eWe zkW32hz$0h;TwNgSCJ#2THiZH$%_pFz7z~x6=D5%(H^WWvSgT$Bk5mAPLNu)htE*J% zu&ws^QLX>%hY|b&$C?EaO#lA_dz4^L*Z@Oy;f~yM4ypv{hw9rl^28L-UkKfGBV|NaEXjuIx(l3 z)wJntMilBz3E|qZwIYpzY>I-L)^oij^L0md`!4#j91iqum5l0>MiQTzEEYe>_ug+7 z`O^$$Y{#>KS`wwKJFtn8F`APmxE3(6&{~S1W`?kgnME8Y>=|Y9P1i?V;|gpj%D8K^ zj&6+?SyMh02%M6~??sKw(Y+3N*l|Er4>*Qp$KFwyOt5&i^FYm2&}kAmf?OI0E|#DZ zan*s;hln`@-N*r?`HQ3WY#P&cZ^Y&YvEV{PB?Bt$Kw}+`kZT?ym%x zuNW*8g2g#amdo%BHBZm8)c&wHGL7ISG|Zlkc;{}c9B@)7QN>bW-)7$iT=_T=A`F?f zG&p?W(Q(HaV)qWzTopjSWwez-i+TNwG*hVQM+$X1g)hs>=PQ{LN@nJ_?sTN$qy6s; z+3$3We#bGBkxyh>#@paG{>CvHPdPR|ZyQlU#|zbpw{dWMI@vf?I37|)Y@aV1?*r$P z$%eO51uAUW+NfrO#bS^rPL?Pr3Ny#)8EN%eWgfPU-RX0gB|+GWqC_tU!alsQd+2!O zbLq3`FXQ7rL3mz&!&1$lkk&V0TcrrQB$Zt@Z8k*%91)d5WCMUQQ*-D{s(`x^`>qi` z18yixrw80O=7kD~nuG`n%Ejd%st^SyGEA{gWxtr2a%^x$G0V4kxs3ki<$K~I!KtZW z29g8&c&4nTF{lO00{a5{0%!v156})-sh+l?+8!PoOM?Xy6=A~4dJh%$c-{Z_So*Qs zJ|4X5CcK{DN7#vnnA^Mi_i^IyKYr6)LA?G4KLU2%=CHXANYD!<^78x*Ks{Htp#l{P zrlD8f!3#{BG-GF0FGA&kmmVGMQMC?Du(7=jb?Jj zHE)L3z2jf=wyECbnJ?g5r{BbBf-WnDc_=6e(GVp{w8F(wX@)=#!AOKTTz!SCfMSZe z%Et2P=IuTqEyir8k4#%#Y~;vcm$$qT&W8ox?Xdkx<$pZbVx9y3y7&k8J0ezioe-zl z8ap=TVJ+F^BRgmt1cT6)dRmnXg2C|=U}4T)@UKI* z5bRpe7A{`&xh|k$7yRCu&$UOeHAA7MO?aGWujcCi4-LVh)TV(mappXP zUd)}LV89_n!Ykkr6rtYZehw#BkXo|*k?*%4SR#&aehkrnwI!?5L9GG8DM#raxsOBQ zcI2}jlw5B4-HJ3i=U_`UL3CRo0!hIUtRGD(QE@wrQRK&ulLvrN#|8$nP{>CVkOs(j zMdd&b#}6abM{S zQ}4^AUpq2#Z+PfkBY)+$mA_L`$5(K#5){UD_Wy#5P~-qo+6+q-=pUNma6uDAREzSw zwI~R69hk~izfFVzcHTZaOL9crFzDPl?DZu=VXx2kooaK@SG{rZ!ZGh;e1Fmw^CMTPk4O=-v_I{#b(vN_~zq}PR56rUGRu!(`UtCB%pX= zw#5GUB!K-9i-1)<1-!LT3A3B*KW zB9t8QOHYnyBiZ45bKdxfEEj#@LfRG1r`fN&!v1hJ;Bh`#e7!2iQ`yXamhP7t(jN}r zR*{QMc{1(hU&Xw^7+|%qe+DVpg-R2zf!P6FS4hJl%VmI1Xu)84*UQPd5T1%7~qnCB?c)(0E{V<{Zp=Do1X|&$JOMy^tFnWSp$cCp@2&$ z24%ZV5fDL&V-DtyF_a-isH@EZb`w>Lu4qggFICiV(icvq72GyDe9w!z-jT^wDuJ2t z&VBL8v*{BH%we~NaamEY+tMM$g}8}W-Y?h?=X&f&VEpzs=DTX=ITSCC25gFJw2%#q z-^iVKn2M(5BzIaNra3}X+>d5M0@zpskAi1-Yi@P-Zsw}w^5@4~i^Y4VP%xj}m{SEu z{JLMxc$fwS_Cu+r z8$xBNVyTHA%#=jvi4hzdwAk_j$;-7e>P5!_eyLDJ;Cd_&j9{mRqkl$bgTCH&u+%9% zU$4yj0%En$_63~~>1ve)CYeeCX#+<74kj2JkXrd6NC@@ANo5460$B`s2nI7SB(lfT zVMm=k87LObzv;Lf-R%QGl}vRcH`|PN+J{=okL}zidmv76J�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 @@ + + + + + + + + + + +