Skip to content

Wallet for storing credits cards. JetWallet's written in Compose with Material 3

Notifications You must be signed in to change notification settings

ZhEgor/JetWallet

Repository files navigation

JetWallet

Wallet for storing credits cards. JetWallet's written in Compose with Material3

Preview

Theme

Android SDK >= Api 31 Android SDK < Api 31

Add card feature

Add a card manually Add a card using a card recognizer

Special features

Rotating Card skin

Tech stack

  • Compose
  • Material 3
  • MVI
  • Multi-moduling
  • GradleKts
  • Koin
  • Coroutines
  • Room
  • SqlCipher
  • Google MLKit
  • CameraX
  • Coil

Development subtleties

Storing data:

  • We store card details encrypted using SqlCipher in Room database, to store a password for encryption, we use KeyStore.

Multi-moduling:

The app has structure:

  • app
  • core
    • common
    • navigation
    • theme
    • components
    • cardrecognition
  • data
  • domain
  • presentation
    • mywallet
    • auth
  • buildSrc

UI:

  • The app supports DarkTheme, LightTheme and DynamicTheme.

  • Below you can see generated compose metrics report for :presentation:mywallet module, for more information on how to get this report follow the link.

mywallet_debug-composables.txt
    
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardCarouselRoute()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardCarouselScreen(
  stable viewModel: CardCarouselViewModel
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardsAreLoaded(
  stable viewModel: CardCarouselViewModel
  stable state: Loaded
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardsAreLoading()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardsAreEmpty()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardCarouselScreenPreview()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun AddCardButton(
  stable : BoxScope
  stable onClick: Function0
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun BankCard(
  stable card: BankCard
  stable onCopy: Function1
  stable onEditClick: Function1
  stable onDeleteClick: Function1
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun EditableBankCard(
  stable bankCard: BankCard
  stable isFrontSide: Boolean
  stable onPhotoUriChange: Function1
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun PickSkinButton(
  stable modifier: Modifier? = @static Companion
  stable onClick: Function0
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DeleteSkinButton(
  stable modifier: Modifier? = @static Companion
  stable onClick: Function0
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ShimmeringBankCard()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ImmutableBackSideCard(
  stable bankCard: BankCard
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ImmutableBackSideCardPreview()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ShimmeringBackSideCard()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ShimmeringBackSideCardPreview()
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun StatelessBackSideCard(
  stable modifier: Modifier? = @static Companion
  stable verificationNumberContent: Function2
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun AuthorizedSignatureStripe(
  stable modifier: Modifier? = @static Companion
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun MagneticStripe(
  stable modifier: Modifier? = @static Companion
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun SignatureAndVerificationNumberRow(
  stable modifier: Modifier? = @static Companion
  stable verificationNumberContent: Function2
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun VerificationNumberContainer(
  stable modifier: Modifier? = @static Companion
  stable content: Function2
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun VerificationNumberText(
  stable modifier: Modifier? = @static Companion
  stable number: String
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun CardTextContainer(
  stable modifier: Modifier? = @static Companion
  stable content: Function2
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun EmptyBankCard(
  stable modifier: Modifier? = @static Companion
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun ImmutableFrontSideCard(
  stable bankCard: BankCard
  stable toolsContent: @[ExtensionFunctionType] Function3? = @static null
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun FrontSideCardPreview()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ShimmeringFrontSideCard()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ShimmeringFrontSideCardPreview()
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable], [androidx.compose.ui.UiComposable], [androidx.compose.ui.UiComposable], [androidx.compose.ui.UiComposable]]") fun StatelessFrontSideCard(
  stable modifier: Modifier? = @static Companion
  stable cardNumberContent: Function2
  stable expirationDateContent: Function2
  stable cardholderContent: Function2
  stable skinContent: Function2? = @static null
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun CardNumberContainer(
  stable modifier: Modifier? = @static Companion
  stable content: Function2
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardNumberText(
  stable modifier: Modifier? = @static Companion
  stable number: String
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun CardholderContainer(
  stable modifier: Modifier? = @static Companion
  stable content: Function2
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardholderText(
  stable modifier: Modifier? = @static Companion
  stable cardholder: String
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun ExpirationDateContainer(
  stable modifier: Modifier? = @static Companion
  stable content: Function2
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ExpirationDateText(
  stable modifier: Modifier? = @static Companion
  stable expirationDate: String
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun SkinContainer(
  stable modifier: Modifier? = @static Companion
  stable content: Function2
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun ToolsContainer(
  stable : BoxScope
  stable modifier: Modifier? = @static Companion
  stable toolsContent: @[ExtensionFunctionType] Function3? = @static null
)
restartable skippable scheme("[androidx.compose.ui.UiComposable, [androidx.compose.ui.UiComposable]]") fun RotatingCardWrapper(
  stable modifier: Modifier? = @static Companion
  stable initialIsFrontSide: Boolean = @dynamic LiveLiterals$RotatingCardWrapperKt.Boolean$param-initialIsFrontSide$fun-RotatingCardWrapper()
  stable onLongPress: Function1<@[ParameterName(name = 'isFront')] Boolean, Unit>? = @static null
  stable cardContent: Function3<@[ParameterName(name = 'isFrontSide')] Boolean, Composer, Int, Unit>
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun AddCardPopUp(
  popUpState: PopUpState
  stable onHide: Function0
  stable onAddClick: Function1
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun EditCardPopUp(
  popUpState: PopUpState
  stable bankCard: BankCard
  stable onHide: Function0
  stable onSaveClick: Function1
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun CardRecognizerPage(
  stable modifier: Modifier? = @static Companion
  stable forceToLeaveComposition: Boolean
  stable onReturnToPreviousPage: Function0
  stable onFrontSideCardRecognized: Function2<@[ParameterName(name = 'cardNumber')] String, @[ParameterName(name = 'expirationDate')] String, Unit>
  stable onBackSideCardRecognized: Function1<@[ParameterName(name = 'verificationNumber')] String, Unit>
)
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun EditableCardPage(
  stable modifier: Modifier? = @static Companion
  stable bankCard: BankCard
  stable editBankCardCallbacks: EditBankCardCallbacks
  stable onDone: Function0
)
    
  
mywallet_debug-classes.txt
  
stable class LoadCards {
   = Stable
}
stable class LoadedCards {
  stable val cards: StableList
   = Stable
}
stable class FailedToLoadCards {
  stable val errorMessage: String?
   = Stable
}
stable class ChangeStateOfAddPopUp {
  stable val show: Boolean
   = Stable
}
stable class ChangeStateOfEditPopUp {
  stable val show: Boolean
  stable val cardToEdit: BankCard?
   = Stable
}
stable class AddCard {
  stable val bankCard: BankCard
   = Stable
}
stable class AddedCardSuccessfully {
   = Stable
}
stable class SaveCard {
  stable val bankCard: BankCard
   = Stable
}
stable class SavedCardSuccessfully {
   = Stable
}
stable class DeleteCard {
  stable val bankCard: BankCard
   = Stable
}
stable class DeletedCardSuccessfully {
   = Stable
}
stable class Loading {
   = Stable
}
stable class Loaded {
  stable val cards: StableList
   = Stable
}
stable class Empty {
   = Stable
}

mywallet_debug-module.json
    
{
 "skippableComposables": 91,
 "restartableComposables": 91,
 "readonlyComposables": 0,
 "totalComposables": 91,
 "restartGroups": 91,
 "totalGroups": 108,
 "staticArguments": 67,
 "certainArguments": 66,
 "knownStableArguments": 617,
 "knownUnstableArguments": 3,
 "unknownStableArguments": 8,
 "totalArguments": 628,
 "markedStableClasses": 0,
 "inferredStableClasses": 14,
 "inferredUnstableClasses": 0,
 "inferredUncertainClasses": 0,
 "effectivelyStableClasses": 14,
 "totalClasses": 14,
 "memoizedLambdas": 64,
 "singletonLambdas": 0,
 "singletonComposableLambdas": 20,
 "composableLambdas": 37,
 "totalLambdas": 72
}
    
  
mywallet_debug-composables.csv
    
package,name,composable,skippable,restartable,readonly,inline,isLambda,hasDefaults,defaultsGroup,groups,calls,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.CardCarouselRoute,CardCarouselRoute,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.CardCarouselScreen,CardCarouselScreen,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.CardsAreLoaded,CardsAreLoaded,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.CardsAreLoading,CardsAreLoading,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.CardsAreEmpty,CardsAreEmpty,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.CardCarouselScreenPreview,CardCarouselScreenPreview,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.button.AddCardButton,AddCardButton,1,1,1,0,0,0,0,0,1,5,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.BankCard,BankCard,1,1,1,0,0,0,0,0,1,3,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.EditableBankCard,EditableBankCard,1,1,1,0,0,0,0,0,1,3,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.PickSkinButton,PickSkinButton,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.DeleteSkinButton,DeleteSkinButton,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.ShimmeringBankCard,ShimmeringBankCard,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.ImmutableBackSideCard,ImmutableBackSideCard,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.ImmutableBackSideCardPreview,ImmutableBackSideCardPreview,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.ShimmeringBackSideCard,ShimmeringBackSideCard,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.ShimmeringBackSideCardPreview,ShimmeringBackSideCardPreview,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.StatelessBackSideCard,StatelessBackSideCard,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.components.AuthorizedSignatureStripe,AuthorizedSignatureStripe,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.components.MagneticStripe,MagneticStripe,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.components.SignatureAndVerificationNumberRow,SignatureAndVerificationNumberRow,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.components.VerificationNumberContainer,VerificationNumberContainer,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.back.components.VerificationNumberText,VerificationNumberText,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.common.CardTextContainer,CardTextContainer,1,1,1,0,0,0,0,0,1,3,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.EmptyBankCard,EmptyBankCard,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.ImmutableFrontSideCard,ImmutableFrontSideCard,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.FrontSideCardPreview,FrontSideCardPreview,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.ShimmeringFrontSideCard,ShimmeringFrontSideCard,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.ShimmeringFrontSideCardPreview,ShimmeringFrontSideCardPreview,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.StatelessFrontSideCard,StatelessFrontSideCard,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.components.CardNumberContainer,CardNumberContainer,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.components.CardNumberText,CardNumberText,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.components.CardholderContainer,CardholderContainer,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.components.CardholderText,CardholderText,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.components.ExpirationDateContainer,ExpirationDateContainer,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.components.ExpirationDateText,ExpirationDateText,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.components.SkinContainer,SkinContainer,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.card.front.components.ToolsContainer,ToolsContainer,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.cardwrapper.RotatingCardWrapper,RotatingCardWrapper,1,1,1,0,0,0,1,0,2,8,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.popups.AddCardPopUp,AddCardPopUp,1,1,1,0,0,0,0,0,1,4,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.popups.EditCardPopUp,EditCardPopUp,1,1,1,0,0,0,0,0,1,2,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.popups.components.CardRecognizerPage,CardRecognizerPage,1,1,1,0,0,0,0,0,1,1,
com.zhiroke.presentation.mywallet.presentation.cardcarousel.components.popups.components.EditableCardPage,EditableCardPage,1,1,1,0,0,0,0,0,1,3,
    
  

About

Wallet for storing credits cards. JetWallet's written in Compose with Material 3

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages