isundil 7 years ago
parent
commit
3540099668
29 changed files with 149 additions and 99 deletions
  1. 12 0
      app/build.gradle
  2. 1 1
      app/src/main/java/info/knacki/pass/generator/ui/PasswordGeneratorWizard.java
  3. 0 3
      app/src/main/java/info/knacki/pass/git/BaseGitProtocol.java
  4. 6 3
      app/src/main/java/info/knacki/pass/git/GitLocal.java
  5. 10 6
      app/src/main/java/info/knacki/pass/git/GitSha1.java
  6. 4 4
      app/src/main/java/info/knacki/pass/git/Pacman.java
  7. 2 1
      app/src/main/java/info/knacki/pass/git/PacmanBuilder.java
  8. 3 5
      app/src/main/java/info/knacki/pass/git/SSHGitProtocol.java
  9. 3 1
      app/src/main/java/info/knacki/pass/git/entities/GitCommit.java
  10. 9 5
      app/src/main/java/info/knacki/pass/git/entities/GitObject.java
  11. 3 2
      app/src/main/java/info/knacki/pass/input/InputService.java
  12. 0 4
      app/src/main/java/info/knacki/pass/io/AppendableInputStream.java
  13. 1 1
      app/src/main/java/info/knacki/pass/io/FileMigratoryUtils.java
  14. 19 4
      app/src/main/java/info/knacki/pass/io/FileUtils.java
  15. 1 0
      app/src/main/java/info/knacki/pass/io/IFileInterface.java
  16. 3 2
      app/src/main/java/info/knacki/pass/io/NetworkUtils.java
  17. 0 2
      app/src/main/java/info/knacki/pass/io/pgp/GPGUtil.java
  18. 4 4
      app/src/main/java/info/knacki/pass/io/pgp/UberGPGStorage.java
  19. 2 2
      app/src/main/java/info/knacki/pass/io/ssh/JSchWrapper.java
  20. 2 2
      app/src/main/java/info/knacki/pass/io/ssh/SSHConnection.java
  21. 17 20
      app/src/main/java/info/knacki/pass/settings/ui/SettingsActivity.java
  22. 13 13
      app/src/main/java/info/knacki/pass/ui/GitPullActivity.java
  23. 3 5
      app/src/main/java/info/knacki/pass/ui/MainActivity.java
  24. 4 0
      app/src/main/java/info/knacki/pass/ui/alertPrompt/views/ConflictView.java
  25. 7 2
      app/src/main/java/info/knacki/pass/ui/alertPrompt/views/SimpleTextEditWithKeyboard.java
  26. 12 1
      app/src/main/java/info/knacki/pass/ui/alertPrompt/views/UsernameAndEmail.java
  27. 3 3
      app/src/main/java/info/knacki/pass/ui/passwordList/PasswordListView.java
  28. 3 1
      app/src/main/java/info/knacki/pass/ui/passwordPicker/FingerprintPicker.java
  29. 2 2
      app/src/main/java/info/knacki/pass/ui/widget/Checkbox.java

+ 12 - 0
app/build.gradle

@@ -1,6 +1,14 @@
 apply plugin: 'com.android.application'
 
 android {
+    signingConfigs {
+        release {
+            keyAlias 'knacki.info'
+            keyPassword 'android'
+            storeFile file('Z:/androidKey.gpg')
+            storePassword 'android'
+        }
+    }
     compileSdkVersion 28
     defaultConfig {
         applicationId "info.knacki.pass2"
@@ -10,11 +18,13 @@ android {
         versionName "1.0"
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
         vectorDrawables.useSupportLibrary = true
+        signingConfig signingConfigs.release
     }
     buildTypes {
         release {
             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+            signingConfig signingConfigs.release
         }
         debug {
             applicationIdSuffix ".debug"
@@ -25,6 +35,8 @@ android {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
     }
+    productFlavors {
+    }
 }
 
 dependencies {

+ 1 - 1
app/src/main/java/info/knacki/pass/generator/ui/PasswordGeneratorWizard.java

@@ -55,7 +55,7 @@ public class PasswordGeneratorWizard extends LinearLayout implements PasswordGen
         private boolean locked = true;
 
         @Override
-        public void onCheckedChanged(Checkbox checkbox, boolean b) {
+        public void onCheckedChanged() {
             if (!locked)
                 fDifficulty.setSelection(4, true);
         }

+ 0 - 3
app/src/main/java/info/knacki/pass/git/BaseGitProtocol.java

@@ -1,7 +1,5 @@
 package info.knacki.pass.git;
 
-import java.util.logging.Logger;
-
 import info.knacki.pass.git.entities.GitCommit;
 import info.knacki.pass.git.entities.GitRef;
 import info.knacki.pass.io.CharsetHelper;
@@ -11,7 +9,6 @@ import info.knacki.pass.settings.SettingsManager;
 
 public abstract class BaseGitProtocol implements GitInterface {
     protected final SettingsManager.Git fConfig;
-    private final Logger log = Logger.getLogger(BaseGitProtocol.class.getName());
 
     BaseGitProtocol(SettingsManager.Git config) {
         fConfig = config;

+ 6 - 3
app/src/main/java/info/knacki/pass/git/GitLocal.java

@@ -12,6 +12,8 @@ import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import info.knacki.pass.io.FileUtils;
+
 public class GitLocal {
     final private static Logger log = Logger.getLogger(GitLocal.class.getName());
     final private Map<String, String> cache;
@@ -61,14 +63,15 @@ public class GitLocal {
 
     public void Write(File f) {
         try {
-            f.createNewFile();
+            FileUtils.Touch(f);
             FileWriter writer = new FileWriter(f);
             for (HashMap.Entry<String, String> i: cache.entrySet())
                 writer.write(i.getValue() +" " +i.getKey() +"\n");
             writer.close();
         }
-        catch (IOException e)
-        {}
+        catch (IOException e) {
+            log.log(Level.SEVERE, "Write local git index file error: " +e.getMessage(), e);
+        }
     }
 
     public boolean HasHash(String path) {

+ 10 - 6
app/src/main/java/info/knacki/pass/git/GitSha1.java

@@ -5,12 +5,16 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import info.knacki.pass.git.entities.GitObject;
 import info.knacki.pass.git.entities.GitPackable;
 import info.knacki.pass.git.entities.GitPackableUtil;
 
 public class GitSha1 {
+    private final static Logger log = Logger.getLogger(GitSha1.class.getName());
+
     public static byte[] StringToBytes(String hash) {
         byte[] result = new byte[20];
         int j =0;
@@ -45,12 +49,12 @@ public class GitSha1 {
                     return GitObject.GitBlob.GetFilePack(f);
                 }
                 catch (FileNotFoundException e) {
-
+                    return null;
                 }
                 catch (IOException e) {
-
+                    log.log(Level.SEVERE, "Cannot compute sha1 sum for file " +f.getAbsolutePath() +": " +e.getMessage(), e);
+                    return null;
                 }
-                return null;
             }
         });
     }
@@ -95,11 +99,11 @@ public class GitSha1 {
         return raw.length == 0 ? "" : BytesToString(raw);
     }
 
-    public static boolean CompareChecksums(byte[] a, byte[] b) {
-        return CompareChecksums(a, b, 0);
+    public static boolean CompareChecksum(byte[] a, byte[] b) {
+        return CompareChecksum(a, b, 0);
     }
 
-    public static boolean CompareChecksums(byte[] checksum, byte[] data, int offset) {
+    public static boolean CompareChecksum(byte[] checksum, byte[] data, int offset) {
         for (int i=0; i < checksum.length; ++i)
             if (data[offset +i] != checksum[i])
                 return false;

+ 4 - 4
app/src/main/java/info/knacki/pass/git/Pacman.java

@@ -17,8 +17,8 @@ import info.knacki.pass.io.OutputStreamWithCheckSum;
 
 class Pacman {
     private static final Logger log = Logger.getLogger(Pacman.class.getName());
-    Collection<GitPackable> fPacked;
-    Map<GitPackable.eType, Collection<GitPackable>> fPackedByType;
+    final Collection<GitPackable> fPacked;
+    final Map<GitPackable.eType, Collection<GitPackable>> fPackedByType;
 
     public Pacman(Collection<GitPackable> objectsToPack) {
         fPacked = objectsToPack;
@@ -37,7 +37,7 @@ class Pacman {
         Collection<GitPackable> col = fPackedByType.get(GitPackable.eType.eType_Tree);
         if (col != null)
             for (GitPackable i: col)
-                if (GitSha1.CompareChecksums(hash, GitSha1.getRawSha1OfPackable(i)))
+                if (GitSha1.CompareChecksum(hash, GitSha1.getRawSha1OfPackable(i)))
                     return (GitObject.GitTree) i;
         return null;
     }
@@ -46,7 +46,7 @@ class Pacman {
         Collection<GitPackable> col = fPackedByType.get(GitPackable.eType.eType_Blob);
         if (col != null)
             for (GitPackable i: col)
-                if (GitSha1.CompareChecksums(hash, GitSha1.getRawSha1OfPackable(i)))
+                if (GitSha1.CompareChecksum(hash, GitSha1.getRawSha1OfPackable(i)))
                     return (GitObject.GitRawData) i;
         return null;
     }

+ 2 - 1
app/src/main/java/info/knacki/pass/git/PacmanBuilder.java

@@ -39,7 +39,7 @@ public class PacmanBuilder {
         try {
             MessageDigest checksum = MessageDigest.getInstance("SHA1");
             checksum.update(data, 0, data.length - 20);
-            if (!GitSha1.CompareChecksums(checksum.digest(), data, data.length - 20))
+            if (!GitSha1.CompareChecksum(checksum.digest(), data, data.length - 20))
                 throw new InvalidPackException("Checksum mismatch");
         }
         catch (NoSuchAlgorithmException e) {
@@ -144,6 +144,7 @@ public class PacmanBuilder {
                     break;
 
                 case eType_OfsDelta:
+                    //noinspection StatementWithEmptyBody
                     while ((fBuffer[fPos++] & 0b10000000) != 0) {}
                     InflateDiscard(header.length);
                     break;

+ 3 - 5
app/src/main/java/info/knacki/pass/git/SSHGitProtocol.java

@@ -94,7 +94,7 @@ public class SSHGitProtocol extends BaseGitProtocol {
         if (in.read(lineLenBytes, 0, 4) < 4) {
             return null;
         }
-        int lineLen = 0;
+        int lineLen;
         try {
             lineLen = Integer.parseInt(CharsetHelper.ByteArrayToString(lineLenBytes), 16);
         }
@@ -197,13 +197,12 @@ public class SSHGitProtocol extends BaseGitProtocol {
             out.write(GitLine("deepen 1"));
             out.write(GitLine(null));
             out.write(GitLine("done"));
-            log.severe(CharsetHelper.ByteArrayToString(out.toByteArray()));
             sshWrapper.fStdin.write(out.toByteArray());
             String line;
             do {
                 byte[] b = ReadLine(sshWrapper.fStdout);
                 line = b == null ? null : CharsetHelper.ByteArrayToString(b);
-            } while (line == null || !"NAK\n".equals(line));
+            } while (!"NAK\n".equals(line));
             return FileUtils.ReadAllStream(sshWrapper.fStdout);
         }
         catch (IOException e) {
@@ -248,7 +247,6 @@ public class SSHGitProtocol extends BaseGitProtocol {
                 }
                 catch (PacmanBuilder.InvalidPackException e) {
                     response.OnError("Cannot read pack object: " +e.getMessage(), e);
-                    return;
                 }
             }
 
@@ -297,7 +295,7 @@ public class SSHGitProtocol extends BaseGitProtocol {
         if (blob instanceof GitObject.GitRawData)
             response.OnResponse(((GitObject.GitRawData) blob).GetData());
         else
-            response.OnError("Unsupported object " +blob.GetHash(), new InvalidParameterException());
+            response.OnError("Unsupported object " +CharsetHelper.ByteArrayToString(blob.GetHash()), new InvalidParameterException());
     }
 
     @Override

+ 3 - 1
app/src/main/java/info/knacki/pass/git/entities/GitCommit.java

@@ -1,5 +1,7 @@
 package info.knacki.pass.git.entities;
 
+import android.support.annotation.NonNull;
+
 import java.io.File;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -167,7 +169,7 @@ public class GitCommit implements GitPackable {
         }
     }
 
-    @Override
+    @Override @NonNull
     public String toString() {
         return "Commit " +fHash;
     }

+ 9 - 5
app/src/main/java/info/knacki/pass/git/entities/GitObject.java

@@ -291,7 +291,9 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
                 }
                 if (mode == null)
                     break;
-                for (len =0; i +len < data.length && data[i +len] != 0; ++len);
+                len =0;
+                while (i +len < data.length && data[i +len] != 0)
+                    ++len;
                 byte[] filenameBytes = new byte[len];
                 System.arraycopy(data, i, filenameBytes, 0, len);
                 i += len +1;
@@ -311,9 +313,9 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
 
         @Override
         public byte[] GetPack() {
-            ByteArrayOutputStream str = new ByteArrayOutputStream();
-
             try {
+                ByteArrayOutputStream str = new ByteArrayOutputStream();
+
                 for (GitObject go : GetObjects()) {
                     str.write((go.GetMode() + ' ').getBytes());
                     if (go.fName.equals("Aa") || "Oo".equals(go.fName))
@@ -322,9 +324,11 @@ public abstract class GitObject implements Comparable<GitObject>, GitPackable {
                     str.write(new byte[]{0});
                     str.write(go.GetHash());
                 }
+                return str.toByteArray();
+            }
+            catch (IOException e) {
+                return null;
             }
-            catch (IOException e) {}
-            return str.toByteArray();
         }
 
         @Override

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

@@ -20,6 +20,7 @@ import info.knacki.pass.R;
 import info.knacki.pass.generator.PasswordGenerator;
 import info.knacki.pass.generator.ui.PasswordGeneratorWizard;
 import info.knacki.pass.io.FileInterfaceFactory;
+import info.knacki.pass.io.FileUtils;
 import info.knacki.pass.io.IFileInterface;
 import info.knacki.pass.io.OnResponseListener;
 import info.knacki.pass.io.PathUtils;
@@ -77,7 +78,7 @@ public class InputService extends InputMethodService implements PasswordClickLis
                     }
                     File f = new File(fPasswordListView.fCurrentDir +"/" +filename +FileInterfaceFactory.GetExtension(SettingsManager.GetDefaultEncryptionType(InputService.this)));
                     try {
-                        f.createNewFile();
+                        FileUtils.Touch(f);
                         fPasswordListView.refresh();
                     }
                     catch (IOException e) {
@@ -115,7 +116,7 @@ public class InputService extends InputMethodService implements PasswordClickLis
                         });
                     }
                     catch (IFileInterface.InvalidPasswordException e) {
-                        f.delete();
+                        FileUtils.DeleteFile(f);
                         log.log(Level.WARNING, e.getMessage(), e);
                         Toast.makeText(InputService.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
                         fPasswordListView.refresh();

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

@@ -16,10 +16,6 @@ public class AppendableInputStream extends InputStream {
             buffer.addData(new byte[] {(byte) (b & 0xFF)}, 0, 1);
         }
 
-        public void AddBytes(byte[] bytes) {
-            buffer.addData(bytes, 0, bytes.length);
-        }
-
         public void close() {
             fClosed = true;
         }

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

@@ -52,7 +52,7 @@ public class FileMigratoryUtils {
         MigratePassword(FileInterfaceFactory.GetFileInterface(ctx, passInput, from), FileInterfaceFactory.GetFileInterface(ctx, passInput, to), from.getAbsolutePath().equals(to.getAbsolutePath()) || !removePrevious ? onDone : new OnResponseListener<Void>() {
             @Override
             public void OnResponse(Void result) {
-                from.delete();
+                FileUtils.DeleteFile(from);
                 onDone.OnResponse(result);
             }
 

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

@@ -12,14 +12,14 @@ import java.util.logging.Logger;
 public class FileUtils {
     private static final Logger log = Logger.getLogger(FileUtils.class.getName());
 
-    public static void rmdir(File f) {
+    public static void Rmdir(File f) {
         if (!f.isDirectory()) {
-            f.delete();
+            FileUtils.DeleteFile(f);
         }
         else {
             for (File i : f.listFiles())
-                rmdir(i);
-            f.delete();
+                Rmdir(i);
+            FileUtils.DeleteFile(f);
         }
     }
 
@@ -80,4 +80,19 @@ public class FileUtils {
                 log.severe("Cannot rename file " + f.getAbsolutePath() + " to " + dest);
         }
     }
+
+    public static void Touch(File f) throws IOException {
+        f.createNewFile();
+    }
+
+    public static void DeleteFile(File f) {
+        f.delete();
+    }
+
+    public static void MkDir(File f, boolean recursive) {
+        if (recursive)
+            f.mkdirs();
+        else
+            f.mkdir();
+    }
 }

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

@@ -14,6 +14,7 @@ public interface IFileInterface {
         private final String fWhat;
 
         InvalidPasswordException(String what) {
+            super(what);
             fWhat = what;
         }
 

+ 3 - 2
app/src/main/java/info/knacki/pass/io/NetworkUtils.java

@@ -179,9 +179,10 @@ public class NetworkUtils {
 
         @Override
         protected void ManageResponse(byte[] result) {
-            int i;
+            int i =0;
 
-            for (i =0; i < result.length && result[i] != 0; ++i);
+            while (i < result.length && result[i] != 0)
+                ++i;
 
             if (i != result.length) {
                 ++i;

+ 0 - 2
app/src/main/java/info/knacki/pass/io/pgp/GPGUtil.java

@@ -49,7 +49,6 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -64,7 +63,6 @@ import java.util.logging.Logger;
 
 import info.knacki.pass.io.FileInterfaceFactory;
 import info.knacki.pass.io.OnResponseListener;
-import info.knacki.pass.settings.SettingsManager;
 
 public class GPGUtil {
     private final static Logger log = Logger.getLogger(GPGUtil.class.getName());

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

@@ -75,7 +75,7 @@ public class UberGPGStorage implements GPGStorageEngine.IGPGStorage {
                 ks.load(null, null);
             }
         } catch (KeyStoreException | NoSuchProviderException | IOException | CertificateException | NoSuchAlgorithmException e) {
-            log.log(Level.SEVERE, "Cannot retreive KeyStore", e);
+            log.log(Level.SEVERE, "Cannot retrieve KeyStore", e);
             ks = null;
         }
         return ks;
@@ -148,9 +148,9 @@ public class UberGPGStorage implements GPGStorageEngine.IGPGStorage {
         KeyStore.SecretKeyEntry secretKey = GetGPGKeyContent();
 
         try {
-            Iterator<PGPSecretKeyRing> keyrings = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(secretKey.getSecretKey().getEncoded())), new JcaKeyFingerprintCalculator()).iterator();
-            if (keyrings.hasNext())
-                return keyrings.next();
+            Iterator<PGPSecretKeyRing> keyringCollection = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(secretKey.getSecretKey().getEncoded())), new JcaKeyFingerprintCalculator()).iterator();
+            if (keyringCollection.hasNext())
+                return keyringCollection.next();
         } catch (PGPException e) {
             log.log(Level.WARNING, "Cannot load key: " + e.getMessage(), e);
             throw new GPGUtil.MalformedKeyException(e);

+ 2 - 2
app/src/main/java/info/knacki/pass/io/ssh/JSchWrapper.java

@@ -21,7 +21,7 @@ public class JSchWrapper extends AsyncTask<InputStream, Void, Void> implements S
     private static final Logger log = Logger.getLogger(JSchWrapper.class.getName());
     private final String fCommand;
     private final ConnectionParams fAuth;
-    private OnConnectionReady fReadyListener;
+    private OnConnectionReadyListener fReadyListener;
 
     private final PipedOutputStream stdout_out = new PipedOutputStream();
     private final PipedOutputStream stderr_out = new PipedOutputStream();
@@ -36,7 +36,7 @@ public class JSchWrapper extends AsyncTask<InputStream, Void, Void> implements S
     }
 
     @Override
-    public SSHConnection SetOnConnectionReadyListener(OnConnectionReady listener) {
+    public SSHConnection SetOnConnectionReadyListener(OnConnectionReadyListener listener) {
         fReadyListener = listener;
         return this;
     }

+ 2 - 2
app/src/main/java/info/knacki/pass/io/ssh/SSHConnection.java

@@ -6,7 +6,7 @@ import java.io.InputStream;
 import info.knacki.pass.io.NetworkUtils;
 
 public interface SSHConnection {
-    interface OnConnectionReady {
+    interface OnConnectionReadyListener {
         void OnConnectionReady(SSHConnection connection, InputStream stdout, InputStream stderr);
     }
 
@@ -14,7 +14,7 @@ public interface SSHConnection {
         String GetHostname();
     }
 
-    SSHConnection SetOnConnectionReadyListener(OnConnectionReady listener);
+    SSHConnection SetOnConnectionReadyListener(OnConnectionReadyListener listener);
     byte[] GetOutputStd() throws IOException;
     byte[] GetOutputErr() throws IOException;
     SSHConnection connect();

+ 17 - 20
app/src/main/java/info/knacki/pass/settings/ui/SettingsActivity.java

@@ -246,24 +246,24 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             abstract class PrefListener implements Preference.OnPreferenceChangeListener {
                 @Override
                 public boolean onPreferenceChange(Preference preference, Object o) {
-                    savePref(preference, o);
+                    SavePref(o);
                     VCSPreferenceFragment.this.reload();
                     return true;
                 }
 
-                abstract void savePref(Preference preference, Object o);
+                abstract void SavePref(Object o);
             }
 
             findPreference(getResources().getString(R.string.id_vcs_enable)).setOnPreferenceChangeListener(new PrefListener() {
                 @Override
-                void savePref(Preference preference, Object o) {
+                void SavePref(Object o) {
                     Context ctx = getActivity();
                     SettingsManager.SetVCS(ctx, (boolean) o ? new SettingsManager.Git(ctx) : null);
                 }
             });
             findPreference(getResources().getString(R.string.id_vcs_list)).setOnPreferenceChangeListener(new PrefListener() {
                 @Override
-                void savePref(Preference preference, Object o) {
+                void SavePref(Object o) {
                     SettingsManager.VCS vcs = SettingsManager.GetVCS(getActivity());
                     SettingsManager.VCS newVcs;
 
@@ -281,7 +281,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             });
             findPreference(getResources().getString(R.string.id_vcs_git_url)).setOnPreferenceChangeListener(new PrefListener() {
                 @Override
-                void savePref(Preference preference, Object o) {
+                void SavePref(Object o) {
                     SettingsManager.Git git = (SettingsManager.Git) SettingsManager.GetVCS(getActivity());
                     git.SetUrl(((String) o).trim());
                     SettingsManager.SetVCS(getActivity(), git);
@@ -290,7 +290,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
 
             findPreference(getResources().getString(R.string.id_vcs_git_user)).setOnPreferenceChangeListener(new PrefListener() {
                 @Override
-                void savePref(Preference preference, Object o) {
+                void SavePref(Object o) {
                     SettingsManager.Git git = (SettingsManager.Git) SettingsManager.GetVCS(getActivity());
                     git.SetUser(((String) o).trim());
                     SettingsManager.SetVCS(getActivity(), git);
@@ -298,26 +298,23 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             });
             findPreference(getResources().getString(R.string.id_vcs_git_pass)).setOnPreferenceChangeListener(new PrefListener() {
                 @Override
-                void savePref(Preference preference, Object o) {
+                void SavePref(Object o) {
                     SettingsManager.Git git = (SettingsManager.Git) SettingsManager.GetVCS(getActivity());
                     git.SetPassword(((String) o).trim());
                     git.SetPrivateKey(false);
                     SettingsManager.SetVCS(getActivity(), git);
                 }
             });
-            findPreference(getResources().getString(R.string.id_vcs_git_private_key)).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
-                @Override
-                public boolean onPreferenceClick(Preference preference) {
-                    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
-                    i.setType("*/*");
-                    i.addCategory(Intent.CATEGORY_OPENABLE);
-                    VCSPreferenceFragment.this.startActivityForResult(i, ACTIVITY_REQUEST_CODE_BROWSE_PRIVATE_KEY);
-                    return true;
-                }
+            findPreference(getResources().getString(R.string.id_vcs_git_private_key)).setOnPreferenceClickListener(preference -> {
+                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
+                i.setType("*/*");
+                i.addCategory(Intent.CATEGORY_OPENABLE);
+                VCSPreferenceFragment.this.startActivityForResult(i, ACTIVITY_REQUEST_CODE_BROWSE_PRIVATE_KEY);
+                return true;
             });
             findPreference(getResources().getString(R.string.id_vcs_git_ci_username)).setOnPreferenceChangeListener(new PrefListener() {
                 @Override
-                void savePref(Preference preference, Object o) {
+                void SavePref(Object o) {
                     SettingsManager.Git git = (SettingsManager.Git) SettingsManager.GetVCS(getActivity());
                     git.SetUsername(((String) o).trim());
                     SettingsManager.SetVCS(getActivity(), git);
@@ -325,7 +322,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             });
             findPreference(getResources().getString(R.string.id_vcs_git_ci_user_email)).setOnPreferenceChangeListener(new PrefListener() {
                 @Override
-                void savePref(Preference preference, Object o) {
+                void SavePref(Object o) {
                     SettingsManager.Git git = (SettingsManager.Git) SettingsManager.GetVCS(getActivity());
                     git.SetUserEmail(((String) o).trim());
                     SettingsManager.SetVCS(getActivity(), git);
@@ -333,7 +330,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             });
             findPreference(getResources().getString(R.string.id_vcs_git_branches)).setOnPreferenceChangeListener(new PrefListener() {
                 @Override
-                void savePref(Preference preference, Object o) {
+                void SavePref(Object o) {
                     SettingsManager.Git git = (SettingsManager.Git) SettingsManager.GetVCS(getActivity());
                     git.SetBranch(((String) o).trim());
                     SettingsManager.SetVCS(getActivity(), git);
@@ -615,7 +612,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
         private void DoExportKey() {
             final File outFile = new File(getActivity().getCacheDir().getAbsolutePath() + "/secret_key.gpg");
             try {
-                outFile.createNewFile();
+                FileUtils.Touch(outFile);
                 FileOutputStream fileOut = new FileOutputStream(outFile);
                 GPGStorageEngine.GetDefaultEngine(getActivity()).ExportGPGKeyContent(fileOut);
                 fileOut.close();

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

@@ -145,16 +145,16 @@ public class GitPullActivity extends AppCompatActivity {
     int CheckNewFiles(GitLocal localVersion, File root, String rootPath, Set<String> filesToPull, Set<String> filesToPush, Set<String> conflicts) {
         int newFiles = 0;
         for (final File i: root.listFiles()) {
-            if (PathUtils.IsHidden(i.getAbsolutePath())) {
-                continue;
-            } else if (i.isDirectory()) {
-                newFiles += CheckNewFiles(localVersion, i, rootPath +"/" +i.getName(), filesToPull, filesToPush, conflicts);
-            } else if (i.isFile()){
-                String path = rootPath +"/" +i.getName();
-                if (!localVersion.HasHash(path) && !filesToPull.contains(path) && !conflicts.contains(path)) {
-                    // New file
-                    filesToPush.add(path);
-                    newFiles++;
+            if (!PathUtils.IsHidden(i.getAbsolutePath())) {
+                if (i.isDirectory()) {
+                    newFiles += CheckNewFiles(localVersion, i, rootPath + "/" + i.getName(), filesToPull, filesToPush, conflicts);
+                } else if (i.isFile()) {
+                    String path = rootPath + "/" + i.getName();
+                    if (!localVersion.HasHash(path) && !filesToPull.contains(path) && !conflicts.contains(path)) {
+                        // New file
+                        filesToPush.add(path);
+                        newFiles++;
+                    }
                 }
             }
         }
@@ -186,7 +186,7 @@ public class GitPullActivity extends AppCompatActivity {
 
         if (null == content || content.length == 0) {
             if (!isRoot)
-                dir.delete();
+                FileUtils.DeleteFile(dir);
             return;
         }
         for (File i: content)
@@ -252,7 +252,7 @@ public class GitPullActivity extends AppCompatActivity {
 
                 @Override
                 public void OnMsg(String message) {
-                    OnMsg(message);
+                    GitPullActivity.this.OnMsg(message);
                 }
             });
         }
@@ -326,7 +326,7 @@ public class GitPullActivity extends AppCompatActivity {
 
     void WriteFile(String filename, final GitLocal localVersion, GitObject.GitBlob blob, byte[] result) {
         File f = new File(PathUtils.GetPassDir(this) +filename);
-        f.getParentFile().mkdirs();
+        FileUtils.MkDir(f.getParentFile(), true);
         final int chunkSize = 1024;
         final int nbChunk = (int) Math.ceil((double) result.length / chunkSize);
 

+ 3 - 5
app/src/main/java/info/knacki/pass/ui/MainActivity.java

@@ -82,7 +82,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
                     }
                     File f = new File(fPasswordListView.fCurrentDir +"/" +filename +FileInterfaceFactory.GetExtension(SettingsManager.GetDefaultEncryptionType(MainActivity.this)));
                     try {
-                        f.createNewFile();
+                        FileUtils.Touch(f);
                         fPasswordListView.refresh();
                     }
                     catch (IOException e) {
@@ -124,8 +124,6 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
         ((ScrollView)findViewById(R.id.passwordListContainer)).addView(fPasswordListView);
 
         requestPermissions();
-
-        startActivity(new Intent(this, GitPullActivity.class));
     }
 
     @Override
@@ -192,7 +190,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
                 }
                 catch (IFileInterface.InvalidPasswordException e) {
                     if (removeOnError)
-                        f.delete();
+                        FileUtils.DeleteFile(f);
                     log.log(Level.WARNING, e.getMessage(), e);
                     Toast.makeText(MainActivity.this, "Error: " +e.getMessage(), Toast.LENGTH_LONG).show();
                     fPasswordListView.refresh();
@@ -234,7 +232,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
     }
 
     private void DoRemoveDir(File f) {
-        FileUtils.rmdir(f);
+        FileUtils.Rmdir(f);
         fPasswordListView.refresh();
     }
 

+ 4 - 0
app/src/main/java/info/knacki/pass/ui/alertPrompt/views/ConflictView.java

@@ -52,12 +52,16 @@ public class ConflictView extends HorizontalScrollView {
         fConflicts.put(name, new ConflictState());
         mine.setOnCheckedChangeListener((buttonView, isChecked) -> {
             if (isChecked) {
+                // name added just before
+                //noinspection ConstantConditions
                 fConflicts.get(name).UseMine();
                 UpdateButtonState();
             }
         });
         their.setOnCheckedChangeListener((buttonView, isChecked) -> {
             if (isChecked) {
+                // name added just before
+                //noinspection ConstantConditions
                 fConflicts.get(name).UseTheir();
                 UpdateButtonState();
             }

+ 7 - 2
app/src/main/java/info/knacki/pass/ui/alertPrompt/views/SimpleTextEditWithKeyboard.java

@@ -37,12 +37,17 @@ public class SimpleTextEditWithKeyboard  extends LinearLayout implements Keyboar
         final int selectionStart = fTextEdit.getSelectionStart();
         final int selectionEnd = fTextEdit.getSelectionEnd();
 
-        fTextEdit.getText().replace(selectionStart, selectionEnd, c);
+        Editable text = fTextEdit.getText();
+        if (null != text)
+            text.replace(selectionStart, selectionEnd, c);
     }
 
     public void OnBackspace() {
         final int selectionEnd = fTextEdit.getSelectionEnd();
         final int selectionStart = Math.max(0, fTextEdit.getSelectionStart() == selectionEnd ? fTextEdit.getSelectionStart() - 1 : fTextEdit.getSelectionStart());
-        fTextEdit.getText().replace(selectionStart, selectionEnd, "");
+
+        Editable text = fTextEdit.getText();
+        if (null != text)
+            text.replace(selectionStart, selectionEnd, "");
     }
 }

+ 12 - 1
app/src/main/java/info/knacki/pass/ui/alertPrompt/views/UsernameAndEmail.java

@@ -2,6 +2,7 @@ package info.knacki.pass.ui.alertPrompt.views;
 
 import android.content.Context;
 import android.support.v7.widget.AppCompatEditText;
+import android.text.Editable;
 import android.view.LayoutInflater;
 import android.widget.ScrollView;
 
@@ -19,7 +20,17 @@ public class UsernameAndEmail extends ScrollView {
         emailEdit = findViewById(R.id.email);
     }
 
+    public String GetUser() {
+        Editable text = usernameEdit.getText();
+        return null == text ? "" : text.toString();
+    }
+
+    public String GetEmail() {
+        Editable text = emailEdit.getText();
+        return null == text ? "" : text.toString();
+    }
+
     public String GetUserAndEmail() {
-        return usernameEdit.getText().toString() +" <" +emailEdit.getText().toString() +">";
+        return GetUser() +" <" +GetEmail() +">";
     }
 }

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

@@ -8,6 +8,7 @@ import java.io.File;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import info.knacki.pass.io.FileUtils;
 import info.knacki.pass.io.PathUtils;
 
 public class PasswordListView<T extends Context & PasswordClickListener> extends LinearLayout {
@@ -77,9 +78,8 @@ public class PasswordListView<T extends Context & PasswordClickListener> extends
         super(ctx);
         fClickListener = ctx;
         File rootDir = new File(rootPath);
-        if (!rootDir.exists()) {
-            rootDir.mkdirs();
-        }
+        if (!rootDir.exists())
+            FileUtils.MkDir(rootDir, true);
         fRootPath = rootPath;
         setOrientation(VERTICAL);
         DisplayDir(rootDir);

+ 3 - 1
app/src/main/java/info/knacki/pass/ui/passwordPicker/FingerprintPicker.java

@@ -2,6 +2,8 @@ package info.knacki.pass.ui.passwordPicker;
 
 import android.content.Context;
 import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.support.annotation.RequiresApi;
@@ -289,7 +291,7 @@ class FingerprintPicker extends PasswordPicker {
 
             @Override
             public void OnError(String msg, Throwable e) {
-                // FIXME Toast error
+                new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(fContext, msg, Toast.LENGTH_LONG).show());
                 // Fallback password
                 fPassword = new ArrayDeque<>();
                 fPasswordIterator = new ArrayDeque<>();

+ 2 - 2
app/src/main/java/info/knacki/pass/ui/widget/Checkbox.java

@@ -16,7 +16,7 @@ public class Checkbox extends LinearLayout implements Checkable {
     protected boolean fChecked;
 
     public interface OnCheckedChangeListener {
-        void onCheckedChanged(Checkbox checkbox, boolean isChecked);
+        void onCheckedChanged();
     }
 
     public Checkbox(Context context, int text, OnCheckedChangeListener onCheckedChangeListener, ViewGroup parent) {
@@ -43,7 +43,7 @@ public class Checkbox extends LinearLayout implements Checkable {
             fChecked = checked;
             SetIcon();
             if (fCheckedChangeListener != null)
-                fCheckedChangeListener.onCheckedChanged(this, isChecked());
+                fCheckedChangeListener.onCheckedChanged();
         }
     }