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 68ee0e5a..66537386 100644 --- a/presentation/src/main/java/org/cryptomator/presentation/service/PhotoContentJob.kt +++ b/presentation/src/main/java/org/cryptomator/presentation/service/PhotoContentJob.kt @@ -7,12 +7,13 @@ 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 @@ -21,72 +22,69 @@ import org.cryptomator.presentation.util.ResourceHelper import org.cryptomator.util.file.MimeTypeMap_Factory import org.cryptomator.util.file.MimeTypes import timber.log.Timber -import java.lang.String.format -import java.util.* @RequiresApi(api = Build.VERSION_CODES.N) class PhotoContentJob : JobService() { - private val mHandler = Handler() - private val mWorker: Runnable = Runnable { + private val handler = Handler() + private val worker: Runnable = Runnable { scheduleJob(applicationContext) - jobFinished(mRunningParams, false) + jobFinished(runningParams, false) } - private lateinit var mRunningParams: JobParameters + private lateinit var runningParams: JobParameters override fun onStartJob(params: JobParameters): Boolean { Timber.tag("PhotoContentJob").i("Job started!") val fileUtil = FileUtil(baseContext, MimeTypes(MimeTypeMap_Factory.newInstance())) - mRunningParams = params - if (params.triggeredContentAuthorities != null) { + runningParams = params + + params.triggeredContentAuthorities?.let { if (params.triggeredContentUris != null) { val ids = getIds(params) - if (ids != null && ids.size > 0) { + if (ids != null && ids.isNotEmpty()) { val selection = buildSelection(ids) - var cursor: Cursor? = null - try { - cursor = contentResolver.query(EXTERNAL_CONTENT_URI, PROJECTION, selection, null, null) - cursor?.let { - while (cursor.moveToNext()) { - val dir = cursor.getString(PROJECTION_DATA) - try { - fileUtil.addImageToAutoUploads(dir) - Timber.tag("PhotoContentJob").i("Added file to UploadList") - Timber.tag("PhotoContentJob").d(format("Added file to UploadList %s", dir)) - } catch (e: FatalBackendException) { - Timber.tag("PhotoContentJob").e(e, "Failed to add image to auto upload list") + 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") + } } } - } ?: Timber.tag("PhotoContentJob").e("Error: no access to media!") - } catch (e: SecurityException) { - Timber.tag("PhotoContentJob").e("Error: no access to media!") - } finally { - cursor?.close() + } } + } else { + Timber.tag("PhotoContentJob").d("ids are null or 0: %s", ids) } } else { Timber.tag("PhotoContentJob").w("Photos rescan needed!") return true } - } else { - Timber.tag("PhotoContentJob").w("No photos content") - } + } ?: Timber.tag("PhotoContentJob").w("No photos content") - mHandler.post(mWorker) + handler.post(worker) return false } - private fun getIds(params: JobParameters): ArrayList? { + private fun getIds(params: JobParameters): Set? { return params.triggeredContentUris ?.map { it.pathSegments } - ?.filter { it != null && it.size == EXTERNAL_PATH_SEGMENTS.size + 1 } - ?.mapTo(ArrayList()) { it[it.size - 1] } + ?.filter { it != null && (it.size == EXTERNAL_CONTENT_URI.pathSegments.size + 1 || it.size == INTERNAL_CONTENT_URI.pathSegments.size + 1) } + ?.mapTo(HashSet()) { it[it.size - 1] } } - private fun buildSelection(ids: ArrayList): String { + private fun buildSelection(ids: Set): String { val selection = StringBuilder() ids.indices.forEach { i -> if (selection.isNotEmpty()) { @@ -94,7 +92,7 @@ class PhotoContentJob : JobService() { } selection.append(MediaStore.Images.ImageColumns._ID) selection.append("='") - selection.append(ids[i]) + selection.append(ids.elementAt(i)) selection.append("'") } return selection.toString() @@ -102,7 +100,7 @@ class PhotoContentJob : JobService() { override fun onStopJob(params: JobParameters): Boolean { Timber.tag("PhotoContentJob").i("onStopJob called, must stop, reschedule later") - mHandler.removeCallbacks(mWorker) + handler.removeCallbacks(worker) return true } @@ -114,7 +112,6 @@ class PhotoContentJob : JobService() { companion object { private val MEDIA_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/") - internal val EXTERNAL_PATH_SEGMENTS = EXTERNAL_CONTENT_URI.pathSegments internal val PROJECTION = arrayOf(MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA) internal const val PROJECTION_DATA = 1 @@ -125,6 +122,7 @@ 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(MEDIA_URI, FLAG_NOTIFY_FOR_DESCENDANTS)) jobInfo = builder.build() }