Skip to content

Commit

Permalink
feat: Added cart sharing, updated cart and item models, and improved …
Browse files Browse the repository at this point in the history
…database interactions

- Implemented cart sharing functionality, allowing users to share their carts via a deep link.
- Updated `ShoppingCartTable` to store the `date` as a `Long` instead of `Date`, and added a `toDate()` method for conversion.
- Made `ShoppingCartItemsTable` serializable using `@Serializable`.
- Refactored `NewCartDao` to return a nullable `ShoppingCartTable?` for `getCartById`.
- Added `importSharedCart` function to `MainViewModel` to handle shared cart data.
- Implemented `importSharedCartImplementation` in `MainRepositoryImplementation` to handle the decoding and import logic of shared cart data.
- Updated `MainScaffoldContent` to add padding for the AdView when ads are enabled.
- Updated `HomeScreen` to be able to show AdBanners in the middle of the list of cart items.
- Added a new `shareCart` method to `CartViewModel` to allow for sharing the cart.
- Refactored `CartRepository` to add new `generateCartShareLink` method to generate the deep link and updated the `loadCartIdImplementation` method to return a `ShoppingCartTable?`
- Updated `AddNewCartAlertDialog` to create the `ShoppingCartTable` with the date as a Long.
- Fixed the app full name in the `VersionInfoAlertDialog`.
- Updated the app `roundIcon` and added a new `intent-filter` for the deep link.
- Updated the `onNewIntent` method in the `MainActivity` to handle deep links.
- Added `kotlin-serialization` and `jetbrainsKotlinParcelize` plugins.
- Updated various dependencies in `libs.versions.toml` and `build.gradle.kts`.
- Added `Json` and `Base64` to the app.
  • Loading branch information
Mihai-Cristian Condrea committed Feb 18, 2025
1 parent 84ab764 commit 0611c31
Show file tree
Hide file tree
Showing 22 changed files with 342 additions and 97 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
plugins {
alias(notation = libs.plugins.androidApplication)
alias(notation = libs.plugins.jetbrainsKotlinAndroid)
alias(notation = libs.plugins.jetbrainsKotlinParcelize)
alias(notation = libs.plugins.kotlin.serialization)
alias(notation = libs.plugins.googlePlayServices)
alias(notation = libs.plugins.googleFirebase)
alias(notation = libs.plugins.compose.compiler)
Expand Down
16 changes: 15 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
android:localeConfig="@xml/locales_config"
android:logo="@drawable/ic_launcher_foreground"
android:resizeableActivity="true"
android:roundIcon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:targetApi="33">
Expand All @@ -45,14 +45,28 @@
<activity
android:name=".ui.screens.main.MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:theme="@style/SplashScreenTheme"
android:windowSoftInputMode="adjustResize">

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="import"
android:scheme="cartcalculator" />
</intent-filter>

<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ interface NewCartDao {
suspend fun getAll(): List<ShoppingCartTable>

@Query("SELECT * FROM ShoppingCartTable WHERE cartId = :cartId")
suspend fun getCartById(cartId: Int): ShoppingCartTable
suspend fun getCartById(cartId: Int): ShoppingCartTable?
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.d4rk.cartcalculator.data.database.table

import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable

/**
* Data class representing a shopping cart item in the database.
Expand All @@ -12,6 +13,7 @@ import androidx.room.PrimaryKey
* @property price The price of the item.
* @property quantity The quantity of the item in the shopping cart.
*/
@Serializable
@Entity
data class ShoppingCartItemsTable(
@PrimaryKey(autoGenerate = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package com.d4rk.cartcalculator.data.database.table

import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.d4rk.cartcalculator.data.repository.DateConverter
import kotlinx.serialization.Serializable
import java.util.Date

@Serializable
@Entity
@TypeConverters(DateConverter::class)
data class ShoppingCartTable(
@PrimaryKey(autoGenerate = true) val cartId: Int = 0, val name: String, val date: Date
)
@PrimaryKey(autoGenerate = true) val cartId: Int = 0,
val name: String,
val date: Long
) {
fun toDate(): Date = Date(date)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.d4rk.cartcalculator.data.model.ui.screens

import com.d4rk.cartcalculator.data.database.table.ShoppingCartItemsTable
import com.d4rk.cartcalculator.data.database.table.ShoppingCartTable
import kotlinx.serialization.Serializable

@Serializable
data class UiCartScreen(
val selectedCurrency: String = "" ,
val totalPrice: Double = 0.0 ,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data class UiHomeModel(
val carts: MutableList<ShoppingCartTable> = mutableListOf() ,
val cartToDelete: ShoppingCartTable? = null ,
val showCreateCartDialog: Boolean = false ,
val showImportDialog: Boolean = false ,
val showDeleteCartDialog: Boolean = false ,
val showSnackbar: Boolean = false ,
val snackbarMessage: String = "" ,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ fun AddNewCartAlertDialogContent(
imeAction = ImeAction.Done
) ,
keyboardActions = KeyboardActions(onDone = {
newCart.value =
ShoppingCartTable(name = nameText.value.ifEmpty { defaultName } ,
date = currentDate)
newCart.value = ShoppingCartTable(
name = nameText.value.ifEmpty { defaultName },
date = currentDate.time
)

keyboardController?.hide()
}) ,
placeholder = { Text(text = stringResource(id = R.string.shopping_cart)) })
Expand All @@ -90,6 +92,5 @@ fun AddNewCartAlertDialogContent(
MediumVerticalSpacer()
Text(text = stringResource(id = R.string.summary_cart_dialog))
}
newCart.value =
ShoppingCartTable(name = nameText.value.ifEmpty { defaultName } , date = currentDate)
newCart.value = ShoppingCartTable(name = nameText.value.ifEmpty { defaultName } , date = currentDate.time)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.d4rk.cartcalculator.ui.components.dialogs

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ContentPaste
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.res.stringResource

@Composable
fun ImportCartAlertDialog(onDismiss : () -> Unit , onImport : (String) -> Unit) {
var cartLink : String by remember { mutableStateOf(value = "") }

AlertDialog(onDismissRequest = onDismiss , title = { Text(text = "Import Cart") } , text = {
ImportCartAlertDialogContent(cartLink = cartLink , onCartLinkChange = { cartLink = it })
} , confirmButton = {
TextButton(onClick = {
if (cartLink.isNotEmpty()) {
onImport(cartLink)
onDismiss()
}
}) {
Text(text = "Import")
}
} , dismissButton = {
TextButton(onClick = onDismiss) {
Text(text = stringResource(id = android.R.string.cancel))
}
})
}

@Composable
fun ImportCartAlertDialogContent(cartLink : String , onCartLinkChange : (String) -> Unit) {
val clipboardManager : ClipboardManager = LocalClipboardManager.current

Column {
OutlinedTextField(value = cartLink , onValueChange = onCartLinkChange , label = { Text("Paste Cart Link") } , modifier = Modifier.fillMaxWidth() , maxLines = 1 , trailingIcon = {
IconButton(onClick = {
clipboardManager.getText()?.text?.let { text ->
onCartLinkChange(text)
}
}) {
Icon(modifier = Modifier.size(size = ButtonDefaults.IconSize) , imageVector = Icons.Outlined.ContentPaste , contentDescription = stringResource(id = android.R.string.paste))
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ fun TopAppBarScaffoldWithBackButtonAndActions(
})
}
if (showDialog.value) {
VersionInfoAlertDialog(onDismiss = { showDialog.value = false } , copyrightString = R.string.copyright , appName = R.string.app_name , versionName = BuildConfig.VERSION_NAME , versionString = com.d4rk.android.libs.apptoolkit.R.string.version)
VersionInfoAlertDialog(onDismiss = { showDialog.value = false } , copyrightString = R.string.copyright , appName = R.string.app_full_name , versionName = BuildConfig.VERSION_NAME , versionString = com.d4rk.android.libs.apptoolkit.R.string.version)
}
} ,
scrollBehavior = scrollBehavior ,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import androidx.compose.material.icons.outlined.AddCircleOutline
import androidx.compose.material.icons.outlined.AddShoppingCart
import androidx.compose.material.icons.outlined.CreditCard
import androidx.compose.material.icons.outlined.RemoveCircleOutline
import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Checkbox
Expand Down Expand Up @@ -130,6 +131,10 @@ fun CartScreen(activity : CartActivity , cartId : Int) {
Icon(imageVector = Icons.Outlined.CreditCard, contentDescription = "Open Google Pay")
}
}

IconButton(onClick = { viewModel.shareCart(context, cartId) }) {
Icon(imageVector = Icons.Outlined.Share, contentDescription = "Share Cart")
}
} , scrollBehavior = scrollBehavior)
}) { paddingValues ->
Box(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.d4rk.cartcalculator.ui.screens.cart

import android.app.Application
import android.content.Context
import android.content.Intent
import androidx.lifecycle.viewModelScope
import com.d4rk.cartcalculator.data.database.table.ShoppingCartItemsTable
import com.d4rk.cartcalculator.data.database.table.ShoppingCartTable
Expand All @@ -25,15 +27,17 @@ class CartViewModel(application : Application) : BaseViewModel(application) {
fun loadCart(cartId : Int) {
viewModelScope.launch(context = coroutineExceptionHandler) {
showLoading()
val cart : ShoppingCartTable = repository.loadCartIdImplementation(cartId = cartId)
repository.loadCartItemsRepository(cartId = cartId) { items ->
_uiState.update { currentState ->
currentState.copy(
cartItems = items , cart = cart
)
val cart : ShoppingCartTable? = repository.loadCartIdImplementation(cartId = cartId)
cart?.let { safeCart ->
repository.loadCartItemsRepository(cartId = cartId) { items ->
_uiState.update { currentState ->
currentState.copy(
cartItems = items , cart = safeCart
)
}
calculateTotalPrice()
}
calculateTotalPrice()
}
} ?: kotlin.run {}
hideLoading()
initializeVisibilityStates()
}
Expand Down Expand Up @@ -109,7 +113,7 @@ class CartViewModel(application : Application) : BaseViewModel(application) {
cartItem.quantity = newQuantity
repository.updateCartItemRepository(cartItem = cartItem) {
_uiState.update { currentState ->
val updatedQuantities : MutableMap<Int, Int> = currentState.itemQuantities.toMutableMap()
val updatedQuantities : MutableMap<Int , Int> = currentState.itemQuantities.toMutableMap()
updatedQuantities[cartItem.itemId] = newQuantity
return@update currentState.copy(itemQuantities = updatedQuantities)
}
Expand All @@ -118,7 +122,7 @@ class CartViewModel(application : Application) : BaseViewModel(application) {
}
}

fun editCartItem(cartItem: ShoppingCartItemsTable) {
fun editCartItem(cartItem : ShoppingCartItemsTable) {
viewModelScope.launch(context = coroutineExceptionHandler) {
repository.updateCartItemRepository(cartItem = cartItem) {
_uiState.update { currentState ->
Expand Down Expand Up @@ -165,8 +169,7 @@ class CartViewModel(application : Application) : BaseViewModel(application) {

private fun calculateTotalPrice() {
viewModelScope.launch(context = coroutineExceptionHandler) {
val total =
uiState.value.cartItems.sumOf { item -> item.price.toDouble() * item.quantity }
val total = uiState.value.cartItems.sumOf { item -> item.price.toDouble() * item.quantity }
_uiState.update { currentState ->
currentState.copy(totalPrice = total)
}
Expand All @@ -191,14 +194,27 @@ class CartViewModel(application : Application) : BaseViewModel(application) {
}
}

fun toggleEditDialog(cartItem: ShoppingCartItemsTable?) {
fun toggleEditDialog(cartItem : ShoppingCartItemsTable?) {
viewModelScope.launch(context = coroutineExceptionHandler) {
_uiState.update { currentState ->
currentState.copy(
openEditDialog = cartItem != null,
currentCartItemForEdit = cartItem
openEditDialog = cartItem != null , currentCartItemForEdit = cartItem
)
}
}
}

fun shareCart(context : Context , cartId : Int) {
viewModelScope.launch(context = coroutineExceptionHandler) {
repository.generateCartShareLinkRepository(cartId = cartId) { shareLink ->
shareLink?.let {
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT , it)
}
context.startActivity(Intent.createChooser(intent , "Share Cart"))
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.d4rk.cartcalculator.ui.screens.cart.repository

import com.d4rk.cartcalculator.data.core.AppCoreManager
import com.d4rk.cartcalculator.data.database.table.ShoppingCartItemsTable
import com.d4rk.cartcalculator.data.database.table.ShoppingCartTable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

class CartRepository : CartRepositoryImplementation() {

Expand Down Expand Up @@ -54,4 +58,11 @@ class CartRepository : CartRepositoryImplementation() {
}
}
}

suspend fun generateCartShareLinkRepository(cartId: Int, onSuccess: (String?) -> Unit) {
withContext(Dispatchers.IO) {
val shareLink: String? = generateCartShareLinkImplementation(cartId)
withContext(Dispatchers.Main) { onSuccess(shareLink) }
}
}
}
Loading

0 comments on commit 0611c31

Please sign in to comment.