isundil 7 лет назад
Родитель
Сommit
5550602716
29 измененных файлов с 544 добавлено и 257 удалено
  1. 6 1
      .idea/misc.xml
  2. 60 60
      app/src/main/java/info/knacki/pass/git/HttpGitProtocol.java
  3. 2 2
      app/src/main/java/info/knacki/pass/input/InputService.java
  4. 8 3
      app/src/main/java/info/knacki/pass/io/FileInterfaceFactory.java
  5. 107 0
      app/src/main/java/info/knacki/pass/io/FileMigrator.java
  6. 4 0
      app/src/main/java/info/knacki/pass/io/FileUtils.java
  7. 7 7
      app/src/main/java/info/knacki/pass/io/GPGFileInterface.java
  8. 23 23
      app/src/main/java/info/knacki/pass/io/GPGUtil.java
  9. 15 2
      app/src/main/java/info/knacki/pass/io/IFileInterface.java
  10. 1 1
      app/src/main/java/info/knacki/pass/io/OnErrorListener.java
  11. 1 1
      app/src/main/java/info/knacki/pass/io/OnResponseListener.java
  12. 1 1
      app/src/main/java/info/knacki/pass/io/OnStreamResponseListener.java
  13. 32 24
      app/src/main/java/info/knacki/pass/io/PasswordFileInterface.java
  14. 4 4
      app/src/main/java/info/knacki/pass/io/RawFileInterface.java
  15. 12 0
      app/src/main/java/info/knacki/pass/settings/SettingsManager.java
  16. 53 5
      app/src/main/java/info/knacki/pass/settings/ui/SettingsActivity.java
  17. 22 15
      app/src/main/java/info/knacki/pass/ui/EditPasswordActivity.java
  18. 30 30
      app/src/main/java/info/knacki/pass/ui/GitPullActivity.java
  19. 45 35
      app/src/main/java/info/knacki/pass/ui/MainActivity.java
  20. 5 0
      app/src/main/java/info/knacki/pass/ui/alertPrompt/AlertPrompt.java
  21. 5 0
      app/src/main/java/info/knacki/pass/ui/alertPrompt/views/TextView.java
  22. 9 3
      app/src/main/java/info/knacki/pass/ui/passwordList/EditablePasswordListView.java
  23. 20 19
      app/src/main/java/info/knacki/pass/ui/passwordPicker/FingerprintPicker.java
  24. 48 20
      app/src/main/java/info/knacki/pass/ui/passwordPicker/PasswordPicker.java
  25. 7 0
      app/src/main/res/values-fr/lang.xml
  26. 7 0
      app/src/main/res/values/lang.xml
  27. 1 0
      app/src/main/res/values/strings.xml
  28. 6 0
      app/src/main/res/xml/pref_enc_password.xml
  29. 3 1
      app/src/main/res/xml/pref_encryption.xml

+ 6 - 1
.idea/misc.xml

@@ -29,5 +29,10 @@
       </value>
     </option>
   </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
 </project>

+ 60 - 60
app/src/main/java/info/knacki/pass/git/HttpGitProtocol.java

@@ -68,7 +68,7 @@ class HttpGitProtocol implements GitInterface {
         }
 
         protected void ManageResponse(byte[] resp) {
-            fResp.onResponse(resp);
+            fResp.OnResponse(resp);
         }
 
         @Override
@@ -109,7 +109,7 @@ class HttpGitProtocol implements GitInterface {
             }
             catch (IOException e) {
                 log.log(Level.WARNING, e.getMessage(), e);
-                fResp.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
+                fResp.OnError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
             }
             return 0;
         }
@@ -149,7 +149,7 @@ class HttpGitProtocol implements GitInterface {
         }
 
         protected void ManageResponse(byte[] resp) {
-            fResp.onResponse(resp);
+            fResp.OnResponse(resp);
         }
 
         @Override
@@ -164,7 +164,7 @@ class HttpGitProtocol implements GitInterface {
                 if (fConfig.HasAuthentification()) {
                     httpClient.setRequestProperty("Authorization", "basic " +Base64.encodeToString("fConfig.GetUser():fConfig.GetPassword()".getBytes(), Base64.NO_WRAP));
                 }
-                fOnReadyWrite.onResponse(httpClient.getOutputStream());
+                fOnReadyWrite.OnResponse(httpClient.getOutputStream());
                 InputStream in = GetInputFilter(httpClient);
                 ArrayList<byte[]> fullBuffer = new ArrayList<>();
                 int totalRead = 0;
@@ -194,7 +194,7 @@ class HttpGitProtocol implements GitInterface {
             }
             catch (IOException e) {
                 log.log(Level.WARNING, e.getMessage(), e);
-                fResp.onError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
+                fResp.OnError(e.getClass().getSimpleName() +": " +e.getMessage(), e);
             }
             return 0;
         }
@@ -234,13 +234,13 @@ class HttpGitProtocol implements GitInterface {
             super(url, config);
             SetResultHandler(new OnResponseListener<byte[]>() {
                 @Override
-                public void onResponse(byte[] result) {
-                    StringDownloaderTask.this.onResp.onResponse(new String(result, Charset.defaultCharset()));
+                public void OnResponse(byte[] result) {
+                    StringDownloaderTask.this.onResp.OnResponse(new String(result, Charset.defaultCharset()));
                 }
 
                 @Override
-                public void onError(String msg, Throwable e) {
-                    StringDownloaderTask.this.onResp.onError(msg, e);
+                public void OnError(String msg, Throwable e) {
+                    StringDownloaderTask.this.onResp.OnError(msg, e);
                 }
             });
             onResp = resultHandler;
@@ -261,14 +261,14 @@ class HttpGitProtocol implements GitInterface {
 
     public void GetRefs(final OnResponseListener<GitRef[]> callback) {
         if (fRefsCache != null) {
-            callback.onResponse(fRefsCache);
+            callback.OnResponse(fRefsCache);
             return;
         }
         try {
             URL url = new URL(fConfig.GetUrl() + "/info/REFS");
             protoGet(url, new OnResponseListener<String>() {
                 @Override
-                public void onResponse(String result) {
+                public void OnResponse(String result) {
                     String [] refStrings = result.split("\n");
                     fRefsCache = new GitRef[refStrings.length];
                     for (int i =0; i < refStrings.length; ++i) {
@@ -280,29 +280,29 @@ class HttpGitProtocol implements GitInterface {
                             }
                         }
                     }
-                    callback.onResponse(fRefsCache);
+                    callback.OnResponse(fRefsCache);
                 }
 
                 @Override
-                public void onError(String msg, Throwable e) {
-                    callback.onError(msg, e);
+                public void OnError(String msg, Throwable e) {
+                    callback.OnError(msg, e);
                 }
             });
         } catch (MalformedURLException e) {
-            callback.onError(e.getMessage(), e);
+            callback.OnError(e.getMessage(), e);
         }
     }
 
     public void FetchCommit(final GitRef ref, final OnResponseListener<GitCommit> response) {
         PullHash(ref.GetHash(), new OnResponseListener<byte[]>() {
             @Override
-            public void onResponse(byte[] result) {
-                response.onResponse(new GitCommit(ref.GetHash(), new String(result, Charset.defaultCharset())));
+            public void OnResponse(byte[] result) {
+                response.OnResponse(new GitCommit(ref.GetHash(), new String(result, Charset.defaultCharset())));
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
-                response.onError(msg, e);
+            public void OnError(String msg, Throwable e) {
+                response.OnError(msg, e);
             }
         });
     }
@@ -323,22 +323,22 @@ class HttpGitProtocol implements GitInterface {
             }
 
             @Override
-            public void onResponse(byte[] result) {
+            public void OnResponse(byte[] result) {
                 FillTree(fCurrentTree, result);
                 fCurrentTree = fRoot.FindNextUninitialiazedTree();
                 if (fCurrentTree != null)
                     PullHash(fCurrentTree.Initialize().GetHash());
                 else
-                    fResponseListener.onResponse(fRoot);
+                    fResponseListener.OnResponse(fRoot);
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
-                fResponseListener.onError(msg, e);
+            public void OnError(String msg, Throwable e) {
+                fResponseListener.OnError(msg, e);
             }
 
             private void PullHash(String hash) {
-                fResponseListener.onMsg("Reading tree " +hash);
+                fResponseListener.OnMsg("Reading tree " +hash);
                 HttpGitProtocol.this.PullHash(hash, this);
             }
 
@@ -389,54 +389,54 @@ class HttpGitProtocol implements GitInterface {
     public void FetchHead(final OnStreamResponseListener<GitCommit> response) {
         GetRefs(new OnResponseListener<GitRef[]>() {
             @Override
-            public void onResponse(final GitRef[] result) {
+            public void OnResponse(final GitRef[] result) {
                 GitRef myref = null;
-                response.onMsg("Found refs: ");
+                response.OnMsg("Found refs: ");
                 for (GitRef ref: result) {
-                    response.onMsg("\t> "+ref.GetBranch());
+                    response.OnMsg("\t> "+ref.GetBranch());
                     if (ref.GetBranch().equals(fConfig.GetBranch()))
                         myref = ref;
                 }
                 if (myref != null) {
-                    response.onMsg("Checking out branch " +myref.GetBranchName() +" revision " +myref.GetHash());
+                    response.OnMsg("Checking out branch " +myref.GetBranchName() +" revision " +myref.GetHash());
                     FetchCommit(myref, new OnResponseListener<GitCommit>() {
                         @Override
-                        public void onResponse(final GitCommit result) {
-                            response.onMsg("Finished read commit");
-                            response.onMsg(result.GetMessage());
-                            response.onMsg("Reading tree #" +GitSha1.BytesToString(result.GetTreeHash()));
+                        public void OnResponse(final GitCommit result) {
+                            response.OnMsg("Finished read commit");
+                            response.OnMsg(result.GetMessage());
+                            response.OnMsg("Reading tree #" +GitSha1.BytesToString(result.GetTreeHash()));
                             FetchTree(result, new OnStreamResponseListener<GitObject.GitTree>() {
                                 @Override
-                                public void onMsg(String message) {
-                                    response.onMsg(message);
+                                public void OnMsg(String message) {
+                                    response.OnMsg(message);
                                 }
 
                                 @Override
-                                public void onResponse(GitObject.GitTree tree) {
-                                    response.onMsg("Finished reading tree");
-                                    response.onResponse(result);
+                                public void OnResponse(GitObject.GitTree tree) {
+                                    response.OnMsg("Finished reading tree");
+                                    response.OnResponse(result);
                                 }
 
                                 @Override
-                                public void onError(String msg, Throwable e) {
-                                    response.onError(msg, e);
+                                public void OnError(String msg, Throwable e) {
+                                    response.OnError(msg, e);
                                 }
                             });
                         }
 
                         @Override
-                        public void onError(String msg, Throwable e) {
-                            response.onError(msg, e);
+                        public void OnError(String msg, Throwable e) {
+                            response.OnError(msg, e);
                         }
                     });
                 } else {
-                    response.onError("Branch " +fConfig.GetBranch() + " not found on remote", null);
+                    response.OnError("Branch " +fConfig.GetBranch() + " not found on remote", null);
                 }
             }
 
             @Override
-            public void onError(final String msg, final Throwable e) {
-                response.onError(msg, e);
+            public void OnError(final String msg, final Throwable e) {
+                response.OnError(msg, e);
             }
         });
     }
@@ -447,7 +447,7 @@ class HttpGitProtocol implements GitInterface {
             url = new URL(fConfig.GetUrl() + "/objects/" + hash.substring(0, 2) + "/" + hash.substring(2));
         }
         catch (MalformedURLException e) {
-            response.onError(e.getClass().getName() +": " +e.getMessage(), e);
+            response.OnError(e.getClass().getName() +": " +e.getMessage(), e);
             return;
         }
         protoInflateGet(url, response);
@@ -474,7 +474,7 @@ class HttpGitProtocol implements GitInterface {
     public void PushBlobs(final GitCommit.Builder commit, final OnStreamResponseListener<Void> response) {
         GetRefs(new OnResponseListener<GitRef[]>() {
             @Override
-            public void onResponse(final GitRef[] result) {
+            public void OnResponse(final GitRef[] result) {
                 GitRef myref = null;
                 for (GitRef ref: result) {
                     if (ref.GetBranch().equals(fConfig.GetBranch()))
@@ -482,59 +482,59 @@ class HttpGitProtocol implements GitInterface {
                 }
                 if (myref != null) {
                     final GitRef finalRef = myref;
-                    response.onMsg("Pushing over " +myref.GetBranch() +" revision " +myref.GetHash());
+                    response.OnMsg("Pushing over " +myref.GetBranch() +" revision " +myref.GetHash());
                     try {
                         HashMap<String, String> headers = new HashMap<>();
                         headers.put("Content-Type", "application/x-git-receive-pack-request");
                         ProtoPost(new URL(fConfig.GetUrl() +"/git-receive-pack"), headers, new OnResponseListener<byte[]>() {
                             @Override
-                            public void onResponse(byte[] result) {
+                            public void OnResponse(byte[] result) {
                                 Logger.getAnonymousLogger().severe("git-receive-pack " +new String(result, Charset.defaultCharset()));
-                                response.onResponse(null);
+                                response.OnResponse(null);
                             }
 
                             @Override
-                            public void onError(String msg, Throwable e) {
+                            public void OnError(String msg, Throwable e) {
                                 Logger.getAnonymousLogger().severe("git-receive-pack ERROR");
-                                response.onError(msg, e);
+                                response.OnError(msg, e);
                             }
                         }, new OnResponseListener<OutputStream>() {
                             @Override
-                            public void onResponse(OutputStream result) {
+                            public void OnResponse(OutputStream result) {
                                 byte[] msgLine = (finalRef.GetHash() + " " + GitSha1.BytesToString(GitSha1.getRawSha1OfPackable(commit.Build())) +" " +finalRef.GetBranch()).getBytes();
                                 try {
                                     result.write(String.format(Locale.US, "%04X", msgLine.length +4).getBytes());
                                     result.write(msgLine);
                                     result.write("0000".getBytes());
                                     if (!makePack(commit.PreparePack(), result)) {
-                                        onError("Pack error", null);
+                                        OnError("Pack error", null);
                                         return;
                                     }
                                     result.close();
                                 }
                                 catch (IOException e) {
                                     log.log(Level.SEVERE, "Cannot git-upload-pack: " +e.getMessage(), e);
-                                    onError(e.getMessage(), e);
+                                    OnError(e.getMessage(), e);
                                 }
                             }
 
                             @Override
-                            public void onError(String msg, Throwable e) {
-                                response.onError(msg, e);
+                            public void OnError(String msg, Throwable e) {
+                                response.OnError(msg, e);
                             }
                         });
                     }
                     catch (IOException e) {
-                        response.onError(e.getMessage(), e);
+                        response.OnError(e.getMessage(), e);
                     }
                 } else {
-                    response.onError("Branch " +fConfig.GetBranch() + " not found on remote for pushing", null);
+                    response.OnError("Branch " +fConfig.GetBranch() + " not found on remote for pushing", null);
                 }
             }
 
             @Override
-            public void onError(final String msg, final Throwable e) {
-                response.onError(msg, e);
+            public void OnError(final String msg, final Throwable e) {
+                response.OnError(msg, e);
             }
         });
     }

+ 2 - 2
app/src/main/java/info/knacki/pass/input/InputService.java

@@ -60,14 +60,14 @@ public class InputService extends InputMethodService implements PasswordClickLis
     public void OnPasswordClicked(File f) {
         FileInterfaceFactory.GetFileInterface(this, PasswordPickerFactory.GetPasswordPicker(this, fInputView), f).ReadFile(new OnResponseListener<String>() {
             @Override
-            public void onResponse(String passwordContent) {
+            public void OnResponse(String passwordContent) {
                 for (char i: passwordContent.toCharArray()) {
                     sendKeyChar(i);
                 }
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
+            public void OnError(String msg, Throwable e) {
                 Toast.makeText(InputService.this, "Error: " + msg, Toast.LENGTH_LONG).show();
                 log.log(Level.SEVERE, msg, e);
             }

+ 8 - 3
app/src/main/java/info/knacki/pass/io/FileInterfaceFactory.java

@@ -4,16 +4,21 @@ import android.content.Context;
 import android.os.Build;
 
 import java.io.File;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.logging.Logger;
 
 import info.knacki.pass.settings.SettingsManager;
 
 public class FileInterfaceFactory {
     public interface OnPasswordEnteredListener extends OnErrorListener {
-        boolean onResponse(String password);
+        boolean OnResponse(String password);
     }
 
     public interface PasswordGetter {
-        void GetPassword(OnPasswordEnteredListener onPassword);
+        PasswordGetter GetPassword(OnPasswordEnteredListener onPassword);
+        PasswordGetter SetFile(File file);
     }
 
     public interface ChangePasswordGetter extends PasswordGetter {
@@ -31,7 +36,7 @@ public class FileInterfaceFactory {
 
     public static IFileInterface GetFileInterface(Context ctx, PasswordGetter passInput, File f) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && f.getName().endsWith(PASSWORD_SUFFIX))
-            return new PasswordFileInterface(passInput, f);
+            return new PasswordFileInterface(ctx, passInput, f);
         else if (f.getName().endsWith(GPG_SUFFIX))
             return new GPGFileInterface(ctx, passInput, f);
         return new RawFileInterface(f);

+ 107 - 0
app/src/main/java/info/knacki/pass/io/FileMigrator.java

@@ -0,0 +1,107 @@
+package info.knacki.pass.io;
+
+import android.content.Context;
+
+import java.io.File;
+import java.util.ArrayDeque;
+
+public class FileMigrator {
+    public interface MigratePasswordCondition {
+        boolean isRelevantForMigration(File f);
+    }
+
+    private static void ListPasswords(File dir, MigratePasswordCondition condition, ArrayDeque<File> arr) {
+        if (!dir.exists() || !dir.isDirectory())
+            return;
+        for (File f: dir.listFiles()) {
+            if (f.isHidden())
+                continue;
+            if (f.isDirectory())
+                ListPasswords(f, condition, arr);
+            else if (condition != null && condition.isRelevantForMigration(f))
+                arr.add(f);
+        }
+    }
+
+    private static ArrayDeque<File> ListPasswords(Context ctx, MigratePasswordCondition condition) {
+        ArrayDeque<File> files = new ArrayDeque<>();
+        ListPasswords(new File(PathUtils.GetPassDir(ctx)), condition, files);
+        return files;
+    }
+
+    public static void MigratePassword(Context ctx, IFileInterface from, IFileInterface to, OnResponseListener<Void> onDone) {
+        from.ReadFile(new OnResponseListener<String>() {
+            @Override
+            public void OnResponse(String result) {
+                try {
+                    to.WriteFile(result, onDone);
+                }
+                catch (IFileInterface.InvalidPasswordException e) {
+                    OnError(e.getMessage(), e);
+                }
+            }
+
+            @Override
+            public void OnError(String msg, Throwable e) {
+                onDone.OnError(msg, e);
+            }
+        });
+    }
+
+    public static void MigratePassword(Context ctx, File from, File to, FileInterfaceFactory.PasswordGetter passInput, OnResponseListener<Void> onDone) {
+        MigratePassword(ctx, FileInterfaceFactory.GetFileInterface(ctx, passInput, from), FileInterfaceFactory.GetFileInterface(ctx, passInput, to), onDone);
+    }
+
+    public static void MigratePasswordToPassword(final Context ctx, FileInterfaceFactory.PasswordGetter passwordGetter) {
+        final ArrayDeque<String> oldPasswords = new ArrayDeque<>();
+        final ArrayDeque<File> oldPasswordFiles = ListPasswords(ctx, (file) -> file.getName().endsWith(FileInterfaceFactory.PASSWORD_SUFFIX));
+
+        final Runnable nextFile = new Runnable() {
+            private void NextFile() {
+                if (oldPasswordFiles.isEmpty())
+                    return;
+                final File currentFile = oldPasswordFiles.poll();
+                final ArrayDeque<String> currentPasswordToTry = new ArrayDeque(oldPasswords);
+                MigratePassword(ctx, currentFile, currentFile, new FileInterfaceFactory.PasswordGetter() {
+                    @Override
+                    public FileInterfaceFactory.PasswordGetter SetFile(File file) { return this; }
+
+                    @Override
+                    public FileInterfaceFactory.PasswordGetter GetPassword(FileInterfaceFactory.OnPasswordEnteredListener listener) {
+                        if (!currentPasswordToTry.isEmpty())
+                            listener.OnResponse(currentPasswordToTry.poll());
+                        else
+                            passwordGetter.SetFile(currentFile).GetPassword(new FileInterfaceFactory.OnPasswordEnteredListener() {
+                                @Override
+                                public boolean OnResponse(String pass) {
+                                    oldPasswords.add(pass);
+                                    return listener.OnResponse(pass);
+                                }
+
+                                @Override
+                                public void OnError(String what, Throwable e) {
+                                }
+                            });
+                        return this;
+                    }
+                }, new OnResponseListener<Void>() {
+                    @Override
+                    public void OnResponse(Void result) {
+                        NextFile();
+                    }
+
+                    @Override
+                    public void OnError(String msg, Throwable e) {
+                        NextFile();
+                    }
+                });
+            }
+
+            @Override
+            public void run() {
+                NextFile();
+            }
+        };
+        nextFile.run();
+    }
+}

+ 4 - 0
app/src/main/java/info/knacki/pass/io/FileUtils.java

@@ -1,11 +1,15 @@
 package info.knacki.pass.io;
 
+import android.content.Context;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
 
 public class FileUtils {
     public static void rmdir(File f) {

+ 7 - 7
app/src/main/java/info/knacki/pass/io/GPGFileInterface.java

@@ -22,7 +22,7 @@ class GPGFileInterface implements IFileInterface {
     @Override
     public void ReadFile(final OnResponseListener<String> resp) {
         if (fFile.length() == 0) {
-            resp.onResponse("");
+            resp.OnResponse("");
             return;
         }
         try {
@@ -30,17 +30,17 @@ class GPGFileInterface implements IFileInterface {
                 throw new FileNotFoundException("GPG key not set");
             GPGUtil.DecryptFile(fContext, fPasswordGetter, fFile, new OnResponseListener<byte[]>() {
                 @Override
-                public void onResponse(byte[] result) {
-                    resp.onResponse(CharsetHelper.ByteArrayToString(result));
+                public void OnResponse(byte[] result) {
+                    resp.OnResponse(CharsetHelper.ByteArrayToString(result));
                 }
 
                 @Override
-                public void onError(String msg, Throwable e) {
-                    resp.onError(msg, e);
+                public void OnError(String msg, Throwable e) {
+                    resp.OnError(msg, e);
                 }
             });
         } catch (Throwable e) {
-            resp.onError(e.getMessage(), e);
+            resp.OnError(e.getMessage(), e);
         }
     }
 
@@ -52,7 +52,7 @@ class GPGFileInterface implements IFileInterface {
             GPGUtil.CryptFile(fContext, new FileOutputStream(fFile), CharsetHelper.StringToByteArray(content), resp);
         }
         catch (Throwable e) {
-            resp.onError(e.getMessage(), e);
+            resp.OnError(e.getMessage(), e);
         }
     }
 }

+ 23 - 23
app/src/main/java/info/knacki/pass/io/GPGUtil.java

@@ -106,18 +106,18 @@ public class GPGUtil {
         PGPPrivateKeyAndPass priv = TryPassword(pgpSecKey, "");
 
         if (priv != null) {
-            resp.onResponse(priv);
+            resp.OnResponse(priv);
         } else {
             FileInterfaceFactory.OnPasswordEnteredListener onResp = new FileInterfaceFactory.OnPasswordEnteredListener() {
                 @Override
-                public boolean onResponse(String result) {
+                public boolean OnResponse(String result) {
                     if (result == null) {
-                        resp.onError("Invalid password", null);
+                        resp.OnError("Invalid password", null);
                         return false;
                     } else {
                         PGPPrivateKeyAndPass priv = TryPassword(pgpSecKey, result);
                         if (priv != null) {
-                            resp.onResponse(priv);
+                            resp.OnResponse(priv);
                             return true;
                         } else {
                             passwordGetter.GetPassword(this);
@@ -127,8 +127,8 @@ public class GPGUtil {
                 }
 
                 @Override
-                public void onError(String msg, Throwable e) {
-                    resp.onError(msg, e);
+                public void OnError(String msg, Throwable e) {
+                    resp.OnError(msg, e);
                 }
             };
             passwordGetter.GetPassword(onResp);
@@ -205,7 +205,7 @@ public class GPGUtil {
         try {
             InputStream ss = getFirstLiteralDataInputStream(dataStream, sKey);
             if (null == ss) {
-                resp.onError("No literal GPG data found", null);
+                resp.OnError("No literal GPG data found", null);
                 return;
             }
             ByteArrayOutputStream actualOutput = new ByteArrayOutputStream();
@@ -213,10 +213,10 @@ public class GPGUtil {
             actualOutput.close();
             output = actualOutput.toByteArray();
         } catch (PGPException | IOException e) {
-            resp.onError(e.getMessage(), e);
+            resp.OnError(e.getMessage(), e);
             return;
         }
-        resp.onResponse(output);
+        resp.OnResponse(output);
     }
 
     private static PGPSecretKeyRing DoChangePassword(PGPSecretKeyRing secKeyring, String oldPassword, String newPassword) throws PGPException {
@@ -238,17 +238,17 @@ public class GPGUtil {
                     findSecretKey(getKeyInputStream(ctx)),
                     new OnResponseListener<PGPPrivateKeyAndPass>() {
                         @Override
-                        public void onResponse(PGPPrivateKeyAndPass sKey) {
+                        public void OnResponse(PGPPrivateKeyAndPass sKey) {
                             try {
                                 DoDecryptFile(sKey, pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey.fPrivateKey)), resp);
                             } catch (PGPException e) {
-                                onError(e.getMessage(), e);
+                                OnError(e.getMessage(), e);
                             }
                         }
 
                         @Override
-                        public void onError(String msg, Throwable e) {
-                            resp.onError(msg, e);
+                        public void OnError(String msg, Throwable e) {
+                            resp.OnError(msg, e);
                         }
                     });
         }
@@ -285,12 +285,12 @@ public class GPGUtil {
                 encOut.close();
                 out.close();
                 fileOutStream.close();
-                resp.onResponse(null);
+                resp.OnResponse(null);
             } catch (IOException | PGPException e) {
-                resp.onError(e.getMessage(), e);
+                resp.OnError(e.getMessage(), e);
             }
         } catch (IOException e) {
-            resp.onError(e.getMessage(), e);
+            resp.OnError(e.getMessage(), e);
         }
     }
 
@@ -303,10 +303,10 @@ public class GPGUtil {
                 signingKey,
                 new OnResponseListener<PGPPrivateKeyAndPass>() {
                     @Override
-                    public void onResponse(final PGPPrivateKeyAndPass sKey) {
+                    public void OnResponse(final PGPPrivateKeyAndPass sKey) {
                         passwordGetter.SetStep(FileInterfaceFactory.ChangePasswordGetter.eStep.eNewPassword).GetPassword(new FileInterfaceFactory.OnPasswordEnteredListener() {
                             @Override
-                            public boolean onResponse(String result) {
+                            public boolean OnResponse(String result) {
                                 if (result != null) {
                                     try {
                                         ArrayList<PGPSecretKeyRing> keyringCollection = new ArrayList<>();
@@ -314,24 +314,24 @@ public class GPGUtil {
                                         ByteArrayOutputStream stream = new ByteArrayOutputStream();
                                         new PGPSecretKeyRingCollection(keyringCollection).encode(stream);
                                         SettingsManager.SetGPGKeyContent(ctx, SettingsManager.GPG_GENERATED, new ByteArrayInputStream(stream.toByteArray()));
-                                        onDone.onResponse(null);
+                                        onDone.OnResponse(null);
                                         return true;
                                     } catch (PGPException | IOException e) {
-                                        onError(e.getMessage(), e);
+                                        OnError(e.getMessage(), e);
                                     }
                                 }
                                 return false;
                             }
 
                             @Override
-                            public void onError(String msg, Throwable e) {
-                                onDone.onError(msg, e);
+                            public void OnError(String msg, Throwable e) {
+                                onDone.OnError(msg, e);
                             }
                         });
                     }
 
                     @Override
-                    public void onError(String msg, Throwable e) {
+                    public void OnError(String msg, Throwable e) {
                     }
                 });
     }

+ 15 - 2
app/src/main/java/info/knacki/pass/io/IFileInterface.java

@@ -1,7 +1,20 @@
 package info.knacki.pass.io;
 
 public interface IFileInterface {
-    void ReadFile(OnResponseListener<String> resp);
 
-    void WriteFile(String content, OnResponseListener<Void> resp);
+    class InvalidPasswordException extends Throwable {
+        private final String fWhat;
+
+        InvalidPasswordException(String what) {
+            fWhat = what;
+        }
+
+        @Override
+        public String getMessage() {
+            return fWhat;
+        }
+    }
+
+    void ReadFile(OnResponseListener<String> resp);
+    void WriteFile(String content, OnResponseListener<Void> resp) throws InvalidPasswordException;
 }

+ 1 - 1
app/src/main/java/info/knacki/pass/io/OnErrorListener.java

@@ -1,5 +1,5 @@
 package info.knacki.pass.io;
 
 public interface OnErrorListener {
-    void onError(String msg, Throwable e);
+    void OnError(String msg, Throwable e);
 }

+ 1 - 1
app/src/main/java/info/knacki/pass/io/OnResponseListener.java

@@ -1,5 +1,5 @@
 package info.knacki.pass.io;
 
 public interface OnResponseListener<T> extends OnErrorListener {
-    void onResponse(T result);
+    void OnResponse(T result);
 }

+ 1 - 1
app/src/main/java/info/knacki/pass/io/OnStreamResponseListener.java

@@ -1,5 +1,5 @@
 package info.knacki.pass.io;
 
 public interface OnStreamResponseListener<T> extends OnResponseListener<T> {
-    void onMsg(String message);
+    void OnMsg(String message);
 }

+ 32 - 24
app/src/main/java/info/knacki/pass/io/PasswordFileInterface.java

@@ -1,5 +1,7 @@
 package info.knacki.pass.io;
 
+import android.content.Context;
+
 import org.bouncycastle.bcpg.ArmoredOutputStream;
 import org.bouncycastle.bcpg.CompressionAlgorithmTags;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -31,15 +33,17 @@ import java.security.SecureRandom;
 import java.security.Security;
 import java.util.Date;
 
+import info.knacki.pass.settings.SettingsManager;
+
 class PasswordFileInterface implements IFileInterface {
     protected final File fFile;
     protected final FileInterfaceFactory.PasswordGetter fPasswordGetter;
+    private final char[] fPassword;
 
-    protected final String passwordWrite = "mpass"; // FIXME
-
-    PasswordFileInterface(FileInterfaceFactory.PasswordGetter passwordGetter, File f) {
+    PasswordFileInterface(Context ctx, FileInterfaceFactory.PasswordGetter passwordGetter, File f) {
         fFile = f;
         fPasswordGetter = passwordGetter;
+        fPassword = SettingsManager.GetPassword(ctx).toCharArray();
     }
 
     private boolean TryPassword(String pass, OnResponseListener<InputStream> onResponse) {
@@ -50,7 +54,7 @@ class PasswordFileInterface implements IFileInterface {
             PGPEncryptedDataList enc = (o instanceof PGPEncryptedDataList) ? (PGPEncryptedDataList) o : (PGPEncryptedDataList) pgpF.nextObject();
             InputStream clear = ((PGPPBEEncryptedData) enc.get(0)).getDataStream(new JcePBEDataDecryptorFactoryBuilder(new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build()).setProvider("BC").build(pass.toCharArray()));
 
-            onResponse.onResponse(clear);
+            onResponse.OnResponse(clear);
             return true;
         }
         catch (PGPDataValidationException e) {
@@ -58,7 +62,7 @@ class PasswordFileInterface implements IFileInterface {
             return false;
         }
         catch (IOException | PGPException e) {
-            onResponse.onError(e.getMessage(), e);
+            onResponse.OnError(e.getMessage(), e);
             return true;
         }
     }
@@ -66,9 +70,9 @@ class PasswordFileInterface implements IFileInterface {
     private void FindPassword(final OnResponseListener<InputStream> onResponse) {
         FileInterfaceFactory.OnPasswordEnteredListener onResp = new FileInterfaceFactory.OnPasswordEnteredListener() {
             @Override
-            public boolean onResponse(String result) {
+            public boolean OnResponse(String result) {
                 if (result == null) {
-                    onResponse.onError("Invalid password", null);
+                    onResponse.OnError("Invalid password", null);
                     return false;
                 } else if (!TryPassword(result, onResponse)) {
                     fPasswordGetter.GetPassword(this);
@@ -78,8 +82,8 @@ class PasswordFileInterface implements IFileInterface {
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
-                onResponse.onError(msg, e);
+            public void OnError(String msg, Throwable e) {
+                onResponse.OnError(msg, e);
             }
         };
         fPasswordGetter.GetPassword(onResp);
@@ -89,22 +93,22 @@ class PasswordFileInterface implements IFileInterface {
 
         FindPassword(new OnResponseListener<InputStream>() {
             @Override
-            public void onResponse(InputStream clear) {
+            public void OnResponse(InputStream clear) {
                 try {
                     JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(clear);
                     PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject();
                     pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
                     PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
-                    resp.onResponse(Streams.readAll(ld.getInputStream()));
+                    resp.OnResponse(Streams.readAll(ld.getInputStream()));
                 }
                 catch (IOException |PGPException e) {
-                    resp.onError(e.getMessage(), e);
+                    resp.OnError(e.getMessage(), e);
                 }
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
-                resp.onError(msg, e);
+            public void OnError(String msg, Throwable e) {
+                resp.OnError(msg, e);
             }
         });
     }
@@ -130,7 +134,7 @@ class PasswordFileInterface implements IFileInterface {
         OutputStream out = new ArmoredOutputStream(fileOutStream);
 
         PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedDataGenerator.AES_128).setWithIntegrityPacket(true).setSecureRandom(new SecureRandom()).setProvider("BC"));
-        encGen.addMethod(new JcePBEKeyEncryptionMethodGenerator(passwordWrite.toCharArray()).setProvider("BC"));
+        encGen.addMethod(new JcePBEKeyEncryptionMethodGenerator(fPassword).setProvider("BC"));
         OutputStream encOut = encGen.open(fileOutStream, compressedData.length);
 
         encOut.write(compressedData);
@@ -145,28 +149,32 @@ class PasswordFileInterface implements IFileInterface {
         Security.addProvider(new BouncyCastleProvider());
 
         if (fFile.length() == 0) {
-            resp.onResponse("");
+            resp.OnResponse("");
             return;
         }
         try {
             DecryptFile(new OnResponseListener<byte[]>() {
                 @Override
-                public void onResponse(byte[] result) {
-                    resp.onResponse(CharsetHelper.ByteArrayToString(result));
+                public void OnResponse(byte[] result) {
+                    resp.OnResponse(CharsetHelper.ByteArrayToString(result));
                 }
 
                 @Override
-                public void onError(String msg, Throwable e) {
-                    resp.onError(msg, e);
+                public void OnError(String msg, Throwable e) {
+                    resp.OnError(msg, e);
                 }
             });
         } catch (Throwable e) {
-            resp.onError(e.getMessage(), e);
+            resp.OnError(e.getMessage(), e);
         }
     }
 
     @Override
-    public void WriteFile(String content, OnResponseListener<Void> resp) {
+    public void WriteFile(String content, OnResponseListener<Void> resp) throws InvalidPasswordException {
+        if (fPassword.length == 0) {
+            throw new InvalidPasswordException("Password not set. Please set password first");
+        }
+
         Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
         Security.addProvider(new BouncyCastleProvider());
 
@@ -174,9 +182,9 @@ class PasswordFileInterface implements IFileInterface {
             CryptFile(new FileOutputStream(fFile), CharsetHelper.StringToByteArray(content));
         }
         catch (Throwable e) {
-            resp.onError(e.getMessage(), e);
+            resp.OnError(e.getMessage(), e);
             return;
         }
-        resp.onResponse(null);
+        resp.OnResponse(null);
     }
 }

+ 4 - 4
app/src/main/java/info/knacki/pass/io/RawFileInterface.java

@@ -14,10 +14,10 @@ class RawFileInterface implements IFileInterface {
     @Override
     public void ReadFile(OnResponseListener<String> resp) {
         try {
-            resp.onResponse(FileUtils.ReadAllFile(fFile));
+            resp.OnResponse(FileUtils.ReadAllFile(fFile));
         }
         catch (IOException e) {
-            resp.onError(e.getMessage(), e);
+            resp.OnError(e.getMessage(), e);
         }
     }
 
@@ -27,10 +27,10 @@ class RawFileInterface implements IFileInterface {
             FileWriter writer = new FileWriter(fFile);
             writer.write(content);
             writer.close();
-            resp.onResponse(null);
+            resp.OnResponse(null);
         }
         catch (IOException e) {
-            resp.onError(e.getMessage(), e);
+            resp.OnError(e.getMessage(), e);
         }
     }
 }

+ 12 - 0
app/src/main/java/info/knacki/pass/settings/SettingsManager.java

@@ -271,6 +271,18 @@ public class SettingsManager {
         return GetPrefManager(ctx).contains("GPGKeyFile");
     }
 
+    public static boolean HasPassword(Context ctx) {
+        return !("".equals(GetPassword(ctx)));
+    }
+
+    public static String GetPassword(Context ctx) {
+        return GetPrefManager(ctx).getString("encPasswd", "");
+    }
+
+    public static void SetPassword(Context ctx, String password) {
+        GetPrefManager(ctx).edit().putString("encPasswd", password).commit();
+    }
+
     public static boolean IsFingerprintEnabled(Context ctx) {
         return GetPrefManager(ctx).getBoolean("FingerprintEnabled", false);
     }

+ 53 - 5
app/src/main/java/info/knacki/pass/settings/ui/SettingsActivity.java

@@ -22,6 +22,7 @@ import android.support.v4.app.NavUtils;
 import android.support.v4.app.ShareCompat;
 import android.support.v4.content.FileProvider;
 import android.support.v7.app.ActionBar;
+import android.support.v7.widget.AppCompatTextView;
 import android.util.SparseArray;
 import android.view.MenuItem;
 import android.view.View;
@@ -31,6 +32,7 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -39,12 +41,16 @@ import info.knacki.pass.R;
 import info.knacki.pass.git.GitInterface;
 import info.knacki.pass.git.GitInterfaceFactory;
 import info.knacki.pass.git.entities.GitRef;
+import info.knacki.pass.io.FileInterfaceFactory;
+import info.knacki.pass.io.FileMigrator;
 import info.knacki.pass.io.FileUtils;
 import info.knacki.pass.io.GPGUtil;
 import info.knacki.pass.io.OnResponseListener;
 import info.knacki.pass.settings.SettingsManager;
 import info.knacki.pass.ui.GitPullActivity;
+import info.knacki.pass.ui.alertPrompt.AlertPrompt;
 import info.knacki.pass.ui.alertPrompt.AlertPromptGenerator;
+import info.knacki.pass.ui.alertPrompt.views.SimpleTextEdit;
 import info.knacki.pass.ui.alertPrompt.views.TextView;
 import info.knacki.pass.ui.passwordPicker.PasswordPickerFactory;
 
@@ -126,7 +132,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                 || GeneralPreferenceFragment.class.getName().equals(fragmentName)
                 || EncryptionPreferenceFragment.class.getName().equals(fragmentName)
                 || VCSPreferenceFragment.class.getName().equals(fragmentName)
-                || GPGPreferenceFragment.class.getName().equals(fragmentName);
+                || GPGPreferenceFragment.class.getName().equals(fragmentName)
+                || PasswordPreferenceFragment.class.getName().equals(fragmentName);
     }
 
     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
@@ -389,7 +396,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             if (gitInterface != null) {
                 gitInterface.GetRefs(new OnResponseListener<GitRef[]>() {
                     @Override
-                    public void onResponse(final GitRef[] result) {
+                    public void OnResponse(final GitRef[] result) {
                         getActivity().runOnUiThread(() -> {
                             CharSequence[] refNames = new CharSequence[result.length];
                             CharSequence[] refs = new CharSequence[result.length];
@@ -412,7 +419,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                     }
 
                     @Override
-                    public void onError(final String msg, final Throwable e) {
+                    public void OnError(final String msg, final Throwable e) {
                         getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), msg, Toast.LENGTH_LONG).show());
                     }
                 });
@@ -455,12 +462,12 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                 try {
                     GPGUtil.ChangePassword(getActivity(), PasswordPickerFactory.GetPasswordEditor(getActivity()), new OnResponseListener<Void>() {
                         @Override
-                        public void onResponse(Void result) {
+                        public void OnResponse(Void result) {
                             updateGpgFileLabel();
                         }
 
                         @Override
-                        public void onError(String msg, Throwable e) {
+                        public void OnError(String msg, Throwable e) {
                             Toast.makeText(getActivity(), "Error: " + msg, Toast.LENGTH_LONG).show();
                         }
                     });
@@ -553,4 +560,45 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             }
         }
     }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    public static class PasswordPreferenceFragment extends PreferenceFragment {
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            addPreferencesFromResource(R.xml.pref_enc_password);
+            setHasOptionsMenu(true);
+
+            final Preference pwd = findPreference(getString(R.string.id_pwd_password));
+            UpdateSummary();
+            pwd.setOnPreferenceClickListener((preference) -> {
+                PasswordPickerFactory.GetPasswordEditor(getActivity())
+                    .SetStep(FileInterfaceFactory.ChangePasswordGetter.eStep.eNewPassword)
+                    .GetPassword(new FileInterfaceFactory.OnPasswordEnteredListener() {
+                        @Override
+                        public boolean OnResponse(String password) {
+                            SettingsManager.SetPassword(getActivity(), password);
+                            UpdateSummary();
+                            AlertPromptGenerator.StaticMake(getActivity())
+                                    .setTitle(R.string.update_password)
+                                    .setView(new TextView(getActivity()).SetText(R.string.update_password_confirm))
+                                    .setPositiveButton(R.string.yes, (dialogInterface, view) -> FileMigrator.MigratePasswordToPassword(getActivity(), PasswordPickerFactory.GetPasswordPicker(getActivity())))
+                                    .setNegativeButton(R.string.no, null)
+                                    .show();
+                            return true;
+                        }
+
+                        @Override
+                        public void OnError(String msg, Throwable e) {
+
+                        }
+                    });
+                return true;
+            });
+        }
+
+        private void UpdateSummary() {
+            findPreference(getString(R.string.id_pwd_password)).setSummary(SettingsManager.HasPassword(getActivity()) ? R.string.pref_summary_pwd_password : R.string.pref_summary_pwd_newpassword);
+        }
+    }
 }

+ 22 - 15
app/src/main/java/info/knacki/pass/ui/EditPasswordActivity.java

@@ -14,6 +14,7 @@ import java.util.logging.Logger;
 
 import info.knacki.pass.R;
 import info.knacki.pass.io.FileInterfaceFactory;
+import info.knacki.pass.io.IFileInterface;
 import info.knacki.pass.io.OnResponseListener;
 import info.knacki.pass.ui.passwordPicker.PasswordPickerFactory;
 
@@ -44,20 +45,26 @@ public class EditPasswordActivity extends AppCompatActivity {
             final Editable text = fTextEdit.getText();
             final String textStr = text == null ? "" : text.toString();
 
-            FileInterfaceFactory.GetFileInterface(EditPasswordActivity.this, PasswordPickerFactory.GetPasswordPicker(EditPasswordActivity.this), fOutputFile).WriteFile(textStr, new OnResponseListener<Void>() {
-                @Override
-                public void onResponse(Void result) {
-                    EditPasswordActivity.this.finish();
-                }
+            try {
+                FileInterfaceFactory.GetFileInterface(EditPasswordActivity.this, PasswordPickerFactory.GetPasswordPicker(EditPasswordActivity.this), fOutputFile).WriteFile(textStr, new OnResponseListener<Void>() {
+                    @Override
+                    public void OnResponse(Void result) {
+                        EditPasswordActivity.this.finish();
+                    }
 
-                @Override
-                public void onError(final String msg, final Throwable e) {
-                    EditPasswordActivity.this.runOnUiThread(() -> {
-                        Toast.makeText(EditPasswordActivity.this, "Error: " +msg, Toast.LENGTH_LONG).show();
-                        log.log(Level.WARNING, msg, e);
-                    });
-                }
-            });
+                    @Override
+                    public void OnError(final String msg, final Throwable e) {
+                        EditPasswordActivity.this.runOnUiThread(() -> {
+                            Toast.makeText(EditPasswordActivity.this, "Error: " + msg, Toast.LENGTH_LONG).show();
+                            log.log(Level.WARNING, msg, e);
+                        });
+                    }
+                });
+            }
+            catch (IFileInterface.InvalidPasswordException e) {
+                log.log(Level.WARNING, e.getMessage(), e);
+                Toast.makeText(EditPasswordActivity.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
+            }
         });
         boolean editMode = getIntent().getBooleanExtra(INTENT_EDIT, true);
         populateContent(editMode);
@@ -70,7 +77,7 @@ public class EditPasswordActivity extends AppCompatActivity {
         fTextEdit.setEnabled(false);
         FileInterfaceFactory.GetFileInterface(this, PasswordPickerFactory.GetPasswordPicker(this), fOutputFile).ReadFile(new OnResponseListener<String>() {
             @Override
-            public void onResponse(final String pass) {
+            public void OnResponse(final String pass) {
                 EditPasswordActivity.this.runOnUiThread(() -> {
                     fTextEdit.setEnabled(true);
                     fTextEdit.setText(pass);
@@ -82,7 +89,7 @@ public class EditPasswordActivity extends AppCompatActivity {
             }
 
             @Override
-            public void onError(final String msg, final Throwable e) {
+            public void OnError(final String msg, final Throwable e) {
                 EditPasswordActivity.this.runOnUiThread(() -> {
                     Toast.makeText(EditPasswordActivity.this, "Error: " +msg, Toast.LENGTH_LONG).show();
                     log.log(Level.WARNING, msg, e);

+ 30 - 30
app/src/main/java/info/knacki/pass/ui/GitPullActivity.java

@@ -39,7 +39,7 @@ public class GitPullActivity extends AppCompatActivity {
     private GitInterface fGitInterfage;
     private GitCommit fHeadCommit;
 
-    private void onMsg(final String msg) {
+    private void OnMsg(final String msg) {
         GitPullActivity.this.runOnUiThread(() -> {
             TextView logView = findViewById(R.id.logView);
             logView.append(msg +"\n");
@@ -63,18 +63,18 @@ public class GitPullActivity extends AppCompatActivity {
         if (fGitInterfage != null) {
             fGitInterfage.FetchHead(new OnStreamResponseListener<GitCommit>() {
                 @Override
-                public void onMsg(final String msg) {
-                    GitPullActivity.this.onMsg(msg);
+                public void OnMsg(final String msg) {
+                    GitPullActivity.this.OnMsg(msg);
                 }
 
                 @Override
-                public void onResponse(GitCommit result) {
+                public void OnResponse(GitCommit result) {
                     fHeadCommit = result;
                     GitPullActivity.this.runOnUiThread(GitPullActivity.this::OnTreeStructureFetched);
                 }
 
                 @Override
-                public void onError(final String msg, Throwable e) {
+                public void OnError(final String msg, Throwable e) {
                     GitPullActivity.this.runOnUiThread(() -> {
                         ProgressBar pg = findViewById(R.id.progressBar);
                         pg.setIndeterminate(false);
@@ -192,7 +192,7 @@ public class GitPullActivity extends AppCompatActivity {
     void SyncFiles(final GitLocal localVersion, final HashMap<String, GitObject.GitBlob> filesToPull, final Set<String> filesToPush) {
         final OnStreamResponseListener<Void> allDone = new OnStreamResponseListener<Void>() {
             @Override
-            public void onResponse(Void result) {
+            public void OnResponse(Void result) {
                 GitPullActivity.this.runOnUiThread(() -> {
                     ProgressBar pg = findViewById(R.id.progressBar);
                     pg.setIndeterminate(false);
@@ -205,7 +205,7 @@ public class GitPullActivity extends AppCompatActivity {
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
+            public void OnError(String msg, Throwable e) {
                 GitPullActivity.this.runOnUiThread(() -> {
                     ProgressBar pg = findViewById(R.id.progressBar);
                     pg.setIndeterminate(false);
@@ -216,35 +216,35 @@ public class GitPullActivity extends AppCompatActivity {
             }
 
             @Override
-            public void onMsg(String message) {
-                GitPullActivity.this.onMsg(message);
+            public void OnMsg(String message) {
+                GitPullActivity.this.OnMsg(message);
             }
         };
 
         final Runnable afterFetching = () -> {
             if (filesToPush.size() > 0) {
-                GitPullActivity.this.onMsg("Updating remote repository");
+                GitPullActivity.this.OnMsg("Updating remote repository");
                 PushBlobs(filesToPush, localVersion, allDone);
             } else {
-                GitPullActivity.this.onMsg("Nothing to push");
-                allDone.onResponse(null);
+                GitPullActivity.this.OnMsg("Nothing to push");
+                allDone.OnResponse(null);
             }
         };
 
         if (filesToPull.isEmpty()) {
-            onMsg("Nothing to pull");
+            OnMsg("Nothing to pull");
             afterFetching.run();
         } else {
-            onMsg("Updating local repository");
+            OnMsg("Updating local repository");
             DownloadBlobs(filesToPull, localVersion, new OnResponseListener<Void>() {
                 @Override
-                public void onResponse(Void result) {
+                public void OnResponse(Void result) {
                     afterFetching.run();
                 }
 
                 @Override
-                public void onError(String msg, Throwable e) {
-                    allDone.onError(msg, e);
+                public void OnError(String msg, Throwable e) {
+                    allDone.OnError(msg, e);
                 }
             });
         }
@@ -252,7 +252,7 @@ public class GitPullActivity extends AppCompatActivity {
 
     void DownloadBlobs(Map<String, GitObject.GitBlob> blobs, final GitLocal localVersion, final OnResponseListener<Void> resp) {
         if (blobs.size() == 0) {
-            resp.onResponse(null);
+            resp.OnResponse(null);
             return;
         }
         final TextView logView = findViewById(R.id.logView);
@@ -263,7 +263,7 @@ public class GitPullActivity extends AppCompatActivity {
         files.addAll(blobs.entrySet());
         final OnResponseListener<byte[]> downloader = new OnResponseListener<byte[]>() {
             @Override
-            public void onResponse(final byte[] result) {
+            public void OnResponse(final byte[] result) {
                 final String filename = files.peek().getKey();
                 final GitObject.GitBlob blob = files.peek().getValue();
                 final OnResponseListener<byte[]> _this = this;
@@ -277,7 +277,7 @@ public class GitPullActivity extends AppCompatActivity {
             }
 
             @Override
-            public void onError(final String msg, Throwable e) {
+            public void OnError(final String msg, Throwable e) {
                 log.log(Level.SEVERE, msg, e);
 
                 GitPullActivity.this.runOnUiThread(() -> logView.append("Error while fetching " +files.peek().getValue().GetFilename() +": " +msg));
@@ -286,7 +286,7 @@ public class GitPullActivity extends AppCompatActivity {
                 if (!files.empty()) {
                     fGitInterfage.FetchBlob(files.peek().getValue(), this);
                 } else {
-                    resp.onResponse(null);
+                    resp.OnResponse(null);
                 }
             }
         };
@@ -307,7 +307,7 @@ public class GitPullActivity extends AppCompatActivity {
                 fGitInterfage.FetchBlob(files.peek().getValue(), downloader);
             }
         } else {
-            resp.onResponse(null);
+            resp.OnResponse(null);
         }
     }
 
@@ -332,8 +332,8 @@ public class GitPullActivity extends AppCompatActivity {
 
     void PushBlobs(final Set<String> files, final GitLocal localVersion, final OnStreamResponseListener<Void> resp) {
         if (files.isEmpty()) {
-            resp.onMsg("Nothing to commit");
-            resp.onResponse(null);
+            resp.OnMsg("Nothing to commit");
+            resp.OnResponse(null);
         } else {
             SettingsManager.Git config = (SettingsManager.Git) SettingsManager.GetVCS(this);
             final GitCommit.Builder commit = new GitCommit.Builder(fHeadCommit, config.GetUsername(), config.GetUserEmail(), COMMIT_MSG);
@@ -341,12 +341,12 @@ public class GitPullActivity extends AppCompatActivity {
                 commit.AddFile(i, new File(PathUtils.GetPassDir(this) + i));
             fGitInterfage.PushBlobs(commit, new OnStreamResponseListener<Void>() {
                 @Override
-                public void onMsg(String message) {
-                    resp.onMsg(message);
+                public void OnMsg(String message) {
+                    resp.OnMsg(message);
                 }
 
                 @Override
-                public void onResponse(Void result) {
+                public void OnResponse(Void result) {
                     final GitObject.GitTree tree = commit.Build().GetTree();
                     for (String i: files) {
                         if (tree.GetObjectFullPath(i) == null)
@@ -354,12 +354,12 @@ public class GitPullActivity extends AppCompatActivity {
                         else
                             localVersion.SetHash(i, GitSha1.BytesToString(tree.GetObjectFullPath(i).GetHash()));
                     }
-                    onMsg("Done");
-                    resp.onResponse(result);
+                    OnMsg("Done");
+                    resp.OnResponse(result);
                 }
 
                 @Override
-                public void onError(String msg, Throwable e) {
+                public void OnError(String msg, Throwable e) {
                     Logger.getAnonymousLogger().log(Level.SEVERE, msg, e);
                 }
             });

+ 45 - 35
app/src/main/java/info/knacki/pass/ui/MainActivity.java

@@ -37,9 +37,9 @@ import info.knacki.pass.ui.passwordPicker.PasswordPickerFactory;
 
 public class MainActivity extends AppCompatActivity implements PasswordEditListener {
     private final Logger log = Logger.getLogger(MainActivity.class.getName());
-    private EditablePasswordListView vPasswordListView;
+    private EditablePasswordListView fPasswordListView;
 
-    protected TextEditAndCheckbox lastFileName = null;
+    protected TextEditAndCheckbox fLastFileName = null;
 
     protected void CreateNewFolder() {
         AlertPromptGenerator.StaticMake(this)
@@ -52,11 +52,11 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
                         Toast.makeText(MainActivity.this, "Error: Empty file name", Toast.LENGTH_LONG).show();
                         return;
                     }
-                    File f = new File(vPasswordListView.fCurrentDir +"/" +filename);
+                    File f = new File(fPasswordListView.fCurrentDir +"/" +filename);
                     if (!f.mkdir()) {
                         Toast.makeText(MainActivity.this, "Error: cannot create folder", Toast.LENGTH_LONG).show();
                     }
-                    vPasswordListView.refresh();
+                    fPasswordListView.refresh();
                 })
                 .setNegativeButton(R.string.cancel, (dialogInterface, s) -> dialogInterface.cancel())
                 .setTitle(R.string.add_title)
@@ -64,38 +64,38 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
     }
 
     protected void CreateNewPassword() {
-        if (lastFileName == null) {
-            lastFileName = new TextEditAndCheckbox(this);
+        if (fLastFileName == null) {
+            fLastFileName = new TextEditAndCheckbox(this);
         }
         AlertPromptGenerator.StaticMake(this)
                 .setCancelable(true)
                 .setView(new TextEditAndCheckbox(this)
-                    .setText(lastFileName.getStr())
+                    .setText(fLastFileName.getStr())
                     .setCheckboxCaption(R.string.add_generate)
-                    .setChecked(lastFileName.isChecked()))
+                    .setChecked(fLastFileName.isChecked()))
                 .setPositiveButton(R.string.add, (dialogInterface, view) -> {
-                    lastFileName = (TextEditAndCheckbox) view;
-                    final String filename = lastFileName.getStr();
+                    fLastFileName = (TextEditAndCheckbox) view;
+                    final String filename = fLastFileName.getStr();
                     if (filename.length() == 0) {
                         Toast.makeText(MainActivity.this, "Error: Empty file name", Toast.LENGTH_LONG).show();
                         return;
                     }
-                    File f = new File(vPasswordListView.fCurrentDir +"/" +filename +FileInterfaceFactory.GetExtension(SettingsManager.GetDefaultEncryptionType(MainActivity.this)));
+                    File f = new File(fPasswordListView.fCurrentDir +"/" +filename +FileInterfaceFactory.GetExtension(SettingsManager.GetDefaultEncryptionType(MainActivity.this)));
                     try {
                         f.createNewFile();
-                        vPasswordListView.refresh();
+                        fPasswordListView.refresh();
                     }
                     catch (IOException e) {
                         Toast.makeText(MainActivity.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
                         log.log(Level.WARNING, e.getMessage(), e);
                         return;
                     }
-                    if (lastFileName.isChecked()) {
-                        GeneratePassword(f);
+                    if (fLastFileName.isChecked()) {
+                        GeneratePassword(f, true);
                     } else {
                         EditFile(f, true);
                     }
-                    lastFileName = null;
+                    fLastFileName = null;
                 })
                 .setNegativeButton(R.string.cancel, (dialogInterface, s) -> dialogInterface.cancel())
                 .setTitle(R.string.add_title)
@@ -111,7 +111,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
     @Override
     protected void onResume() {
         super.onResume();
-        vPasswordListView.refresh();
+        fPasswordListView.refresh();
     }
 
     @Override
@@ -120,8 +120,8 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
         setContentView(R.layout.activity_pass_list);
 
         setTitle(R.string.title);
-        vPasswordListView = new EditablePasswordListView<>(this, PathUtils.GetPassDir(this));
-        ((ScrollView)findViewById(R.id.passwordListContainer)).addView(vPasswordListView);
+        fPasswordListView = new EditablePasswordListView<>(this, PathUtils.GetPassDir(this));
+        ((ScrollView)findViewById(R.id.passwordListContainer)).addView(fPasswordListView);
 
         requestPermissions();
     }
@@ -160,7 +160,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
         return super.onCreateOptionsMenu(menu);
     }
 
-    protected void GeneratePassword(final File f) {
+    protected void GeneratePassword(final File f, final boolean removeOnError) {
         final PasswordGeneratorWizard wiz = new PasswordGeneratorWizard(this);
 
         AlertPromptGenerator.StaticMake(this)
@@ -168,19 +168,29 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
             .setView(wiz)
             .setPositiveButton(R.string.add, (dialogInterface, view) -> {
                 IFileInterface writer = FileInterfaceFactory.GetFileInterface(MainActivity.this, PasswordPickerFactory.GetPasswordPicker(MainActivity.this), f);
-                writer.WriteFile(PasswordGenerator.generate(wiz), new OnResponseListener<Void>() {
-                    @Override
-                    public void onResponse(Void result) {
-                    }
 
-                    @Override
-                    public void onError(final String msg, final Throwable e) {
-                        MainActivity.this.runOnUiThread(() -> {
-                            Toast.makeText(MainActivity.this, "Error: " +msg, Toast.LENGTH_LONG).show();
-                            log.log(Level.WARNING, msg, e);
-                        });
-                    }
-                });
+                try {
+                    writer.WriteFile(PasswordGenerator.generate(wiz), new OnResponseListener<Void>() {
+                        @Override
+                        public void OnResponse(Void result) {
+                        }
+
+                        @Override
+                        public void OnError(final String msg, final Throwable e) {
+                            MainActivity.this.runOnUiThread(() -> {
+                                Toast.makeText(MainActivity.this, "Error: " + msg, Toast.LENGTH_LONG).show();
+                                log.log(Level.WARNING, msg, e);
+                            });
+                        }
+                    });
+                }
+                catch (IFileInterface.InvalidPasswordException e) {
+                    if (removeOnError)
+                        f.delete();
+                    log.log(Level.WARNING, e.getMessage(), e);
+                    Toast.makeText(MainActivity.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
+                    fPasswordListView.refresh();
+                }
             })
             .setNegativeButton(R.string.cancel, (dialogInterface, s) -> dialogInterface.cancel())
             .setTitle(R.string.generate_title)
@@ -206,12 +216,12 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
     @Override
     public void OnRemovePassword(File f) {
         f.delete();
-        vPasswordListView.refresh();
+        fPasswordListView.refresh();
     }
 
     private void DoRemoveDir(File f) {
         FileUtils.rmdir(f);
-        vPasswordListView.refresh();
+        fPasswordListView.refresh();
     }
 
     @Override
@@ -235,7 +245,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
     public void OnCopyToClipboard(File f) {
         FileInterfaceFactory.GetFileInterface(this, PasswordPickerFactory.GetPasswordPicker(this), f).ReadFile(new OnResponseListener<String>() {
             @Override
-            public void onResponse(String result) {
+            public void OnResponse(String result) {
                 ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
                 if (clip != null) {
                     clip.setPrimaryClip(ClipData.newPlainText("password", result));
@@ -244,7 +254,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
+            public void OnError(String msg, Throwable e) {
                 Toast.makeText(MainActivity.this, "Error: " +msg, Toast.LENGTH_LONG).show();
                 log.log(Level.WARNING, msg, e);
             }

+ 5 - 0
app/src/main/java/info/knacki/pass/ui/alertPrompt/AlertPrompt.java

@@ -72,6 +72,11 @@ public class AlertPrompt {
         return this;
     }
 
+    public AlertPrompt setTitle(String text) {
+        fAlertBuilder.setTitle(text);
+        return this;
+    }
+
     protected void OnBeforeShow() {}
 
     public AlertPrompt show() {

+ 5 - 0
app/src/main/java/info/knacki/pass/ui/alertPrompt/views/TextView.java

@@ -12,4 +12,9 @@ public class TextView extends AppCompatTextView {
         setText(str);
         return this;
     }
+
+    public TextView SetText(String str) {
+        setText(str);
+        return this;
+    }
 }

+ 9 - 3
app/src/main/java/info/knacki/pass/ui/passwordList/EditablePasswordListView.java

@@ -37,7 +37,7 @@ public class EditablePasswordListView<T extends Activity & PasswordEditListener>
 
     @Override
     public boolean onMenuItemClick(MenuItem item) {
-        boolean found = false;
+        boolean found = true;
 
         switch (item.getItemId()) {
             case R.id.remove:
@@ -46,12 +46,18 @@ public class EditablePasswordListView<T extends Activity & PasswordEditListener>
                 } else {
                     fClickListener.OnRemovePassword(new File(fCurrentDir + "/" + fSelectedPassword.fFullname));
                 }
-                found = true;
+                break;
+
+            case R.id.encryption_infos:
+                fClickListener.OnEncryptionInfos(new File(fCurrentDir +"/" +fSelectedPassword.fFullname));
                 break;
 
             case R.id.clipboard:
                 fClickListener.OnCopyToClipboard(new File(fCurrentDir +"/" +fSelectedPassword.fFullname));
-                found = true;
+                break;
+
+            default:
+                found = false;
                 break;
         }
         fSelectedPassword = null;

+ 20 - 19
app/src/main/java/info/knacki/pass/ui/passwordPicker/FingerprintPicker.java

@@ -112,7 +112,7 @@ class FingerprintPicker extends PasswordPicker {
             keyStore.load(null);
         } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
             final String msg = "Cannot get android keystore";
-            resp.onError(msg, e);
+            resp.OnError(msg, e);
             log.log(Level.SEVERE, msg, e);
             return null;
         }
@@ -131,7 +131,7 @@ class FingerprintPicker extends PasswordPicker {
             keyGenerator.generateKey();
         } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
             final String msg = "Cannot create Key generator";
-            resp.onError(msg, e);
+            resp.OnError(msg, e);
             log.log(Level.SEVERE, msg, e);
             return false;
         }
@@ -150,7 +150,7 @@ class FingerprintPicker extends PasswordPicker {
                 // Unrecoverable failure
                 final String msg = "Cannot get android keystore";
                 log.log(Level.SEVERE, msg);
-                resp.onError(msg, null);
+                resp.OnError(msg, null);
                 return null;
             }
         }
@@ -164,7 +164,7 @@ class FingerprintPicker extends PasswordPicker {
         }
         catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
             final String msg = "Cannot create Cipher";
-            resp.onError(msg, e);
+            resp.OnError(msg, e);
             log.log(Level.SEVERE, msg, e);
             return null;
         }
@@ -206,7 +206,7 @@ class FingerprintPicker extends PasswordPicker {
                 .setNegativeButton(R.string.cancel, (dialogInterface, view) -> {
                     fDisplayingPrompt = null;
                     signal.cancel();
-                    resp.onError(fContext.getString(R.string.cancelled), null);
+                    resp.OnError(fContext.getString(R.string.cancelled), null);
                 })
                 .show();
         final FingerprintManagerCompat.CryptoObject cryptoObject = InitCryptoObject(ivForDecoding, resp);
@@ -218,7 +218,7 @@ class FingerprintPicker extends PasswordPicker {
             public void onAuthenticationError(int errMsgId, CharSequence errString) {
                 ClosePrompt();
                 if (errMsgId != 5) // cancelled
-                    resp.onError(errString.toString(), null);
+                    resp.OnError(errString.toString(), null);
             }
 
             @Override
@@ -230,7 +230,7 @@ class FingerprintPicker extends PasswordPicker {
             @Override
             public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
                 ClosePrompt();
-                resp.onResponse(cryptoObject);
+                resp.OnResponse(cryptoObject);
             }
 
             @Override
@@ -259,7 +259,7 @@ class FingerprintPicker extends PasswordPicker {
 
         DisplayPrompt(fEncrypted, new OnResponseListener<FingerprintManagerCompat.CryptoObject>() {
             @Override
-            public void onResponse(FingerprintManagerCompat.CryptoObject cryptoObject) {
+            public void OnResponse(FingerprintManagerCompat.CryptoObject cryptoObject) {
                 try {
                     Cipher cipher = cryptoObject.getCipher();
                     if (cipher == null) {
@@ -280,15 +280,15 @@ class FingerprintPicker extends PasswordPicker {
                     if (fPasswordIterator.isEmpty())
                         SuperGetPassword(true, onResp);
                     else
-                        onResp.onResponse(fPasswordIterator.remove());
+                        onResp.OnResponse(fPasswordIterator.remove());
                 }
                 catch (BadPaddingException | IllegalBlockSizeException | UnsupportedEncodingException e) {
-                    onError(e.getMessage(), e);
+                    OnError(e.getMessage(), e);
                 }
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
+            public void OnError(String msg, Throwable e) {
                 // FIXME Toast error
                 // Fallback password
                 fPassword = new ArrayDeque<>();
@@ -302,29 +302,30 @@ class FingerprintPicker extends PasswordPicker {
         ClosePrompt();
         FingerprintPicker.super.GetPassword(savePassword ? new FileInterfaceFactory.OnPasswordEnteredListener() {
             @Override
-            public boolean onResponse(String result) {
+            public boolean OnResponse(String result) {
                 // TODO checkbox save password ?
                 if (result != null) {
                     AddPassword(result);
                 }
-                return onResp.onResponse(result);
+                return onResp.OnResponse(result);
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
-                onResp.onError(msg, e);
+            public void OnError(String msg, Throwable e) {
+                onResp.OnError(msg, e);
             }
         } : onResp);
     }
 
     @Override
-    public void GetPassword(FileInterfaceFactory.OnPasswordEnteredListener onResp) {
+    public FingerprintPicker GetPassword(FileInterfaceFactory.OnPasswordEnteredListener onResp) {
         if (fPassword == null)
             ReadAllPasswords(onResp);
         else if (fPasswordIterator.isEmpty())
             SuperGetPassword(true, onResp);
         else
-            onResp.onResponse(fPasswordIterator.remove());
+            onResp.OnResponse(fPasswordIterator.remove());
+        return this;
     }
 
     @Override
@@ -336,7 +337,7 @@ class FingerprintPicker extends PasswordPicker {
         fEncrypted = null;
         DisplayPrompt(null, new OnResponseListener<FingerprintManagerCompat.CryptoObject>() {
             @Override
-            public void onResponse(FingerprintManagerCompat.CryptoObject cryptoObject) {
+            public void OnResponse(FingerprintManagerCompat.CryptoObject cryptoObject) {
                 StringBuilder prev = new StringBuilder();
                 if (fPassword != null)
                     for (String i: fPassword)
@@ -346,7 +347,7 @@ class FingerprintPicker extends PasswordPicker {
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
+            public void OnError(String msg, Throwable e) {
             }
         });
     }

+ 48 - 20
app/src/main/java/info/knacki/pass/ui/passwordPicker/PasswordPicker.java

@@ -1,41 +1,63 @@
 package info.knacki.pass.ui.passwordPicker;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.widget.Toast;
 
+import java.io.File;
+
 import info.knacki.pass.R;
 import info.knacki.pass.io.FileInterfaceFactory;
+import info.knacki.pass.io.PathUtils;
 import info.knacki.pass.ui.alertPrompt.AlertPromptGenerator;
 import info.knacki.pass.ui.alertPrompt.views.PasswordTextEdit;
 
 class PasswordPicker implements FileInterfaceFactory.PasswordGetter {
     protected final Context fContext;
     protected final AlertPromptGenerator fAlertFactory;
+    protected String fName = null;
 
     public PasswordPicker(Context context, AlertPromptGenerator alertFactory) {
         fContext = context;
         fAlertFactory = alertFactory;
     }
 
-    public int GetTitle() {
-        return R.string.enter_password;
+    public String GetTitle() {
+        return fName == null ? fContext.getResources().getString(R.string.enter_password) : String.format(fContext.getResources().getString(R.string.enter_password_for), fName);
+    }
+
+    @Override
+    public FileInterfaceFactory.PasswordGetter SetFile(File file) {
+        if (file == null) {
+            fName = null;
+        } else {
+            int rootLen = PathUtils.GetPassDir(fContext).length();
+            fName = file.getAbsolutePath();
+            if (fName.length() > rootLen) {
+                fName = fName.substring(rootLen);
+                if (fName.startsWith("/"))
+                    fName = fName.substring(1);
+            }
+        }
+        return this;
     }
 
     @Override
-    public void GetPassword(final FileInterfaceFactory.OnPasswordEnteredListener onPassword) {
+    public PasswordPicker GetPassword(final FileInterfaceFactory.OnPasswordEnteredListener onPassword) {
         fAlertFactory.Generate(fContext)
             .setCancelable(true)
-                .setView(new PasswordTextEdit(fContext))
+            .setView(new PasswordTextEdit(fContext))
             .setPositiveButton(R.string.ok, (dialogInterface, view) -> {
-                if (!onPassword.onResponse(((PasswordTextEdit) view).getStr()))
+                if (!onPassword.OnResponse(((PasswordTextEdit) view).getStr()))
                     WrongPassword();
             })
             .setNegativeButton(R.string.cancel, (dialogInterface, s) -> {
-                if (!onPassword.onResponse(null))
+                if (!onPassword.OnResponse(null))
                     WrongPassword();
             })
             .setTitle(GetTitle())
             .show();
+        return this;
     }
 
     public void WrongPassword() {
@@ -52,23 +74,24 @@ class PasswordPicker implements FileInterfaceFactory.PasswordGetter {
         }
 
         @Override
-        public int GetTitle() {
+        public String GetTitle() {
+            Resources r = fContext.getResources();
             switch (fCurrentStep) {
                 case eOldPassword:
-                    return R.string.enter_old_password;
+                    return r.getString(R.string.enter_old_password);
                 case eNewPassword:
-                    return R.string.type_new_password;
+                    return r.getString(R.string.type_new_password);
                 case eRetypePassword:
-                    return R.string.retype_new_password;
+                    return r.getString(R.string.retype_new_password);
             }
-            return R.string.enter_password;
+            return r.getString(R.string.enter_password);
         }
 
         private final FileInterfaceFactory.OnPasswordEnteredListener fOnFirstPassEntered = new FileInterfaceFactory.OnPasswordEnteredListener() {
             @Override
-            public boolean onResponse(String result) {
+            public boolean OnResponse(String result) {
                 if (result == null) {
-                    fListener.onResponse(null);
+                    fListener.OnResponse(null);
                     return false;
                 }
                 fFirstPassword = result;
@@ -77,14 +100,14 @@ class PasswordPicker implements FileInterfaceFactory.PasswordGetter {
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
-                fListener.onError(msg, e);
+            public void OnError(String msg, Throwable e) {
+                fListener.OnError(msg, e);
             }
         };
 
         private final FileInterfaceFactory.OnPasswordEnteredListener fOnRetypePassEntered = new FileInterfaceFactory.OnPasswordEnteredListener() {
             @Override
-            public boolean onResponse(String result) {
+            public boolean OnResponse(String result) {
                 if (result == null) {
                     SetStep(eStep.eNewPassword).GetPassword(fListener);
                     return false;
@@ -93,18 +116,18 @@ class PasswordPicker implements FileInterfaceFactory.PasswordGetter {
                     SetStep(eStep.eNewPassword).GetPassword(fListener);
                     return false;
                 }
-                fListener.onResponse(result);
+                fListener.OnResponse(result);
                 return true;
             }
 
             @Override
-            public void onError(String msg, Throwable e) {
-                fListener.onError(msg, e);
+            public void OnError(String msg, Throwable e) {
+                fListener.OnError(msg, e);
             }
         };
 
         @Override
-        public void GetPassword(final FileInterfaceFactory.OnPasswordEnteredListener onPassword) {
+        public PasswordPicker GetPassword(final FileInterfaceFactory.OnPasswordEnteredListener onPassword) {
             fListener = onPassword;
             switch (fCurrentStep) {
                 case eOldPassword:
@@ -117,6 +140,7 @@ class PasswordPicker implements FileInterfaceFactory.PasswordGetter {
                     super.GetPassword(fOnRetypePassEntered);
                     break;
             }
+            return this;
         }
 
         @Override
@@ -124,5 +148,9 @@ class PasswordPicker implements FileInterfaceFactory.PasswordGetter {
             fCurrentStep = step;
             return this;
         }
+
+        @Override
+        public void WrongPassword() {
+        }
     }
 }

+ 7 - 0
app/src/main/res/values-fr/lang.xml

@@ -57,6 +57,7 @@
     <string name="pref_header_gpg_password">Mot de passe de la clef GPG</string>
     <string name="pref_summary_gpg_password">Changer le mot de passe GPG</string>
     <string name="enter_password">Mot de passe</string>
+    <string name="enter_password_for">Mot de passe pour %s</string>
     <string name="wrongPassword">Mot de passe erroné</string>
     <string name="ok">OK</string>
     <string name="are_you_sure">Êtes-vous sur.e ?</string>
@@ -83,4 +84,10 @@
     <string name="pushfinger">Lecture d\'empreinte</string>
     <string name="pushfinger_desc">Touchez le capteur avec le bout de votre doigt</string>
     <string name="fingerprint_init_error">Erreur lors de l\'initialisation du lecteur d\'empreintes.</string>
+    <string name="pref_header_password">Configuration de Mot de Passs</string>
+    <string name="pref_header_pwd_password">Mot de passe</string>
+    <string name="pref_summary_pwd_newpassword">Choisir un mot de passe</string>
+    <string name="pref_summary_pwd_password">Changer le mot de passe</string>
+    <string name="update_password">Changement de mot de passe </string>
+    <string name="update_password_confirm">Ce mot de passe sera maintenant utilisé pour les nouveaux mots de passes. Voulez-vous mettre à jours vos mots de passes ?</string>
 </resources>

+ 7 - 0
app/src/main/res/values/lang.xml

@@ -57,6 +57,7 @@
     <string name="pref_header_gpg_password">GPG key password</string>
     <string name="pref_summary_gpg_password">Change GPG password</string>
     <string name="enter_password">Enter password</string>
+    <string name="enter_password_for">Enter password for %s</string>
     <string name="wrongPassword">Wrong password</string>
     <string name="ok">OK</string>
     <string name="are_you_sure">Are you sure ?</string>
@@ -83,4 +84,10 @@
     <string name="pushfinger">Fingerprint reader</string>
     <string name="pushfinger_desc">Touch the fingerprint reader with the tip or your finger</string>
     <string name="fingerprint_init_error">Error during fingerprint initialization</string>
+    <string name="pref_header_password">Password settings</string>
+    <string name="pref_header_pwd_password">Password</string>
+    <string name="pref_summary_pwd_newpassword">Choose a password</string>
+    <string name="pref_summary_pwd_password">Change password</string>
+    <string name="update_password">Update master password</string>
+    <string name="update_password_confirm">This password will now be used for new passwords. Do you want to update your password encoded data with this master password ?</string>
 </resources>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -25,6 +25,7 @@
     <string name="id_gpg_keyfile">id_gpg_keyfile</string>
     <string name="id_gpg_password">id_gpg_password</string>
     <string name="id_gpg_export">id_gpg_export</string>
+    <string name="id_pwd_password">id_pwd_password</string>
     <integer name="id_keyboard_numbers">-2</integer>
     <integer name="id_keyboard_shift">-1</integer>
     <integer name="id_keyboard_delete">-5</integer>

+ 6 - 0
app/src/main/res/xml/pref_enc_password.xml

@@ -0,0 +1,6 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <Preference
+        android:key="@string/id_pwd_password"
+        android:title="@string/pref_header_pwd_password"
+        android:summary="@string/pref_summary_pwd_newpassword"/>
+</PreferenceScreen>

+ 3 - 1
app/src/main/res/xml/pref_encryption.xml

@@ -13,8 +13,10 @@
         android:title="@string/pref_title_enctype" />
     <PreferenceScreen
         android:fragment="info.knacki.pass.settings.ui.SettingsActivity$GPGPreferenceFragment"
-        android:icon="@drawable/ic_info_black_24dp"
         android:title="@string/pref_header_GPG" />
+    <PreferenceScreen
+        android:fragment="info.knacki.pass.settings.ui.SettingsActivity$PasswordPreferenceFragment"
+        android:title="@string/pref_header_password" />
     <SwitchPreference
         android:key="@string/pref_enctype_fingerprint"
         android:title="@string/pushfinger"