diff --git a/.gitignore b/.gitignore
index 2c9df55..9752de8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ secrets.properties
api-5245190277294621651-718463-f914fb7573c6.json
*.jks
google-services.json
+services.xml
diff --git a/CHANGELOG b/CHANGELOG
index 2b660be..84f6a14 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,6 @@
+## v1.1.1
++ New notifications handler - now you must get notified when expected
+
## v1.1.0
+ NEW: OkHttp for handling file downloading.
+ Corrected a minor bug while displaying images.
diff --git a/README.md b/README.md
index 6683287..712aa89 100644
--- a/README.md
+++ b/README.md
@@ -66,6 +66,9 @@ will try to implement classic APK version.
[Download from Play Store](https://play.google.com/store/apps/details?id=com.javinator9889.handwashingreminder)
+
+
+
## Contributing
If you want to contribute:
diff --git a/ads/build.gradle b/ads/build.gradle
index 0719842..12eb5ec 100644
--- a/ads/build.gradle
+++ b/ads/build.gradle
@@ -33,7 +33,7 @@ dependencies {
// https://developer.android.com/kotlin/ktx#play-core
implementation 'com.google.android.play:core-ktx:1.7.0'
// https://firebase.google.com/docs/admob/android/quick-start#import_the_mobile_ads_sdk
- implementation 'com.google.firebase:firebase-ads:19.0.1'
+ implementation 'com.google.firebase:firebase-ads:19.1.0'
// https://developer.android.com/studio/build/multidex
implementation 'androidx.multidex:multidex:2.0.1'
// https://github.com/JakeWharton/timber
diff --git a/app/build.gradle b/app/build.gradle
index 121cf7f..25c689b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,9 +3,8 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
-apply plugin: 'com.google.gms.google-services'
-apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.firebase.firebase-perf'
+apply plugin: 'com.google.firebase.crashlytics'
def secretsPropertiesFile = rootProject.file("secrets.properties")
@@ -43,8 +42,8 @@ android {
applicationId "com.javinator9889.handwashingreminder"
minSdkVersion 17
targetSdkVersion 29
- versionCode 103
- versionName "1.1.0-${gitCommitHash}"
+ versionCode 107
+ versionName "1.1.1-${gitCommitHash}"
multiDexEnabled true
resConfigs "en", "es"
@@ -143,7 +142,6 @@ dependencies {
// https://kotlinlang.org/docs/reference/reflection.html
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
// https://firebase.google.com/docs/android/setup#add-sdks
- implementation 'com.google.firebase:firebase-core:17.3.0'
implementation 'com.google.firebase:firebase-common-ktx:19.3.0'
implementation 'com.google.firebase:firebase-analytics:17.3.0'
implementation 'com.google.firebase:firebase-crashlytics:17.0.0-beta04'
@@ -152,6 +150,7 @@ dependencies {
implementation "com.airbnb.android:lottie:3.4.0"
// https://firebase.google.com/docs/remote-config/use-config-android
implementation 'com.google.firebase:firebase-config:19.1.3'
+ implementation 'com.google.firebase:firebase-config-ktx:19.1.3'
// https://developer.android.com/jetpack/androidx/releases/work#declaring_dependencies
implementation 'androidx.work:work-runtime-ktx:2.3.4'
// https://mvnrepository.com/artifact/androidx.emoji/emoji/1.0.0
@@ -172,7 +171,7 @@ dependencies {
// https://developer.android.com/jetpack/androidx/releases/lifecycle#declaring_dependencies
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
// https://developer.android.com/reference/kotlin/androidx/preference/package-summary
- implementation 'androidx.preference:preference:1.1.0'
+ implementation 'androidx.preference:preference:1.1.1'
// https://github.com/bumptech/glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0'
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 531af97..4d1d5e2 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -14,6 +14,7 @@
# Uncomment this to preserve the line number information for
# debugging stack traces.
+-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
@@ -78,3 +79,7 @@
#data models
-keep class com.javinator9889.handwashingreminder.collections.** { *;}
+
+# prevent Crashlytics obfuscation
+-keep class com.google.firebase.crashlytics.** { *; }
+-dontwarn com.google.firebase.crashlytics.**
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5d6b744..a4984aa 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -8,6 +8,7 @@
+
@@ -75,6 +76,10 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt
index d31fdd6..203aa7f 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/DynamicFeatureProgress.kt
@@ -116,9 +116,8 @@ class DynamicFeatureProgress : SplitCompatBaseActivity(),
SplitInstallSessionStatus.FAILED -> {
Toast.makeText(
this, getString(
- R.string
- .dynamic_module_loading_error
- ), Toast.LENGTH_LONG
+ R.string.dynamic_module_loading_error, state.errorCode),
+ Toast.LENGTH_LONG
).show()
Timber.e(
"Installation failed - error code: ${state.errorCode}"
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt
index 7db66e8..f229aa9 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/LauncherActivity.kt
@@ -32,15 +32,17 @@ import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.play.core.splitcompat.SplitCompat
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory
import com.google.firebase.analytics.FirebaseAnalytics
+import com.google.firebase.ktx.Firebase
import com.google.firebase.perf.FirebasePerformance
-import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings
+import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.javinator9889.handwashingreminder.R
import com.javinator9889.handwashingreminder.application.HandwashingApplication
import com.javinator9889.handwashingreminder.emoji.EmojiLoader
import com.javinator9889.handwashingreminder.gms.ads.AdLoader
import com.javinator9889.handwashingreminder.gms.ads.AdsEnabler
import com.javinator9889.handwashingreminder.gms.vendor.BillingService
+import com.javinator9889.handwashingreminder.jobs.workers.WorkHandler
import com.javinator9889.handwashingreminder.utils.*
import com.javinator9889.handwashingreminder.utils.Preferences.Companion.ADS_ENABLED
import com.javinator9889.handwashingreminder.utils.Preferences.Companion.APP_INIT_KEY
@@ -54,6 +56,7 @@ import timber.log.Timber
import java.security.Security
import java.util.*
import kotlin.collections.ArrayList
+import com.javinator9889.handwashingreminder.utils.Firebase as FirebaseConf
internal const val FAST_START_KEY = "intent:fast_start"
internal const val PENDING_INTENT_CODE = 201
@@ -94,7 +97,8 @@ class LauncherActivity : AppCompatActivity() {
}
private suspend fun displayWelcomeScreen() {
- val isThereAnySpecialEvent = with(FirebaseRemoteConfig.getInstance()) {
+ app.firebaseInitDeferred.await()
+ val isThereAnySpecialEvent = with(Firebase.remoteConfig) {
getBoolean(SPECIAL_EVENT) && !launchFromNotification
}
var sleepDuration = 0L
@@ -185,10 +189,10 @@ class LauncherActivity : AppCompatActivity() {
modules += AppIntro.MODULE_NAME
launchOnInstall = true
}
- modules += if (isAtLeast(AndroidVersion.LOLLIPOP))
+ /*modules += if (isAtLeast(AndroidVersion.LOLLIPOP))
OkHttp.MODULE_NAME
else
- OkHttpLegacy.MODULE_NAME
+ OkHttpLegacy.MODULE_NAME*/
if (googleApi.isGooglePlayServicesAvailable(
this,
GOOGLE_PLAY_SERVICES_MIN_VERSION
@@ -235,7 +239,9 @@ class LauncherActivity : AppCompatActivity() {
it.putExtra(DynamicFeatureProgress.PACKAGE_NAME, packageName)
}
- private fun initVariables() {
+ private suspend fun initVariables() {
+ app.firebaseInitDeferred.await()
+ Timber.d("Firebase initialized correctly")
Timber.d("Initializing Iconics")
Iconics.init(this)
Timber.d("Setting-up security providers")
@@ -254,19 +260,17 @@ class LauncherActivity : AppCompatActivity() {
}
Timber.d("Initializing Billing Service")
app.billingService = BillingService(this)
- try {
- app.workHandler.enqueuePeriodicNotificationsWorker()
- Timber.d("Adding periodic notifications if not enqueued yet")
- } catch (_: UninitializedPropertyAccessException) {
- Timber.i("Scheduler times have not been initialized")
+ with(WorkHandler(this)) {
+ enqueuePeriodicNotificationsWorker()
}
+ Timber.d("Adding periodic notifications if not enqueued yet")
Timber.d("Setting-up Firebase custom properties")
setupFirebaseProperties()
}
private fun setupFirebaseProperties() {
val firebaseAnalytics = FirebaseAnalytics.getInstance(this)
- val firebaseRemoteConfig = FirebaseRemoteConfig.getInstance()
+ val firebaseRemoteConfig = Firebase.remoteConfig
val firebasePerformance = FirebasePerformance.getInstance()
val config = with(FirebaseRemoteConfigSettings.Builder()) {
minimumFetchIntervalInSeconds = 10
@@ -280,14 +284,14 @@ class LauncherActivity : AppCompatActivity() {
when (Locale.getDefault().language) {
Locale(LanguagesSupport.Language.SPANISH).language -> {
firebaseAnalytics.setUserProperty(
- Firebase.Properties.LANGUAGE,
+ FirebaseConf.Properties.LANGUAGE,
LanguagesSupport.Language.SPANISH
)
R.xml.remote_config_defaults_es
}
else -> {
firebaseAnalytics.setUserProperty(
- Firebase.Properties.LANGUAGE,
+ FirebaseConf.Properties.LANGUAGE,
LanguagesSupport.Language.ENGLISH
)
R.xml.remote_config_defaults
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt
index 74fb405..e6be2f5 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/MainActivity.kt
@@ -29,8 +29,9 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.firebase.analytics.FirebaseAnalytics
+import com.google.firebase.ktx.Firebase
import com.google.firebase.perf.metrics.AddTrace
-import com.google.firebase.remoteconfig.FirebaseRemoteConfig
+import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.javinator9889.handwashingreminder.R
import com.javinator9889.handwashingreminder.activities.support.ActionBarBase
import com.javinator9889.handwashingreminder.activities.views.fragments.diseases.DiseasesFragment
@@ -61,7 +62,7 @@ class MainActivity : ActionBarBase(),
with(FirebaseAnalytics.getInstance(this)) {
setCurrentScreen(this@MainActivity, "Main view", null)
}
- with(FirebaseRemoteConfig.getInstance()) {
+ with(Firebase.remoteConfig) {
fetchAndActivate()
}
delegateMenuIcons(menu)
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/NewsFragment.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/NewsFragment.kt
index 667eacb..6cad6a3 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/NewsFragment.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/news/NewsFragment.kt
@@ -21,7 +21,8 @@ package com.javinator9889.handwashingreminder.activities.views.fragments.news
import android.os.Bundle
import android.view.View
import androidx.annotation.LayoutRes
-import com.google.firebase.remoteconfig.FirebaseRemoteConfig
+import com.google.firebase.ktx.Firebase
+import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.javinator9889.handwashingreminder.R
import com.javinator9889.handwashingreminder.activities.base.BaseFragmentView
import com.javinator9889.handwashingreminder.utils.RemoteConfig
@@ -40,7 +41,7 @@ class NewsFragment : BaseFragmentView() {
ARG_UNDER_CONSTRUCTION_TEXT
)
} else {
- with(FirebaseRemoteConfig.getInstance()) {
+ with(Firebase.remoteConfig) {
underConstructionText.text =
getString(RemoteConfig.WORK_IN_PROGRESS)
}
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/ActivityCheckbox.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/ActivityCheckbox.kt
index 5575ce4..aa3923e 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/ActivityCheckbox.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/ActivityCheckbox.kt
@@ -45,6 +45,8 @@ class ActivityCheckbox : CheckBoxPreference {
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
+ private var firstCheck = true
+
init {
var isViewDisabled = false
with(GoogleApiAvailability.getInstance()) {
@@ -83,6 +85,10 @@ class ActivityCheckbox : CheckBoxPreference {
override fun setChecked(checked: Boolean) {
super.setChecked(checked)
+ if (firstCheck) {
+ firstCheck = false
+ return
+ }
with(HandwashingApplication.getInstance()) {
if (checked) {
activityHandler.startTrackingActivity()
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/ActivityMultiSelectList.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/ActivityMultiSelectList.kt
index a182432..b9fb6a6 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/ActivityMultiSelectList.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/fragments/settings/ActivityMultiSelectList.kt
@@ -29,6 +29,7 @@ import com.mikepenz.iconics.utils.sizeDp
import java.util.*
class ActivityMultiSelectList : MultiSelectListPreference {
+ private var isFirstCall = true
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
@@ -48,7 +49,10 @@ class ActivityMultiSelectList : MultiSelectListPreference {
override fun notifyChanged() {
super.notifyChanged()
loadSummary()
- reloadActivityHandler()
+ if (!isFirstCall)
+ reloadActivityHandler()
+ else
+ isFirstCall = false
}
private fun loadSummary() {
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/DiseaseInformationViewModel.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/DiseaseInformationViewModel.kt
index f582f0b..e978ada 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/DiseaseInformationViewModel.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/DiseaseInformationViewModel.kt
@@ -27,7 +27,8 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import com.beust.klaxon.Klaxon
-import com.google.firebase.remoteconfig.FirebaseRemoteConfig
+import com.google.firebase.ktx.Firebase
+import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.javinator9889.handwashingreminder.collections.DiseasesInformation
import com.javinator9889.handwashingreminder.collections.DiseasesList
import com.javinator9889.handwashingreminder.collections.DiseasesListWrapper
@@ -55,7 +56,7 @@ class DiseaseInformationViewModel(
state.get>(PARSED_JSON_KEY)!!
)
val diseasesString =
- with(FirebaseRemoteConfig.getInstance()) {
+ with(Firebase.remoteConfig) {
getString(DISEASES_JSON)
}
Klaxon().parse(diseasesString)
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/VideoModel.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/VideoModel.kt
index 2c03d8f..dbd3a77 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/VideoModel.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/VideoModel.kt
@@ -42,7 +42,6 @@ import java.io.FileInputStream
import java.io.InputStream
import java.math.BigInteger
import java.security.MessageDigest
-import javax.inject.Inject
private const val LIVEDATA_KEY = "videomodel:livedata"
@@ -128,7 +127,7 @@ class VideoModel(
}
}
-class VideoModelFactory @Inject constructor(private val position: Int) :
+class VideoModelFactory constructor(private val position: Int) :
ViewModelAssistedFactory {
override fun create(handle: SavedStateHandle) = VideoModel(handle, position)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/WashingHandsModel.kt b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/WashingHandsModel.kt
index b3bf94a..afa4234 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/WashingHandsModel.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/activities/views/viewmodels/WashingHandsModel.kt
@@ -27,7 +27,6 @@ import androidx.lifecycle.liveData
import com.javinator9889.handwashingreminder.R
import com.javinator9889.handwashingreminder.application.HandwashingApplication
import com.javinator9889.handwashingreminder.emoji.EmojiLoader
-import javax.inject.Inject
internal data class Measurements(var width: Int, var height: Int)
@@ -102,7 +101,7 @@ class WashingHandsModel(
}
}
-class WashingHandsModelFactory @Inject constructor(
+class WashingHandsModelFactory constructor(
private val position: Int
) : ViewModelAssistedFactory {
override fun create(handle: SavedStateHandle) =
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/application/HandwashingApplication.kt b/app/src/main/java/com/javinator9889/handwashingreminder/application/HandwashingApplication.kt
index ca67d1e..4929eaf 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/application/HandwashingApplication.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/application/HandwashingApplication.kt
@@ -20,27 +20,30 @@ package com.javinator9889.handwashingreminder.application
import android.content.Context
import android.content.SharedPreferences
+import android.util.Log
import androidx.multidex.MultiDex
import androidx.preference.PreferenceManager
import com.google.android.play.core.splitcompat.SplitCompat
+import com.google.firebase.FirebaseApp
+import com.google.firebase.FirebaseOptions
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.javinator9889.handwashingreminder.gms.activity.ActivityHandler
import com.javinator9889.handwashingreminder.gms.ads.AdLoader
import com.javinator9889.handwashingreminder.gms.vendor.BillingService
-import com.javinator9889.handwashingreminder.jobs.workers.WorkHandler
import com.javinator9889.handwashingreminder.utils.LogReportTree
import com.javinator9889.handwashingreminder.utils.isDebuggable
import javinator9889.localemanager.application.BaseApplication
import javinator9889.localemanager.utils.languagesupport.LanguagesSupport.Language
+import kotlinx.coroutines.*
import timber.log.Timber
class HandwashingApplication : BaseApplication() {
var adLoader: AdLoader? = null
- lateinit var workHandler: WorkHandler
lateinit var billingService: BillingService
lateinit var activityHandler: ActivityHandler
lateinit var sharedPreferences: SharedPreferences
+ lateinit var firebaseInitDeferred: Deferred
companion object {
private lateinit var instance: HandwashingApplication
@@ -63,8 +66,8 @@ class HandwashingApplication : BaseApplication() {
super.onCreate()
instance = this
sharedPreferences = getCustomSharedPreferences(this)
-
- if (isDebuggable()) {
+ activityHandler = ActivityHandler(this)
+ /*if (isDebuggable()) {
Timber.plant(Timber.DebugTree())
Timber.d("Application is in DEBUG mode")
with(FirebaseCrashlytics.getInstance()) {
@@ -72,9 +75,30 @@ class HandwashingApplication : BaseApplication() {
}
} else {
Timber.plant(LogReportTree())
+ }*/
+ firebaseInitDeferred = initFirebaseAppAsync()
+ Log.d("Application", "Deferred Firebase Instantiating")
+ }
+
+ private fun initFirebaseAppAsync(): Deferred {
+ return GlobalScope.async {
+ withContext(Dispatchers.IO) {
+ FirebaseApp.initializeApp(
+ this@HandwashingApplication,
+ FirebaseOptions
+ .fromResource(this@HandwashingApplication)!!
+ )
+ if (isDebuggable()) {
+ Timber.plant(Timber.DebugTree())
+ Timber.d("Application is in DEBUG mode")
+ with(FirebaseCrashlytics.getInstance()) {
+ setCrashlyticsCollectionEnabled(false)
+ }
+ } else {
+ Timber.plant(LogReportTree())
+ }
+ }
}
- activityHandler = ActivityHandler(this)
- workHandler = WorkHandler(this)
}
/**
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/emoji/EmojiLoader.kt b/app/src/main/java/com/javinator9889/handwashingreminder/emoji/EmojiLoader.kt
index 98386ca..1f6019a 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/emoji/EmojiLoader.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/emoji/EmojiLoader.kt
@@ -38,10 +38,12 @@ object EmojiLoader {
emojiCompat.registerInitCallback(
object : EmojiCompat.InitCallback() {
override fun onInitialized() {
+ emojiCompat.unregisterInitCallback(this)
deferred.complete(EmojiCompat.get())
}
override fun onFailed(throwable: Throwable?) {
+ emojiCompat.unregisterInitCallback(this)
val exception = throwable
?: RuntimeException("EmojiCompat failed to load")
deferred.completeExceptionally(exception)
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/BootCompletedJob.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/BootCompletedJob.kt
index 1af5a8c..69d0cdd 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/BootCompletedJob.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/BootCompletedJob.kt
@@ -22,6 +22,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.javinator9889.handwashingreminder.application.HandwashingApplication
+import com.javinator9889.handwashingreminder.jobs.workers.WorkHandler
import com.javinator9889.handwashingreminder.utils.Preferences
import timber.log.Timber
@@ -39,9 +40,14 @@ class BootCompletedJob : BroadcastReceiver() {
else
app.activityHandler.disableActivityTracker()
try {
- app.workHandler.enqueuePeriodicNotificationsWorker()
- } catch (e: UninitializedPropertyAccessException) {
- Timber.e(e, "Schedule times have not been initialized yet")
+ Timber.d("Enqueuing notifications as the device has rebooted")
+ with(WorkHandler(requireNotNull(context))) {
+ enqueuePeriodicNotificationsWorker()
+ }
+ } catch (_: IllegalArgumentException) {
+ Timber.w(
+ "Context is null so notifications cannot be scheduled"
+ )
}
}
}
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt
index 9a0b4c7..df52419 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/UpdateReceiver.kt
@@ -21,12 +21,22 @@ package com.javinator9889.handwashingreminder.jobs
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import com.javinator9889.handwashingreminder.application.HandwashingApplication
+import com.javinator9889.handwashingreminder.jobs.workers.WorkHandler
+import timber.log.Timber
class UpdateReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
- with(HandwashingApplication.getInstance().workHandler) {
- enqueuePeriodicNotificationsWorker(true)
+ if (intent?.action == Intent.ACTION_MY_PACKAGE_REPLACED) {
+ Timber.d("Package updated so rescheduling jobs")
+ try {
+ with(WorkHandler(requireNotNull(context))) {
+ enqueuePeriodicNotificationsWorker(true)
+ }
+ } catch (_: IllegalArgumentException) {
+ Timber.w(
+ "Context is null so notifications cannot be rescheduled"
+ )
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/AbstractNotificationsWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/AbstractNotificationsWorker.kt
new file mode 100644
index 0000000..29f7136
--- /dev/null
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/AbstractNotificationsWorker.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2020 - present | Handwashing reminder by Javinator9889
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see https://www.gnu.org/licenses/.
+ *
+ * Created by Javinator9889 on 22/04/20 - Handwashing reminder.
+ */
+package com.javinator9889.handwashingreminder.jobs.workers
+
+import android.content.Context
+import androidx.annotation.ArrayRes
+import androidx.annotation.IntRange
+import androidx.annotation.StringRes
+import androidx.preference.PreferenceManager
+import androidx.work.*
+import com.javinator9889.handwashingreminder.R
+import com.javinator9889.handwashingreminder.application.HandwashingApplication
+import com.javinator9889.handwashingreminder.emoji.EmojiLoader
+import com.javinator9889.handwashingreminder.notifications.NotificationsHandler
+import com.javinator9889.handwashingreminder.utils.TIME_CHANNEL_ID
+import com.javinator9889.handwashingreminder.utils.runAt
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeoutOrNull
+import timber.log.Timber
+import java.util.concurrent.TimeUnit
+
+data class WorkScheduleParams(
+ @IntRange(from = 0, to = 23) val hour: Int,
+ @IntRange(from = 0, to = 59) val minute: Int
+)
+
+abstract class AbstractNotificationsWorker(
+ context: Context,
+ params: WorkerParameters
+) : CoroutineWorker(context, params) {
+ protected var maxRetries = 5
+ protected var shouldScheduleNext = true
+ protected var workConstraints = with(Constraints.Builder()) {
+ setRequiredNetworkType(NetworkType.NOT_REQUIRED)
+ setRequiresBatteryNotLow(false)
+ setRequiresCharging(false)
+ setRequiresDeviceIdle(false)
+ setRequiresStorageNotLow(false)
+ build()
+ }
+ protected abstract val clazz: Class
+ protected abstract val workUniqueName: String
+ protected abstract val preferencesKey: String
+ protected abstract val titleRes: Int
+ protected abstract val commentsRes: Int
+ protected val workParams: WorkScheduleParams
+ get() {
+ val preferences =
+ PreferenceManager.getDefaultSharedPreferences(applicationContext)
+ val time = preferences.getString(preferencesKey, "")
+ if (time == "" || time == null)
+ throw IllegalStateException("Time value cannot be null")
+ val splitTime = time.split(":")
+ val hour = Integer.parseInt(splitTime[0])
+ val minute = Integer.parseInt(splitTime[1])
+ return WorkScheduleParams(hour, minute)
+ }
+
+ override suspend fun doWork(): Result = coroutineScope {
+ with(HandwashingApplication.getInstance()) {
+ withTimeoutOrNull(10_000L) {
+ firebaseInitDeferred.await()
+ }
+ }
+ shouldScheduleNext = true
+ var data: Data? = null
+ try {
+ data = work()
+ Result.success()
+ } catch (e: Exception) {
+ catchBlock(e)
+ } finally {
+ if (shouldScheduleNext) {
+ scheduleNext(data)
+ }
+ }
+ }
+
+ protected open suspend fun work(): Data? {
+ val emojiLoader = EmojiLoader.get(applicationContext)
+ val notificationsHandler = NotificationsHandler(
+ context = applicationContext,
+ channelId = TIME_CHANNEL_ID,
+ channelName = getString(R.string.time_notification_channel_name),
+ channelDesc = getString(R.string.time_notification_channel_desc)
+ )
+ val emojiCompat = emojiLoader.await()
+ var title: CharSequence
+ var content: CharSequence
+ try {
+ title = emojiCompat.process(getText(titleRes))
+ content = emojiCompat.process(
+ getStringArray(commentsRes).toList().random()
+ )
+ } catch (_: IllegalStateException) {
+ title = getText(titleRes)
+ content = getStringArray(commentsRes).toList().random()
+ }
+ withContext(Dispatchers.Main) {
+ notificationsHandler.createNotification(
+ iconDrawable = R.drawable.ic_stat_handwashing,
+ largeIcon = R.drawable.handwashing_app_logo,
+ title = title,
+ content = content,
+ longContent = content
+ )
+ }
+ return null
+ }
+
+ protected fun catchBlock(e: Exception): Result {
+ if (runAttemptCount >= maxRetries) {
+ Timber.d("Exceeded max attempts: $maxRetries")
+ return Result.failure()
+ }
+ return when (e.cause) {
+ is IllegalStateException -> {
+ Timber.w(e, "IllegalStateException on worker class")
+ shouldScheduleNext = false
+ Result.retry()
+ }
+ else -> {
+ Timber.e(e, "Uncaught exception on worker class")
+ Result.failure()
+ }
+ }
+ }
+
+ protected fun scheduleNext(data: Data?) {
+ val workManager = WorkManager.getInstance(applicationContext)
+ val nextExecutionDelay = runAt(workParams.hour, workParams.minute)
+ Timber.d("Executing $workUniqueName in ${nextExecutionDelay / 1000} s")
+ val jobRequest = with(OneTimeWorkRequest.Builder(clazz)) {
+ data?.let { setInputData(it) }
+ setInitialDelay(nextExecutionDelay, TimeUnit.MILLISECONDS)
+ setConstraints(workConstraints)
+ build()
+ }
+ workManager.enqueueUniqueWork(
+ workUniqueName, ExistingWorkPolicy.REPLACE, jobRequest
+ )
+ }
+
+ private fun getString(@StringRes resId: Int): String =
+ applicationContext.getString(resId)
+
+ private fun getText(@StringRes resId: Int): CharSequence =
+ applicationContext.getText(resId)
+
+ private fun getStringArray(@ArrayRes resId: Int): Array =
+ applicationContext.resources.getStringArray(resId)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/BreakfastWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/BreakfastWorker.kt
new file mode 100644
index 0000000..252fad2
--- /dev/null
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/BreakfastWorker.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2020 - present | Handwashing reminder by Javinator9889
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see https://www.gnu.org/licenses/.
+ *
+ * Created by Javinator9889 on 22/04/20 - Handwashing reminder.
+ */
+package com.javinator9889.handwashingreminder.jobs.workers
+
+import android.content.Context
+import androidx.work.ListenableWorker
+import androidx.work.WorkerParameters
+import com.javinator9889.handwashingreminder.R
+import com.javinator9889.handwashingreminder.utils.Preferences
+import com.javinator9889.handwashingreminder.utils.Workers
+
+class BreakfastWorker(context: Context, params: WorkerParameters) :
+ AbstractNotificationsWorker(context, params) {
+ override val clazz: Class =
+ BreakfastWorker::class.java
+ override val workUniqueName: String = Workers.BREAKFAST_UUID
+ override val preferencesKey: String = Preferences.BREAKFAST_TIME
+ override val titleRes: Int = R.string.breakfast_title
+ override val commentsRes: Int = R.array.breakfast_comments
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/DinnerWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/DinnerWorker.kt
new file mode 100644
index 0000000..8465623
--- /dev/null
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/DinnerWorker.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2020 - present | Handwashing reminder by Javinator9889
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see https://www.gnu.org/licenses/.
+ *
+ * Created by Javinator9889 on 22/04/20 - Handwashing reminder.
+ */
+package com.javinator9889.handwashingreminder.jobs.workers
+
+import android.content.Context
+import androidx.work.ListenableWorker
+import androidx.work.WorkerParameters
+import com.javinator9889.handwashingreminder.R
+import com.javinator9889.handwashingreminder.utils.Preferences
+import com.javinator9889.handwashingreminder.utils.Workers
+
+class DinnerWorker(context: Context, params: WorkerParameters) :
+ AbstractNotificationsWorker(context, params) {
+ override val clazz: Class = DinnerWorker::class.java
+ override val workUniqueName: String = Workers.DINNER_UUID
+ override val preferencesKey: String = Preferences.DINNER_TIME
+ override val titleRes: Int = R.string.dinner_title
+ override val commentsRes: Int = R.array.dinner_comments
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/LunchWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/LunchWorker.kt
new file mode 100644
index 0000000..404c102
--- /dev/null
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/LunchWorker.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2020 - present | Handwashing reminder by Javinator9889
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see https://www.gnu.org/licenses/.
+ *
+ * Created by Javinator9889 on 22/04/20 - Handwashing reminder.
+ */
+package com.javinator9889.handwashingreminder.jobs.workers
+
+import android.content.Context
+import androidx.work.ListenableWorker
+import androidx.work.WorkerParameters
+import com.javinator9889.handwashingreminder.R
+import com.javinator9889.handwashingreminder.utils.Preferences
+import com.javinator9889.handwashingreminder.utils.Workers
+
+class LunchWorker(context: Context, params: WorkerParameters) :
+ AbstractNotificationsWorker(context, params) {
+ override val clazz: Class = LunchWorker::class.java
+ override val workUniqueName: String = Workers.LUNCH_UUID
+ override val preferencesKey: String = Preferences.LUNCH_TIME
+ override val titleRes: Int = R.string.lunch_title
+ override val commentsRes: Int = R.array.lunch_comments
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/NotificationsWorker.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/NotificationsWorker.kt
deleted file mode 100644
index 87e9616..0000000
--- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/NotificationsWorker.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright © 2020 - present | Handwashing reminder by Javinator9889
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see https://www.gnu.org/licenses/.
- *
- * Created by Javinator9889 on 10/04/20 - Handwashing reminder.
- */
-package com.javinator9889.handwashingreminder.jobs.workers
-
-import android.content.Context
-import androidx.annotation.ArrayRes
-import androidx.annotation.StringRes
-import androidx.work.CoroutineWorker
-import androidx.work.Data
-import androidx.work.WorkerParameters
-import com.javinator9889.handwashingreminder.R
-import com.javinator9889.handwashingreminder.emoji.EmojiLoader
-import com.javinator9889.handwashingreminder.notifications.NotificationsHandler
-import com.javinator9889.handwashingreminder.utils.TIME_CHANNEL_ID
-import com.javinator9889.handwashingreminder.utils.Workers
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.withContext
-import timber.log.Timber
-import java.util.*
-
-
-data class NotificationStructure(
- @StringRes val title: Int,
- @ArrayRes val content: Int
-)
-
-class NotificationsWorker(
- private val context: Context,
- params: WorkerParameters
-) : CoroutineWorker(context, params) {
-
- override suspend fun doWork(): Result = coroutineScope {
- try {
- val emojiLoader = EmojiLoader.get(context)
- val notificationsHandler = NotificationsHandler(
- context,
- TIME_CHANNEL_ID,
- context.getString(R.string.time_notification_channel_name),
- context.getString(R.string.time_notification_channel_desc)
- )
- val workHandler = WorkHandler(context)
-
- val notificationData =
- setNotificationData(
- inputData.getInt(
- Workers.WHO,
- -1
- )
- )
- val delay = nextExecutionDelay(inputData)
- if (delay == -1L)
- return@coroutineScope Result.failure()
-
-
- val emojiCompat = emojiLoader.await()
- var title: CharSequence
- var comment: CharSequence
- try {
- title =
- emojiCompat.process(context.getString(notificationData.title))
- val comments =
- context.resources.getStringArray(notificationData.content)
- comment = emojiCompat.process(comments.asList().random())
- } catch (_: IllegalStateException) {
- title = context.getText(notificationData.title)
- comment = context.resources
- .getStringArray(notificationData.content).asList().random()
- }
-
- withContext(Dispatchers.Main) {
- notificationsHandler.createNotification(
- R.drawable.ic_handwashing_icon,
- R.drawable.handwashing_app_logo,
- title,
- comment,
- longContent = comment
- )
- }
-
- with(Data.Builder()) {
- putAll(inputData)
- build()
- }.let {
- Timber.d(it.toString())
- workHandler.enqueueNotificationsWorker(delay, it)
- }
- Result.success()
- } catch (e: Exception) {
- Timber.e(e, "Uncaught exception on worker class")
- if (runAttemptCount < 5)
- Result.retry()
- else
- Result.failure()
- }
- }
-
- private fun setNotificationData(who: Int): NotificationStructure =
- when (who) {
- Workers.BREAKFAST ->
- NotificationStructure(
- R.string.breakfast_title,
- R.array.breakfast_comments
- )
- Workers.LUNCH ->
- NotificationStructure(
- R.string.lunch_title,
- R.array.lunch_comments
- )
- Workers.DINNER ->
- NotificationStructure(
- R.string.dinner_title,
- R.array.dinner_comments
- )
- else -> throw IllegalArgumentException("Worker $who not found")
- }
-
- private fun nextExecutionDelay(data: Data): Long {
- val currentDate = Calendar.getInstance()
- val dueDate = Calendar.getInstance()
-
- val hour = data.getInt(Workers.HOUR, -1)
- val minute = data.getInt(Workers.MINUTE, -1)
- if (hour == -1 || hour == -1) {
- Timber.e("Hour or minute not provided")
- return -1L
- }
-
- dueDate.set(Calendar.HOUR_OF_DAY, hour)
- dueDate.set(Calendar.MINUTE, minute)
- dueDate.set(Calendar.SECOND, 0)
- if (dueDate.before(currentDate))
- dueDate.add(Calendar.HOUR_OF_DAY, 24)
- Timber.i("Next execution scheduled at: ${dueDate.time}")
- return dueDate.timeInMillis - currentDate.timeInMillis
- }
-}
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/WorkHandler.kt b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/WorkHandler.kt
index 90898da..b04539d 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/WorkHandler.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/jobs/workers/WorkHandler.kt
@@ -23,18 +23,21 @@ import androidx.preference.PreferenceManager
import androidx.work.*
import com.javinator9889.handwashingreminder.utils.Preferences
import com.javinator9889.handwashingreminder.utils.Workers
+import com.javinator9889.handwashingreminder.utils.runAt
import timber.log.Timber
-import java.util.*
import java.util.concurrent.TimeUnit
-data class Who(val uuid: String, val id: Int)
+data class Who(
+ val uuid: String,
+ val id: Int,
+ val clazz: Class
+)
class WorkHandler(private val context: Context) {
private val workManager: WorkManager
get() = WorkManager.getInstance(context)
fun enqueuePeriodicNotificationsWorker(forceUpdate: Boolean = false) {
- val currentDate = Calendar.getInstance()
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val breakfastTime =
@@ -47,34 +50,37 @@ class WorkHandler(private val context: Context) {
}
val times = arrayOf(breakfastTime, lunchTime, dinnerTime)
times.forEach { time ->
- val dueDate = Calendar.getInstance()
val splittedTime = time.split(":")
val hour = Integer.parseInt(splittedTime[0].trim())
val minute = Integer.parseInt(splittedTime[1].trim())
- dueDate.set(Calendar.HOUR_OF_DAY, hour)
- dueDate.set(Calendar.MINUTE, minute)
- dueDate.set(Calendar.SECOND, 0)
- if (dueDate.before(currentDate))
- dueDate.add(Calendar.HOUR_OF_DAY, 24)
- val timeDiff = dueDate.timeInMillis - currentDate.timeInMillis
-
+ val timeDiff = runAt(hour, minute)
val who = when (time) {
- breakfastTime -> Who(Workers.BREAKFAST_UUID, Workers.BREAKFAST)
- lunchTime -> Who(Workers.LUNCH_UUID, Workers.LUNCH)
- dinnerTime -> Who(Workers.DINNER_UUID, Workers.DINNER)
- else -> return // This should never happen
+ breakfastTime -> Who(
+ Workers.BREAKFAST_UUID,
+ Workers.BREAKFAST,
+ BreakfastWorker::class.java
+ )
+ lunchTime -> Who(
+ Workers.LUNCH_UUID,
+ Workers.LUNCH,
+ LunchWorker::class.java
+ )
+ dinnerTime -> Who(
+ Workers.DINNER_UUID,
+ Workers.DINNER,
+ DinnerWorker::class.java
+ )
+ else -> {
+ Timber.e("Unmatched time: $time against $times")
+ return
+ }
}
Timber.i(
- "Scheduled activity ${who.uuid} at ${dueDate.time}"
+ "Scheduled activity ${who.uuid} in $timeDiff ms"
)
- val workData = workDataOf(
- Workers.WHO to who.id,
- Workers.HOUR to hour,
- Workers.MINUTE to minute
- )
- val jobRequest = createJobRequest(timeDiff, workData)
+ val jobRequest = createJobRequest(timeDiff, who.clazz)
val policy = if (forceUpdate)
ExistingWorkPolicy.REPLACE
@@ -91,27 +97,10 @@ class WorkHandler(private val context: Context) {
}
}
- fun enqueueNotificationsWorker(delay: Long, data: Data) {
- val jobRequest = createJobRequest(delay, data)
- val who = when (data.getInt(Workers.WHO, -1)) {
- Workers.BREAKFAST -> Workers.BREAKFAST_UUID
- Workers.LUNCH -> Workers.LUNCH_UUID
- Workers.DINNER -> Workers.DINNER_UUID
- else -> return
- }
- Timber.d("Enqueuing job with ID: $who")
- with(workManager) {
- enqueueUniqueWork(
- who,
- ExistingWorkPolicy.APPEND,
- jobRequest
- )
- }
- }
-
private fun createJobRequest(
initialDelayMillis: Long,
- inputData: Data
+ clazz: Class,
+ inputData: Data? = null
): OneTimeWorkRequest {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.NOT_REQUIRED)
@@ -120,15 +109,16 @@ class WorkHandler(private val context: Context) {
.setRequiresDeviceIdle(false)
.setRequiresStorageNotLow(false)
.build()
- return OneTimeWorkRequestBuilder()
- .setInitialDelay(initialDelayMillis, TimeUnit.MILLISECONDS)
- .setInputData(inputData)
- .setConstraints(constraints)
- .setBackoffCriteria(
+ return with(OneTimeWorkRequest.Builder(clazz)) {
+ setInitialDelay(initialDelayMillis, TimeUnit.MILLISECONDS)
+ inputData?.let { setInputData(it) }
+ setConstraints(constraints)
+ setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
15000L,
TimeUnit.MILLISECONDS
)
- .build()
+ build()
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/network/HttpDownloader.kt b/app/src/main/java/com/javinator9889/handwashingreminder/network/HttpDownloader.kt
index a230b66..fb2eb75 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/network/HttpDownloader.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/network/HttpDownloader.kt
@@ -18,20 +18,18 @@
*/
package com.javinator9889.handwashingreminder.network
-import com.javinator9889.handwashingreminder.utils.AndroidVersion
-import com.javinator9889.handwashingreminder.utils.OkHttp
-import com.javinator9889.handwashingreminder.utils.OkHttpLegacy
-import com.javinator9889.handwashingreminder.utils.isAtLeast
+import com.javinator9889.handwashingreminder.network.okhttp.OkHttpDownloader as Downloader
object HttpDownloader {
fun newInstance(): OkHttpDownloader {
- val className = if (isAtLeast(AndroidVersion.LOLLIPOP))
+ /*val className = if (isAtLeast(AndroidVersion.LOLLIPOP))
"${OkHttp.PACKAGE_NAME}.${OkHttp.CLASS_NAME}\$${OkHttp.PROVIDER_NAME}"
else
"${OkHttpLegacy.PACKAGE_NAME}.${OkHttpLegacy
.CLASS_NAME}\$${OkHttpLegacy.PROVIDER_NAME}"
val okHttpProvider = Class.forName(className).kotlin.objectInstance
as OkHttpDownloader.Provider
- return okHttpProvider.newInstance()
+ return okHttpProvider.newInstance()*/
+ return Downloader.newInstance()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/network/okhttp/OkHttpDownloader.kt b/app/src/main/java/com/javinator9889/handwashingreminder/network/okhttp/OkHttpDownloader.kt
new file mode 100644
index 0000000..763497d
--- /dev/null
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/network/okhttp/OkHttpDownloader.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2020 - present | Handwashing reminder by Javinator9889
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see https://www.gnu.org/licenses/.
+ *
+ * Created by Javinator9889 on 21/04/20 - Handwashing reminder.
+ */
+package com.javinator9889.handwashingreminder.network.okhttp
+
+import com.javinator9889.handwashingreminder.network.OkHttpDownloader
+import okhttp3.CacheControl
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okio.BufferedSource
+import java.io.IOException
+
+class OkHttpDownloader : OkHttpDownloader {
+ private val client = OkHttpClient()
+
+ companion object Provider : OkHttpDownloader.Provider {
+ override fun newInstance(): OkHttpDownloader = OkHttpDownloader()
+ }
+
+ override fun downloadFile(url: String): BufferedSource {
+ val request = with(Request.Builder()) {
+ url(url)
+ cacheControl(CacheControl.FORCE_NETWORK)
+ build()
+ }
+ with(client.newCall(request).execute()) {
+ if (!isSuccessful) {
+ close()
+ throw IOException("Unexpected code $this")
+ }
+ return body()!!.source()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/utils/LogReportTree.kt b/app/src/main/java/com/javinator9889/handwashingreminder/utils/LogReportTree.kt
index 386900f..f7aa96f 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/utils/LogReportTree.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/utils/LogReportTree.kt
@@ -37,9 +37,10 @@ class LogReportTree : Timber.Tree() {
) {
when (priority) {
Log.DEBUG, Log.VERBOSE -> return
+ Log.WARN -> crashlytics.log("W: $tag: $message")
Log.ERROR -> {
- crashlytics.log("E/$tag: $message");
- t?.let { crashlytics.recordException(it) }
+ crashlytics.log("E/$tag: $message")
+ t?.let { crashlytics.recordException(t) }
}
}
}
diff --git a/app/src/main/java/com/javinator9889/handwashingreminder/utils/Time.kt b/app/src/main/java/com/javinator9889/handwashingreminder/utils/Time.kt
index e683dee..1bb51da 100644
--- a/app/src/main/java/com/javinator9889/handwashingreminder/utils/Time.kt
+++ b/app/src/main/java/com/javinator9889/handwashingreminder/utils/Time.kt
@@ -18,19 +18,52 @@
*/
package com.javinator9889.handwashingreminder.utils
-import android.annotation.SuppressLint
-import java.text.SimpleDateFormat
+import androidx.annotation.IntRange
+import java.time.Duration
+import java.time.LocalDateTime
+import java.time.LocalTime
+import java.time.temporal.ChronoUnit
import java.util.*
+import kotlin.math.abs
-@SuppressLint("SimpleDateFormat")
-fun timeDifferenceSecs(to: String): Long {
- if (to == "") return 0L
- val dateFormat = SimpleDateFormat("HH:mm")
- val fromDate = dateFormat.parse(to) ?: return 0L
- val cTime = Calendar.getInstance().time
- val diff = fromDate.time - cTime.time
- dateFormat.parse(dateFormat.format(diff))?.let { return it.time / 1000 }
- return 0L
-}
-fun formatTime(time: Int) = if (time < 10) "0$time" else time.toString()
\ No newline at end of file
+fun formatTime(time: Int) = if (time < 10) "0$time" else time.toString()
+
+fun runAt(
+ @IntRange(from = 0, to = 23) hour: Int,
+ @IntRange(from = 0, to = 59) minute: Int
+): Long =
+ if (isAtLeast(AndroidVersion.O)) {
+ // trigger at hour:minute
+ val alarmTime = LocalTime.of(hour, minute)
+ var now = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)
+ val nowTime = now.toLocalTime()
+ // check if is the same time or if today's time has passed so
+ // then schedule for next day
+ if (nowTime == alarmTime || nowTime.isAfter(alarmTime)) {
+ now = now.plusDays(1)
+ }
+ now = now
+ .withHour(alarmTime.hour)
+ .withMinute(alarmTime.minute)
+ abs(Duration.between(LocalDateTime.now(), now).toMillis())
+ } else {
+ // get now time and truncate it to minutes
+ val now = Calendar.getInstance()
+ // clone now time truncated to minutes and set the specified hour and
+ // minute in the new Calendar object
+ val alarm = Calendar.getInstance().apply {
+ set(Calendar.HOUR_OF_DAY, hour)
+ set(Calendar.MINUTE, minute)
+ set(Calendar.SECOND, 0)
+ }
+ val nowTime = now.time
+ val alarmTime = alarm.time
+ // check if they are the same time or if today's time has passed so
+ // then schedule for next day
+ if (nowTime == alarmTime || nowTime.after(alarmTime)) {
+ alarm.add(Calendar.HOUR_OF_DAY, 24)
+ }
+ abs(alarm.timeInMillis - now.timeInMillis)
+ }
+
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index e11aa59..a17df80 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -53,7 +53,7 @@
contenido a la aplicación para que funcione correctamente. Por favor,
espera un minuto - este proceso no debería durar mucho.
¡Vaya! Algo ha ido
- mal 😥 - intenta el proceso de nuevo
+ mal 😥 - intenta el proceso de nuevo | código de error: %1$d
Hecho
Preparando…
Instalando…
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fdfe0ae..8741206 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -52,7 +52,7 @@
content to the app so it works correctly. Please, wait a minute
- this process should not last long.
Oops! Something went
- wrong 😥 - please retry the process
+ wrong 😥 - please retry the process | error code: %1$d
Done
Preparing…
Installing…
diff --git a/appintro/build.gradle b/appintro/build.gradle
index 89274e3..5dbb6a6 100644
--- a/appintro/build.gradle
+++ b/appintro/build.gradle
@@ -71,7 +71,6 @@ dependencies {
// https://github.com/JakeWharton/timber
implementation 'com.jakewharton.timber:timber:4.7.1'
// https://firebase.google.com/docs/android/setup#add-sdks
- implementation 'com.google.firebase:firebase-core:17.3.0'
implementation 'com.google.firebase:firebase-common-ktx:19.3.0'
implementation 'com.google.firebase:firebase-analytics:17.3.0'
implementation 'com.google.firebase:firebase-crashlytics:17.0.0-beta04'
diff --git a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt
index 3c13fd8..9b4275f 100644
--- a/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt
+++ b/appintro/src/main/java/com/javinator9889/handwashingreminder/appintro/IntroActivity.kt
@@ -53,6 +53,7 @@ import com.javinator9889.handwashingreminder.appintro.fragments.TimeConfigIntroF
import com.javinator9889.handwashingreminder.appintro.timeconfig.TimeConfigViewHolder
import com.javinator9889.handwashingreminder.appintro.utils.AnimatedResources
import com.javinator9889.handwashingreminder.application.HandwashingApplication
+import com.javinator9889.handwashingreminder.jobs.workers.WorkHandler
import com.javinator9889.handwashingreminder.listeners.ViewHolder
import com.javinator9889.handwashingreminder.utils.*
import kotlinx.android.synthetic.main.animated_intro.*
@@ -72,7 +73,7 @@ class IntroActivity : AppIntro2(),
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
- SplitCompat.installActivity(base)
+ SplitCompat.installActivity(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -180,7 +181,9 @@ class IntroActivity : AppIntro2(),
app.activityHandler.startTrackingActivity()
else
app.activityHandler.disableActivityTracker()
- app.workHandler.enqueuePeriodicNotificationsWorker()
+ with(WorkHandler(this)) {
+ enqueuePeriodicNotificationsWorker()
+ }
val firebaseAnalytics = FirebaseAnalytics.getInstance(this)
with(Bundle(2)) {
putBoolean(
diff --git a/build.gradle b/build.gradle
index 5da1a83..dad0e28 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,9 @@ buildscript {
repositories {
google()
jcenter()
-
+ maven {
+ url 'https://maven.fabric.io/public'
+ }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
diff --git a/bundledemoji/build.gradle b/bundledemoji/build.gradle
index 3dc3fb8..80d84d4 100644
--- a/bundledemoji/build.gradle
+++ b/bundledemoji/build.gradle
@@ -25,9 +25,8 @@ dependencies {
implementation 'androidx.emoji:emoji-appcompat:1.0.0'
implementation 'androidx.emoji:emoji-bundled:1.0.0'
implementation 'androidx.core:core-ktx:1.2.0'
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
// https://firebase.google.com/docs/android/setup#add-sdks
- implementation 'com.google.firebase:firebase-core:17.3.0'
implementation 'com.google.firebase:firebase-common-ktx:19.3.0'
implementation 'com.google.firebase:firebase-analytics:17.3.0'
implementation 'com.google.firebase:firebase-crashlytics:17.0.0-beta04'
diff --git a/secrets.properties b/secrets.properties
new file mode 100644
index 0000000..1958fa6
--- /dev/null
+++ b/secrets.properties
@@ -0,0 +1,6 @@
+# (optional) Project id from Google Developer Console, https://console.developers.google.com/
+google_project_id="api-5245190277294621651-718463"
+# (optional) Settings for building a signed release apk
+signing_key_password="dhc9Xe7XoidlRo77hohh"
+signing_keystore_password="dhc9Xe7XoidlRo77hohh"
+signing_key_alias="key0"
diff --git a/tgs b/tgs
index b238b79..d2ddbad 160000
--- a/tgs
+++ b/tgs
@@ -1 +1 @@
-Subproject commit b238b796feed1b47ca0f926627305ce5ef866719
+Subproject commit d2ddbad8db2e29a090122f2a7da50aaa2ff2c3d2