implementation of view-viewmodel pattern MVVM - kotlin

I am new to development and I want to learn how to use the MVVM pattern. I have a problem with the fact that I need to implement the methods that I have in SignUpFragment in the ViewModel class. Please help me understand how to finally do this. I will be very grateful! Thanks in advance
please do not send links to materials, I want to understand my example, thanks
My fragment
class SignUpFragment : Fragment() {
private val viewModel: SignUpViewModel by activityViewModels()
private val binding: FragmentSignUpBinding? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val viewModel:SignUpViewModel = ViewModelProviders.of(this)[SignUpViewModel::class.java]
val binding: FragmentSignUpBinding =
DataBindingUtil.inflate(
inflater, R.layout.fragment_sign_up, container, false
)
return binding.root
}
// call next sign up screen window
fun callNextSignUpScreen(view: View) {
//validate form
if (!(validateFullName() || validateUserName() as Boolean || validateEmail() as Boolean || validatePassword() as Boolean)) {
return
} else {
binding?.signupNextBtn?.setOnClickListener {
findNavController()
.navigate(
R.id.action_signUpFragment_to_signUpSecondFragment
)
}
}
//add transition
val pairs: Array<Pair<*, *>?> = arrayOfNulls(4)
pairs[0] = Pair<View, String>(binding?.signupBackButton, "transition_back_arrow_btn")
pairs[1] = Pair<View, String>(binding?.signupLoginBtn, "transition_login_btn")
pairs[2] = Pair<View, String>(binding?.signupNextBtn, "transition_next_btn")
pairs[3] = Pair<View, String>(binding?.signupTitleText, "transition_title_btn")
}
//validate field full name
private fun validateFullName(): Boolean {
val validName: String = binding!!.signupFullname.editText?.text.toString().trim()
return if (validName.isEmpty()) {
binding.signupFullname.error = "field can not be Empty"
false
} else {
binding.signupFullname.error = null
binding.signupFullname.isErrorEnabled = false
true
}
}
//validate field user name
private fun validateUserName(): Any {
val userName: String = binding!!.signupUserName.editText?.text.toString().trim()
val checksPaces: Regex = "[a-zA-Z0-9._-]+#[a-z]+\\.++[a-z]+".toRegex()
return if (userName.isEmpty()) {
binding.signupUserName.error = "field can not be Empty"
false
} else (if (userName.length > 20) {
binding.signupUserName.error = "Username is too large!"
false
} else if (!userName.matches(checksPaces)) {
binding.signupEmail.error = "Invalid Email"
} else {
binding.signupUserName.error = null
binding.signupUserName.isErrorEnabled = false
true
})
}
//validate field email
private fun validateEmail(): Any {
val email: String = binding!!.signupEmail.editText?.text.toString().trim()
val checkEmail: Regex = "[a-zA-Z0-9._-]+#[a-z]+\\.++[a-z]+".toRegex()
return if (email.isEmpty()) {
binding.signupEmail.error = "field can not be Empty"
false
} else if (!email.matches(checkEmail)) {
binding.signupEmail.error = "Invalid Email"
} else {
binding.signupEmail.error = null
binding.signupEmail.isErrorEnabled = false
true
}
}
//validate field password
private fun validatePassword(): Any {
val password: String = binding!!.signupPassword.editText?.text.toString().trim()
val checkPassword: Regex =
"^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[##\$%^&+=])(?=\\S+\$).{8,}\$".toRegex()
return if (password.isEmpty()) {
binding.signupPassword.error = "Password should contain4 characters!"
false
} else if (!password.matches(checkPassword)) {
binding.signupPassword.error = "Invalid Email"
} else {
binding.signupPassword.error = null
binding.signupPassword.isErrorEnabled = false
true
}
}

Related

Why my return value is coming as NaN or default in kotlin?

For a while, I want to convert the entered currencies to each other and show them on the other screen, but when I check the view model with println, the result I can see is NaN when I make viewmodel.result in the ui. What is the reason for this and how can I solve it?
my ui
If the user presses oncofirm on the button, the operations in the view model are performed.
if (viewModel.isDialogShown) {
AlertDialog(
onDismiss = {
viewModel.onDismissClick()
},
onConfirm = {
viewModel.getConversionRateByCurrency()
viewModel.onDismissClick()
//viewModel.calculate()
println(viewModel.resultState)
With println(viewModel.resultState) comes 0.0
but when I press the button for the second time and say confirm, then the correct result comes.
my view model
#HiltViewModel
class ExchangeMainViewModel #Inject constructor(
private val exchangeInsertUseCase: InsertExchangeUseCase,
private val exchangeGetAllUseCase: GetAllExchangeUseCase,
private val getConversionRateByCurrencyUseCase: GetConversionRateByCurrencyUseCase
) : ViewModel() {
var isDialogShown by mutableStateOf(false)
private set
var dropDownMenuItem1 by mutableStateOf("")
var dropDownMenuItem2 by mutableStateOf("")
var outLineTxtFieldValue by mutableStateOf(TextFieldValue(""))
var firstConversionRate by mutableStateOf(0.0)
var secondConversionRate by mutableStateOf(0.0)
var resultState by mutableStateOf(0.0)
fun onConfirmClick() {
isDialogShown = true
}
fun onDismissClick() {
isDialogShown = false
}
fun check(context: Context): Boolean {
if (outLineTxtFieldValue.text.isNullOrEmpty() || dropDownMenuItem1 == "" || dropDownMenuItem2 == "") {
Toast.makeText(context, "please select a value and currency ", Toast.LENGTH_LONG).show()
return false
}
return true
}
fun getConversionRateByCurrency() {
viewModelScope.launch {
val firstRate = async {
getConversionRateByCurrencyUseCase.getConversionRateByCurrency(dropDownMenuItem1)
}
val secondRate = async {
getConversionRateByCurrencyUseCase.getConversionRateByCurrency(dropDownMenuItem2)
}
firstConversionRate = firstRate.await()
secondConversionRate = secondRate.await()
delay(200L)
val result = async {
calculate()
}
resultState = result.await()
}
}
private fun calculate(): Double {
if (!firstConversionRate.equals(0.0) && !secondConversionRate.equals(0.0)) {
val amount = outLineTxtFieldValue.text.toInt()
val resultOfCalculate = (amount / firstConversionRate) * secondConversionRate
return resultOfCalculate
}
return 1.1
}
}
I can see the value in the view model but not the ui. Also, I do a lot of checking with if and 0.0 because I couldn't get out of it, so I followed a method like this because I couldn't solve the problem. Anyone have a better idea?

show RewardedAd from object class

I need to reduce my app size by collect recurring functions in one place
All functions works fine Except RewardedAd
I want to return a value confirming that ad was shown until end by set variable named adRewardedBoolean to True
Here's my Kotlin code
//Rewarded
private const val adRewardedID = "ca-app-pub-3306064401573277/5337641258"
private var adRewardedBoolean: Boolean = false
private var adRewardedIsLoading = false
private var adRewarded: RewardedAd? = null
private var adRewardAmount: Int = 0
private var adRewardedTimes: Int = 0
//Call loadRewardedAd from any activities
//Ads.loadRewardedAd(this)
fun loadRewardedAd(context: Context): Boolean {
context as Activity
if (adRewarded == null) {
adRewardedIsLoading = true
val adRequest = AdRequest.Builder().build()
RewardedAd.load(context,
adRewardedID,
adRequest,
object : RewardedAdLoadCallback() {
override fun onAdFailedToLoad(adError: LoadAdError) {
adRewardedIsLoading = false
adRewarded = null
if (adRewardedTimes == 3) {
adRewardAmount++
adRewardedBoolean = true
log("onAdFailedToLoad")
//checkRewardedAd(adRewardAmount)
} else {
adRewardedTimes++
loadRewardedAd(context)
}
}
override fun onAdLoaded(rewardedAd: RewardedAd) {
adRewarded = rewardedAd
adRewardedIsLoading = false
adRewarded?.fullScreenContentCallback =
object : FullScreenContentCallback() {
override fun onAdDismissedFullScreenContent() {
adRewarded = null
adRewardedBoolean = true
log("onAdDismissedFullScreenContent")
}
override fun onAdFailedToShowFullScreenContent(adError: AdError) {
adRewarded = null
loadRewardedAd(context)
}
override fun onAdShowedFullScreenContent() {
}
}
adRewarded?.show(context) { rewardItem ->
adRewardAmount = rewardItem.amount
}
}
})
}
log(adRewardedBoolean.toString())
return adRewardedBoolean
}

Data fetch from API not showing on recyclerview MVVM kotlin

I got blocked for this problem, Api will call and get the objects from the response and it will display on the recyclerview. But it is not showing,
fetchProductCategories will do the api call.
prepareProducts will handle the fetched from fetchProductCategories
Fragment:
#AndroidEntryPoint
class ProductsWelcomeFragment : BaseProductsFragment<ProductsWelcomeViewModel, ProductsWelcomeFragmentBinding>() {
private val TAG = "ProductsWelcomeFragment"
#Inject
lateinit var animationQueue: AnimationQueue
override fun getViewModelClass(): KClass<ProductsWelcomeViewModel> = ProductsWelcomeViewModel::class
override fun getContentViewRes(): Int = R.layout.products_welcome_fragment
private val cordovaViewModel: CordovaViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/**
* Initialize only once when everytime accessing products screen.
* */
sharedViewModel.initialSetUpRequest()
viewModel.fetchProductCategories()
}
override fun onBindView() {
with(dataBinding) {
viewModel = this#ProductsWelcomeFragment.viewModel
title = getString(R.string.products_screen_welcome_header)
recyclerViewProducts.configure()
executePendingBindings()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.productCategoriesLiveData.observe(viewLifecycleOwner) {
Log.i(TAG, it.toString())
viewModel.items.observe(viewLifecycleOwner) {
viewModel.prepareProducts()
}
}
viewModel.showProgress.observe(viewLifecycleOwner) {}
viewModel.failedAtRetrievingData.observe(viewLifecycleOwner) {
// showErrorScreen(true)
}
}
private fun RecyclerView.configure() {
with(dataBinding.recyclerViewProducts) {
addItemBindings(SectionHeaderItemViewBinder)
addItemBindings(SpaceItemViewDtoBinder)
addItemBindings(getListButtonItemViewBinder(ProductItem.NormalProduct::dto, ::onProductItemClick))
addItemBindings(getListBigtileItemViewBinder(ProductItem.FeaturedProduct::dto, ::onProductItemClick))
}
}
private fun onProductItemClick(product: ProductItem.NormalProduct) {
when (product.id) {
else -> { // TODO : )}
}
// TODO : implement move to another screen
}
private fun onProductItemClick(product: ProductItem.FeaturedProduct) {
// TODO : implement move to another screen
}
private fun showErrorScreen(isDataRetrievalError: Boolean) {
val dismissAction = ErrorHandlingAction(
getString(R.string.native_done),
null,
null,
ButtonType.PRIMARY
) { dismissDialog ->
dismissDialog.dismiss()
if (isDataRetrievalError) {
findNavController().popBackStack()
}
}
DialogFactory().showBottomDialog(
fragmentManager = parentFragmentManager,
window = requireActivity().window,
title = getString(R.string.native_error_title),
description = getString(R.string.online_identity_something_went_wrong_error_description),
iconId = R.drawable.ic_error_thick_exclamation_icon,
errorHandlingActions = arrayOf(dismissAction),
isHtmlDescription = true
)
}
private fun openCordovaScreen(destination: CordovaPage) {
cordovaViewModel.requestPage(destination)
findNavController().popBackStack(R.id.homeScreenMainFragment, false)
}
}
ViewModel:
#HiltViewModel
class ProductsWelcomeViewModel #Inject constructor(
private val productsRepository: ProductsRepository
) : BaseRequestViewModel(), ViewModelWithItems {
private val TAG = "ProductsWelcomeViewModel"
private val _failedAtRetrievingData = SingleLiveEvent<Boolean>()
val failedAtRetrievingData: LiveData<Boolean> = _failedAtRetrievingData
private val _showProgress = MutableLiveData<Boolean>()
val showProgress: LiveData<Boolean> = _showProgress
private val onProductCategories: SingleLiveEvent<ProductBasketsCategoriesModel?> = SingleLiveEvent()
val productCategoriesLiveData: LiveData<ProductBasketsCategoriesModel?> = onProductCategories
private val _items: MutableLiveData<List<Any>> = MutableLiveData()
override val items: LiveData<List<Any>> = _items
fun fetchProductCategories() {
viewModelScope.launch {
request({ productsRepository.getProductCategories() },
success = { response -> onProductCategories.value = response },
failure = { })
}
}
fun prepareProducts() {
_items.value = mutableListOf<Any>().apply {
productCategoriesLiveData.value?.embedded?.categories?.filter { categories ->
categories.isFeatured == true }?.let { filteredCategories ->
add(HeaderItem(ListSectionHeaderItemViewDto(
text = TextLine(
textRes = if (filteredCategories.isNotEmpty()) {
R.string.products_screen_welcome_header } else { null }
)
)
))
filteredCategories.toItems()?.let { addAll(it) }
}
productCategoriesLiveData.value?.embedded?.categories?.filter { categories ->
categories.isFeatured == false }.let { filteredCategories ->
if (filteredCategories != null) {
add(HeaderItem(ListSectionHeaderItemViewDto(
text = TextLine(
textRes = if (filteredCategories.isNotEmpty()) {
R.string.products_screen_welcome_header } else { null }
)
)
))
}
filteredCategories.toItems()?.let { addAll(it) }
}
}
}
private fun List<CategoriesItemModel>?.toItems(): List<ProductItem>? =
this?.mapIndexed { index, item ->
if (item.isFeatured == true) {
ProductItem.FeaturedProduct(
id = item.id as Any,
name = item.name,
dto = BigTileDto(
title = TextLine(text = item.name),
image = item.id.toString().let { toFeatureIcon(it, item.isFeatured) },
description = TextLine(item.description.toString()),
background = BackgroundType.SINGLE
)
)
} else {
ProductItem.NormalProduct(
id = item.id as Any,
name = item.name,
dto = ListButtonItemViewDto(
firstLine = TextLine(text = item.name),
rightDrawable = R.drawable.ic_arrow_right,
separatorDrawable = R.drawable.list_divider_margin_start_72dp,
background = index.indexToBackgroundType(this.size),
avatarDto = AvatarDto(iconBackgroundColor = R.color.ubs_concrete, avatarSize = AvatarDto.AvatarSize.SMALL_ICON_SIZE, iconId = toFeatureIcon(
item.id, item.isFeatured
)),
)
)
}
}
private fun toFeatureIcon(id: String?, isFeature: Boolean?): Int = if (ProductFeature.verify(id) == true) {
ProductFeature.icon()
} else { if (isFeature == true) { R.drawable.abc_vector_test } else {
R.drawable.balloon_illustration } }
}

how can i make menu item (single) reset button?

I want to make reset button.
when I click reset button, ischecked will be set to false for R.id.item1, R.id.item2, and R.id.item3.
when I click button, I want to Group 'Mmenu' be checked to false
I tried button.setOnClickListener(){item.isChecked = false} in onCreateOptionsMenu(menu: Menu)
but didn't work...
And I tried same thing behind when (item.itemId) {R.id.itemRotate ->
but didn't work too...
class MainActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "제주도 풍경"
button.setOnClickListener(){
imageView1.visibility = View.INVISIBLE
imageView1.rotation = Float.parseFloat("0")
edtAngle.setText("0")
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
val mInflater = menuInflater
mInflater.inflate(R.menu.menu1, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem) : Boolean {
when (item.itemId) {
R.id.itemRotate -> {
imageView1.visibility = View.VISIBLE
imageView1.rotation = Float.parseFloat(edtAngle.text.toString())
return true
}
R.id.item1 -> {
imageView1.visibility = View.VISIBLE
imageView1.setImageResource(R.drawable.jeju2)
item.isChecked = true
return true
}
R.id.item2 -> {
imageView1.visibility = View.VISIBLE
imageView1.setImageResource(R.drawable.jeju14)
item.isChecked = true
return true
}
R.id.item3 -> {
imageView1.visibility = View.VISIBLE
imageView1.setImageResource(R.drawable.jeju6)
item.isChecked = true
return true
}
}
return false
}
}
You can do this using a HashMap :
var checked: HashMap<Int, Boolean> = HashMap()
Initialize to false
val ids = listOf(R.id.item1, R.id.item2, R.id.item3)
ids.forEachIndexed { index, _ ->
checked[index] = false
}
Then
when (item.itemId) {
R.id.itemRotate -> {
imageView1.visibility = View.VISIBLE
imageView1.rotation = Float.parseFloat(edtAngle.text.toString())
return true
}
R.id.item1 -> {
imageView1.visibility = View.VISIBLE
imageView1.setImageResource(R.drawable.jeju2)
checked[item.itemId] = true
return true
}
R.id.item2 -> {
imageView1.visibility = View.VISIBLE
imageView1.setImageResource(R.drawable.jeju14)
checked[item.itemId] = true
return true
}
R.id.item3 -> {
imageView1.visibility = View.VISIBLE
imageView1.setImageResource(R.drawable.jeju6)
checked[item.itemId] = true
return true
}
}
To reset:
fun reset() {
ids.forEachIndexed { index, _ ->
checked[index] = false
}
}
To be called here:
button.setOnClickListener(){
imageView1.visibility = View.INVISIBLE
imageView1.rotation = Float.parseFloat("0")
edtAngle.setText("0")
reset()
}
invalidateOptionsMenu()
this is solution to reset!!

How can I check if a user has uploaded photos to my image views in Kotlin android

How can I check if a user has uploaded photos to my image views in Kotlin android, so that I can allow them to move to the next fragment. I want to include this in my on Apply function where I have including the other checks for is Null Or Empty for the text views. I have 4 image views I want the user to upload photos to. I want to check this in on apply function.
private lateinit var userId: String
private lateinit var userDatabase: DatabaseReference
private var callback: TinderCallback?=null
fun setCallback(callback: TinderCallback) {
this.callback = callback
userId= callback.onGetUserId()
userDatabase= callback.getUserDatabase().child(userId)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_profile, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
progressLayout.setOnTouchListener { view, event -> true }
populateInfo()
photoIV.setOnClickListener{callback?.startActivityForPhoto()}
image1IV.setOnClickListener{callback?.startActivityForPhoto1()}
image2IV.setOnClickListener{callback?.startActivityForPhoto2()}
image3IV.setOnClickListener{callback?.startActivityForPhoto3()}
applyButton.setOnClickListener { onApply()}
signoutButton.setOnClickListener{callback?.onSignOut()}
}
fun populateInfo(){
progressLayout.visibility = View.VISIBLE
userDatabase.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(isAdded){
val user = snapshot.getValue(User::class.java)
nameET.setText(user?.name, TextView.BufferType.EDITABLE)
emailET.setText(user?.email, TextView.BufferType.EDITABLE)
ageET.setText(user?.age, TextView.BufferType.EDITABLE)
occupationET.setText(user?.occupation, TextView.BufferType.EDITABLE)
countryET.setText(user?.country, TextView.BufferType.EDITABLE)
cityET.setText(user?.city, TextView.BufferType.EDITABLE)
interestsET.setText(user?.interests, TextView.BufferType.EDITABLE)
if(user?.gender == GENDER_MALE) {
radioMan1.isChecked = true
}
if (user?.gender == GENDER_FEMALE){
radioWoman1.isChecked = true
}
if(user?.preferredGender == GENDER_MALE) {
radioMan2.isChecked = true
}
if(user?.preferredGender == GENDER_FEMALE){
radioWoman2.isChecked = true
}
if (!user?.imageUrl.isNullOrEmpty()) {
populateImage(user?.imageUrl!!)
}
if (!user?.image1url.isNullOrEmpty()) {
populateImage1(user?.image1url!!)
}
if (!user?.image2url.isNullOrEmpty()) {
populateImage2(user?.image2url!!)
}
if (!user?.image3url.isNullOrEmpty()) {
populateImage3(user?.image3url!!)
}
progressLayout.visibility = View.GONE
}
}
override fun onCancelled(error: DatabaseError) {
progressLayout.visibility = View.GONE
}
})
}
private fun onApply(){
if(nameET.text.toString().isNullOrEmpty() ||
ageET.text.toString().isNullOrEmpty() ||
emailET.text.toString().isNullOrEmpty() ||
radioGroup1.checkedRadioButtonId == -1 ||
radioGroup2.checkedRadioButtonId == -1) {
Toast.makeText(context, getString(R.string.error_profile_incomplete), Toast.LENGTH_SHORT).show()
} else {
val name = nameET.text.toString()
val age = ageET.text.toString()
val email = emailET.text.toString()
val occupation = occupationET.text.toString()
val country = countryET.text.toString()
val city = cityET.text.toString()
val interests = interestsET.text.toString()
val gender =
if(radioMan1.isChecked) GENDER_MALE
else GENDER_FEMALE
val preferredGender =
if(radioMan2.isChecked) GENDER_MALE
else GENDER_FEMALE
userDatabase.child(DATA_NAME).setValue(name)
userDatabase.child(DATA_AGE).setValue(age)
userDatabase.child(DATA_EMAIL).setValue(email)
userDatabase.child(DATA_COUNTRY).setValue(country)
userDatabase.child(DATA_CITY).setValue(city)
userDatabase.child(DATA_OCCUPATION).setValue(occupation)
userDatabase.child(DATA_INTERESTS).setValue(interests)
userDatabase.child(DATA_GENDER).setValue(gender)
userDatabase.child(DATA_GENDER_PREFERRENCE).setValue(preferredGender)
callback?.profileComplete()
}
}
fun updateImageUri(Uri:String){
userDatabase.child(DATA_IMAGE_URL).setValue(Uri)
populateImage(Uri)
}
fun updateImageUri1(Uri:String){
userDatabase.child(DATA_PHOTO1_URL).setValue(Uri)
populateImage1(Uri)
}
fun populateImage(Uri: String) {
Glide.with(this)
.load(Uri)
.into(photoIV)
}
fun populateImage1(Uri: String){
Glide.with(this)
.load(Uri)
.into(image1IV)
}
fun updateImageUri2(Uri:String){
userDatabase.child(DATA_PHOTO2_URL).setValue(Uri)
populateImage2(Uri)
}
fun populateImage2(Uri: String) {
Glide.with(this)
.load(Uri)
.into(image2IV)
}
fun updateImageUri3(Uri:String){
userDatabase.child(DATA_PHOTO3_URL).setValue(Uri)
populateImage3(Uri)
}
fun populateImage3(Uri: String) {
Glide.with(this)
.load(Uri)
.into(image3IV)
}```
If those populateImage functions are where you add the images to the ImageViews, you could set an imageAdded var in there, one for each ImageView. Then your code can check if all 4 are set to true