Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RMET-1240 H&F Plugin - Implement Notification Frequency (waiting period) on Android #49

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
The changes documented here do not include those from the original repository.

## [Unreleased]
- Implementation of notificaiton frequency (waiting period) for Android (https://outsystemsrd.atlassian.net/browse/RMET-1240)

## [Version 1.0.2]

## 2021-11-18
- Implementation of new variables and new version of background job iOS (https://outsystemsrd.atlassian.net/browse/RMET-1138)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ data class BackgroundJobParameters (
@SerializedName("TimeUnit") val timeUnit : String? = null,
@SerializedName("TimeUnitGrouping") val timeUnitGrouping : Int? = 1,
@SerializedName("JobFrequency") val jobFrequency : String? = null,
@SerializedName("NotificationFrequency") val notificationFrequency : String? = null,
@SerializedName("NotificationFrequencyGrouping") val notificationFrequencyGrouping : Int? = 1,
@SerializedName("NotificationHeader") val notificationHeader : String,
@SerializedName("NotificationBody") val notificationBody : String
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.room.Room
import com.outsystems.plugins.healthfitnesslib.background.database.AppDatabase
import com.outsystems.plugins.healthfitnesslib.background.database.BackgroundJob
import com.outsystems.plugins.healthfitnesslib.store.AdvancedQueryParameters
import com.outsystems.plugins.healthfitnesslib.store.HealthFitnessManager
Expand Down Expand Up @@ -55,60 +53,98 @@ class VariableUpdateService : BroadcastReceiver() {

backgroundJobs?.forEach { job ->

job.notificationId?.let { notificationId ->
db.fetchNotification(notificationId)?.let { notification ->

val notificationTitle = notification.title
val notificationBody = notification.body
val notificationID = notification.notificationID

val endDate: Long = Date().time
val month = 2592000000
val startDate: Long = endDate - month

val queryParams = AdvancedQueryParameters(
variableName,
Date(startDate),
Date(endDate),
job.timeUnit,
job.timeUnitGrouping,
operationType
)
store.advancedQueryAsync(
queryParams,
{ response ->
var willTriggerJob = false

if(response.results.isNotEmpty()) {
val comparison = job.comparison
val triggerValue = job.value
val currentValue = response.results.last().values.last()
when(comparison){
BackgroundJob.ComparisonOperationEnum.EQUALS.id ->
willTriggerJob = currentValue == triggerValue
BackgroundJob.ComparisonOperationEnum.GREATER.id ->
willTriggerJob = currentValue > triggerValue
BackgroundJob.ComparisonOperationEnum.LESSER.id ->
willTriggerJob = currentValue < triggerValue
BackgroundJob.ComparisonOperationEnum.GREATER_OR_EQUALS.id ->
willTriggerJob = currentValue >= triggerValue
BackgroundJob.ComparisonOperationEnum.LESSER_OR_EQUALS.id ->
willTriggerJob = currentValue <= triggerValue
}
}
val currentTimestamp = System.currentTimeMillis()
val nextNotificationTimestamp = job.nextNotificationTimestamp

if(willTriggerJob){
sendNotification(context, notificationTitle, notificationBody, notificationID)
}
},
{ error ->
//TODO: What should we do with errors?
}
)
}
if(currentTimestamp >= nextNotificationTimestamp || job.notificationFrequency == "ALWAYS") {

val nextNotificationCalendar = Calendar.getInstance()
nextNotificationCalendar.timeInMillis = currentTimestamp
nextNotificationCalendar.minimalDaysInFirstWeek = 4
nextNotificationCalendar.firstDayOfWeek = Calendar.MONDAY

if(job.notificationFrequency == "SECOND") {
nextNotificationCalendar.add(Calendar.SECOND, job.notificationFrequencyGrouping)
}
else if(job.notificationFrequency == "MINUTE") {
nextNotificationCalendar.add(Calendar.MINUTE, job.notificationFrequencyGrouping)
}
else if(job.notificationFrequency == "HOUR") {
nextNotificationCalendar.add(Calendar.HOUR, job.notificationFrequencyGrouping)
nextNotificationCalendar.startOfUnit(Calendar.HOUR)
}
else if(job.notificationFrequency == "DAY") {
nextNotificationCalendar.add(Calendar.DATE, job.notificationFrequencyGrouping)
nextNotificationCalendar.startOfUnit(Calendar.DATE)
}
else if(job.notificationFrequency == "WEEK") {
nextNotificationCalendar.add(Calendar.WEEK_OF_MONTH, job.notificationFrequencyGrouping)
nextNotificationCalendar.startOfUnit(Calendar.WEEK_OF_MONTH)
}
else if(job.notificationFrequency == "MONTH") {
nextNotificationCalendar.add(Calendar.MONTH, job.notificationFrequencyGrouping)
nextNotificationCalendar.startOfUnit(Calendar.MONTH)
}
else if (job.notificationFrequency == "YEAR") {
nextNotificationCalendar.add(Calendar.YEAR, job.notificationFrequencyGrouping)
nextNotificationCalendar.startOfUnit(Calendar.YEAR)
}

job.nextNotificationTimestamp = nextNotificationCalendar.timeInMillis

database.updateBackgroundJob(job)
job.notificationId?.let { notificationId ->
db.fetchNotification(notificationId)?.let { notification ->

val notificationTitle = notification.title
val notificationBody = notification.body
val notificationID = notification.notificationID

val endDate: Long = Date().time
val month = 2592000000
val startDate: Long = endDate - month

val queryParams = AdvancedQueryParameters(
variableName,
Date(startDate),
Date(endDate),
job.timeUnit,
job.timeUnitGrouping,
operationType
)
store.advancedQueryAsync(
queryParams,
{ response ->
var willTriggerJob = false

if(response.results.isNotEmpty()) {
val comparison = job.comparison
val triggerValue = job.value
val currentValue = response.results.last().values.last()
when(comparison){
BackgroundJob.ComparisonOperationEnum.EQUALS.id ->
willTriggerJob = currentValue == triggerValue
BackgroundJob.ComparisonOperationEnum.GREATER.id ->
willTriggerJob = currentValue > triggerValue
BackgroundJob.ComparisonOperationEnum.LESSER.id ->
willTriggerJob = currentValue < triggerValue
BackgroundJob.ComparisonOperationEnum.GREATER_OR_EQUALS.id ->
willTriggerJob = currentValue >= triggerValue
BackgroundJob.ComparisonOperationEnum.LESSER_OR_EQUALS.id ->
willTriggerJob = currentValue <= triggerValue
}
}

if(willTriggerJob){
sendNotification(context, notificationTitle, notificationBody, notificationID)
}
},
{ error ->
//TODO: What should we do with errors?
}
)
}
}
}
}
}
Expand Down Expand Up @@ -150,6 +186,37 @@ class VariableUpdateService : BroadcastReceiver() {

}

private fun Calendar.startOfUnit(unit: Int): Calendar {
var unitVal = 0
when(unit){
Calendar.HOUR -> unitVal = 1
Calendar.DATE -> unitVal = 2
Calendar.WEEK_OF_MONTH -> unitVal = 3
Calendar.MONTH -> unitVal = 4
Calendar.YEAR -> unitVal = 5
}

if(unitVal >= 5){
this.set(Calendar.MONTH, 0)
}
if(unitVal >= 4){
this.set(Calendar.DAY_OF_MONTH, 1)
}
if(unitVal == 3){
this.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY)
}
if(unitVal >= 2){
this.set(Calendar.HOUR_OF_DAY, 0)
}
if(unitVal >= 1){
this.set(Calendar.MILLISECOND, 0)
this.set(Calendar.SECOND, 0)
this.set(Calendar.MINUTE, 0)
}

return this
}

companion object {
const val VARIABLE_NAME = "VARIABLE_NAME"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,30 @@ package com.outsystems.plugins.healthfitnesslib.background.database

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.room.migration.Migration

@Database(entities = [BackgroundJob::class, Notification::class], version = 1)
@Database(
entities = [BackgroundJob::class, Notification::class],
version = 2
)
abstract class AppDatabase : RoomDatabase() {
abstract fun backgroundJobDao(): BackgroundJobDao
abstract fun notificationDao(): NotificationDao

companion object {
val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"ALTER TABLE ${BackgroundJob.TABLE_NAME} " +
"ADD COLUMN notification_frequency TEXT NOT NULL DEFAULT 'ALWAYS'")
database.execSQL(
"ALTER TABLE ${BackgroundJob.TABLE_NAME} " +
"ADD COLUMN notification_frequency_grouping INTEGER NOT NULL DEFAULT 1")
database.execSQL(
"ALTER TABLE ${BackgroundJob.TABLE_NAME} " +
"ADD COLUMN next_notification_timestamp INTEGER NOT NULL DEFAULT 0")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.room.Entity
import androidx.room.ForeignKey

@Entity(
tableName = BackgroundJob.TABLE_NAME,
primaryKeys = ["variable", "comparison", "value"],
foreignKeys = [ForeignKey(
entity = Notification::class,
Expand All @@ -27,9 +28,11 @@ open class BackgroundJob {
@ColumnInfo(name = "time_unit") var timeUnit: String? = null
@ColumnInfo(name = "time_unit_grouping") var timeUnitGrouping: Int? = null
@ColumnInfo(name = "notification_id") var notificationId: Long? = null
}




@ColumnInfo(name = "notification_frequency") var notificationFrequency: String = "ALWAYS"
@ColumnInfo(name = "notification_frequency_grouping") var notificationFrequencyGrouping: Int = 1
@ColumnInfo(name = "next_notification_timestamp") var nextNotificationTimestamp: Long = 0

companion object {
const val TABLE_NAME: String= "backgroundJob"
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package com.outsystems.plugins.healthfitnesslib.background.database

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.*

@Dao
interface BackgroundJobDao {

@Query("SELECT * FROM backgroundJob")
@Query("SELECT * FROM ${BackgroundJob.TABLE_NAME}")
fun getAll(): List<BackgroundJob>

//@Query("SELECT * FROM backgroundJob WHERE variable = :variable AND comparison = :comparison AND value = :value")
//fun findByPrimaryKey(variable: String, comparison: String, value: Float)

@Query("SELECT * FROM backgroundJob WHERE variable = :name")
@Query("SELECT * FROM ${BackgroundJob.TABLE_NAME} WHERE variable = :name")
fun findByVariableName(name : String): List<BackgroundJob>

@Insert
Expand All @@ -23,4 +20,7 @@ interface BackgroundJobDao {
@Delete
fun delete(backgroundJob: BackgroundJob)

@Update
fun update(backgroundJob: BackgroundJob)

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ class DatabaseManager(context : Context) : DatabaseManagerInterface {
// Should we close the database instance? If so, when.
database = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java, "database-name"
).build()
AppDatabase::class.java, "database-name")
.addMigrations(AppDatabase.MIGRATION_1_2)
.build()

backgroundJobDao = database!!.backgroundJobDao()
notificationDao = database!!.notificationDao()
Expand Down Expand Up @@ -67,6 +68,10 @@ class DatabaseManager(context : Context) : DatabaseManagerInterface {
backgroundJobDao?.delete(backgroundJob)
}

override fun updateBackgroundJob(backgroundJob: BackgroundJob) {
backgroundJobDao?.update(backgroundJob)
}

override fun runInTransaction(closude : () -> Unit){
database?.runInTransaction(Runnable {
closude()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ interface DatabaseManagerInterface {
fun fetchBackgroundJobs(variable : String) : List<BackgroundJob>?
fun fetchNotification(id : Long) : Notification?
fun deleteBackgroundJob(backgroundJob : BackgroundJob)
fun updateBackgroundJob(backgroundJob: BackgroundJob)
fun runInTransaction(closude : () -> Unit)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ class Notification{
return random.nextInt()
}

companion object {
const val TABLE_NAME: String= "notification"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import androidx.room.Query
@Dao
interface NotificationDao {

@Query("SELECT * FROM notification")
@Query("SELECT * FROM ${Notification.TABLE_NAME}")
fun getAll(): List<Notification>

@Query("SELECT * FROM notification WHERE id = :id")
@Query("SELECT * FROM ${Notification.TABLE_NAME} WHERE id = :id")
fun findById(id : Long): List<Notification>

@Insert
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -669,8 +669,8 @@ class HealthStore(
launch(Dispatchers.IO) {

try {
database.runInTransaction({
val nNotification = database.fetchNotifications()
database.runInTransaction {

val notification = Notification().apply {
this.title = parameters.notificationHeader
this.body = parameters.notificationBody
Expand All @@ -685,9 +685,16 @@ class HealthStore(
this.notificationId = notificationId
this.timeUnit = parameters.timeUnit
this.timeUnitGrouping = parameters.timeUnitGrouping

this.notificationFrequency =
parameters.notificationFrequency.toString()
this.notificationFrequencyGrouping =
parameters.notificationFrequencyGrouping!!

this.nextNotificationTimestamp = System.currentTimeMillis()
}
database.insert(backgroundJob)
})
}
onSuccess("success")
} catch(sqle : SQLiteException) {
onError(HealthFitnessError.BACKGROUND_JOB_ALREADY_EXISTS_ERROR)
Expand Down