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

Feature/50 domain data acc #69

Merged
merged 29 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6797187
adds parcelize plugin support
artwist-polyakov Feb 1, 2024
14b8bf0
adds credentials dataclass
artwist-polyakov Feb 1, 2024
a570d63
adds crypto dependency
artwist-polyakov Feb 1, 2024
28b2b7c
adds converter-gson
artwist-polyakov Feb 1, 2024
527b162
adds credentials storage
artwist-polyakov Feb 1, 2024
71f3af7
adds hilt annotation to app
artwist-polyakov Feb 1, 2024
2867706
updates dagger version to sync with kotlin
artwist-polyakov Feb 1, 2024
d504ce7
fixes missprint in variable name
artwist-polyakov Feb 1, 2024
9f135bc
configure data module di
artwist-polyakov Feb 1, 2024
6feddd5
adds button to check encripted prefs
artwist-polyakov Feb 1, 2024
5294ee3
configures backup rules
artwist-polyakov Feb 1, 2024
b1da81d
adds delete credentials button
artwist-polyakov Feb 1, 2024
79ef27c
configures account information data
artwist-polyakov Feb 1, 2024
cc897d0
adds interaction module
artwist-polyakov Feb 1, 2024
be4bfb5
formatting
artwist-polyakov Feb 1, 2024
243b781
deletes unusual comment
artwist-polyakov Feb 1, 2024
74267c2
removes unusual emit
artwist-polyakov Feb 1, 2024
024a21b
50 Kotlin parcelize plugin setup fixes
Feb 3, 2024
3f01595
replaces gson to kotlinx.serialization
artwist-polyakov Feb 8, 2024
e5eff62
removes gson's gradle dependencies
artwist-polyakov Feb 8, 2024
0922855
adds Serializable annotation to CredentialsDto
artwist-polyakov Feb 8, 2024
bfffda8
splits DataModule at common and feature parts
artwist-polyakov Feb 8, 2024
7048245
renames auth data module
artwist-polyakov Feb 8, 2024
d071a39
move interactor module to auth package
artwist-polyakov Feb 8, 2024
6feb803
Merge branch 'dev' into feature/50-domain-data-acc
artwist-polyakov Feb 8, 2024
6a55d78
Merge branch 'dev' into feature/50-domain-data-acc
artwist-polyakov Feb 9, 2024
a8789a6
switch storage to repository in credential storage name
artwist-polyakov Feb 12, 2024
8ae8dc9
migrates to sharedstateflow
artwist-polyakov Feb 12, 2024
63cdff0
Merge branch 'dev' into feature/50-domain-data-acc
artwist-polyakov Feb 12, 2024
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
5 changes: 5 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ plugins {
alias(libs.plugins.firebase.crashlytics.gradle)
alias(libs.plugins.gms.googleServices)
alias(libs.plugins.firebase.appdistribution)
alias(libs.plugins.kotlin.parcelize)
}

android {
Expand Down Expand Up @@ -184,6 +185,10 @@ dependencies {

//Sign-In
implementation(libs.play.services.auth)

//Crypto
implementation(libs.crypto)

}

kapt {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.cashadvisor.authorization.data.dto

import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable

@Serializable
@Parcelize
data class CredentialsDto(
val accessToken: String,
val refreshToken: String,
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package app.cashadvisor.authorization.data.impl
import kotlinx.serialization.json.Json
import android.content.SharedPreferences
import app.cashadvisor.authorization.data.dto.CredentialsDto
import app.cashadvisor.authorization.domain.api.CredentialsStorage
import kotlinx.serialization.encodeToString

class CredentialsStorageImpl(
private val storage: SharedPreferences,
private val key: String,
private val json: Json
) : CredentialsStorage {

/**
* todo подумать, чтобы отнаследоваться от CoroutineScope by CoroutineScope(
* Dispatchers.IO +
* SupervisorJob() и транслировать key в asSharedFlow()
*/

override fun saveCredentials(credentials: CredentialsDto) {
val data = json.encodeToString(credentials)
storage.edit().putString(key, data).apply()
}

override fun getCredentials(): CredentialsDto? {
val data = storage.getString(key, null)
return data?.let { json.decodeFromString<CredentialsDto>(it) }
}

override fun hasCredentials(): Boolean {
return storage.contains(key)
}

override fun clearCredentials() {
storage.edit().remove(key).apply()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package app.cashadvisor.authorization.di

import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import app.cashadvisor.authorization.data.impl.CredentialsStorageImpl
import app.cashadvisor.authorization.domain.api.CredentialsStorage
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import javax.inject.Singleton


@Module
@InstallIn(SingletonComponent::class)
class AuthorizationDataModule {


@Provides
@Singleton
fun providesMasterKey(): String =
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)

@Provides
@Singleton
fun providesEncriptedSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
val masterKey = providesMasterKey()
return EncryptedSharedPreferences.create(
SECRET_SETTINGS,
masterKey,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}

@Provides
@Singleton
fun providesCredintialsStorage(
@ApplicationContext context: Context,
storage: SharedPreferences = providesEncriptedSharedPreferences(context),
key: String = CREDENTIALS_KEY,
gson: Json
): CredentialsStorage = CredentialsStorageImpl(storage, key, gson)
companion object {
private const val SECRET_SETTINGS = "secret_shared_prefs"
private const val CREDENTIALS_KEY = "secret_credentials"
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package app.cashadvisor.authorization.di


import app.cashadvisor.authorization.domain.api.AccountInformationInteractor
import app.cashadvisor.authorization.domain.api.CredentialsStorage
import app.cashadvisor.authorization.domain.impl.AccountInformationInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent


@Module
@InstallIn(ViewModelComponent::class)
abstract class DomainModule {
@Binds
abstract fun bindAccountInformationInteractor(
accountInformationInteractorImpl: AccountInformationInteractorImpl
): AccountInformationInteractor
class AuthorizationInteractorModule {
@Provides
fun providesAccountInformationInteractor(
JudjinGM marked this conversation as resolved.
Show resolved Hide resolved
storage: CredentialsStorage,
): AccountInformationInteractor = AccountInformationInteractorImpl(storage)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.cashadvisor.authorization.domain.api

import app.cashadvisor.authorization.data.dto.CredentialsDto

interface CredentialsStorage {

fun saveCredentials(credentials: CredentialsDto)
fun getCredentials(): CredentialsDto?
fun hasCredentials(): Boolean
fun clearCredentials()

}
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
package app.cashadvisor.authorization.domain.impl

import app.cashadvisor.authorization.domain.api.AccountInformationInteractor
import app.cashadvisor.authorization.domain.api.CredentialsStorage
import app.cashadvisor.authorization.domain.models.AccountInformation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import java.util.Date
import javax.inject.Inject

class AccountInformationInteractorImpl @Inject constructor() : AccountInformationInteractor {
class AccountInformationInteractorImpl(
private val storage: CredentialsStorage
) : AccountInformationInteractor {
override fun getAccountInformation(): Flow<AccountInformation> = flow {
// todo обновить класс с учетом реальных данных
emit(MOCK_ACCOUNT_INFORMATION)
if (storage.hasCredentials()) {
val data = storage.getCredentials()
avanisimov marked this conversation as resolved.
Show resolved Hide resolved
data?.let {
emit(
AccountInformation.Authorized(
accessToken = it.accessToken,
refreshToken = it.refreshToken
)
)
}
} else {
emit(AccountInformation.NotAuthorized)
}
}

companion object {
private val MOCK_ACCOUNT_INFORMATION = AccountInformation.Authorized(
email = "test@test.test",
token = "123",
accessToken = "123",
refreshToken = "123",
isEmailVerified = true,
tokenValidTill = Date()
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
package app.cashadvisor.authorization.domain.models

import java.util.Date

sealed class AccountInformation {
object NotAuthorized : AccountInformation()
data class Authorized(
// todo пока модель черновая, перепроверить что тут должно быть
val email: String? = null,
val token: String? = null,
val refreshToken: String? = null,
val isEmailVerified: Boolean = false,
val tokenValidTill: Date? = null
val accessToken: String,
val refreshToken: String
) : AccountInformation()
}
18 changes: 18 additions & 0 deletions app/src/main/java/app/cashadvisor/common/di/DataModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package app.cashadvisor.common.di

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import javax.inject.Singleton


@Module
@InstallIn(SingletonComponent::class)
class DataModule {
@Provides
@Singleton
fun provideJson(): Json = Json { ignoreUnknownKeys = true }

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@ package app.cashadvisor.main.presentation.ui

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Gravity
import android.view.View
import android.widget.Button
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
import app.cashadvisor.R
import app.cashadvisor.authorization.data.dto.CredentialsDto
import app.cashadvisor.authorization.domain.api.CredentialsStorage
import app.cashadvisor.databinding.ActivityMainBinding
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding
private lateinit var navController: NavController

@Inject
lateinit var storage: CredentialsStorage

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -71,6 +78,47 @@ class MainActivity : AppCompatActivity() {
}
binding.root.addView(logAndCrashButton)

val storageButton = Button(this).apply {
text = "Storage"
setOnClickListener {
storage.saveCredentials(CredentialsDto(
accessToken = "test",
refreshToken = "test",
))
Timber.tag("MainActivity").d("Credentials saved")
val credentials = storage.getCredentials()
Timber.tag("MainActivity").d("Credentials: $credentials")
}
}
var layoutParams =
ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT)
layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID
layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID

with(binding.root as ConstraintLayout) {
addView(storageButton, layoutParams)
}

val deleteStorageButton = Button(this).apply {
text = "Delete Strorage"
setOnClickListener {
storage.clearCredentials()
Timber.tag("MainActivity").d("Credentials removed")
val credentials = storage.getCredentials()
Timber.tag("MainActivity").d("Credentials: $credentials")
}
}
layoutParams =
ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT)
layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID

with(binding.root as ConstraintLayout) {
addView(deleteStorageButton, layoutParams)
}


// Creates a button that mimics a crash when pressed
/*val crashButton = TextView(this)
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/res/xml/backup_rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
<include domain="sharedpref" path="."/>-->
<exclude domain="sharedpref" path="secret_shared_prefs.xml"/>
</full-backup-content>
2 changes: 2 additions & 0 deletions app/src/main/res/xml/data_extraction_rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
<include .../>
<exclude .../>
-->
<exclude domain="sharedpref" path="secret_shared_prefs.xml"/>
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
<exclude domain="sharedpref" path="secret_shared_prefs.xml"/>
</data-extraction-rules>
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ plugins {
alias(libs.plugins.gms.googleServices) apply false
alias(libs.plugins.firebase.appdistribution) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.parcelize) apply false
}
13 changes: 13 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ dependencyResolutionManagement {
version("firebase.appdistribution", "4.0.1")
version("android-library", "8.1.1")
version("gms-play-services-auth", "20.7.0")
version("crypto", "1.0.0-alpha02")

plugin(
"android-application",
Expand Down Expand Up @@ -98,6 +99,11 @@ dependencyResolutionManagement {
"com.android.library"
).versionRef("android-library")

plugin(
"kotlin-parcelize",
"org.jetbrains.kotlin.plugin.parcelize"
).versionRef("kotlin")

// Core
library(
"androidx-core-ktx",
Expand Down Expand Up @@ -297,6 +303,13 @@ dependencyResolutionManagement {
"com.google.android.gms",
"play-services-auth"
).versionRef("gms-play-services-auth")

library(
"crypto",
"androidx.security",
"security-crypto"
).versionRef("crypto")

}
}
}
Expand Down
Loading