migration (#362)

* migration

* spotless run
This commit is contained in:
Yogesh Choudhary Paliyal
2023-03-05 17:53:39 +05:30
committed by GitHub
parent 146c6178ce
commit 9bbfef35e7
6 changed files with 286 additions and 93 deletions

View File

@@ -2,7 +2,6 @@ package com.yogeshpaliyal.keypass.ui.home
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
@@ -20,7 +19,10 @@ import javax.inject.Inject
* created on 30-01-2021 23:02
*/
@HiltViewModel
class DashboardViewModel @Inject constructor(application: Application, val appDb: com.yogeshpaliyal.common.AppDatabase) :
class DashboardViewModel @Inject constructor(
application: Application,
val appDb: com.yogeshpaliyal.common.AppDatabase
) :
AndroidViewModel(application) {
val keyword by lazy {
@@ -32,17 +34,23 @@ class DashboardViewModel @Inject constructor(application: Application, val appDb
private val appDao = appDb.getDao()
val mediator = MediatorLiveData<LiveData<List<AccountModel>>>()
val mediator = MediatorLiveData<List<AccountModel>>()
init {
mediator.addSource(keyword) {
mediator.postValue(appDao.getAllAccounts(keyword.value, tag.value))
viewModelScope.launch(Dispatchers.IO) {
mediator.postValue(appDao.getAllAccounts(keyword.value, tag.value))
}
}
mediator.addSource(tag) {
viewModelScope.launch(Dispatchers.IO) {
mediator.postValue(appDao.getAllAccounts(keyword.value, tag.value))
}
}
viewModelScope.launch(Dispatchers.IO) {
mediator.postValue(appDao.getAllAccounts(keyword.value, tag.value))
}
mediator.postValue(appDao.getAllAccounts(keyword.value, tag.value))
reloadData()
}

View File

@@ -7,22 +7,63 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.ContentCopy
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.viewmodel.compose.viewModel
import com.yogeshpaliyal.common.constants.AccountType
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.data.MyAccountModel
import com.yogeshpaliyal.keypass.databinding.FragmentHomeBinding
import com.yogeshpaliyal.keypass.listener.AccountsClickListener
import com.yogeshpaliyal.keypass.ui.addTOTP.AddTOTPActivity
import com.yogeshpaliyal.keypass.ui.detail.DetailActivity
import com.yogeshpaliyal.universalAdapter.adapter.UniversalAdapterViewType
import com.yogeshpaliyal.universalAdapter.adapter.UniversalRecyclerAdapter
import com.yogeshpaliyal.universalAdapter.utils.Resource
import com.yogeshpaliyal.keypass.ui.style.KeyPassTheme
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
/*
* @author Yogesh Paliyal
@@ -32,90 +73,240 @@ import dagger.hilt.android.AndroidEntryPoint
*/
@AndroidEntryPoint
class HomeFragment : Fragment() {
private lateinit var binding: FragmentHomeBinding
private val mViewModel by lazy {
activityViewModels<DashboardViewModel>().value
}
private val mAdapter by lazy {
UniversalRecyclerAdapter.Builder<MyAccountModel>(
this,
content = UniversalAdapterViewType.Content(
R.layout.item_accounts,
listener = mListener
),
noData = UniversalAdapterViewType.NoData(R.layout.layout_no_accounts)
).build()
}
val mListener = object : AccountsClickListener<AccountModel> {
override fun onItemClick(view: View, model: AccountModel) {
if (model.type == AccountType.TOTP) {
AddTOTPActivity.start(context, model.uniqueId)
} else {
DetailActivity.start(context, model.id)
}
}
private fun getPassword(model: AccountModel): String {
if (model.type == AccountType.TOTP) {
return model.getOtp()
}
return model.password.orEmpty()
}
override fun onCopyClicked(model: AccountModel) {
val clipboard =
ContextCompat.getSystemService(
requireContext(),
ClipboardManager::class.java
)
val clip = ClipData.newPlainText("KeyPass", getPassword(model))
clipboard?.setPrimaryClip(clip)
Toast.makeText(context, getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT)
.show()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(layoutInflater, container, false)
return binding.root
}
private val observer = Observer<List<AccountModel>> {
val newList = it.map { accountModel ->
MyAccountModel().also {
it.map(accountModel)
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
Main(mViewModel)
}
}
mAdapter.updateData(Resource.success(ArrayList(newList)))
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
private fun getPassword(model: AccountModel): String {
if (model.type == AccountType.TOTP) {
return model.getOtp()
}
return model.password.orEmpty()
}
binding.recyclerView.adapter = mAdapter.getAdapter()
@Composable()
fun Main(mViewModel: DashboardViewModel = viewModel()) {
KeyPassTheme {
Surface(modifier = Modifier.padding(bottom = dimensionResource(id = R.dimen.bottom_app_bar_height))) {
val listOfAccountsLiveData by mViewModel.mediator.observeAsState()
mViewModel.mediator.observe(
viewLifecycleOwner,
Observer {
it.removeObserver(observer)
it.observe(viewLifecycleOwner, observer)
AccountsList(listOfAccountsLiveData)
}
)
}
}
/* lifecycleScope.launch() {
mViewModel.result
mViewModel.loadData(args.tag).collect {
withContext(Dispatchers.Main) {
mAdapter.updateData(Resource.success(ArrayList(it)))
}
}
}*/
@Composable
fun AccountsList(accounts: List<AccountModel>? = null) {
val context = LocalContext.current
if (accounts?.isNotEmpty() != null) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(accounts) { account ->
Account(
account,
onClick = {
if (it.type == AccountType.TOTP) {
AddTOTPActivity.start(context, it.uniqueId)
} else {
DetailActivity.start(context, it.id)
}
},
onCopyClicked = {
val clipboard = ContextCompat.getSystemService(
context, ClipboardManager::class.java
)
val clip = ClipData.newPlainText("KeyPass", getPassword(it))
clipboard?.setPrimaryClip(clip)
Toast.makeText(
context,
getString(R.string.copied_to_clipboard),
Toast.LENGTH_SHORT
)
.show()
}
)
}
item {
Spacer(modifier = Modifier.height(32.dp))
}
}
} else {
NoDataFound()
}
}
@Preview
@Composable
fun PreviewAccount() {
KeyPassTheme {
Account(
accountModel = AccountModel(),
onClick = {
},
onCopyClicked = {
}
)
}
}
@Composable
fun Account(
accountModel: AccountModel,
onClick: (AccountModel) -> Unit,
onCopyClicked: (AccountModel) -> Unit
) {
Card(
elevation = androidx.compose.material3.CardDefaults.cardElevation(defaultElevation = 1.dp),
onClick = { onClick(accountModel) }
) {
Row(modifier = Modifier.padding(12.dp)) {
Box(
modifier = Modifier
.align(Alignment.CenterVertically)
.size(60.dp)
.background(
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f),
shape = RoundedCornerShape(50)
)
) {
Text(
modifier = Modifier.align(Alignment.Center),
text = accountModel.getInitials(),
textAlign = TextAlign.Center,
)
if (accountModel.type == AccountType.TOTP) {
WrapWithProgress(accountModel)
}
}
Column(
modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically)
.padding(horizontal = 16.dp)
) {
Text(
text = accountModel.title ?: "",
style = MaterialTheme.typography.headlineSmall.merge(
TextStyle(
fontSize = 16.sp
)
)
)
RenderUserName(accountModel)
}
IconButton(
modifier = Modifier.align(Alignment.CenterVertically),
onClick = { onCopyClicked(accountModel) }
) {
Icon(
painter = rememberVectorPainter(image = Icons.TwoTone.ContentCopy),
contentDescription = "Copy To Clipboard"
)
}
}
}
}
@Composable
fun WrapWithProgress(accountModel: AccountModel) {
val (progress, setProgress) = remember { mutableStateOf(0f) }
LaunchedEffect(Unit) {
if (accountModel.type == AccountType.TOTP) {
while (true) {
delay(1.seconds)
val newProgress = accountModel.getTOtpProgress().toFloat() / 30
setProgress(newProgress)
}
}
}
CircularProgressIndicator(
modifier = Modifier.fillMaxSize(),
progress = progress
)
}
private fun getUsernameOrOtp(accountModel: AccountModel): String? {
return if (accountModel.type == AccountType.TOTP) accountModel.getOtp() else accountModel.username
}
@Composable
fun RenderUserName(accountModel: AccountModel) {
val (username, setUsername) = remember { mutableStateOf(getUsernameOrOtp(accountModel)) }
LaunchedEffect(Unit) {
if (accountModel.type == AccountType.TOTP) {
while (true) {
delay(1.seconds)
setUsername(accountModel.getOtp())
}
}
}
Text(
text = username ?: "",
style = MaterialTheme.typography.bodyMedium.merge(
TextStyle(
fontSize = 14.sp
)
)
)
}
@Composable
fun NoDataFound() {
Column(
modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom
) {
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
verticalArrangement = Arrangement.Center
) {
Text(
text = stringResource(id = R.string.message_no_accounts),
modifier = Modifier
.padding(32.dp)
.align(alignment = Alignment.CenterHorizontally),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center
)
}
Image(
painter = painterResource(R.drawable.ic_undraw_empty_street_sfxm),
contentDescription = ""
)
}
}
}

View File

@@ -69,18 +69,6 @@ class DashboardActivity :
this@DashboardActivity
)
/* val intent = Intent(this, AuthenticationActivity::class.java)
startActivity(intent)*/
/* val autoFillService = getAutoFillService()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
if (autoFillService?.isAutofillSupported == true && autoFillService.hasEnabledAutofillServices().not()) {
val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
intent.data = Uri.parse("package:$packageName")
startActivityForResult(intent,777)
}
}*/
binding.btnAdd.setOnClickListener {
currentNavigationFragment?.apply {
exitTransition = MaterialElevationScale(false).apply {

View File

@@ -32,6 +32,7 @@
<item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.KeyPass.LargeComponent</item>
<!--Typography-->
<item name="textAppearanceHeadline1">@style/TextAppearance.KeyPass.Headline1</item>
<item name="textAppearanceHeadline2">@style/TextAppearance.KeyPass.Headline2</item>
<item name="textAppearanceHeadline3">@style/TextAppearance.KeyPass.Headline3</item>
<item name="textAppearanceHeadline4">@style/TextAppearance.KeyPass.Headline4</item>

View File

@@ -15,6 +15,11 @@
<resources>
<!--Typography-->
<style name="TextAppearance.KeyPass.Headline1" parent="TextAppearance.MaterialComponents.Headline1">
<item name="fontFamily">@font/work_sans_medium</item>
<item name="android:textColor">@color/color_on_surface_emphasis_high</item>
</style>
<style name="TextAppearance.KeyPass.Headline2" parent="TextAppearance.MaterialComponents.Headline2">
<item name="fontFamily">@font/work_sans_medium</item>
<item name="android:textColor">@color/color_on_surface_emphasis_high</item>

View File

@@ -42,7 +42,7 @@ interface DbDao {
"OR (notes LIKE '%'||:query||'%' )) " +
"ORDER BY title ASC"
)
fun getAllAccounts(query: String?, tag: String?): LiveData<List<AccountModel>>
fun getAllAccounts(query: String?, tag: String?): List<AccountModel>
@Query("SELECT * FROM account WHERE id = :id")
suspend fun getAccount(id: Long?): AccountModel?