Skip to content

Commit

Permalink
Merge branch 'main' into sneh/fix-dependencies-with-latest-versions
Browse files Browse the repository at this point in the history
# Conflicts:
#	data/build.gradle.kts
  • Loading branch information
cp-sneh-s committed Jan 9, 2025
2 parents fa0a641 + cfdf12b commit 87751ae
Show file tree
Hide file tree
Showing 50 changed files with 2,523 additions and 511 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ If you are reporting a bug, please help speed up problem diagnosis by providing
We actively welcome your pull requests. You can find instructions on building the project in [README.md](https://github.com/canopas/group-track-android).
1. Fork the repo and create your branch from `master`.
2. If you've added code that should be tested, add tests
4. Make sure your code lints.
3. Make sure your code lints.

## Labels
Labels on issues are managed by contributors, you don't have to worry about them. Here's a list of what they mean:
Expand All @@ -29,4 +29,4 @@ Labels on issues are managed by contributors, you don't have to worry about them
* **non-library**: issue is not in the core library code, but rather in documentation, samples, build process, releases

## License
By contributing to GroupTrack, you agree that your contributions will be licensed under its Apache License, Version 2.0. See LICENSE file for details.
By contributing to Grouptrack, you agree that your contributions will be licensed under its Apache License, Version 2.0. See LICENSE file for details.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Grouptrack ensures the privacy and security of your data by implementing end-to-

<table>
<tr>
<th width="33%" >Create/Join Space</th>
<th width="33%" >Create/Join Group</th>
<th width="33%" >Share Location</th>
<th width="33%" >Location History</th>
</tr>
Expand Down
16 changes: 11 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import org.jetbrains.kotlin.konan.properties.hasProperty
import java.util.Properties

Expand All @@ -14,6 +15,7 @@ plugins {
var versionMajor = 1
var versionMinor = 0
var versionBuild = 0
val targetSdkVersion: Int = 34

android {
namespace = "com.canopas.yourspace"
Expand All @@ -30,7 +32,8 @@ android {
defaultConfig {
applicationId = "com.canopas.yourspace"
minSdk = 24
targetSdk = 34
targetSdk = targetSdkVersion

versionCode = versionMajor * 1000000 + versionMinor * 10000 + versionBuild
versionName = "$versionMajor.$versionMinor.$versionBuild"
setProperty("archivesBaseName", "GroupTrack-$versionName-$versionCode")
Expand Down Expand Up @@ -60,7 +63,6 @@ android {
buildConfigField("String", "PLACE_API_KEY", "\"${p.getProperty("PLACE_API_KEY")}\"")
}
}

signingConfigs {
if (System.getenv("APKSIGN_KEYSTORE") != null) {
create("release") {
Expand Down Expand Up @@ -103,12 +105,12 @@ android {
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
buildFeatures {
compose = true
Expand Down Expand Up @@ -213,5 +215,9 @@ dependencies {
// Gson
implementation("com.google.code.gson:gson:2.10.1")

// Signal Protocol
implementation("org.signal:libsignal-client:0.64.1")
implementation("org.signal:libsignal-android:0.64.1")

implementation(project(":data"))
}
8 changes: 8 additions & 0 deletions app/src/main/java/com/canopas/yourspace/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import com.canopas.yourspace.ui.flow.messages.chat.MessagesScreen
import com.canopas.yourspace.ui.flow.messages.thread.ThreadsScreen
import com.canopas.yourspace.ui.flow.onboard.OnboardScreen
import com.canopas.yourspace.ui.flow.permission.EnablePermissionsScreen
import com.canopas.yourspace.ui.flow.pin.enterpin.EnterPinScreen
import com.canopas.yourspace.ui.flow.pin.setpin.SetPinScreen
import com.canopas.yourspace.ui.flow.settings.SettingsScreen
import com.canopas.yourspace.ui.flow.settings.profile.EditProfileScreen
import com.canopas.yourspace.ui.flow.settings.space.SpaceProfileScreen
Expand Down Expand Up @@ -124,6 +126,12 @@ fun MainApp(viewModel: MainViewModel) {
slideComposable(AppDestinations.signIn.path) {
SignInMethodsScreen()
}
slideComposable(AppDestinations.setPin.path) {
SetPinScreen()
}
slideComposable(AppDestinations.enterPin.path) {
EnterPinScreen()
}

slideComposable(AppDestinations.home.path) {
navController.currentBackStackEntry
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/com/canopas/yourspace/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,18 @@ class MainViewModel @Inject constructor(

init {
viewModelScope.launch {
val currentUser = authService.getUser()
val isExistingUser = currentUser != null
val identityKeysMatch = currentUser?.let {
it.identity_key_public?.toBytes().contentEquals(it.identity_key_private?.toBytes())
} ?: false
val showSetPinScreen = isExistingUser && identityKeysMatch
val showEnterPinScreen = showSetPinScreen && userPreferences.getPasskey().isNullOrEmpty()
val initialRoute = when {
!userPreferences.isIntroShown() -> AppDestinations.intro.path
userPreferences.currentUser == null -> AppDestinations.signIn.path
showEnterPinScreen -> AppDestinations.enterPin.path
showSetPinScreen -> AppDestinations.setPin.path
!userPreferences.isOnboardShown() -> AppDestinations.onboard.path
else -> AppDestinations.home.path
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ fun OtpInputField(
pinText: String,
onPinTextChange: (String) -> Unit,
textStyle: TextStyle = AppTheme.appTypography.header2,
digitCount: Int = 6
digitCount: Int = 6,
keyboardType: KeyboardType = KeyboardType.Text
) {
val focusRequester = remember { FocusRequester() }
BoxWithConstraints(
Expand All @@ -55,7 +56,7 @@ fun OtpInputField(
onPinTextChange(it)
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
keyboardOptions = KeyboardOptions(keyboardType = keyboardType),
modifier = Modifier.focusRequester(focusRequester),
decorationBox = {
Row(
Expand All @@ -67,7 +68,7 @@ fun OtpInputField(
repeat(digitCount) { index ->
OTPDigit(index, pinText, textStyle, focusRequester, width = width)

if (index == 2) {
if (index == 2 && digitCount > 4) {
HorizontalDivider(
modifier = Modifier
.width(16.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ class SignInMethodViewModel @Inject constructor(
_state.emit(_state.value.copy(showGoogleLoading = true))
try {
val firebaseToken = firebaseAuth.signInWithGoogleAuthCredential(account.idToken)
val isNewUser = authService.verifiedGoogleLogin(
authService.verifiedGoogleLogin(
firebaseAuth.currentUserUid,
firebaseToken,
account
)
onSignUp(isNewUser)
onSignUp()
_state.emit(_state.value.copy(showGoogleLoading = false))
} catch (e: Exception) {
Timber.e(e, "Failed to sign in with google")
Expand All @@ -65,7 +65,7 @@ class SignInMethodViewModel @Inject constructor(
_state.emit(_state.value.copy(showAppleLoading = true))
try {
val firebaseToken = authResult.user?.getIdToken(true)?.await()
val isNewUser = authService.verifiedAppleLogin(
authService.verifiedAppleLogin(
firebaseAuth.currentUserUid,
firebaseToken?.token ?: "",
authResult.user ?: run {
Expand All @@ -78,7 +78,7 @@ class SignInMethodViewModel @Inject constructor(
return@launch
}
)
onSignUp(isNewUser)
onSignUp()
_state.emit(_state.value.copy(showAppleLoading = false))
} catch (e: Exception) {
Timber.e(e, "Failed to sign in with Apple")
Expand All @@ -95,17 +95,22 @@ class SignInMethodViewModel @Inject constructor(
_state.value = _state.value.copy(error = null)
}

private fun onSignUp(isNewUser: Boolean) = viewModelScope.launch(appDispatcher.MAIN) {
if (isNewUser) {
private fun onSignUp() = viewModelScope.launch(appDispatcher.MAIN) {
val currentUser = authService.currentUser ?: return@launch
val showSetPinScreen = currentUser.identity_key_public?.toBytes()
.contentEquals(currentUser.identity_key_private?.toBytes())
val showEnterPinScreen = !showSetPinScreen && userPreferences.getPasskey()
.isNullOrEmpty()

if (showSetPinScreen) {
navigator.navigateTo(
AppDestinations.onboard.path,
AppDestinations.setPin.path,
popUpToRoute = AppDestinations.signIn.path,
inclusive = true
)
} else {
userPreferences.setOnboardShown(true)
} else if (showEnterPinScreen) {
navigator.navigateTo(
AppDestinations.home.path,
AppDestinations.enterPin.path,
popUpToRoute = AppDestinations.signIn.path,
inclusive = true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ private fun MemberInfoView(
val state by viewModel.state.collectAsState()

var address by remember { mutableStateOf("") }
val time = timeAgo(location?.created_at ?: 0)
val time = location?.created_at?.let { timeAgo(it) } ?: ""
val userStateText = if (user.noNetwork) {
stringResource(R.string.map_selected_user_item_no_network_state)
} else if (user.locationPermissionDenied) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ fun JourneyLocationItem(
.padding(start = 16.dp)
.weight(1f)
) {
val time = getFormattedJourneyTime(location.created_at ?: 0, location.update_at ?: 0)
val time = getFormattedJourneyTime(location.created_at ?: 0, location.updated_at ?: 0)
val distance = getDistanceString(location.route_distance ?: 0.0)

PlaceInfo(title, "$time - $distance")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private fun JourneyInfo(journey: LocationJourney) {
.padding(start = 16.dp)
.weight(1f)
) {
journey.update_at?.let { getFormattedLocationTime(it) }
journey.updated_at?.let { getFormattedLocationTime(it) }
?.let { PlaceInfo(toAddressStr, it) }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class UserJourneyDetailViewModel @Inject constructor(
private fun fetchJourney() = viewModelScope.launch(appDispatcher.IO) {
try {
_state.value = _state.value.copy(isLoading = true)
val journey = journeyService.getLocationJourneyFromId(userId, journeyId)
val journey = journeyService.getLocationJourneyFromId(journeyId)
if (journey == null) {
_state.value = _state.value.copy(
isLoading = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class JourneyTimelineViewModel @Inject constructor(
try {
val from = _state.value.selectedTimeFrom
val to = _state.value.selectedTimeTo
val lastJourneyTime = allJourneys.minOfOrNull { it.update_at!! }
val lastJourneyTime = allJourneys.minOfOrNull { it.updated_at }

val locations = if (loadMore) {
journeyService.getMoreJourneyHistory(userId, lastJourneyTime)
Expand All @@ -105,8 +105,8 @@ class JourneyTimelineViewModel @Inject constructor(
}

val filteredLocations = locations.filter {
(it.created_at?.let { created -> created in from..to } ?: false) ||
(it.update_at?.let { updated -> updated in from..to } ?: false)
it.created_at in from..to ||
it.updated_at in from..to
}

val locationJourneys = (allJourneys + filteredLocations).groupByDate()
Expand Down Expand Up @@ -158,12 +158,12 @@ class JourneyTimelineViewModel @Inject constructor(

private fun List<LocationJourney>.groupByDate(): Map<Long, List<LocationJourney>> {
val journeys = this.distinctBy { it.id }
.sortedByDescending { it.update_at!! }
.sortedByDescending { it.updated_at }

val groupedItems = mutableMapOf<Long, MutableList<LocationJourney>>()

for (journey in journeys) {
val date = getDayStartTimestamp(journey.created_at!!)
val date = getDayStartTimestamp(journey.created_at)

if (!groupedItems.containsKey(date)) {
groupedItems[date] = mutableListOf()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.canopas.yourspace.ui.flow.pin.enterpin

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.canopas.yourspace.R
import com.canopas.yourspace.ui.component.OtpInputField
import com.canopas.yourspace.ui.component.PrimaryButton

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EnterPinScreen() {
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(R.string.enter_pin_top_bar_title)) },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background
)
)
},
containerColor = MaterialTheme.colorScheme.background
) {
EnterPinContent(modifier = Modifier.padding(it))
}
}

@Composable
private fun EnterPinContent(modifier: Modifier) {
val viewModel = hiltViewModel<EnterPinViewModel>()
val state by viewModel.state.collectAsState()

Column(
modifier = modifier
.padding(32.dp)
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = stringResource(R.string.enter_pin_header_text_part_one),
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 24.dp),
textAlign = TextAlign.Center
)

Text(
text = stringResource(R.string.enter_pin_header_text_part_two),
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 24.dp),
textAlign = TextAlign.Center
)

OtpInputField(
pinText = state.pin,
onPinTextChange = { viewModel.onPinChanged(it) },
digitCount = 4,
keyboardType = KeyboardType.Number
)

Spacer(modifier = Modifier.height(16.dp))

if (state.isPinInvalid) {
Text(
text = stringResource(R.string.enter_pin_invalid_pin_text),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(top = 8.dp)
)
}

Spacer(modifier = Modifier.height(24.dp))

PrimaryButton(
label = stringResource(R.string.enter_pin_continue_button_text),
onClick = {
viewModel.processPin()
},
enabled = state.pin != "" && !state.isPinInvalid && state.pin.length == 4,
modifier = Modifier.fillMaxWidth(),
showLoader = state.showLoader
)
}
}
Loading

0 comments on commit 87751ae

Please sign in to comment.