瀏覽代碼

First batch of magic

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 7 年之前
父節點
當前提交
1360e6c536

+ 1 - 8
src/main/java/com/owncloud/android/MainApp.java

@@ -32,7 +32,6 @@ import android.support.multidex.MultiDexApplication;
 import android.support.v4.util.Pair;
 import android.support.v4.util.Pair;
 
 
 import com.evernote.android.job.JobManager;
 import com.evernote.android.job.JobManager;
-import com.evernote.android.job.JobRequest;
 import com.owncloud.android.authentication.PassCodeManager;
 import com.owncloud.android.authentication.PassCodeManager;
 import com.owncloud.android.datamodel.MediaFolder;
 import com.owncloud.android.datamodel.MediaFolder;
 import com.owncloud.android.datamodel.MediaProvider;
 import com.owncloud.android.datamodel.MediaProvider;
@@ -40,7 +39,6 @@ import com.owncloud.android.datamodel.SyncedFolder;
 import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.db.PreferenceManager;
 import com.owncloud.android.db.PreferenceManager;
-import com.owncloud.android.jobs.FilesSyncJob;
 import com.owncloud.android.jobs.NCJobCreator;
 import com.owncloud.android.jobs.NCJobCreator;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy;
@@ -130,12 +128,7 @@ public class MainApp extends MultiDexApplication {
 
 
         initiateExistingAutoUploadEntries();
         initiateExistingAutoUploadEntries();
 
 
-        new JobRequest.Builder(FilesSyncJob.TAG)
-                .setPeriodic(900000L, 300000L)
-                .setUpdateCurrent(true)
-                .build()
-                .schedule();
-
+        FilesSyncHelper.scheduleFilesSyncIfNeeded();
         FilesSyncHelper.restartJobsIfNeeded();
         FilesSyncHelper.restartJobsIfNeeded();
 
 
         // register global protection with pass code
         // register global protection with pass code

+ 79 - 0
src/main/java/com/owncloud/android/jobs/M1ContentObserverJob.java

@@ -0,0 +1,79 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ * Copyright (C) 2017 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.jobs;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+
+import com.evernote.android.job.JobRequest;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.datamodel.MediaFolder;
+import com.owncloud.android.datamodel.SyncedFolder;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
+import com.owncloud.android.utils.FilesSyncHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public class M1ContentObserverJob extends JobService {
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(MainApp.getAppContext().
+                getContentResolver());
+
+        List<SyncedFolder> syncedFolderListImages = new ArrayList<>();
+        List<SyncedFolder> syncedFolderListVideos = new ArrayList<>();
+
+        for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
+            if (MediaFolder.IMAGE == syncedFolder.getType()) {
+                syncedFolderListImages.add(syncedFolder);
+            } else if (MediaFolder.VIDEO == syncedFolder.getType()) {
+                syncedFolderListVideos.add(syncedFolder);
+            }
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            if (params.getJobId() == FilesSyncHelper.ContentSyncJobId) {
+                if (params.getTriggeredContentAuthorities() != null && params.getTriggeredContentUris() != null
+                        && params.getTriggeredContentUris().length > 0) {
+                    new JobRequest.Builder(FilesSyncJob.TAG)
+                            .setExecutionWindow(1, TimeUnit.SECONDS.toMillis(2))
+                            .setBackoffCriteria(TimeUnit.SECONDS.toMillis(5), JobRequest.BackoffPolicy.LINEAR)
+                            .setUpdateCurrent(false)
+                            .build()
+                            .schedule();
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return true;
+    }
+}

+ 105 - 0
src/main/java/com/owncloud/android/utils/FilesSyncHelper.java

@@ -21,14 +21,20 @@
 package com.owncloud.android.utils;
 package com.owncloud.android.utils;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.Cursor;
 import android.net.Uri;
 import android.net.Uri;
+import android.os.Build;
 import android.provider.MediaStore;
 import android.provider.MediaStore;
+import android.support.annotation.RequiresApi;
 import android.text.TextUtils;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Log;
 
 
+import com.evernote.android.job.JobRequest;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
@@ -39,6 +45,8 @@ import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.UploadsStorageManager;
 import com.owncloud.android.datamodel.UploadsStorageManager;
 import com.owncloud.android.db.OCUpload;
 import com.owncloud.android.db.OCUpload;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.jobs.FilesSyncJob;
+import com.owncloud.android.jobs.M1ContentObserverJob;
 
 
 import org.lukhnos.nnio.file.FileVisitResult;
 import org.lukhnos.nnio.file.FileVisitResult;
 import org.lukhnos.nnio.file.Files;
 import org.lukhnos.nnio.file.Files;
@@ -53,10 +61,13 @@ import java.io.RandomAccessFile;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.channels.FileLock;
 import java.nio.channels.OverlappingFileLockException;
 import java.nio.channels.OverlappingFileLockException;
+import java.util.List;
 
 
 public class FilesSyncHelper {
 public class FilesSyncHelper {
     public static final String TAG = "FileSyncHelper";
     public static final String TAG = "FileSyncHelper";
 
 
+    public static int ContentSyncJobId = 315;
+
     public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) {
     public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) {
         final Context context = MainApp.getAppContext();
         final Context context = MainApp.getAppContext();
         final ContentResolver contentResolver = context.getContentResolver();
         final ContentResolver contentResolver = context.getContentResolver();
@@ -213,5 +224,99 @@ public class FilesSyncHelper {
                 null
                 null
         );
         );
     }
     }
+
+    @RequiresApi(api = Build.VERSION_CODES.M)
+    public static boolean isContentObserverJobScheduled() {
+        JobScheduler js = MainApp.getAppContext().getSystemService(JobScheduler.class);
+        List<JobInfo> jobs = js.getAllPendingJobs();
+
+        if (jobs == null || jobs.size() == 0) {
+            return false;
+        }
+
+        for (int i = 0; i < jobs.size(); i++) {
+            if (jobs.get(i).getId() == ContentSyncJobId) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static void scheduleFilesSyncIfNeeded() {
+        SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(MainApp.getAppContext().
+                getContentResolver());
+
+
+        boolean hasCustomFolders = false;
+        boolean hasVideoFolders = false;
+        boolean hasImageFolders = false;
+
+        if (syncedFolderProvider.getSyncedFolders() != null) {
+            for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) {
+                if (MediaFolder.CUSTOM == syncedFolder.getType()) {
+                    hasCustomFolders = true;
+                } else if (MediaFolder.VIDEO == syncedFolder.getType()) {
+                    hasVideoFolders = true;
+                } else if (MediaFolder.IMAGE == syncedFolder.getType()) {
+                    hasImageFolders = true;
+                }
+            }
+        }
+
+        new JobRequest.Builder(FilesSyncJob.TAG)
+                .setPeriodic(900000L, 300000L)
+                .setUpdateCurrent(true)
+                .build()
+                .schedule();
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
+            if (hasImageFolders || hasVideoFolders) {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                    scheduleJobOnM1(hasImageFolders, hasVideoFolders);
+                }
+            } else {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                    cancelJobsOnM1();
+                }
+            }
+        }
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    private static void cancelJobsOnM1() {
+        JobScheduler jobScheduler = MainApp.getAppContext().getSystemService(JobScheduler.class);
+        if (isContentObserverJobScheduled()) {
+            jobScheduler.cancel(ContentSyncJobId);
+        }
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    private static void scheduleJobOnM1(boolean hasImageFolders, boolean hasVideoFolders) {
+        JobScheduler jobScheduler = MainApp.getAppContext().getSystemService(JobScheduler.class);
+
+        if ((android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) && (hasImageFolders || hasVideoFolders)) {
+            if (!isContentObserverJobScheduled()) {
+                JobInfo.Builder builder = new JobInfo.Builder(ContentSyncJobId, new ComponentName(MainApp.getAppContext(),
+                        M1ContentObserverJob.class.getName()));
+                builder.addTriggerContentUri(new JobInfo.TriggerContentUri(android.provider.MediaStore.
+                        Images.Media.INTERNAL_CONTENT_URI,
+                        JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
+                builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MediaStore.
+                        Images.Media.EXTERNAL_CONTENT_URI,
+                        JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
+                builder.addTriggerContentUri(new JobInfo.TriggerContentUri(android.provider.MediaStore.
+                        Video.Media.INTERNAL_CONTENT_URI,
+                        JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
+                builder.addTriggerContentUri(new JobInfo.TriggerContentUri(MediaStore.
+                        Video.Media.EXTERNAL_CONTENT_URI,
+                        JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS));
+                builder.setPersisted(true);
+                builder.setTriggerContentMaxDelay(2000);
+                jobScheduler.schedule(builder.build());
+            }
+        }
+
+    }
 }
 }