-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8e3d639
commit 080aa07
Showing
7 changed files
with
288 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package emulator | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
|
||
"golang.org/x/text/cases" | ||
"golang.org/x/text/language" | ||
) | ||
|
||
// CreateAVD creates a new Android Virtual Device and returns its name and path. | ||
// | ||
// It wraps the avdmanager tool from Android SDK.Example AVD manager invocation: | ||
// | ||
// avdmanager create avd \ | ||
// --sdcard '8192M' \ | ||
// --package "system-images;android-34;google_apis_playstore;arm64-v8a" \ | ||
// --name "Pixel_7_API_34" \ | ||
// --device "pixel_7" | ||
// | ||
// In addition, it also automatically enables keyboard input. | ||
func CreateAVD(osimage SystemImage, skin string, sdcardMB int) (string, string, error) { | ||
avdName := cases.Title(language.English, cases.NoLower).String(skin) | ||
avdName = fmt.Sprint(avdName, "_API_", osimage.ApiLevel()) | ||
args := []string{"create", "avd"} | ||
args = append(args, "--sdcard", strconv.Itoa(sdcardMB)) | ||
args = append(args, "--package", string(osimage)) | ||
args = append(args, "--name", avdName) | ||
args = append(args, "--device", skin) | ||
|
||
var stderr bytes.Buffer | ||
cmd := exec.Command("avdmanager", args...) | ||
printInvocation(cmd) | ||
cmd.Stderr = &stderr | ||
err := cmd.Run() | ||
if err != nil { | ||
return "", "", fmt.Errorf("failed to run %s: %v, %v", cmd, err, stderr.String()) | ||
} | ||
|
||
avdPath := filepath.Join(os.Getenv("ANDROID_USER_HOME"), "avd", avdName+".avd") | ||
err = updateConfig(avdPath) | ||
if err != nil { | ||
return "", "", fmt.Errorf("failed to update config %s: %v", avdPath, err) | ||
} | ||
|
||
return avdName, avdPath, nil | ||
} | ||
|
||
func Skins() ([]string, error) { | ||
var directories []string | ||
|
||
androidHome := os.Getenv("ANDROID_HOME") | ||
if androidHome == "" { | ||
return nil, fmt.Errorf("ANDROID_HOME environment variable not set") | ||
} | ||
|
||
skinsPath := filepath.Join(androidHome, "skins") | ||
|
||
entries, err := os.ReadDir(skinsPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("read directory %s: %v", skinsPath, err) | ||
} | ||
|
||
for _, entry := range entries { | ||
if entry.IsDir() { | ||
directories = append(directories, entry.Name()) | ||
} | ||
} | ||
|
||
return directories, nil | ||
} | ||
|
||
// updateConfig updates the config.ini file to enable keyboard support. | ||
func updateConfig(avdDir string) error { | ||
configIniPath := filepath.Join(avdDir, "config.ini") | ||
|
||
file, err := os.OpenFile(configIniPath, os.O_RDWR, os.ModePerm) | ||
if err != nil { | ||
return fmt.Errorf("open config.ini file: %v", err) | ||
} | ||
defer file.Close() | ||
|
||
var lines []string | ||
scanner := bufio.NewScanner(file) | ||
for scanner.Scan() { | ||
line := scanner.Text() | ||
line = strings.TrimSpace(line) | ||
if strings.HasPrefix(line, "hw.keyboard=") { | ||
fmt.Println("HAS PREFIX:!!, line: ", line) | ||
lines = append(lines, "hw.keyboard = yes") | ||
} else { | ||
lines = append(lines, line) | ||
} | ||
} | ||
|
||
err = scanner.Err() | ||
if err != nil { | ||
return fmt.Errorf("scanning %s: %v", configIniPath, err) | ||
} | ||
|
||
err = os.Truncate(configIniPath, 0) | ||
if err != nil { | ||
return fmt.Errorf("truncating %s: %v", configIniPath, err) | ||
} | ||
|
||
writer := bufio.NewWriter(file) | ||
for _, line := range lines { | ||
_, err := writer.WriteString(line + "\n") | ||
if err != nil { | ||
return fmt.Errorf("writing %s: %v", configIniPath, err) | ||
} | ||
} | ||
|
||
err = writer.Flush() | ||
if err != nil { | ||
return fmt.Errorf("flushing %s: %v", configIniPath, err) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package emulator | ||
|
||
import ( | ||
"fmt" | ||
"os/exec" | ||
"strings" | ||
) | ||
|
||
// SystemImage is a unique identifier of an Android OS image .Examples: | ||
// - system-images;android-34;google_apis_playstore;arm64-v8a | ||
// - system-images;android-35;google_apis;x86_64 | ||
type SystemImage string | ||
|
||
// ApiLevel returns the API level of this system image. | ||
// Error can be returned when e.g. the new Android version still has only a codename, not a number. | ||
func (s SystemImage) ApiLevel() string { | ||
substrings := strings.Split(string(s), ";") | ||
if len(substrings) != 4 { | ||
panic("invalid output") | ||
} | ||
|
||
// E.g. android-35, android-Baklava, or android-34-ext9 | ||
androidPart := substrings[1] | ||
substrings = strings.Split(androidPart, "-") | ||
if len(substrings) == 0 { | ||
panic("invalid output") | ||
} | ||
|
||
return substrings[1] | ||
} | ||
|
||
// SystemImages returns installed Android system images. | ||
func SystemImages() ([]SystemImage, error) { | ||
systemImages := make([]SystemImage, 0) | ||
|
||
cmd := exec.Command("sdkmanager", "--list_installed") | ||
printInvocation(cmd) | ||
output, err := cmd.Output() | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to run sdkmanager: %v", err) | ||
} | ||
|
||
// Sample output: | ||
// system-images;android-33;google_apis;arm64-v8a | 17 | Google APIs ARM 64 v8a System Image | system-images/android-33/google_apis/arm64-v8a | ||
// system-images;android-33;google_apis_playstore;arm64-v8a | 9 | Google Play ARM 64 v8a System Image | system-images/android-33/google_apis_playstore/arm64-v8a | ||
lines := strings.Split(string(output), "\n") | ||
|
||
for _, line := range lines { | ||
line = strings.TrimSpace(line) | ||
line := strings.Split(line, " ")[0] | ||
if strings.HasPrefix(line, "system-images;") { | ||
systemImages = append(systemImages, SystemImage(line)) | ||
} | ||
} | ||
|
||
for _, systemImage := range systemImages { | ||
fmt.Printf("%s\n", systemImage) | ||
} | ||
|
||
return systemImages, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package emulator | ||
|
||
import ( | ||
"log" | ||
"os/exec" | ||
) | ||
|
||
func printInvocation(cmd *exec.Cmd) { | ||
if PrintInvocations { | ||
log.Println(cmd.String()) | ||
} | ||
} |