diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 8199eb17..9c508e65 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -15,7 +15,7 @@ - + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5f96a01c..b6d553fd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -44,6 +44,7 @@ android { buildFeatures { viewBinding = true compose = true + buildConfig = true } composeOptions { kotlinCompilerExtensionVersion = "1.5.4" @@ -145,10 +146,15 @@ dependencies { // Compose-lint lintChecks("com.slack.lint.compose:compose-lint-checks:1.3.1") + // Timber + implementation ("com.jakewharton.timber:timber:5.0.1") + //kotlinx.collections.immutable implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7") // Circular Progress bar implementation ("com.mikhaellopez:circularprogressbar:3.1.0") + // Coil + implementation("io.coil-kt:coil-compose:2.6.0") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5fa82ccf..174a6e3f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,7 +32,7 @@ android:theme="@style/Theme.UnCrack"/> @@ -43,7 +43,17 @@ + + + + + + diff --git a/app/src/main/java/com/geekymusketeers/uncrack/components/AccountOption.kt b/app/src/main/java/com/geekymusketeers/uncrack/components/AccountOption.kt new file mode 100644 index 00000000..698fa90f --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/components/AccountOption.kt @@ -0,0 +1,61 @@ +package com.geekymusketeers.uncrack.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.geekymusketeers.uncrack.presentation.account.AccountItems +import com.geekymusketeers.uncrack.ui.theme.OnSurfaceLight +import com.geekymusketeers.uncrack.ui.theme.normal16 + +@Composable +fun AccountOption( + accountItem: AccountItems, + modifier: Modifier = Modifier, + onClick: (AccountItems) -> Unit +) { + + val context = LocalContext.current + + Row( + modifier = modifier + .fillMaxWidth() + .clickable { onClick(accountItem) } + .padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.Start), + verticalAlignment = Alignment.CenterVertically, + ) { + + AsyncImage( + model = ImageRequest.Builder(context) + .data(accountItem.icon) + .size(coil.size.Size.ORIGINAL) + .build(), contentDescription = null, + modifier = Modifier.size(24.dp) + ) + + Text( + text = accountItem.itemsName, + style = normal16.copy(color = OnSurfaceLight) + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun SettingsItemPreview() { + AccountOption(AccountItems.BIOMETRIC) { + // Do something + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/components/OnboardingComponent.kt b/app/src/main/java/com/geekymusketeers/uncrack/components/OnboardingComponent.kt index 64e104f0..42ed4537 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/components/OnboardingComponent.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/components/OnboardingComponent.kt @@ -1,7 +1,7 @@ package com.geekymusketeers.uncrack.components import androidx.compose.runtime.Composable -import com.geekymusketeers.uncrack.presentation.introScreens.model.OnBoardingItem +import com.geekymusketeers.uncrack.presentation.intro.model.OnBoardingItem import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* import androidx.compose.material3.Text diff --git a/app/src/main/java/com/geekymusketeers/uncrack/components/ProfileContainer.kt b/app/src/main/java/com/geekymusketeers/uncrack/components/ProfileContainer.kt new file mode 100644 index 00000000..202590aa --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/components/ProfileContainer.kt @@ -0,0 +1,48 @@ +package com.geekymusketeers.uncrack.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Add +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.geekymusketeers.uncrack.R + +@Composable +fun ProfileContainer( + modifier: Modifier = Modifier, + onEditButtonClick: () -> Unit +) { + Box(contentAlignment = Alignment.BottomEnd) { + Image( + painter = painterResource(id = R.drawable.no_user_profile_picture), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = modifier + .size(120.dp) + .clip(CircleShape) + ) + + Button( + modifier = Modifier.size(25.dp), + shape = CircleShape, + colors = ButtonDefaults.buttonColors(containerColor = Color.Blue), + contentPadding = PaddingValues(0.dp), + onClick = { onEditButtonClick() } + ) { + Icon(imageVector = Icons.Outlined.Add, contentDescription = null, tint = Color.White) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/components/ThemeDialog.kt b/app/src/main/java/com/geekymusketeers/uncrack/components/ThemeDialog.kt new file mode 100644 index 00000000..ecb132c9 --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/components/ThemeDialog.kt @@ -0,0 +1,116 @@ +package com.geekymusketeers.uncrack.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.BasicAlertDialog +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import com.geekymusketeers.uncrack.R +import com.geekymusketeers.uncrack.ui.theme.light16 +import com.geekymusketeers.uncrack.ui.theme.medium14 +import com.geekymusketeers.uncrack.ui.theme.normal24 + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ThemeDialog( + onDismissRequest: () -> Unit +) { + + BasicAlertDialog( + onDismissRequest = { onDismissRequest() } + ) { + Column( + Modifier + .background(MaterialTheme.colorScheme.background, RoundedCornerShape(28.dp)) + .padding(vertical = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.themes), + contentDescription = null, + tint = MaterialTheme.colorScheme.onPrimaryContainer + ) + + Text( + text = "Change Theme", + style = normal24.copy(MaterialTheme.colorScheme.onSurface) + ) + + Column { + + + Text( + text = "Dark Mode", + modifier = Modifier + .clickable { + // TODO: Impl the logic + onDismissRequest() + } + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 32.dp), + style = light16.copy(color = MaterialTheme.colorScheme.onSurfaceVariant), + ) + + Text( + text = "Light Mode", + modifier = Modifier + .clickable { + // TODO: Impl the logic + onDismissRequest() + } + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 32.dp), + style = light16.copy(color = MaterialTheme.colorScheme.onSurfaceVariant), + ) + + Text( + text = "Dynamic Theme", + modifier = Modifier + .clickable { + // TODO: Impl the logic + onDismissRequest() + } + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 32.dp), + style = light16.copy(color = MaterialTheme.colorScheme.onSurfaceVariant), + ) + } + + TextButton( + onClick = { + onDismissRequest() + }, + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer + ), + modifier = Modifier + .align(Alignment.End) + .padding(end = 24.dp) + ) { + Text( + text = "Dismiss", + style = medium14 + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/components/UCTopAppBar.kt b/app/src/main/java/com/geekymusketeers/uncrack/components/UCTopAppBar.kt new file mode 100644 index 00000000..c800365c --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/components/UCTopAppBar.kt @@ -0,0 +1,59 @@ +package com.geekymusketeers.uncrack.components + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.sp +import com.geekymusketeers.uncrack.ui.theme.BackgroundLight +import com.geekymusketeers.uncrack.ui.theme.DMSansFontFamily +import com.geekymusketeers.uncrack.ui.theme.OnSurfaceLight + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun UCTopAppBar( + modifier: Modifier = Modifier, + title: String = "", + navigationIcon: ImageVector = Icons.AutoMirrored.Filled.ArrowBack, + shouldShowBackButton: Boolean = true, + fontSize: TextUnit = 22.sp, + colors: TopAppBarColors = TopAppBarDefaults.topAppBarColors(BackgroundLight), + onBackPress: () -> Unit = {}, +) { + TopAppBar( + modifier = modifier, + title = { + Text( + text = title, + fontFamily = DMSansFontFamily, + fontSize = fontSize, + color = OnSurfaceLight, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + }, + navigationIcon = { + if (shouldShowBackButton) { + IconButton(onClick = { + onBackPress() + }) { + Icon( + imageVector = navigationIcon, + contentDescription = null + ) + } + } + }, + colors = colors + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/data/room/AccountDao.kt b/app/src/main/java/com/geekymusketeers/uncrack/data/dao/AccountDao.kt similarity index 51% rename from app/src/main/java/com/geekymusketeers/uncrack/data/room/AccountDao.kt rename to app/src/main/java/com/geekymusketeers/uncrack/data/dao/AccountDao.kt index fc5da1e9..71fb45f1 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/data/room/AccountDao.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/data/dao/AccountDao.kt @@ -1,4 +1,4 @@ -package com.geekymusketeers.uncrack.data.room +package com.geekymusketeers.uncrack.data.dao import androidx.lifecycle.LiveData import androidx.room.Dao @@ -16,27 +16,12 @@ interface AccountDao { @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun addAccount(account: Account) - @Update + @Update(onConflict = OnConflictStrategy.REPLACE) suspend fun editAccount(account: Account) @Delete suspend fun deleteAccount(account: Account) @Query("SELECT * FROM account_table ORDER BY id ASC") - fun readAllAccount(): LiveData> - - // Favourite - @Query("SELECT * FROM account_table WHERE isFavourite = 1") - fun getFavourite(): LiveData> - - @Query("SELECT isFavourite from account_table where id = :id") - suspend fun updateFavourite(id: Int): Boolean - - @Query("UPDATE account_table SET isFavourite = 1 WHERE id = :id") - suspend fun isSelected(id: Int) - - @Query("UPDATE account_table SET isFavourite = 0 WHERE id = :id") - suspend fun isNotSelected(id: Int) - - + fun readAllAccount(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/data/room/CardDao.kt b/app/src/main/java/com/geekymusketeers/uncrack/data/dao/CardDao.kt similarity index 80% rename from app/src/main/java/com/geekymusketeers/uncrack/data/room/CardDao.kt rename to app/src/main/java/com/geekymusketeers/uncrack/data/dao/CardDao.kt index ff9303a3..a4ed4661 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/data/room/CardDao.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/data/dao/CardDao.kt @@ -1,8 +1,7 @@ -package com.geekymusketeers.uncrack.data.room +package com.geekymusketeers.uncrack.data.dao import androidx.lifecycle.LiveData import androidx.room.* -import com.geekymusketeers.uncrack.domain.model.Account import com.geekymusketeers.uncrack.domain.model.Card @Dao diff --git a/app/src/main/java/com/geekymusketeers/uncrack/data/room/KeyDao.kt b/app/src/main/java/com/geekymusketeers/uncrack/data/dao/KeyDao.kt similarity index 72% rename from app/src/main/java/com/geekymusketeers/uncrack/data/room/KeyDao.kt rename to app/src/main/java/com/geekymusketeers/uncrack/data/dao/KeyDao.kt index e6ed68b0..84b07dc0 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/data/room/KeyDao.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/data/dao/KeyDao.kt @@ -1,11 +1,11 @@ -package com.geekymusketeers.uncrack.data.room +package com.geekymusketeers.uncrack.data.dao -import androidx.lifecycle.LiveData import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import com.geekymusketeers.uncrack.domain.model.Key +import kotlinx.coroutines.flow.Flow @Dao interface KeyDao { @@ -14,5 +14,5 @@ interface KeyDao { suspend fun setMasterKey(key: Key) @Query("SELECT * FROM master_key") - fun getMasterKey(): LiveData> + fun getMasterKey(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/data/datastore/DataStoreUtil.kt b/app/src/main/java/com/geekymusketeers/uncrack/data/datastore/DataStoreUtil.kt index e1af95d2..99e72b6b 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/data/datastore/DataStoreUtil.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/data/datastore/DataStoreUtil.kt @@ -14,5 +14,6 @@ class DataStoreUtil @Inject constructor(context: Context) { companion object { private val Context.dataStore: DataStore by preferencesDataStore("settings") val IS_DARK_MODE_KEY = booleanPreferencesKey("dark_mode") + val IS_SS_BLOCK_KEY = booleanPreferencesKey("ss_block") } } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/data/db/AccountDatabase.kt b/app/src/main/java/com/geekymusketeers/uncrack/data/db/AccountDatabase.kt index f97b05f9..a4f87146 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/data/db/AccountDatabase.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/data/db/AccountDatabase.kt @@ -1,41 +1,15 @@ package com.geekymusketeers.uncrack.data.db -import android.content.Context import androidx.room.Database -import androidx.room.Room import androidx.room.RoomDatabase -import com.geekymusketeers.uncrack.data.room.AccountDao +import com.geekymusketeers.uncrack.data.dao.AccountDao import com.geekymusketeers.uncrack.domain.model.Account @Database( entities = [Account::class], - version = 4 + version = 4, + exportSchema = false ) -abstract class AccountDatabase : RoomDatabase(){ - - abstract fun accountDao() : AccountDao - - companion object{ - @Volatile - var INSTANCE : AccountDatabase? = null - - @Synchronized - fun getDatabase(context:Context) : AccountDatabase { - - val tempInstance = INSTANCE - - if (tempInstance!=null){ - return tempInstance - } - synchronized(this) { - val instance = Room.databaseBuilder( - context.applicationContext, - AccountDatabase::class.java, - "account_database" - ).fallbackToDestructiveMigration().build() - INSTANCE = instance - return instance - } - } - } +abstract class AccountDatabase : RoomDatabase() { + abstract val accountDao : AccountDao } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/data/db/CardDatabase.kt b/app/src/main/java/com/geekymusketeers/uncrack/data/db/CardDatabase.kt index d16ad07d..d1c2f344 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/data/db/CardDatabase.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/data/db/CardDatabase.kt @@ -4,7 +4,7 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase -import com.geekymusketeers.uncrack.data.room.CardDao +import com.geekymusketeers.uncrack.data.dao.CardDao import com.geekymusketeers.uncrack.domain.model.Card @Database( diff --git a/app/src/main/java/com/geekymusketeers/uncrack/data/db/KeyDatabase.kt b/app/src/main/java/com/geekymusketeers/uncrack/data/db/KeyDatabase.kt index 3beb97c0..30dd59d4 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/data/db/KeyDatabase.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/data/db/KeyDatabase.kt @@ -4,36 +4,14 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase -import com.geekymusketeers.uncrack.data.room.KeyDao +import com.geekymusketeers.uncrack.data.dao.KeyDao import com.geekymusketeers.uncrack.domain.model.Key -@Database(entities = [Key::class], - version = 3) +@Database( + entities = [Key::class], + version = 3, + exportSchema = false +) abstract class KeyDatabase : RoomDatabase() { - - abstract fun keyDao(): KeyDao - - companion object { - @Volatile - var INSTANCE : KeyDatabase? = null - - @Synchronized - fun getDatabase(context: Context) : KeyDatabase { - - val tempInstance = INSTANCE - - if (tempInstance!=null) { - return tempInstance - } - synchronized(this) { - val instance = Room.databaseBuilder ( - context.applicationContext, - KeyDatabase::class.java, - "masterKey_database" - ).fallbackToDestructiveMigration().build() - INSTANCE = instance - return instance - } - } - } + abstract val keyDao: KeyDao } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/di/AppModule.kt b/app/src/main/java/com/geekymusketeers/uncrack/di/AppModule.kt index 29acb1ab..12acbb96 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/di/AppModule.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/di/AppModule.kt @@ -1,12 +1,22 @@ package com.geekymusketeers.uncrack.di +import android.app.Application import android.content.Context +import androidx.room.Room import com.geekymusketeers.uncrack.data.datastore.DataStoreUtil +import com.geekymusketeers.uncrack.data.db.AccountDatabase +import com.geekymusketeers.uncrack.data.db.KeyDatabase +import com.geekymusketeers.uncrack.data.db.KeyDatabase_Impl +import com.geekymusketeers.uncrack.domain.repository.AccountRepository +import com.geekymusketeers.uncrack.domain.repository.AccountRepositoryImpl +import com.geekymusketeers.uncrack.domain.repository.KeyRepository +import com.geekymusketeers.uncrack.domain.repository.KeyRepositoryImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) @@ -15,4 +25,46 @@ object AppModule { @Provides fun provideDataStoreUtil(@ApplicationContext context: Context): DataStoreUtil = DataStoreUtil(context) + + @Provides + @Singleton + fun provideAccountDatabase(app: Application) : AccountDatabase { + + return Room.databaseBuilder( + app, + AccountDatabase::class.java, + "account_database" + ) + .fallbackToDestructiveMigration() + .build() + } + + @Provides + @Singleton + fun provideAccountRepository( + db: AccountDatabase + ): AccountRepository { + return AccountRepositoryImpl(accountDao = db.accountDao) + } + + @Provides + @Singleton + fun provideKeyDatabase(app: Application): KeyDatabase { + + return Room.databaseBuilder( + app, + KeyDatabase::class.java, + "masterKey_database" + ) + .fallbackToDestructiveMigration() + .build() + } + + @Provides + @Singleton + fun provideKeyRepository( + keyDB: KeyDatabase + ): KeyRepository { + return KeyRepositoryImpl(keyDao = keyDB.keyDao) + } } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/AccountRepository.kt b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/AccountRepository.kt index bcffff3b..99adb532 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/AccountRepository.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/AccountRepository.kt @@ -1,35 +1,15 @@ package com.geekymusketeers.uncrack.domain.repository -import androidx.lifecycle.LiveData -import com.geekymusketeers.uncrack.data.room.AccountDao import com.geekymusketeers.uncrack.domain.model.Account +import kotlinx.coroutines.flow.Flow +interface AccountRepository { -class AccountRepository(private val accountDao: AccountDao) { - val readAllAccount: LiveData> = accountDao.readAllAccount() + suspend fun addAccount(account: Account) - suspend fun addAccount(account: Account){ - accountDao.addAccount(account) - } + suspend fun editAccount(account: Account) - suspend fun editAccount(account: Account){ - accountDao.editAccount(account) - } + suspend fun deleteAccount(account: Account) - suspend fun deleteAccount(account: Account){ - accountDao.deleteAccount(account) - } - - fun getFavouriteAccount(){ - accountDao.getFavourite() - } - - suspend fun updateFavourite(account: Account){ - - val id = account.id - val test:Boolean = accountDao.updateFavourite(id) - if (test) accountDao.isNotSelected(id) - else accountDao.isSelected(id) - - } + fun getAllAccounts(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/AccountRepositoryImpl.kt b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/AccountRepositoryImpl.kt new file mode 100644 index 00000000..fc8e4167 --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/AccountRepositoryImpl.kt @@ -0,0 +1,28 @@ +package com.geekymusketeers.uncrack.domain.repository + +import androidx.lifecycle.LiveData +import com.geekymusketeers.uncrack.data.dao.AccountDao +import com.geekymusketeers.uncrack.domain.model.Account +import kotlinx.coroutines.flow.Flow + + +class AccountRepositoryImpl( + private val accountDao: AccountDao +): AccountRepository { + override suspend fun addAccount(account: Account){ + accountDao.addAccount(account) + } + + override suspend fun editAccount(account: Account){ + accountDao.editAccount(account) + } + + override suspend fun deleteAccount(account: Account){ + accountDao.deleteAccount(account) + } + + override fun getAllAccounts(): Flow> { + return accountDao.readAllAccount() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/CardRepository.kt b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/CardRepository.kt index 3daed7fb..3b4d3b53 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/CardRepository.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/CardRepository.kt @@ -2,7 +2,7 @@ package com.geekymusketeers.uncrack.domain.repository import androidx.lifecycle.LiveData import com.geekymusketeers.uncrack.domain.model.Card -import com.geekymusketeers.uncrack.data.room.CardDao +import com.geekymusketeers.uncrack.data.dao.CardDao class CardRepository(private val cardDao: CardDao) { val readAllCard: LiveData> = cardDao.readAllCard() diff --git a/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/KeyRepository.kt b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/KeyRepository.kt index 7ebb29d6..09cefb50 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/KeyRepository.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/KeyRepository.kt @@ -1,14 +1,11 @@ package com.geekymusketeers.uncrack.domain.repository -import androidx.lifecycle.LiveData import com.geekymusketeers.uncrack.domain.model.Key -import com.geekymusketeers.uncrack.data.room.KeyDao +import kotlinx.coroutines.flow.Flow -class KeyRepository(private val keyDao: KeyDao) { +interface KeyRepository { - fun getMasterKey(): LiveData> = keyDao.getMasterKey() + suspend fun setMasterKey(key: Key) - suspend fun setMasterKey(key: Key) { - keyDao.setMasterKey(key) - } + fun getMasterKey(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/KeyRepositoryImpl.kt b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/KeyRepositoryImpl.kt new file mode 100644 index 00000000..4fda0084 --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/domain/repository/KeyRepositoryImpl.kt @@ -0,0 +1,18 @@ +package com.geekymusketeers.uncrack.domain.repository + +import com.geekymusketeers.uncrack.domain.model.Key +import com.geekymusketeers.uncrack.data.dao.KeyDao +import kotlinx.coroutines.flow.Flow + +class KeyRepositoryImpl( + private val keyDao: KeyDao +): KeyRepository { + + override fun getMasterKey(): Flow> { + return keyDao.getMasterKey() + } + + override suspend fun setMasterKey(key: Key) { + keyDao.setMasterKey(key) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/navigation/Navigation.kt b/app/src/main/java/com/geekymusketeers/uncrack/navigation/Navigation.kt index 3ba9fddc..9bbabb89 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/navigation/Navigation.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/navigation/Navigation.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.font.FontWeight +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavBackStackEntry import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController @@ -21,10 +22,17 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.geekymusketeers.uncrack.R -import com.geekymusketeers.uncrack.presentation.homeScreen.HomeScreen -import com.geekymusketeers.uncrack.presentation.passwordScreen.PasswordScreen -import com.geekymusketeers.uncrack.presentation.profileScreen.ProfileScreen -import com.geekymusketeers.uncrack.presentation.shieldScreen.ShieldScreen +import com.geekymusketeers.uncrack.presentation.account.AccountScreen +import com.geekymusketeers.uncrack.presentation.account.PasswordGenerator +import com.geekymusketeers.uncrack.presentation.account.PasswordGeneratorViewModel +import com.geekymusketeers.uncrack.presentation.home.HomeScreen +import com.geekymusketeers.uncrack.presentation.masterKey.ConfirmMasterKeyScreen +import com.geekymusketeers.uncrack.presentation.masterKey.CreateMasterKeyScreen +import com.geekymusketeers.uncrack.presentation.masterKey.UpdateMasterKey +import com.geekymusketeers.uncrack.presentation.password.PasswordScreen +import com.geekymusketeers.uncrack.presentation.profile.ProfileScreen +import com.geekymusketeers.uncrack.presentation.shield.ShieldScreen +import com.geekymusketeers.uncrack.sharedViewModel.ThemeViewModel import com.geekymusketeers.uncrack.ui.theme.BackgroundLight import com.geekymusketeers.uncrack.ui.theme.DMSansFontFamily import com.geekymusketeers.uncrack.ui.theme.FadeIn @@ -33,18 +41,29 @@ import com.geekymusketeers.uncrack.ui.theme.OnPrimaryContainerLight import com.geekymusketeers.uncrack.ui.theme.OnSurfaceVariantLight import com.geekymusketeers.uncrack.ui.theme.PrimaryDark import com.geekymusketeers.uncrack.util.BackPressHandler +import com.geekymusketeers.uncrack.viewModel.KeyViewModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable -fun Navigation(modifier: Modifier = Modifier) { +fun Navigation( + modifier: Modifier = Modifier, + masterKeyViewModel: KeyViewModel = hiltViewModel(), + passwordGeneratorViewModel: PasswordGeneratorViewModel = hiltViewModel(), + themeViewModel: ThemeViewModel = hiltViewModel() +) { val navController = rememberNavController() val backStackEntry = navController.currentBackStackEntryAsState() val screensWithoutNavigationBar = persistentListOf( - "add_password_screen" + "add_password_screen", + "profile_screen", + "update_master_key_screen", + "create_new_master_key_screen", + "confirm_master_key_screen", + "password_generator_screen" ) BackPressHandler() @@ -83,6 +102,41 @@ fun Navigation(modifier: Modifier = Modifier) { composable(route = "profile_screen") { ProfileScreen() } + + composable(route = "account_screen") { + AccountScreen( + navController, + themeViewModel + ) + } + + composable(route = "update_master_key_screen") { + UpdateMasterKey( + navController, + masterKeyViewModel + ) + } + + composable(route = "create_new_master_key_screen") { + CreateMasterKeyScreen( + navController, + masterKeyViewModel + ) + } + + composable(route = "confirm_master_key_screen") { + ConfirmMasterKeyScreen( + navController, + masterKeyViewModel + ) + } + + composable(route = "password_generator_screen") { + PasswordGenerator( + navController, + passwordGeneratorViewModel + ) + } } } } @@ -118,7 +172,7 @@ fun ShowBottomNavigation( ), BottomNavItem( name = "Profile", - route = "profile_screen", + route = "account_screen", icon = ImageVector.vectorResource(id = R.drawable.person_icon) ) ) diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/AccountItems.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/AccountItems.kt new file mode 100644 index 00000000..99e562ab --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/AccountItems.kt @@ -0,0 +1,27 @@ +package com.geekymusketeers.uncrack.presentation.account + +import com.geekymusketeers.uncrack.R + +// TODO: Need to change icons for some of the items +enum class AccountItems(val itemsName: String, val icon: Int) { + // Security + CHANGE_MASTER_KEY("Change master password", R.drawable.lock), + BIOMETRIC("Unlock with biometric", R.drawable.fingerprint), + BLOCK_SS("Block screenshots", R.drawable.block_ss), + + //BACKUP + EXPORT_IMPORT("Export/Import", R.drawable.import_export), + BACKUP_RESTORE("Backup/Restore", R.drawable.backup_restore), + + // General + THEME("Theme", R.drawable.themes), + PASSWORD_GENERATOR("Password Generator", R.drawable.generate_password_icon), + INVITE_FRIENDS("Invite Friends", R.drawable.share_app), + RATE_UNCRACK("Rate UnCrack", R.drawable.rating), + SEND_FEEDBACK("Send Feedback", R.drawable.feedback), + PRIVACY_POLICY("Privacy Policy", R.drawable.shield), + + // Danger Zone + LOG_OUT("Log out", R.drawable.logout), + DELETE_ACCOUNT("Delete Account", R.drawable.delete_icon) +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/AccountScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/AccountScreen.kt new file mode 100644 index 00000000..afed5d52 --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/AccountScreen.kt @@ -0,0 +1,295 @@ +package com.geekymusketeers.uncrack.presentation.account + +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.geekymusketeers.uncrack.BuildConfig +import com.geekymusketeers.uncrack.R +import com.geekymusketeers.uncrack.components.AccountOption +import com.geekymusketeers.uncrack.components.ProfileContainer +import com.geekymusketeers.uncrack.components.ThemeDialog +import com.geekymusketeers.uncrack.sharedViewModel.ThemeViewModel +import com.geekymusketeers.uncrack.ui.theme.OnPrimaryContainerLight +import com.geekymusketeers.uncrack.ui.theme.OnSurfaceVariantLight +import com.geekymusketeers.uncrack.ui.theme.SurfaceTintLight +import com.geekymusketeers.uncrack.ui.theme.bold18 +import com.geekymusketeers.uncrack.ui.theme.medium14 +import com.geekymusketeers.uncrack.ui.theme.medium32 +import com.geekymusketeers.uncrack.ui.theme.normal14 +import com.geekymusketeers.uncrack.ui.theme.normal16 +import com.geekymusketeers.uncrack.util.Constants.INVITE +import com.geekymusketeers.uncrack.util.Util + +@Composable +fun AccountScreen( + navController: NavHostController, + themeViewModel: ThemeViewModel, + modifier: Modifier = Modifier +) { + + val context = LocalContext.current + var openThemeDialog by remember { mutableStateOf(false) } + var openLogoutDialog by remember { mutableStateOf(false) } + + when { + openThemeDialog -> { + ThemeDialog(onDismissRequest = { openThemeDialog = false }) + } + + openLogoutDialog -> { + AlertDialog( + onDismissRequest = { openLogoutDialog = false }, + icon = { + Icon( + painter = painterResource(id = R.drawable.logout), + contentDescription = null, + tint = MaterialTheme.colorScheme.onPrimaryContainer + ) + }, + title = { + Text( + text = stringResource(R.string.logout), + style = normal16.copy(color = OnPrimaryContainerLight) + ) + }, + text = { + Text( + text = stringResource(R.string.are_you_sure_you_want_to_logout), + style = normal16.copy(color = OnSurfaceVariantLight) + ) + }, + confirmButton = { + TextButton( + onClick = { + // TODO: IMPL the logout logic + openLogoutDialog = false + }, + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer + ) + ) { + Text( + "Logout", + style = medium14 + ) + } + }, + dismissButton = { + TextButton( + onClick = { + openLogoutDialog = false + }, + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = MaterialTheme.colorScheme.onPrimaryContainer + ) + ) { + Text( + text = stringResource(R.string.cancel), + style = medium14 + ) + } + }, + containerColor = MaterialTheme.colorScheme.background + ) + } + } + + Column( + modifier = modifier + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 52.dp), + verticalAlignment = Alignment.CenterVertically + ) { + + Column( + modifier = Modifier + .fillMaxWidth() + .clickable { }, + horizontalAlignment = Alignment.CenterHorizontally + ) { + ProfileContainer { + navController.navigate("profile_screen") + } + + Spacer(modifier = Modifier.height(15.dp)) + + Text( + text = "John Doe", + style = medium32.copy(color = OnPrimaryContainerLight) + ) + } + } + + Spacer(modifier = Modifier.height(40.dp)) + + LazyColumn( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + item { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 18.dp, top = 18.dp), + text = stringResource(id = R.string.security), + style = bold18.copy(color = OnPrimaryContainerLight) + ) + } + + items(AccountItems.entries.subList(0,3)) { + AccountOption(it) { onClick -> + when(onClick) { + AccountItems.BIOMETRIC -> {} + AccountItems.CHANGE_MASTER_KEY -> { + navController.navigate("update_master_key_screen") + } + AccountItems.BLOCK_SS -> { + themeViewModel.blockScreenShort() + } + else -> {} + } + } + } + + item { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 18.dp, top = 18.dp), + text = stringResource(R.string.backup), + style = bold18.copy(color = OnPrimaryContainerLight) + ) + } + + items(AccountItems.entries.subList(3,5)) { + AccountOption(it) { onClick -> + when(onClick) { + AccountItems.EXPORT_IMPORT -> {} + AccountItems.BACKUP_RESTORE -> {} + else -> {} + } + } + } + + item { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 18.dp, top = 18.dp), + text = stringResource(R.string.general), + style = bold18.copy(color = OnPrimaryContainerLight) + ) + } + + items(AccountItems.entries.subList(5,11)) { + AccountOption(it) { onClick -> + when(onClick) { + AccountItems.THEME -> { + openThemeDialog = true + } + + AccountItems.PASSWORD_GENERATOR -> { + navController.navigate("password_generator_screen") + } + + AccountItems.INVITE_FRIENDS -> { + val sendIntent = Intent(Intent.ACTION_SEND).apply { + putExtra(Intent.EXTRA_TEXT, INVITE) + type = "text/plain" + } + val shareIntent = Intent.createChooser(sendIntent, null) + context.startActivity(shareIntent) + } + + AccountItems.RATE_UNCRACK -> { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(Util.PLAYSTORE_URL) + context.startActivity(intent) + } + + AccountItems.SEND_FEEDBACK -> { + val openURL = Intent(Intent.ACTION_VIEW) + openURL.data = Uri.parse(context.resources.getString(R.string.mailTo)) + context.startActivity(openURL) + } + + AccountItems.PRIVACY_POLICY -> {} + else -> {} + } + } + } + + item { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 18.dp, top = 18.dp), + text = stringResource(R.string.danger_zone), + style = bold18.copy(color = OnPrimaryContainerLight) + ) + } + + items(AccountItems.entries.subList(11, AccountItems.entries.size)) { + AccountOption(it) { onClick -> + when(onClick) { + AccountItems.LOG_OUT -> { + openLogoutDialog = true + } + AccountItems.DELETE_ACCOUNT -> {} + else -> {} + } + } + } + + item { + Text( + modifier = Modifier.padding(top = 20.dp, bottom = 10.dp), + text = "Version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})", + style = normal14.copy(color = SurfaceTintLight) + ) + Text( + modifier = Modifier.padding(bottom = 150.dp), + text = stringResource(R.string.by_aritra_das), + style = normal14.copy(color = SurfaceTintLight) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/PasswordGenerator.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/PasswordGenerator.kt new file mode 100644 index 00000000..a1f2059a --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/PasswordGenerator.kt @@ -0,0 +1,219 @@ +package com.geekymusketeers.uncrack.presentation.account + +import android.widget.Toast +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.Scaffold +import androidx.compose.material3.Slider +import androidx.compose.material3.SliderDefaults +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.geekymusketeers.uncrack.R +import com.geekymusketeers.uncrack.components.UCButton +import com.geekymusketeers.uncrack.components.UCTopAppBar +import com.geekymusketeers.uncrack.ui.theme.BackgroundLight +import com.geekymusketeers.uncrack.ui.theme.OnPrimaryContainerLight +import com.geekymusketeers.uncrack.ui.theme.PrimaryLight +import com.geekymusketeers.uncrack.ui.theme.SurfaceLight +import com.geekymusketeers.uncrack.ui.theme.medium24 +import com.geekymusketeers.uncrack.ui.theme.medium30 +import com.geekymusketeers.uncrack.ui.theme.normal16 +import com.geekymusketeers.uncrack.util.Constants.sliderStepRange +import com.geekymusketeers.uncrack.util.Constants.sliderSteps +import timber.log.Timber + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PasswordGenerator( + navController: NavHostController, + passwordGeneratorViewModel: PasswordGeneratorViewModel, + modifier: Modifier = Modifier +) { + val context = LocalContext.current + val clipboardManager = LocalClipboardManager.current + val password by passwordGeneratorViewModel.password.observeAsState("") + val passwordLength by passwordGeneratorViewModel.passwordLength.observeAsState(0.0f) + val includeUppercase by passwordGeneratorViewModel.includeUppercase.observeAsState(true) + val includeLowercase by passwordGeneratorViewModel.includeLowercase.observeAsState(true) + val includeNumbers by passwordGeneratorViewModel.includeNumbers.observeAsState(true) + val includeSpecialChars by passwordGeneratorViewModel.includeSpecialChars.observeAsState(true) + + Scaffold( + topBar = { + UCTopAppBar( + modifier = modifier.fillMaxWidth(), + title = "Password Generator", + onBackPress = { navController.popBackStack() } + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(16.dp) + .background(BackgroundLight), + verticalArrangement = Arrangement.Center + ) { + + Text( + modifier = Modifier.fillMaxWidth(), + text = buildAnnotatedString { + password.forEach { + val textColor = when { + it.isDigit() -> Color.Blue + it.isLetterOrDigit().not() -> Color.Magenta + else -> OnPrimaryContainerLight + } + withStyle(style = SpanStyle(color = textColor)) { + append(it.toString()) + } + } + }, + style = medium30, + textAlign = TextAlign.Center, + letterSpacing = TextUnit(1F, TextUnitType.Sp) + ) + + Spacer(modifier = Modifier.height(90.dp)) + + Text( + text = stringResource(R.string.password_generated_length, passwordLength.toInt()), + style = medium24.copy(OnPrimaryContainerLight) + ) + + Spacer(modifier = Modifier.height(10.dp)) + + Slider( + modifier = Modifier.fillMaxWidth(), + value = passwordLength, + onValueChange = { newPasswordLength -> + passwordGeneratorViewModel.updatePasswordLength(newPasswordLength) + }, + steps = sliderSteps, + valueRange = sliderStepRange, + colors = SliderDefaults.colors( + thumbColor = OnPrimaryContainerLight, + activeTrackColor = PrimaryLight, + inactiveTrackColor = SurfaceLight, + activeTickColor = Color.Transparent, + inactiveTickColor = Color.Transparent + ) + ) + + Spacer(modifier = Modifier.height(30.dp)) + + Text( + text = stringResource(R.string.include_following), + style = medium24.copy(OnPrimaryContainerLight) + ) + + Spacer(modifier = Modifier.height(15.dp)) + + SwitchItem( + label = stringResource(R.string.numbers), + checked = includeNumbers + ) { + passwordGeneratorViewModel.updateIncludeNumbers(it) + } + SwitchItem( + label = stringResource(R.string.uppercase_letters), + checked = includeUppercase + ) { + passwordGeneratorViewModel.updateIncludeUppercase(it) + } + SwitchItem( + label = stringResource(R.string.lowercase_letters), + checked = includeLowercase + ) { + passwordGeneratorViewModel.updateIncludeLowercase(it) + } + SwitchItem( + label = stringResource(R.string.special_symbols), + checked = includeSpecialChars + ) { + passwordGeneratorViewModel.updateIncludeSpecialChars(it) + } + + Spacer(modifier = Modifier.weight(1f)) + + UCButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.generate), + onClick = { + passwordGeneratorViewModel.generatePassword() + Timber.d("Password ${passwordGeneratorViewModel.generatePassword()}") + }, + leadingIcon = painterResource(id = R.drawable.generate_password) + ) + + Spacer(modifier = Modifier.height(10.dp)) + + UCButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.copy), + onClick = { + password.let { passwordToCopy -> +// clipboardManager.setText() //TODO: Need to check + } + Toast.makeText(context, "Copied", Toast.LENGTH_SHORT).show() + }, + leadingIcon = painterResource(id = R.drawable.copy_password) + ) + } + } +} + + +@Composable +fun SwitchItem( + label: String, + checked: Boolean, + modifier: Modifier = Modifier, + onCheckedChange: (Boolean) -> Unit +) { + Row( + modifier = modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = label, + style = normal16.copy(OnPrimaryContainerLight) + ) + + Spacer(modifier = Modifier.weight(1f)) + + Switch( + checked = checked, + onCheckedChange = onCheckedChange, + modifier = Modifier.padding(start = 8.dp) + ) + } +} diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/PasswordGeneratorViewModel.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/PasswordGeneratorViewModel.kt new file mode 100644 index 00000000..a3a1b14a --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/account/PasswordGeneratorViewModel.kt @@ -0,0 +1,85 @@ +package com.geekymusketeers.uncrack.presentation.account + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.geekymusketeers.uncrack.util.Constants.DEFAULT_PASSWORD_LENGTH +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class PasswordGeneratorViewModel @Inject constructor() : ViewModel() { + + private val _password = MutableLiveData() + val password: LiveData = _password + + private val _passwordLength = MutableLiveData(DEFAULT_PASSWORD_LENGTH) + val passwordLength: LiveData = _passwordLength + + private val _includeUppercase = MutableLiveData(true) + val includeUppercase: LiveData = _includeUppercase + + private val _includeLowercase = MutableLiveData(true) + val includeLowercase: LiveData = _includeLowercase + + private val _includeNumbers = MutableLiveData(true) + val includeNumbers: LiveData = _includeNumbers + + private val _includeSpecialChars = MutableLiveData(true) + val includeSpecialChars: LiveData = _includeSpecialChars + + fun generatePassword() { + _password.value = generatePassword( + length = _passwordLength.value ?: DEFAULT_PASSWORD_LENGTH, + includeUppercase = _includeUppercase.value ?: true, + includeLowercase = _includeLowercase.value ?: true, + includeNumbers = _includeNumbers.value ?: true, + includeSpecialChars = _includeSpecialChars.value ?: true + ) + } + + fun updatePasswordLength(newLength: Float) { + _passwordLength.value = newLength + } + + fun updateIncludeUppercase(include: Boolean) { + _includeUppercase.value = include + } + + fun updateIncludeLowercase(include: Boolean) { + _includeLowercase.value = include + } + + fun updateIncludeNumbers(include: Boolean) { + _includeNumbers.value = include + } + + fun updateIncludeSpecialChars(include: Boolean) { + _includeSpecialChars.value = include + } + + private fun generatePassword( + length: Float, + includeUppercase: Boolean, + includeLowercase: Boolean, + includeNumbers: Boolean, + includeSpecialChars: Boolean + ): String { + val charPool = buildString { + if (includeUppercase) { + append("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + } + if (includeLowercase) { + append("abcdefghijklmnopqrstuvwxyz") + } + if (includeNumbers) { + append("0123456789") + } + if (includeSpecialChars) { + append("!@#$%^&*()_+") + } + } + + return (1..length.toInt()).map { charPool.random() }.joinToString("") + } +} diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/authScreens/login/LoginScreens.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/auth/login/LoginScreens.kt similarity index 83% rename from app/src/main/java/com/geekymusketeers/uncrack/presentation/authScreens/login/LoginScreens.kt rename to app/src/main/java/com/geekymusketeers/uncrack/presentation/auth/login/LoginScreens.kt index 4ccf63e4..f4487cf3 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/authScreens/login/LoginScreens.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/auth/login/LoginScreens.kt @@ -1,10 +1,13 @@ -package com.geekymusketeers.uncrack.presentation.authScreens.login +package com.geekymusketeers.uncrack.presentation.auth.login +import android.app.Activity +import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -13,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold @@ -25,6 +29,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -32,6 +37,7 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.geekymusketeers.uncrack.MainActivity import com.geekymusketeers.uncrack.R import com.geekymusketeers.uncrack.components.UCButton import com.geekymusketeers.uncrack.components.UCTextField @@ -40,7 +46,10 @@ import com.geekymusketeers.uncrack.ui.theme.OnPrimaryContainerLight import com.geekymusketeers.uncrack.ui.theme.PrimaryLight import com.geekymusketeers.uncrack.ui.theme.UnCrackTheme import com.geekymusketeers.uncrack.ui.theme.medium16 +import com.geekymusketeers.uncrack.util.UtilsKt.findActivity +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class LoginScreens : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -57,15 +66,16 @@ class LoginScreens : ComponentActivity() { setContent { UnCrackTheme { - LoginContent() + LoginContent(this@LoginScreens) } } } } @Composable -fun LoginContent(modifier: Modifier = Modifier) { +fun LoginContent(activity: Activity, modifier: Modifier = Modifier) { + val context = LocalContext.current val email by remember { mutableStateOf("") } @@ -145,18 +155,25 @@ fun LoginContent(modifier: Modifier = Modifier) { text = stringResource(R.string.login), onClick = { // TODO: Perform req operation and navigate to Home Screen + context.findActivity()?.apply { + startActivity(Intent(activity, MainActivity::class.java)) + } }, - enabled = false +// enabled = false ) - Spacer(modifier = Modifier.height(10.dp)) + Spacer(modifier = Modifier.height(15.dp)) - Row { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { Text( text = stringResource(R.string.don_t_have_an_account), style = medium16.copy(color = OnPrimaryContainerLight) ) + Spacer(modifier = Modifier.width(8.dp)) Text( text = stringResource(R.string.create), style = medium16.copy(color = PrimaryLight) diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/authScreens/signup/SignupScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/auth/signup/SignupScreen.kt similarity index 97% rename from app/src/main/java/com/geekymusketeers/uncrack/presentation/authScreens/signup/SignupScreen.kt rename to app/src/main/java/com/geekymusketeers/uncrack/presentation/auth/signup/SignupScreen.kt index e8635dde..628caaa3 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/authScreens/signup/SignupScreen.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/auth/signup/SignupScreen.kt @@ -1,4 +1,4 @@ -package com.geekymusketeers.uncrack.presentation.authScreens.signup +package com.geekymusketeers.uncrack.presentation.auth.signup import android.os.Bundle import androidx.activity.ComponentActivity @@ -40,7 +40,9 @@ import com.geekymusketeers.uncrack.ui.theme.OnPrimaryContainerLight import com.geekymusketeers.uncrack.ui.theme.PrimaryLight import com.geekymusketeers.uncrack.ui.theme.UnCrackTheme import com.geekymusketeers.uncrack.ui.theme.medium16 +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class SignupScreen : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/homeScreen/HomeScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/home/HomeScreen.kt similarity index 56% rename from app/src/main/java/com/geekymusketeers/uncrack/presentation/homeScreen/HomeScreen.kt rename to app/src/main/java/com/geekymusketeers/uncrack/presentation/home/HomeScreen.kt index 412df2d4..0c1c950a 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/homeScreen/HomeScreen.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/home/HomeScreen.kt @@ -1,4 +1,4 @@ -package com.geekymusketeers.uncrack.presentation.homeScreen +package com.geekymusketeers.uncrack.presentation.home import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/OnboardingScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/OnboardingScreen.kt similarity index 90% rename from app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/OnboardingScreen.kt rename to app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/OnboardingScreen.kt index 16de9529..c9fc168f 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/OnboardingScreen.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/OnboardingScreen.kt @@ -1,5 +1,6 @@ -package com.geekymusketeers.uncrack.presentation.introScreens +package com.geekymusketeers.uncrack.presentation.intro +import android.app.Activity import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity @@ -40,18 +41,21 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.geekymusketeers.uncrack.R import com.geekymusketeers.uncrack.components.OnboardingComponent -import com.geekymusketeers.uncrack.presentation.authScreens.login.LoginScreens -import com.geekymusketeers.uncrack.presentation.introScreens.model.OnBoardingItem +import com.geekymusketeers.uncrack.presentation.auth.login.LoginScreens +import com.geekymusketeers.uncrack.presentation.intro.model.OnBoardingItem import com.geekymusketeers.uncrack.ui.theme.DMSansFontFamily import com.geekymusketeers.uncrack.ui.theme.OnSurfaceLight import com.geekymusketeers.uncrack.ui.theme.OnSurfaceVariantLight import com.geekymusketeers.uncrack.ui.theme.PrimaryDark +import com.geekymusketeers.uncrack.ui.theme.UnCrackTheme import com.geekymusketeers.uncrack.ui.theme.navigationTopBarHeight import com.geekymusketeers.uncrack.ui.theme.normal16 import com.geekymusketeers.uncrack.util.UtilsKt.findActivity import com.geekymusketeers.uncrack.util.onClick +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch +@AndroidEntryPoint class OnboardingScreen : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -67,14 +71,16 @@ class OnboardingScreen : ComponentActivity() { super.onCreate(savedInstanceState) setContent { - OnboardingContent() + UnCrackTheme { + OnboardingContent(this@OnboardingScreen) + } } } } @OptIn(ExperimentalFoundationApi::class) @Composable -fun OnboardingContent(modifier: Modifier = Modifier) { +fun OnboardingContent(activity: Activity, modifier: Modifier = Modifier) { val scope = rememberCoroutineScope() val context = LocalContext.current val pages = OnBoardingItem.onboardingScreenItems(context) @@ -110,7 +116,7 @@ fun OnboardingContent(modifier: Modifier = Modifier) { context .findActivity() ?.apply { - val loginIntent = Intent(this, LoginScreens::class.java) + val loginIntent = Intent(activity, LoginScreens::class.java) startActivity(loginIntent) finish() } @@ -148,7 +154,7 @@ fun OnboardingContent(modifier: Modifier = Modifier) { context .findActivity() ?.apply { - val loginIntent = Intent(this, LoginScreens::class.java) + val loginIntent = Intent(activity, LoginScreens::class.java) startActivity(loginIntent) finish() } diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/SplashScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/SplashScreen.kt similarity index 92% rename from app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/SplashScreen.kt rename to app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/SplashScreen.kt index c797adb6..61acfcb1 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/SplashScreen.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/SplashScreen.kt @@ -1,4 +1,4 @@ -package com.geekymusketeers.uncrack.presentation.introScreens +package com.geekymusketeers.uncrack.presentation.intro import android.annotation.SuppressLint import android.app.Activity @@ -32,8 +32,11 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.geekymusketeers.uncrack.R import com.geekymusketeers.uncrack.ui.theme.BackgroundLight +import com.geekymusketeers.uncrack.ui.theme.UnCrackTheme +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay +@AndroidEntryPoint @SuppressLint("CustomSplashScreen") class SplashScreen : ComponentActivity() { @@ -50,7 +53,9 @@ class SplashScreen : ComponentActivity() { super.onCreate(savedInstanceState) setContent { - SplashContent(this@SplashScreen) + UnCrackTheme { + SplashContent(this@SplashScreen) + } } } } diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/model/OnBoardingItem.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/model/OnBoardingItem.kt similarity index 91% rename from app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/model/OnBoardingItem.kt rename to app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/model/OnBoardingItem.kt index 3ff7e003..1758bcc5 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/introScreens/model/OnBoardingItem.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/intro/model/OnBoardingItem.kt @@ -1,4 +1,4 @@ -package com.geekymusketeers.uncrack.presentation.introScreens.model +package com.geekymusketeers.uncrack.presentation.intro.model import android.content.Context import com.geekymusketeers.uncrack.R diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/ConfirmMasterKeyScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/ConfirmMasterKeyScreen.kt new file mode 100644 index 00000000..ea937cd9 --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/ConfirmMasterKeyScreen.kt @@ -0,0 +1,107 @@ +package com.geekymusketeers.uncrack.presentation.masterKey + +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.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +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.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.geekymusketeers.uncrack.R +import com.geekymusketeers.uncrack.components.UCButton +import com.geekymusketeers.uncrack.components.UCTextField +import com.geekymusketeers.uncrack.ui.theme.bold30 +import com.geekymusketeers.uncrack.ui.theme.normal20 +import com.geekymusketeers.uncrack.viewModel.KeyViewModel + +@Composable +fun ConfirmMasterKeyScreen( + navController: NavHostController, + masterKeyViewModel: KeyViewModel, + modifier: Modifier = Modifier +) { + + + var confirmMasterKey by remember { mutableStateOf("") } + + var passwordVisibility by remember { mutableStateOf(false) } + + Scaffold( + modifier.fillMaxSize() + ) { paddingValues -> + + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(16.dp) + ) { + Text( + text = "Welcome Back", + style = bold30.copy(color = Color.Black) + ) + + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = "Kindly provide your Master Key", + style = normal20.copy(color = Color.Black) + ) + + Spacer(modifier = Modifier.height(50.dp)) + + UCTextField( + modifier = Modifier + .fillMaxWidth(), + headerText = "Master Key", + hintText = "****************", + value = confirmMasterKey, + onValueChange = { confirmMasterKey = it }, + visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + + val image = if (passwordVisibility) + painterResource(id = R.drawable.visibility_on) + else painterResource(id = R.drawable.visibility_off) + + IconButton(onClick = + { passwordVisibility = passwordVisibility.not() } + ) { + Icon( + modifier = Modifier.size(24.dp), + painter = image, + contentDescription = null + ) + } + } + ) + + Spacer(modifier = Modifier.weight(1f)) + + UCButton( + modifier = Modifier + .fillMaxWidth(), + text = "Continue", + onClick = { + // TODO: Perform req operation + }, + enabled = false + ) + } + } +} diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/CreateMasterKeyScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/CreateMasterKeyScreen.kt new file mode 100644 index 00000000..d8fcf401 --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/CreateMasterKeyScreen.kt @@ -0,0 +1,138 @@ +package com.geekymusketeers.uncrack.presentation.masterKey + +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.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.geekymusketeers.uncrack.R +import com.geekymusketeers.uncrack.components.UCButton +import com.geekymusketeers.uncrack.components.UCTextField +import com.geekymusketeers.uncrack.domain.model.Key +import com.geekymusketeers.uncrack.ui.theme.bold30 +import com.geekymusketeers.uncrack.viewModel.KeyViewModel + +@Composable +fun CreateMasterKeyScreen( + navController: NavHostController, + masterKeyViewModel: KeyViewModel, + modifier: Modifier = Modifier +) { + + val masterKeyObserver by masterKeyViewModel.masterKeyLiveData.observeAsState("") + val confirmMasterKeyObserver by masterKeyViewModel.confirmMasterKeyLiveData.observeAsState("") + val enableButtonObserver by masterKeyViewModel.enableButtonLiveData.observeAsState(false) + var passwordVisibility by remember { mutableStateOf(false) } + + + Scaffold( + modifier.fillMaxSize() + ) { paddingValues -> + + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(16.dp) + ) { + Text( + text = "Create Master Key", + style = bold30.copy(color = Color.Black) + ) + + Spacer(modifier = Modifier.height(50.dp)) + + UCTextField( + modifier = Modifier + .fillMaxWidth(), + headerText = stringResource(id = R.string.master_key), + hintText = stringResource(id = R.string.password_hint), + value = masterKeyObserver, + onValueChange = { + masterKeyViewModel.setMasterKey(it) + }, + visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + + val image = if (passwordVisibility) + painterResource(id = R.drawable.visibility_on) + else painterResource(id = R.drawable.visibility_off) + + IconButton(onClick = + { passwordVisibility = passwordVisibility.not() } + ) { + Icon( + modifier = Modifier.size(24.dp), + painter = image, + contentDescription = null + ) + } + } + ) + + Spacer(modifier = Modifier.height(30.dp)) + + UCTextField( + modifier = Modifier + .fillMaxWidth(), + headerText = stringResource(id = R.string.confirm_master_key), + hintText = stringResource(id = R.string.password_hint), + value = confirmMasterKeyObserver, + onValueChange = { + masterKeyViewModel.setConfirmMasterKey(it) + }, + visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + + val image = if (passwordVisibility) + painterResource(id = R.drawable.visibility_on) + else painterResource(id = R.drawable.visibility_off) + + IconButton(onClick = + { passwordVisibility = passwordVisibility.not() } + ) { + Icon( + modifier = Modifier.size(24.dp), + painter = image, + contentDescription = null + ) + } + } + ) + + Spacer(modifier = Modifier.weight(1f)) + + UCButton( + modifier = Modifier + .fillMaxWidth(), + text = stringResource(R.string.save), + onClick = { + val key = Key(0,masterKeyObserver) + masterKeyViewModel.saveMasterKey(key) + navController.navigate("home_screen") + }, + enabled = enableButtonObserver.not() + ) + } + } +} diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/UpdateMasterKey.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/UpdateMasterKey.kt new file mode 100644 index 00000000..6768a589 --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/masterKey/UpdateMasterKey.kt @@ -0,0 +1,177 @@ +package com.geekymusketeers.uncrack.presentation.masterKey + +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.foundation.layout.size +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.geekymusketeers.uncrack.R +import com.geekymusketeers.uncrack.components.UCButton +import com.geekymusketeers.uncrack.components.UCTextField +import com.geekymusketeers.uncrack.components.UCTopAppBar +import com.geekymusketeers.uncrack.ui.theme.BackgroundLight +import com.geekymusketeers.uncrack.ui.theme.bold30 +import com.geekymusketeers.uncrack.viewModel.KeyViewModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun UpdateMasterKey( + navController: NavHostController, + masterKeyViewModel: KeyViewModel, + modifier: Modifier = Modifier +) { + + + var oldMasterKey by remember { + mutableStateOf("") + } + + var newMasterKey by remember { + mutableStateOf("") + } + + var confirmMasterKey by remember { + mutableStateOf("") + } + + var passwordVisibility by remember { + mutableStateOf(false) + } + + + Scaffold( + modifier.fillMaxSize(), + topBar = { + UCTopAppBar( + modifier = Modifier.fillMaxWidth(), + colors = TopAppBarDefaults.topAppBarColors(BackgroundLight), + onBackPress = { navController.popBackStack() } + ) + } + ) { paddingValues -> + + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(16.dp) + ) { + Text( + text = stringResource(R.string.update_master_key), + style = bold30.copy(color = Color.Black) + ) + + Spacer(modifier = Modifier.height(50.dp)) + + UCTextField( + modifier = Modifier + .fillMaxWidth(), + headerText = stringResource(R.string.old_master_key), + hintText = stringResource(id = R.string.password_hint), + value = oldMasterKey, + onValueChange = { oldMasterKey = it }, + visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + val image = if (passwordVisibility) + painterResource(id = R.drawable.visibility_on) + else painterResource(id = R.drawable.visibility_off) + + IconButton(onClick = { passwordVisibility = passwordVisibility.not() } + ) { + Icon( + modifier = Modifier.size(24.dp), + painter = image, + contentDescription = null + ) + } + } + ) + + Spacer(modifier = Modifier.height(30.dp)) + + UCTextField( + modifier = Modifier + .fillMaxWidth(), + headerText = stringResource(R.string.new_master_key), + hintText = stringResource(id = R.string.password_hint), + value = newMasterKey, + onValueChange = { newMasterKey = it }, + visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + val image = if (passwordVisibility) + painterResource(id = R.drawable.visibility_on) + else painterResource(id = R.drawable.visibility_off) + + IconButton(onClick = { passwordVisibility = passwordVisibility.not() } + ) { + Icon( + modifier = Modifier.size(24.dp), + painter = image, + contentDescription = null + ) + } + } + ) + + Spacer(modifier = Modifier.height(30.dp)) + + UCTextField( + modifier = Modifier + .fillMaxWidth(), + headerText = stringResource(id = R.string.confirm_master_key), + hintText = stringResource(id = R.string.password_hint), + value = confirmMasterKey, + onValueChange = { confirmMasterKey = it }, + visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + val image = if (passwordVisibility) + painterResource(id = R.drawable.visibility_on) + else painterResource(id = R.drawable.visibility_off) + + IconButton(onClick = { passwordVisibility = passwordVisibility.not() } + ) { + Icon( + modifier = Modifier.size(24.dp), + painter = image, + contentDescription = null + ) + } + } + ) + + Spacer(modifier = Modifier.weight(1f)) + + UCButton( + modifier = Modifier + .fillMaxWidth(), + text = stringResource(id = R.string.update), + onClick = { + // TODO: Perform req operation + }, + enabled = false + ) + } + } +} diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/passwordScreen/PasswordScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/password/PasswordScreen.kt similarity index 56% rename from app/src/main/java/com/geekymusketeers/uncrack/presentation/passwordScreen/PasswordScreen.kt rename to app/src/main/java/com/geekymusketeers/uncrack/presentation/password/PasswordScreen.kt index d019565c..9ed224b2 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/passwordScreen/PasswordScreen.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/password/PasswordScreen.kt @@ -1,4 +1,4 @@ -package com.geekymusketeers.uncrack.presentation.passwordScreen +package com.geekymusketeers.uncrack.presentation.password import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/profile/ProfileScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/profile/ProfileScreen.kt new file mode 100644 index 00000000..f5b84e6b --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/profile/ProfileScreen.kt @@ -0,0 +1,101 @@ +package com.geekymusketeers.uncrack.presentation.profile + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +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.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.geekymusketeers.uncrack.R +import com.geekymusketeers.uncrack.components.ProfileContainer +import com.geekymusketeers.uncrack.components.UCButton +import com.geekymusketeers.uncrack.components.UCTextField +import com.geekymusketeers.uncrack.components.UCTopAppBar + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ProfileScreen( + modifier: Modifier = Modifier +) { + + Scaffold( + topBar = { + UCTopAppBar( + title = "Profile", + colors = TopAppBarDefaults.topAppBarColors( + Color.White + ) + ) + } + ) { paddingValues -> + + Box( + modifier = modifier + .fillMaxSize() + .background(Color.White) + .padding(paddingValues) + .padding(bottom = 20.dp) + .imePadding() + ) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + ProfileContainer { + + } + + Spacer(modifier = Modifier.height(20.dp)) + + UCTextField( + modifier = Modifier + .fillMaxWidth() + .padding(20.dp, 0.dp), + headerText = "Name", + hintText = stringResource(id = R.string.name_hint), + value = "", + onValueChange = {} + ) + + Spacer(modifier = Modifier.height(20.dp)) + + UCTextField( + modifier = Modifier + .fillMaxWidth() + .padding(20.dp, 0.dp), + headerText = "Email", + hintText = stringResource(id = R.string.email_hint), + value = "", + onValueChange = {} + ) + + Spacer(modifier = Modifier.weight(1f)) + + UCButton( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp,0.dp), + text = stringResource(id = R.string.save), + onClick = {} + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/profileScreen/ProfileScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/profileScreen/ProfileScreen.kt deleted file mode 100644 index 166ae514..00000000 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/profileScreen/ProfileScreen.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.geekymusketeers.uncrack.presentation.profileScreen - -import androidx.compose.runtime.Composable - -@Composable -fun ProfileScreen() { - -} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/presentation/shieldScreen/ShieldScreen.kt b/app/src/main/java/com/geekymusketeers/uncrack/presentation/shield/ShieldScreen.kt similarity index 56% rename from app/src/main/java/com/geekymusketeers/uncrack/presentation/shieldScreen/ShieldScreen.kt rename to app/src/main/java/com/geekymusketeers/uncrack/presentation/shield/ShieldScreen.kt index 603549c8..93c5edcf 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/presentation/shieldScreen/ShieldScreen.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/presentation/shield/ShieldScreen.kt @@ -1,4 +1,4 @@ -package com.geekymusketeers.uncrack.presentation.shieldScreen +package com.geekymusketeers.uncrack.presentation.shield import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/geekymusketeers/uncrack/sharedViewModel/ThemeViewModel.kt b/app/src/main/java/com/geekymusketeers/uncrack/sharedViewModel/ThemeViewModel.kt index b6f2001a..54d5ea10 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/sharedViewModel/ThemeViewModel.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/sharedViewModel/ThemeViewModel.kt @@ -5,6 +5,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.geekymusketeers.uncrack.data.datastore.DataStoreUtil import com.geekymusketeers.uncrack.data.datastore.DataStoreUtil.Companion.IS_DARK_MODE_KEY +import com.geekymusketeers.uncrack.data.datastore.DataStoreUtil.Companion.IS_SS_BLOCK_KEY +import com.geekymusketeers.uncrack.util.runIO +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -12,13 +15,14 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import javax.inject.Inject -data class ThemeState(val isDarkMode: Boolean) +data class ThemeState(val isDarkMode: Boolean, val isSsBlocked: Boolean) +@HiltViewModel class ThemeViewModel @Inject constructor( dataStoreUtil: DataStoreUtil ): ViewModel() { - private val _themeState = MutableStateFlow(ThemeState(false)) + private val _themeState = MutableStateFlow(ThemeState(isDarkMode = false, isSsBlocked = false)) val themeState: StateFlow = _themeState private val dataStore = dataStoreUtil.dataStore @@ -26,7 +30,10 @@ class ThemeViewModel @Inject constructor( init { viewModelScope.launch(Dispatchers.IO) { dataStore.data.map { preferences -> - ThemeState(preferences[IS_DARK_MODE_KEY] ?: false) + ThemeState( + isDarkMode = preferences[IS_DARK_MODE_KEY] ?: false, + isSsBlocked = preferences[IS_SS_BLOCK_KEY] ?: false + ) }.collect { _themeState.value = it } @@ -40,4 +47,10 @@ class ThemeViewModel @Inject constructor( } } } + + fun blockScreenShort() = runIO { + dataStore.edit { preferences -> + preferences[IS_SS_BLOCK_KEY] = !(preferences[IS_SS_BLOCK_KEY] ?: false) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/ui/auth/master_key/LockMasterKeyFragment.kt b/app/src/main/java/com/geekymusketeers/uncrack/ui/auth/master_key/LockMasterKeyFragment.kt index 53550074..a506f1a8 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/ui/auth/master_key/LockMasterKeyFragment.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/ui/auth/master_key/LockMasterKeyFragment.kt @@ -62,35 +62,35 @@ class LockMasterKeyFragment : Fragment() { private fun clickHandlers() { buttonLayout.setOnClickListener { - checkKeyViewModel.getMasterKey().observe(viewLifecycleOwner) { key -> - val inputMasterKey = binding.inputMasterKey.text.toString() - showProgress() - val encryption = Encryption.getDefault("Key", "Salt", ByteArray(16)) - val correctMasterKey = encryption.decryptOrNull(key[0].password) - if (inputMasterKey.isEmpty() || inputMasterKey.isBlank()){ - binding.apply { - inputMasterKeyHelperTV.text = getString(R.string.master_key_cannot_be_blank) - inputMasterKeyHelperTV.visibility = View.VISIBLE - } - stopProgress() - return@observe - } else { - if (inputMasterKey == correctMasterKey) { - lifecycleScope.launch(Dispatchers.Main) { - delay(1000L) - goToMainActivity() - } - } - else { - binding.apply { - inputMasterKeyHelperTV.text = getString(R.string.incorrect_password) - inputMasterKeyHelperTV.visibility = View.VISIBLE - } - stopProgress() - return@observe - } - } - } +// checkKeyViewModel.getMasterKey().observe(viewLifecycleOwner) { key -> +// val inputMasterKey = binding.inputMasterKey.text.toString() +// showProgress() +// val encryption = Encryption.getDefault("Key", "Salt", ByteArray(16)) +// val correctMasterKey = encryption.decryptOrNull(key[0].password) +// if (inputMasterKey.isEmpty() || inputMasterKey.isBlank()){ +// binding.apply { +// inputMasterKeyHelperTV.text = getString(R.string.master_key_cannot_be_blank) +// inputMasterKeyHelperTV.visibility = View.VISIBLE +// } +// stopProgress() +// return@observe +// } else { +// if (inputMasterKey == correctMasterKey) { +// lifecycleScope.launch(Dispatchers.Main) { +// delay(1000L) +// goToMainActivity() +// } +// } +// else { +// binding.apply { +// inputMasterKeyHelperTV.text = getString(R.string.incorrect_password) +// inputMasterKeyHelperTV.visibility = View.VISIBLE +// } +// stopProgress() +// return@observe +// } +// } +// } } binding.passwordToggle.setOnClickListener { diff --git a/app/src/main/java/com/geekymusketeers/uncrack/ui/auth/master_key/UpdateMasterKeyFragment.kt b/app/src/main/java/com/geekymusketeers/uncrack/ui/auth/master_key/UpdateMasterKeyFragment.kt index 48191810..96364395 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/ui/auth/master_key/UpdateMasterKeyFragment.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/ui/auth/master_key/UpdateMasterKeyFragment.kt @@ -67,60 +67,60 @@ class UpdateMasterKeyFragment : Fragment() { } private fun clickHandlers() { - buttonLayout.setOnClickListener { - updateKeyViewModel.getMasterKey().observe(viewLifecycleOwner) {masterKey -> - - val oldMasterKey = binding.layoutOldMasterKey.text.toString() - val updatedMasterKey = binding.layoutMasterKey.text.toString() - val encryption = Encryption.getDefault("Key", "Salt", ByteArray(16)) - val correctOldMasterKey = encryption.decryptOrNull(masterKey[0].password) - showProgress() - - if (oldMasterKey.isEmpty() || oldMasterKey.isBlank()) { - binding.apply { - oldMasterKeyHelperTV.text = getString(R.string.old_master_key_cannot_be_blank) - oldMasterKeyHelperTV.visibility = View.VISIBLE - } - stopProgress() - } - else { - if (oldMasterKey!=correctOldMasterKey){ - binding.apply { - oldMasterKeyHelperTV.text = getString(R.string.old_master_key_is_incorrect) - oldMasterKeyHelperTV.visibility = View.VISIBLE - } - stopProgress() - } - else { - if (updatedMasterKey.isBlank() || updatedMasterKey.isEmpty()) { - binding.apply { - updatedMasterKeyHelperTV.text = getString(R.string.master_key_cannot_be_blank) - updatedMasterKeyHelperTV.visibility = View.VISIBLE - } - stopProgress() - } - else { - if (updatedMasterKey.length <= 5) { - binding.apply { - updatedMasterKeyHelperTV.text = getString(R.string.at_least_6_letter_long) - updatedMasterKeyHelperTV.visibility = View.VISIBLE - } - stopProgress() - } - else { - lifecycleScope.launch(Dispatchers.Main) { - delay(800L) - updateKey() - Toast.makeText(requireContext(),"Successfully Updated the Master Key",Toast.LENGTH_SHORT).show() - goToSecurityFragment() - } - } - } - } - } - - } - } +// buttonLayout.setOnClickListener { +// updateKeyViewModel.getMasterKey().observe(viewLifecycleOwner) {masterKey -> +// +// val oldMasterKey = binding.layoutOldMasterKey.text.toString() +// val updatedMasterKey = binding.layoutMasterKey.text.toString() +// val encryption = Encryption.getDefault("Key", "Salt", ByteArray(16)) +// val correctOldMasterKey = encryption.decryptOrNull(masterKey[0].password) +// showProgress() +// +// if (oldMasterKey.isEmpty() || oldMasterKey.isBlank()) { +// binding.apply { +// oldMasterKeyHelperTV.text = getString(R.string.old_master_key_cannot_be_blank) +// oldMasterKeyHelperTV.visibility = View.VISIBLE +// } +// stopProgress() +// } +// else { +// if (oldMasterKey!=correctOldMasterKey){ +// binding.apply { +// oldMasterKeyHelperTV.text = getString(R.string.old_master_key_is_incorrect) +// oldMasterKeyHelperTV.visibility = View.VISIBLE +// } +// stopProgress() +// } +// else { +// if (updatedMasterKey.isBlank() || updatedMasterKey.isEmpty()) { +// binding.apply { +// updatedMasterKeyHelperTV.text = getString(R.string.master_key_cannot_be_blank) +// updatedMasterKeyHelperTV.visibility = View.VISIBLE +// } +// stopProgress() +// } +// else { +// if (updatedMasterKey.length <= 5) { +// binding.apply { +// updatedMasterKeyHelperTV.text = getString(R.string.at_least_6_letter_long) +// updatedMasterKeyHelperTV.visibility = View.VISIBLE +// } +// stopProgress() +// } +// else { +// lifecycleScope.launch(Dispatchers.Main) { +// delay(800L) +// updateKey() +// Toast.makeText(requireContext(),"Successfully Updated the Master Key",Toast.LENGTH_SHORT).show() +// goToSecurityFragment() +// } +// } +// } +// } +// } +// +// } +// } } private fun goToSecurityFragment() { diff --git a/app/src/main/java/com/geekymusketeers/uncrack/ui/fragments/account/HomeFragment.kt b/app/src/main/java/com/geekymusketeers/uncrack/ui/fragments/account/HomeFragment.kt index db8939d8..9977ceec 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/ui/fragments/account/HomeFragment.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/ui/fragments/account/HomeFragment.kt @@ -267,21 +267,21 @@ class HomeFragment : Fragment() { - viewModel = ViewModelProvider(this)[AccountViewModel::class.java] - viewModel.readAllData.observe(viewLifecycleOwner) { account -> - - - accountList.clear() - accountList.addAll(account) - - accountAdapter.setData(account) - if (account.isEmpty()) { - binding.emptyList.visibility = View.VISIBLE - } else { - binding.emptyList.visibility = View.GONE - } - - } +// viewModel = ViewModelProvider(this)[AccountViewModel::class.java] +// viewModel.getAccounts().observe(viewLifecycleOwner) { account -> +// +// +// accountList.clear() +// accountList.addAll(account) +// +// accountAdapter.setData(account) +// if (account.isEmpty()) { +// binding.emptyList.visibility = View.VISIBLE +// } else { +// binding.emptyList.visibility = View.GONE +// } +// +// } // Moving to AddFragment binding.fab.setOnClickListener { @@ -387,16 +387,16 @@ class HomeFragment : Fragment() { } - override fun onResume() { - super.onResume() - viewModel.readAllData.observe(viewLifecycleOwner) { account -> - accountAdapter.setData(account) - if (account.isEmpty()) { - binding.emptyList.visibility = View.VISIBLE - } else { - binding.emptyList.visibility = View.GONE - } - - } - } +// override fun onResume() { +// super.onResume() +// viewModel.readAllData.observe(viewLifecycleOwner) { account -> +// accountAdapter.setData(account) +// if (account.isEmpty()) { +// binding.emptyList.visibility = View.VISIBLE +// } else { +// binding.emptyList.visibility = View.GONE +// } +// +// } +// } } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/ui/splash_activity/SplashActivity.kt b/app/src/main/java/com/geekymusketeers/uncrack/ui/splash_activity/SplashActivity.kt index e5d32eb9..6ce2dc7f 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/ui/splash_activity/SplashActivity.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/ui/splash_activity/SplashActivity.kt @@ -12,7 +12,7 @@ import com.geekymusketeers.uncrack.databinding.ActivitySplashBinding import com.geekymusketeers.uncrack.util.Util import com.geekymusketeers.uncrack.ui.auth.MasterKeyActivity import com.geekymusketeers.uncrack.viewModel.KeyViewModel -import com.geekymusketeers.uncrack.viewModel.ViewModelFactory +//import com.geekymusketeers.uncrack.viewModel.ViewModelFactory import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -25,7 +25,7 @@ class SplashActivity : AppCompatActivity() { lateinit var binding: ActivitySplashBinding private var cancellationSignal: CancellationSignal? = null private lateinit var biometricPrompt: BiometricPrompt - private val keyViewModel by viewModels { ViewModelFactory() } +// private val keyViewModel by viewModels { ViewModelFactory() } override fun onCreate(savedInstanceState: Bundle?) { @@ -60,28 +60,28 @@ class SplashActivity : AppCompatActivity() { } private fun handleMasterKey() { - keyViewModel.getMasterKey().observe(this) { - when { - it.isEmpty() -> { - CoroutineScope(Dispatchers.Main).launch { - delay(1000) - val intent = Intent(this@SplashActivity, MasterKeyActivity::class.java) - intent.putExtra("flow","createMasterKey") - startActivity(intent) - finish() - } - } - else -> { - CoroutineScope(Dispatchers.Main).launch { - delay(1000) - val intent = Intent(this@SplashActivity, MasterKeyActivity::class.java) - intent.putExtra("flow","askForMasterKey") - startActivity(intent) - finish() - } - } - } - } +// keyViewModel.getMasterKey().observe(this) { +// when { +// it.isEmpty() -> { +// CoroutineScope(Dispatchers.Main).launch { +// delay(1000) +// val intent = Intent(this@SplashActivity, MasterKeyActivity::class.java) +// intent.putExtra("flow","createMasterKey") +// startActivity(intent) +// finish() +// } +// } +// else -> { +// CoroutineScope(Dispatchers.Main).launch { +// delay(1000) +// val intent = Intent(this@SplashActivity, MasterKeyActivity::class.java) +// intent.putExtra("flow","askForMasterKey") +// startActivity(intent) +// finish() +// } +// } +// } +// } } private fun showDialog() { diff --git a/app/src/main/java/com/geekymusketeers/uncrack/util/Constants.kt b/app/src/main/java/com/geekymusketeers/uncrack/util/Constants.kt new file mode 100644 index 00000000..7948b6e6 --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/util/Constants.kt @@ -0,0 +1,11 @@ +package com.geekymusketeers.uncrack.util + +object Constants { + + val sliderStepRange = 0f..32f + const val sliderSteps = 32 + const val DEFAULT_PASSWORD_LENGTH = 12.0f + const val INVITE = """ + Hey there! I've been using this amazing app and thought you might like it too! 😊 Check it out: https://play.google.com/store/apps/details?id=com.geekymusketeers.uncrack + """ +} \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/util/ViewModelExtension.kt b/app/src/main/java/com/geekymusketeers/uncrack/util/ViewModelExtension.kt new file mode 100644 index 00000000..17242c4c --- /dev/null +++ b/app/src/main/java/com/geekymusketeers/uncrack/util/ViewModelExtension.kt @@ -0,0 +1,13 @@ +package com.geekymusketeers.uncrack.util + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +fun ViewModel.runIO(function: suspend CoroutineScope.() -> Unit) = + viewModelScope.launch(Dispatchers.IO) { function() } + +fun ViewModel.runDefault(function: suspend CoroutineScope.() -> Unit) = + viewModelScope.launch(Dispatchers.Default) { function() } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/viewModel/AccountViewModel.kt b/app/src/main/java/com/geekymusketeers/uncrack/viewModel/AccountViewModel.kt index 234bf0ce..08a1ff09 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/viewModel/AccountViewModel.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/viewModel/AccountViewModel.kt @@ -1,45 +1,46 @@ package com.geekymusketeers.uncrack.viewModel -import android.app.Application -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.geekymusketeers.uncrack.data.db.AccountDatabase import com.geekymusketeers.uncrack.domain.model.Account import com.geekymusketeers.uncrack.domain.repository.AccountRepository +import com.geekymusketeers.uncrack.util.runIO +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import javax.inject.Inject -class AccountViewModel(application: Application) : AndroidViewModel(application) { +@HiltViewModel +class AccountViewModel @Inject constructor( + private val repository: AccountRepository +) : ViewModel() { - val readAllData: LiveData> - private val repository : AccountRepository - init { - val accountDao = AccountDatabase.getDatabase(application).accountDao() - repository = AccountRepository(accountDao) - readAllData = repository.readAllAccount + var accountModel by mutableStateOf(emptyList()) + + fun getAccounts() = runIO { + repository.getAllAccounts().collect { response -> + accountModel = response + } } - fun addAccount(account: Account){ + fun addAccount(account: Account) { viewModelScope.launch{ repository.addAccount(account) } } - fun editAccount(account: Account){ + fun editAccount(account: Account) { viewModelScope.launch{ repository.editAccount(account) } } - fun deleteAccount(account: Account){ + fun deleteAccount(account: Account) { viewModelScope.launch { repository.deleteAccount(account) } } - fun updateBookmark(account: Account) { - viewModelScope.launch { - repository.updateFavourite(account) - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/viewModel/KeyViewModel.kt b/app/src/main/java/com/geekymusketeers/uncrack/viewModel/KeyViewModel.kt index 5b38509e..e48e187b 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/viewModel/KeyViewModel.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/viewModel/KeyViewModel.kt @@ -1,44 +1,57 @@ package com.geekymusketeers.uncrack.viewModel -import android.app.Application -import androidx.lifecycle.AndroidViewModel +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.ViewModel import com.geekymusketeers.uncrack.domain.model.Key import com.geekymusketeers.uncrack.domain.repository.KeyRepository -import com.geekymusketeers.uncrack.data.db.KeyDatabase -import kotlinx.coroutines.launch +import com.geekymusketeers.uncrack.util.runIO +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject -class KeyViewModel(application: Application) : AndroidViewModel(application) { +@HiltViewModel +class KeyViewModel @Inject constructor( + private val repository: KeyRepository +) : ViewModel() { - private val keyRepository: KeyRepository - private val masterKeyLiveData = MutableLiveData() - private val confirmMasterKeyLiveData = MutableLiveData() - val enableButtonLiveData = MutableLiveData() + var keyModel by mutableStateOf(emptyList()) - init { - val keyDao = KeyDatabase.getDatabase(application).keyDao() - keyRepository = KeyRepository(keyDao) - } + private val _masterKeyLiveData = MutableLiveData() + val masterKeyLiveData: LiveData = _masterKeyLiveData + + private val _confirmMasterKeyLiveData = MutableLiveData() + val confirmMasterKeyLiveData: LiveData = _confirmMasterKeyLiveData + + private val _enableButtonLiveData = MutableLiveData() + val enableButtonLiveData: LiveData = _enableButtonLiveData fun setMasterKey(masterKey: String) { - masterKeyLiveData.value = masterKey + _masterKeyLiveData.value = masterKey checkMasterKey() } fun setConfirmMasterKey(confirmMasterKey: String) { - confirmMasterKeyLiveData.value = confirmMasterKey + _confirmMasterKeyLiveData.value = confirmMasterKey checkMasterKey() } + fun checkMasterKey() { - enableButtonLiveData.value = !masterKeyLiveData.value.isNullOrBlank() && !confirmMasterKeyLiveData.value.isNullOrBlank() && - masterKeyLiveData.value == confirmMasterKeyLiveData.value + _enableButtonLiveData.value = + _masterKeyLiveData.value.isNullOrBlank() + .not() && _confirmMasterKeyLiveData.value.isNullOrBlank().not() && + _masterKeyLiveData.value == _confirmMasterKeyLiveData.value } - fun saveMasterKey(key: Key){ - viewModelScope.launch { - keyRepository.setMasterKey(key) + + fun saveMasterKey(key: Key) = runIO { + repository.setMasterKey(key) + } + + fun getMasterKey() = runIO { + repository.getMasterKey().collect { response -> + keyModel = response } } - fun getMasterKey() : LiveData> = keyRepository.getMasterKey() } \ No newline at end of file diff --git a/app/src/main/java/com/geekymusketeers/uncrack/viewModel/ViewModelFactory.kt b/app/src/main/java/com/geekymusketeers/uncrack/viewModel/ViewModelFactory.kt index 8d0c733e..cae13702 100644 --- a/app/src/main/java/com/geekymusketeers/uncrack/viewModel/ViewModelFactory.kt +++ b/app/src/main/java/com/geekymusketeers/uncrack/viewModel/ViewModelFactory.kt @@ -1,21 +1,21 @@ -package com.geekymusketeers.uncrack.viewModel - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.CreationExtras - - -class ViewModelFactory : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class, extras: CreationExtras): T = - with(modelClass) { - // Get the Application object from extras - val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]) - when { - isAssignableFrom(KeyViewModel::class.java) -> { - KeyViewModel(application) - } - else -> throw IllegalArgumentException("Unknown ViewModel class") - } - } as T -} \ No newline at end of file +//package com.geekymusketeers.uncrack.viewModel +// +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.ViewModelProvider +//import androidx.lifecycle.viewmodel.CreationExtras +// +// +//class ViewModelFactory : ViewModelProvider.Factory { +// @Suppress("UNCHECKED_CAST") +// override fun create(modelClass: Class, extras: CreationExtras): T = +// with(modelClass) { +// // Get the Application object from extras +// val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]) +// when { +// isAssignableFrom(KeyViewModel::class.java) -> { +// KeyViewModel(application) +// } +// else -> throw IllegalArgumentException("Unknown ViewModel class") +// } +// } as T +//} \ No newline at end of file diff --git a/app/src/main/res/drawable/backup_restore.xml b/app/src/main/res/drawable/backup_restore.xml new file mode 100644 index 00000000..767441a6 --- /dev/null +++ b/app/src/main/res/drawable/backup_restore.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/block_ss.xml b/app/src/main/res/drawable/block_ss.xml new file mode 100644 index 00000000..398aa40a --- /dev/null +++ b/app/src/main/res/drawable/block_ss.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/copy_password.xml b/app/src/main/res/drawable/copy_password.xml new file mode 100644 index 00000000..95bafa83 --- /dev/null +++ b/app/src/main/res/drawable/copy_password.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/drawable/delete_icon.xml b/app/src/main/res/drawable/delete_icon.xml index 47a8f9c5..aabfca50 100644 --- a/app/src/main/res/drawable/delete_icon.xml +++ b/app/src/main/res/drawable/delete_icon.xml @@ -8,6 +8,6 @@ android:pathData="M0,0h24v24h-24z"/> + android:fillColor="#FF0000"/> diff --git a/app/src/main/res/drawable/generate_password.xml b/app/src/main/res/drawable/generate_password.xml new file mode 100644 index 00000000..4d0a6ece --- /dev/null +++ b/app/src/main/res/drawable/generate_password.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/import_export.xml b/app/src/main/res/drawable/import_export.xml new file mode 100644 index 00000000..7a3a8b9c --- /dev/null +++ b/app/src/main/res/drawable/import_export.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/logout.xml b/app/src/main/res/drawable/logout.xml new file mode 100644 index 00000000..b4e40835 --- /dev/null +++ b/app/src/main/res/drawable/logout.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/no_user_profile_picture.xml b/app/src/main/res/drawable/no_user_profile_picture.xml new file mode 100644 index 00000000..c03084a0 --- /dev/null +++ b/app/src/main/res/drawable/no_user_profile_picture.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7fd8f4e5..86378a03 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -150,6 +150,9 @@ Register Already have an account Skip + Cancel + Are you sure you want to logout? + Logout? Enter your email johndoe123@gmail.com Enter your password @@ -159,6 +162,21 @@ Login Don\'t have an account? Create + By Aritra Das 💙 + Save + Numbers + Uppercase letters + Lowercase letters + Special symbols + Include following: + Copy + Backup + General + Danger Zone + Update Master Key + Old Master Key + New Master Key + Password Length %1$s Others PayPal