Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This table describes the current status of each package in `gioui.org/x`:

| Name | Purpose | Intended for core? | Non-core dependencies? | API Stability |
| ----------- | ------------------------------------------- | ------------------ | ---------------------- | ------------- |
| browser | Open browser url | no | yes | unstable |
| colorpicker | Widgets for choosing colors | uncertain | no | unstable |
| component | Material.io components | uncertain | no | unstable |
| eventx | Event management tools | yes | no | unstable |
Expand Down
9 changes: 9 additions & 0 deletions browser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Open browser

Cross platform open url for [Gio](https://gioui.org) applications.

## Usage

```go
browser.OpenUrl("https://gioui.org/")
```
38 changes: 38 additions & 0 deletions browser/browser_android.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//go:build android
// +build android

package browser

/*
#cgo LDFLAGS: -landroid

#include <jni.h>
#include <stdlib.h>
*/

import (
"gioui.org/app"
"git.wow.st/gmp/jni"
)

//go:generate javac -source 8 -target 8 -bootclasspath $ANDROID_HOME/platforms/android-30/android.jar -d $TEMP/browser_browser_android/classes browser_android.java
//go:generate jar cf browser_android.jar -C $TEMP/browser_browser_android/classes .

func OpenUrl(url string) error {
err := jni.Do(jni.JVMFor(app.JavaVM()), func(env jni.Env) error {
class, err := jni.LoadClass(env, jni.ClassLoaderFor(env, jni.Object(app.AppContext())), "org/gioui/x/browser/browser_android")
if err != nil {
return err
}

methodId := jni.GetStaticMethodID(env, class, "openUrl", "(Landroid/content/Context;Ljava/lang/String;)V")
err = jni.CallStaticVoidMethod(env, class, methodId, jni.Value(app.AppContext()), jni.Value(jni.JavaString(env, url)))
if err != nil {
return err
}

return nil
})

return err
}
Binary file added browser/browser_android.jar
Binary file not shown.
14 changes: 14 additions & 0 deletions browser/browser_android.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.gioui.x.browser;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;

public class browser_android {
public static void openUrl(Context context, String url) {
Uri webpage = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, webpage);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
13 changes: 13 additions & 0 deletions browser/browser_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build darwin
// +build darwin

package browser

import (
"os/exec"
)

func OpenUrl(url string) error {
cmd := exec.Command("open", url)
return cmd.Run()
}
32 changes: 32 additions & 0 deletions browser/browser_ios.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//go:build ios
// +build ios

package browser

/*
#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc
extern static CFTypeRef newNSString(unichar *chars, NSUInteger length);
extern static void openUrl(CFTypeRef str);
*/

import "C"
import (
"unicode/utf16"
"unsafe"
)

func stringToNSString(str string) C.CFTypeRef {
u16 := utf16.Encode([]rune(str))
var chars *C.unichar
if len(u16) > 0 {
chars = (*C.unichar)(unsafe.Pointer(&u16[0]))
}
return C.newNSString(chars, C.NSUInteger(len(u16)))
}

func OpenUrl(url string) error {
cstr := stringToNSString(url)
defer C.CFRelease(cstr)
C.openUrl(cstr)
return nil
}
22 changes: 22 additions & 0 deletions browser/browser_ios.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <Foundation/Foundation.h>
#include <UIKit/UIKit.h>
#include <stdint.h>

static CFTypeRef newNSString(unichar *chars, NSUInteger length) {
@autoreleasepool {
NSString *s = [NSString string];
if (length > 0) {
s = [NSString stringWithCharacters:chars length:length];
}
return CFBridgingRetain(s);
}
}

static void openUrl(CFTypeRef str) {
@autoreleasepool {
NSString *s = (__bridge NSString *)str;
NSURL *url = [NSURL URLWithString:s];
UIApplication *application = [UIApplication sharedApplication];
[application openURL:url options:@{} completionHandler:nil];
}
}
13 changes: 13 additions & 0 deletions browser/browser_js.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build js
// +build js

package browser

import (
"syscall/js"
)

func OpenUrl(url string) error {
js.Global().Call("open", url, "_blank", "")
return nil
}
Comment on lines +10 to +13
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How widely did you test this across browser platforms? I'm curious whether @inkeliz's sophisticated hacks are needed here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Safari, the open function must be invoked inside onClick event. However, Gio gets and releases this event.

If open is called without direct user interaction, outside of onClick, it may not always work. It's inconsistent, so sometimes it works, but often it doesn't.

The "hack" requires the user to click anywhere in the page to open, it's not perfect, but works as fallback.

14 changes: 14 additions & 0 deletions browser/browser_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//go:build (linux && !android) || freebsd || openbsd
// +build linux,!android freebsd openbsd

package browser

import (
"os/exec"
)

func OpenUrl(url string) error {
// xdg-open is part of the freedesktop.org suite and should be available on all distro
cmd := exec.Command("xdg-open", url)
return cmd.Run()
}
13 changes: 13 additions & 0 deletions browser/browser_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build windows
// +build windows

package browser

import (
"os/exec"
)

func OpenUrl(url string) error {
cmd := exec.Command("cmd", "/c", "start", url)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we can avoid spawning a cmd instance for this, I think I'd prefer using that more direct approach.

return cmd.Run()
}