Add Android shared vault import flow

This commit is contained in:
Joe Julian
2026-04-06 16:48:04 -07:00
parent af44810a1c
commit 05345559ee
3 changed files with 157 additions and 0 deletions
+66
View File
@@ -304,6 +304,7 @@ type ui struct {
editEntry widget.Clickable
cancelEdit widget.Clickable
pickVaultPath widget.Clickable
importSharedVault widget.Clickable
pickKeyFile widget.Clickable
pickSyncLocalPath widget.Clickable
clearVaultSelection widget.Clickable
@@ -758,6 +759,10 @@ func supportsDesktopFilePicker(goos string) bool {
return !strings.EqualFold(strings.TrimSpace(goos), "android")
}
func supportsSharedVaultImport(goos string) bool {
return strings.EqualFold(strings.TrimSpace(goos), "android")
}
func (u *ui) selectedAttachmentItems() []attachmentItem {
item, ok := u.selectedEntry()
if !ok || len(item.Attachments) == 0 {
@@ -1418,6 +1423,29 @@ func (u *ui) startChooseSyncLocalSourceAction() {
})
}
func (u *ui) startImportSharedVaultAction() {
if !supportsSharedVaultImport(runtime.GOOS) || u.fileExplorer == nil {
return
}
u.runBackgroundAction("import shared vault", func() (func() error, error) {
file, err := u.fileExplorer.ChooseFile(".kdbx")
if err != nil {
if errors.Is(err, explorer.ErrUserDecline) {
return func() error { return nil }, nil
}
return nil, err
}
defer file.Close()
content, err := io.ReadAll(file)
if err != nil {
return nil, err
}
return func() error {
return u.importSharedVaultBytesAction("shared-vault.kdbx", content)
}, nil
})
}
func (u *ui) advancedSyncToAction() error {
switch u.syncSourceMode {
case syncSourceRemote:
@@ -1452,6 +1480,38 @@ func (u *ui) saveAsTargetPath() string {
return u.defaultSaveAsPath
}
func (u *ui) importedVaultDestination(name string) string {
baseTarget := u.saveAsTargetPath()
baseDir := filepath.Dir(baseTarget)
baseName := filepath.Base(strings.TrimSpace(name))
switch {
case baseName == "" || baseName == "." || baseName == string(filepath.Separator):
return baseTarget
case strings.HasSuffix(strings.ToLower(baseName), ".kdbx"):
return filepath.Join(baseDir, baseName)
default:
return baseTarget
}
}
func (u *ui) importSharedVaultBytesAction(name string, content []byte) error {
target := u.importedVaultDestination(name)
if err := os.MkdirAll(filepath.Dir(target), 0o700); err != nil {
return err
}
if err := os.WriteFile(target, append([]byte(nil), content...), 0o600); err != nil {
return err
}
u.lifecycleMode = "local"
u.vaultPath.SetText(target)
u.noteRecentVault(target)
u.state.ErrorMessage = ""
u.state.StatusMessage = ""
u.requestMasterPassFocus = true
u.filter()
return nil
}
func (u *ui) noteRecentVault(path string) {
path = strings.TrimSpace(path)
if path == "" {
@@ -3490,6 +3550,12 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
}
u.runAction("choose vault path", func() error { return u.chooseExistingFileAction(&u.vaultPath) })
}
for u.importSharedVault.Clicked(gtx) {
if u.lifecycleBusy() {
continue
}
u.startImportSharedVaultAction()
}
for u.pickKeyFile.Clicked(gtx) {
if u.lifecycleBusy() {
continue