Эх сурвалжийг харах

Introduce rewritten auto upload

Mario Danic 8 жил өмнө
parent
commit
b5a2adf44b

+ 1 - 1
build.gradle

@@ -48,7 +48,7 @@ dependencies {
     compile "com.android.support:appcompat-v7:${supportLibraryVersion}"
     compile 'com.getbase:floatingactionbutton:1.10.1'
     compile 'com.google.code.findbugs:annotations:2.0.1'
-
+    compile group: 'commons-io', name: 'commons-io', version: '2.4'
 
     /// dependencies for local unit tests
     testCompile 'junit:junit:4.12'

+ 1 - 1
src/com/owncloud/android/datamodel/OCFile.java

@@ -262,7 +262,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
             return null;
         }
         if (mExposedFileUri == null) {
-            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                 // TODO - use FileProvider with any Android version, with deeper testing -> 2.2.0
                 mExposedFileUri = Uri.parse(
                     ContentResolver.SCHEME_FILE + "://" + WebdavUtils.encodePath(mLocalPath)

+ 1 - 1
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java

@@ -58,7 +58,7 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
             Log_OC.d(TAG, "Received: " + intent.getAction());
             if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
                 handleNewPictureAction(context, intent);

+ 123 - 0
src/com/owncloud/android/services/FileAlterationMagicListener.java

@@ -0,0 +1,123 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ * <p>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.services;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.PersistableBundle;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.datamodel.SyncedFolder;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import org.apache.commons.io.monitor.FileAlterationListener;
+import org.apache.commons.io.monitor.FileAlterationObserver;
+
+import java.io.File;
+import java.util.Date;
+
+/**
+ * Magical file alteration listener
+ */
+
+public class FileAlterationMagicListener implements FileAlterationListener {
+
+    public static final String TAG = "FileAlterationMagicListener";
+
+    private Context context;
+
+    private SyncedFolder syncedFolder;
+
+    public FileAlterationMagicListener(SyncedFolder syncedFolder) {
+        super();
+
+        context = MainApp.getAppContext();
+        this.syncedFolder = syncedFolder;
+    }
+
+    @Override
+    public void onStart(FileAlterationObserver observer) {
+
+    }
+
+    @Override
+    public void onDirectoryCreate(File directory) {
+
+    }
+
+    @Override
+    public void onDirectoryChange(File directory) {
+
+    }
+
+    @Override
+    public void onDirectoryDelete(File directory) {
+
+    }
+
+    @Override
+    public void onFileCreate(File file) {
+        PersistableBundle bundle = new PersistableBundle();
+        // TODO extract
+        bundle.putString(SyncedFolderJobService.LOCAL_PATH, file.getAbsolutePath());
+        bundle.putString(SyncedFolderJobService.REMOTE_PATH, FileStorageUtils.getInstantUploadFilePath(
+                syncedFolder.getRemotePath(), file.getName(),
+                new Date().getTime(),
+                syncedFolder.getSubfolderByDate()));
+        bundle.putString(SyncedFolderJobService.ACCOUNT, syncedFolder.getAccount());
+        bundle.putInt(SyncedFolderJobService.UPLOAD_BEHAVIOUR, syncedFolder.getUploadAction());
+
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        Long date = new Date().getTime();
+        JobInfo job = new JobInfo.Builder(
+                date.intValue(),
+                new ComponentName(context, SyncedFolderJobService.class))
+                .setRequiresCharging(syncedFolder.getChargingOnly())
+                .setMinimumLatency(10000)
+                .setRequiredNetworkType(syncedFolder.getWifiOnly() ? JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY)
+                .setExtras(bundle)
+                .setPersisted(true)
+                .build();
+
+        Integer result = js.schedule(job);
+        if (result <= 0) {
+            Log_OC.d(TAG, "Job failed to start: " + result);
+        }
+
+    }
+
+    @Override
+    public void onFileChange(File file) {
+    }
+
+    @Override
+    public void onFileDelete(File file) {
+
+    }
+
+    @Override
+    public void onStop(FileAlterationObserver observer) {
+
+    }
+}

+ 1 - 1
src/com/owncloud/android/services/SyncedFolderJobService.java

@@ -35,7 +35,6 @@ import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.operations.UploadFileOperation;
-import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.MimeTypeUtil;
 
 import java.io.File;
@@ -72,6 +71,7 @@ public class SyncedFolderJobService extends JobService {
             String mimeType = MimeTypeUtil.getBestMimeTypeByFilename(file.getAbsolutePath());
 
             FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
+
             requester.uploadNewFile(
                     context,
                     account,

+ 0 - 78
src/com/owncloud/android/services/observer/SyncedFolderObserver.java

@@ -1,78 +0,0 @@
-package com.owncloud.android.services.observer;
-
-import android.annotation.TargetApi;
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Build;
-import android.os.FileObserver;
-import android.os.PersistableBundle;
-import android.util.Log;
-
-import com.owncloud.android.MainApp;
-import com.owncloud.android.datamodel.SyncedFolder;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.services.SyncedFolderJobService;
-import com.owncloud.android.utils.FileStorageUtils;
-import com.owncloud.android.utils.RecursiveFileObserver;
-
-import java.io.File;
-import java.util.Date;
-
-class SyncedFolderObserver extends RecursiveFileObserver {
-
-    private Context context;
-
-    public static final String TAG = "SyncedFolderObserver";
-    private SyncedFolder syncedFolder;
-
-
-    public SyncedFolderObserver(SyncedFolder syncedFolder) {
-        super(syncedFolder.getLocalPath(), FileObserver.CREATE + FileObserver.MOVED_TO);
-
-        context = MainApp.getAppContext();
-        this.syncedFolder = syncedFolder;
-        Log_OC.d("SyncedFolderObserver", "Started watching: " + syncedFolder.getLocalPath());
-    }
-
-
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    @Override
-    public void onEvent(int event, String path) {
-        Log.d(TAG, "Event: " + event + " Path: " + path);
-
-        File temp = new File(path);
-
-        // do not upload "null"-files, test if file exists and is a real file
-        if (!temp.getName().equalsIgnoreCase("null") && temp.isFile() && !temp.getName().endsWith(".tmp")) {
-            PersistableBundle bundle = new PersistableBundle();
-            // TODO extract
-            bundle.putString(SyncedFolderJobService.LOCAL_PATH, path);
-            bundle.putString(SyncedFolderJobService.REMOTE_PATH, FileStorageUtils.getInstantUploadFilePath(
-                                                                 syncedFolder.getRemotePath(), temp.getName(),
-                                                                 new Date().getTime(),
-                                                                 syncedFolder.getSubfolderByDate()));
-            bundle.putString(SyncedFolderJobService.ACCOUNT, syncedFolder.getAccount());
-            bundle.putInt(SyncedFolderJobService.UPLOAD_BEHAVIOUR, syncedFolder.getUploadAction());
-
-            JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-
-            Long date = new Date().getTime();
-            JobInfo job = new JobInfo.Builder(
-                    date.intValue(),
-                    new ComponentName(context, SyncedFolderJobService.class))
-                    .setRequiresCharging(syncedFolder.getChargingOnly())
-                    .setMinimumLatency(10000)
-                    .setRequiredNetworkType(syncedFolder.getWifiOnly() ? JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY)
-                    .setExtras(bundle)
-                    .setPersisted(true)
-                    .build();
-
-            Integer result = js.schedule(job);
-            if (result <= 0) {
-                Log_OC.d(TAG, "Job failed to start: " + result);
-            }
-        }
-    }
-}

+ 85 - 13
src/com/owncloud/android/services/observer/SyncedFolderObserverService.java

@@ -1,3 +1,24 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * At least some parts are Copyright (C) 2017 Mario Danic
+ * Those parts are under the following licence:
+ * <p>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
 package com.owncloud.android.services.observer;
 
 import android.app.Service;
@@ -9,14 +30,22 @@ import com.owncloud.android.MainApp;
 import com.owncloud.android.datamodel.SyncedFolder;
 import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.services.FileAlterationMagicListener;
+
+import org.apache.commons.io.monitor.FileAlterationMonitor;
+import org.apache.commons.io.monitor.FileAlterationObserver;
 
+import java.io.File;
+import java.io.FileFilter;
 import java.util.HashMap;
 
 public class SyncedFolderObserverService extends Service {
     private static final String TAG = "SyncedFolderObserverService";
     private SyncedFolderProvider mProvider;
-    private HashMap<String, SyncedFolderObserver> syncedFolderMap = new HashMap<>();
+    private HashMap<String, FileAlterationObserver> syncedFolderMap = new HashMap<>();
     private final IBinder mBinder = new SyncedFolderObserverBinder();
+    private FileAlterationMonitor monitor;
+    private FileFilter fileFilter;
 
     @Override
     public void onCreate() {
@@ -25,13 +54,32 @@ public class SyncedFolderObserverService extends Service {
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
+        monitor = new FileAlterationMonitor();
+        fileFilter = new FileFilter() {
+            @Override
+            public boolean accept(File pathname) {
+                if (pathname.getName().startsWith(".")) {
+                    return false;
+                }
+
+                return true;
+            }
+        };
         Log_OC.d(TAG, "start");
         for (SyncedFolder syncedFolder : mProvider.getSyncedFolders()) {
-            if (syncedFolder.isEnabled()) {
+            if (syncedFolder.isEnabled() && !syncedFolderMap.containsKey(syncedFolder.getLocalPath())) {
                 Log_OC.d(TAG, "start observer: " + syncedFolder.getLocalPath());
-                SyncedFolderObserver observer = new SyncedFolderObserver(syncedFolder);
-                observer.startWatching();
-                syncedFolderMap.put(syncedFolder.getLocalPath(), observer);
+
+                FileAlterationObserver observer = new FileAlterationObserver(syncedFolder.getLocalPath(), fileFilter);
+                observer.addListener(new FileAlterationMagicListener(syncedFolder));
+                monitor.addObserver(observer);
+                try {
+                    monitor.start();
+                    syncedFolderMap.put(syncedFolder.getLocalPath(), observer);
+                } catch (Exception e) {
+                    Log_OC.d(TAG, "Something went very wrong at onStartCommand");
+                }
+
             }
         }
 
@@ -40,8 +88,13 @@ public class SyncedFolderObserverService extends Service {
 
     @Override
     public void onDestroy() {
-        for (SyncedFolderObserver observer : syncedFolderMap.values()) {
-            observer.stopWatching();
+        for (FileAlterationObserver observer : syncedFolderMap.values()) {
+            monitor.removeObserver(observer);
+            try {
+                observer.destroy();
+            } catch (Exception e) {
+                Log_OC.d(TAG, "Something went very wrong at onDestroy");
+            }
             syncedFolderMap.remove(observer);
         }
     }
@@ -49,23 +102,42 @@ public class SyncedFolderObserverService extends Service {
     /**
      * Restart oberver if it is enabled
      * If syncedFolder exists already, use it, otherwise create new observer
+     *
      * @param syncedFolder
      */
-    public void restartObserver(SyncedFolder syncedFolder){
+
+    public void restartObserver(SyncedFolder syncedFolder) {
+        FileAlterationObserver fileAlterationObserver;
         if (syncedFolderMap.containsKey(syncedFolder.getLocalPath())) {
             Log_OC.d(TAG, "stop observer: " + syncedFolder.getLocalPath());
-            syncedFolderMap.get(syncedFolder.getLocalPath()).stopWatching();
+            fileAlterationObserver = syncedFolderMap.get(syncedFolder.getLocalPath());
+            monitor.removeObserver(fileAlterationObserver);
+            try {
+                fileAlterationObserver.destroy();
+            } catch (Exception e) {
+                Log_OC.d(TAG, "Something went very wrong at onDestroy");
+            }
             syncedFolderMap.remove(syncedFolder.getLocalPath());
         }
 
         if (syncedFolder.isEnabled()) {
             Log_OC.d(TAG, "start observer: " + syncedFolder.getLocalPath());
             if (syncedFolderMap.containsKey(syncedFolder.getLocalPath())) {
-                syncedFolderMap.get(syncedFolder.getLocalPath()).startWatching();
+                fileAlterationObserver = syncedFolderMap.get(syncedFolder.getLocalPath());
+                if (fileAlterationObserver.getListeners() == null) {
+                    fileAlterationObserver.addListener(new FileAlterationMagicListener(syncedFolder));
+                }
+                monitor.addObserver(fileAlterationObserver);
             } else {
-                SyncedFolderObserver observer = new SyncedFolderObserver(syncedFolder);
-                observer.startWatching();
-                syncedFolderMap.put(syncedFolder.getLocalPath(), observer);
+                fileAlterationObserver = new FileAlterationObserver(syncedFolder.getLocalPath(), fileFilter);
+                fileAlterationObserver.addListener(new FileAlterationMagicListener(syncedFolder));
+                monitor.addObserver(fileAlterationObserver);
+                try {
+                    syncedFolderMap.put(syncedFolder.getLocalPath(), fileAlterationObserver);
+                } catch (Exception e) {
+                    Log_OC.d(TAG, "Something went very wrong on RestartObserver");
+                }
+
             }
         }
     }

+ 1 - 1
src/com/owncloud/android/ui/activity/DrawerActivity.java

@@ -176,7 +176,7 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
             setupQuotaElement();
 
             // show folder sync menu item only for Android 7+
-            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
                 mNavigationView.getMenu().removeItem(R.id.nav_folder_sync);
             }
         }

+ 2 - 2
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -256,8 +256,8 @@ public class FileDisplayActivity extends HookActivity
      * Opens a pop up info for the new instant upload and disabled the old instant upload.
      */
     private void upgradeNotificationForInstantUpload() {
-        // check for Android 7+ if legacy instant upload is activated --> disable + show info
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
+        // check for Android 5+ if legacy instant upload is activated --> disable + show info
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
                 (PreferenceManager.instantPictureUploadEnabled(this) ||
                         PreferenceManager.instantPictureUploadEnabled(this))) {
 

+ 2 - 2
src/com/owncloud/android/ui/activity/Preferences.java

@@ -368,8 +368,8 @@ public class Preferences extends PreferenceActivity
 
         mPrefInstantUploadCategory = (PreferenceCategory) findPreference("instant_uploading_category");
 
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
-            // Instant upload via preferences on pre Android Nougat
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+            // Instant upload via preferences on pre pre Lollipop
             mPrefInstantUploadPath = findPreference("instant_upload_path");
             if (mPrefInstantUploadPath != null) {
 

+ 0 - 109
src/com/owncloud/android/utils/RecursiveFileObserver.java

@@ -1,109 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2012 Bartek Przybylski
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.utils;
-
-import android.os.FileObserver;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Stack;
-
-public class RecursiveFileObserver extends FileObserver {
-
-    private final List<SingleFileObserver> mObservers =  new ArrayList<>();
-    private boolean watching = false;
-    private String mPath;
-    private int mMask;
-    
-    public RecursiveFileObserver(String path) {
-        this(path, ALL_EVENTS);
-    }
-    
-    public RecursiveFileObserver(String path, int mask) {
-        super(path, mask);
-        mPath = path;
-        mMask = mask;
-    }
-
-    @Override
-    public void startWatching() {
-        if (watching) {
-            return;
-        }
-        watching = true;
-        final Stack<String> stack = new Stack<String>();
-        stack.push(mPath);
-        
-        while (!stack.empty()) {
-            String parent = stack.pop();
-            mObservers.add(new SingleFileObserver(parent, mMask));
-            File path = new File(parent);
-            File[] files = path.listFiles();
-            if (files == null) {
-                continue;
-            }
-            for (final File file : files) {
-                if (file.isDirectory() && !file.getName().equals(".")
-                        && !file.getName().equals("..")) {
-                    stack.push(file.getPath());
-                }
-            }
-        }
-        for (int i = 0; i < mObservers.size(); i++) {
-            mObservers.get(i).startWatching();
-        }
-    }
-    
-    @Override
-    public void stopWatching() {
-        if (!watching) {
-            return;
-        }
-
-        for (int i = 0; i < mObservers.size(); ++i) {
-            mObservers.get(i).stopWatching();
-        }
-        mObservers.clear();
-        watching = false;
-    }
-    
-    @Override
-    public void onEvent(int event, String path) {
-        
-    }
-    
-    private class SingleFileObserver extends FileObserver {
-        private String mPath;
-
-        SingleFileObserver(String path, int mask) {
-            super(path, mask);
-            mPath = path;
-        }
-        
-        @Override
-        public void onEvent(int event, String path) {
-            String newPath = mPath + "/" + path;
-            RecursiveFileObserver.this.onEvent(event, newPath);
-        } 
-        
-    }
-}