Compare commits

...

5 Commits

Author SHA1 Message Date
dc7661e226 Android: launch the 'All files access' settings screen at startup.
MANAGE_EXTERNAL_STORAGE (needed to read the store at
/storage/emulated/0/...) cannot be requested with a runtime
permission dialog; the user must toggle it in system settings.
Port the mechanism from the pad app:

- Permissions.java: a Fragment registered at window attach that
  launches Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
  (per-app settings screen) when Environment.isExternalStorageManager()
  is false on API 30+ (runtime READ_EXTERNAL_STORAGE request on
  older APIs). The guard means the screen appears only until the
  user grants it.
- The compiled classes ship in PgpConnect.jar's directory jar
  (Permissions.jar); gogio picks *.jar up from the package dir and
  dexes them. go:generate now rebuilds it.
- jni_android.c/h: registerPermissionsFragment(), mirroring
  registerFragment() for PgpConnect.
- Impl wiring: call it from the AndroidViewEvent handler right
  after InitPgp.

Verified on the x86_64 emulator: with the appop denied the system
settings screen opens automatically on launch; after granting it,
launch is clean and list + OpenKeychain decryption from
/sdcard/Pass work end to end.
2026-08-21 14:44:38 -04:00
4b74367a55 GUI: fix invisible text on Linux, tiny button hit-areas, missing labels.
Found by running and debugging the Linux build on a virtual display:

- impl_linux.go: set fontSize (16, like Darwin). It was only defined
  for the Android and Darwin builds, so on Linux every label and
  button was rendered at size zero (all text invisible).
- ui.go: extend the Button click area to the full button width, not
  just the label width. Short labels (e.g. a few characters in a
  full-width list row) had a tiny tap target that was easy to miss.
- main.go: set Color: black on the insert-page buttons (<, >, generate,
  @, #). A zero-value NRGBA has A=0 (transparent), so their labels
  were invisible even though the buttons worked.
- main.go: gofmt (modern operator spacing).
2026-08-21 13:48:14 -04:00
e7218b1efd Complete the Gio v0.10.2 GUI migration (working on Android).
- Event loop: call w.Event() and FrameEvent.Frame() on the same
  goroutine; no channel bridge (the bridge broke frame rendering).
- clip: use clip.UniformRRect(...).Push(gtx.Ops) / Pop() for real
  clipping in layoutRRect (old .Add(ops) painted unclipped).
- Window clear color is white in v0.10.2.
- Fonts: always provide gofont; on Android use
  WithCollection(gofont.Collection()).
- Button labels: explicitly set Color (zero-value NRGBA has A=0,
  i.e. transparent, so labels were invisible).
- Filter: detect changes by comparing the editor text, since
  material.Editor.Layout consumes the editor's input events
  internally and a later FilterEd.Update(gtx) never sees them.
- idPage: persist the manually entered ID with store.SetID (on
  Android nativeIdentities is unimplemented, so there are no id
  buttons to click).
- Use U+2026 (gofont) instead of U+22EE (not in gofont) for the
  menu button.
- Split getConfDir/noidLabelText out into impl_linux.go for the
  Linux build.
2026-08-21 13:00:35 -04:00
af1b878308 passgo package: modernize for go 1.24; reset store.Empty in GetStore.
- Update to //go:build tags, gofmt, and the android-35 javac template
  in //go:generate.
- GetStore: reset store.Empty when the store directory exists (it was
  only set true when the dir was missing, so a reconfigured store kept
  the stale 'Empty' state and showed the configure button).
- Rebuild PgpConnect.jar (android-35).
2026-08-21 13:00:04 -04:00
edb38a6b85 Bump dependencies: gioui.org v0.10.2, fsnotify v1.8.0, go 1.24.
fsnotify v1.4.x's inotify_poller uses the legacy epoll_wait(2) syscall,
which is blocked by seccomp on x86_64 Android emulators (SIGSYS crash on
launch). v1.8.0 uses blocking reads on the inotify fd instead.

Also drop the unused git.wow.st/gmp/jni dependency.
2026-08-21 12:59:51 -04:00
20 changed files with 423 additions and 244 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ cmd/passgo/passgo
cmd/passgo-gui/passgo-gui
nohup.out
*.apk
*.idsig
/passgo-gui

BIN
Permissions.jar Normal file

Binary file not shown.

91
Permissions.java Normal file
View File

@ -0,0 +1,91 @@
package st.wow.git.passgo;
import java.lang.Runnable;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.content.Context;
import android.util.Log;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.Manifest;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.view.View;
// Requests the storage permissions the app needs to read the password
// store in shared storage. On Android 11+ (API 30+) the
// MANAGE_EXTERNAL_STORAGE ("All files access") permission cannot be
// requested with a dialog; the user must toggle it in the system
// settings, so we launch that settings screen directly for this app.
public class Permissions extends Fragment {
Context ctx;
Handler handler;
final int PERMISSIONS_REQUEST = 1;
public Permissions(View view) {
Log.d("passgo", "Permissions()");
this.ctx = view.getContext();
this.handler = new Handler(this.ctx.getMainLooper());
Permissions inst = this;
handler.post(new Runnable() {
public void run() {
Activity act = (Activity)ctx;
FragmentTransaction ft = act.getFragmentManager().beginTransaction();
ft.add(inst, "Permissions");
ft.commitNow();
}
});
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d("passgo", "Permissions onAttach()");
if (!(context instanceof Activity)) {
Log.w("passgo", "Context is not an Activity");
return;
}
Activity activity = (Activity) context;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
if (context.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
PERMISSIONS_REQUEST
);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
&& !Environment.isExternalStorageManager()) {
Log.d("passgo", "Requesting all files access");
Intent intent = new Intent(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
Uri.parse("package:" + activity.getPackageName())
);
if (intent.resolveActivity(activity.getPackageManager()) != null) {
startActivity(intent);
} else {
Intent fallback = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
if (fallback.resolveActivity(activity.getPackageManager()) != null) {
startActivity(fallback);
} else {
Log.e("passgo", "No activity found for all files access settings");
}
}
}
}
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d("passgo", "Permissions onActivityResult(" + requestCode + "): " + resultCode);
super.onActivityResult(requestCode, resultCode, data);
}
}

Binary file not shown.

View File

@ -1,13 +1,13 @@
//+build android
//go:build android
package main
import (
"os"
"git.wow.st/gmp/passgo"
"gioui.org/app"
"gioui.org/io/event"
"git.wow.st/gmp/passgo"
)
var (
@ -22,8 +22,15 @@ func init() {
func handleEvent(e event.Event) {
switch e := e.(type) {
case app.ViewEvent:
initPgp(e.View)
case app.AndroidViewEvent:
// A zero View means the view is detached (e.g. the activity going
// away on a recents-wipe); there is nothing to register for a
// detached view. A re-attach arrives as a fresh event with a live
// view.
if e.View != 0 {
initPgp(e.View)
initPermissions(e.View)
}
}
}
@ -31,6 +38,10 @@ func initPgp(view uintptr) {
passgo.InitPgp(view)
}
func initPermissions(view uintptr) {
passgo.InitPermissions(view)
}
func getConfDir() (string, error) {
ret, err := app.DataDir()
if err != nil {

View File

@ -1,14 +1,14 @@
//+build darwin
//go:build darwin
package main
import (
"io"
"os"
"os/user"
"path"
"gioui.org/font/opentype"
"gioui.org/text"
)
var (
@ -22,22 +22,18 @@ func setFont() error {
return err
}
fnts, err := opentype.ParseCollectionReaderAt(f)
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
log(Info, "Cannot read system font collection")
return err
}
fnts, err := opentype.ParseCollection(data)
if err != nil {
log(Info, "Cannot parse font collection")
return err
}
fnt, err := fnts.Font(0)
if err != nil {
log(Info, "Cannot get font from collection")
return err
}
collection = append(collection, text.FontFace{Font: text.Font{}, Face: fnt})
if err != nil {
log(Info, "Cannot access font from font collection")
return err
}
//font.Register(text.Font{}, face)
collection = append(collection, fnts...)
return nil
}

View File

@ -0,0 +1,33 @@
//go:build linux && !android
package main
import (
"os"
"gioui.org/app"
)
var (
noidLabelText = "No GPG ids available"
)
func init() {
fontSize = 16
}
func getConfDir() (string, error) {
ret, err := app.DataDir()
if err != nil {
log(Error, "Cannot get data directory:", err)
return "", err
}
if _, err := os.Stat(ret); os.IsNotExist(err) {
err = os.MkdirAll(ret, 0700)
if err != nil {
log(Error, "Cannot create configuration directory ", ret)
return "", err
}
}
return ret, nil
}

View File

@ -1,4 +1,4 @@
//+build !android
//go:build !android
package main

View File

@ -1,5 +1,3 @@
// +build darwin linux
package main
import (
@ -16,8 +14,6 @@ import (
"gioui.org/app"
"gioui.org/font/gofont"
"gioui.org/io/key"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/text"
@ -43,24 +39,26 @@ type conf struct {
}
func main() {
if false { go func() {
f, err := os.Create("cpuprofile")
if err != nil {
fmt.Printf("Can't create CPU profile\n")
os.Exit(-1)
}
fmt.Printf("Starting CPU profile\n")
if err := pprof.StartCPUProfile(f); err != nil {
fmt.Printf("Can't start CPU profile\n")
if false {
go func() {
f, err := os.Create("cpuprofile")
if err != nil {
fmt.Printf("Can't create CPU profile\n")
os.Exit(-1)
}
fmt.Printf("Starting CPU profile\n")
if err := pprof.StartCPUProfile(f); err != nil {
fmt.Printf("Can't start CPU profile\n")
f.Close()
os.Exit(-1)
}
time.Sleep(time.Second * 10)
fmt.Printf("Stopping CPU profile\n")
pprof.StopCPUProfile()
f.Close()
os.Exit(-1)
}
time.Sleep(time.Second * 10)
fmt.Printf("Stopping CPU profile\n")
pprof.StopCPUProfile()
f.Close()
fmt.Printf("CPU profile written\n")
}() }
fmt.Printf("CPU profile written\n")
}()
}
var fd *os.File
var err error
confDir, err = getConfDir()
@ -106,7 +104,7 @@ func main() {
passch = make(chan []byte)
go func() {
log(Info,"passgo.Identities()")
log(Info, "passgo.Identities()")
passgo.Identities()
}()
go Updater()
@ -117,18 +115,18 @@ func main() {
}
var (
fontSize float32
fontSize float32
collection []text.FontFace
confDir string
Config conf
l []passgo.Pass
mux sync.Mutex
store passgo.Store
reload chan struct{}
updated chan struct{}
chdir chan struct{}
passch chan []byte
th *material.Theme
confDir string
Config conf
l []passgo.Pass
mux sync.Mutex
store passgo.Store
reload chan struct{}
updated chan struct{}
chdir chan struct{}
passch chan []byte
th *material.Theme
)
func Updater() {
@ -196,14 +194,22 @@ func saveConf(fds ...*os.File) {
}
func eventLoop() {
if collection == nil {
collection = gofont.Collection()
}
var ops op.Ops
th = material.NewTheme(collection)
th = material.NewTheme()
th.TextSize = unit.Sp(fontSize)
w := app.NewWindow(
if len(collection) > 0 {
// Use the embedded Go font plus the platform fonts (e.g. the
// Unicode font installed on Darwin) instead of the system fonts.
th.Shaper = text.NewShaper(text.NoSystemFonts(),
text.WithCollection(append(gofont.Collection(), collection...)))
} else {
// Always provide the embedded Go font: the system font scan
// finds no fonts on Android, and a fontless shaper renders a
// blank screen.
th.Shaper = text.NewShaper(text.WithCollection(gofont.Collection()))
}
w := new(app.Window)
w.Option(
app.Size(unit.Dp(250), unit.Dp(500)),
app.Title("passgo"))
@ -211,13 +217,12 @@ func eventLoop() {
var c1 layout.FlexChild // flex child for title bar
sysinset := &layout.Inset{}
margin := layout.UniformInset(unit.Dp(10))
title := material.Body1(th, "passgo")
dotsBtn := &Button{
Size: unit.Sp(fontSize),
Label: "\xe2\x8b\xae",
Label: "\u2026",
Alignment: text.Middle,
Color: black,
Background: gray,
@ -243,6 +248,7 @@ func eventLoop() {
var overlayStart time.Time
FilterEd := &widget.Editor{SingleLine: true}
var lastFilter string
updateBtns := func() {
passBtns = passBtns[:0]
pathnames = pathnames[:0]
@ -256,10 +262,10 @@ func eventLoop() {
d, n := path.Split(x.Pathname)
x = passgo.Pass{
Pathname: d,
Level: x.Level - 1,
Dir: true,
Level: x.Level - 1,
Dir: true,
}
fmt.Printf("addd(): d/n = '%s'/'%s'\n", d,n)
fmt.Printf("addd(): d/n = '%s'/'%s'\n", d, n)
if dirs[d] != true {
addd(x)
}
@ -267,8 +273,9 @@ func eventLoop() {
s := strings.Repeat(" /", x.Level)
lbl := strings.Join([]string{s, d}, "")
passBtns = append(passBtns, &Button{
Size: unit.Sp(fontSize),
Label: lbl,
Size: unit.Sp(fontSize),
Label: lbl,
Color: black,
Background: gray,
})
pathnames = append(pathnames, x.Pathname)
@ -288,6 +295,7 @@ func eventLoop() {
passBtns = append(passBtns, &Button{
Size: unit.Sp(fontSize),
Label: lbl,
Color: black,
Background: gray,
})
pathnames = append(pathnames, x.Pathname)
@ -304,7 +312,7 @@ func eventLoop() {
idBtns := make([]*Button, 0)
updateIdBtns := func() {
log(Info,"passgo.Identities()")
log(Info, "passgo.Identities()")
ids, err := passgo.Identities()
if err != nil {
log(Info, err)
@ -335,7 +343,7 @@ func eventLoop() {
}
storeDirLabel := material.Label(th, unit.Sp(fontSize), "Store directory")
storeDirEd := &widget.Editor{ SingleLine: true}
storeDirEd := &widget.Editor{SingleLine: true}
storeDirEd.SetText(store.Dir)
saveBtn := &Button{
Size: unit.Sp(fontSize),
@ -361,7 +369,7 @@ func eventLoop() {
}
promptLabel := material.Label(th, unit.Sp(fontSize), "passphrase")
promptEd := &widget.Editor{ SingleLine: true, Submit: true }
promptEd := &widget.Editor{SingleLine: true, Submit: true}
okBtn := &Button{
Size: unit.Sp(fontSize),
Label: "ok",
@ -386,9 +394,9 @@ func eventLoop() {
insertLabel := material.Label(th, unit.Sp(fontSize), "Insert")
passnameLabel := material.Label(th, unit.Sp(fontSize), "password name:")
passnameEd := &widget.Editor{ SingleLine: true }
passnameEd := &widget.Editor{SingleLine: true}
passvalLabel := material.Label(th, unit.Sp(fontSize), "password value:")
passvalEd := &widget.Editor{ SingleLine: true, Submit: true }
passvalEd := &widget.Editor{SingleLine: true, Submit: true}
noidLabel := material.Label(th, unit.Sp(fontSize), noidLabelText)
idLabel := material.Label(th, unit.Sp(fontSize), "Select ID")
@ -433,6 +441,7 @@ func eventLoop() {
})
if xBtn.Clicked() {
FilterEd.SetText("")
lastFilter = ""
updateBtns()
}
return lpf1.Layout(gtx, c21, c22)
@ -440,9 +449,6 @@ func eventLoop() {
c3 := layout.Flexed(1.0, func(gtx C) D {
var ret D
mux.Lock()
if lst.Dragging() {
key.HideInputOp{}.Add(gtx.Ops)
}
switch {
case store.Empty:
confBtn.Layout(gtx)
@ -543,7 +549,11 @@ func eventLoop() {
if animating && x > end {
animOff()
}
if len(FilterEd.Events()) > 0 {
// material.Editor.Layout (above) already consumes the editor's
// input events, so we detect a filter change by comparing the
// current text against the previous frame's.
if t := FilterEd.Text(); t != lastFilter {
lastFilter = t
updateBtns()
}
return ret
@ -588,19 +598,14 @@ func eventLoop() {
c4 = layout.Rigid(func(gtx C) D {
return material.Editor(th, idEd, "id").Layout(gtx)
})
for _, e := range idEd.Events() {
switch e.(type) {
case widget.SubmitEvent:
log(Info, "Submit")
store.Id = idEd.Text()
page = listPage
}
}
c5 = layout.Rigid(func(gtx C) D {
return idSubmitBtn.Layout(gtx)
})
if idSubmitBtn.Clicked() {
store.Id = idEd.Text()
// Persist the ID so it survives a restart (the manual entry
// path has no keyring to re-derive it from).
store.SetID(store.Id)
page = listPage
}
c6 = layout.Rigid(func(gtx C) D {
@ -632,22 +637,31 @@ func eventLoop() {
}
return ret
})
if ev, ok := idEd.Update(gtx); ok {
if _, isSubmit := ev.(widget.SubmitEvent); isSubmit {
log(Info, "Submit")
store.Id = idEd.Text()
store.SetID(store.Id)
w.Invalidate()
page = listPage
}
}
return flex.Layout(gtx, c1, c2, c3, c4, c5, c6, c7)
}
var insName, insValue string
genBtn := &SelButton{SelColor: gray}
genBtn.Button = Button{Size: unit.Sp(fontSize), Label: "generate"}
genBtn.Button = Button{Size: unit.Sp(fontSize), Label: "generate", Color: black}
symBtn := &SelButton{SelColor: gray}
numBtn := &SelButton{SelColor: gray}
symBtn.Button = Button{Size: unit.Sp(fontSize), Label: "@"}
numBtn.Button = Button{Size: unit.Sp(fontSize), Label: "#"}
symBtn.Button = Button{Size: unit.Sp(fontSize), Label: "@", Color: black}
numBtn.Button = Button{Size: unit.Sp(fontSize), Label: "#", Color: black}
symBtn.Select()
numBtn.Select()
lenEd := &widget.Editor{ SingleLine: true, Alignment: text.End }
lenEd := &widget.Editor{SingleLine: true, Alignment: text.End}
lenEd.SetText("15")
lBtn := &Button{Size: unit.Sp(fontSize), Label: "<", Background: gray}
rBtn := &Button{Size: unit.Sp(fontSize), Label: ">", Background: gray}
lBtn := &Button{Size: unit.Sp(fontSize), Label: "<", Color: black, Background: gray}
rBtn := &Button{Size: unit.Sp(fontSize), Label: ">", Color: black, Background: gray}
updatePw := func() {
if !genBtn.Selected {
@ -835,9 +849,8 @@ func eventLoop() {
promptPage = func(gtx C) D {
submit := false
for _, e := range promptEd.Events() {
switch e.(type) {
case widget.SubmitEvent:
if ev, ok := promptEd.Update(gtx); ok {
if _, isSubmit := ev.(widget.SubmitEvent); isSubmit {
log(Info, "Submit")
submit = true
}
@ -882,78 +895,73 @@ func eventLoop() {
ms := &runtime.MemStats{}
x := 0
var mallocs uint64
var idsInit bool
for {
// Window.Event and FrameEvent.Frame must be called from the same
// goroutine; drain the auxiliary channels non-blocking instead of
// bridging the window events through a channel.
select {
case <-updated:
log(Info, "UPDATE")
updateBtns()
w.Invalidate()
default:
}
select {
case <-anim.C:
w.Invalidate()
case e := <-w.Events():
default:
}
switch e := w.Event().(type) {
case app.DestroyEvent:
os.Exit(0)
case app.FrameEvent:
x++
if x == 100 {
runtime.ReadMemStats(ms)
mallocs = ms.Mallocs
if !idsInit { // the app is running; refresh the ID list
idsInit = true
go updateIdBtns()
}
switch e := e.(type) {
case system.DestroyEvent:
os.Exit(0)
case system.StageEvent:
if e.Stage == system.StageRunning {
go func() {
updateIdBtns()
}()
}
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)
// NewContext resets ops and adjusts for e.Insets.
gtx := app.NewContext(&ops, e)
sysinset.Top = e.Insets.Top
sysinset.Bottom = e.Insets.Bottom
sysinset.Left = e.Insets.Left
sysinset.Right = e.Insets.Right
sysinset.Layout(gtx, func(gtx C) D {
return margin.Layout(gtx, func(gtx C) D {
margincs = gtx.Constraints
c1 = layout.Rigid(func(gtx C) D {
ct2 := layout.Rigid(func(gtx C) D {
return plusBtn.Layout(gtx)
})
ct3 := layout.Rigid(func(gtx C) D {
return dotsBtn.Layout(gtx)
})
ct1 := layout.Flexed(1.0, func(gtx C) D {
gtx.Constraints.Min.X = gtx.Constraints.Max.X
return title.Layout(gtx)
})
return titleflex.Layout(gtx, ct1, ct2, ct3)
})
return page(gtx)
margin.Layout(gtx, func(gtx C) D {
margincs = gtx.Constraints
c1 = layout.Rigid(func(gtx C) D {
ct2 := layout.Rigid(func(gtx C) D {
return plusBtn.Layout(gtx)
})
ct3 := layout.Rigid(func(gtx C) D {
return dotsBtn.Layout(gtx)
})
ct1 := layout.Flexed(1.0, func(gtx C) D {
gtx.Constraints.Min.X = gtx.Constraints.Max.X
return title.Layout(gtx)
})
return titleflex.Layout(gtx, ct1, ct2, ct3)
})
if dotsBtn.Clicked() {
log(Info, "Configure")
w.Invalidate()
page = confPage
}
if plusBtn.Clicked() {
log(Info, "Plus")
w.Invalidate()
insName, insValue = "", ""
page = insertPage
}
e.Frame(gtx.Ops)
default:
handleEvent(e)
return page(gtx)
})
if dotsBtn.Clicked() {
log(Info, "Configure")
w.Invalidate()
page = confPage
}
if x == 100 {
x = 0
runtime.ReadMemStats(ms)
fmt.Printf("mallocs: %d\n", ms.Mallocs-mallocs)
if plusBtn.Clicked() {
log(Info, "Plus")
w.Invalidate()
insName, insValue = "", ""
page = insertPage
}
e.Frame(&ops)
default:
handleEvent(e)
}
if x == 100 {
x = 0
runtime.ReadMemStats(ms)
fmt.Printf("mallocs: %d\n", ms.Mallocs-mallocs)
}
}
}

View File

@ -4,9 +4,8 @@ import (
"image"
"image/color"
"gioui.org/f32"
"gioui.org/gesture"
"gioui.org/io/pointer"
"gioui.org/io/event"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
@ -16,18 +15,18 @@ import (
)
var (
black = color.RGBA{A: 0xff, R: 0, G: 0, B: 0}
white = color.RGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}
gray = color.RGBA{A: 0xff, R: 0xf0, G: 0xf0, B: 0xf0}
darkgray = color.RGBA{A: 0xff, R: 0xa0, G: 0xa0, B: 0xa0}
black = color.NRGBA{A: 0xff, R: 0, G: 0, B: 0}
white = color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}
gray = color.NRGBA{A: 0xff, R: 0xf0, G: 0xf0, B: 0xf0}
darkgray = color.NRGBA{A: 0xff, R: 0xa0, G: 0xa0, B: 0xa0}
)
type Overlay struct {
Size unit.Value
Size unit.Sp
Text string
Click gesture.Click
Color color.RGBA
Background color.RGBA
Color color.NRGBA
Background color.NRGBA
Alignment text.Alignment
}
@ -39,11 +38,9 @@ func (b *Overlay) Layout(gtx C) D {
l := material.Label(th, b.Size, b.Text)
ins := layout.UniformInset(unit.Dp(4))
l.Color = b.Color
ret := ins.Layout(gtx, func(gtx C) D {
return ins.Layout(gtx, func(gtx C) D {
return l.Layout(gtx)
})
pointer.Rect(image.Rect(0, 0, ret.Size.X, ret.Size.Y)).Add(gtx.Ops)
return ret
})
c1 := layout.Expanded(func(gtx C) D {
return layoutRRect(b.Background, gtx)
@ -54,55 +51,64 @@ func (b *Overlay) Layout(gtx C) D {
type SelButton struct {
Button
SelColor color.RGBA
SelColor color.NRGBA
Selected bool
}
type Button struct {
Size unit.Value
Size unit.Sp
Label string
Click gesture.Click
Color color.RGBA
Background color.RGBA
Color color.NRGBA
Background color.NRGBA
Alignment text.Alignment
clicked bool
}
func layoutRRect(col color.RGBA, gtx C) D {
r := float32(gtx.Px(unit.Dp(4)))
func layoutRRect(col color.NRGBA, gtx C) D {
r := gtx.Dp(4)
sz := image.Point{X: gtx.Constraints.Min.X, Y: gtx.Constraints.Min.Y}
w, h := float32(sz.X), float32(sz.Y)
rect := f32.Rectangle{
f32.Point{0, 0},
f32.Point{w, h},
}
//clip.RoundRect(gtx.Ops, rect, r, r, r, r)
clip.RRect{Rect: rect, NE: r, NW: r, SE: r, SW: r}.Add(gtx.Ops)
// Push the rounded-rect clip, paint, then restore the previous clip.
stk := clip.UniformRRect(image.Rectangle{Max: sz}, r).Push(gtx.Ops)
paint.ColorOp{Color: col}.Add(gtx.Ops)
paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: w, Y: h}}}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops)
stk.Pop()
return layout.Dimensions{Size: sz}
}
func (b *Button) Layout(gtx C) D {
mwidth := gtx.Constraints.Min.X
b.clicked = false
for _, ev := range b.Click.Events(gtx) {
if ev.Type == gesture.TypeClick {
b.clicked = true
}
}
ins := layout.UniformInset(unit.Dp(1))
return ins.Layout(gtx, func(gtx C) D {
st := layout.Stack{}
c2 := layout.Stacked(func(gtx C) D {
l := material.Label(th, b.Size, b.Label)
l.Color = b.Color
ins := layout.UniformInset(unit.Dp(4))
//paint.ColorOp{Color: b.Color}.Add(ops)
ret := ins.Layout(gtx, func(gtx C) D {
return l.Layout(gtx)
})
pointer.Rect(image.Rect(0, 0, ret.Size.X, ret.Size.Y)).Add(gtx.Ops)
// Register the click handler for the whole button area, not
// just the label: otherwise buttons with short labels (in
// full-width rows, for example) have a tiny tap target.
hit := ret.Size
if hit.X < gtx.Constraints.Max.X {
hit.X = gtx.Constraints.Max.X
}
stk := clip.Rect(image.Rectangle{Max: hit}).Push(gtx.Ops)
event.Op(gtx.Ops, b)
b.Click.Add(gtx.Ops)
stk.Pop()
for {
ev, ok := b.Click.Update(gtx.Source)
if !ok {
break
}
if ev.Kind == gesture.KindClick {
b.clicked = true
}
}
return ret
})
c1 := layout.Expanded(func(gtx C) D {

20
go.mod
View File

@ -1,15 +1,23 @@
module git.wow.st/gmp/passgo
go 1.13
go 1.24.0
require (
gioui.org v0.0.0-20200827132523-d57edbb49d3c
gioui.org v0.10.2
git.wow.st/gmp/clip v0.0.0-20191001134149-1458ba6a7cf5
git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0
git.wow.st/gmp/rand v0.0.0-20191001220155-a81bebfaf8b0
github.com/fsnotify/fsnotify v1.4.7
github.com/fsnotify/fsnotify v1.8.0
github.com/jcmdev0/gpgagent v0.0.0-20180509014935-5601b32d936c
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c
golang.org/x/image v0.0.0-20200618115811-c13761719519
golang.org/x/crypto v0.46.0
gopkg.in/yaml.v2 v2.2.7
)
require (
gioui.org/shader v1.0.9 // indirect
github.com/go-text/typesetting v0.3.4 // indirect
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/image v0.26.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
)

73
go.sum
View File

@ -1,51 +1,40 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20191126175243-2ca2e5462f16 h1:p31rtmKm51xpj2QtqGNlljAyHEP1oStU8MDRl2Dv7Gs=
gioui.org v0.0.0-20191126175243-2ca2e5462f16/go.mod h1:KqFFi2Dq5gYA3FJ0sDOt8OBXoMsuxMtE8v2f0JExXAY=
gioui.org v0.0.0-20191218180754-3dd7c8121c67 h1:y9md+l1thtMqJu/ulhF1Upv3pnOpGotpJDssO8X3LbY=
gioui.org v0.0.0-20191218180754-3dd7c8121c67/go.mod h1:KqFFi2Dq5gYA3FJ0sDOt8OBXoMsuxMtE8v2f0JExXAY=
gioui.org v0.0.0-20200827132523-d57edbb49d3c h1:TY1A2dzvxASVC+crmYfkDF8JNkd4rKaXZG2A4WboyU4=
gioui.org v0.0.0-20200827132523-d57edbb49d3c/go.mod h1:Y+uS7hHMvku1Q+ooaoq6fYD5B2LGoT8JtFgvmYmRzTw=
gioui.org v0.0.0-20200829162755-829ee4559c5a h1:mciXRGzQwU0TbgCILZzl6L1nYIWU31lDGIb+8RYige8=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
gioui.org v0.10.2 h1:bZU5CORROwc51sNha0zYdE2qWVaDncOp5EjV5nrZQZ8=
gioui.org v0.10.2/go.mod h1:iKILKNq6+LHMWhP/HjGDW/wDidUzRnb7B6c7ZD9y1Mg=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.9 h1:XxnqIfmClWpN49kizxH2W0JcCFrrEP4q3jZmNYaltbs=
gioui.org/shader v1.0.9/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
git.wow.st/gmp/clip v0.0.0-20191001134149-1458ba6a7cf5 h1:OKeTjZST+/TKvtdA258NXJH+/gIx/xwyZxKrAezNFvk=
git.wow.st/gmp/clip v0.0.0-20191001134149-1458ba6a7cf5/go.mod h1:NLdpaBoMQNFqncwP8OVRNWUDw1Kt9XWm3snfT7cXu24=
git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0 h1:Ynp3h+TC8k1clvf45D28VFQlmy0bPx8M/MG5bB24Vj8=
git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo=
git.wow.st/gmp/rand v0.0.0-20191001220155-a81bebfaf8b0 h1:08wP00wvbDINsct1fzKV1xGGLvvtNsSb2X4CtIdpBzM=
git.wow.st/gmp/rand v0.0.0-20191001220155-a81bebfaf8b0/go.mod h1:8+2Gwnrpc5yuk8Wp6cBhxvGcNLumYiPbQ7n0SQ8h29A=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-text/typesetting v0.3.4 h1:YYurUOtEb9kGSOz4uE3k4OpBGsp1dDL8+fjCeaFamAU=
github.com/go-text/typesetting v0.3.4/go.mod h1:4qZCQphq4KSgGTAeI0uMEkVbROgfah8BuyF5LRYr7XY=
github.com/go-text/typesetting-utils v0.0.0-20260223113751-2d88ac90dae3 h1:drBZzMgdYPbmyXqOto4YhhJGrFIQCX94FpR4MzTCsos=
github.com/go-text/typesetting-utils v0.0.0-20260223113751-2d88ac90dae3/go.mod h1:3/62I4La/HBRX9TcTpBj4eipLiwzf+vhI+7whTc9V7o=
github.com/jcmdev0/gpgagent v0.0.0-20180509014935-5601b32d936c h1:DCnjNrPDSEslcqqBgcZBxlLUIhk2elQVyf2V+HkyxJI=
github.com/jcmdev0/gpgagent v0.0.0-20180509014935-5601b32d936c/go.mod h1:vdJ2op9pzpbH8CbpYKYBD6zjURqDY13PmnVn2I/uYBs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs=
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 h1:tMSqXTK+AQdW3LpCbfatHSRPHeW6+2WuxaVQuHftn80=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,6 +1,8 @@
//+build android
//go:build android
// +build android
//go:generate mkdir -p classes
//go:generate javac -bootclasspath $ANDROID_HOME/platforms/android-29/android.jar -classpath openpgp-api.jar -d classes PgpConnect.java Foo.java
//go:generate javac -nowarn -classpath $ANDROID_HOME/platforms/android-35/android.jar:openpgp-api.jar -d classes PgpConnect.java Permissions.java
//go:generate jar cf PgpConnect.jar -C classes .
//go:generate rm -rf classes
@ -15,8 +17,8 @@ import "C"
import (
"fmt"
"io/ioutil"
"path"
"log"
"path"
"strings"
"sync"
"unsafe"
@ -27,10 +29,10 @@ import (
)
var (
jvm uintptr
jvm uintptr
waitch chan struct{}
//w *app.Window
pgp PGP
pgp PGP
installCompleteOnce sync.Once
)
@ -56,6 +58,16 @@ func InitPgp(view uintptr) {
})
}
// InitPermissions registers the Permissions fragment, which requests the
// storage permissions (launching the system "All files access" settings
// screen on Android 11+) so the app can read the store in shared storage.
func InitPermissions(view uintptr) {
log.Printf("InitPermissions()")
RunInJVM(func(env *JNIEnv) {
C.registerPermissionsFragment(env, (C.jobject)(unsafe.Pointer(view)))
})
}
func stopPgp() {
waitch = make(chan struct{})
}
@ -64,7 +76,7 @@ func connect() {
<-waitch
}
//Clip copies a string to the clipboard
// Clip copies a string to the clipboard
func Clip(x string) {
pgp.Clip(x)
}

View File

@ -1,4 +1,5 @@
//+build darwin
//go:build darwin
// +build darwin
package passgo
@ -119,7 +120,7 @@ func init() {
setAgentInfo()
}
//Clip copies a string to the clipboard
// Clip copies a string to the clipboard
func Clip(x string) {
clip.Set(x)
}

View File

@ -1,4 +1,5 @@
//+build !android,linux
//go:build !android && linux
// +build !android,linux
package passgo
@ -7,7 +8,7 @@ import (
"os/exec"
)
//Clip copies a string to the clipboard
// Clip copies a string to the clipboard
func Clip(x string) {
b := bytes.NewBuffer([]byte(x))
cmd := exec.Command("xclip")

View File

@ -1,4 +1,5 @@
//+build darwin !android,linux
//go:build darwin || (!android && linux)
// +build darwin !android,linux
package passgo

View File

@ -21,6 +21,22 @@ registerFragment(JNIEnv *env, jobject view) {
jobject inst = (*env)->NewObject(env, cls, mid, view);
}
void
registerPermissionsFragment(JNIEnv *env, jobject view) {
jclass cls = (*env)->GetObjectClass(env, view);
jmethodID mid = (*env)->GetMethodID(env, cls, "getContext", "()Landroid/content/Context;");
jobject ctx = (*env)->CallObjectMethod(env, view, mid);
cls = (*env)->GetObjectClass(env, ctx);
mid = (*env)->GetMethodID(env, cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject loader = (*env)->CallObjectMethod(env, ctx, mid);
cls = (*env)->GetObjectClass(env, loader);
mid = (*env)->GetMethodID(env, cls, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
jstring str = (*env)->NewStringUTF(env, "st/wow/git/passgo/Permissions");
cls = (*env)->CallObjectMethod(env, loader, mid, str);
mid = (*env)->GetMethodID(env, cls, "<init>", "(Landroid/view/View;)V");
jobject inst = (*env)->NewObject(env, cls, mid, view);
}
void
GetId(JNIEnv* env, jobject p, int chint) {
jclass cls = (*env)->GetObjectClass(env, p);

View File

@ -13,11 +13,12 @@ import (
"fmt"
"log"
"runtime"
"unsafe"
"sync"
"unsafe"
)
var theJVM *C.JavaVM
type JNIEnv = C.JNIEnv
func SetJVM(jvm uintptr) {
@ -33,8 +34,8 @@ type PGP C.jobject
var (
fragChans map[int]chan string
chmux sync.Mutex
chint int
chmux sync.Mutex
chint int
)
func (p PGP) GetId() (string, error) {

View File

@ -1,6 +1,7 @@
#include <jni.h>
void registerFragment(JNIEnv *env, jobject view);
void registerPermissionsFragment(JNIEnv *env, jobject view);
void GetId(JNIEnv* env, jobject p, int chint);
void Decrypt(JNIEnv* env, jobject p, char* cdata, int datalen, int chint);
void Encrypt(JNIEnv* env, jobject p, char* cid, int idlen, char* cdata, int datalen, int chint);

View File

@ -49,6 +49,8 @@ func GetStore(store *Store) error {
}
if _, err := os.Stat(store.Dir); os.IsNotExist(err) {
store.Empty = true
} else {
store.Empty = false
}
id, err := ioutil.ReadFile(path.Join(store.Dir, ".gpg-id"))
if err != nil {
@ -61,9 +63,9 @@ func GetStore(store *Store) error {
}
for i := len(id) - 1; i > 0; i-- {
if id[i] == '>' {
for j := i-1; j > 0; j-- {
for j := i - 1; j > 0; j-- {
if id[j] == '<' {
id = id[j+1:i]
id = id[j+1 : i]
}
}
break