Selaa lähdekoodia

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

isundil 7 vuotta sitten
vanhempi
commit
382b538234

+ 6 - 8
app/src/main/java/info/knacki/pass/git/entities/GitCommit.java

@@ -130,17 +130,15 @@ public class GitCommit implements GitPackable {
                 } while (obj != null);
             } else {
                 GitObject obj = fTree.GetObjectFullPath(relativeFilename);
-                do {
-                    if (obj != null && obj.GetParent() != null) {
+                if (obj != null)
+                    do {
                         obj.GetParent().Remove(obj.fName);
                         fToPack.remove(obj.GetGitPath());
                         obj = obj.GetParent();
-                    }
-                } while (obj instanceof GitObject.GitTree && ((GitObject.GitTree) obj).fItems.size() == 0);
-                if (obj instanceof GitObject.GitTree)
-                    for (String i: fToPack)
-                        if (i.startsWith(relativeFilename +"/"))
-                            fToPack.remove(i);
+                    } while (obj != null && ((GitObject.GitTree) obj).fItems.size() == 0 && obj.GetParent() != null);
+                for (String i: fToPack)
+                    if (i.startsWith(relativeFilename +"/"))
+                        fToPack.remove(i);
                 while (obj != null) {
                     fToPack.add(obj.GetGitPath());
                     obj.fSha1 = null;

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

@@ -57,8 +57,12 @@ public class FileInterfaceFactory {
     }
 
     protected static EncryptionMethodItem GetEncryptionMethod(File f) {
+        String fileName = f.getName();
+        if (PathUtils.IsHidden(f.getAbsolutePath())) {
+            fileName = PathUtils.UnHiddenPath(fileName);
+        }
         for (EncryptionMethodItem i: fEncryptionMethods)
-            if (f.getName().endsWith(i.fFileSuffix))
+            if (fileName.endsWith(i.fFileSuffix))
                 return i;
         log.severe("Unknown Interface for file type " +f.getAbsolutePath());
         throw new NoSuchMethodError();

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

@@ -6,8 +6,11 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.logging.Logger;
 
 public class FileUtils {
+    private static final Logger log = Logger.getLogger(FileUtils.class.getName());
+
     public static void rmdir(File f) {
         if (!f.isDirectory()) {
             f.delete();
@@ -50,4 +53,24 @@ public class FileUtils {
             return in.substring(0, lastIndex);
         return in;
     }
+
+    public static void TrashFile(File f) {
+        if (PathUtils.IsHidden(f.getAbsolutePath())) {
+            if (!f.delete())
+                log.severe("Cannot remove file " +f.getAbsolutePath());
+        } else {
+            final String dest = PathUtils.HiddenPath(f.getAbsolutePath());
+
+            if (!f.renameTo(new File(dest)))
+                log.severe("Cannot rename file " +f.getAbsolutePath() +" to " +dest);
+        }
+    }
+
+    public static void RestoreFile(File f) {
+        if (PathUtils.IsHidden(f.getAbsolutePath())) {
+            final String dest = PathUtils.UnHiddenPath(f.getAbsolutePath());
+            if (!f.renameTo(new File(dest)))
+                log.severe("Cannot rename file " + f.getAbsolutePath() + " to " + dest);
+        }
+    }
 }

+ 14 - 0
app/src/main/java/info/knacki/pass/io/PathUtils.java

@@ -23,4 +23,18 @@ public class PathUtils {
     public static String GetFingerprintFile(Context ctx) {
         return GetAppRootDir(ctx) +"/" +DATA_FINGER_LOCAL;
     }
+
+    public static final String TRASH_SUFFIX = ".trash";
+
+    public static boolean IsHidden(String path) {
+        return path.endsWith(TRASH_SUFFIX);
+    }
+
+    public static String UnHiddenPath(String path) {
+        return IsHidden(path) ? path.substring(0, path.length() -TRASH_SUFFIX.length()) : path;
+    }
+
+    public static String HiddenPath(String path) {
+        return IsHidden(path) ? path : path +TRASH_SUFFIX;
+    }
 }

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

@@ -84,7 +84,7 @@ public class EncryptionInformationActivity extends AppCompatActivity {
                     .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();
+                        FileUtils.TrashFile(fEncrypted);
                         EncryptionInformationActivity.this.finish();
                     })
                     .setNegativeButton(R.string.cancel, null)

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

@@ -24,6 +24,7 @@ import info.knacki.pass.git.GitLocal;
 import info.knacki.pass.git.GitSha1;
 import info.knacki.pass.git.entities.GitCommit;
 import info.knacki.pass.git.entities.GitObject;
+import info.knacki.pass.io.FileUtils;
 import info.knacki.pass.io.OnResponseListener;
 import info.knacki.pass.io.OnStreamResponseListener;
 import info.knacki.pass.io.PathUtils;
@@ -41,6 +42,7 @@ public class GitPullActivity extends AppCompatActivity {
     private GitCommit fHeadCommit;
 
     private void OnMsg(final String msg) {
+        log.info(msg);
         GitPullActivity.this.runOnUiThread(() -> {
             TextView logView = findViewById(R.id.logView);
             logView.append(msg +"\n");
@@ -97,6 +99,8 @@ public class GitPullActivity extends AppCompatActivity {
         Set<String> filesToPush = new HashSet<>();
         HashMap<String, GitObject.GitBlob> conflictingFiles = new HashMap<>();
 
+        OnMsg("Done reading remote tree");
+        OnMsg("Building change list");
         for (Map.Entry<String, GitObject.GitBlob>i: fHeadCommit.GetTree().FindAllBlobs().entrySet()) {
             final String remoteKnownHash = localVersion.GetHash(i.getKey(), "");
             final String remoteHash = GitSha1.BytesToString(i.getValue().GetHash());
@@ -141,7 +145,9 @@ 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 (i.isDirectory()) {
+            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();
@@ -298,7 +304,7 @@ public class GitPullActivity extends AppCompatActivity {
             if (files.peek().getValue() == null) {
                 // remove file
                 String filename = files.pop().getKey();
-                new File(PathUtils.GetPassDir(this) +filename).delete();
+                FileUtils.TrashFile(new File(PathUtils.GetPassDir(this) +filename));
                 localCache.remove(filename);
                 DownloadNext(files, localCache, downloader, resp);
                 log.info("Removed file " +filename);

+ 12 - 2
app/src/main/java/info/knacki/pass/ui/MainActivity.java

@@ -120,7 +120,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
         setContentView(R.layout.activity_pass_list);
 
         setTitle(R.string.title);
-        fPasswordListView = new EditablePasswordListView<>(this, PathUtils.GetPassDir(this));
+        fPasswordListView = new EditablePasswordListView<>(this, PathUtils.GetPassDir(this), false);
         ((ScrollView)findViewById(R.id.passwordListContainer)).addView(fPasswordListView);
 
         requestPermissions();
@@ -143,6 +143,10 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
             case R.id.settings:
                 ShowSettings();
                 break;
+            case R.id.trash:
+                boolean checked = !item.isChecked();
+                item.setChecked(checked);
+                fPasswordListView.SetDisplayHiddenFiles(checked);
             default:
                 log.log(Level.WARNING, "Error: Unknown MenuItem id: " +item.getItemId());
                 return false;
@@ -219,7 +223,7 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
                 .setTitle(R.string.are_you_sure)
                 .setView(new TextView(this).SetText(String.format(getString(R.string.about_to_rm_file), f.getName())))
                 .setPositiveButton(R.string.ok, (dialogInterface, view) -> {
-                    f.delete();
+                    FileUtils.TrashFile(f);
                     fPasswordListView.refresh();
                 })
                 .setNegativeButton(R.string.cancel, null)
@@ -248,6 +252,12 @@ public class MainActivity extends AppCompatActivity implements PasswordEditListe
         }
     }
 
+    @Override
+    public void OnRestorePassword(File f) {
+        FileUtils.RestoreFile(f);
+        fPasswordListView.refresh();
+    }
+
     @Override
     public void OnCopyToClipboard(File f) {
         FileInterfaceFactory.GetFileInterface(this, PasswordPickerFactory.GetPasswordPicker(this), f).ReadFile(new OnResponseListener<String>() {

+ 25 - 2
app/src/main/java/info/knacki/pass/ui/passwordList/EditablePasswordListView.java

@@ -7,12 +7,31 @@ import android.widget.PopupMenu;
 import java.io.File;
 
 import info.knacki.pass.R;
+import info.knacki.pass.io.PathUtils;
 
 public class EditablePasswordListView<T extends Activity & PasswordEditListener> extends PasswordListView<T> implements PopupMenu.OnMenuItemClickListener  {
     private PasswordView fSelectedPassword;
+    private boolean fDisplayHiddenFiles;
 
-    public EditablePasswordListView(T ctx, String rootPath) {
+    public EditablePasswordListView(T ctx, String rootPath, boolean displayHidden) {
         super(ctx, rootPath);
+        fDisplayHiddenFiles = displayHidden;
+    }
+
+    public void SetDisplayHiddenFiles(boolean displayHidden) {
+        fDisplayHiddenFiles = displayHidden;
+        refresh();
+    }
+
+    @Override
+    protected boolean IsHidden(File f) {
+        return (PathUtils.IsHidden(f.getAbsolutePath()) != fDisplayHiddenFiles);
+    }
+
+    protected int GetMenuForFile(FileIdentity f) {
+        if (f.fIsDir)
+            return R.menu.context_dir_menu;
+        return (PathUtils.IsHidden(f.fName)) ? R.menu.context_trashed_menu : R.menu.context_file_menu;
     }
 
     protected void AddContextMenu(final FileIdentity file, final PasswordView pv) {
@@ -21,7 +40,7 @@ public class EditablePasswordListView<T extends Activity & PasswordEditListener>
         pv.setOnLongClickListener(v -> {
             fSelectedPassword = pv;
             PopupMenu menu = new PopupMenu(fClickListener, pv);
-            menu.getMenuInflater().inflate(file.fIsDir ? R.menu.context_dir_menu : R.menu.context_file_menu, menu.getMenu());
+            menu.getMenuInflater().inflate(GetMenuForFile(file), menu.getMenu());
             menu.setOnMenuItemClickListener(EditablePasswordListView.this);
             menu.show();
             return true;
@@ -48,6 +67,10 @@ public class EditablePasswordListView<T extends Activity & PasswordEditListener>
                 }
                 break;
 
+            case R.id.restore:
+                fClickListener.OnRestorePassword(new File(fCurrentDir + "/" + fSelectedPassword.fFullName));
+                break;
+
             case R.id.encryption_information:
                 fClickListener.OnEncryptionInformation(new File(fCurrentDir +"/" +fSelectedPassword.fFullName));
                 break;

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

@@ -4,6 +4,7 @@ import java.io.File;
 
 public interface PasswordEditListener extends PasswordClickListener {
     void OnRemovePassword(File f);
+    void OnRestorePassword(File f);
     void OnRemoveDirectory(File f);
     void OnEncryptionInformation(File f);
     void OnCopyToClipboard(File f);

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

@@ -8,6 +8,8 @@ import java.io.File;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import info.knacki.pass.io.PathUtils;
+
 public class PasswordListView<T extends Context & PasswordClickListener> extends LinearLayout {
     public final String fRootPath;
     public String fCurrentDir;
@@ -75,11 +77,17 @@ public class PasswordListView<T extends Context & PasswordClickListener> extends
         DisplayDir(rootDir);
     }
 
+    protected boolean IsHidden(File f) {
+        return PathUtils.IsHidden(f.getAbsolutePath());
+    }
+
     protected Boolean IsAccessGranted(File f) {
         if ((f.getName().charAt(0) == '.') && !f.getName().equals("..")) {
             return false;
         } else if (fRootPath.equals(f.getAbsolutePath())) {
             return false;
+        } else if (IsHidden(f)) {
+            return false;
         }
         return f.getAbsolutePath().startsWith(fRootPath);
     }

+ 6 - 0
app/src/main/res/menu/context_trashed_menu.xml

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

+ 1 - 0
app/src/main/res/menu/main_menu.xml

@@ -4,6 +4,7 @@
         android:icon="@drawable/ic_sync"
         android:id="@+id/id_sync"
         android:title="@string/sync" />
+    <item android:checkable="true" android:title="@string/trash" android:id="@+id/trash" />
     <item android:title="@string/New">
         <menu>
             <item android:id="@+id/new_folder" android:title="@string/newFolder"/>

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

@@ -74,6 +74,7 @@
     <string name="useMine">Utiliser ma version</string>
     <string name="useTheir">Utiliser leur version</string>
     <string name="remove">Supprimer</string>
+    <string name="restore">Restaurer</string>
     <string name="copy_clipboard">Copier dans le presse-papier</string>
     <string name="copied_clipboard">Mot de passe dans le presse-papier</string>
     <string name="gpg_import_ok">Clé GPG importée avec succès</string>
@@ -104,4 +105,5 @@
     <string name="path">Fichier</string>
     <string name="change">Changer</string>
     <string name="change_encryption_method">Changer la méthode de chiffrement</string>
+    <string name="trash">Poubelle</string>
 </resources>

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

@@ -74,6 +74,7 @@
     <string name="useMine">Use mine</string>
     <string name="useTheir">Use their</string>
     <string name="remove">Remove</string>
+    <string name="restore">Restore</string>
     <string name="copy_clipboard">Copy to clipboard</string>
     <string name="copied_clipboard">Password copied to Clipboard</string>
     <string name="gpg_import_ok">Done importing GPG Key</string>
@@ -104,4 +105,5 @@
     <string name="path">File</string>
     <string name="change">Change</string>
     <string name="change_encryption_method">Change encryption method</string>
+    <string name="trash">Trash</string>
 </resources>