diff --git a/gogio/androidbuild.go b/gogio/androidbuild.go index 79943de..23d21d7 100644 --- a/gogio/androidbuild.go +++ b/gogio/androidbuild.go @@ -48,6 +48,7 @@ type manifestData struct { Features []string IconSnip string AppName string + Schemes []string } const ( @@ -445,6 +446,7 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe Features: features, IconSnip: iconSnip, AppName: appName, + Schemes: bi.schemes, } tmpl, err := template.New("test").Parse( ` @@ -461,11 +463,20 @@ func exeAndroid(tmpDir string, tools *androidTools, bi *buildInfo, extraJars, pe android:theme="@style/Theme.GioApp" android:configChanges="screenSize|screenLayout|smallestScreenSize|orientation|keyboardHidden" android:windowSoftInputMode="adjustResize" + android:launchMode= "singleInstance" android:exported="true"> + {{range .Schemes}} + + + + + + + {{end}} `) diff --git a/gogio/build_info.go b/gogio/build_info.go index 1797296..0dc8edc 100644 --- a/gogio/build_info.go +++ b/gogio/build_info.go @@ -30,6 +30,7 @@ type buildInfo struct { notaryAppleID string notaryPassword string notaryTeamID string + schemes []string } type Semver struct { @@ -51,6 +52,10 @@ func newBuildInfo(pkgPath string) (*buildInfo, error) { if *name != "" { appName = *name } + schemes := strings.Split(*schemes, ",") + for i, scheme := range schemes { + schemes[i] = strings.TrimSpace(scheme) + } ver, err := parseSemver(*version) if err != nil { return nil, err @@ -72,6 +77,7 @@ func newBuildInfo(pkgPath string) (*buildInfo, error) { notaryAppleID: *notaryID, notaryPassword: *notaryPass, notaryTeamID: *notaryTeamID, + schemes: schemes, } return bi, nil } diff --git a/gogio/help.go b/gogio/help.go index 2561c2b..b2dfe21 100644 --- a/gogio/help.go +++ b/gogio/help.go @@ -77,4 +77,9 @@ for details. If not provided, the password will be prompted. The -notaryteamid flag specifies the team ID to use for notarization of MacOS app, ignored if -notaryid is not provided. + +The -schemes flag specifies a list of comma separated URI schemes that the program can +handle. For example, use -schemes yourAppName to receive a transfer.URLEvent for URIs +starting with yourAppName://. It is only supported on Android, iOS, macOS and Windows. +On Windows, it will restrict the program to a single instance. ` diff --git a/gogio/iosbuild.go b/gogio/iosbuild.go index 1126cd5..79b0e2f 100644 --- a/gogio/iosbuild.go +++ b/gogio/iosbuild.go @@ -4,6 +4,7 @@ package main import ( "archive/zip" + "bytes" "crypto/sha1" "encoding/hex" "errors" @@ -14,6 +15,7 @@ import ( "path/filepath" "strconv" "strings" + "text/template" "time" "golang.org/x/sync/errgroup" @@ -203,7 +205,10 @@ func exeIOS(tmpDir, target, app string, bi *buildInfo) error { if _, err := runCmd(lipo); err != nil { return err } - infoPlist := buildInfoPlist(bi) + infoPlist, err := buildInfoPlist(bi) + if err != nil { + return err + } plistFile := filepath.Join(app, "Info.plist") if err := os.WriteFile(plistFile, []byte(infoPlist), 0660); err != nil { return err @@ -291,7 +296,7 @@ func iosIcons(bi *buildInfo, tmpDir, appDir, icon string) (string, error) { return assetPlist, err } -func buildInfoPlist(bi *buildInfo) string { +func buildInfoPlist(bi *buildInfo) (string, error) { appName := UppercaseName(bi.name) platform := iosPlatformFor(bi.target) var supportPlatform string @@ -301,36 +306,57 @@ func buildInfoPlist(bi *buildInfo) string { case "tvos": supportPlatform = "AppleTVOS" } - return fmt.Sprintf(` + + manifestSrc := struct { + AppName string + AppID string + Version string + VersionCode uint32 + Platform string + MinVersion int + SupportPlatform string + Schemes []string + }{ + AppName: appName, + AppID: bi.appID, + Version: bi.version.String(), + VersionCode: bi.version.VersionCode, + Platform: platform, + MinVersion: minIOSVersion, + SupportPlatform: supportPlatform, + Schemes: bi.schemes, + } + + tmpl, err := template.New("manifest").Parse(` CFBundleDevelopmentRegion en CFBundleExecutable - %s + {{.AppName}} CFBundleIdentifier - %s + {{.AppID}} CFBundleInfoDictionaryVersion 6.0 CFBundleName - %s + {{.AppName}} CFBundlePackageType APPL CFBundleShortVersionString - %s + {{.Version}} CFBundleVersion - %d + {{.VersionCode}} UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities arm64 DTPlatformName - %s + {{.Platform}} DTPlatformVersion 12.4 MinimumOSVersion - %d + {{.MinVersion}} UIDeviceFamily 1 @@ -338,7 +364,7 @@ func buildInfoPlist(bi *buildInfo) string { CFBundleSupportedPlatforms - %s + {{.SupportPlatform}} UISupportedInterfaceOrientations @@ -353,13 +379,36 @@ func buildInfoPlist(bi *buildInfo) string { DTSDKBuild 16G73 DTSDKName - %s12.4 + {{.Platform}}12.4 DTXcode 1030 DTXcodeBuild 10G8 + {{if .Schemes}} + CFBundleURLTypes + + {{range .Schemes}} + + CFBundleURLSchemes + + {{.}} + + + {{end}} + + {{end}} -`, appName, bi.appID, appName, bi.version, bi.version.VersionCode, platform, minIOSVersion, supportPlatform, platform) +`) + if err != nil { + panic(err) + } + + var manifestBuffer bytes.Buffer + if err := tmpl.Execute(&manifestBuffer, manifestSrc); err != nil { + panic(err) + } + + return manifestBuffer.String(), nil } func iosPlatformFor(target string) string { diff --git a/gogio/macosbuild.go b/gogio/macosbuild.go index 88e9463..d95e34a 100644 --- a/gogio/macosbuild.go +++ b/gogio/macosbuild.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "errors" "fmt" "os" @@ -124,6 +125,19 @@ func (b *macBuilder) setIcon(path string) (err error) { } func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) error { + + manifestSrc := struct { + Name string + Bundle string + Version Semver + Schemes []string + }{ + Name: name, + Bundle: buildInfo.appID, + Version: buildInfo.version, + Schemes: buildInfo.schemes, + } + t, err := template.New("manifest").Parse(` @@ -138,20 +152,28 @@ func (b *macBuilder) setInfo(buildInfo *buildInfo, name string) error { CFBundlePackageType APPL + {{if .Schemes}} + CFBundleURLTypes + + {{range .Schemes}} + + CFBundleURLSchemes + + {{.}} + + + {{end}} + + {{end}} `) if err != nil { - return err + panic(err) } - var manifest bufferCoff - if err := t.Execute(&manifest, struct { - Name, Bundle string - }{ - Name: name, - Bundle: buildInfo.appID, - }); err != nil { - return err + var manifest bytes.Buffer + if err := t.Execute(&manifest, manifestSrc); err != nil { + panic(err) } b.Manifest = manifest.Bytes() diff --git a/gogio/main.go b/gogio/main.go index 5fe373e..3834710 100644 --- a/gogio/main.go +++ b/gogio/main.go @@ -40,6 +40,7 @@ var ( notaryID = flag.String("notaryid", "", "specify the apple id to use for notarization.") notaryPass = flag.String("notarypass", "", "specify app-specific password of the Apple ID to be used for notarization.") notaryTeamID = flag.String("notaryteamid", "", "specify the team id to use for notarization.") + schemes = flag.String("schemes", "", "specify a list of comma separated deep-linking schemes that the program accepts") ) func main() { diff --git a/gogio/windowsbuild.go b/gogio/windowsbuild.go index c867e03..5404893 100644 --- a/gogio/windowsbuild.go +++ b/gogio/windowsbuild.go @@ -202,10 +202,18 @@ func (b *windowsBuilder) buildProgram(buildInfo *buildInfo, name string, arch st dest = filepath.Join(filepath.Dir(b.DestDir), name+"_"+arch+".exe") } + ldflags := buildInfo.ldflags + if buildInfo.schemes != nil { + ldflags += ` -X "gioui.org/app.schemesURI=` + strings.Join(buildInfo.schemes, ",") + `" ` + } + if buildInfo.appID != "" { + ldflags += ` -X "gioui.org/app.ID=` + buildInfo.appID + `" ` + } + cmd := exec.Command( "go", "build", - "-ldflags=-H=windowsgui "+buildInfo.ldflags, + "-ldflags=-H=windowsgui "+ldflags, "-tags="+buildInfo.tags, "-o", dest, buildInfo.pkgPath,