Skip to content

Commit

Permalink
update-user-journey-algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-megh-l committed Sep 24, 2024
1 parent 94229ac commit b98f9a8
Show file tree
Hide file tree
Showing 24 changed files with 474 additions and 460 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
mv app/build/outputs/apk/release/*.apk .
- name: Upload Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: YourSpace APK
path: YourSpace*.apk
Expand Down
28 changes: 28 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class YourSpaceApplication :

super<Application>.onCreate()
Timber.plant(Timber.DebugTree())
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false)
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
authService.addListener(this)
setNotificationChannel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,17 @@ fun Address.formattedTitle(toAddress: Address?): String {
val fromState = this.adminArea ?: this.countryName ?: "Unknown"
val toState = toAddress?.adminArea ?: toAddress?.countryName ?: "Unknown"

val fromAddressLine = this.getAddressLine(0).split(",").firstOrNull {
it.matches(Regex("^[a-zA-Z ]+\$"))
}?.trim()

val toAddressLine = toAddress?.getAddressLine(0)?.split(",")?.firstOrNull {
it.matches(Regex("^[a-zA-Z ]+\$"))
}?.trim()

return when {
toAddress == null -> "$fromArea, $fromCity"
fromArea == toArea && !fromAddressLine.isNullOrEmpty() && !toAddressLine.isNullOrEmpty() -> "$fromAddressLine to $toAddressLine, $fromCity"
fromArea == toArea -> "$fromArea, $fromCity"
fromCity == toCity -> "$fromArea to $toArea, $fromCity"
fromState == toState -> "$fromArea, $fromCity to $toArea, $toCity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.canopas.yourspace.data.models.location.LocationJourney
import com.canopas.yourspace.data.models.user.ApiUser
import com.canopas.yourspace.data.repository.JourneyRepository
import com.canopas.yourspace.data.service.auth.AuthService
import com.canopas.yourspace.data.service.location.ApiJourneyService
import com.canopas.yourspace.data.service.user.ApiUserService
Expand All @@ -26,6 +27,7 @@ class JourneyTimelineViewModel @Inject constructor(
private val journeyService: ApiJourneyService,
private val apiUserService: ApiUserService,
private val authService: AuthService,
private val journeyRepository: JourneyRepository,
private val appDispatcher: AppDispatcher
) : ViewModel() {

Expand Down Expand Up @@ -99,6 +101,23 @@ class JourneyTimelineViewModel @Inject constructor(
appending = false,
isLoading = false
)

if (!hasMoreItems && locationJourneys.isEmpty()) {
// No journey found. Try checking in local database
// If any location is found for current user and current date then add it to the list as well as remote database
val lastJourney = journeyRepository.checkAndAddLocalJourneyToRemoteDatabase(userId)
val locationJourney = (
lastJourney?.let {
listOf(it)
} ?: emptyList()
).groupByDate()
_state.value = _state.value.copy(
groupedLocation = locationJourney,
hasMoreLocations = false,
appending = false,
isLoading = false
)
}
} catch (e: Exception) {
Timber.e(e, "Failed to fetch location history")
_state.value =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.canopas.yourspace.data.models.user.LOGIN_TYPE_GOOGLE
import com.canopas.yourspace.data.models.user.LOGIN_TYPE_PHONE
import com.canopas.yourspace.data.repository.SpaceRepository
import com.canopas.yourspace.data.service.auth.AuthService
import com.canopas.yourspace.data.storage.room.LocationTableDatabase
import com.canopas.yourspace.data.storage.LocationCache
import com.canopas.yourspace.data.utils.AppDispatcher
import com.canopas.yourspace.ui.navigation.AppDestinations
import com.canopas.yourspace.ui.navigation.AppNavigator
Expand All @@ -26,7 +26,7 @@ class EditProfileViewModel @Inject constructor(
private val appDispatcher: AppDispatcher,
private val spaceRepository: SpaceRepository,
private val authService: AuthService,
private val locationTableDatabase: LocationTableDatabase
private val locationCache: LocationCache
) : ViewModel() {

private val _state = MutableStateFlow(EditProfileState())
Expand Down Expand Up @@ -176,7 +176,7 @@ class EditProfileViewModel @Inject constructor(
)
spaceRepository.deleteUserSpaces()
authService.deleteAccount()
locationTableDatabase.locationTableDao().deleteLocationData()
locationCache.clear()
navigator.navigateTo(
AppDestinations.signIn.path,
clearStack = true
Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.3.2" apply false
id("com.android.application") version "8.6.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
id("com.android.library") version "8.3.2" apply false
id("com.android.library") version "8.6.0" apply false
id("com.google.dagger.hilt.android") version "2.50" apply false
id("com.google.gms.google-services") version "4.4.0" apply false
id("org.jlleitschuh.gradle.ktlint") version "11.4.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.canopas.yourspace.data.di

import android.content.Context
import com.canopas.yourspace.data.storage.room.LocationTableDatabase
import com.canopas.yourspace.data.utils.Config
import com.google.android.gms.location.GeofencingClient
import com.google.android.gms.location.LocationServices
Expand Down Expand Up @@ -38,11 +37,6 @@ class AppDataProvider {
fun provideFirebaseFunctions(): FirebaseFunctions =
FirebaseFunctions.getInstance(FirebaseApp.getInstance(), Config.FIREBASE_REGION)

@Provides
@Singleton
fun provideLocationTableDatabase(@ApplicationContext context: Context): LocationTableDatabase =
LocationTableDatabase.getInstance(context)

@Provides
@Singleton
fun provideGeoFencingClient(@ApplicationContext context: Context): GeofencingClient =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,14 @@ import androidx.annotation.Keep
import com.squareup.moshi.JsonClass
import java.util.UUID

/**
* User state that can be either [UserState.STEADY] or [UserState.MOVING]
* */
enum class UserState(val value: Int) {
STEADY(0),
MOVING(1)
}

@Keep
@JsonClass(generateAdapter = true)
data class ApiLocation(
val id: String = UUID.randomUUID().toString(),
val user_id: String = "",
val latitude: Double = 0.0,
val longitude: Double = 0.0,
val created_at: Long? = System.currentTimeMillis(),
val user_state: Int? = UserState.MOVING.value
val created_at: Long? = System.currentTimeMillis()
)

fun ApiLocation.toLocation() = android.location.Location("").apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,10 @@ fun LocationJourney.toLocationFromMovingJourney() = Location("").apply {
latitude = this@toLocationFromMovingJourney.to_latitude ?: 0.0
longitude = this@toLocationFromMovingJourney.to_longitude ?: 0.0
}

fun Location.toLocationJourney(userId: String, journeyId: String) = LocationJourney(
id = journeyId,
user_id = userId,
from_latitude = latitude,
from_longitude = longitude
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ data class LocationTable(
@ColumnInfo(name = "user_id")
val userId: String = "",

@ColumnInfo(name = "last_five_minutes_locations")
val lastFiveMinutesLocations: String? = null, // last 5 min ApiLocations
@ColumnInfo(name = "last_five_locations")
val lastFiveLocations: String? = null, // last 5 extracted locations

@ColumnInfo(name = "last_location_journey")
val lastLocationJourney: String? = null // last journey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import com.canopas.yourspace.data.service.auth.AuthService
import com.canopas.yourspace.data.service.location.ApiJourneyService
import com.canopas.yourspace.data.service.location.ApiLocationService
import com.canopas.yourspace.data.service.location.LocationManager
import com.canopas.yourspace.data.storage.room.LocationTableDatabase
import com.canopas.yourspace.data.utils.LocationConverters
import com.canopas.yourspace.data.storage.LocationCache
import com.google.android.gms.location.LocationResult
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
Expand All @@ -20,6 +19,7 @@ import timber.log.Timber
import javax.inject.Inject

const val ACTION_LOCATION_UPDATE = "action.LOCATION_UPDATE"
const val MIN_LOCATION_DISTANCE = 10f

@AndroidEntryPoint
class LocationUpdateReceiver : BroadcastReceiver() {
Expand All @@ -33,17 +33,14 @@ class LocationUpdateReceiver : BroadcastReceiver() {
@Inject
lateinit var locationManager: LocationManager

@Inject
lateinit var locationTableDatabase: LocationTableDatabase

@Inject
lateinit var authService: AuthService

@Inject
lateinit var converters: LocationConverters
lateinit var journeyRepository: JourneyRepository

@Inject
lateinit var journeyRepository: JourneyRepository
lateinit var locationCache: LocationCache

private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

Expand All @@ -53,16 +50,21 @@ class LocationUpdateReceiver : BroadcastReceiver() {
try {
val userId = authService.currentUser?.id ?: return@launch

val lastLocation = locationCache.getLastLocation(userId)
locationResult.locations.forEach { extractedLocation ->
val userState = journeyRepository.getUserState(userId, extractedLocation)
locationService.saveCurrentLocation(
userId,
extractedLocation.latitude,
extractedLocation.longitude,
System.currentTimeMillis(),
userState = userState
)
journeyRepository.saveLocationJourney(userState, extractedLocation, userId)
val distance = lastLocation?.distanceTo(extractedLocation) ?: 10f
// Save location only if the distance is greater than 10 meters
// to avoid saving the same location
if (distance >= MIN_LOCATION_DISTANCE) {
locationService.saveCurrentLocation(
userId,
extractedLocation.latitude,
extractedLocation.longitude,
System.currentTimeMillis()
)
locationCache.putLastLocation(extractedLocation, userId)
}
journeyRepository.saveLocationJourney(extractedLocation, userId)
}
} catch (e: Exception) {
Timber.e(e, "Error while saving location")
Expand Down
Loading

0 comments on commit b98f9a8

Please sign in to comment.