Browse Source

Merge branch 'issue-28' of isundil/pass into master

isundil 7 years ago
parent
commit
6d5fb8a33a

+ 1 - 0
app/build.gradle

@@ -30,6 +30,7 @@ dependencies {
     implementation 'com.android.support.constraint:constraint-layout:1.1.3'
     implementation 'com.android.support:support-v4:28.0.0'
     implementation 'com.android.support:support-vector-drawable:28.0.0'
+    implementation 'com.android.support:design:28.0.0'
     testImplementation 'junit:junit:4.12'
     androidTestImplementation 'com.android.support.test:runner:1.0.2'
     androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

+ 7 - 4
app/src/main/AndroidManifest.xml

@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="info.knacki.pass">
+
     <!-- For sync -->
-    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.INTERNET" />
 
     <!-- For fingerprint unlock -->
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
@@ -13,12 +14,12 @@
 
     <application
         android:allowBackup="true"
+        android:extractNativeLibs="false"
         android:fullBackupContent="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:extractNativeLibs="false"
         android:theme="@style/AppTheme">
         <service
             android:name=".input.InputService"
@@ -50,15 +51,17 @@
         <activity
             android:name=".ui.EditPasswordActivity"
             android:windowSoftInputMode="adjustResize" />
-        <activity android:name=".ui.GitPullActivity"/>
+        <activity android:name=".ui.GitPullActivity" />
+        <activity android:name=".ui.EncryptionInformationActivity" />
 
         <provider
-            android:authorities="info.knacki.pass.provider"
             android:name="android.support.v4.content.FileProvider"
+            android:authorities="info.knacki.pass.provider"
             android:grantUriPermissions="true">
             <meta-data
                 android:name="android.support.FILE_PROVIDER_PATHS"
                 android:resource="@xml/provider_paths" />
         </provider>
     </application>
+
 </manifest>

+ 49 - 13
app/src/main/java/info/knacki/pass/io/FileInterfaceFactory.java

@@ -1,17 +1,28 @@
 package info.knacki.pass.io;
 
 import android.content.Context;
-import android.os.Build;
+import android.content.res.Resources;
 
 import java.io.File;
+import java.util.AbstractMap;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Map;
 import java.util.logging.Logger;
 
+import info.knacki.pass.R;
 import info.knacki.pass.settings.SettingsManager;
 
 public class FileInterfaceFactory {
+    private static final Logger log = Logger.getLogger(FileInterfaceFactory.class.getName());
+    private static final ArrayDeque<EncryptionMethodItem> fEncryptionMethods = new ArrayDeque<>();
+
+    static {
+        fEncryptionMethods.add(new EncryptionMethodItem((ctx, passwordGetter, file) -> new RawFileInterface(ctx.getResources(), file), SettingsManager.EncryptionType.TYPE_RAW,".raw", R.string.pref_enctype_title_none));
+        fEncryptionMethods.add(new EncryptionMethodItem(GPGFileInterface::new, SettingsManager.EncryptionType.TYPE_GPG,".gpg", R.string.pref_enctype_title_gpg));
+        fEncryptionMethods.add(new EncryptionMethodItem(PasswordFileInterface::new, SettingsManager.EncryptionType.TYPE_PASSWORD, PasswordFileInterface.PASSWORD_SUFFIX, R.string.pref_enctype_title_password));
+    }
+
     public interface OnPasswordEnteredListener extends OnErrorListener {
         boolean OnResponse(String password);
     }
@@ -31,22 +42,47 @@ public class FileInterfaceFactory {
         ChangePasswordGetter SetStep(eStep step);
     }
 
-    public final static String PASSWORD_SUFFIX = ".pwd";
-    public final static String GPG_SUFFIX = ".gpg";
+    private static class EncryptionMethodItem {
+        final IFileInterface.IFileInterfaceCreator fFactory;
+        final String fFileSuffix;
+        final int fName;
+        SettingsManager.EncryptionType fEncryptionType;
+
+        EncryptionMethodItem(IFileInterface.IFileInterfaceCreator factory, SettingsManager.EncryptionType type, String fileSuffix, int name) {
+            fFactory = factory;
+            fFileSuffix = fileSuffix;
+            fName = name;
+            fEncryptionType = type;
+        }
+    }
+
+    protected static EncryptionMethodItem GetEncryptionMethod(File f) {
+        for (EncryptionMethodItem i: fEncryptionMethods)
+            if (f.getName().endsWith(i.fFileSuffix))
+                return i;
+        log.severe("Unknown Interface for file type " +f.getAbsolutePath());
+        throw new NoSuchMethodError();
+    }
+
+    public static SettingsManager.EncryptionType GetEncryptionType(File f) {
+        return GetEncryptionMethod(f).fEncryptionType;
+    }
 
     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(ctx, passInput, f);
-        else if (f.getName().endsWith(GPG_SUFFIX))
-            return new GPGFileInterface(ctx, passInput, f);
-        return new RawFileInterface(f);
+        return GetEncryptionMethod(f).fFactory.Create(ctx, passInput, f);
     }
 
     public static String GetExtension(SettingsManager.EncryptionType type) {
-        if (SettingsManager.EncryptionType.TYPE_PASSWORD.equals(type))
-            return PASSWORD_SUFFIX;
-        else if (SettingsManager.EncryptionType.TYPE_GPG.equals(type))
-            return GPG_SUFFIX;
+        for (EncryptionMethodItem i: fEncryptionMethods)
+            if (i.fEncryptionType.equals(type))
+                return i.fFileSuffix;
         return "";
     }
+
+    public static Collection<Map.Entry<SettingsManager.EncryptionType, String>> GetEncryptionMethodNames(Resources r) {
+        ArrayDeque<Map.Entry<SettingsManager.EncryptionType, String>> result = new ArrayDeque<>(fEncryptionMethods.size());
+        for (EncryptionMethodItem enc: fEncryptionMethods)
+            result.add(new AbstractMap.SimpleEntry<>(enc.fEncryptionType, r.getString(enc.fName)));
+        return result;
+    }
 }

+ 16 - 5
app/src/main/java/info/knacki/pass/io/FileMigrator.java

@@ -48,20 +48,31 @@ public class FileMigrator {
         });
     }
 
-    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 MigratePassword(Context ctx, File from, File to, FileInterfaceFactory.PasswordGetter passInput, OnResponseListener<Void> onDone, boolean removePrevious) {
+        MigratePassword(ctx, 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();
+                onDone.OnResponse(result);
+            }
+
+            @Override
+            public void OnError(String msg, Throwable e) {
+                onDone.OnError(msg, e);
+            }
+        });
     }
 
     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 ArrayDeque<File> oldPasswordFiles = ListPasswords(ctx, (file) -> file.getName().endsWith(PasswordFileInterface.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);
+                final ArrayDeque<String> currentPasswordToTry = new ArrayDeque<>(oldPasswords);
                 MigratePassword(ctx, currentFile, currentFile, new FileInterfaceFactory.PasswordGetter() {
                     @Override
                     public FileInterfaceFactory.PasswordGetter SetFile(File file) { return this; }
@@ -94,7 +105,7 @@ public class FileMigrator {
                     public void OnError(String msg, Throwable e) {
                         NextFile();
                     }
-                });
+                }, false);
             }
 
             @Override

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

@@ -1,15 +1,11 @@
 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) {
@@ -47,4 +43,11 @@ public class FileUtils {
             }
         } while (len > 0);
     }
+
+    public static String TrimExtension(String in) {
+        int lastIndex = in.lastIndexOf('.');
+        if (lastIndex >= Math.max(in.length() -5, 0))
+            return in.substring(0, lastIndex);
+        return in;
+    }
 }

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

@@ -1,11 +1,14 @@
 package info.knacki.pass.io;
 
 import android.content.Context;
+import android.content.res.Resources;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 
+import info.knacki.pass.R;
 import info.knacki.pass.settings.SettingsManager;
 
 class GPGFileInterface implements IFileInterface {
@@ -55,4 +58,20 @@ class GPGFileInterface implements IFileInterface {
             resp.OnError(e.getMessage(), e);
         }
     }
+
+    @Override
+    public String GetMethodName() {
+        Resources r = fContext.getResources();
+        String details;
+        if (SettingsManager.HasGPGKey(fContext)) {
+            try {
+                details = r.getString(GPGUtil.CheckIsPasswordProtected(fContext) ? R.string.protected_key : R.string.unprotected_key_short);
+            } catch (IOException e) {
+                details = e.getMessage();
+            }
+        } else {
+            details = r.getString(R.string.unset_gpg_key);
+        }
+        return r.getString(R.string.pref_enctype_title_gpg) +" (" +details +")";
+    }
 }

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

@@ -1,7 +1,15 @@
 package info.knacki.pass.io;
 
+import android.content.Context;
+
+import java.io.File;
+
 public interface IFileInterface {
 
+    interface IFileInterfaceCreator {
+        IFileInterface Create(Context ctx, FileInterfaceFactory.PasswordGetter passwordGetter, File file);
+    }
+
     class InvalidPasswordException extends Throwable {
         private final String fWhat;
 
@@ -17,4 +25,5 @@ public interface IFileInterface {
 
     void ReadFile(OnResponseListener<String> resp);
     void WriteFile(String content, OnResponseListener<Void> resp) throws InvalidPasswordException;
+    String GetMethodName();
 }

+ 11 - 0
app/src/main/java/info/knacki/pass/io/PasswordFileInterface.java

@@ -1,6 +1,7 @@
 package info.knacki.pass.io;
 
 import android.content.Context;
+import android.content.res.Resources;
 
 import org.bouncycastle.bcpg.ArmoredOutputStream;
 import org.bouncycastle.bcpg.CompressionAlgorithmTags;
@@ -33,17 +34,22 @@ import java.security.SecureRandom;
 import java.security.Security;
 import java.util.Date;
 
+import info.knacki.pass.R;
 import info.knacki.pass.settings.SettingsManager;
 
 class PasswordFileInterface implements IFileInterface {
+    public static final String PASSWORD_SUFFIX = ".pwd";
     protected final File fFile;
     protected final FileInterfaceFactory.PasswordGetter fPasswordGetter;
     private final char[] fPassword;
+    private final String fMethodName;
 
     PasswordFileInterface(Context ctx, FileInterfaceFactory.PasswordGetter passwordGetter, File f) {
         fFile = f;
         fPasswordGetter = passwordGetter;
         fPassword = SettingsManager.GetPassword(ctx).toCharArray();
+        Resources r = ctx.getResources();
+        fMethodName = r.getString(R.string.pref_enctype_title_password);
     }
 
     private boolean TryPassword(String pass, OnResponseListener<InputStream> onResponse) {
@@ -187,4 +193,9 @@ class PasswordFileInterface implements IFileInterface {
         }
         resp.OnResponse(null);
     }
+
+    @Override
+    public String GetMethodName() {
+        return fMethodName;
+    }
 }

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

@@ -1,14 +1,20 @@
 package info.knacki.pass.io;
 
+import android.content.res.Resources;
+
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 
+import info.knacki.pass.R;
+
 class RawFileInterface implements IFileInterface {
     private static File fFile;
+    private final String fMethodName;
 
-    RawFileInterface(File f) {
+    RawFileInterface(Resources r, File f) {
         fFile = f;
+        fMethodName = r.getString(R.string.pref_enctype_title_none);
     }
 
     @Override
@@ -33,4 +39,9 @@ class RawFileInterface implements IFileInterface {
             resp.OnError(e.getMessage(), e);
         }
     }
+
+    @Override
+    public String GetMethodName() {
+        return fMethodName;
+    }
 }

+ 34 - 8
app/src/main/java/info/knacki/pass/settings/ui/SettingsActivity.java

@@ -22,7 +22,6 @@ 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;
@@ -32,8 +31,10 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.ArrayList;
+import java.io.InputStream;
+import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -48,9 +49,7 @@ 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;
 
@@ -163,6 +162,21 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
 
     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
     public static class EncryptionPreferenceFragment extends PreferenceFragment {
+        String[] fEncryptionMethodNames;
+
+        protected int GetEncryptionMethodNames(SettingsManager.EncryptionType selectedType) {
+            Collection<Map.Entry<SettingsManager.EncryptionType, String>> encMethods = FileInterfaceFactory.GetEncryptionMethodNames(getResources());
+            fEncryptionMethodNames = new String[encMethods.size()];
+            int i =0;
+            int selectedIndex = -1;
+            for (Map.Entry<SettingsManager.EncryptionType, String> method: encMethods) {
+                if (selectedIndex == -1 && method.getKey().equals(selectedType))
+                    selectedIndex = i;
+                fEncryptionMethodNames[i++] = method.getValue();
+            }
+            return selectedIndex;
+        }
+
         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
@@ -170,7 +184,12 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             setHasOptionsMenu(true);
 
             ListPreference encTypePref = (ListPreference) findPreference(getResources().getString(R.string.id_enctype));
-            final int encTypeIndex = SettingsManager.GetDefaultEncryptionType(getActivity()).IntValue();
+            final int encTypeIndex = GetEncryptionMethodNames(SettingsManager.GetDefaultEncryptionType(getActivity()));
+            encTypePref.setEntries(fEncryptionMethodNames);
+            String[] encValues = new String[fEncryptionMethodNames.length];
+            for (int i =0; i < fEncryptionMethodNames.length; ++i)
+                encValues[i] = "" +i;
+            encTypePref.setEntryValues(encValues);
             encTypePref.setSummary(encTypePref.getEntries()[encTypeIndex]);
             encTypePref.setValueIndex(encTypeIndex);
             encTypePref.setOnPreferenceChangeListener((preference, o) -> {
@@ -180,7 +199,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                 return true;
             });
 
-            SwitchPreference fingerPref = (SwitchPreference) findPreference(getString(R.string.pref_enctype_fingerprint));
+            SwitchPreference fingerPref = (SwitchPreference) findPreference(getString(R.string.id_pref_enctype_fingerprint));
             fingerPref.setChecked(SettingsManager.IsFingerprintEnabled(getActivity()));
             fingerPref.setOnPreferenceChangeListener((preference, o) -> {
                 SettingsManager.SetFingerprintEnabled(getActivity(), (Boolean) o);
@@ -502,7 +521,9 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             try {
                 outFile.createNewFile();
                 FileOutputStream fout = new FileOutputStream(outFile);
-                FileUtils.pipe(SettingsManager.GetGPGKeyContent(getActivity()), fout);
+                InputStream keyContent = SettingsManager.GetGPGKeyContent(getActivity());
+                if (keyContent != null)
+                    FileUtils.pipe(keyContent, fout);
                 fout.close();
             } catch (IOException e) {
                 Toast.makeText(getActivity(), "Cannot prepare key for sharing: " + e.getMessage(), Toast.LENGTH_LONG).show();
@@ -538,7 +559,12 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
         public void onActivityResult(int requestCode, int resultCode, Intent data) {
             if (requestCode == ACTIVITY_REQUEST_CODE_BROWSEGPG && resultCode == RESULT_OK) {
                 try {
-                    boolean gpgImportStatus = SettingsManager.SetGPGKeyContent(getActivity(), GetFileName(data.getData()), getActivity().getContentResolver().openInputStream(data.getData()));
+                    final Uri intentData = data.getData();
+                    boolean gpgImportStatus;
+                    if (intentData != null)
+                        gpgImportStatus = SettingsManager.SetGPGKeyContent(getActivity(), GetFileName(intentData), getActivity().getContentResolver().openInputStream(intentData));
+                    else
+                        gpgImportStatus = false;
                     Toast.makeText(getActivity(), getResources().getString(gpgImportStatus ? R.string.gpg_import_ok : R.string.gpg_import_ko), Toast.LENGTH_LONG).show();
                 } catch (FileNotFoundException e) {
                     Toast.makeText(getActivity(), getResources().getString(R.string.filenotfound), Toast.LENGTH_LONG).show();

+ 102 - 0
app/src/main/java/info/knacki/pass/ui/EncryptionInformationActivity.java

@@ -0,0 +1,102 @@
+package info.knacki.pass.ui;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.AppCompatRadioButton;
+import android.support.v7.widget.AppCompatTextView;
+import android.widget.RadioGroup;
+
+import java.io.File;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import info.knacki.pass.R;
+import info.knacki.pass.io.FileInterfaceFactory;
+import info.knacki.pass.io.FileMigrator;
+import info.knacki.pass.io.FileUtils;
+import info.knacki.pass.io.IFileInterface;
+import info.knacki.pass.io.OnResponseListener;
+import info.knacki.pass.settings.SettingsManager;
+import info.knacki.pass.ui.alertPrompt.AlertPromptGenerator;
+import info.knacki.pass.ui.alertPrompt.views.TextView;
+import info.knacki.pass.ui.passwordPicker.PasswordPickerFactory;
+
+public class EncryptionInformationActivity extends AppCompatActivity {
+    public static final String FILE_PATH = EncryptionInformationActivity.class.getName() +"FILE_PATH";
+    private static final Logger log = Logger.getLogger(EncryptionInformationActivity.class.getName());
+    private File fEncrypted;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        String filePath = getIntent().getStringExtra(FILE_PATH);
+        if (filePath == null) {
+            finish();
+            return;
+        }
+        fEncrypted = new File(filePath);
+        setContentView(R.layout.activity_encryption_infos);
+
+        findViewById(R.id.change_encmethod).setOnClickListener((v) -> {
+            RadioGroup radioGroup = new RadioGroup(EncryptionInformationActivity.this);
+            final SettingsManager.EncryptionType prevEncType = FileInterfaceFactory.GetEncryptionType(fEncrypted);
+
+            for (Map.Entry<SettingsManager.EncryptionType, String> enc: FileInterfaceFactory.GetEncryptionMethodNames(getResources())) {
+                AppCompatRadioButton radioButton = new AppCompatRadioButton(this);
+                radioButton.setId(enc.getKey().IntValue());
+                radioButton.setText(enc.getValue());
+                radioGroup.addView(radioButton);
+                if (prevEncType.equals(enc.getKey()))
+                    radioGroup.check(radioButton.getId());
+            }
+            AlertPromptGenerator.StaticMake(EncryptionInformationActivity.this)
+                    .setView(radioGroup)
+                    .setNegativeButton(R.string.cancel, null)
+                    .setPositiveButton(R.string.change, (dialogInterface, view) -> {
+                        if (radioGroup.getCheckedRadioButtonId() != prevEncType.IntValue()) {
+                            String path = fEncrypted.getAbsolutePath();
+                            final String previousExtension = FileInterfaceFactory.GetExtension(prevEncType);
+                            final SettingsManager.EncryptionType newEncType = SettingsManager.EncryptionType.FromInt(radioGroup.getCheckedRadioButtonId());
+                            if (path.endsWith(previousExtension))
+                                path = path.substring(0, path.length() -previousExtension.length());
+                            path += FileInterfaceFactory.GetExtension(newEncType);
+                            File fileTo = new File(path);
+                            log.info("Migrating from " +fEncrypted.getAbsolutePath() +" to " +path);
+                            FileMigrator.MigratePassword(EncryptionInformationActivity.this, fEncrypted, fileTo, PasswordPickerFactory.GetPasswordPicker(EncryptionInformationActivity.this), new OnResponseListener<Void>() {
+                                @Override
+                                public void OnResponse(Void result) {
+                                    fEncrypted = fileTo;
+                                    FillView();
+                                }
+
+                                @Override
+                                public void OnError(String msg, Throwable e) {
+
+                                }
+                            }, true);
+                        }
+                    })
+                    .setTitle(R.string.change_encmethod)
+                    .show();
+        });
+        findViewById(R.id.remove).setOnClickListener((view) -> {
+            AlertPromptGenerator.StaticMake(this)
+                    .setTitle(R.string.are_you_sure)
+                    .setView(new TextView(this).SetText(String.format(getString(R.string.about_to_rm_file), fEncrypted.getName())))
+                    .setPositiveButton(R.string.ok, (dialogInterface, v) -> {
+                        fEncrypted.delete();
+                        EncryptionInformationActivity.this.finish();
+                    })
+                    .setNegativeButton(R.string.cancel, null)
+                    .show();
+        });
+        FillView();
+    }
+
+    private void FillView() {
+        setTitle(FileUtils.TrimExtension(fEncrypted.getName()));
+        IFileInterface f = FileInterfaceFactory.GetFileInterface(this, null, fEncrypted);
+        ((AppCompatTextView) findViewById(R.id.filepath)).setText(fEncrypted.getName());
+        ((AppCompatTextView) findViewById(R.id.enc_method)).setText(f.GetMethodName());
+    }
+}

+ 7 - 0
app/src/main/java/info/knacki/pass/ui/MainActivity.java

@@ -260,4 +260,11 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
             }
         });
     }
+
+    @Override
+    public void OnEncryptionInfos(File f) {
+        Intent i = new Intent(this, EncryptionInformationActivity.class);
+        i.putExtra(EncryptionInformationActivity.FILE_PATH, f.getAbsolutePath());
+        startActivity(i);
+    }
 }

+ 1 - 0
app/src/main/java/info/knacki/pass/ui/passwordList/PasswordEditListener.java

@@ -5,5 +5,6 @@ import java.io.File;
 public interface PasswordEditListener extends PasswordClickListener {
     void OnRemovePassword(File f);
     void OnRemoveDirectory(File f);
+    void OnEncryptionInfos(File f);
     void OnCopyToClipboard(File f);
 }

+ 2 - 8
app/src/main/java/info/knacki/pass/ui/passwordList/PasswordView.java

@@ -7,6 +7,7 @@ import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import info.knacki.pass.R;
+import info.knacki.pass.io.FileUtils;
 
 public class PasswordView extends LinearLayout {
     public final static int ICON_SIZE = 128;
@@ -19,13 +20,6 @@ public class PasswordView extends LinearLayout {
     public static final int TYPE_PARENT = 2;
     public static final int TYPE_PASSWORD = 4;
 
-    protected static String trimExtension(String in) {
-        int lastIndex = in.lastIndexOf('.');
-        if (lastIndex >= Math.max(in.length() -5, 0))
-            return in.substring(0, lastIndex);
-        return in;
-    }
-
     public PasswordView(Context ctx, int type, String name) {
         super(ctx);
         fFullname = name;
@@ -43,7 +37,7 @@ public class PasswordView extends LinearLayout {
         vName = new TextView(ctx);
         vName.setHeight(ICON_SIZE);
         vName.setGravity(Gravity.CENTER_VERTICAL);
-        vName.setText(trimExtension(name));
+        vName.setText(FileUtils.TrimExtension(name));
         addView(vName);
         setClickable(true);
     }

+ 26 - 0
app/src/main/res/layout/activity_encryption_infos.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<GridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:rowCount="4"
+    android:columnCount="2">
+    <android.support.v7.widget.AppCompatTextView android:text="@string/path"
+        android:paddingLeft="@dimen/fab_margin" android:paddingRight="@dimen/fab_margin"
+        android:layout_row="0" android:layout_column="0" />
+    <android.support.v7.widget.AppCompatTextView android:id="@+id/filepath"
+        android:layout_row="0" android:layout_column="1" />
+
+    <android.support.v7.widget.AppCompatTextView android:text="@string/encryption_method"
+        android:paddingLeft="@dimen/fab_margin" android:paddingRight="@dimen/fab_margin"
+        android:layout_row="1" android:layout_rowSpan="2" android:layout_column="0" />
+    <android.support.v7.widget.AppCompatTextView android:id="@+id/enc_method"
+        android:layout_row="1" android:layout_column="1" />
+    <android.support.v7.widget.AppCompatButton android:id="@+id/change_encmethod" android:text="@string/change"
+        android:layout_row="2" android:layout_column="1" />
+
+    <android.support.v7.widget.AppCompatTextView android:text="@string/remove"
+        android:paddingLeft="@dimen/fab_margin" android:paddingRight="@dimen/fab_margin"
+        android:layout_row="3" android:layout_column="0" />
+    <android.support.v7.widget.AppCompatButton android:id="@+id/remove" android:text="@string/remove"
+        android:layout_row="3" android:layout_column="1" />
+</GridLayout>

+ 3 - 2
app/src/main/res/menu/context_file_menu.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
-<item android:id="@+id/remove" android:title="@string/remove"/>
-<item android:id="@+id/clipboard" android:title="@string/copy_clipboard"/>
+    <item android:id="@+id/clipboard" android:title="@string/copy_clipboard"/>
+    <item android:id="@+id/encryption_infos" android:title="@string/encryption_infos"/>
+    <item android:id="@+id/remove" android:title="@string/remove"/>
 </menu>

+ 12 - 5
app/src/main/res/values-fr/lang.xml

@@ -19,6 +19,8 @@
     <string name="cancelled">Annulé</string>
     <string name="ChangeKeyboard">Revenir au clavier</string>
     <string name="settings">Configuration</string>
+    <string name="encryption_infos">Méthode de chiffrement</string>
+    <string name="encryption_method">Méthode de chiffrement</string>
     <string name="pref_title_system_keyboard_settings">Paramètres système du clavier</string>
     <string name="pref_header_general">Général</string>
     <string name="pref_header_Encryption">Chiffrement</string>
@@ -34,11 +36,9 @@
     <string name="pref_vcs_git_username_title">Nom d\'utilisateur</string>
     <string name="pref_vcs_git_useremail_title">Adresse email</string>
     <string name="pref_header_GPG">Configuration de GPG</string>
-    <array name="pref_enctype_title">
-        <item>Pas de chiffrement</item>
-        <item>GPG</item>
-        <item>Mot de passe</item>
-    </array>
+    <string name="pref_enctype_title_none">Pas de chiffrement</string>
+    <string name="pref_enctype_title_gpg">GPG</string>
+    <string name="pref_enctype_title_password">Mot de passe</string>
     <string name="pref_vcs_title">Autoriser la gestion de version</string>
     <string name="pref_vcs_enable">pref_vcs_enable</string>
     <string name="pref_vcs_list_title">Outil de gestion de version</string>
@@ -62,6 +62,7 @@
     <string name="ok">OK</string>
     <string name="are_you_sure">Êtes-vous sur.e ?</string>
     <string name="about_to_rm_directory">Vous allez supprimer tout les mots de passes dans le dossier</string>
+    <string name="about_to_rm_file">Vous allez supprimer "%s"</string>
     <string name="conflictingFiles">Résolution des conflits</string>
     <string name="useMine">Utiliser ma version</string>
     <string name="useTheir">Utiliser leur version</string>
@@ -78,6 +79,9 @@
     <string name="yes">Oui</string>
     <string name="no">Non</string>
     <string name="unprotected_key">Cette clé n\'est pas protégée par un mot de passe, Voulez-vous continuer ?</string>
+    <string name="unprotected_key_short">Clé non protégée</string>
+    <string name="protected_key">Protégé par un mot de passe</string>
+    <string name="unset_gpg_key">Clé GPG manquante</string>
     <string name="pref_header_gpg_export">Export GPG key</string>
     <string name="sync">Synchroniser</string>
     <string name="close">Fermer</string>
@@ -90,4 +94,7 @@
     <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>
+    <string name="path">Fichier</string>
+    <string name="change">Changer</string>
+    <string name="change_encmethod">Changer la méthode de chiffrement</string>
 </resources>

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

@@ -4,4 +4,5 @@
     <dimen name="big_icon_height">64dp</dimen>
     <dimen name="keyboard_min_height">375px</dimen>
     <dimen name="keyboard_max_height">664px</dimen>
+    <dimen name="fab_margin">16dp</dimen>
 </resources>

+ 12 - 5
app/src/main/res/values/lang.xml

@@ -19,16 +19,16 @@
     <string name="cancelled">Cancelled</string>
     <string name="ChangeKeyboard">Change keyboard</string>
     <string name="settings">Settings</string>
+    <string name="encryption_infos">Encryption</string>
+    <string name="encryption_method">Encryption Method</string>
     <string name="pref_title_system_keyboard_settings">Keyboard system settings</string>
     <string name="pref_header_general">General</string>
     <string name="pref_header_Encryption">Encryption</string>
     <string name="pref_header_VCS">VCS</string>
     <string name="pref_title_enctype">Encryption type</string>
-    <array name="pref_enctype_title">
-        <item>No encryption</item>
-        <item>GPG</item>
-        <item>Password</item>
-    </array>
+    <string name="pref_enctype_title_none">No encryption</string>
+    <string name="pref_enctype_title_gpg">GPG</string>
+    <string name="pref_enctype_title_password">Password</string>
     <string name="pref_vcs_title">Enable password versioning</string>
     <string name="pref_vcs_enable">pref_vcs_enable</string>
     <string name="pref_vcs_list_title">Versioning tool</string>
@@ -62,6 +62,7 @@
     <string name="ok">OK</string>
     <string name="are_you_sure">Are you sure ?</string>
     <string name="about_to_rm_directory">You are about to remove all password from this folder</string>
+    <string name="about_to_rm_file">You are about to remove "%s"</string>
     <string name="conflictingFiles">Solve conflicts</string>
     <string name="useMine">Use mine</string>
     <string name="useTheir">Use their</string>
@@ -78,6 +79,9 @@
     <string name="yes">Yes</string>
     <string name="no">No</string>
     <string name="unprotected_key">This key is not protected with a password, do you want to continue ?</string>
+    <string name="unprotected_key_short">Not password-protected</string>
+    <string name="protected_key">Password-protected</string>
+    <string name="unset_gpg_key">GPG key not set</string>
     <string name="pref_header_gpg_export">Export GPG key</string>
     <string name="sync">Synchronize</string>
     <string name="close">Close</string>
@@ -90,4 +94,7 @@
     <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>
+    <string name="path">File</string>
+    <string name="change">Change</string>
+    <string name="change_encmethod">Change encryption method</string>
 </resources>

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

@@ -5,12 +5,15 @@
         <item>1</item>
         <item>2</item>
     </string-array>
+    <integer name="method_raw">0</integer>
+    <integer name="method_GPG">1</integer>
+    <integer name="method_pass">2</integer>
     <string-array name="pref_vcs_list_values">
         <item>0</item>
     </string-array>
     <string name="id_softSettings">id_softSettings</string>
     <string name="id_enctype">id_enctype</string>
-    <string name="pref_enctype_fingerprint">pref_enctype_fingerprint</string>
+    <string name="id_pref_enctype_fingerprint">id_pref_enctype_fingerprint</string>
     <string name="id_vcs_enable">id_vcs_enable</string>
     <string name="id_vcs_list">id_vcs_list</string>
     <string name="id_vcs_git_url">id_vcs_git_url</string>

+ 9 - 0
app/src/main/res/values/styles.xml

@@ -8,4 +8,13 @@
         <item name="colorAccent">@color/colorAccent</item>
     </style>
 
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+    </style>
+
+    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
 </resources>

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

@@ -5,8 +5,6 @@
     <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
     <ListPreference
         android:defaultValue="180"
-        android:entries="@array/pref_enctype_title"
-        android:entryValues="@array/pref_enctype_values"
         android:key="@string/id_enctype"
         android:negativeButtonText="@null"
         android:positiveButtonText="@null"
@@ -18,7 +16,7 @@
         android:fragment="info.knacki.pass.settings.ui.SettingsActivity$PasswordPreferenceFragment"
         android:title="@string/pref_header_password" />
     <SwitchPreference
-        android:key="@string/pref_enctype_fingerprint"
+        android:key="@string/id_pref_enctype_fingerprint"
         android:title="@string/pushfinger"
         android:icon="@drawable/ic_fingerprint" />
 </PreferenceScreen>