Skip to content

Commit

Permalink
Merge branch 'dev' into bugfix/sendToQaFix
Browse files Browse the repository at this point in the history
  • Loading branch information
JudjinGM committed Feb 15, 2024
2 parents 2d9eb22 + 7bd7acf commit 2b82156
Show file tree
Hide file tree
Showing 16 changed files with 323 additions and 32 deletions.
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 @@ -189,6 +190,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,75 @@
package app.cashadvisor.authorization.data.impl

import kotlinx.serialization.json.Json
import android.content.SharedPreferences
import androidx.core.content.edit
import app.cashadvisor.authorization.data.dto.CredentialsDto
import app.cashadvisor.authorization.domain.api.CredentialsRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString

class CredentialsRepositoryImpl(
private val storage: SharedPreferences,
private val key: String,
private val json: Json
) : CredentialsRepository, CoroutineScope by CoroutineScope(
Dispatchers.IO +
SupervisorJob()
) {

private val _credentialsUpdateFlow = MutableStateFlow<CredentialsDto?>(null)

init {
storage.registerOnSharedPreferenceChangeListener(this)
launch {
_credentialsUpdateFlow.value = getCredentials()
}
}

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

override suspend fun getCredentials(): CredentialsDto? {
return withContext(Dispatchers.IO) {
storage.getString(key, null)?.let {
json.decodeFromString<CredentialsDto>(it)
}
}
}

override fun getCredentialsFlow(): Flow<CredentialsDto?> = _credentialsUpdateFlow

override suspend fun hasCredentials(): Boolean {
return withContext(Dispatchers.IO) {
storage.contains(key)
}
}

override suspend fun clearCredentials() {
withContext(Dispatchers.IO) {
storage.edit { remove(key) }
_credentialsUpdateFlow.emit(null)
}
}

override fun onSharedPreferenceChanged(
p0: SharedPreferences?,
p1: String?
) {
if (p1 == key) {
launch {
_credentialsUpdateFlow.value = getCredentials()
}
}
}
}
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.CredentialsRepositoryImpl
import app.cashadvisor.authorization.domain.api.CredentialsRepository
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
): CredentialsRepository = CredentialsRepositoryImpl(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.CredentialsRepository
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(
storage: CredentialsRepository,
): AccountInformationInteractor = AccountInformationInteractorImpl(storage)

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import app.cashadvisor.authorization.domain.models.AccountInformation
import kotlinx.coroutines.flow.Flow

interface AccountInformationInteractor {
fun getAccountInformation(): Flow<AccountInformation>
suspend fun getAccountInformation(): Flow<AccountInformation>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package app.cashadvisor.authorization.domain.api

import android.content.SharedPreferences
import app.cashadvisor.authorization.data.dto.CredentialsDto
import kotlinx.coroutines.flow.Flow

interface CredentialsRepository : SharedPreferences.OnSharedPreferenceChangeListener {

suspend fun saveCredentials(credentials: CredentialsDto)
suspend fun getCredentials(): CredentialsDto?
fun getCredentialsFlow(): Flow<CredentialsDto?>
suspend fun clearCredentials()
suspend fun hasCredentials(): Boolean

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

import app.cashadvisor.authorization.domain.api.AccountInformationInteractor
import app.cashadvisor.authorization.domain.api.CredentialsRepository
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
import kotlinx.coroutines.flow.map

class AccountInformationInteractorImpl(
private val storage: CredentialsRepository
) : AccountInformationInteractor {
override suspend fun getAccountInformation(): Flow<AccountInformation> =
storage.getCredentialsFlow().map { credentials ->
credentials?.let {
AccountInformation.Authorized(
accessToken = it.accessToken,
refreshToken = it.refreshToken
)
} ?: AccountInformation.NotAuthorized
}

class AccountInformationInteractorImpl @Inject constructor() : AccountInformationInteractor {
override fun getAccountInformation(): Flow<AccountInformation> = flow {
// todo обновить класс с учетом реальных данных
emit(MOCK_ACCOUNT_INFORMATION)
}

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

}
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 @@ -4,21 +4,30 @@ import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.activity.viewModels
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.lifecycleScope
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.CredentialsRepository
import app.cashadvisor.databinding.ActivityMainBinding
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

val viewModel: MainViewModel by viewModels()
private lateinit var binding: ActivityMainBinding
private lateinit var navController: NavController

@Inject
lateinit var storage: CredentialsRepository

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -72,6 +81,45 @@ class MainActivity : AppCompatActivity() {
binding.root.addView(logAndCrashButton)


this.lifecycleScope.launch {
viewModel.state.collect {
Timber.tag("MainActivity").d("AccountInformation: $it")
}
}

val storageButton = Button(this).apply {
text = "Storage"
setOnClickListener {
viewModel.saveCredentials( "test", "test")
}
}
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 = "Logout"
setOnClickListener {
viewModel.logout()
}
}
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)
crashButton.text = "Test Crash"
Expand Down
Loading

0 comments on commit 2b82156

Please sign in to comment.