1// Copyright 2019 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import '../base/process.dart'; 6import '../globals.dart'; 7 8import 'fuchsia_device.dart'; 9 10// Usage: tiles_ctl <command> 11// Supported commands: 12// start 13// add [--disable-focus] <url> [<args>...] 14// remove <key> 15// list 16// quit 17 18/// A simple wrapper around the 'tiles_ctl' tool running on the Fuchsia device. 19class FuchsiaTilesCtl { 20 /// Finds the key for the app called [appName], or returns -1 if it can't be 21 /// found. 22 static Future<int> findAppKey(FuchsiaDevice device, String appName) async { 23 final FuchsiaTilesCtl tilesCtl = fuchsiaDeviceTools.tilesCtl; 24 final Map<int, String> runningApps = await tilesCtl.list(device); 25 if (runningApps == null) { 26 printTrace('tiles_ctl is not running'); 27 return -1; 28 } 29 for (MapEntry<int, String> entry in runningApps.entries) { 30 if (entry.value.contains('$appName#meta')) { 31 return entry.key; 32 } 33 } 34 return -1; 35 } 36 37 /// Ensures that tiles is running on the device. 38 static Future<bool> ensureStarted(FuchsiaDevice device) async { 39 final FuchsiaTilesCtl tilesCtl = fuchsiaDeviceTools.tilesCtl; 40 final Map<int, String> runningApps = await tilesCtl.list(device); 41 if (runningApps == null) { 42 return tilesCtl.start(device); 43 } 44 return true; 45 } 46 47 /// Instructs 'tiles' to start on the device. 48 /// 49 /// Returns true on success and false on failure. 50 Future<bool> start(FuchsiaDevice device) async { 51 final RunResult result = await device.shell('tiles_ctl start'); 52 return result.exitCode == 0; 53 } 54 55 /// Returns a mapping of tile keys to app urls. 56 /// 57 /// Returns an empty mapping if tiles_ctl is running but no apps are running. 58 /// Returns null if tiles_ctl is not running. 59 Future<Map<int, String>> list(FuchsiaDevice device) async { 60 // Output of tiles_ctl list has the format: 61 // Found 1 tiles: 62 // Tile key 1 url fuchsia-pkg://fuchsia.com/stocks#meta/stocks.cmx ... 63 final Map<int, String> tiles = <int, String>{}; 64 final RunResult result = await device.shell('tiles_ctl list'); 65 if (result.exitCode != 0) { 66 return null; 67 } 68 // Look for evidence that tiles_ctl is not running. 69 if (result.stdout.contains("Couldn't find tiles component in realm")) { 70 return null; 71 } 72 // Find lines beginning with 'Tile' 73 for (String line in result.stdout.split('\n')) { 74 final List<String> words = line.split(' '); 75 if (words.isNotEmpty && words[0] == 'Tile') { 76 final int key = int.tryParse(words[2]); 77 final String url = words[4]; 78 tiles[key] = url; 79 } 80 } 81 return tiles; 82 } 83 84 /// Instructs tiles on the device to begin running the app at [url] in a new 85 /// tile. 86 /// 87 /// The app is passed the arguments in [args]. Flutter apps receive these 88 /// arguments as arguments to `main()`. [url] should be formatted as a 89 /// Fuchsia-style package url, e.g.: 90 /// fuchsia-pkg://fuchsia.com/flutter_gallery#meta/flutter_gallery.cmx 91 /// Returns true on success and false on failure. 92 Future<bool> add(FuchsiaDevice device, String url, List<String> args) async { 93 final RunResult result = await device.shell( 94 'tiles_ctl add $url ${args.join(" ")}'); 95 return result.exitCode == 0; 96 } 97 98 /// Instructs tiles on the device to remove the app with key [key]. 99 /// 100 /// Returns true on success and false on failure. 101 Future<bool> remove(FuchsiaDevice device, int key) async { 102 final RunResult result = await device.shell('tiles_ctl remove $key'); 103 return result.exitCode == 0; 104 } 105 106 /// Instructs tiles on the device to quit. 107 /// 108 /// Returns true on success and false on failure. 109 Future<bool> quit(FuchsiaDevice device) async { 110 final RunResult result = await device.shell('tiles_ctl quit'); 111 return result.exitCode == 0; 112 } 113} 114