### Load and Setup AndroidWorld Environment Source: https://context7.com/google-research/android_world/llms.txt Initializes the AsyncEnv by connecting to an Android emulator. Use `emulator_setup=True` for first-time installations of apps and permissions. Set `freeze_datetime=True` for reproducible benchmarking. ```python from android_world.env import env_launcher # Launch emulator first: # ~/Android/Sdk/emulator/emulator -avd AndroidWorldAvd -no-snapshot -grpc 8554 # First-time setup: installs apps and sets permissions (takes a few minutes). env = env_launcher.load_and_setup_env( console_port=5554, # ADB console port (adb devices output) emulator_setup=True, # Install open-source apps; one-time only freeze_datetime=True, # Freeze clock to Oct 2023 for reproducibility grpc_port=8554, # Must match -grpc flag used when launching AVD ) # Subsequent runs — skip emulator_setup once apps are installed. env = env_launcher.load_and_setup_env( console_port=5554, emulator_setup=False, freeze_datetime=True, ) print(env.device_screen_size) # e.g. (1080, 2400) print(env.logical_screen_size) # e.g. (1080, 2400) — changes with orientation env.close() ``` -------------------------------- ### Run AndroidWorld Benchmark Source: https://github.com/google-research/android_world/blob/main/README.md Executes the AndroidWorld benchmark suite. Use --perform_emulator_setup for the initial setup, which installs necessary apps and permissions. This setup is a one-time process. ```bash python run.py \ --suite_family=android_world \ --agent_name=t3a_gpt4 \ --perform_emulator_setup \ --tasks=ContactsAddContact,ClockStopWatchRunning ``` -------------------------------- ### Environment Setup - env_launcher.load_and_setup_env Source: https://context7.com/google-research/android_world/llms.txt This function creates and initializes the AsyncEnv by connecting to a running Android emulator over ADB and gRPC. It can optionally perform one-time app installation and freeze the device clock for consistent benchmarking. ```APIDOC ## Environment Setup — `env_launcher.load_and_setup_env` Creates and initializes the `AsyncEnv` by connecting to a running Android emulator over ADB and gRPC. Optionally performs one-time app installation (`emulator_setup=True`) and freezes the device clock to October 2023 for consistent benchmarking. ```python from android_world.env import env_launcher # Launch emulator first: # ~/Android/Sdk/emulator/emulator -avd AndroidWorldAvd -no-snapshot -grpc 8554 # First-time setup: installs apps and sets permissions (takes a few minutes). env = env_launcher.load_and_setup_env( console_port=5554, # ADB console port (adb devices output) emulator_setup=True, # Install open-source apps; one-time only freeze_datetime=True, # Freeze clock to Oct 2023 for reproducibility grpc_port=8554, # Must match -grpc flag used when launching AVD ) # Subsequent runs — skip emulator_setup once apps are installed. env = env_launcher.load_and_setup_env( console_port=5554, emulator_setup=False, freeze_datetime=True, ) print(env.device_screen_size) # e.g. (1080, 2400) print(env.logical_screen_size) # e.g. (1080, 2400) — changes with orientation env.close() ``` ``` -------------------------------- ### Initialize and Start Episode Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/drag-cube.html Sets up the cube puzzle and starts a new episode when the window loads. This is the entry point for the task. ```javascript window.onload = function(){ cubePuzzle.setup(); core.startEpisode(); } ``` -------------------------------- ### Create and Activate Conda Environment Source: https://github.com/google-research/android_world/blob/main/README.md Recommended setup using conda to create an isolated Python environment with a specific version. Download conda from the provided link if not already installed. ```bash conda create -n android_world python=3.11.8 conda activate android_world ``` -------------------------------- ### Install ffmpeg Source: https://github.com/google-research/android_world/blob/main/README.md Install the ffmpeg utility, which may be required for media processing. Instructions are provided for Linux and macOS. ```bash # Linux (Ubuntu/Debian) # sudo apt update && sudo apt install ffmpeg # macOS brew install ffmpeg ``` -------------------------------- ### Start Episode on Window Load Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/click-dialog-2.html Initializes the task by starting a new episode when the window loads. This is the entry point for the task execution. ```javascript window.onload = function() { core.startEpisode(); } ``` -------------------------------- ### Initialize Episode on Window Load Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/flight/Alaska-auto-medium/wrapper.html Sets up the environment by starting a new episode and removing a specific click event listener when the window finishes loading. ```javascript window.onload = function() { core.startEpisode(); document.body.removeEventListener('click', core.canvasDrawClick); } ``` -------------------------------- ### Initialize Episode on Page Load Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/click-checkboxes-transfer.html This code ensures that the core episode starts when the page is fully loaded. It's a standard setup for initiating tasks in this environment. ```javascript window.onload = function () { core.startEpisode(); }; ``` -------------------------------- ### Initialize Episode on Load Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/click-collapsible.html Starts the task episode when the page is loaded. This is the entry point for the task. ```javascript window.onload = function() { core.startEpisode(); } ``` -------------------------------- ### Window Load Initialization Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/click-menu-2.html Initializes the UI and starts a new episode when the window loads. This is the entry point for the task's execution. ```javascript window.onload = function() { setupUI(); core.startEpisode(); } ``` -------------------------------- ### Email Inbox UI Setup Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/email-inbox-nl-turk.html Initializes the email inbox UI by clearing the area and setting up the main and search templates. This function is called at the start of each episode. ```javascript var setupInbox = function(){ $("#area").empty(); var main = document.createElement('div'); main.setAttribute('id', 'main'); main.innerHTML = MAIN_TEMPLATE; $("#area").append(main); var search = document.createElement('div') search.setAttribute('id', 'search'); search.setAttribute('class', 'hide'); search.innerHTML = SEARCH_TEMPLATE; $("#area").append(search); } ``` -------------------------------- ### Initialize and Start Episode Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/flight/Alaska-auto/wrapper.html This JavaScript code sets up the window's onload event to start a new episode using `core.startEpisode()` and removes a click event listener from the body. This is typically used to initialize the application or a specific module when the page loads. ```javascript window.onload = function() { core.startEpisode(); document.body.removeEventListener('click', core.canvasDrawClick); } ``` -------------------------------- ### Keyboard Event Handling Setup Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/flight/AA/original.html Sets up event listeners for keydown, keyup, and keypress events, filtering for specific keys and modifiers. ```javascript var _gaq_oqkNjzf = { KEY_DOWN: +true, KEY_UP: -~+true, KEY_PRESS: 3 }; function _gaq_pqkNjzf(_gaq_qqkNjzf) { return ( ( (_gaq_qqkNjzf >= ~-1 + (21 + (+true + 5 + (15 + 5 + +true)))) && _gaq_qqkNjzf <= 25 + 4 + (3 + 11 + (-~1 + +true) + 11) ) || (_gaq_qqkNjzf >= 58 && _gaq_qqkNjzf <= 40 + 24) || (_gaq_qqkNjzf >= 65 && _gaq_qqkNjzf <= +[] + (+[] + 4) + 6 + 30 + (16 + (+"" + 4 + 5 + 25))) || (_gaq_qqkNjzf >= 60 + (24 + 12) && _gaq_qqkNjzf <= 111) || (_gaq_qqkNjzf >= 160 && _gaq_qqkNjzf <= 176) || (_gaq_qqkNjzf >= 14 + (81 + 91) && _gaq_qqkNjzf <= 192) || (_gaq_qqkNjzf >= 219 && _gaq_qqkNjzf <= 222) ); } function _gaq_rqkNjzf(_gaq_sqkNjzf) { this.events = []; this.ctx = _gaq_sqkNjzf; } _gaq_rqkNjzf.prototype.addKeyEvent = function (_gaq_tqkNjzf, _gaq_uqkNjzf) { var _gaq_vqkNjzf = this.ctx.lib.getEventTarget(_gaq_uqkNjzf); var _gaq_wqkNjzf = _gaq_uqkNjzf.which || _gaq_uqkNjzf.keyCode; if ( !_gaq_uqkNjzf.altKey && !_gaq_uqkNjzf.ctrlKey && !_gaq_uqkNjzf.metaKey && _gaq_pqkNjzf(_gaq_wqkNjzf) ) { _gaq_wqkNjzf = +true; } this.events.push({ eventType: _gaq_tqkNjzf, timestamp: this.ctx.lib.now(), altKey: !!_gaq_uqkNjzf.altKey, ctrlKey: !!_gaq_uqkNjzf.ctrlKey, metaKey: !!_gaq_uqkNjzf.metaKey, shiftKey: !!_gaq_uqkNjzf.shiftKey, keyCode: _gaq_wqkNjzf, target: _gaq_vqkNjzf, }); }; _gaq_rqkNjzf.prototype.formatEvents = function (_gaq_xqkNjzf) { return { keyDown: this.ctx.lib.filter(_gaq_xqkNjzf, function (_gaq_yqkNjzf) { return _gaq_yqkNjzf.eventType === +true; }), keyUp: this.ctx.lib.filter(_gaq_xqkNjzf, function (_gaq_zqkNjzf) { return _gaq_zqkNjzf.eventType === -~+true; }), keyPress: this.ctx.lib.filter(_gaq_xqkNjzf, function (_gaq_AqkNjzf) { return _gaq_AqkNjzf.eventType === 3; }), }; }; _gaq_lqkNjzf["default"] = { name: "keyboardEvents", group: "user", setup: function (_gaq_BqkNjzf) { var _gaq_CqkNjzf = this; this.ctx = _gaq_BqkNjzf; this.eventManager = new _gaq_rqkNjzf(_gaq_BqkNjzf); this.lastOOBIndex = ~-+true; _gaq_BqkNjzf.lib.addEvent(document, "keydown", function (_gaq_DqkNjzf) { _gaq_CqkNjzf.eventManager.addKeyEvent(_gaq_oqkNjzf.KEY_DOWN, _gaq_DqkNjzf); }); _gaq_BqkNjzf.lib.addEvent(document, "keyup", function (_gaq_EqkNjzf) { _gaq_CqkNjzf.eventManager.addKeyEvent(_gaq_oqkNjzf.KEY_UP, _gaq_EqkNjzf); }); _gaq_BqkNjzf.lib.addEvent(document, "keypress", function (_gaq_FqkNjzf) { _gaq_CqkNjzf.eventManager.addKeyEvent(_gaq_oqkNjzf.KEY_PRESS, _gaq_FqkNjzf); }); }, inBand: function () { var _gaq_GqkNjzf = this.eventManager.events; _gaq_GqkNjzf = _gaq_GqkNjzf.slice( _gaq_GqkNjzf.length - (this.ctx.userEventLimit || _gaq_GqkNjzf.length), ) ``` -------------------------------- ### Setup Email Inbox UI Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/email-inbox-star-reply.html Initializes the email inbox user interface by clearing the existing content and appending the main and search templates. This function should be called at the start of an episode. ```javascript var setupInbox = function(){ $('#area').empty(); var main = document.createElement('div'); main.setAttribute('id', 'main'); main.innerHTML = MAIN_TEMPLATE; $('#area').append(main); var search = document.createElement('div') search.setAttribute('id', 'search'); search.setAttribute('class', 'hide'); search.innerHTML = SEARCH_TEMPLATE; $('#area').append(search); } ``` -------------------------------- ### Generate Problem Setup Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/find-midpoint.html Clears the SVG, generates the circles for the problem, and sets up click listeners for graph and submit button. ```javascript var genProblem = function() { var svg = d3.select('svg'); svg.selectAll('*').remove(); var jsonCircles = generateCircles(); shapes.drawCircles(jsonCircles, svg); svg.on('click', function(){ graphClicked(event); }); d3.select('#subbtn').on('click', function(){ computeDistances(); }); } ``` -------------------------------- ### Episode Initialization Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/chase-circle.html Initializes the episode when the window loads. This function should be called to start the task. ```javascript window.onload = function() { core.startEpisode(); } ``` -------------------------------- ### Problem Setup Function Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/number-checkboxes.html Sets up a new problem by selecting a random number, updating the query text, and displaying the corresponding number pattern in the aside section. Returns the expected number. ```javascript var setupProblem = function(){ var expectedNumber = core.randi(0, 10); d3.select('#query').html('Draw the number "' + expectedNumber + '" in the checkboxes using the example on the right and press Submit when finished.'); d3.selectAll('aside div').style('display', 'none'); d3.select('#o' + expectedNumber).style('display', 'block'); return expectedNumber; } ``` -------------------------------- ### Start Episode on Window Load Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/book-flight-nodelay.html Initiates the start of a new episode when the browser window finishes loading. ```javascript window.onload = function() { core.startEpisode(); } ``` -------------------------------- ### Setup UI Elements for Copy-Paste Task Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/copy-paste.html Initializes the UI by randomly placing textarea and input fields. Adjusts textarea dimensions based on random values. ```javascript var TEXT_AREA = ''; var ANSWER_INPUT = ''; // reset the UI, randomly place the textarea and input fields, randomize textarea height var setupTextArea = function(){ var problemType = core.randi(0,2); if (problemType===0) document.getElementById('container').innerHTML = TEXT_AREA + ANSWER_INPUT; else document.getElementById('container').innerHTML = ANSWER_INPUT + TEXT_AREA; var textAreaHeight = core.randi(6,15)*5; var textAreaWidth = core.randi(11,30)*5; document.getElementById('to-copy').setAttribute('style', 'height: ' + textAreaHeight + 'px; width: ' + textAreaWidth + 'px;'); } ``` -------------------------------- ### Initialize Episode on Window Load Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/click-tab-2-easy.html Starts the MiniWob episode when the browser window finishes loading. This is the entry point for the task's execution within the MiniWob environment. ```javascript window.onload = function() { core.startEpisode(); } ``` -------------------------------- ### Setup UI for Click Menu Task Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/click-menu-2.html Initializes the jQuery menu widget and sets up a click event listener for the menu toggle button. Use this to prepare the UI elements for interaction. ```javascript var setupUI = function(){ $('#menu').menu(); $('#open-menu').on('click', function(){ $('#menu').toggle(); }); } ``` -------------------------------- ### Initialize MiniWob Episode Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/multi-orderings.html Sets the maximum duration for an episode in the MiniWob environment. This should be called before starting an episode. ```javascript core.EPISODE_MAX_TIME = 20000; // 20 seconds ``` -------------------------------- ### Setup Inbox UI Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/email-inbox-forward.html This function initializes the email inbox interface by clearing the existing content and appending the main and search templates. It ensures the UI is ready for displaying emails and search functionality. ```javascript var setupInbox = function(){ $('#area').empty(); var main = document.createElement('div'); main.setAttribute('id', 'main'); main.innerHTML = MAIN_TEMPLATE; $('#area').append(main); var search = document.createElement('div') search.setAttribute('id', 'search'); search.setAttribute('class', 'hide'); search.innerHTML = SEARCH_TEMPLATE; $('#area').append(search); } ``` -------------------------------- ### Generate Problem Setup Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/book-flight-nodelay.html Sets up the flight booking problem by clearing the UI, selecting random origin, destination, and date, and populating the search form with autocomplete and datepicker widgets. It also defines the user query. ```javascript var genProblem = function(){ // clear the UI. if($('body').find('ui-autocomplete-input').length > 0) $('#flight-from, #flight-to').autocomplete('destroy') if($('body').find('hasDatepicker').length > 0) $('#datepicker').datepicker('destroy') $('#search').unbind('click') $('#area input').val('') $('#menu').removeClass('hide') $('#results').addClass('hide').empty() $('.error').removeClass('.error') var origin = core.sample(DOMESTIC_FLIGHTS) var splitOrigin = origin.replace(/(\s\()|(\))/g, '_').split('_') var cityOrigin = splitOrigin[0] var airportOrigin = splitOrigin[1] var userOrigin = core.sample([cityOrigin, airportOrigin]) var destination = core.sample(DOMESTIC_FLIGHTS) var splitDesination = destination.replace(/(\s\()|(\))/g, '_').split('_') var cityDesination = splitDesination[0] var airportDesination = splitDesination[1] var userDestination = core.sample([cityDesination, airportDesination]) var expectedDate = ui_utils.randomDate(START_DATE, END_DATE) var userDate = ui_utils.toDateString(expectedDate) var bookAction = core.sample(DESIRED_FLIGHT) $('#query').html('Book the ' + bookAction + ' one-way flight from: ' + userOrigin + ' to: ' + userDestination + ' on ' + userDate + '.') $('#flight-from, #flight-to').autocomplete({ source: DOMESTIC_FLIGHTS, delay: 0 }) $("#datepicker").datepicker({ beforeShow: function (textbox, instance) { var txtBoxOffset = $(this).offset() var top = txtBoxOffset.top var left = txtBoxOffset.left var textBoxWidth = $(this).outerWidth() setTimeout(function () { instance.dpDiv.css({top: top-107, left: left}) }, 0) }, minDate: START_DATE, maxDate: END_DATE, showAnim: '' }) var flightResults = generateFlights(origin, userDestination, userDate) $('#search').on('click', function(){ $('#results').empty() $('.error').removeClass('error') var inputOrigin = $('#flight-from').val() var inputDestination = $('#flight-to').val() var inputDate = $('#datepicker').val() // input validation. if the text inputs are invalid, or blank, outline them in red. if(inputOrigin === '' || DOMESTIC_FLIGHTS.indexOf(inputOrigin) === -1) $('#flight-from').addClass('error') if(inputDestination === '' || DOMESTIC_FLIGHTS.indexOf(inputDestination) === -1) $('#flight-to').addClass('error') if(inputDate === '') $('#datepicker').addClass('error') if($('.error').length > 0) return $('#results').removeClass('hide') $('#menu').addClass('hide') var cheapestPrice = MAX_PRICE var shortest = DAY_MILLISECONDS*2 // create the header at the top of the flight results, // and allow the user to return back to search if needed. createFlightHeader(inputOrigin, inputDestination, inputDate) $('#menu-back').unbind('click') $('#menu-back').on('click', function(){ $('#results').addClass('hide') $('#menu').removeClass('hide') }) // all the shenanigans used to determine whether or not the user // has picked the correct flight: the origin city, the desination city, // the date, and whether or not it's the cheapest/shorest flight. var correctOrigin = inputOrigin.indexOf(userOrigin) !== -1 var correctDestination = inputDestination.indexOf(userDestination) !== -1 var correctDate = inputDate === userDate if(correctOrigin && correctDestination && correctDate){ for(var i=0;i 0){ $('#area').tabs('destroy'); } generateLinks(div); var any = core.getOpt(core.QueryString, 'any', false); // click Any link? var correctText = bindClickEvents(); // generate query text in the UI d3.select('#query').html('Switch between the tabs to find and click on the link "' + correctText + '".'); } ``` -------------------------------- ### Generate Terminal Output and Initialize Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/terminal.html Sets up the terminal interface, including welcome messages, login information, file generation, and the query for the user's task. It also initializes event listeners for keyboard input. ```javascript var genProblem = function(){ $('#area').empty(); var newTerminal = document.createElement('div'); newTerminal.innerHTML = TERMINAL_TEMPLATE; $('#area').append(newTerminal); $('#terminal').on('click', function(){ $('#terminal-target').focus(); }); // cause the input caret to flicker. clearInterval(flicker); flicker = setInterval(function(){ $('#input-flicker').toggleClass('hide'); }, 800); var currentTime = new Date(); generateTerminalOutput('Welcome! Type help for a list of available commands.'); generateTerminalOutput('Last login: ' + currentTime.toDateString()); currentFiles = []; currentExtensions = []; generateFiles(); var expectedExtension = core.sample(currentExtensions); if(expectedExtension !== ''){ $('#query').html('Use the terminal below to delete a file ending with the extension .' + expectedExtension + ''); } else { $('#query').html('Use the terminal below to delete a file that has no file extension.'); } $('#terminal-target').on('keyup', function(e){ pressedKeys[e.keyCode] = null; }); $('#terminal-target').on('keydown', function(e){ var currentChar = e.key; var currentText = $('#active-input').text(); pressedKeys[e.keyCode] = true; if(modifierKeyPressed()) return; if(TYPABLE_KEYS.indexOf(currentChar) !== -1){ // handle typing regular keys $('#active-input').append(e.key); } else if(e.keyCode === 8){ // handle delete key $('#active-input').text(currentText.substring(0, currentText.length-1)); } else if (e.keyCode ===13){ // handle newline/returns generateTerminalLine(currentText); parseCommand(currentText, expectedExtension); $('#active-input').text(''); } $('#terminal-target').val(''); // scroll down if needed $('#terminal-contents').scrollTop($('#terminal-contents').height()); }); // autofocus into terminal for convenience. setTimeout(function(){$('#terminal-target').select()}, 200); } ``` -------------------------------- ### Install AndroidWorld and Dependencies Source: https://github.com/google-research/android_world/blob/main/README.md Clone the AndroidWorld repository and install its Python dependencies using pip. Requires Python 3.11 or above. ```python git clone https://github.com/google-research/android_world.git cd ./android_world pip install -r requirements.txt python setup.py install ``` -------------------------------- ### Get Screenshot from Docker Server Source: https://context7.com/google-research/android_world/llms.txt Retrieves the current screenshot from the Android World Docker server via a GET request to the /state endpoint. The response is base64-encoded PNG data, piped to 'python3 -m json.tool' for pretty-printing. ```bash # 3. Get the current screenshot (returns base64-encoded PNG). curl http://localhost:5000/state | python3 -m json.tool ``` -------------------------------- ### Create Task Suite Source: https://context7.com/google-research/android_world/llms.txt Shows how to create a collection of TaskEval objects from a registry. Tasks can be instantiated multiple times with different random parameters for evaluation. Supports creating a full suite or a targeted mini-suite. ```python from android_world import registry, suite_utils from android_world.env import env_launcher env = env_launcher.load_and_setup_env(console_port=5554) task_registry = registry.TaskRegistry() android_registry = task_registry.get_registry('android_world') # Create a full suite with 3 parameter variations per task. full_suite = suite_utils.create_suite( task_registry=android_registry, n_task_combinations=3, seed=42, # Reproducible parameter generation env=env, ) print(len(full_suite)) # Number of distinct task types print(len(full_suite['ContactsAddContact'])) # 3 instances # Create a targeted mini-suite for specific tasks only. mini_suite = suite_utils.create_suite( task_registry=android_registry, n_task_combinations=1, seed=0, tasks=['ContactsAddContact', 'ClockStopWatchRunning', 'SimpleSmsSend'], env=env, ) for task_name, instances in mini_suite.items(): for inst in instances: print(f"{task_name}: {inst.goal}") # ContactsAddContact: "Create a new contact for Alice Smith. Their number is 555-0198." # ClockStopWatchRunning: "Run the stopwatch." # SimpleSmsSend: "Send a text message using Simple SMS Messenger to 555-0101 with message: Hi!" ``` -------------------------------- ### Problem Instance Creation and Event Handling Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/identify-shape.html Sets up a new problem instance by drawing a shape and attaching a click event listener to buttons. The listener checks if the clicked button's type matches the drawn shape and ends the episode with an appropriate reward. ```javascript var genProblem = function() { var chosenShape = drawShapes(); d3.selectAll('#area-buttons button').on('click', function(){ var clickedType = this.getAttribute('data-type'); var r = clickedType === chosenShape ? 1.0 : -1.0; core.endEpisode(r, r > 0); }); } window.onload = function() { core.startEpisode(); } ``` -------------------------------- ### Generate Problem and Query Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/click-dialog-2.html Sets up the problem by generating the dialog, resetting the UI, and updating the query to instruct the user. It also attaches an event listener to the close button. ```javascript var genProblem = function() { // generate the task var div = $('#dialog'); resetUI(div); var expectedButton = generateDialog(div); $('#query').html('Click the button in the dialog box labeled "' + expectedButton + '".'); $('.ui-dialog-titlebar button.ui-button').on('click', function(e){ var r = expectedButton === 'x' ? 1.0 : -1.0; core.endEpisode(r, r > 0); }); } ``` -------------------------------- ### Define Chrome Webstore Module Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/flight/AA/original.html Defines a module for interacting with the Chrome Webstore, including setup and in-band/out-of-band data handling. ```javascript var _gaq_HvkNjzf = [ "contextMenu", "innerHTML", "innerText", "mozRequestFullScreen", "requestFullScreen", "webdriver", "webkitRequestFullScreen", ]; var _gaq_IvkNjzf = ["vibrate"]; ``` ```javascript var _gaq_JvkNjzf = ["Sequentum"]; function _gaq_EvkNjzf(_gaq_LvkNjzf, _gaq_MvkNjzf) { var _gaq_NvkNjzf, _gaq_OvkNjzf, _gaq_PvkNjzf, _gaq_QvkNjzf = {}; for ( _gaq_NvkNjzf = +[], _gaq_OvkNjzf = _gaq_LvkNjzf.length; _gaq_NvkNjzf < _gaq_OvkNjzf; ++_gaq_NvkNjzf ) { _gaq_PvkNjzf = _gaq_LvkNjzf[_gaq_NvkNjzf]; _gaq_QvkNjzf[_gaq_PvkNjzf] = _gaq_MvkNjzf != null && _gaq_PvkNjzf in _gaq_MvkNjzf; } return _gaq_QvkNjzf; } }, ); _gaq_zikNjzf.define( "fc27a636-2b70-4950-8bc6-15a1efc89daa", function (_gaq_RvkNjzf, _gaq_SvkNjzf, _gaq_TvkNjzf, _gaq_UvkNjzf) { _gaq_SvkNjzf["default"] = { name: "chromeWebstore", group: "browser", setup: function (_gaq_VvkNjzf) { var _gaq_WvkNjzf = _gaq_VvkNjzf.global.chrome != null && _gaq_VvkNjzf.global.chrome.webstore != null && typeof _gaq_VvkNjzf.global.chrome.webstore.install === "function"; this.data = _gaq_WvkNjzf; }, inBand: function () { return this.data; }, outOfBandOnce: function () { return this.data; }, }; }, ); return _gaq_zikNjzf("bef78352-667c-429a-a4bb-53d4570fa89e"); }).call(this, this); var v = {}; v["v_locale"] = "en_US"; ``` -------------------------------- ### Initialize Email UI Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/email-inbox-forward-nl.html Clears the existing UI and sets up the main email view and the search interface using predefined templates. ```javascript var setupInbox = function(){ $('#area').empty(); var main = document.createElement('div'); main.setAttribute('id', 'main'); main.innerHTML = MAIN_TEMPLATE; $('#area').append(main); var search = document.createElement('div') search.setAttribute('id', 'search'); search.setAttribute('class', 'hide'); search.innerHTML = SEARCH_TEMPLATE; $('#area').append(search); } ``` -------------------------------- ### Generate Problem Instance Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/drag-shapes.html Sets up a new problem instance by generating a random container position, creating a grid of shapes, rendering them, and setting the user query. It also attaches the drag behavior and submission logic. ```javascript var genProblem = function() { // generate a new random grid of shapes containerX = core.randi(4, 150-containerWidth); containerY = core.randi(4, 130-containerHeight) var grid = genPolygons(); var svg_elt = d3.select('#area_svg'); shapes.renderGrid(svg_elt, grid); // instantiate the actual SVG shapes genContainer(); var shapeIndex = core.randi(0,SHAPE_TYPES.length); // generate a problem instance var gtix = core.randi(0, grid.shapes.length); var gtdesc = shapes.generalDesc(grid.shapes[gtix]); d3.select('#query').html('Drag all ' + SHAPE_TYPES[shapeIndex] + ' into the black box.'); // render query d3.selectAll('rect:not(#shape-container), circle, polygon').call(shapes.drag); d3.select('#subbtn').on('click', function(){ var expectedShape = HTML_TAGS[shapeIndex]; var otherShapes = HTML_TAGS.slice(); otherShapes.splice(shapeIndex, 1); var expectedShapesContained = d3.selectAll(expectedShape)[0].every(function(v){return v===null || insideContainer(v);}); var otherShapesOutside = d3.selectAll(otherShapes.join(', ОБРАТНО))[0].every(function(v){return v===null || !insideContainer(v);}); var r = expectedShapesContained && otherShapesOutside ? 1.0 : -1.0; core.endEpisode(r, r>0); }); } ``` -------------------------------- ### Generate Login Problem Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/login-user-popup.html Sets up the login form, generates credentials, and defines the logic for submitting the form and handling rewards. It also includes logic for displaying a random popup. ```javascript core.EPISODE_MAX_TIME = 15000; // 15 seconds var genProblem = function () { d3.select("#username")[0][0].value = ""; d3.select("#password")[0][0].value = ""; var user = core.sample(ui_utils.FIFTY_NAMES).toLowerCase(); var password = ui_utils.generateString(2, 6); d3.select("#query").html( 'Enter the username "' + user + '" and the password "' + password + '" into the text fields and press login.', ); // reward awarder d3.select("#subbtn").on("click", function () { var u = d3.select("#username")[0][0].value; var p = d3.select("#password")[0][0].value; var r = u === user && p === password ? 1.0 : -1.0; core.endEpisode(r, r > 0); }); // Clean the previous states d3.selectAll("#username, #password, #subbtn").attr("disabled", null); d3.select("#popup").remove(); var popupShown = false; // Random Popup function showPopup() { if (popupShown) return; d3.selectAll("#username, #password, #subbtn").attr("disabled", "disabled"); var message; if (Math.random() < 0.85) { message = "Your session is " + core.sample([ "about to expire.", "about to time out.", "expiring soon.", "soon to expire.", "timing out soon.", "going to expire soon.", "going to time out soon.", ]); } else { message = core.sample([ "You are running out of time, aren't you?", "You have 10 new messages.", "Your mother is calling you for dinner.", "Please do not panic.", "This is an annoying popup message.", "It looks like you are trying to log in.", "You look good today.", "Sorry for this annoying message.", ]); } d3.select("#area") .append("div") .attr("id", "popup") .html( `

` + message + `

Exit to home page?

`, ); d3.select("#popup-ok").on("click", function () { core.endEpisode(-1); }); d3.select("#popup-cancel").on("click", function () { d3.selectAll("#username, #password, #subbtn").attr("disabled", null); d3.select("#popup").remove(); }); popupShown = true; } var popupMode = core.sample(["username", "password", null, null]); d3.select("#username").on("focus", popupMode != "username" ? null : showPopup); d3.select("#password").on("focus", popupMode != "password" ? null : showPopup); }; window.onload = function () { core.startEpisode(); }; ``` -------------------------------- ### Run AndroidWorld Docker Container Source: https://github.com/google-research/android_world/blob/main/README.md Starts the Android emulator and FastAPI server within a Docker container. The server will be accessible on http://localhost:5000. ```bash docker run --privileged -p 5000:5000 -it android_world:latest ``` -------------------------------- ### Get Math Support Hash Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/flight/AA/original.html Calculates a hash based on the available Math properties in the browser. This can be used to fingerprint or check for specific math capabilities. ```javascript _gaq_zikNjzf.define( "a155ca7e-07dc-40f0-9a54-2f67c258b428", function (_gaq_clkNjzf, _gaq_dlkNjzf, _gaq_elkNjzf, _gaq_flkNjzf) { _gaq_dlkNjzf["default"] = { name: "mathSupport", group: "browser", setup: function (_gaq_glkNjzf) { var _gaq_hlkNjzf = _gaq_glkNjzf.lib.hash(""); try { _gaq_hlkNjzf = _gaq_glkNjzf.lib.hash(Object.getOwnPropertyNames(Math).join("\0")); } catch (_gaq_ilkNjzf) {} this.data = _gaq_hlkNjzf; }, inBand: function () { return this.data; }, outOfBandOnce: function () { return this.data; }, }; }, ); ``` -------------------------------- ### Generate Transition Timers with JavaScript Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/moving-items.html Creates an array of timer values (in milliseconds) to control when each circle starts its transition. Padding is added to ensure a staggered appearance. ```javascript var generateTimers = function(){ var timers = []; for(var i=0; i0); }); } window.onload = function() { var spinner = $('#spinner').spinner(); // prevent manual entry via keyboard to this text field $('#spinner').bind('keydown', function (event) { event.preventDefault(); }); // prevent it from even receiving focus //$("#spinner").focus(function () { // $(this).blur(); //}); // note: disabling this because when this is turned on somehow holding down on the arrows stops working core.startEpisode(); } ``` -------------------------------- ### Download File from Device (Bash) Source: https://github.com/google-research/android_world/blob/main/docs/tasks_guide.md Copy a file from the device's app data directory to your local machine for more detailed analysis using local tools. ```bash adb pull data/data//files/.txt /local/directory/ ``` -------------------------------- ### Output Help Information Source: https://github.com/google-research/android_world/blob/main/apps/java/com/google/androidenv/miniwob/app/assets/html/miniwob/terminal.html Displays usage instructions for available commands like 'ls' and 'rm' in the terminal. ```javascript var outputHelp = function(){ generateTerminalOutput('ls: list contents'); generateTerminalOutput('Usage: ls'); generateTerminalOutput('rm: remove entries'); generateTerminalOutput('Usage: rm file'); } ``` -------------------------------- ### Run MiniWoB++ Tasks in AndroidWorld Source: https://github.com/google-research/android_world/blob/main/README.md Executes MiniWoB++ web-based tasks within AndroidWorld. Set --suite_family to 'miniwob' and include --perform_emulator_setup for the initial setup. ```bash python run.py \ --suite_family=miniwob \ --perform_emulator_setup ```