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

Inject AnalyticsHelper with Hilt #118

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
10 changes: 5 additions & 5 deletions app/src/main/java/com/waseefakhtar/doseapp/DoseApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ import com.waseefakhtar.doseapp.ui.theme.DoseAppTheme
import com.waseefakhtar.doseapp.util.SnackbarUtil

@Composable
fun DoseApp() {
fun DoseApp(
analyticsHelper: AnalyticsHelper
) {
DoseAppTheme {
// A surface container using the 'background' color from the theme
Surface(
Expand All @@ -73,9 +75,6 @@ fun DoseApp() {
val bottomBarVisibility = rememberSaveable { (mutableStateOf(true)) }
val fabVisibility = rememberSaveable { (mutableStateOf(true)) }

val context = LocalContext.current
val analyticsHelper = AnalyticsHelper.getInstance(context)

Scaffold(
modifier = Modifier.padding(16.dp, 0.dp),
containerColor = Color.Transparent,
Expand Down Expand Up @@ -216,7 +215,8 @@ fun DoseFAB(navController: NavController, analyticsHelper: AnalyticsHelper) {
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
val context = LocalContext.current
DoseAppTheme {
DoseApp()
DoseApp(analyticsHelper = AnalyticsHelper(context = context))
}
}
9 changes: 7 additions & 2 deletions app/src/main/java/com/waseefakhtar/doseapp/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@ import androidx.activity.compose.setContent
import com.waseefakhtar.doseapp.analytics.AnalyticsEvents
import com.waseefakhtar.doseapp.analytics.AnalyticsHelper
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

@Inject
lateinit var analyticsHelper: AnalyticsHelper
Comment on lines +15 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe could be important inject AnalyticsHelper using Hilt DSL for Compose screens/components instead inject directly on activity.

I think this because on your approuch you will need pass the analytics value as a cascate to anothers components and screens and one of the DI ideia is: Not depend from another components to receive the value, you could recover the value from anywhere, injecting the value.

My suggestion could be strange here because the AnalyticsHelper wasn't used on ViewModels, and may the best approach is migrate AnalyticsHelper to ViewModels instead.

What do you think about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered this approach as well, and that's why I initially discussed it with @waseefakhtar here: #74 (comment).

Given the project's current structure, this was my initial approach to refactoring. I remain open to suggestions, and if this approach proves unsuitable, I can always revert to the second option mentioned in the comment link.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I didn't know about the approach dicussed, thanks for showing to me @ankur141295!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the approach of migrating AnalyticsHelper to ViewModels instead! That'd make the code a ton simpler and easy to test.

@ankur141295 my bad regarding the discussion. I might've missed your point there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@waseefakhtar No worries, I'll make the code changes again and inject AnalyticsHelper into ViewModel instead.


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
DoseApp()
DoseApp(analyticsHelper = analyticsHelper)
}
parseIntent(intent)
}

private fun parseIntent(intent: Intent?) {
val isMedicationNotification = intent?.getBooleanExtra(MEDICATION_NOTIFICATION, false) ?: false
if (isMedicationNotification) {
AnalyticsHelper.getInstance(this).logEvent(AnalyticsEvents.REMINDER_NOTIFICATION_CLICKED)
analyticsHelper.logEvent(AnalyticsEvents.REMINDER_NOTIFICATION_CLICKED)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ import android.os.Build
import androidx.core.app.NotificationCompat
import com.waseefakhtar.doseapp.analytics.AnalyticsHelper
import com.waseefakhtar.doseapp.domain.model.Medication
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

const val MEDICATION_INTENT = "medication_intent"
const val MEDICATION_NOTIFICATION = "medication_notification"

@AndroidEntryPoint
class MedicationNotificationReceiver : BroadcastReceiver() {

@Inject
lateinit var analyticsHelper: AnalyticsHelper

override fun onReceive(context: Context?, intent: Intent?) {
context?.let {
intent?.getParcelableExtra<Medication>(MEDICATION_INTENT)?.let { medication ->
Expand Down Expand Up @@ -59,6 +66,6 @@ class MedicationNotificationReceiver : BroadcastReceiver() {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(medication.hashCode(), notification)

AnalyticsHelper.getInstance(context).trackNotificationShown(medication)
analyticsHelper.trackNotificationShown(medication)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class MedicationNotificationService(
private val context: Context
) {

fun scheduleNotification(medication: Medication) {
fun scheduleNotification(medication: Medication, analyticsHelper: AnalyticsHelper) {
val intent = Intent(context, MedicationNotificationReceiver::class.java)
intent.putExtra(MEDICATION_INTENT, medication)

Expand All @@ -40,7 +40,7 @@ class MedicationNotificationService(
}
}

AnalyticsHelper.getInstance(context).trackNotificationScheduled(medication)
analyticsHelper.trackNotificationScheduled(medication)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,40 @@ package com.waseefakhtar.doseapp.analytics

import android.content.Context
import android.os.Bundle
import androidx.core.os.bundleOf
import com.google.firebase.analytics.FirebaseAnalytics
import com.waseefakhtar.doseapp.domain.model.Medication
import com.waseefakhtar.doseapp.extension.toFormattedDateString
import java.util.Date

class AnalyticsHelper private constructor(context: Context) {
private const val MEDICATION_TIME = "medication_time"
private const val MEDICATION_END_DATE = "medication_end_date"
private const val NOTIFICATION_TIME = "notification_time"

class AnalyticsHelper(
context: Context
) {
private val firebaseAnalytics = FirebaseAnalytics.getInstance(context)

companion object {
@Volatile
private var instance: AnalyticsHelper? = null

fun getInstance(context: Context): AnalyticsHelper {
return instance ?: synchronized(this) {
instance ?: AnalyticsHelper(context).also { instance = it }
}
}
}

fun logEvent(eventName: String, params: Bundle? = null) {
firebaseAnalytics.logEvent(eventName, params)
}

fun trackNotificationShown(medication: Medication) {
val params = Bundle()
params.putString("medication_time", medication.medicationTime.toFormattedDateString())
params.putString("medication_end_date", medication.endDate.toFormattedDateString())
params.putString("notification_time", Date().toFormattedDateString())
val params = bundleOf(
MEDICATION_TIME to medication.medicationTime.toFormattedDateString(),
MEDICATION_END_DATE to medication.endDate.toFormattedDateString(),
NOTIFICATION_TIME to Date().toFormattedDateString()
)
logEvent(AnalyticsEvents.MEDICATION_NOTIFICATION_SHOWN, params)
}

fun trackNotificationScheduled(medication: Medication) {
val params = Bundle()
params.putString("medication_time", medication.medicationTime.toFormattedDateString())
params.putString("medication_end_date", medication.endDate.toFormattedDateString())
params.putString("notification_time", Date().toFormattedDateString())
val params = bundleOf(
MEDICATION_TIME to medication.medicationTime.toFormattedDateString(),
MEDICATION_END_DATE to medication.endDate.toFormattedDateString(),
NOTIFICATION_TIME to Date().toFormattedDateString()
)
logEvent(AnalyticsEvents.MEDICATION_NOTIFICATION_SCHEDULED, params)
}

fun logEvent(eventName: String, params: Bundle? = null) {
firebaseAnalytics.logEvent(eventName, params)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.waseefakhtar.doseapp.di

import android.content.Context
import com.waseefakhtar.doseapp.analytics.AnalyticsHelper
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object AnalyticsHelperModule {

@Provides
@Singleton
fun provideAnalyticsHelper(
@ApplicationContext context: Context,
): AnalyticsHelper {
return AnalyticsHelper(context = context)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.waseefakhtar.doseapp.R
import com.waseefakhtar.doseapp.analytics.AnalyticsEvents
import com.waseefakhtar.doseapp.analytics.AnalyticsHelper
import com.waseefakhtar.doseapp.domain.model.Medication
import com.waseefakhtar.doseapp.extension.toFormattedDateString
import com.waseefakhtar.doseapp.feature.addmedication.model.CalendarInformation
Expand All @@ -80,16 +79,14 @@ fun AddMedicationRoute(
navigateToMedicationConfirm: (List<Medication>) -> Unit,
viewModel: AddMedicationViewModel = hiltViewModel()
) {
val analyticsHelper = AnalyticsHelper.getInstance(LocalContext.current)
AddMedicationScreen(onBackClicked, viewModel, analyticsHelper, navigateToMedicationConfirm)
AddMedicationScreen(onBackClicked, viewModel, navigateToMedicationConfirm)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddMedicationScreen(
onBackClicked: () -> Unit,
viewModel: AddMedicationViewModel,
analyticsHelper: AnalyticsHelper,
navigateToMedicationConfirm: (List<Medication>) -> Unit,
) {
var medicationName by rememberSaveable { mutableStateOf("") }
Expand All @@ -101,12 +98,12 @@ fun AddMedicationScreen(

fun addTime(time: CalendarInformation) {
selectedTimes.add(time)
analyticsHelper.logEvent(AnalyticsEvents.ADD_MEDICATION_ADD_TIME_CLICKED)
viewModel.logEvent(eventName = AnalyticsEvents.ADD_MEDICATION_ADD_TIME_CLICKED)
}

fun removeTime(time: CalendarInformation) {
selectedTimes.remove(time)
analyticsHelper.logEvent(AnalyticsEvents.ADD_MEDICATION_DELETE_TIME_CLICKED)
viewModel.logEvent(eventName = AnalyticsEvents.ADD_MEDICATION_DELETE_TIME_CLICKED)
}

Scaffold(
Expand All @@ -117,7 +114,7 @@ fun AddMedicationScreen(
navigationIcon = {
FloatingActionButton(
onClick = {
analyticsHelper.logEvent(AnalyticsEvents.ADD_MEDICATION_ON_BACK_CLICKED)
viewModel.logEvent(eventName = AnalyticsEvents.ADD_MEDICATION_ON_BACK_CLICKED)
onBackClicked()
},
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp)
Expand Down Expand Up @@ -164,11 +161,11 @@ fun AddMedicationScreen(
AnalyticsEvents.ADD_MEDICATION_MEDICATION_VALUE_INVALIDATED,
invalidatedValue
)
analyticsHelper.logEvent(event)
viewModel.logEvent(eventName = event)
},
onValidate = {
navigateToMedicationConfirm(it)
analyticsHelper.logEvent(AnalyticsEvents.ADD_MEDICATION_NAVIGATING_TO_MEDICATION_CONFIRM)
viewModel.logEvent(eventName = AnalyticsEvents.ADD_MEDICATION_NAVIGATING_TO_MEDICATION_CONFIRM)
},
viewModel = viewModel
)
Expand Down Expand Up @@ -277,7 +274,9 @@ fun AddMedicationScreen(
selectedTimes[index] = it
},
onDeleteClick = { removeTime(selectedTimes[index]) },
analyticsHelper = analyticsHelper
logEvent = {
viewModel.logEvent(AnalyticsEvents.ADD_MEDICATION_NEW_TIME_SELECTED)
},
)
}

Expand Down Expand Up @@ -471,7 +470,7 @@ fun TimerTextField(
isOnlyItem: Boolean,
time: (CalendarInformation) -> Unit,
onDeleteClick: () -> Unit,
analyticsHelper: AnalyticsHelper
logEvent: () -> Unit
) {
val interactionSource = remember { MutableInteractionSource() }
val isPressed: Boolean by interactionSource.collectIsPressedAsState()
Expand All @@ -484,7 +483,7 @@ fun TimerTextField(
showDialog = isPressed,
selectedDate = selectedTime,
onSelectedTime = {
analyticsHelper.logEvent(AnalyticsEvents.ADD_MEDICATION_NEW_TIME_SELECTED)
logEvent.invoke()
selectedTime = it
time(it)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.waseefakhtar.doseapp.feature.addmedication.viewmodel

import androidx.lifecycle.ViewModel
import com.waseefakhtar.doseapp.analytics.AnalyticsHelper
import com.waseefakhtar.doseapp.domain.model.Medication
import com.waseefakhtar.doseapp.feature.addmedication.model.CalendarInformation
import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.Calendar
import java.util.Date
import javax.inject.Inject

class AddMedicationViewModel : ViewModel() {
@HiltViewModel
class AddMedicationViewModel @Inject constructor(
private val analyticsHelper: AnalyticsHelper
) : ViewModel() {

fun createMedications(
name: String,
Expand Down Expand Up @@ -59,4 +65,8 @@ class AddMedicationViewModel : ViewModel() {
calendar.set(Calendar.MINUTE, medicationTime.dateInformation.minute)
return calendar.time
}

fun logEvent(eventName: String) {
analyticsHelper.logEvent(eventName = eventName)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.waseefakhtar.doseapp.R
import com.waseefakhtar.doseapp.analytics.AnalyticsHelper
import com.waseefakhtar.doseapp.domain.model.Medication
import com.waseefakhtar.doseapp.extension.hasPassed
import com.waseefakhtar.doseapp.feature.history.viewmodel.HistoryState
Expand All @@ -36,14 +34,19 @@ fun HistoryRoute(
navigateToMedicationDetail: (Medication) -> Unit,
viewModel: HistoryViewModel = hiltViewModel()
) {
val analyticsHelper = AnalyticsHelper.getInstance(LocalContext.current)
val state = viewModel.state
HistoryScreen(analyticsHelper, state, navigateToMedicationDetail)
HistoryScreen(
state = state,
navigateToMedicationDetail = navigateToMedicationDetail
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HistoryScreen(analyticsHelper: AnalyticsHelper, state: HistoryState, navigateToMedicationDetail: (Medication) -> Unit) {
fun HistoryScreen(
state: HistoryState,
navigateToMedicationDetail: (Medication) -> Unit
) {
Scaffold(
topBar = {
TopAppBar(
Expand All @@ -64,13 +67,19 @@ fun HistoryScreen(analyticsHelper: AnalyticsHelper, state: HistoryState, navigat
modifier = Modifier.padding(innerPadding),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
MedicationList(analyticsHelper, state, navigateToMedicationDetail)
MedicationList(
state = state,
navigateToMedicationDetail = navigateToMedicationDetail
)
}
}
}

@Composable
fun MedicationList(analyticsHelper: AnalyticsHelper, state: HistoryState, navigateToMedicationDetail: (Medication) -> Unit) {
fun MedicationList(
state: HistoryState,
navigateToMedicationDetail: (Medication) -> Unit
) {

val filteredMedicationList = state.medications.filter { it.medicationTime.hasPassed() }
val sortedMedicationList: List<MedicationListItem> = filteredMedicationList.sortedBy { it.medicationTime }.map { MedicationListItem.MedicationItem(it) }
Expand Down
Loading
Loading