From d6d5546c17bac6b6f3fd8a7d71228ff36a29e675 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Sun, 21 Feb 2021 00:19:23 +0100 Subject: [PATCH] #268 Optionally upload newly created videos via automatic upload as well --- .../presentation/service/PhotoContentJob.kt | 61 ++++++++++++------- presentation/src/main/res/values/strings.xml | 1 + presentation/src/main/res/xml/preferences.xml | 5 ++ .../util/SharedPreferencesHandler.kt | 5 ++ 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/presentation/src/main/java/org/cryptomator/presentation/service/PhotoContentJob.kt b/presentation/src/main/java/org/cryptomator/presentation/service/PhotoContentJob.kt index 66537386..8e39c493 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/service/PhotoContentJob.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/service/PhotoContentJob.kt @@ -7,18 +7,18 @@ import android.app.job.JobScheduler import android.app.job.JobService import android.content.ComponentName import android.content.Context +import android.database.Cursor import android.database.MergeCursor import android.net.Uri import android.os.Build import android.os.Handler import android.provider.MediaStore -import android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI -import android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI import androidx.annotation.RequiresApi import org.cryptomator.domain.exception.FatalBackendException import org.cryptomator.presentation.R import org.cryptomator.presentation.util.FileUtil import org.cryptomator.presentation.util.ResourceHelper +import org.cryptomator.util.SharedPreferencesHandler import org.cryptomator.util.file.MimeTypeMap_Factory import org.cryptomator.util.file.MimeTypes import timber.log.Timber @@ -45,22 +45,17 @@ class PhotoContentJob : JobService() { if (params.triggeredContentUris != null) { val ids = getIds(params) if (ids != null && ids.isNotEmpty()) { - val selection = buildSelection(ids) - contentResolver.query(EXTERNAL_CONTENT_URI, PROJECTION, selection, null, null).use { externalCursor -> - contentResolver.query(INTERNAL_CONTENT_URI, PROJECTION, selection, null, null).use { internalCursor -> - MergeCursor(arrayOf(externalCursor, internalCursor)).use { cursor -> - while (cursor.moveToNext()) { - try { - val dir = cursor.getString(PROJECTION_DATA) - fileUtil.addImageToAutoUploads(dir) - Timber.tag("PhotoContentJob").i("Added file to UploadList") - Timber.tag("PhotoContentJob").d(String.format("Added file to UploadList %s", dir)) - } catch (e: FatalBackendException) { - Timber.tag("PhotoContentJob").e(e, "Failed to add image to auto upload list") - } catch (e: SecurityException) { - Timber.tag("PhotoContentJob").e(e, "No access to storage") - } - } + MergeCursor(getContentResolvers(ids)).use { + while (it.moveToNext()) { + try { + val dir = it.getString(PROJECTION_DATA) + fileUtil.addImageToAutoUploads(dir) + Timber.tag("PhotoContentJob").i("Added file to UploadList") + Timber.tag("PhotoContentJob").d(String.format("Added file to UploadList %s", dir)) + } catch (e: FatalBackendException) { + Timber.tag("PhotoContentJob").e(e, "Failed to add image to auto upload list") + } catch (e: SecurityException) { + Timber.tag("PhotoContentJob").e(e, "No access to storage") } } } @@ -77,10 +72,29 @@ class PhotoContentJob : JobService() { return false } + private fun getContentResolvers(ids: Set): Array { + val selection = buildSelection(ids) + + var resolvers = arrayOf(contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION_IMAGES, selection, null, null), + contentResolver.query(MediaStore.Images.Media.INTERNAL_CONTENT_URI, PROJECTION_IMAGES, selection, null, null)) + + if (SharedPreferencesHandler(applicationContext).autoPhotoUploadIncludingVideos()) { + resolvers += arrayOf(contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, PROJECTION_VIDEOS, selection, null, null), + contentResolver.query(MediaStore.Video.Media.INTERNAL_CONTENT_URI, PROJECTION_VIDEOS, selection, null, null)) + } + + return resolvers + } + private fun getIds(params: JobParameters): Set? { return params.triggeredContentUris ?.map { it.pathSegments } - ?.filter { it != null && (it.size == EXTERNAL_CONTENT_URI.pathSegments.size + 1 || it.size == INTERNAL_CONTENT_URI.pathSegments.size + 1) } + ?.filter { + it != null && (it.size == MediaStore.Images.Media.EXTERNAL_CONTENT_URI.pathSegments.size + 1 + || it.size == MediaStore.Video.Media.EXTERNAL_CONTENT_URI.pathSegments.size + 1 + || it.size == MediaStore.Images.Media.INTERNAL_CONTENT_URI.pathSegments.size + 1 + || it.size == MediaStore.Video.Media.INTERNAL_CONTENT_URI.pathSegments.size + 1) + } ?.mapTo(HashSet()) { it[it.size - 1] } } @@ -112,7 +126,8 @@ class PhotoContentJob : JobService() { companion object { private val MEDIA_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/") - internal val PROJECTION = arrayOf(MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA) + internal val PROJECTION_IMAGES = arrayOf(MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA) + internal val PROJECTION_VIDEOS = arrayOf(MediaStore.Video.VideoColumns._ID, MediaStore.Video.VideoColumns.DATA) internal const val PROJECTION_DATA = 1 @@ -121,8 +136,10 @@ class PhotoContentJob : JobService() { init { val builder = JobInfo.Builder(PHOTOS_CONTENT_JOB, ComponentName(ResourceHelper.getString(R.string.app_id), PhotoContentJob::class.java.name)) - builder.addTriggerContentUri(JobInfo.TriggerContentUri(EXTERNAL_CONTENT_URI, FLAG_NOTIFY_FOR_DESCENDANTS)) - builder.addTriggerContentUri(JobInfo.TriggerContentUri(INTERNAL_CONTENT_URI, FLAG_NOTIFY_FOR_DESCENDANTS)) + builder.addTriggerContentUri(JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, FLAG_NOTIFY_FOR_DESCENDANTS)) + builder.addTriggerContentUri(JobInfo.TriggerContentUri(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, FLAG_NOTIFY_FOR_DESCENDANTS)) + builder.addTriggerContentUri(JobInfo.TriggerContentUri(MediaStore.Images.Media.INTERNAL_CONTENT_URI, FLAG_NOTIFY_FOR_DESCENDANTS)) + builder.addTriggerContentUri(JobInfo.TriggerContentUri(MediaStore.Video.Media.INTERNAL_CONTENT_URI, FLAG_NOTIFY_FOR_DESCENDANTS)) builder.addTriggerContentUri(JobInfo.TriggerContentUri(MEDIA_URI, FLAG_NOTIFY_FOR_DESCENDANTS)) jobInfo = builder.build() } diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 240d713f..9059d03e 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -212,6 +212,7 @@ Choose vault for upload Activate Upload only using WIFI + Upload videos Save auto upload files to… diff --git a/presentation/src/main/res/xml/preferences.xml b/presentation/src/main/res/xml/preferences.xml index 8130ff88..1b92024f 100644 --- a/presentation/src/main/res/xml/preferences.xml +++ b/presentation/src/main/res/xml/preferences.xml @@ -110,6 +110,11 @@ android:key="photoUploadOnlyUsingWifi" android:title="@string/screen_settings_section_auto_photo_upload_only_wifi_toggle" /> + + diff --git a/util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt b/util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt index 127f746f..bf9141b8 100644 --- a/util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt +++ b/util/src/main/java/org/cryptomator/util/SharedPreferencesHandler.kt @@ -148,6 +148,10 @@ constructor(context: Context) : SharedPreferences.OnSharedPreferenceChangeListen defaultSharedPreferences.setValue(PHOTO_UPLOAD_FOLDER, location) } + fun autoPhotoUploadIncludingVideos(): Boolean { + return defaultSharedPreferences.getValue(PHOTO_UPLOAD_INCLUDING_VIDEOS, false) + } + fun useLruCache(): Boolean { return defaultSharedPreferences.getValue(USE_LRU_CACHE, false) } @@ -243,6 +247,7 @@ constructor(context: Context) : SharedPreferences.OnSharedPreferenceChangeListen const val PHOTO_UPLOAD_ONLY_USING_WIFI = "photoUploadOnlyUsingWifi" const val PHOTO_UPLOAD_VAULT = "photoUploadVault" const val PHOTO_UPLOAD_FOLDER = "photoUploadFolder" + const val PHOTO_UPLOAD_INCLUDING_VIDEOS = "photoUploadIncludingVideos" const val USE_LRU_CACHE = "lruCache" const val LRU_CACHE_SIZE = "lruCacheSize" const val MAIL = "mail"