blob: 71bb44114c83e4b5dc0a90ec105a52906f7dc4ef [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"strings"
"v.io/x/lib/cmdline"
"v.io/x/lib/gosh"
)
var (
forceStopFlag bool
forceInstallFlag bool
)
func init() {
initializePropertyCacheFlags(&cmdMadbStart.Flags)
initializeBuildFlags(&cmdMadbStart.Flags)
cmdMadbStart.Flags.BoolVar(&forceStopFlag, "force-stop", true, `Force stop the target app before starting the activity.`)
cmdMadbStart.Flags.BoolVar(&forceInstallFlag, "force-install", false, `Force install the target app before starting the activity.`)
}
var cmdMadbStart = &cmdline.Command{
Runner: subCommandRunner{initMadbStart, runMadbStartForDevice, true},
Name: "start",
Short: "Launch your app on all devices",
Long: `
Launches your app on all devices.
In most cases, running "madb start" from an Android Gradle project directory will do the right thing
for you. "madb start" will build the project first. After the project build is completed, this
command will install the best matching .apk for each device, only if one or more of the following
conditions are met:
- the app is not found on the device
- the installed app is outdated (determined by comparing the last update time of the
installed app and the last modification time of the local .apk file)
- "-force-install" flag is set
If you would like to run the same version of the app repeatedly (e.g., for QA testing), you can
explicitly turn off the build flag by providing "-build=false" to skip the build step.
To run your app as a specific user on a particular device, use 'madb user set' command to set the
default user ID for that device. (See 'madb help user' for more details.)
`,
ArgsName: "[<application_id> <activity_name>]",
ArgsLong: `
<application_id> is usually the package name where the activities are defined.
(See: http://tools.android.com/tech-docs/new-build-system/applicationid-vs-packagename)
<activity_name> is the Java class name for the activity you want to launch.
If the package name of the activity is different from the application ID, the activity name must be
a fully-qualified name (e.g., com.yourcompany.yourapp.MainActivity).
If either <application_id> or <activity_name> is provided, the other must be provided as well.
If no arguments are specified, madb automatically determines which app to launch, based on the build
scripts found in the current working directory.
1) If the working directory contains a Flutter project (i.e., has "flutter.yaml"), this command will
run "flutter start --device-id <device serial>" for all the specified devices.
2) If the working directory contains a Gradle Android project (i.e., has "build.gradle"), this
command will run a small Gradle script to extract the application ID and the main activity name.
In this case, the extracted IDs are cached, so that "madb start" can be repeated without even
running the Gradle script again. The IDs can be re-extracted by clearing the cache by providing
"-clear-cache" flag.
`,
}
func initMadbStart(env *cmdline.Env, args []string, properties variantProperties) ([]string, error) {
// If the "-build" flag is set, call the init function of the install command, which would run
// the relevant Gradle build tasks to build the project.
if buildFlag {
newArgs, err := initMadbInstall(env, args, properties)
if err != nil {
return nil, err
}
args = newArgs
}
return initMadbCommand(env, args, properties, true, true)
}
func runMadbStartForDevice(env *cmdline.Env, args []string, d device, properties variantProperties) error {
// If the "-build" flag is set, install the app first.
if err := installVariantToDevice(d, properties, forceInstallFlag); err != nil {
return err
}
sh := gosh.NewShell(nil)
defer sh.Cleanup()
sh.ContinueOnError = true
if len(args) == 2 {
appID, activity := args[0], args[1]
// In case the activity name is a simple name (i.e. without the package name), add a dot in
// the front. This is a shorthand syntax to prepend the activity name with the package name
// provided in the manifest.
// http://developer.android.com/guide/topics/manifest/activity-element.html#nm
if !strings.ContainsAny(activity, ".") {
activity = "." + activity
}
// More details on the "adb shell am" command can be found at:
// http://developer.android.com/tools/help/shell.html#am
cmdArgs := []string{"-s", d.Serial, "shell", "am", "start"}
if forceStopFlag {
cmdArgs = append(cmdArgs, "-S")
}
// Specify the user ID if applicable.
if d.UserID != "" {
cmdArgs = append(cmdArgs, "--user", d.UserID)
}
cmdArgs = append(cmdArgs, "-n", appID+"/"+activity)
cmd := sh.Cmd("adb", cmdArgs...)
return runGoshCommandForDevice(cmd, d, true)
}
// In case of flutter, the application ID is not even needed.
// Simply run "flutter run --device-id <device_serial>" on all devices.
if isFlutterProject(wd) {
cmdArgs := []string{"run", "--device-id", d.Serial}
cmd := sh.Cmd("flutter", cmdArgs...)
return runGoshCommandForDevice(cmd, d, false)
}
return fmt.Errorf("No arguments are provided and failed to extract the properties from the build scripts.")
}