diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt index 629f29df79db..56d84ba4fe39 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -11,13 +11,14 @@ package com.nextcloud.client.jobs import android.content.ContentResolver import android.content.Context import android.content.res.Resources +import android.database.Cursor import android.text.TextUtils import androidx.exifinterface.media.ExifInterface import androidx.work.Worker import androidx.work.WorkerParameters import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.device.PowerManagementService -import com.nextcloud.client.jobs.upload.FileUploadHelper +import com.nextcloud.client.jobs.upload.FileUploadHelper.Companion.instance import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.SubFolderRule @@ -35,7 +36,6 @@ import com.owncloud.android.ui.activity.SettingsActivity import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.FilesSyncHelper import com.owncloud.android.utils.MimeType -import com.owncloud.android.utils.MimeTypeUtil import java.io.File import java.text.ParsePosition import java.text.SimpleDateFormat @@ -60,15 +60,19 @@ class FilesSyncWork( const val OVERRIDE_POWER_SAVING = "overridePowerSaving" const val CHANGED_FILES = "changedFiles" const val SYNCED_FOLDER_ID = "syncedFolderId" + + var realScanIntervalMS = 1000L } private lateinit var syncedFolder: SyncedFolder + private var cursor: android.database.Cursor? = null @Suppress("MagicNumber") override fun doWork(): Result { val syncFolderId = inputData.getLong(SYNCED_FOLDER_ID, -1) val changedFiles = inputData.getStringArray(CHANGED_FILES) + backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class) + "_" + syncFolderId) Log_OC.d(TAG, "File-sync worker started for folder ID: $syncFolderId") @@ -82,8 +86,19 @@ class FilesSyncWork( return result } - val resources = context.resources - val lightVersion = resources.getBoolean(R.bool.syncedFolder_light) + val filesystemDataProvider = FilesystemDataProvider(contentResolver) + var current = System.currentTimeMillis() + FilesSyncHelper.insertNewFilesIntoFSDatabase(syncedFolder.localPath, syncedFolder, filesystemDataProvider) + Log_OC.d(TAG, "File-sync worker (${syncedFolder.remotePath}) inserted new files into DB in " + (System.currentTimeMillis() - current) + "ms") + current = System.currentTimeMillis() + cursor = filesystemDataProvider.getFilesCursorAscOrderedForSyncedFolder(syncedFolder) + Log_OC.d(TAG, "File-sync worker (${syncedFolder.remotePath}) got files cursor in " + (System.currentTimeMillis() - current) + "ms") + current = System.currentTimeMillis() + checkFilesFormDBForChanges(syncedFolder, filesystemDataProvider, cursor) + Log_OC.d(TAG, "File-sync worker (${syncedFolder.remotePath}) checked files for changes in " + (System.currentTimeMillis() - current) + "ms") + + // Todo: update syncedFolder.lastScanTimestampMs + FilesSyncHelper.restartUploadsIfNeeded( uploadsStorageManager, userAccountManager, @@ -97,24 +112,8 @@ class FilesSyncWork( "File-sync worker (${syncedFolder.remotePath}) changed files from observer: " + changedFiles.contentToString() ) - collectChangedFiles(changedFiles) - Log_OC.d(TAG, "File-sync worker (${syncedFolder.remotePath}) finished checking files.") - // Create all the providers we'll need - val filesystemDataProvider = FilesystemDataProvider(contentResolver) - val currentLocale = resources.configuration.locale - val dateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale) - dateFormat.timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) - syncFolder( - context, - resources, - lightVersion, - filesystemDataProvider, - currentLocale, - dateFormat, - syncedFolder - ) Log_OC.d(TAG, "File-sync worker (${syncedFolder.remotePath}) finished") val result = Result.success() @@ -126,6 +125,11 @@ class FilesSyncWork( return result } + override fun onStopped() { + this.cursor?.close() + super.onStopped() + } + private fun setSyncedFolder(syncedFolderID: Long): Boolean { val syncedFolderTmp = syncedFolderProvider.getSyncedFolderByID(syncedFolderID) if (syncedFolderTmp == null || !syncedFolderTmp.isEnabled || !syncedFolderTmp.isExisting) { @@ -164,6 +168,12 @@ class FilesSyncWork( return true } + if ((syncedFolder.lastScanTimestampMs + realScanIntervalMS) > System.currentTimeMillis()){ + Log_OC.d(TAG, "File-sync kill worker since started before scan " + + "Interval and nothing todo (${syncedFolder.localPath})!") + return true + } + if (syncedFolder.isChargingOnly && !powerManagementService.battery.isCharging && !powerManagementService.battery.isFull @@ -175,62 +185,58 @@ class FilesSyncWork( return false } - @Suppress("MagicNumber") - private fun collectChangedFiles(changedFiles: Array?) { - if (!changedFiles.isNullOrEmpty()) { - FilesSyncHelper.insertChangedEntries(syncedFolder, changedFiles) - } else { - // Check every file in synced folder for changes and update - // filesystemDataProvider database (potentially needs a long time) - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder) + private fun checkFilesFormDBForChanges( + syncedFolder: SyncedFolder, + filesystemDataProvider: FilesystemDataProvider, + cursor: Cursor? + ) { + // TODO When completely done with folder set syncedFolder lastScanTimestampMs + if (cursor == null) { + return } + if (cursor.moveToFirst()) { + do { + val fileData = + filesystemDataProvider.getFileSystemDataSetFromCursor(cursor, syncedFolder) + + if ((fileData.foundAt + realScanIntervalMS) + > System.currentTimeMillis() + ) { + break + } + + if(FilesSyncHelper.checkFileForChanges(fileData, filesystemDataProvider)){ + scheduleForUpload(File(fileData.localPath), syncedFolder) + } + } while (cursor.moveToNext() && !cursor.isAfterLast) + } + cursor.close() } - @Suppress("LongMethod") // legacy code - private fun syncFolder( - context: Context, - resources: Resources, - lightVersion: Boolean, - filesystemDataProvider: FilesystemDataProvider, - currentLocale: Locale, - sFormatter: SimpleDateFormat, + private fun scheduleForUpload( + file: File, syncedFolder: SyncedFolder ) { + val uploadAction: Int? val needsCharging: Boolean val needsWifi: Boolean - var file: File val accountName = syncedFolder.account val optionalUser = userAccountManager.getUser(accountName) if (!optionalUser.isPresent) { return } val user = optionalUser.get() + + val resources = context.resources + val lightVersion = resources.getBoolean(R.bool.syncedFolder_light) + val arbitraryDataProvider: ArbitraryDataProvider? = if (lightVersion) { ArbitraryDataProviderImpl(context) } else { null } - val paths = filesystemDataProvider.getFilesForUpload( - syncedFolder.localPath, - syncedFolder.id.toString() - ) - - if (paths.size == 0) { - return - } - val pathsAndMimes = paths.map { path -> - file = File(path) - val localPath = file.absolutePath - Triple( - localPath, - getRemotePath(file, syncedFolder, sFormatter, lightVersion, resources, currentLocale), - MimeTypeUtil.getBestMimeTypeByFilename(localPath) - ) - } - val localPaths = pathsAndMimes.map { it.first }.toTypedArray() - val remotePaths = pathsAndMimes.map { it.second }.toTypedArray() if (lightVersion) { needsCharging = resources.getBoolean(R.bool.syncedFolder_light_on_charging) @@ -245,28 +251,26 @@ class FilesSyncWork( needsWifi = syncedFolder.isWifiOnly uploadAction = syncedFolder.uploadAction } - FileUploadHelper.instance().uploadNewFiles( + + val currentLocale = resources.configuration.locales.get(0) + val dateFormat = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale) + dateFormat.timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) + + //Todo: Check if startUpload works without restarting upload worker + instance().scheduleNewFileForUpload( user, - localPaths, - remotePaths, - uploadAction!!, - // create parent folder if not existent - true, + file.path, + getRemotePath(file, syncedFolder, dateFormat, lightVersion, resources, Locale.getDefault()), + uploadAction, + true, // create parent folder if not existent UploadFileOperation.CREATED_AS_INSTANT_PICTURE, needsWifi, needsCharging, syncedFolder.nameCollisionPolicy ) - - for (path in paths) { - // TODO batch update - filesystemDataProvider.updateFilesystemFileAsSentForUpload( - path, - syncedFolder.id.toString() - ) - } } + private fun getRemotePath( file: File, syncedFolder: SyncedFolder, @@ -327,7 +331,7 @@ class FilesSyncWork( return lastModificationTime } - private fun getUploadAction(action: String): Int? { + private fun getUploadAction(action: String): Int { return when (action) { "LOCAL_BEHAVIOUR_FORGET" -> FileUploadWorker.LOCAL_BEHAVIOUR_FORGET "LOCAL_BEHAVIOUR_MOVE" -> FileUploadWorker.LOCAL_BEHAVIOUR_MOVE diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index ec0da40536ee..cd9a90e9f33a 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -162,6 +162,57 @@ class FileUploadHelper { return showNotExistMessage } + private fun createUploadFromParams( + user: User, + localPath: String, + remotePath: String, + localBehavior: Int, + createRemoteFolder: Boolean, + createdBy: Int, + requiresWifi: Boolean, + requiresCharging: Boolean, + nameCollisionPolicy: NameCollisionPolicy + ): OCUpload { + return OCUpload(localPath, remotePath, user.accountName).apply { + this.nameCollisionPolicy = nameCollisionPolicy + isUseWifiOnly = requiresWifi + isWhileChargingOnly = requiresCharging + uploadStatus = UploadStatus.UPLOAD_IN_PROGRESS + this.createdBy = createdBy + isCreateRemoteFolder = createRemoteFolder + localAction = localBehavior + } + } + + /** + * Used for quickly adding a file without restarting the Worker. + * To guarantee the file is uploaded, the Worker should be restarted. + */ + fun scheduleNewFileForUpload( + user: User, + localPath: String, + remotePath: String, + localBehavior: Int, + createRemoteFolder: Boolean, + createdBy: Int, + requiresWifi: Boolean, + requiresCharging: Boolean, + nameCollisionPolicy: NameCollisionPolicy + ) { + val upload = createUploadFromParams( + user, + localPath, + remotePath, + localBehavior, + createRemoteFolder, + createdBy, + requiresWifi, + requiresCharging, + nameCollisionPolicy) + uploadsStorageManager.storeUpload(upload) + } + + @Suppress("LongParameterList") fun uploadNewFiles( user: User, @@ -175,15 +226,16 @@ class FileUploadHelper { nameCollisionPolicy: NameCollisionPolicy ) { val uploads = localPaths.mapIndexed { index, localPath -> - OCUpload(localPath, remotePaths[index], user.accountName).apply { - this.nameCollisionPolicy = nameCollisionPolicy - isUseWifiOnly = requiresWifi - isWhileChargingOnly = requiresCharging - uploadStatus = UploadStatus.UPLOAD_IN_PROGRESS - this.createdBy = createdBy - isCreateRemoteFolder = createRemoteFolder - localAction = localBehavior - } + createUploadFromParams( + user, + localPath, + remotePaths[index], + localBehavior, + createRemoteFolder, + createdBy, + requiresWifi, + requiresCharging, + nameCollisionPolicy) } uploadsStorageManager.storeUploads(uploads) backgroundJobManager.startFilesUploadJob(user) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java index f4b6a03ef23c..f40108fcfeeb 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -106,6 +106,55 @@ public Set getFilesForUpload(String localPath, String syncedFolderId) { return localPathsToUpload; } + /** + * Add path to DB if it not already exists + */ + public void addNewFilesystemFileToDB(String localPath, SyncedFolder syncedFolder) { + if (getFilesystemDataSet(localPath, syncedFolder) == null) { + ContentValues cv = new ContentValues(); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, 0); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, localPath); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID, syncedFolder.getId()); + contentResolver.insert( + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + cv); + } + + } + + public void insertFileIntoDB(File file, SyncedFolder syncedFolder) { + // TODO: skip check and handle via primary key + if (getFilesystemDataSet(file.getPath(), syncedFolder) == null) { + ContentValues cv = new ContentValues(); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, 0); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, file.getPath()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID, syncedFolder.getId()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, file.lastModified()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER, file.isDirectory()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, Boolean.FALSE); + + contentResolver.insert( + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + cv); + } + + } + + public Cursor getFilesCursorAscOrderedForSyncedFolder(SyncedFolder syncedFolder) { + String query = ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID + " = ? "; + String sortOrder = ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY + " ASC"; + + return contentResolver.query( + ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + null, + query, + new String[]{String.valueOf(syncedFolder.getId())}, + sortOrder + ); + } + + + public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, SyncedFolder syncedFolder) { // takes multiple milliseconds to query data from database (around 75% of execution time) (6ms) @@ -161,6 +210,35 @@ public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean is } } + public FileSystemDataSet getFileSystemDataSetFromCursor(Cursor cursor, SyncedFolder syncedFolder) { + try { + int id = cursor.getInt(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta._ID)); + String localPath = cursor.getString(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); + long modifiedAt = cursor.getLong(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED)); + boolean isFolder = cursor.getInt(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)) != 0; + long foundAt = cursor.getLong(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)); + boolean isSentForUpload = cursor.getInt(cursor.getColumnIndexOrThrow( + ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0; + + String crc32 = cursor.getString(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32)); + + if (id == -1) { + Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); + } else { + return new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt, + syncedFolder.getId(), crc32); + } + } catch (Exception e) { + Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); + } + + return null; + } + private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFolder syncedFolder) { Cursor cursor = contentResolver.query( @@ -175,33 +253,7 @@ private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFold FileSystemDataSet dataSet = null; if (cursor != null) { if (cursor.moveToFirst()) { - int id = cursor.getInt(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta._ID)); - String localPath = cursor.getString(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH)); - long modifiedAt = cursor.getLong(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED)); - boolean isFolder = false; - if (cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_IS_FOLDER)) != 0) { - isFolder = true; - } - long foundAt = cursor.getLong(cursor.getColumnIndexOrThrow(ProviderMeta. - ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY)); - - boolean isSentForUpload = false; - if (cursor.getInt(cursor.getColumnIndexOrThrow( - ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD)) != 0) { - isSentForUpload = true; - } - - String crc32 = cursor.getString(cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32)); - - if (id == -1) { - Log_OC.e(TAG, "Arbitrary value could not be created from cursor"); - } else { - dataSet = new FileSystemDataSet(id, localPath, modifiedAt, isFolder, isSentForUpload, foundAt, - syncedFolder.getId(), crc32); - } + dataSet = getFileSystemDataSetFromCursor(cursor, syncedFolder); } cursor.close(); } else { @@ -211,7 +263,25 @@ private FileSystemDataSet getFilesystemDataSet(String localPathParam, SyncedFold return dataSet; } - private long getFileChecksum(String filepath) { + public void updateFilesystemDataSet(FileSystemDataSet fileData) { + + ContentValues cv = new ContentValues(); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_LOCAL_PATH, fileData.getLocalPath()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_FOUND_RECENTLY, fileData.getFoundAt()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_CRC32, fileData.getCrc32()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_MODIFIED, fileData.getModifiedAt()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_SYNCED_FOLDER_ID, fileData.getSyncedFolderId()); + cv.put(ProviderMeta.ProviderTableMeta._ID, fileData.getId()); + cv.put(ProviderMeta.ProviderTableMeta.FILESYSTEM_FILE_SENT_FOR_UPLOAD, fileData.isSentForUpload()); + + contentResolver.update(ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, + cv, + ProviderMeta.ProviderTableMeta._ID + "=?", + new String[]{String.valueOf(fileData.getId())} + ); + } + + public static long getFileChecksum(String filepath) { try (FileInputStream fileInputStream = new FileInputStream(filepath); InputStream inputStream = new BufferedInputStream(fileInputStream)) { diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 1be5ebc802a7..2a3df5e48195 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -10,6 +10,7 @@ import android.accounts.Account; import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; @@ -19,15 +20,18 @@ import com.nextcloud.client.device.BatteryStatus; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.jobs.BackgroundJobManager; +import com.nextcloud.client.jobs.FilesSyncWork; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.MainApp; +import com.owncloud.android.datamodel.FileSystemDataSet; import com.owncloud.android.datamodel.FilesystemDataProvider; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.db.OCUpload; +import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.UploadResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -55,6 +59,69 @@ private FilesSyncHelper() { // utility class -> private constructor } + // Must run completely to make FilesSyncWork persistent + public static void insertNewFilesIntoFSDatabase( + String folderPath, + SyncedFolder syncedFolder, + FilesystemDataProvider filesystemDataProvider){ + + //TODO: Maybe File not needed -> Path is enough + File[] files = new File(folderPath).listFiles(); + if (files == null) { + return; + } + + // TODO: Add multiple files at once -> Maybe more efficient + // see storeUploads() + // ContentResolver.applyBatch() -> https://developer.android.com/guide/topics/providers/content-provider-basics + for (File file : files) { + filesystemDataProvider.insertFileIntoDB(file, syncedFolder); + if (file.isDirectory()){ + insertNewFilesIntoFSDatabase(file.getAbsolutePath(), syncedFolder, filesystemDataProvider); + } + } + } + + public static boolean checkFileForChanges( + FileSystemDataSet fileData, + FilesystemDataProvider filesystemDataProvider + ){ + + File file = new File(fileData.getLocalPath()); + boolean changed = false; + if (file.lastModified() > fileData.getFoundAt()){ + long newCrc32 = FilesystemDataProvider.getFileChecksum(fileData.getLocalPath()); + if (fileData.getCrc32() == null || (newCrc32 != -1 && !fileData.getCrc32().equals(Long.toString(newCrc32)))) { + changed = true; + fileData.setCrc32(Long.toString(newCrc32)); + } + } + + fileData.setFoundAt(System.currentTimeMillis()); + filesystemDataProvider.updateFilesystemDataSet(fileData); + return changed; + } + + + + + public static void addNewFilesToDB(SyncedFolder syncedFolder, FilesystemDataProvider filesystemDataProvider) { + + long current = System.nanoTime(); + String[] paths = new File(syncedFolder.getLocalPath()).list(); + if (paths == null) { + return; + } + Log_OC.d(TAG, "File-sync reading files took " + (System.nanoTime() - current) + "ns"); + current = System.nanoTime(); + + for (String localPath : paths){ + filesystemDataProvider.addNewFilesystemFileToDB(localPath, syncedFolder); + } + Log_OC.d(TAG, "File-sync adding files to DB took " + (System.nanoTime() - current) + "ns"); + + } + private static void insertCustomFolderIntoDB(Path path, SyncedFolder syncedFolder, FilesystemDataProvider filesystemDataProvider,