I agree that my use of the services are subject to my adherence to, and acceptance of, the Terms of Service*, Privacy Notice and Privacy Policy.
4 | TERMS OF SERVICE
5 | 1. Introduction
6 | These Terms of Service cover your access to the Anbox Cloud Demo Service (“Anbox Cloud Demo”) made available by Canonical Group Limited ("Canonical", "us", "our" or "we") to you ("you" or "your") subject to and in accordance with these Terms of Service (the “Service”).
7 |
8 | Please read these Terms of Service carefully before you use the Services. By using Services, you agree to become bound by these Terms of Service. You may not use the Service for illegal or unauthorised purposes or otherwise in accordance with these Terms of Service.
9 |
10 | If you are entering into these Terms of Service on behalf of a company or legal entity, you represent that you have authority to bind such company or legal entity, its officers, employees and agents and all users who access the Service through the account, to these Terms of Service. “you” and “your” shall refer to the company or legal entity and such users. If you do not have such authority do not accept these Terms of Service.
11 |
12 | You must be at least 13 years old to use the Service. If you are between age 13 and 18, we require your parent's or legal guardian's consent to use of the Services, please contact us at legal@canonical.com.
13 |
14 | Any change or update to the Services or Terms of Service will be made in accordance with section 6 below.
15 |
16 | 2. Services
17 | The Service enables you to access a demo of Anbox Cloud (https://anbox-cloud.io) which includes streaming an Android system from the cloud.
18 |
19 | 3. Account
20 | You will need an account to access and use the Service. We reserve the right to reject your request for an account or to immediately cancel or suspend your account and your use of the Services at any time if you do not comply with the following requirements. You must not attempt to create an account or use the Service if doing so would violate these Terms of Service. You must:
21 |
22 | have a Service account
23 | not use the Service for illegal or unauthorised purposes (or which encourage or permit illegal or authorised purposes), in infringement of a third party's’ rights or otherwise in accordance with these Terms of Service
24 | not take any action or use the Service in any way that might bring Canonical into disrepute or affect the ability of Canonical to provide the Service
25 | not use the Service in any manner that might be libellous or defamatory, that contains threats or incites violence towards individuals or entities, or that violates the privacy rights of any third party
26 | not be located in or use the Services in an OFAC/EAR embargoed or sanctioned country or be on the U.S. Commerce Department’s Denied Persons List, Entity List, or Unverified List
27 |
28 | In order to access the Anbox Cloud Demo you will require an invitation code and a Single Sign On account with Canonical. If you do not have an account you will need to create one. You are responsible for choosing an appropriate password for your accounts and for keeping such password secure. Canonical will not ask you for your password and you should not reveal it to anyone. You are responsible for keeping your account details up to date.
29 |
30 | 4. End of Service
31 |
32 | We look forward to providing you with the Service for a limited trial period only. The trial period will be specified on the invitation code and may differ from user to user. However, there are also some circumstances under which the Service may be suspended or terminated:
33 |
34 | We cease to make the Service (or any part thereof) available
35 | By us at any time as described in Section 3 above
36 |
37 | We may also cease to offer the Services for any other reason, in which case we will provide you with notice on your account page.
38 |
39 | 6. Changes
40 |
41 | We aim to continually improve the delivery and content of the Service and accordingly may make changes to the Service throughout the trial.
42 |
43 | This Service is offered on a trial basis, and your use of this Service will be limited to the period of your trial. New features may be added, but we may also modify or discontinue (temporarily or permanently) part of the Service, in part or in whole before the end of the trial period.
44 |
45 | In the event of a material change to the Service, we will notify you on your account page. What constitutes a material change in this circumstance will be determined by Canonical, in good faith and using common sense and reasonable judgment.
46 |
47 | Similarly, we may occasionally make changes to these Terms of Service and will notify you of material changes either by email or on your account page. If Canonical does make changes to these Terms of Service, all changes will go into effect at the time we post the updated Terms of Service on your account page or as otherwise notified at that time.
48 | 7. Intellectual property
49 |
50 | You have a non-exclusive, non-transferable (to the extent permitted by law) right to view, access and use the Services for such time as it is made available by us strictly in accordance with these Terms of Service.
51 |
52 | You will not acquire any rights to the Service (or the intellectual property rights contained therein) from your use of the Service, other than as set out in these Terms of Service.
53 |
54 | 8. Personal data
55 |
56 | In order to provide the Service to you, you will be required to provide information about yourself such as your name, address, job title and company. Any such information you provide to Canonical must always be accurate, correct and up to date.
57 |
58 | Our Privacy Notice and Privacy Policy explain how we treat your personal data and protect your privacy when using the Services and our services in general. Canonical may provide limited personal data, such as your name, email address and related information, to third parties Ampere Computing LLC with a registered office at 4655 Great America Pkwy Suite 601 Santa Clara, CA 95054, United States and Packet Host Inc., with a registered office in 30 Vesey Street, New York, 10007 NY, United States. By accessing the Anbox Cloud Demo you are agreeing to the use of your personal data in this way.
59 |
60 | We may also collect certain non-personally-identifiable information, which is located on your computer. The information collected may include statistics relating to how often data is transferred, and performance metrics in relation to software and configuration. You agree this information may be retained and used by Canonical.
61 |
62 | Canonical may disclose any or all personal data and contents you have sent, posted or published if required to comply with applicable law or the order or requirement of a court, administrative agency or other governmental body. All other use of your personal data is subject to the Privacy Policy.
63 | 9. Liability
64 | Your use of the Service is at your sole risk. The Service is provided on an “as is” and “as available” basis without warranty of any kind.
65 |
66 | In the case of the Service provided by Canonical, the Service is provided “as is” and, other than as expressly set out in these Terms of Service, all warranties (whether express, implied, statutory or otherwise) in respect of the Services are expressly excluded to the maximum extent permitted by law.
67 |
68 | Canonical will provide the Services with reasonable care and skill, and Canonical will use reasonable efforts to ensure the availability of the Services, but makes no guarantee that the Services will be available without interruption or will be error-free.
69 |
70 | Canonical will not be liable in contract, tort or otherwise for any: indirect or consequential loss; loss of profits; loss of revenue; loss of anticipated savings; loss of business or business opportunity; loss of goodwill; or loss of or corruption to data. Otherwise, Canonical’s total liability in contract, tort or otherwise for any claims is limited to £10.
71 |
72 | Nothing in these Terms of Service will exclude or limit Canonical’s liability for: death or personal injury caused by the negligence of Canonical; fraud or fraudulent misrepresentation; or any other liability that cannot be excluded or limited by law.
73 |
74 | 9. General
75 | These Terms of Service are governed by the laws of England and any dispute will be heard by the courts in England.
76 |
77 | Failure by Canonical to enforce any right or provision of these Terms of Service shall not constitute a waiver of such right or provision. If any part of these Terms of Service is held invalid or unenforceable, that part will be construed to reflect the parties original intent, and the remaining portions will remain in full force and effect. The terms of these Terms of Service do not affect your statutory rights.
78 |
79 | Any notices should be sent by email to legal@canonical.com or by registered post to:
80 |
81 | Canonical Group Limited,
82 | 5 New Street Square,
83 | London,
84 | EC4A 3TW.
85 |
86 | Version: January 2020
87 |
88 |
--------------------------------------------------------------------------------
/templates/includes/_country-select.html:
--------------------------------------------------------------------------------
1 | {% if raw != "true" %}
2 |
3 |
4 | {% endif %}
5 |
263 | {% if raw != "true" %}
{% endif %}
--------------------------------------------------------------------------------
/run:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 |
3 | # CAUTION:
4 | # This file was generated by generator-canonical-webteam@3.4.3
5 | # and should usually not be edited directly.
6 | #
7 | # This file was generated by the "canonical-webteam" Yeoman generator
8 | # https://npmjs.org/package/generator-canonical-webteam
9 | #
10 | # Update it to the latest version with:
11 | #
12 | # $ sudo npm install -g yo generator-canonical-webteam
13 | # $ yo canonical-webteam:run
14 |
15 | set -euo pipefail
16 |
17 | USAGE="How to use ./run v3.4.3
18 | ===
19 |
20 | $ ./run \\
21 | [-e|--env VAR_NAME=value] # Declare an environment variable to use while running commands \\
22 | [-m|--node-module PATH] # A path to a local node module to use instead of the installed dependencies \\
23 | [COMMAND] # Optionally provide a command to run
24 |
25 | If no COMMAND is provided, \`serve\` will be run.
26 |
27 | Commands
28 | ---
29 |
30 | - serve [-p|--port PORT] [-d|--detach] [-f|--forward-port]: Run a development server
31 | - watch [-s|--watch-site]: Run \`yarn run watch\` (for jekyll sites, watch for changes with \`--watch-site\`)
32 | - build: Run \`yarn run build\`
33 | - test: Run \`yarn run test\`
34 | - test-python: Run \`yarn run test-python\`
35 | - lint-python: Run \`yarn run lint-python\`
36 | - stop: Stop any running containers
37 | - exec [-r|--root] [-p|--expose-port PORT] : Run a command in the development container (optionally exposing a port to the host)
38 | - yarn [-r|--root] [-p|--expose-port PORT] : Run a yarn script from package.json
39 | - clean: Remove all images and containers, any installed dependencies and the .docker-project file
40 | - clean-cache: Empty cache files, which are saved between projects (eg, yarn)
41 | "
42 |
43 | ##
44 | # Variable definitions
45 | ##
46 |
47 | # Define docker images versions
48 | dev_image="canonicalwebteam/dev:v1.6.7"
49 | if [ -n "${DOCKER_REGISTRY:-}" ]; then
50 | dev_image="${DOCKER_REGISTRY}/${dev_image}"
51 | fi
52 |
53 | # Interactivity options
54 | [ -t 1 ] && tty="--tty --interactive" || tty="" # Do we have a terminal?
55 | [ -f .env ] && env_file="--env-file .env" || env_file="" # Do we have an env file?
56 | [ -f .env.local ] && env_file="${env_file} --env-file .env.local" || env_file=$env_file # Do we have a local env file?
57 |
58 | # Defaults environment settings
59 | PORT=8000
60 |
61 | # Import environment settings
62 | if [ -f .env ]; then
63 | source .env
64 | fi
65 |
66 | # Other variables
67 | run_serve_docker_opts="${CANONICAL_WEBTEAM_RUN_SERVE_DOCKER_OPTS:-}"
68 | module_volumes=""
69 | env_vars=""
70 |
71 | # Decide which md5 command to use
72 | if $(command -v md5sum > /dev/null); then md5_command="md5sum";
73 | elif $(command -v md5 > /dev/null); then md5_command="md5";
74 | else echo "No md5 tool available. Exiting."; exit 1; fi
75 |
76 | ##
77 | # Check docker is installed correctly
78 | ##
79 | if ! command -v docker >/dev/null 2>&1; then
80 | echo "
81 | Error: Docker not installed
82 | ==
83 | Please install Docker before continuing:
84 | https://www.docker.com/products/docker
85 | "
86 | exit 1
87 | fi
88 | if grep -q '^docker:' /etc/group && ! groups | grep -q '\bdocker\b'; then
89 | echo "
90 | Error: `whoami` not in docker group
91 | ===
92 | Please add this user to the docker group, e.g. with:
93 | \$ newgrp docker
94 | "
95 | exit 1
96 | fi
97 |
98 | # Grab HTTP_PROXY settings from the host
99 | http_proxy=""
100 | yarn_proxy=""
101 | if [ -n "${HTTP_PROXY:-}" ]; then
102 | http_proxy="--env HTTP_PROXY=${HTTP_PROXY} --env http_proxy=${HTTP_PROXY}"
103 | yarn_proxy="--proxy ${HTTP_PROXY}"
104 | fi
105 | if [ -n "${HTTPS_PROXY:-}" ]; then
106 | http_proxy="${http_proxy} --env HTTPS_PROXY=${HTTPS_PROXY} --env https_proxy=${HTTPS_PROXY}"
107 | yarn_proxy="${yarn_proxy} --https-proxy ${HTTPS_PROXY}"
108 | fi
109 |
110 | # Generate the project name
111 | if [[ -f ".docker-project" ]]; then
112 | project=$(cat .docker-project)
113 | else
114 | directory="$(basename "$(pwd)")"
115 | hash=$(pwd | ${md5_command} | cut -c1-8)
116 | project=canonical-webteam-${directory}-${hash}
117 | echo $project > .docker-project
118 | fi
119 |
120 | # Volume names
121 | cache_volume="${CANONICAL_WEBTEAM_CACHE_VOLUME:-canonical-webteam-cache}"
122 | etc_volume="${project}-etc"
123 | usr_local_volume="${project}-usr-local"
124 | db_volume="${project}-db"
125 |
126 | # Container names
127 | db_container="${project}-db"
128 | pip_container="${project}-pip"
129 |
130 | # Network name
131 | network_name="${project}-net"
132 |
133 | invalid() {
134 | message=${1}
135 | echo "Error: ${message}"
136 | echo ""
137 | echo "$USAGE"
138 | exit 1
139 | }
140 |
141 | # Read optional arguments
142 | while [[ -n "${1:-}" ]] && [[ "${1:0:1}" == "-" ]]; do
143 | key="$1"
144 |
145 | case $key in
146 | -e|--env)
147 | if [ -z "${2:-}" ]; then invalid "Missing environment variables. Usage: --env XXXX=yyyy"; fi
148 | env_vars="${env_vars} --env ${2}"
149 | shift
150 | ;;
151 | -m|--node-module)
152 | if [ -z "${2:-}" ]; then invalid "Missing module name. Usage: --node-module ."; fi
153 | # Ensure directories exist, ready to host module volumes
154 | if [ ! -d "`pwd`/node_modules/$(basename ${2})" ]; then
155 | mkdir -p "`pwd`/node_modules/$(basename ${2})"
156 | fi
157 | module_volumes="${module_volumes} --volume=${2}:`pwd`/node_modules/$(basename ${2})"
158 | shift
159 | ;;
160 | -h|--help) echo "$USAGE"; exit ;;
161 | -v|--version) echo "Generated from generator-canonical-webteam@3.4.3"; exit ;;
162 | *) invalid "Option '${key}' not recognised." ;;
163 | esac
164 | shift
165 | done
166 |
167 | start_django_db () {
168 | # Run the database if necessary
169 | if grep -q django.db.backends.postgresql_psycopg2 */settings.py 2> /dev/null; then
170 | # Create isolated network
171 | if ! docker network inspect ${network_name} &> /dev/null; then
172 | docker network create ${network_name}
173 | fi
174 |
175 | # Start the database
176 | trap "kill_container ${db_container}" EXIT;
177 | if ! docker inspect -f {{.State.Running}} ${db_container} &>/dev/null; then
178 | docker run \
179 | --name ${db_container} `# Name the container` \
180 | --rm `# Remove the container once it's finished` \
181 | --volume "${db_volume}":/var/lib/postgresql/data `# Store dependencies in a docker volume` \
182 | ${http_proxy} `# Include HTTP proxy if needed` \
183 | --network ${network_name} `# Use an isolated network` \
184 | --network-alias db `# Call this container "db" on the network so it can be found` \
185 | --detach `# Run in the background` \
186 | postgres `# Use the image for node version 7`
187 | fi
188 |
189 | # Wait for it
190 | wait_time=0
191 | until docker exec ${db_container} pg_isready || [ $wait_time -eq 4 ]; do
192 | sleep $(( wait_time++ ))
193 | done
194 |
195 | # Provision database
196 | run_as_user "${network}" python3 manage.py migrate
197 | fi
198 | }
199 |
200 | kill_container () {
201 | container_name="${1}"
202 |
203 | # Kill any previous containers
204 | previous_id=$(docker ps --all --quiet --filter "name=^/${container_name}$")
205 | if [ -n "${previous_id}" ]; then
206 | docker rm --force ${previous_id} > /dev/null;
207 | fi
208 | }
209 |
210 | docker_run () {
211 | # Get options
212 | docker_run_options="${1}"; shift
213 |
214 | # Generate container name from command
215 | container_name="${project}-${@}"
216 | container_name="${container_name// /_}" # Replace spaces with underscores
217 | container_name=$(echo ${container_name} | tr -dc '[:alnum:]_.-') # Remove disallowed chars
218 |
219 | # Use network if it's been setup
220 | network=""
221 | if docker network inspect ${network_name} &> /dev/null; then
222 | network="--network ${network_name}"
223 | fi
224 |
225 | # Kill existing containers
226 | kill_container "${container_name}"
227 |
228 | # Environment info
229 | commit_id=$(git rev-parse HEAD || echo "unknown")
230 |
231 | # Start the new container
232 | docker run \
233 | --name ${container_name} `# Name the container` \
234 | --rm `# Remove the container once it's finished` \
235 | --volume "$(pwd):$(pwd)" `# Mirror current directory inside container` \
236 | --workdir "$(pwd)" `# Set current directory to the image's work directory` \
237 | --volume ${etc_volume}:/etc `# Use etc with corresponding user added` \
238 | --volume ${usr_local_volume}:/usr/local/ `# Bind local folder to volume` \
239 | --volume ${cache_volume}:/home/shared/.cache/ `# Bind cache to volume` \
240 | --env COMMIT_ID=${commit_id} `# Pass through the commit ID` \
241 | ${network} `# Network settings, if needed` \
242 | ${env_file} `# Pass any files of environment variables to the container` \
243 | ${env_vars} `# Pass explicit environment variables to the container` \
244 | ${http_proxy} `# Include HTTP proxy if needed` \
245 | ${tty} `# Attach a pseudo-terminal, if relevant` \
246 | ${docker_run_options} `# Extra options` \
247 | ${dev_image} $@ `# Run command in the image`
248 | }
249 |
250 | run_as_user () {
251 | run_as_user_options="${1}"; shift
252 |
253 | create_etc_volume
254 |
255 | docker_run "--user $(id -u):$(id -g) ${run_as_user_options}" $@
256 | }
257 |
258 | create_etc_volume() {
259 | # Create local user and group in the dev image
260 | uid=$(id -u)
261 | gid=$(id -g)
262 |
263 | if ! docker volume inspect -f " " ${etc_volume} 2> /dev/null; then
264 | etc_run="docker run --rm --volume ${etc_volume}:/etc ${dev_image}"
265 | if ! ${etc_run} grep -P "${gid}:$" /etc/group; then
266 | ${etc_run} groupadd -g ${gid} app-user
267 | fi
268 |
269 | if ! ${etc_run} grep -P "x:${uid}:" /etc/passwd; then
270 | ${etc_run} useradd -u ${uid} -g ${gid} app-user
271 | fi
272 | fi
273 | }
274 |
275 | update_dependencies() {
276 | # Make sure the etc volume has been created first
277 | create_etc_volume
278 |
279 | # Install yarn dependencies
280 | if [ -f package.json ]; then
281 | package_json_hash=$(${md5_command} package.json | cut -c1-8)
282 | if [ -d node_modules ]; then
283 | yarn_dependencies_hash=$(find node_modules -type f ! -wholename 'node_modules/.cache/*' -print0 | sort -z | xargs -0 ${md5_command} | ${md5_command} | cut -c1-8)-${package_json_hash}
284 | fi
285 | if [ -z "${yarn_dependencies_hash:-}" ] || [ ! -f .yarn.${project}.hash ] || [ "${yarn_dependencies_hash}" != "$(cat .yarn.${project}.hash)" ]; then
286 | echo "Installing new Yarn dependencies"
287 | run_as_user "" yarn install --force ${yarn_proxy}
288 | yarn_dependencies_hash=$(find node_modules -type f ! -wholename 'node_modules/.cache/*' -print0 | sort -z | xargs -0 ${md5_command} | ${md5_command} | cut -c1-8)-${package_json_hash}
289 | echo ${yarn_dependencies_hash} > .yarn.${project}.hash
290 | echo "Saved ${yarn_dependencies_hash} to .yarn.${project}.hash"
291 | else
292 | echo "Yarn dependencies haven't changed. To force an update, delete .yarn.${project}.hash."
293 | fi
294 | fi
295 |
296 | # Install bower dependencies
297 | if [ -f bower.json ]; then
298 | bower_json_hash=$(${md5_command} bower.json | cut -c1-8)
299 | if [ -d bower_components ]; then
300 | bower_dependencies_hash=$(find bower_components -type f -print0 | sort -z | xargs -0 ${md5_command} | ${md5_command} | cut -c1-8)-${bower_json_hash}
301 | fi
302 | if [ -z "${bower_dependencies_hash:-}" ] || [ ! -f .bower.${project}.hash ] || [ "${bower_dependencies_hash}" != "$(cat .bower.${project}.hash)" ]; then
303 | echo "Installing new bower dependencies"
304 | run_as_user "" bower install
305 | bower_dependencies_hash=$(find bower_components -type f -print0 | sort -z | xargs -0 ${md5_command} | ${md5_command} | cut -c1-8)-${bower_json_hash}
306 | echo ${bower_dependencies_hash} > .bower.${project}.hash
307 | else
308 | echo "Bower dependencies haven't changed. To force an update, delete .bower.${project}.hash."
309 | fi
310 | fi
311 |
312 | # Install ruby dependencies
313 | if [ -f Gemfile ]; then
314 | gemfile_hash=$(${md5_command} Gemfile | cut -c1-8)
315 | if [ -d vendor/bundle ]; then
316 | bundler_dependencies_hash=$(find vendor/bundle -type f -print0 | sort -z | xargs -0 ${md5_command} | ${md5_command} | cut -c1-8)-${gemfile_hash}
317 | fi
318 | if [ -z "${bundler_dependencies_hash:-}" ] || [ ! -f .bundler.${project}.hash ] || [ "${bundler_dependencies_hash}" != "$(cat .bundler.${project}.hash)" ]; then
319 | echo "Installing new bundler dependencies"
320 | run_as_user "" bundle install --path vendor/bundle
321 | bundler_dependencies_hash=$(find vendor/bundle -type f -print0 | sort -z | xargs -0 ${md5_command} | ${md5_command} | cut -c1-8)-${gemfile_hash}
322 | echo ${bundler_dependencies_hash} > .bundler.${project}.hash
323 | else
324 | echo "Bundler dependencies haven't changed. To force an update, delete .bundler.${project}.hash."
325 | fi
326 | fi
327 |
328 | # Install pip dependecies
329 | if [ -f requirements.txt ]; then
330 | requirements_hash=$(${md5_command} requirements.txt | cut -c1-8)
331 | pip_dependencies_hash=$(docker run --volume ${etc_volume}:/etc ${dev_image} bash -c 'find $(find /usr/local/lib/ -maxdepth 1 -name "python*" -type d | sort | tail -n 1)/dist-packages -type f -print0 | sort -z | xargs -0 '${md5_command}' | '${md5_command}' | cut -c1-8')-${requirements_hash}
332 | if [ ! -f .pip.${project}.hash ] || [ "${pip_dependencies_hash}" != "$(cat .pip.${project}.hash)" ]; then
333 | echo "Installing new pip dependencies"
334 | docker_run "" pip3 install --requirement requirements.txt
335 | pip_dependencies_hash=$(docker run --volume ${etc_volume}:/etc ${dev_image} bash -c 'find $(find /usr/local/lib/ -maxdepth 1 -name "python*" -type d | sort | tail -n 1)/dist-packages -type f -print0 | sort -z | xargs -0 '${md5_command}' | '${md5_command}' | cut -c1-8')-${requirements_hash}
336 | echo ${pip_dependencies_hash} > .pip.${project}.hash
337 | else
338 | echo "Pip dependencies haven't changed. To force an update, delete .pip.${project}.hash."
339 | fi
340 | fi
341 | }
342 |
343 | # Find current run command
344 | run_command=${1:-}
345 | if [[ -n "${run_command}" ]]; then shift; fi
346 |
347 | # Do the real business
348 | case $run_command in
349 | ""|"serve")
350 | update_dependencies
351 |
352 | # Read optional arguments
353 | detach=""
354 | forward_ports=("")
355 | run_watcher=false
356 | while [[ -n "${1:-}" ]] && [[ "${1:0:1}" == "-" ]]; do
357 | key="$1"
358 |
359 | case $key in
360 | -d|--detach) detach="--detach" ;;
361 | -p|--port)
362 | if [ -z "${2:-}" ]; then invalid "Missing port number. Usage: --port XXXX"; fi
363 | PORT=${2}
364 | shift
365 | ;;
366 | -f|--forward-port)
367 | if [ -z "${2:-}" ]; then invalid "Missing port number. Usage: --port XXXX"; fi
368 | forward_ports+=("${2}")
369 | shift
370 | ;;
371 | *) invalid "Option '${key}' not recognised." ;;
372 | esac
373 | shift
374 | done
375 |
376 | # Setup yarn dependencies
377 | if [ -f package.json ]; then
378 | run_as_user "${module_volumes}" yarn run build
379 | fi
380 |
381 | # Run watch command in the background
382 | if ${run_watcher}; then
383 | if [ -z "${detach}" ]; then trap "kill_container ${project}-watch" EXIT; fi
384 | run_as_user "--detach" yarn run watch # Run watch in the background
385 | fi
386 |
387 | publish_extra_ports=""
388 | if [ -n "${EXTRA_PORTS:-}" ]; then
389 | IFS=', ' read -r -a ports_array <<< "$EXTRA_PORTS"
390 | for extra_port in "${ports_array[@]}"; do
391 | publish_extra_ports="${publish_extra_ports} --publish $extra_port:$extra_port"
392 | done
393 | fi
394 |
395 | publish_forward_ports=""
396 | for forward_port in "${forward_ports[@]}"; do
397 | if [ -n "${forward_port}" ]; then
398 | publish_forward_ports="${publish_forward_ports} --publish ${forward_port}:${PORT}"
399 | fi
400 | done
401 |
402 | start_django_db
403 |
404 | # Run the serve container, publishing the port, and detaching if required
405 | run_as_user "--env PORT=${PORT} --publish ${PORT}:${PORT} ${publish_forward_ports} ${publish_extra_ports} ${detach} ${run_serve_docker_opts} ${module_volumes}" yarn run serve $*
406 | ;;
407 | "stop")
408 | echo "Stopping all running containers for ${project}"
409 | running_containers="$(docker ps --quiet --filter name=${project})"
410 | if [ -z "${running_containers}" ]; then
411 | echo "No running containers found"
412 | exit 0
413 | fi
414 | docker kill ${running_containers}
415 | ;;
416 | "watch")
417 | update_dependencies
418 |
419 | # Read optional arguments
420 | watch_site=false
421 | while [[ -n "${1:-}" ]] && [[ "${1:0:1}" == "-" ]]; do
422 | key="$1"
423 |
424 | case $key in
425 | -s|--watch-site)
426 | # Error if not a jekyll site
427 | if [ ! -f _config.yml ]; then
428 | echo "Error: Not a Jekyll site";
429 | exit 1;
430 | fi
431 | watch_site=true
432 | ;;
433 | *) invalid "Option '${key}' not recognised." ;;
434 | esac
435 | shift
436 | done
437 | if ${watch_site}; then
438 | trap "kill_container ${project}-watch-site" EXIT
439 | run_as_user "--detach" jekyll build --watch # Run site watcher in the background
440 | fi
441 | run_as_user "${module_volumes}" yarn run build
442 | run_as_user "${module_volumes}" yarn run watch
443 | ;;
444 | "build")
445 | update_dependencies
446 |
447 | run_as_user "${module_volumes}" yarn run build
448 |
449 | if [ -f _config.yml ]; then
450 | # For jekyll sites
451 | run_as_user "" bundle exec jekyll build
452 | fi
453 | ;;
454 | "test")
455 | update_dependencies
456 |
457 | test_error=false
458 |
459 | # Run node tests
460 | echo "- Running yarn tests"
461 | run_as_user "" yarn run test || test_error=true
462 |
463 | # Report success or failure
464 | if ${test_error}; then
465 | echo "==="
466 | echo "Tests failed"
467 | echo "==="
468 | exit 1
469 | else
470 | echo "==="
471 | echo "Tests succeeded"
472 | echo "==="
473 | fi
474 | ;;
475 | "test-python")
476 | update_dependencies
477 |
478 | test_error=false
479 |
480 | # Run node tests
481 | echo "- Running python tests"
482 | run_as_user "" yarn run test-python || test_error=true
483 |
484 | # Report success or failure
485 | if ${test_error}; then
486 | echo "==="
487 | echo "Tests failed"
488 | echo "==="
489 | exit 1
490 | else
491 | echo "==="
492 | echo "Tests succeeded"
493 | echo "==="
494 | fi
495 | ;;
496 | "lint-python")
497 | update_dependencies
498 |
499 | lint_error=false
500 |
501 | # Run node tests
502 | echo "- Running python lint"
503 | run_as_user "" yarn run lint-python || lint_error=true
504 |
505 | # Report success or failure
506 | if ${lint_error}; then
507 | echo "==="
508 | echo "Lint failed"
509 | echo "==="
510 | exit 1
511 | else
512 | echo "==="
513 | echo "Lint succeeded"
514 | echo "==="
515 | fi
516 | ;;
517 | "clean")
518 | echo "Remove hash files"
519 | rm -rf .*.hash
520 |
521 | echo "Running 'clean' yarn script"
522 | run_as_user "" yarn run clean || true # Run the clean script
523 |
524 | echo "Removing docker objects for project: ${project}"
525 |
526 | echo "- Removing containers using project volumes"
527 | project_volumes="$(docker volume ls --quiet --filter name=${project})"
528 | for volume in ${project_volumes}; do
529 | echo " > Removing containers using volume ${volume}"
530 | containers_using_volume="$(docker ps --all --quiet --filter volume=${volume})"
531 | if [ -n "${containers_using_volume}" ]; then docker rm --force ${containers_using_volume}; fi
532 | done
533 | echo "- Removing project volumes"
534 | if [ -n "${project_volumes}" ]; then docker volume rm ${project_volumes}; fi
535 |
536 | echo "- Removing remaining project containers"
537 | project_containers="$(docker ps --all --quiet --filter name=${project})"
538 | if [ -n "${project_containers}" ]; then docker rm --force ${project_containers}; fi
539 |
540 | echo "- Removing project networks"
541 | project_networks="$(docker network ls --quiet --filter name=${project})"
542 | if [ -n "${project_networks}" ]; then docker network rm ${project_networks}; fi
543 |
544 | echo "Removing .docker-project file"
545 | rm -rf .docker-project # Remove the project file
546 | ;;
547 | "clean-cache")
548 | # Clean node cache volume
549 | echo "Removing cache volume ${cache_volume}"
550 | containers_using_volume=$(docker ps --quiet --all --filter "volume=${cache_volume}")
551 | if [ -n "${containers_using_volume}" ]; then docker rm --force ${containers_using_volume}; fi
552 | docker volume rm ${cache_volume}
553 | ;;
554 | "exec")
555 | expose_ports=""
556 | run_as_root=false
557 |
558 | while [[ -n "${1:-}" ]] && [[ "${1:0:1}" == "-" ]]; do
559 | key="$1"
560 |
561 | case $key in
562 | -r|--root)
563 | run_as_root=true
564 | ;;
565 | -p|--expose-port)
566 | if [ -z "${2:-}" ]; then invalid "Missing port number. Usage: --expose-port XXXX"; fi
567 | expose_ports="${expose_ports} --publish ${2}:${2}"
568 | shift
569 | ;;
570 | *) invalid "Option '${key}' not recognised." ;;
571 | esac
572 | shift
573 | done
574 |
575 | update_dependencies
576 | start_django_db
577 |
578 | if ${run_as_root}; then
579 | docker_run "${expose_ports}" $@
580 | else
581 | run_as_user "${expose_ports}" $@
582 | fi
583 | ;;
584 | "yarn")
585 | expose_ports=""
586 | run_as_root=false
587 |
588 | while [[ -n "${1:-}" ]] && [[ "${1:0:1}" == "-" ]]; do
589 | key="$1"
590 |
591 | case $key in
592 | -r|--root)
593 | run_as_root=true
594 | ;;
595 | -p|--expose-port)
596 | if [ -z "${2:-}" ]; then invalid "Missing port number. Usage: --expose-port XXXX"; fi
597 | expose_ports="${expose_ports} --publish ${2}:${2}"
598 | shift
599 | ;;
600 | *) invalid "Option '${key}' not recognised." ;;
601 | esac
602 | shift
603 | done
604 |
605 | update_dependencies
606 | start_django_db
607 |
608 | if ${run_as_root}; then
609 | docker_run "${expose_ports}" yarn run $@
610 | else
611 | run_as_user "${expose_ports}" yarn run $@
612 | fi
613 | ;;
614 | *) invalid "Command '${run_command}' not recognised." ;;
615 | esac
616 |
--------------------------------------------------------------------------------
/permanent-redirects.yaml:
--------------------------------------------------------------------------------
1 | robots.txt?: "/static/files/robots.txt"
2 |
3 | # Documentation redirects
4 | # Home page
5 | /: https://canonical.com/anbox-cloud
6 | /contact-us: https://canonical.com/anbox-cloud#get-in-touch
7 | /thank-you: https://canonical.com/anbox-cloud#contact-form-success
8 | /terms: https://ubuntu.com/legal/terms-and-policies
9 | /privacy: https://ubuntu.com/legal/data-privacy
10 | docs/?: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/
11 | # Tutorials
12 | docs/tutorial/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/tutorial/landing/
13 | docs/tutorial/installing-appliance: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/tutorial/installing-appliance/
14 | # Show by `pro enable anbox-cloud` and cannot be easily changed
15 | docs/tut/installing-appliance: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/tutorial/installing-appliance/
16 | docs/tutorial/getting-started-dashboard: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/tutorial/getting-started-dashboard/
17 | docs/tutorial/getting-started: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/tutorial/getting-started/
18 | docs/tutorial/stream-client: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/tutorial/set-up-stream-client/
19 | docs/tutorial/getting-started-aaos: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/tutorial/getting-started-aaos/
20 | docs/tutorial/creating-addon: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/tutorial/creating-addon/
21 | # How-to guides
22 | docs/howto/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/landing/
23 | docs/howto/install-appliance/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install-appliance/landing/
24 | docs/howto/install-appliance/aws: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install-appliance/install-on-aws/
25 | docs/howto/install-appliance/azure: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install-appliance/install-on-azure/
26 | docs/howto/install-appliance/google-cloud: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install-appliance/install-on-google-cloud/
27 | docs/howto/install/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install/landing/
28 | docs/howto/install/deploy-juju: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install/deploy-juju/
29 | docs/howto/install/deploy-bare-metal: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install/deploy-bare-metal/
30 | docs/howto/install/customise: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install/customise-installation/
31 | docs/howto/install/high-availability: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install/enable-high-availability/
32 | docs/howto/install/validate: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/install/validate-deployment/
33 | docs/howto/update/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/update/landing/
34 | docs/howto/update/control: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/update/control-updates/
35 | docs/howto/update/upgrade-appliance: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/update/upgrade-appliance/
36 | docs/howto/update/upgrade-anbox: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/update/upgrade-anbox/
37 | docs/howto/manage/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox/landing/
38 | docs/howto/manage/tls-for-appliance: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox/tls-for-appliance/
39 | docs/howto/manage/images: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox/manage-images/
40 | docs/howto/manage/ams-access: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox/control-ams-remotely/
41 | docs/howto/manage/benchmarks: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox/benchmarks/
42 | docs/howto/manage/resize-storage: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox/resize-storage/
43 | docs/howto/manage/web-dashboard: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/dashboard/landing/
44 | docs/howto/application/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/landing/
45 | docs/howto/application/create: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/create-application/
46 | docs/howto/application/stream: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/stream-application/
47 | docs/howto/application/wait: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/wait-for-application/
48 | docs/howto/application/list: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/list-applications/
49 | docs/howto/application/userdata: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/pass-custom-data/
50 | docs/howto/application/test: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/test-application/
51 | docs/howto/application/update: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/update-application/
52 | docs/howto/application/delete: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/delete-application/
53 | docs/howto/application/extend: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/application/extend-application/
54 | docs/howto/application/virtual-devices: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/android/create-virtual-device/
55 | docs/howto/aar/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/aar/landing/
56 | docs/howto/aar/deploy: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/aar/deploy/
57 | docs/howto/aar/configure: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/aar/configure/
58 | docs/howto/aar/revoke: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/aar/revoke/
59 | docs/howto/port/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/port/landing/
60 | docs/howto/port/permissions: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/port/grant-runtime-permissions/
61 | docs/howto/port/architecture: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/port/choose-apk-architecture/
62 | docs/howto/port/obb-files: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/port/port-apk-obb-files/
63 | docs/howto/port/configure-watchdog: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/port/configure-watchdog/
64 | docs/howto/port/install-system-app: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/port/install-apk-system-app/
65 | docs/howto/instance/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/landing/
66 | docs/howto/instance/create: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/create-instance/
67 | docs/howto/instance/start: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/start-instance/
68 | docs/howto/instance/wait: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/wait-for-instance/
69 | docs/howto/instance/access: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/access-instance/
70 | docs/howto/instance/list: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/list-instances/
71 | docs/howto/instance/geographic-location: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/configure-geographic-location/
72 | docs/howto/instance/logs: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/view-instance-logs/
73 | docs/howto/instance/stop: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/stop-instance/
74 | docs/howto/instance/backup-and-restore: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/backup-restore-application-data/
75 | docs/howto/instance/delete: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/delete-instance/
76 | docs/howto/instance/expose-services: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/instance/expose-services/
77 | docs/howto/addons/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/landing/
78 | docs/howto/addons/create: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/create-addon/
79 | docs/howto/addons/enable-globally: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/enable-addons-globally/
80 | docs/howto/addons/update: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/update-addon/
81 | docs/howto/addons/migrate: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/migrate-addon/
82 | docs/howto/addons/install-tools: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/install-tools-example/
83 | docs/howto/addons/backup-and-restore: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/backup-and-restore-example/
84 | docs/howto/addons/customise-android: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/customise-android-example/
85 | docs/howto/addons/emulate-platforms: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/addons/emulate-platforms-example/
86 | docs/howto/stream/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/stream/landing/
87 | docs/howto/stream/access: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/stream/access-stream-gateway/
88 | docs/howto/stream/oob-data: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/stream/exchange-oob-data/
89 | docs/howto/stream/client-side-keyboard: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/stream/integrate-virtual-keyboard/
90 | docs/howto/cluster/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/cluster/landing/
91 | docs/howto/cluster/configure-nodes: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/cluster/configure-nodes/
92 | docs/howto/cluster/scale-up: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/cluster/scale-up/
93 | docs/howto/cluster/scale-down: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/cluster/scale-down/
94 | docs/howto/anbox/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox-runtime/landing/
95 | docs/howto/anbox/develop-platform: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox-runtime/develop-platform-plugin/
96 | docs/howto/anbox/develop-addon: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/anbox-runtime/develop-addon-devmode/
97 | docs/howto/android/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/android/landing/
98 | docs/howto/android/graphics-debugging-with-renderdoc: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/android/debug-graphics-renderdoc/
99 | docs/howto/android/custom_vhal: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/android/custom-vhal/
100 | docs/howto/troubleshoot/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/troubleshoot/landing/
101 | docs/howto/troubleshoot/initial-setup: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/troubleshoot/troubleshoot-initial-setup/
102 | docs/howto/troubleshoot/logs: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/troubleshoot/view-logs/
103 | docs/howto/troubleshoot/application-creation: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/troubleshoot/troubleshoot-application-creation/
104 | docs/howto/troubleshoot/instance-failures: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/troubleshoot/troubleshoot-instance-failures/
105 | docs/howto/troubleshoot/lxd-cluster: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/troubleshoot/troubleshoot-cluster-issues/
106 | docs/howto/troubleshoot/dashboard-issues: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/troubleshoot/troubleshoot-dashboard-issues/
107 | docs/howto/troubleshoot/streaming-issues: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/troubleshoot/troubleshoot-streaming-issues/
108 | docs/howto/monitor/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/howto/monitor/landing/
109 | # Reference
110 | docs/reference/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/landing/
111 | docs/reference/releases-versions: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/release-notes/
112 | docs/reference/roadmap: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/roadmap/
113 | docs/reference/release-notes/release-notes: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/release-notes/
114 | docs/reference/supported-versions: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/supported-versions/
115 | docs/reference/component-versions: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/component-versions/
116 | docs/reference/requirements: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/requirements/
117 | docs/reference/appliance-command-reference/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/landing/
118 | docs/reference/amc-command-reference/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/landing/
119 | docs/reference/provided-images: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/provided-images/
120 | docs/reference/supported-rendering-resources: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/supported-rendering-resources/
121 | docs/reference/supported-codecs: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/supported-codecs/
122 | docs/reference/android-features: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/android-features/
123 | docs/reference/anbox-features: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/anbox-features/
124 | docs/reference/ams-configuration: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/ams-configuration/
125 | docs/reference/application-manifest: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/application-manifest/
126 | docs/reference/api-reference: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/api-reference/
127 | docs/reference/anbox-https-api: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/anbox-https-api/
128 | docs/reference/sdks: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/sdks/
129 | docs/reference/network-ports: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/network-ports/
130 | docs/reference/addon-manifest: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/addon-manifest/
131 | docs/reference/hooks: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/hooks/
132 | docs/reference/webrtc-streamer: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/webrtc-streamer/
133 | docs/reference/prometheus: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/prometheus/
134 | docs/reference/perf-benchmarks: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/perf-benchmarks/
135 | docs/reference/deprecation-notices: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/deprecation-notices/
136 | docs/reference/license-information: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/license-information/
137 | docs/reference/glossary: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/glossary/
138 | docs/reference/release-notes/1.22.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.22.2/
139 | docs/reference/release-notes/1.22.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.22.1/
140 | docs/reference/release-notes/1.22.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.22.0/
141 | docs/reference/release-notes/1.21.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.21.2/
142 | docs/reference/release-notes/1.21.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.21.1/
143 | docs/reference/release-notes/1.21.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.21.0/
144 | docs/reference/release-notes/1.20.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.20.2/
145 | docs/reference/release-notes/1.20.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.20.1/
146 | docs/reference/release-notes/1.20.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.20.0/
147 | docs/reference/release-notes/1.19.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.19.2/
148 | docs/reference/release-notes/1.19.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.19.1/
149 | docs/reference/release-notes/1.19.0-fix1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.19.0-fix1/
150 | docs/reference/release-notes/1.19.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.19.0/
151 | docs/reference/release-notes/1.18.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.18.2/
152 | docs/reference/release-notes/1.18.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.18.1/
153 | docs/reference/release-notes/1.18.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.18.0/
154 | docs/reference/release-notes/1.17.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.17.2/
155 | docs/reference/release-notes/1.17.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.17.1/
156 | docs/reference/release-notes/1.17.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.17.0/
157 | docs/reference/release-notes/1.16.4: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.16.4/
158 | docs/reference/release-notes/1.16.3: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.16.3/
159 | docs/reference/release-notes/1.16.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.16.2/
160 | docs/reference/release-notes/1.16.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.16.1/
161 | docs/reference/release-notes/1.16.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.16.0/
162 | docs/reference/release-notes/1.15.3: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.15.3/
163 | docs/reference/release-notes/1.15.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.15.2/
164 | docs/reference/release-notes/1.15.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.15.1/
165 | docs/reference/release-notes/1.15.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.15.0/
166 | docs/reference/release-notes/1.14.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.14.2/
167 | docs/reference/release-notes/1.14.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.14.1/
168 | docs/reference/release-notes/1.14.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.14.0/
169 | docs/reference/release-notes/1.13.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.13.2/
170 | docs/reference/release-notes/1.13.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.13.1/
171 | docs/reference/release-notes/1.13.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.13.0/
172 | docs/reference/release-notes/1.12.5: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.12.5/
173 | docs/reference/release-notes/1.12.4: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.12.4/
174 | docs/reference/release-notes/1.12.3: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.12.3/
175 | docs/reference/release-notes/1.12.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.12.2/
176 | docs/reference/release-notes/1.12.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.12.1/
177 | docs/reference/release-notes/1.12.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.12.0/
178 | docs/reference/release-notes/1.11.5: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.11.5/
179 | docs/reference/release-notes/1.11.4: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.11.4/
180 | docs/reference/release-notes/1.11.3: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.11.3/
181 | docs/reference/release-notes/1.11.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.11.2/
182 | docs/reference/release-notes/1.11.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.11.1/
183 | docs/reference/release-notes/1.11.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.11.0/
184 | docs/reference/release-notes/1.10.3: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.10.3/
185 | docs/reference/release-notes/1.10.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.10.2/
186 | docs/reference/release-notes/1.10.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.10.1/
187 | docs/reference/release-notes/1.10.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.10.0/
188 | docs/reference/release-notes/1.9.5: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.9.5/
189 | docs/reference/release-notes/1.9.4: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.9.4/
190 | docs/reference/release-notes/1.9.3: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.9.3/
191 | docs/reference/release-notes/1.9.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.9.2/
192 | docs/reference/release-notes/1.9.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.9.1/
193 | docs/reference/release-notes/1.9.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.9.0/
194 | docs/reference/release-notes/1.8.3: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.8.3/
195 | docs/reference/release-notes/1.8.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.8.2/
196 | docs/reference/release-notes/1.8.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.8.1/
197 | docs/reference/release-notes/1.8.0: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.8.0/
198 | docs/reference/release-notes/1.7.4: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.7.4/
199 | docs/reference/release-notes/1.7.3: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.7.3/
200 | docs/reference/release-notes/1.7.2: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.7.2/
201 | docs/reference/release-notes/1.7.1: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/release-notes/1.7.1/
202 | docs/reference/appliance-command-reference/ams: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/ams/
203 | docs/reference/appliance-command-reference/dashboard: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/dashboard/
204 | docs/reference/appliance-command-reference/destroy: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/destroy/
205 | docs/reference/appliance-command-reference/gateway: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/gateway/
206 | docs/reference/appliance-command-reference/help: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/help/
207 | docs/reference/appliance-command-reference/init: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/init/
208 | docs/reference/appliance-command-reference/status: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/status/
209 | docs/reference/appliance-command-reference/upgrade: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/appliance-command-reference/upgrade/
210 | docs/reference/amc-command-reference/addon: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/addon/
211 | docs/reference/amc-command-reference/application: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/application/
212 | docs/reference/amc-command-reference/benchmark: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/benchmark/
213 | docs/reference/amc-command-reference/completion: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/completion/
214 | docs/reference/amc-command-reference/config: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/config/
215 | docs/reference/amc-command-reference/delete: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/delete/
216 | docs/reference/amc-command-reference/exec: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/exec/
217 | docs/reference/amc-command-reference/help: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/help/
218 | docs/reference/amc-command-reference/image: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/image/
219 | docs/reference/amc-command-reference/info: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/info/
220 | docs/reference/amc-command-reference/init: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/init/
221 | docs/reference/amc-command-reference/launch: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/launch/
222 | docs/reference/amc-command-reference/list: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/list/
223 | docs/reference/amc-command-reference/logs: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/logs/
224 | docs/reference/amc-command-reference/node: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/node/
225 | docs/reference/amc-command-reference/remote: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/remote/
226 | docs/reference/amc-command-reference/shell: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/shell/
227 | docs/reference/amc-command-reference/show-log: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/show-log/
228 | docs/reference/amc-command-reference/show: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/show/
229 | docs/reference/amc-command-reference/start: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/start/
230 | docs/reference/amc-command-reference/stop: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/stop/
231 | docs/reference/amc-command-reference/wait: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/reference/cmd-ref/amc-command-reference/wait/
232 | # Explanation
233 | docs/explanation/landing: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/landing/
234 | docs/explanation/anbox-cloud: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/anbox-cloud/
235 | docs/explanation/rendering-architecture: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/rendering-architecture/
236 | docs/explanation/aaos: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/aaos/
237 | docs/explanation/security: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/security/
238 | docs/explanation/ams: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/ams/
239 | docs/explanation/aar: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/aar/
240 | docs/explanation/web-dashboard: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/web-dashboard/
241 | docs/explanation/applications: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/applications/
242 | docs/explanation/resources: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/resources/
243 | docs/explanation/addons: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/addons/
244 | docs/explanation/application-streaming: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/application-streaming/
245 | docs/explanation/instances: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/instances/
246 | docs/explanation/images: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/images/
247 | docs/explanation/custom-images: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/custom-images/
248 | docs/explanation/nodes: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/nodes/
249 | docs/explanation/platforms: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/platforms/
250 | docs/explanation/gpus-instances: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/gpus-instances/
251 | docs/explanation/clustering: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/clustering/
252 | docs/explanation/performance: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/performance/
253 | docs/explanation/capacity-planning: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/capacity-planning/
254 | docs/explanation/production: https://canonical-anbox-cloud-documentation.readthedocs-hosted.com/en/latest/explanation/production-planning/
255 |
--------------------------------------------------------------------------------
/static/js/anbox-stream-sdk.js:
--------------------------------------------------------------------------------
1 | // Anbox Stream SDK
2 | // Copyright 2019 Canonical Ltd. All rights reserved.
3 |
4 | class AnboxStream {
5 | /**
6 | * AnboxStream creates a connection between your client and an Android instance and
7 | * displays its video & audio feed in an HTML5 player
8 | *
9 | * @param options: {object} {
10 | * targetElement: ID of the DOM element to attach the video to. (required)
11 | * url: Address of the service. (required)
12 | * authToken: Authentication token acquired through /1.0/login (required)
13 | * stunServers: List ICE servers (default: [{"urls": ['stun:stun.l.google.com:19302'], username: "", password: ""}])
14 | * session: {
15 | * app: Android application ID or name. (required)
16 | * },
17 | * screen: {
18 | * width: screen width (default: 1280)
19 | * height: screen height (default: 720)
20 | * fps: screen frame rate (default: 60)
21 | * density: screen density (default: 240)
22 | * },
23 | * controls: {
24 | * keyboard: true or false, send keypress events to the Android instance. (default: true)
25 | * mouse: true or false, send mouse and touch events to the Android instance. (default: true)
26 | * gamepad: true or false, send gamepad events to the Android instance. (default: true)
27 | * },
28 | * callbacks: {
29 | * ready: function, called when the video and audio stream are ready to be inserted. (default: none)
30 | * error: function, called on stream error with the message as parameter. (default: none)
31 | * done: function, called when the stream is closed. (default: none)
32 | * }
33 | * }
34 | */
35 | constructor(options) {
36 | if (this._nullOrUndef(options))
37 | throw new Error('invalid options');
38 |
39 | this._fillDefaults(options);
40 | this._validateOptions(options);
41 | this._options = options;
42 |
43 | this._id = Math.random().toString(36).substr(2, 9);
44 | this._containerID = options.targetElement;
45 | this._videoID = 'anbox-stream-video-' + this._id;
46 | this._audioID = 'anbox-stream-audio-' + this._id;
47 |
48 | // WebRTC
49 | this._ws = null; // WebSocket
50 | this._pc = null; // PeerConnection
51 | this._controlChan = null; // Channel to send inputs
52 | this._timedout = false;
53 | this._timer = -1;
54 | this._ready = false;
55 |
56 | // Media streams
57 | this._videoStream = null;
58 | this._audioStream = null;
59 |
60 | // Control options
61 | this._modifierState = 0;
62 | this._dimensions = null;
63 | this._gamepadManager = null;
64 | };
65 |
66 | /**
67 | * Connect a new instance for the configured application or attach to an existing one
68 | */
69 | connect() {
70 | // We first have to check if an instance for the application already
71 | // exists. If we already have one we attach to the existing instance.
72 | // Otherwise we create a new instance for the application.
73 | fetch(this._options.url + '/1.0/instances/', {
74 | method: 'GET',
75 | headers: {
76 | 'Accept': 'application/json, text/plain, */*',
77 | 'Authorization': 'Macaroon root=' + this._options.authToken,
78 | 'Content-Type': 'application/json',
79 | },
80 | })
81 | .then(response => {
82 | if (response.status !== 200)
83 | throw new Error("Failed to retrieve list of instances");
84 |
85 | return response.json();
86 | })
87 | .then(jsonResp => {
88 | if (jsonResp.status !== "success")
89 | throw new Error(jsonResp.error);
90 |
91 | var instanceID = "";
92 | for (var n = 0; n < jsonResp.metadata.length; n++) {
93 | var instance = jsonResp.metadata[n];
94 | if (instance.application === this._options.session.app) {
95 | instanceID = instance.id;
96 | break;
97 | }
98 | }
99 |
100 | if (instanceID.length === 0) {
101 | this._createNewInstance();
102 | return;
103 | }
104 |
105 | this._attachToInstance(instanceID);
106 | })
107 | .catch(error => {
108 | this._options.callbacks.error(error);
109 | });
110 | };
111 |
112 | _attachToInstance(instanceID) {
113 | const details = {
114 | screen: {
115 | width: this._options.screen.width,
116 | height: this._options.screen.height,
117 | fps: this._options.screen.fps,
118 | density: this._options.screen.density,
119 | }
120 | }
121 | fetch(this._options.url + '/1.0/instances/' + instanceID + '/join', {
122 | method: 'POST',
123 | headers: {
124 | 'Accept': 'application/json, text/plain, */*',
125 | 'Authorization': 'Macaroon root=' + this._options.authToken,
126 | 'Content-Type': 'application/json',
127 | },
128 | body: JSON.stringify(details),
129 | })
130 | .then(response => {
131 | if (response.status !== 200)
132 | throw new Error("Failed to join instance");
133 |
134 | return response.json();
135 | })
136 | .then(jsonResp => {
137 | if (jsonResp.status !== "success")
138 | throw new Error(jsonResp.error)
139 |
140 | // If we received any additional STUN/TURN servers from the gateway use them
141 | // If we received any additional STUN/TURN servers from the gateway use them
142 | if (!this._nullOrUndef(jsonResp.metadata.stun_servers) && jsonResp.metadata.stun_servers.length > 0) {
143 | for (var n = 0; n < jsonResp.metadata.stun_servers.length; n++) {
144 | this._options.stunServers.push({
145 | "urls": jsonResp.metadata.stun_servers[n].urls,
146 | "username": jsonResp.metadata.stun_servers[n].username,
147 | "credential": jsonResp.metadata.stun_servers[n].password
148 | });
149 | }
150 | }
151 |
152 | this._connectSignaler(jsonResp.metadata.websocket_url);
153 | })
154 | .catch(error => {
155 | this._options.callbacks.error(error);
156 | })
157 | }
158 |
159 | _createNewInstance() {
160 | const details = {
161 | name: this._options.session.app,
162 | application: this._options.session.app,
163 | }
164 |
165 | fetch(this._options.url + '/1.0/instances/', {
166 | method: 'POST',
167 | headers: {
168 | 'Accept': 'application/json, text/plain, */*',
169 | 'Authorization': 'Macaroon root=' + this._options.authToken,
170 | 'Content-Type': 'application/json',
171 | },
172 | body: JSON.stringify(details),
173 | })
174 | .then(response => {
175 | if (response.status !== 200)
176 | throw new Error("Failed to create new instance");
177 |
178 | return response.json();
179 | })
180 | .then(jsonResp => {
181 | if (jsonResp.status !== "success")
182 | throw new Error(jsonResp.error);
183 |
184 | this._attachToInstance(jsonResp.metadata.id);
185 | })
186 | .catch(error => {
187 | this._options.callbacks.error(error);
188 | });
189 | }
190 |
191 | _connectSignaler(url) {
192 | let ws = new WebSocket(url);
193 | ws.onopen = this._onWsOpen.bind(this);
194 | ws.onclose = this._onWsClose.bind(this);
195 | ws.onerror = this._onWsError.bind(this);
196 | ws.onmessage = this._onWsMessage.bind(this);
197 |
198 | this._ws = ws;
199 | this._timer = window.setTimeout(this._onTimeout.bind(this), 2 * 60 * 1000);
200 | }
201 |
202 | /**
203 | * Disconnect an existing stream and remove the video & audio elements.
204 | *
205 | * This will stop the underlying Android instance.
206 | */
207 | disconnect() {
208 | this._stopStreaming();
209 | };
210 |
211 | /**
212 | * Toggle fullscreen for the streamed video.
213 | *
214 | * IMPORTANT: fullscreen can only be toggled following a user input.
215 | * If you call this method when your page loads, it will not work.
216 | */
217 | requestFullscreen() {
218 | if (!document.fullscreenEnabled) {
219 | console.error("fullscreen not supported");
220 | } else {
221 | const video = document.getElementById(this._videoID);
222 | if (video.requestFullscreen) {
223 | video.requestFullscreen();
224 | } else if (video.mozRequestFullScreen) { /* Firefox */
225 | video.mozRequestFullScreen();
226 | } else if (video.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
227 | video.webkitRequestFullscreen();
228 | } else if (video.msRequestFullscreen) { /* IE/Edge */
229 | video.msRequestFullscreen();
230 | }
231 | }
232 | };
233 |
234 | /**
235 | * Exit fullscreen mode.
236 | */
237 | exitFullscreen() {
238 | document.exitFullscreen();
239 | };
240 |
241 | /**
242 | * Return the stream ID you can use to access video and audio elements with getElementById
243 | */
244 | getId() {
245 | return this._id;
246 | }
247 |
248 | _fillDefaults(options) {
249 | if (this._nullOrUndef(options.screen))
250 | options.screen = {};
251 |
252 | if (this._nullOrUndef(options.screen.width))
253 | options.screen.width = 1280;
254 |
255 | if (this._nullOrUndef(options.screen.height))
256 | options.screen.height = 720;
257 |
258 | if (this._nullOrUndef(options.screen.fps))
259 | options.screen.fps = 60;
260 |
261 | if (this._nullOrUndef(options.screen.density))
262 | options.screen.density = 240;
263 |
264 | if (this._nullOrUndef(options.controls))
265 | options.controls = {};
266 |
267 | if (this._nullOrUndef(options.controls.key))
268 | options.controls.keyboard = true;
269 |
270 | if (this._nullOrUndef(options.controls.mouse))
271 | options.controls.mouse = true;
272 |
273 | if (this._nullOrUndef(options.controls.gamepad))
274 | options.controls.gamepad = true;
275 |
276 | if (this._nullOrUndef(options.stunServers))
277 | options.stunServers = [{ urls: ['stun:stun.l.google.com:19302'], username: "", password: ""}];
278 |
279 | if (this._nullOrUndef(options.callbacks))
280 | options.callbacks = {};
281 |
282 | if (this._nullOrUndef(options.callbacks.ready))
283 | options.callbacks.ready = () => {};
284 |
285 | if (this._nullOrUndef(options.callbacks.error))
286 | options.callbacks.error = () => {};
287 |
288 | if (this._nullOrUndef(options.callbacks.done))
289 | options.callbacks.done = () => {};
290 | };
291 |
292 | _validateOptions(options) {
293 | // Required
294 | if (this._nullOrUndef(options.targetElement))
295 | throw new Error('missing targetElement parameter');
296 | if (document.getElementById(options.targetElement) === null) {
297 | throw new Error(`target element "${options.targetElement}" does not exist`)
298 | }
299 |
300 | if (this._nullOrUndef(options.authToken))
301 | throw new Error('missing authToken parameter');
302 |
303 | if (this._nullOrUndef(options.session))
304 | throw new Error('missing session parameter');
305 |
306 | if (this._nullOrUndef(options.session.app))
307 | throw new Error('missing session.app parameter');
308 |
309 | if (this._nullOrUndef(options.url))
310 | throw new Error('missing url parameter');
311 |
312 | if (!options.url.includes('https') && !options.url.includes('http'))
313 | throw new Error('unsupported scheme');
314 | }
315 |
316 | _insertMedia(videoSource, audioSource) {
317 | this._ready = true;
318 | let mediaContainer = document.getElementById(this._containerID);
319 |
320 | const video = document.createElement('video');
321 | video.srcObject = videoSource;
322 | video.muted = true;
323 | video.autoplay = true;
324 | video.controls = false;
325 | video.id = this._videoID;
326 |
327 | const audio = document.createElement('audio');
328 | audio.id = this._audioID;
329 | audio.srcObject = audioSource;
330 | audio.autoplay = true;
331 | audio.controls = false;
332 |
333 | mediaContainer.appendChild(video);
334 | mediaContainer.appendChild(audio);
335 |
336 | this._registerControls()
337 | };
338 |
339 | _removeMedia() {
340 | const video = document.getElementById(this._videoID);
341 | const audio = document.getElementById(this._audioID);
342 |
343 | if (video)
344 | video.remove();
345 | if (audio)
346 | audio.remove();
347 | };
348 |
349 | _stopStreaming() {
350 | if (this._pc !== null) {
351 | this._pc.close();
352 | this._pc = null;
353 | }
354 | if (this._ws !== null) {
355 | this._ws.close();
356 | this._ws = null;
357 | }
358 | this._removeMedia();
359 | this._unregisterControls();
360 |
361 | if (this._gamepadManager) {
362 | this._gamepadManager.stopPolling()
363 | }
364 | this._options.callbacks.done()
365 | };
366 |
367 | _onTimeout() {
368 | if (this._pc == null || this._pc.iceConnectionState === 'connected')
369 | return;
370 |
371 | this._timedout = true;
372 | this._stopStreaming();
373 | };
374 |
375 | _onRtcOfferCreated(description) {
376 | this._pc.setLocalDescription(description);
377 | let msg = {type: 'offer', sdp: btoa(description.sdp)};
378 | if (this._ws.readyState === 1)
379 | this._ws.send(JSON.stringify(msg))
380 | };
381 |
382 | _onRtcTrack(event) {
383 | const kind = event.track.kind;
384 | if (kind === 'video') {
385 | this._videoStream = event.streams[0];
386 | this._videoStream.onremovetrack = this._stopStreaming;
387 | } else if (kind === 'audio') {
388 | this._audioStream = event.streams[0];
389 | this._audioStream.onremovetrack = this._stopStreaming;
390 | }
391 |
392 | // Start streaming until audio and video tracks both are available
393 | if (this._videoStream && this._audioStream) {
394 | this._insertMedia(this._videoStream, this._audioStream)
395 | this._options.callbacks.ready();
396 | }
397 | };
398 |
399 | _onRtcIceConnectionStateChange() {
400 | if (this._pc === null)
401 | return;
402 |
403 | if (this._pc.iceConnectionState === 'failed') {
404 | this._stopStreaming();
405 | this._options.callbacks.error(new Error('Failed to establish a connection via ICE'));
406 | } else if (this._pc.iceConnectionState === 'disconnected' ||
407 | this._pc.iceConnectionState === 'closed') {
408 | if (this._timedout) {
409 | this._options.callbacks.error(new Error('Connection timed out'));
410 | return;
411 | }
412 | this._options.callbacks.error(new Error('Connection lost'));
413 | this._stopStreaming();
414 | } else if (this._pc.iceConnectionState === 'connected') {
415 | window.clearTimeout(this._timer);
416 | this._ws.close();
417 | }
418 | };
419 |
420 | _onRtcIceCandidate(event) {
421 | if (event.candidate !== null && event.candidate.candidate !== "") {
422 | const msg = {
423 | type: 'candidate',
424 | candidate: btoa(event.candidate.candidate),
425 | sdpMid: event.candidate.sdpMid,
426 | sdpMLineIndex: event.candidate.sdpMLineIndex,
427 | };
428 | if (this._ws.readyState === 1)
429 | this._ws.send(JSON.stringify(msg));
430 | }
431 | };
432 |
433 | _registerControls() {
434 | const v = document.getElementById(this._videoID);
435 |
436 | if (this._options.controls.mouse) {
437 | if (window.matchMedia('(pointer:fine)')) {
438 | v.addEventListener('mousemove', this._onMouseMove.bind(this));
439 | v.addEventListener('mousedown', this._onMouseButton.bind(this));
440 | v.addEventListener('mouseup', this._onMouseButton.bind(this));
441 | v.addEventListener('touchstart', this._onTouchStart.bind(this));
442 | v.addEventListener('touchend', this._onTouchEnd.bind(this));
443 | v.addEventListener('touchcancel', this._onTouchCancel.bind(this));
444 | v.addEventListener('touchmove', this._onTouchMove.bind(this));
445 | v.addEventListener('resize', this._refreshWindowMath.bind(this));
446 | } else
447 | console.warn("Device does not have mouse support")
448 | }
449 |
450 | if (this._options.controls.keyboard) {
451 | window.addEventListener('keydown', this._onKey.bind(this));
452 | window.addEventListener('keyup', this._onKey.bind(this));
453 | window.addEventListener('gamepadconnected', this._queryGamePadEvents.bind(this));
454 | }
455 |
456 | if (this._options.controls.keyboard || this._options.controls.mouse) {
457 | // Call it once for the initial values and refresh it every time the window
458 | // or video element is resized
459 | this._refreshWindowMath();
460 | window.addEventListener('resize', this._refreshWindowMath.bind(this));
461 | }
462 | };
463 |
464 | _unregisterControls() {
465 | const v = document.getElementById(this._videoID);
466 |
467 | // Removing the video container should automatically remove all event listeners
468 | // but this is dependant on the garbage collector, so we manually do it if we can
469 | if (v) {
470 | v.removeEventListener('mousemove', this._onMouseMove);
471 | v.removeEventListener('mousedown', this._onMouseButton);
472 | v.removeEventListener('mouseup', this._onMouseButton);
473 | v.removeEventListener('touchstart', this._onTouchStart);
474 | v.removeEventListener('touchend', this._onTouchEnd);
475 | v.removeEventListener('touchcancel', this._onTouchCancel);
476 | v.removeEventListener('touchmove', this._onTouchMove);
477 | v.removeEventListener('resize', this._refreshWindowMath);
478 | }
479 |
480 | window.removeEventListener('resize', this._refreshWindowMath.bind(this));
481 | window.removeEventListener('keydown', this._onKey.bind(this));
482 | window.removeEventListener('keyup', this._onKey.bind(this));
483 | window.removeEventListener('gamepadconnected', this._queryGamePadEvents.bind(this));
484 | };
485 |
486 | _clientToServerX(clientX, d) {
487 | let serverX = Math.round((clientX - d.containerOffsetX) * d.scalingFactorX);
488 | if (serverX === d.frameW - 1) serverX = d.frameW;
489 | if (serverX > d.frameW) serverX = d.frameW;
490 | if (serverX < 0) serverX = 0;
491 | return serverX;
492 | };
493 |
494 | _clientToServerY(clientY, m) {
495 | let serverY = Math.round((clientY - m.containerOffsetY) * m.scalingFactorY);
496 | if (serverY === m.frameH - 1) serverY = m.frameH;
497 | if (serverY > m.frameH) serverY = m.frameH;
498 | if (serverY < 0) serverY = 0;
499 | return serverY;
500 | };
501 |
502 | _triggerModifierEvent(event, key) {
503 | if (event.getModifierState(key)) {
504 | if (!(this._modifierState & _modifierEnum[key])) {
505 | this._modifierState = this._modifierState | _modifierEnum[key];
506 | this._sendEvent('key', {code: _keyScancodes[key], pressed: true});
507 | }
508 | } else {
509 | if ((this._modifierState & _modifierEnum[key])) {
510 | this._modifierState = this._modifierState & ~_modifierEnum[key];
511 | this._sendEvent('key', {code: _keyScancodes[key], pressed: false});
512 | }
513 | }
514 | };
515 |
516 | _sendEvent(type, data) {
517 | if (this._pc === null || this._controlChan.readyState !== 'open')
518 | return;
519 | this._controlChan.send(JSON.stringify({type: 'input::' + type, data: data}));
520 | };
521 |
522 | _refreshWindowMath() {
523 | let video = document.getElementById(this._videoID);
524 |
525 | // timing issues can occur when removing the component
526 | if (!video) {
527 | return
528 | }
529 |
530 | const windowW = video.offsetWidth;
531 | const windowH = video.offsetHeight;
532 | const frameW = video.videoWidth;
533 | const frameH = video.videoHeight;
534 |
535 | const multi = Math.min(windowW / frameW, windowH / frameH);
536 | const vpWidth = frameW * multi;
537 | const vpHeight = frameH * multi;
538 |
539 | this._dimensions = {
540 | scalingFactorX: frameW / vpWidth,
541 | scalingFactorY: frameH / vpHeight,
542 | containerOffsetX: Math.max((windowW - vpWidth) / 2.0, 0),
543 | containerOffsetY: Math.max((windowH - vpHeight) / 2.0, 0),
544 | frameW,
545 | frameH,
546 | };
547 | };
548 |
549 | _onMouseMove(event) {
550 | const x = this._clientToServerX(event.offsetX, this._dimensions);
551 | const y = this._clientToServerY(event.offsetY, this._dimensions);
552 | this._sendEvent('mouse-move', {x: x, y: y, rx: event.movementX, ry: event.movementY})
553 | };
554 |
555 | _onMouseButton(event) {
556 | const down = event.type === 'mousedown';
557 | let button;
558 |
559 | if (down && event.button === 0 && event.ctrlKey && event.shiftKey)
560 | return;
561 |
562 | switch (event.button) {
563 | case 0: button = 1; break;
564 | case 1: button = 2; break;
565 | case 2: button = 3; break;
566 | case 3: button = 4; break;
567 | case 4: button = 5; break;
568 | default: break;
569 | }
570 |
571 | this._sendEvent('mouse-button', {button: button, pressed: down})
572 | };
573 |
574 | _onKey(event) {
575 | // Disable any problematic browser shortcuts
576 | if (event.code === 'F5' || // Reload
577 | (event.code === 'KeyR' && event.ctrlKey) || // Reload
578 | (event.code === 'F5' && event.ctrlKey) || // Hard reload
579 | (event.code === 'KeyI' && event.ctrlKey && event.shiftKey) ||
580 | (event.code === 'F11') || // Fullscreen
581 | (event.code === 'F12') // Developer tools
582 | ) return;
583 |
584 | event.preventDefault();
585 |
586 | const code = _keyScancodes[event.code];
587 | const pressed = (event.type === 'keydown');
588 | if (code) {
589 | // NOTE: no need to check the following modifier keys
590 | // 'ScrollLock', 'NumLock', 'CapsLock'
591 | // as they're mapped to event.code correctly
592 | const modifierKeys = ['Control', 'Shift', 'Alt', 'Meta', 'AltGraph'];
593 | for (let i = 0; i < modifierKeys.length; i++) {
594 | this._triggerModifierEvent(event, modifierKeys[i]);
595 | }
596 |
597 | this._sendEvent('key', {code: code, pressed: pressed});
598 | }
599 | };
600 |
601 | _touchEvent(event, eventType) {
602 | event.preventDefault();
603 | for (let n = 0; n < event.changedTouches.length; n++) {
604 | let touch = event.changedTouches[n];
605 | let x = this._clientToServerX(touch.clientX, this._dimensions);
606 | let y = this._clientToServerY(touch.clientY, this._dimensions);
607 | this._sendEvent(eventType, {id: n, x: x, y: y});
608 | }
609 | };
610 |
611 | _onTouchStart(event) {this._touchEvent(event, 'touch-start')};
612 | _onTouchEnd(event) {this._touchEvent(event, 'touch-end')};
613 | _onTouchCancel(event) {this._touchEvent(event, 'touch-cancel')};
614 | _onTouchMove(event) {this._touchEvent(event, 'touch-move')};
615 |
616 | _queryGamePadEvents() {
617 | if (!this._options.controls.gamepad)
618 | return;
619 | let gamepads = navigator.getGamepads();
620 | if (gamepads.length > 0) {
621 | this._gamepadManager = new _gamepadEventManager(this._sendEvent.bind(this));
622 | this._gamepadManager.startPolling()
623 | }
624 | };
625 |
626 | _nullOrUndef(obj) { return obj === null || obj === undefined };
627 |
628 | _onWsOpen() {
629 | const config = { iceServers: this._options.stunServers };
630 | this._pc = new RTCPeerConnection(config);
631 | this._pc.ontrack = this._onRtcTrack.bind(this);
632 | this._pc.oniceconnectionstatechange = this._onRtcIceConnectionStateChange.bind(this);
633 | this._pc.onicecandidate = this._onRtcIceCandidate.bind(this);
634 |
635 | this._controlChan = this._pc.createDataChannel('control');
636 | let options = {offerToReceiveVideo: true, offerToReceiveAudio: true};
637 | this._pc.createOffer(options).then(this._onRtcOfferCreated.bind(this)).catch(function(err) {
638 | console.error(err)
639 | });
640 | };
641 |
642 | _onWsClose() {
643 | if (!this._ready) {
644 | this._options.callbacks.error(new Error('Connection was interrupted while connecting'));
645 | }
646 | };
647 |
648 | _onWsError(event) {
649 | if (event.type === 'error') {
650 | this._stopStreaming();
651 | }
652 | this._options.callbacks.error(new Error('failed to communicate with backend service'));
653 | };
654 |
655 | _onWsMessage(event) {
656 | const msg = JSON.parse(event.data);
657 | if (msg.type === 'answer') {
658 | this._pc.setRemoteDescription(new RTCSessionDescription({type: 'answer', sdp: atob(msg.sdp)}));
659 | } else if (msg.type === 'candidate') {
660 | this._pc.addIceCandidate({'candidate': atob(msg.candidate), 'sdpMLineIndex': msg.sdpMLineIndex, 'sdpMid': msg.sdpMid})
661 | } else {
662 | console.log('Unknown message type ' + msg.type)
663 | }
664 | };
665 | }
666 |
667 |
668 | class _gamepadEventManager {
669 | constructor(sendEvent) {
670 | this._polling = false;
671 | this._state = {};
672 | this._dpad_remap_start_index = 6;
673 | this._dpad_standard_start_index = 12;
674 | this._sendEvent = sendEvent
675 | }
676 |
677 | startPolling() {
678 | if (this._polling === true)
679 | return;
680 |
681 | // Since chrome only supports event polling and we don't want
682 | // to send any gamepad events to Android isntance if the state
683 | // of any button or axis of gamepad is not changed. Hence we
684 | // cache all keys state whenever it gets connected and provide
685 | // event-driven gamepad events mechanism for gamepad events processing.
686 | let gamepads = navigator.getGamepads();
687 | for (let i = 0; i < gamepads.length; i++) {
688 | if (gamepads[i])
689 | this.cacheState(gamepads[i]);
690 | }
691 |
692 | this._polling = true;
693 | this.tick()
694 | };
695 |
696 | stopPolling() {
697 | if (this._polling === true)
698 | this._polling = false;
699 | };
700 |
701 | tick() {
702 | this.queryEvents();
703 | if (this._polling)
704 | window.requestAnimationFrame(this.tick.bind(this));
705 | };
706 |
707 | queryEvents() {
708 | let gamepads = navigator.getGamepads();
709 | for (let i = 0; i < gamepads.length; i++) {
710 | let gamepad = gamepads[i];
711 | if (gamepad) {
712 | // A new gamepad is added
713 | if (!this._state[gamepad])
714 | this.cacheState(gamepad);
715 | else {
716 | const buttons = gamepad.buttons;
717 | const cacheButtons = this._state[gamepad].buttons;
718 | for (let j = 0; j < buttons.length; j++) {
719 | if (cacheButtons[j].pressed !== buttons[j].pressed) {
720 | // Check the table at the following link that describes the buttons/axes
721 | // index and their physical locations.
722 | this._sendEvent('gamepad-button', {id: gamepad.index, index: j, pressed: buttons[j].pressed});
723 | cacheButtons[j].pressed = buttons[j].pressed;
724 | }
725 | }
726 |
727 | // NOTE: For some game controllers, E.g. PS3 or Xbox 360 controller, DPAD buttons
728 | // were translated to axes via html5 gamepad APIs and located in gamepad.axes array
729 | // indexed starting from 6 to 7.
730 | // When a DPAD button is pressed/unpressed, the corresponding value as follows
731 | //
732 | // Button | Index | Pressed | Unpressed |
733 | // DPAD_LEFT_BUTTON | 6 | -1 | 0 |
734 | // DPAD_RIGHT_BUTTON | 6 | 1 | 0 |
735 | // DPAD_UP_BUTTON | 7 | -1 | 0 |
736 | // DPAD_DOWN_BUTTON | 7 | 1 | 0 |
737 | //
738 | // When the above button was pressed/unpressed, we will send the gamepad-button
739 | // event instead.
740 | const axes = gamepad.axes;
741 | let dpad_button_index = 0;
742 | const cacheAxes = this._state[gamepad].axes;
743 | for (let k = 0; k < axes.length; k++) {
744 | if (cacheAxes[k] !== axes[k]) {
745 | switch (true) {
746 | case k < this._dpad_remap_start_index: // Standard axes
747 | this._sendEvent('gamepad-axes', {id: gamepad.index, index: k, value: axes[k]});
748 | break;
749 | case k === this._dpad_remap_start_index: // DPAD left and right buttons
750 | if (axes[k] === 0) {}
751 | else if (axes[k] === -1) {
752 | dpad_button_index = this._dpad_standard_start_index + 2;
753 | } else {
754 | dpad_button_index = this._dpad_standard_start_index + 3;
755 | }
756 |
757 | this._sendEvent('gamepad-button', {
758 | id: gamepad.index,
759 | index: dpad_button_index,
760 | pressed: axes[k] !== 0
761 | });
762 | break;
763 | case k === this._dpad_remap_start_index + 1: // DPAD up and down buttons
764 | if (axes[k] === 0) {}
765 | else if (axes[k] === -1) {
766 | dpad_button_index = this._dpad_standard_start_index;
767 | } else {
768 | dpad_button_index = this._dpad_standard_start_index + 1;
769 | }
770 |
771 | this._sendEvent('gamepad-button', {
772 | id: gamepad.index,
773 | index: dpad_button_index,
774 | pressed: axes[k] !== 0
775 | });
776 | break;
777 | default:
778 | console.log("Unsupported axes index", k);
779 | break;
780 | }
781 | cacheAxes[k] = axes[k]
782 | }
783 | }
784 | }
785 | }
786 | }
787 | };
788 |
789 | cacheState(gamepad) {
790 | if (!gamepad)
791 | return;
792 |
793 | const gamepadState = {};
794 | const buttons = gamepad.buttons;
795 | for (let index = 0; index < buttons.length; index++) {
796 | let buttonState = {
797 | pressed: buttons[index].pressed
798 | };
799 | if (gamepadState.buttons)
800 | gamepadState.buttons.push(buttonState);
801 | else
802 | gamepadState.buttons = [buttonState];
803 | }
804 |
805 | const axes = gamepad.axes;
806 | for (let index = 0; index < axes.length; index++) {
807 | if (gamepadState.axes)
808 | gamepadState.axes.push(axes[index]);
809 | else
810 | gamepadState.axes = [axes[index]];
811 | }
812 |
813 | this._state[gamepad] = gamepadState;
814 | }
815 | }
816 |
817 | const _keyScancodes = {
818 | KeyA: 4,
819 | KeyB: 5,
820 | KeyC: 6,
821 | KeyD: 7,
822 | KeyE: 8,
823 | KeyF: 9,
824 | KeyG: 10,
825 | KeyH: 11,
826 | KeyI: 12,
827 | KeyJ: 13,
828 | KeyK: 14,
829 | KeyL: 15,
830 | KeyM: 16,
831 | KeyN: 17,
832 | KeyO: 18,
833 | KeyP: 19,
834 | KeyQ: 20,
835 | KeyR: 21,
836 | KeyS: 22,
837 | KeyT: 23,
838 | KeyU: 24,
839 | KeyV: 25,
840 | KeyW: 26,
841 | KeyX: 27,
842 | KeyY: 28,
843 | KeyZ: 29,
844 | Digit1: 30,
845 | Digit2: 31,
846 | Digit3: 32,
847 | Digit4: 33,
848 | Digit5: 34,
849 | Digit6: 35,
850 | Digit7: 36,
851 | Digit8: 37,
852 | Digit9: 38,
853 | Digit0: 39,
854 | Enter: 40,
855 | Escape: 41,
856 | Backspace: 42,
857 | Tab: 43,
858 | Space: 44,
859 | Minus: 45,
860 | Equal: 46,
861 | BracketLeft: 47,
862 | BracketRight: 48,
863 | Backslash: 49,
864 | Semicolon: 51,
865 | Comma: 54,
866 | Period: 55,
867 | Slash: 56,
868 | CapsLock: 57,
869 | F1: 58,
870 | F2: 59,
871 | F3: 60,
872 | F4: 61,
873 | F5: 62,
874 | F6: 63,
875 | F7: 64,
876 | F8: 65,
877 | F9: 66,
878 | F10: 67,
879 | F11: 68,
880 | F12: 69,
881 | PrintScreen: 70,
882 | ScrollLock: 71,
883 | Pause: 72,
884 | Insert: 73,
885 | Home: 74,
886 | PageUp: 75,
887 | Delete: 76,
888 | End: 77,
889 | PageDown: 78,
890 | ArrowRight: 79,
891 | ArrowLeft: 80,
892 | ArrowDown: 81,
893 | ArrowUp: 82,
894 | Control: 83,
895 | Shift: 84,
896 | Alt: 85,
897 | Meta: 86,
898 | AltGraph: 87,
899 | NumLock: 88,
900 | };
901 |
902 | const _modifierEnum = {
903 | Control: 0x1,
904 | Shift: 0x2,
905 | Alt: 0x4,
906 | Meta: 0x8,
907 | AltGraph: 0x10,
908 | };
909 |
910 | export default AnboxStream;
911 |
--------------------------------------------------------------------------------