Register photo creation receiver for internal storage for auto upload

This commit is contained in:
Julian Raufelder 2021-02-15 16:49:56 +01:00
parent a42919911d
commit 7dca46864d
No known key found for this signature in database
GPG Key ID: 17EE71F6634E381D

View File

@ -7,12 +7,13 @@ import android.app.job.JobScheduler
import android.app.job.JobService import android.app.job.JobService
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.database.Cursor import android.database.MergeCursor
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Handler import android.os.Handler
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI import android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
import android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import org.cryptomator.domain.exception.FatalBackendException import org.cryptomator.domain.exception.FatalBackendException
import org.cryptomator.presentation.R 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.MimeTypeMap_Factory
import org.cryptomator.util.file.MimeTypes import org.cryptomator.util.file.MimeTypes
import timber.log.Timber import timber.log.Timber
import java.lang.String.format
import java.util.*
@RequiresApi(api = Build.VERSION_CODES.N) @RequiresApi(api = Build.VERSION_CODES.N)
class PhotoContentJob : JobService() { class PhotoContentJob : JobService() {
private val mHandler = Handler() private val handler = Handler()
private val mWorker: Runnable = Runnable { private val worker: Runnable = Runnable {
scheduleJob(applicationContext) scheduleJob(applicationContext)
jobFinished(mRunningParams, false) jobFinished(runningParams, false)
} }
private lateinit var mRunningParams: JobParameters private lateinit var runningParams: JobParameters
override fun onStartJob(params: JobParameters): Boolean { override fun onStartJob(params: JobParameters): Boolean {
Timber.tag("PhotoContentJob").i("Job started!") Timber.tag("PhotoContentJob").i("Job started!")
val fileUtil = FileUtil(baseContext, MimeTypes(MimeTypeMap_Factory.newInstance())) val fileUtil = FileUtil(baseContext, MimeTypes(MimeTypeMap_Factory.newInstance()))
mRunningParams = params runningParams = params
if (params.triggeredContentAuthorities != null) {
params.triggeredContentAuthorities?.let {
if (params.triggeredContentUris != null) { if (params.triggeredContentUris != null) {
val ids = getIds(params) val ids = getIds(params)
if (ids != null && ids.size > 0) { if (ids != null && ids.isNotEmpty()) {
val selection = buildSelection(ids) val selection = buildSelection(ids)
var cursor: Cursor? = null contentResolver.query(EXTERNAL_CONTENT_URI, PROJECTION, selection, null, null).use { externalCursor ->
try { contentResolver.query(INTERNAL_CONTENT_URI, PROJECTION, selection, null, null).use { internalCursor ->
cursor = contentResolver.query(EXTERNAL_CONTENT_URI, PROJECTION, selection, null, null) MergeCursor(arrayOf(externalCursor, internalCursor)).use { cursor ->
cursor?.let { while (cursor.moveToNext()) {
while (cursor.moveToNext()) { try {
val dir = cursor.getString(PROJECTION_DATA) val dir = cursor.getString(PROJECTION_DATA)
try { fileUtil.addImageToAutoUploads(dir)
fileUtil.addImageToAutoUploads(dir) Timber.tag("PhotoContentJob").i("Added file to UploadList")
Timber.tag("PhotoContentJob").i("Added file to UploadList") Timber.tag("PhotoContentJob").d(String.format("Added file to UploadList %s", dir))
Timber.tag("PhotoContentJob").d(format("Added file to UploadList %s", dir)) } catch (e: FatalBackendException) {
} catch (e: FatalBackendException) { Timber.tag("PhotoContentJob").e(e, "Failed to add image to auto upload list")
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 { } else {
Timber.tag("PhotoContentJob").w("Photos rescan needed!") Timber.tag("PhotoContentJob").w("Photos rescan needed!")
return true 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 return false
} }
private fun getIds(params: JobParameters): ArrayList<String>? { private fun getIds(params: JobParameters): Set<String>? {
return params.triggeredContentUris return params.triggeredContentUris
?.map { it.pathSegments } ?.map { it.pathSegments }
?.filter { it != null && it.size == EXTERNAL_PATH_SEGMENTS.size + 1 } ?.filter { it != null && (it.size == EXTERNAL_CONTENT_URI.pathSegments.size + 1 || it.size == INTERNAL_CONTENT_URI.pathSegments.size + 1) }
?.mapTo(ArrayList()) { it[it.size - 1] } ?.mapTo(HashSet()) { it[it.size - 1] }
} }
private fun buildSelection(ids: ArrayList<String>): String { private fun buildSelection(ids: Set<String>): String {
val selection = StringBuilder() val selection = StringBuilder()
ids.indices.forEach { i -> ids.indices.forEach { i ->
if (selection.isNotEmpty()) { if (selection.isNotEmpty()) {
@ -94,7 +92,7 @@ class PhotoContentJob : JobService() {
} }
selection.append(MediaStore.Images.ImageColumns._ID) selection.append(MediaStore.Images.ImageColumns._ID)
selection.append("='") selection.append("='")
selection.append(ids[i]) selection.append(ids.elementAt(i))
selection.append("'") selection.append("'")
} }
return selection.toString() return selection.toString()
@ -102,7 +100,7 @@ class PhotoContentJob : JobService() {
override fun onStopJob(params: JobParameters): Boolean { override fun onStopJob(params: JobParameters): Boolean {
Timber.tag("PhotoContentJob").i("onStopJob called, must stop, reschedule later") Timber.tag("PhotoContentJob").i("onStopJob called, must stop, reschedule later")
mHandler.removeCallbacks(mWorker) handler.removeCallbacks(worker)
return true return true
} }
@ -114,7 +112,6 @@ class PhotoContentJob : JobService() {
companion object { companion object {
private val MEDIA_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/") 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 val PROJECTION = arrayOf(MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA)
internal const val PROJECTION_DATA = 1 internal const val PROJECTION_DATA = 1
@ -125,6 +122,7 @@ class PhotoContentJob : JobService() {
init { init {
val builder = JobInfo.Builder(PHOTOS_CONTENT_JOB, ComponentName(ResourceHelper.getString(R.string.app_id), PhotoContentJob::class.java.name)) 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(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)) builder.addTriggerContentUri(JobInfo.TriggerContentUri(MEDIA_URI, FLAG_NOTIFY_FOR_DESCENDANTS))
jobInfo = builder.build() jobInfo = builder.build()
} }