Fix Android local open flow
This commit is contained in:
@@ -577,7 +577,6 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
|
|||||||
apiPolicyGroupScope: true,
|
apiPolicyGroupScope: true,
|
||||||
autofillNoticePreference: autofillNoticeAll,
|
autofillNoticePreference: autofillNoticeAll,
|
||||||
backgroundResults: make(chan backgroundActionResult, 8),
|
backgroundResults: make(chan backgroundActionResult, 8),
|
||||||
requestMasterPassFocus: true,
|
|
||||||
}
|
}
|
||||||
u.apiPolicyAllow.Value = true
|
u.apiPolicyAllow.Value = true
|
||||||
u.apiPolicyGroupScopeW.Value = true
|
u.apiPolicyGroupScopeW.Value = true
|
||||||
@@ -598,10 +597,12 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
|
|||||||
u.loadRecentVaults()
|
u.loadRecentVaults()
|
||||||
u.loadRecentRemotes()
|
u.loadRecentRemotes()
|
||||||
u.restoreStartupLifecycleTarget()
|
u.restoreStartupLifecycleTarget()
|
||||||
|
u.requestMasterPassFocus = u.hasSelectedLifecycleTarget()
|
||||||
u.loadUIPreferences()
|
u.loadUIPreferences()
|
||||||
u.loadSettings()
|
u.loadSettings()
|
||||||
u.loadSettingsFormFromPreferences()
|
u.loadSettingsFormFromPreferences()
|
||||||
u.loadSettingsDraft()
|
u.loadSettingsDraft()
|
||||||
|
u.requestMasterPassFocus = u.hasSelectedLifecycleTarget()
|
||||||
u.filter()
|
u.filter()
|
||||||
u.syncAutofillCache()
|
u.syncAutofillCache()
|
||||||
return u
|
return u
|
||||||
@@ -663,6 +664,10 @@ func shouldUsePreviewWindowSize(mode, goos string) bool {
|
|||||||
return !strings.EqualFold(strings.TrimSpace(goos), "android")
|
return !strings.EqualFold(strings.TrimSpace(goos), "android")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func supportsDesktopFilePicker(goos string) bool {
|
||||||
|
return !strings.EqualFold(strings.TrimSpace(goos), "android")
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ui) selectedAttachmentItems() []attachmentItem {
|
func (u *ui) selectedAttachmentItems() []attachmentItem {
|
||||||
item, ok := u.selectedEntry()
|
item, ok := u.selectedEntry()
|
||||||
if !ok || len(item.Attachments) == 0 {
|
if !ok || len(item.Attachments) == 0 {
|
||||||
@@ -1594,6 +1599,15 @@ func (u *ui) restoreStartupLifecycleTarget() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ui) hasSelectedLifecycleTarget() bool {
|
||||||
|
switch strings.TrimSpace(u.lifecycleMode) {
|
||||||
|
case "remote":
|
||||||
|
return strings.TrimSpace(u.remoteBaseURL.Text()) != "" && strings.TrimSpace(u.remotePath.Text()) != ""
|
||||||
|
default:
|
||||||
|
return strings.TrimSpace(u.vaultPath.Text()) != ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ui) latestRecentVault() (string, time.Time) {
|
func (u *ui) latestRecentVault() (string, time.Time) {
|
||||||
for _, path := range u.recentVaults {
|
for _, path := range u.recentVaults {
|
||||||
if strings.TrimSpace(path) == "" {
|
if strings.TrimSpace(path) == "" {
|
||||||
@@ -3699,7 +3713,10 @@ func (u *ui) syncDialogContent(gtx layout.Context) layout.Dimensions {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return selectorEditorHelp(u.theme, "Local Vault Path", "Choose the other local .kdbx file to synchronize with.", &u.syncLocalPath, &u.pickSyncLocalPath, "Choose File", false)(gtx)
|
if supportsDesktopFilePicker(runtime.GOOS) {
|
||||||
|
return selectorEditorHelp(u.theme, "Local Vault Path", "Choose the other local .kdbx file to synchronize with.", &u.syncLocalPath, &u.pickSyncLocalPath, "Choose File", false)(gtx)
|
||||||
|
}
|
||||||
|
return labeledEditorHelp(u.theme, "Local Vault Path", "Enter the shared-storage path to the other local .kdbx file to synchronize with.", &u.syncLocalPath, false)(gtx)
|
||||||
}),
|
}),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(14)}.Layout),
|
layout.Rigid(layout.Spacer{Height: unit.Dp(14)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
|
|||||||
@@ -3519,6 +3519,50 @@ func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIStartupDoesNotRequestMasterPasswordFocusWithoutSelectedTarget(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
u := newUIWithSession("phone", &session.Manager{}, statePaths{
|
||||||
|
DefaultSaveAsPath: filepath.Join(dir, "vault.kdbx"),
|
||||||
|
RecentVaultsPath: filepath.Join(dir, "recent-vaults.json"),
|
||||||
|
RecentRemotesPath: filepath.Join(dir, "recent-remotes.json"),
|
||||||
|
SettingsPath: filepath.Join(dir, "settings.json"),
|
||||||
|
UIPreferencesPath: filepath.Join(dir, "ui-prefs.json"),
|
||||||
|
AutofillCachePath: filepath.Join(dir, "autofill-cache.json"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if u.requestMasterPassFocus {
|
||||||
|
t.Fatal("requestMasterPassFocus = true without a selected startup target, want false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUIStartupRequestsMasterPasswordFocusForSelectedRecentVault(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
paths := statePaths{
|
||||||
|
DefaultSaveAsPath: filepath.Join(dir, "vault.kdbx"),
|
||||||
|
RecentVaultsPath: filepath.Join(dir, "recent-vaults.json"),
|
||||||
|
RecentRemotesPath: filepath.Join(dir, "recent-remotes.json"),
|
||||||
|
SettingsPath: filepath.Join(dir, "settings.json"),
|
||||||
|
UIPreferencesPath: filepath.Join(dir, "ui-prefs.json"),
|
||||||
|
AutofillCachePath: filepath.Join(dir, "autofill-cache.json"),
|
||||||
|
}
|
||||||
|
|
||||||
|
first := newUIWithSession("phone", &session.Manager{}, paths)
|
||||||
|
first.noteRecentVault("/tmp/demo.kdbx")
|
||||||
|
|
||||||
|
reopened := newUIWithSession("phone", &session.Manager{}, paths)
|
||||||
|
|
||||||
|
if got := reopened.vaultPath.Text(); got != "/tmp/demo.kdbx" {
|
||||||
|
t.Fatalf("vaultPath = %q, want /tmp/demo.kdbx", got)
|
||||||
|
}
|
||||||
|
if !reopened.requestMasterPassFocus {
|
||||||
|
t.Fatal("requestMasterPassFocus = false with a selected startup vault, want true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIGroupToolsDisclosureStatePersists(t *testing.T) {
|
func TestUIGroupToolsDisclosureStatePersists(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -4311,6 +4355,17 @@ func TestShouldUsePreviewWindowSizeSkipsAndroid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSupportsDesktopFilePicker(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if got := supportsDesktopFilePicker("android"); got {
|
||||||
|
t.Fatal("supportsDesktopFilePicker(android) = true, want false")
|
||||||
|
}
|
||||||
|
if got := supportsDesktopFilePicker("linux"); !got {
|
||||||
|
t.Fatal("supportsDesktopFilePicker(linux) = false, want true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEnterOnLocalLifecycleScreenDefaultsToOpenVault(t *testing.T) {
|
func TestEnterOnLocalLifecycleScreenDefaultsToOpenVault(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
+35
-6
@@ -6,6 +6,7 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
@@ -27,7 +28,7 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
|||||||
}),
|
}),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
message := "Choose a recent vault or pick a `.kdbx` file, then unlock it."
|
message := "Choose a recent vault or enter a .kdbx path, then unlock it."
|
||||||
if u.lifecycleMode == "remote" {
|
if u.lifecycleMode == "remote" {
|
||||||
message = "Connect to a remote vault, then unlock it with the KeePass master key."
|
message = "Connect to a remote vault, then unlock it with the KeePass master key."
|
||||||
}
|
}
|
||||||
@@ -182,9 +183,9 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
|||||||
selectedPath := strings.TrimSpace(u.vaultPath.Text())
|
selectedPath := strings.TrimSpace(u.vaultPath.Text())
|
||||||
switch {
|
switch {
|
||||||
case busy:
|
case busy:
|
||||||
return labeledEditorHelp(u.theme, "Vault Path", "Choose the existing .kdbx file to open.", &u.vaultPath, false)(gtx)
|
return labeledEditorHelp(u.theme, "Vault Path", localVaultPathHelp(), &u.vaultPath, false)(gtx)
|
||||||
case selectedPath == "":
|
case selectedPath == "":
|
||||||
return selectorEditorHelp(u.theme, "Vault Path", "Choose the existing .kdbx file to open.", &u.vaultPath, &u.pickVaultPath, "Choose File", false)(gtx)
|
return localPathSelector(u.theme, &u.vaultPath, &u.pickVaultPath)(gtx)
|
||||||
default:
|
default:
|
||||||
lastGroup := u.recentVaultGroup(selectedPath)
|
lastGroup := u.recentVaultGroup(selectedPath)
|
||||||
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
return compactCard(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||||
@@ -242,9 +243,9 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
|
|||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
if busy {
|
if busy {
|
||||||
return labeledEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, false)(gtx)
|
return labeledEditorHelp(u.theme, "Key File", keyFileHelp(), &u.keyFilePath, false)(gtx)
|
||||||
}
|
}
|
||||||
return selectorEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, &u.pickKeyFile, "Choose File", false)(gtx)
|
return keyFileSelector(u.theme, &u.keyFilePath, &u.pickKeyFile)(gtx)
|
||||||
}),
|
}),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
if busy {
|
if busy {
|
||||||
@@ -1149,6 +1150,34 @@ func labeledEditorHelp(th *material.Theme, label, help string, editor *widget.Ed
|
|||||||
return labeledEditorHelpFocus(th, defaultAccessibilityPreferences(), label, help, editor, sensitive, false)
|
return labeledEditorHelpFocus(th, defaultAccessibilityPreferences(), label, help, editor, sensitive, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func localVaultPathHelp() string {
|
||||||
|
if supportsDesktopFilePicker(runtime.GOOS) {
|
||||||
|
return "Choose the existing .kdbx file to open."
|
||||||
|
}
|
||||||
|
return "Enter the shared-storage path to the existing .kdbx file, for example /sdcard/Download/vault.kdbx."
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyFileHelp() string {
|
||||||
|
if supportsDesktopFilePicker(runtime.GOOS) {
|
||||||
|
return "Optional path to a KeePass-compatible key file."
|
||||||
|
}
|
||||||
|
return "Optional shared-storage path to a KeePass-compatible key file."
|
||||||
|
}
|
||||||
|
|
||||||
|
func localPathSelector(th *material.Theme, editor *widget.Editor, click *widget.Clickable) layout.Widget {
|
||||||
|
if supportsDesktopFilePicker(runtime.GOOS) {
|
||||||
|
return selectorEditorHelp(th, "Vault Path", localVaultPathHelp(), editor, click, "Choose File", false)
|
||||||
|
}
|
||||||
|
return labeledEditorHelp(th, "Vault Path", localVaultPathHelp(), editor, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyFileSelector(th *material.Theme, editor *widget.Editor, click *widget.Clickable) layout.Widget {
|
||||||
|
if supportsDesktopFilePicker(runtime.GOOS) {
|
||||||
|
return selectorEditorHelp(th, "Key File", keyFileHelp(), editor, click, "Choose File", false)
|
||||||
|
}
|
||||||
|
return labeledEditorHelp(th, "Key File", keyFileHelp(), editor, false)
|
||||||
|
}
|
||||||
|
|
||||||
func labeledEditorHelpFocus(th *material.Theme, prefs accessibilityPreferences, label, help string, editor *widget.Editor, sensitive bool, focused bool) layout.Widget {
|
func labeledEditorHelpFocus(th *material.Theme, prefs accessibilityPreferences, label, help string, editor *widget.Editor, sensitive bool, focused bool) layout.Widget {
|
||||||
return func(gtx layout.Context) layout.Dimensions {
|
return func(gtx layout.Context) layout.Dimensions {
|
||||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||||
@@ -1252,7 +1281,7 @@ func (u *ui) unlockPanel(gtx layout.Context) layout.Dimensions {
|
|||||||
return u.masterPasswordField(gtx, "Used alone or together with a key file to unlock the vault.")
|
return u.masterPasswordField(gtx, "Used alone or together with a key file to unlock the vault.")
|
||||||
}),
|
}),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout),
|
||||||
layout.Rigid(selectorEditorHelp(u.theme, "Key File", "Optional path to a KeePass-compatible key file.", &u.keyFilePath, &u.pickKeyFile, "Choose File", false)),
|
layout.Rigid(keyFileSelector(u.theme, &u.keyFilePath, &u.pickKeyFile)),
|
||||||
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
|
||||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
return tonedButton(gtx, u.theme, &u.unlockVault, "Unlock")
|
return tonedButton(gtx, u.theme, &u.unlockVault, "Unlock")
|
||||||
|
|||||||
Reference in New Issue
Block a user