diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3712e15ad..c708341f8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -35,4 +35,12 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/HealthSummaryService.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/HealthSummaryService.kt
index ed84519b6..988df5b6f 100644
--- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/HealthSummaryService.kt
+++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/HealthSummaryService.kt
@@ -2,9 +2,9 @@ package edu.stanford.bdh.engagehf.messages
import android.content.Context
import android.content.Intent
-import android.os.Environment
import androidx.core.content.FileProvider
import dagger.hilt.android.qualifiers.ApplicationContext
+import edu.stanford.bdh.engagehf.R
import edu.stanford.spezi.core.coroutines.di.Dispatching
import edu.stanford.spezi.core.logging.speziLogger
import edu.stanford.spezi.core.utils.MessageNotifier
@@ -31,21 +31,25 @@ class HealthSummaryService @Inject constructor(
"${context.packageName}.provider",
savePdfToFile
)
- Intent(Intent.ACTION_VIEW).run {
+ val pdfIntent = Intent(Intent.ACTION_VIEW).apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setDataAndType(pdfUri, MIME_TYPE_PDF)
- context.startActivity(this)
+ }
+ if (pdfIntent.resolveActivity(context.packageManager) == null) {
+ messageNotifier.notify(messageId = R.string.no_pdf_reader_app_installed_error_message)
+ } else {
+ context.startActivity(pdfIntent)
}
}.onFailure {
- messageNotifier.notify("Failed to generate Health Summary")
+ messageNotifier.notify(messageId = R.string.health_summary_generate_error_message)
}
}
private fun savePdfToFile(pdfBytes: ByteArray): File {
logger.i { "PDF size: ${pdfBytes.size}" }
- val storageDir =
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+
+ val storageDir = context.getExternalFilesDir(null) ?: context.filesDir
if (!storageDir.exists()) {
storageDir.mkdirs()
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 46fc54299..0399e4cf4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -122,4 +122,6 @@
Dizziness Score
Your dizziness is important to keep track of because dizziness can be a side effect of your heart or your heart medicines.
Error while handling message action.
+ Health Summary generated successfully, but no PDF reader is installed on the device
+ Failed to generate Health Summary
diff --git a/core/utils/build.gradle.kts b/core/utils/build.gradle.kts
index ea20b01da..7139dbe61 100644
--- a/core/utils/build.gradle.kts
+++ b/core/utils/build.gradle.kts
@@ -7,6 +7,10 @@ plugins {
android {
namespace = "edu.stanford.spezi.utils"
+
+ defaultConfig {
+ testInstrumentationRunner = "edu.stanford.spezi.core.utils.HiltApplicationTestRunner"
+ }
}
dependencies {
@@ -16,4 +20,5 @@ dependencies {
implementation(libs.kotlinx.serialization.json)
testImplementation(libs.bundles.unit.testing)
+ androidTestImplementation(libs.bundles.unit.testing)
}
diff --git a/core/utils/src/androidTest/kotlin/edu/stanford/spezi/core/utils/HiltApplicationTestRunner.kt b/core/utils/src/androidTest/kotlin/edu/stanford/spezi/core/utils/HiltApplicationTestRunner.kt
new file mode 100644
index 000000000..2464d6baa
--- /dev/null
+++ b/core/utils/src/androidTest/kotlin/edu/stanford/spezi/core/utils/HiltApplicationTestRunner.kt
@@ -0,0 +1,13 @@
+package edu.stanford.spezi.core.utils
+
+import android.app.Application
+import android.content.Context
+import androidx.test.runner.AndroidJUnitRunner
+import dagger.hilt.android.testing.HiltTestApplication
+
+@Suppress("Unused")
+class HiltApplicationTestRunner : AndroidJUnitRunner() {
+ override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
+ return super.newApplication(cl, HiltTestApplication::class.java.name, context)
+ }
+}
diff --git a/core/utils/src/androidTest/kotlin/edu/stanford/spezi/core/utils/MessageNotifierTest.kt b/core/utils/src/androidTest/kotlin/edu/stanford/spezi/core/utils/MessageNotifierTest.kt
new file mode 100644
index 000000000..fffaf4274
--- /dev/null
+++ b/core/utils/src/androidTest/kotlin/edu/stanford/spezi/core/utils/MessageNotifierTest.kt
@@ -0,0 +1,38 @@
+package edu.stanford.spezi.core.utils
+
+import androidx.test.platform.app.InstrumentationRegistry
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import javax.inject.Inject
+
+@HiltAndroidTest
+class MessageNotifierTest {
+
+ @get:Rule(order = 1)
+ val hiltRule = HiltAndroidRule(this)
+
+ @Inject
+ lateinit var messageNotifier: MessageNotifier
+
+ @Before
+ fun init() {
+ hiltRule.inject()
+ }
+
+ @Test
+ fun `it should not fail when toasting in main thread`() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ messageNotifier.notify("Some message")
+ }
+ }
+
+ @Test
+ fun `it should not fail when notifying on a background thread`() = runTest(StandardTestDispatcher()) {
+ messageNotifier.notify("Some message")
+ }
+}
diff --git a/core/utils/src/main/kotlin/edu/stanford/spezi/core/utils/MessageNotifier.kt b/core/utils/src/main/kotlin/edu/stanford/spezi/core/utils/MessageNotifier.kt
index 3b83ba4c5..ad7c1da00 100644
--- a/core/utils/src/main/kotlin/edu/stanford/spezi/core/utils/MessageNotifier.kt
+++ b/core/utils/src/main/kotlin/edu/stanford/spezi/core/utils/MessageNotifier.kt
@@ -1,6 +1,8 @@
package edu.stanford.spezi.core.utils
import android.content.Context
+import android.os.Handler
+import android.os.Looper
import android.widget.Toast
import androidx.annotation.StringRes
import dagger.hilt.android.qualifiers.ApplicationContext
@@ -9,9 +11,15 @@ import javax.inject.Inject
class MessageNotifier @Inject constructor(
@ApplicationContext private val context: Context,
) {
+ private val mainHandler by lazy { Handler(Looper.getMainLooper()) }
fun notify(message: String, duration: Duration = Duration.SHORT) {
- Toast.makeText(context, message, duration.value).show()
+ val toast = { Toast.makeText(context, message, duration.value).show() }
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ toast()
+ } else {
+ mainHandler.post { toast() }
+ }
}
fun notify(@StringRes messageId: Int, duration: Duration = Duration.SHORT) {