tobiasKaminsky 8 年之前
父节点
当前提交
281f970869

+ 4 - 0
AndroidManifest.xml

@@ -131,6 +131,10 @@
                 android:name="android.content.SyncAdapter"
                 android:resource="@xml/syncadapter_files" />
         </service>
+        <service
+            android:name=".services.SyncedFolderJobService"
+            android:permission="android.permission.BIND_JOB_SERVICE"
+            android:exported="true"/>
 
         <provider
             android:name=".providers.FileContentProvider"

+ 83 - 0
src/com/owncloud/android/datamodel/SyncedFolder.java

@@ -0,0 +1,83 @@
+/**
+ *   Nextcloud Android client application
+ *
+ *   @author Tobias Kaminsky
+ *   Copyright (C) 2016 Tobias Kaminsky
+ *   Copyright (C) 2016 Nextcloud
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ *   License as published by the Free Software Foundation; either
+ *   version 3 of the License, or any later version.
+ *
+ *   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.
+ *
+ *   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.datamodel;
+
+public class SyncedFolder {
+    private long id;
+    private String localPath;
+    private String remotePath;
+    private Boolean wifiOnly;
+    private Boolean chargingOnly;
+    private Boolean subfolderByDate;
+    private String account;
+    private Integer uploadAction;
+    private boolean enabled;
+
+    public SyncedFolder(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
+                        Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled) {
+        this.id = id;
+        this.localPath = localPath;
+        this.remotePath = remotePath;
+        this.wifiOnly = wifiOnly;
+        this.chargingOnly = chargingOnly;
+        this.subfolderByDate = subfolderByDate;
+        this.account = account;
+        this.uploadAction = uploadAction;
+        this.enabled = enabled;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public String getLocalPath() {
+        return localPath;
+    }
+
+    public String getRemotePath() {
+        return remotePath;
+    }
+
+    public Boolean getWifiOnly() {
+        return wifiOnly;
+    }
+
+    public Boolean getChargingOnly() {
+        return chargingOnly;
+    }
+
+    public Boolean getSubfolderByDate() {
+        return subfolderByDate;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public Integer getUploadAction() {
+        return uploadAction;
+    }
+
+    public boolean isEnabled() {
+        return enabled;
+    }
+}

+ 1 - 1
src/com/owncloud/android/db/PreferenceManager.java

@@ -174,7 +174,7 @@ public abstract class PreferenceManager {
         appPreferences.apply();
     }
 
-    private static SharedPreferences getDefaultSharedPreferences(Context context) {
+    public static SharedPreferences getDefaultSharedPreferences(Context context) {
         return android.preference.PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
     }
 }

+ 2 - 0
src/com/owncloud/android/db/ProviderMeta.java

@@ -56,6 +56,8 @@ public class ProviderMeta {
                 + MainApp.getAuthority() + "/capabilities");
         public static final Uri CONTENT_URI_UPLOADS = Uri.parse("content://"
                 + MainApp.getAuthority() + "/uploads");
+        public static final Uri CONTENT_URI_SYNCED_FOLDERS = Uri.parse("content://"
+                + MainApp.getAuthority() + "/synced_folders");
 
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";
         public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";

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

@@ -32,6 +32,7 @@ import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
 import android.support.v4.content.ContextCompat;
 
+import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.db.PreferenceManager;
 import com.owncloud.android.files.services.FileUploader;
@@ -131,12 +132,17 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         new FileUploader.UploadRequester();
 
         int behaviour = getUploadBehaviour(context);
+        Boolean subfolderByDate = com.owncloud.android.db.PreferenceManager.instantPictureUploadPathUseSubfolders(context);
+            SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
+        String uploadPathdef = context.getString(R.string.instant_upload_path);
+        String uploadPath = pref.getString("instant_upload_path", uploadPathdef);
+
         FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
         requester.uploadNewFile(
                 context,
                 account,
                 file_path,
-                FileStorageUtils.getInstantUploadFilePath(context, file_name, date_taken),
+                FileStorageUtils.getInstantUploadFilePath(uploadPath, file_name, date_taken, subfolderByDate),
                 behaviour,
                 mime_type,
                 true,           // create parent folder if not existent

+ 14 - 2
src/com/owncloud/android/providers/FileContentProvider.java

@@ -69,6 +69,7 @@ public class FileContentProvider extends ContentProvider {
     private static final int SHARES = 4;
     private static final int CAPABILITIES = 5;
     private static final int UPLOADS = 6;
+    private static final int SYNCED_FOLDERS = 7;
 
     private static final String TAG = FileContentProvider.class.getSimpleName();
 
@@ -335,6 +336,7 @@ public class FileContentProvider extends ContentProvider {
         mUriMatcher.addURI(authority, "capabilities/#", CAPABILITIES);
         mUriMatcher.addURI(authority, "uploads/", UPLOADS);
         mUriMatcher.addURI(authority, "uploads/#", UPLOADS);
+        mUriMatcher.addURI(authority, "synced_folders", SYNCED_FOLDERS);
 
         return true;
     }
@@ -409,6 +411,13 @@ public class FileContentProvider extends ContentProvider {
                             + uri.getPathSegments().get(1));
                 }
                 break;
+            case SYNCED_FOLDERS:
+                sqlQuery.setTables(ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME);
+                if (uri.getPathSegments().size() > 1) {
+                    sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+                            + uri.getPathSegments().get(1));
+                }
+                break;
             default:
                 throw new IllegalArgumentException("Unknown uri id: " + uri);
         }
@@ -425,6 +434,9 @@ public class FileContentProvider extends ContentProvider {
                 case UPLOADS:
                     order = ProviderTableMeta.UPLOADS_DEFAULT_SORT_ORDER;
                     break;
+                case SYNCED_FOLDERS:
+                    order = ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH;
+                    break;
                 default: // Files
                     order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER;
                     break;
@@ -916,7 +928,7 @@ public class FileContentProvider extends ContentProvider {
                 + ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY + " INTEGER, "      // charging only
                 + ProviderTableMeta.SYNCED_FOLDER_ENABLED + " INTEGER, "            // enabled
                 + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, "  // subfolder by date
-                + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " INTEGER, "            // account
+                + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + "  TEXT, "              // account
                 + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_OPTION + " INTEGER );"     // upload action
         );
 
@@ -930,7 +942,7 @@ public class FileContentProvider extends ContentProvider {
                 + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + ", "      // subfolder by date
                 + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + ", "                // account
                 + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_OPTION + ") "          // upload action
-                + "VALUES ('/sdcard/DCIM/', 'syncTest', 0, 0, 1, 1, 'tobi', 1)");
+                + "VALUES ('/sdcard/DCIM/', '/syncTest', 0, 0, 1, 1, 'tobi', 1)");
     }
 
     /**

+ 97 - 0
src/com/owncloud/android/services/SyncedFolderJobService.java

@@ -0,0 +1,97 @@
+/**
+ *   Nextcloud Android client application
+ *
+ *   @author Tobias Kaminsky
+ *   Copyright (C) 2016 Tobias Kaminsky
+ *   Copyright (C) 2016 Nextcloud
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ *   License as published by the Free Software Foundation; either
+ *   version 3 of the License, or any later version.
+ *
+ *   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.
+ *
+ *   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.accounts.Account;
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.owncloud.android.MainApp;
+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 java.io.File;
+import java.util.Date;
+
+/**
+ * Created by tobi on 25.09.16.
+ */
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class SyncedFolderJobService extends JobService {
+    private static final String TAG = "SyncedFolderJobService";
+    private Context mContext;
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        // TODO Tobi why is this null?
+        mContext = MainApp.getAppContext();
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        Log_OC.d(TAG, "startJob: " + params.getJobId());
+
+        // TODO Tobi just for testing!
+        Context context = MainApp.getAppContext();
+        Account account = AccountUtils.getCurrentOwnCloudAccount(context);
+
+        PersistableBundle bundle = params.getExtras();
+        String filePath = bundle.getString("filePath");
+        String remoteFolder = bundle.getString("remoteFolder");
+        Long dateTaken = bundle.getLong("dateTaken");
+        Boolean subfolderByDate = bundle.getInt("subfolderByDate") == 1;
+
+        File file = new File(filePath);
+
+        FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
+        requester.uploadNewFile(
+                context,
+                account,
+                filePath,
+                FileStorageUtils.getInstantUploadFilePath(remoteFolder, file.getName(), dateTaken, subfolderByDate),
+                FileUploader.LOCAL_BEHAVIOUR_FORGET,
+                "image/jpg",
+                true,           // create parent folder if not existent
+                UploadFileOperation.CREATED_AS_INSTANT_PICTURE
+        );
+        return false;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return false;
+    }
+}

+ 31 - 17
src/com/owncloud/android/services/observer/SyncedFolderObserver.java

@@ -1,49 +1,63 @@
 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.lib.common.utils.Log_OC;
+import com.owncloud.android.services.SyncedFolderJobService;
 import com.owncloud.android.utils.RecursiveFileObserver;
 
 import java.io.File;
+import java.util.Date;
 
-public class SyncedFolderObserver extends RecursiveFileObserver {
+class SyncedFolderObserver extends RecursiveFileObserver {
 
-    File dirToWatch;
-    String remoteFolder;
-    Context context;
+    private Context context;
 
-    public static final int MY_BACKGROUND_JOB = 0;
+    private static final int MY_BACKGROUND_JOB = 0;
+    public static final String TAG = "SyncedFolderObserver";
+    private String remoteFolder;
 
 
     public SyncedFolderObserver(String path, String remoteFolder) {
         super(path, FileObserver.CREATE + FileObserver.MOVED_TO);
 
         context = MainApp.getAppContext();
-        dirToWatch = new File(path);
         this.remoteFolder = remoteFolder;
-        Log_OC.d("SyncedFolderObserver", "Started watching: "+ path);
+        Log_OC.d("SyncedFolderObserver", "Started watching: " + path);
     }
 
 
-
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     @Override
     public void onEvent(int event, String path) {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putString("filePath", path);
+        bundle.putString("remoteFolder", remoteFolder);
+        bundle.putLong("dateTaken", new Date().getTime());
+
         JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-//        JobInfo job = new JobInfo.Builder(
-//                MY_BACKGROUND_JOB,
-//                new ComponentName(context, MyJobService.class))
-//                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
-//                .setRequiresCharging(true)
-//                .build();
-//        js.schedule(job);
-
-        Log.d("SyncedFolder", "Event: " + event + " Path: " + path);
+        JobInfo job = new JobInfo.Builder(
+                MY_BACKGROUND_JOB,
+                new ComponentName(context, SyncedFolderJobService.class))
+                .setRequiresCharging(false)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+                .setExtras(bundle)
+                .build();
+
+        Integer result = js.schedule(job);
+        if (result <= 0) {
+            Log_OC.d(TAG, "Job failed to start: " + result);
+        }
+
+        Log.d(TAG, "Event: " + event + " Path: " + path);
     }
 }

+ 68 - 32
src/com/owncloud/android/services/observer/SyncedFolderObserverService.java

@@ -1,59 +1,95 @@
 package com.owncloud.android.services.observer;
 
-import java.io.File;
-import java.util.HashSet;
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.IBinder;
 
 import com.owncloud.android.MainApp;
+import com.owncloud.android.datamodel.SyncedFolder;
+import com.owncloud.android.db.ProviderMeta;
 import com.owncloud.android.lib.common.utils.Log_OC;
 
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.IBinder;
-import android.widget.Toast;
+import java.util.ArrayList;
 
 public class SyncedFolderObserverService extends Service {
-    private SyncedFolderObserver fileOb;
-    private static final String TAG = "InstantUploadFolderObserverService";
+    private static final String TAG = "SyncedFolderObserverService";
+    private ContentResolver database;
+    private ArrayList<SyncedFolderObserver> syncedFolderObservers = new ArrayList<>();
 
     @Override
     public void onCreate() {
-        File sdcard = new File("/mnt/sdcard/DCIM/");
-        Log_OC.d("SyncedFolderObserverService", "watching file: " + sdcard.getAbsolutePath());
-        fileOb = new SyncedFolderObserver(sdcard.getAbsolutePath(), "WhatsApp");
-        fileOb.startWatching();
+        database = MainApp.getAppContext().getContentResolver();
     }
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        // TODO Auto-generated method stub
-        Log_OC.d("SyncedFolderObserverService", "start");
-        return Service.START_NOT_STICKY;
-        //return super.onStartCommand(intent, flags, startId);
+        for (SyncedFolder syncedFolder : getSyncedFolders()) {
+            SyncedFolderObserver observer = new SyncedFolderObserver(syncedFolder.getLocalPath(),
+                    syncedFolder.getRemotePath());
 
-    }
+            observer.startWatching();
+            syncedFolderObservers.add(observer);
+        }
 
-    @Override
-    public void onStart(Intent intent, int startid) {
-        Log_OC.d("SyncedFolderObserverService", "start");
-        fileOb.startWatching();
-        /*for (int i = 0; i < fileOb_list.size(); ++i) {
-            fileOb_list.get(i).startWatching();
-        }*/
-        Toast.makeText(this.getApplicationContext(), "start monitoring file modification", Toast.LENGTH_SHORT).show();
+        return Service.START_NOT_STICKY;
     }
+
     @Override
     public void onDestroy() {
-        fileOb.stopWatching();
-        /*for (int i = 0; i < fileOb_list.size(); ++i) {
-            fileOb_list.get(i).stopWatching();
-        }*/
-        Toast.makeText(this.getApplicationContext(), "stop monitoring file modification", Toast.LENGTH_SHORT).show();
+        for (SyncedFolderObserver observer : syncedFolderObservers) {
+            observer.stopWatching();
+            syncedFolderObservers.remove(observer);
+        }
     }
 
     @Override
     public IBinder onBind(Intent arg0) {
         return null;
     }
+
+    private SyncedFolder[] getSyncedFolders() {
+        Cursor c = database.query(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
+                null,
+                "1=1",
+                null,
+                null
+        );
+        SyncedFolder[] list = new SyncedFolder[c.getCount()];
+        if (c.moveToFirst()) {
+            do {
+                SyncedFolder syncedFolder = createSyncedFolderFromCursor(c);
+                if (syncedFolder == null) {
+                    Log_OC.e(TAG, "SyncedFolder could not be created from cursor");
+                } else {
+                    list[c.getPosition()] = syncedFolder;
+                }
+            } while (c.moveToNext());
+
+        }
+        c.close();
+
+        return list;
+    }
+
+    private SyncedFolder createSyncedFolderFromCursor(Cursor c) {
+        SyncedFolder syncedFolder = null;
+        if (c != null) {
+            long id = c.getLong(c.getColumnIndex(ProviderMeta.ProviderTableMeta._ID));
+            String localPath = c.getString(c.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH));
+            String remotePath = c.getString(c.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH));
+            Boolean wifiOnly = c.getInt(c.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1;
+            Boolean chargingOnly = c.getInt(c.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1;
+            Boolean subfolderByDate = c.getInt(c.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1;
+            String accountName = c.getString(c.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT));
+            Integer uploadAction = c.getInt(c.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_OPTION));
+            Boolean enabled = c.getInt(c.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1;
+
+            syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate,
+                    accountName, uploadAction, enabled);
+        }
+        return syncedFolder;
+    }
 }

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

@@ -46,7 +46,7 @@ import com.owncloud.android.ui.adapter.FolderSyncAdapter;
 /**
  * Activity displaying all auto-synced folders and/or instant upload media folders.
  */
-public class FolderSyncActivity extends DrawerActivity {
+public class FolderSyncActivity extends FileActivity {
     private static final String TAG = FolderSyncActivity.class.getSimpleName();
     private RecyclerView mRecyclerView;
     private FolderSyncAdapter mAdapter;

+ 4 - 8
src/com/owncloud/android/utils/FileStorageUtils.java

@@ -151,21 +151,17 @@ public class FileStorageUtils {
     /**
      * Returns the InstantUploadFilePath on the owncloud instance
      *
-     * @param context
      * @param fileName
      * @param dateTaken: Time in milliseconds since 1970 when the picture was taken.
      * @return
      */
-    public static String getInstantUploadFilePath(Context context, String fileName, long dateTaken) {
-        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
-        String uploadPathdef = context.getString(R.string.instant_upload_path);
-        String uploadPath = pref.getString("instant_upload_path", uploadPathdef);
+    public static String getInstantUploadFilePath(String remotePath, String fileName, long dateTaken,
+                                                  Boolean subfolderByDate) {
         String subPath = "";
-        if (com.owncloud.android.db.PreferenceManager.instantPictureUploadPathUseSubfolders(context)) {
+        if (subfolderByDate) {
            subPath = getSubpathFromDate(dateTaken);
         }
-        return uploadPath + OCFile.PATH_SEPARATOR + subPath
-                + (fileName == null ? "" : fileName);
+        return remotePath + OCFile.PATH_SEPARATOR + subPath + (fileName == null ? "" : fileName);
     }
 
     /**