From 1b45db5b981ac367f8287d9074b89df914590fb7 Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Tue, 17 Nov 2020 12:24:49 +0000 Subject: [PATCH 1/2] Using CoroutinesWorker to save the blurred image in the database --- app/build.gradle | 6 +++ .../example/background/data/BlurredImage.kt | 35 ++++++++++++ .../background/data/BlurredImageDao.kt | 42 +++++++++++++++ .../example/background/data/ImagesDatabase.kt | 53 +++++++++++++++++++ .../example/background/workers/BlurWorker.kt | 49 ++++++++++------- build.gradle | 1 + 6 files changed, 167 insertions(+), 19 deletions(-) create mode 100755 app/src/main/java/com/example/background/data/BlurredImage.kt create mode 100755 app/src/main/java/com/example/background/data/BlurredImageDao.kt create mode 100755 app/src/main/java/com/example/background/data/ImagesDatabase.kt diff --git a/app/build.gradle b/app/build.gradle index 3937c7a..56117dd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,4 +67,10 @@ dependencies { implementation "com.github.bumptech.glide:glide:4.10.0" implementation "com.jakewharton.timber:timber:4.7.1" implementation "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin" + + // Room components + implementation "androidx.room:room-ktx:$versions.room" + kapt "androidx.room:room-compiler:$versions.room" + androidTestImplementation "androidx.room:room-testing:$versions.room" + } diff --git a/app/src/main/java/com/example/background/data/BlurredImage.kt b/app/src/main/java/com/example/background/data/BlurredImage.kt new file mode 100755 index 0000000..4ba2b75 --- /dev/null +++ b/app/src/main/java/com/example/background/data/BlurredImage.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.background.data + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +/** + * A basic class representing an entity that is a row in a one-column database table. + * + * @ Entity - You must annotate the class as an entity and supply a table name if not class name. + * @ PrimaryKey - You must identify the primary key. + * @ ColumnInfo - You must supply the column name if it is different from the variable name. + * + * See the documentation for the full rich set of annotations. + * https://developer.android.com/topic/libraries/architecture/room.html + */ + +@Entity(tableName = "images_table") +data class BlurredImage(@PrimaryKey @ColumnInfo(name = "uri") val uri: String) diff --git a/app/src/main/java/com/example/background/data/BlurredImageDao.kt b/app/src/main/java/com/example/background/data/BlurredImageDao.kt new file mode 100755 index 0000000..c2a75cd --- /dev/null +++ b/app/src/main/java/com/example/background/data/BlurredImageDao.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.background.data + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import kotlinx.coroutines.flow.Flow + +/** + * The Room Magic is in this file, where you map a method call to an SQL query. + * + * When you are using complex data types, such as Date, you have to also supply type converters. + * To keep this example basic, no types that require type converters are used. + * See the documentation at + * https://developer.android.com/topic/libraries/architecture/room.html#type-converters + */ + +@Dao +interface BlurredImageDao { + + @Query("SELECT * FROM images_table") + fun getBlurredImages(): Flow> + + @Insert(onConflict = OnConflictStrategy.IGNORE) + suspend fun insert(blurredImage: BlurredImage) +} diff --git a/app/src/main/java/com/example/background/data/ImagesDatabase.kt b/app/src/main/java/com/example/background/data/ImagesDatabase.kt new file mode 100755 index 0000000..149a195 --- /dev/null +++ b/app/src/main/java/com/example/background/data/ImagesDatabase.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.background.data + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +/** + * This is the backend. The database. This used to be done by the OpenHelper. + * The fact that this has very few comments emphasizes its coolness. + */ +@Database(entities = [BlurredImage::class], version = 1) +abstract class ImagesDatabase : RoomDatabase() { + + abstract fun blurredImageDao(): BlurredImageDao + + companion object { + @Volatile + private var INSTANCE: ImagesDatabase? = null + + fun getDatabase(context: Context): ImagesDatabase { + // if the INSTANCE is not null, then return it, + // if it is, then create the database + return INSTANCE ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + ImagesDatabase::class.java, + "images_database" + ).build() + INSTANCE = instance + // return instance + instance + } + } + + } +} diff --git a/app/src/main/java/com/example/background/workers/BlurWorker.kt b/app/src/main/java/com/example/background/workers/BlurWorker.kt index a51bed9..6eefeb6 100644 --- a/app/src/main/java/com/example/background/workers/BlurWorker.kt +++ b/app/src/main/java/com/example/background/workers/BlurWorker.kt @@ -19,45 +19,56 @@ package com.example.background.workers import android.content.Context import android.graphics.BitmapFactory import android.net.Uri -import android.text.TextUtils +import androidx.work.CoroutineWorker +import androidx.work.Data import androidx.work.Worker import androidx.work.WorkerParameters import androidx.work.workDataOf import com.example.background.KEY_IMAGE_URI +import com.example.background.data.BlurredImage +import com.example.background.data.ImagesDatabase import timber.log.Timber -class BlurWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) { - - override fun doWork(): Result { - val appContext = applicationContext +class BlurWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) { + override suspend fun doWork(): Result { val resourceUri = inputData.getString(KEY_IMAGE_URI) - makeStatusNotification("Blurring image", appContext) + makeStatusNotification("Blurring image", applicationContext) sleep() return try { - if (TextUtils.isEmpty(resourceUri)) { + if (resourceUri.isNullOrEmpty()) { Timber.e("Invalid input uri") throw IllegalArgumentException("Invalid input uri") } - val resolver = appContext.contentResolver - - val picture = BitmapFactory.decodeStream( - resolver.openInputStream(Uri.parse(resourceUri))) - - val output = blurBitmap(picture, appContext) - - // Write bitmap to a temp file - val outputUri = writeBitmapToFile(appContext, output) - - val outputData = workDataOf(KEY_IMAGE_URI to outputUri.toString()) - + val outputData = blurAndSaveImage(resourceUri) + recordImageSaved(resourceUri) Result.success(outputData) } catch (throwable: Throwable) { Timber.e(throwable, "Error applying blur") Result.failure() } } + + private suspend fun recordImageSaved(resourceUri: String) { + val imageDao = ImagesDatabase.getDatabase(applicationContext).blurredImageDao() + imageDao.insert(BlurredImage(resourceUri)) + } + + private fun blurAndSaveImage(resourceUri: String): Data { + val resolver = applicationContext.contentResolver + + val picture = BitmapFactory.decodeStream( + resolver.openInputStream(Uri.parse(resourceUri))) + + val output = blurBitmap(picture, applicationContext) + + // Write bitmap to a temp file + val outputUri = writeBitmapToFile(applicationContext, output) + + val outputData = workDataOf(KEY_IMAGE_URI to outputUri.toString()) + return outputData + } } diff --git a/build.gradle b/build.gradle index de92411..b669a03 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,7 @@ buildscript { versions.targetSdk = 29 versions.kotlin = '1.4.10' versions.work = "2.4.0" + versions.room = '2.2.5' repositories { google() From 7aeb7b0e9201cf3b0ece1f43eff0fa9bc7b68f5d Mon Sep 17 00:00:00 2001 From: Florina Muntenescu Date: Wed, 18 Nov 2020 11:23:08 +0000 Subject: [PATCH 2/2] Using workDataOf and renaming blur and save to file function --- .../java/com/example/background/BlurViewModel.kt | 16 +++------------- .../com/example/background/workers/BlurWorker.kt | 6 ++---- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/example/background/BlurViewModel.kt b/app/src/main/java/com/example/background/BlurViewModel.kt index 86773d9..d5fc902 100644 --- a/app/src/main/java/com/example/background/BlurViewModel.kt +++ b/app/src/main/java/com/example/background/BlurViewModel.kt @@ -27,6 +27,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkInfo import androidx.work.WorkManager +import androidx.work.workDataOf import com.example.background.workers.BlurWorker import com.example.background.workers.CleanupWorker import com.example.background.workers.SaveImageToFileWorker @@ -48,18 +49,6 @@ class BlurViewModel(application: Application) : AndroidViewModel(application) { workManager.cancelUniqueWork(IMAGE_MANIPULATION_WORK_NAME) } - /** - * Creates the input data bundle which includes the Uri to operate on - * @return Data which contains the Image Uri as a String - */ - private fun createInputDataForUri(): Data { - val builder = Data.Builder() - imageUri?.let { - builder.putString(KEY_IMAGE_URI, imageUri.toString()) - } - return builder.build() - } - /** * Create the WorkRequest to apply the blur and save the resulting image * @param blurLevel The amount to blur the image @@ -81,7 +70,8 @@ class BlurViewModel(application: Application) : AndroidViewModel(application) { // After the first blur operation the input will be the output of previous // blur operations. if (i == 0) { - blurBuilder.setInputData(createInputDataForUri()) + val data = workDataOf(KEY_IMAGE_URI to imageUri.toString()) + blurBuilder.setInputData(data) } continuation = continuation.then(blurBuilder.build()) diff --git a/app/src/main/java/com/example/background/workers/BlurWorker.kt b/app/src/main/java/com/example/background/workers/BlurWorker.kt index 6eefeb6..88ffc4f 100644 --- a/app/src/main/java/com/example/background/workers/BlurWorker.kt +++ b/app/src/main/java/com/example/background/workers/BlurWorker.kt @@ -21,7 +21,6 @@ import android.graphics.BitmapFactory import android.net.Uri import androidx.work.CoroutineWorker import androidx.work.Data -import androidx.work.Worker import androidx.work.WorkerParameters import androidx.work.workDataOf import com.example.background.KEY_IMAGE_URI @@ -35,7 +34,6 @@ class BlurWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, val resourceUri = inputData.getString(KEY_IMAGE_URI) makeStatusNotification("Blurring image", applicationContext) - sleep() return try { if (resourceUri.isNullOrEmpty()) { @@ -43,7 +41,7 @@ class BlurWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, throw IllegalArgumentException("Invalid input uri") } - val outputData = blurAndSaveImage(resourceUri) + val outputData = blurAndWriteImageToFile(resourceUri) recordImageSaved(resourceUri) Result.success(outputData) } catch (throwable: Throwable) { @@ -57,7 +55,7 @@ class BlurWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, imageDao.insert(BlurredImage(resourceUri)) } - private fun blurAndSaveImage(resourceUri: String): Data { + private fun blurAndWriteImageToFile(resourceUri: String): Data { val resolver = applicationContext.contentResolver val picture = BitmapFactory.decodeStream(