diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/AppBarFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/AppBarFragment.kt new file mode 100644 index 0000000..c7fe0e0 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/AppBarFragment.kt @@ -0,0 +1,36 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.SearchView +import androidx.fragment.app.Fragment +import com.google.android.material.color.MaterialColors +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentAppBarBinding +import com.krunal.demo.uicomponents.extentions.getThemeColor + +class AppBarFragment : Fragment(R.layout.fragment_app_bar) { + + private lateinit var binding: FragmentAppBarBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentAppBarBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupAppBars() + } + + private fun setupAppBars() { + binding.tbItems.inflateMenu(R.menu.toolbar_menu) + val searchItem = binding.tbSearch.menu.findItem(R.id.miSearch) + val searchView = searchItem.actionView as SearchView + searchView.isIconified = false + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/ButtonFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ButtonFragment.kt new file mode 100644 index 0000000..9d3dd7f --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ButtonFragment.kt @@ -0,0 +1,57 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.OnClickListener +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.view.children +import androidx.fragment.app.Fragment +import com.google.android.material.button.MaterialButton +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentButtonBinding + +class ButtonFragment : Fragment(R.layout.fragment_button), OnClickListener { + + private lateinit var binding: FragmentButtonBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentButtonBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupClickListener() + } + + override fun onClick(view: View?) { + (view as? MaterialButton)?.let { + Toast.makeText(requireContext(), "${it.text} clicked", Toast.LENGTH_SHORT).show() + } + } + + private fun setupClickListener() { + + binding.btnNormal.setOnClickListener(this) + binding.btnBordered.setOnClickListener(this) + binding.appCompatButton.setOnClickListener(this) + binding.btnDisabled.setOnClickListener(this) + binding.btnOutlined.setOnClickListener(this) + binding.btnText.setOnClickListener(this) + binding.imgBtnImage.setOnClickListener(this) + binding.btnInfo.setOnClickListener(this) + binding.btnGradient.setOnClickListener(this) + + binding.switchEnable.setOnCheckedChangeListener { btn, checked -> + binding.root.children + .filterNot { it == binding.switchEnable } + .forEach { it.isEnabled = checked } + btn.text = + if (checked) getString(R.string.enabled_switch) else getString(R.string.disabled_switch) + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/CheckboxFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/CheckboxFragment.kt new file mode 100644 index 0000000..329c6dd --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/CheckboxFragment.kt @@ -0,0 +1,58 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.CompoundButton +import android.widget.CompoundButton.OnCheckedChangeListener +import android.widget.Toast +import androidx.fragment.app.Fragment +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentCheckboxBinding + +class CheckboxFragment : Fragment(R.layout.fragment_checkbox), OnCheckedChangeListener { + + private lateinit var binding: FragmentCheckboxBinding + private var selectedLanguages = mutableListOf() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentCheckboxBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupListeners() + } + + private fun setupListeners() { + selectedLanguages.add("English") + selectedLanguages.add("Hindi") + binding.cbEnglish.setOnCheckedChangeListener(this) + binding.cbHindi.setOnCheckedChangeListener(this) + binding.cbGujarati.setOnCheckedChangeListener(this) + binding.cbSpanish.setOnCheckedChangeListener(this) + binding.btnSaveChanges.setOnClickListener { + Toast.makeText(requireContext(), selectedLanguages.joinToString(), Toast.LENGTH_SHORT) + .show() + } + } + + override fun onCheckedChanged(btn: CompoundButton?, isChecked: Boolean) { + val language = when (btn) { + binding.cbEnglish -> "English" + binding.cbHindi -> "Hindi" + binding.cbGujarati -> "Gujarati" + binding.cbSpanish -> "Spanish" + else -> "" + } + if (isChecked) { + selectedLanguages.add(language) + } else { + selectedLanguages.remove(language) + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/ChipFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ChipFragment.kt new file mode 100644 index 0000000..e1986a4 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ChipFragment.kt @@ -0,0 +1,55 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.widget.addTextChangedListener +import androidx.fragment.app.Fragment +import com.google.android.material.chip.Chip +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentChipBinding + +class ChipFragment : Fragment(R.layout.fragment_chip) { + + private lateinit var binding: FragmentChipBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentChipBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupChips() + } + + private fun setupChips() { + binding.etChip.addTextChangedListener { + binding.tilChip.isEndIconVisible = binding.etChip.text.isNullOrEmpty().not() + } + + binding.etChip.setOnEditorActionListener { _, _, _ -> + addChip() + true + } + + binding.tilChip.setEndIconOnClickListener { + addChip() + } + } + + private fun addChip() { + val chip = Chip(requireContext()) + chip.text = binding.etChip.text + chip.setOnCloseIconClickListener { binding.cgProgrammatically.removeView(it) } + chip.setCloseIconResource(R.drawable.ic_cross) + chip.isCloseIconVisible = true + binding.cgProgrammatically.addView(chip) + binding.etChip.text?.clear() + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/CustomViewFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/CustomViewFragment.kt new file mode 100644 index 0000000..04fd5cf --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/CustomViewFragment.kt @@ -0,0 +1,6 @@ +package com.krunal.demo.uicomponents + +import androidx.fragment.app.Fragment +import com.krunal.demo.R + +class CustomViewFragment : Fragment(R.layout.fragment_custom_view) \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/EditTextFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/EditTextFragment.kt new file mode 100644 index 0000000..a2eca4f --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/EditTextFragment.kt @@ -0,0 +1,67 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.util.Patterns +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import androidx.core.view.children +import androidx.core.widget.addTextChangedListener +import androidx.fragment.app.Fragment +import com.google.android.material.textfield.TextInputLayout +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentEditTextBinding +import com.krunal.demo.uicomponents.extentions.hideKeyboard + + +class EditTextFragment : Fragment(R.layout.fragment_edit_text) { + + private lateinit var binding: FragmentEditTextBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentEditTextBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupListeners() + } + + private fun setupListeners() { + binding.root.setOnFocusChangeListener { view, isFocused -> + if (isFocused) view.hideKeyboard() + } + + binding.etEmail.setOnFocusChangeListener { emailView, isFocused -> + val email = binding.etEmail.text.toString() + binding.emailContainer.error = if (!isFocused && !isValidEmail(email)) { + "Invalid email address" + } else { + null + } + } + + binding.etEmail.addTextChangedListener { + if (it?.length == 0) binding.emailContainer.error = null + } + + binding.btnClear.setOnClickListener { + binding.root.children.forEach { + if (it is EditText) { + it.text.clear() + } else if (it is TextInputLayout) { + it.editText?.text?.clear() + } + } + } + } + + private fun isValidEmail(email: String): Boolean { + val pattern = Patterns.EMAIL_ADDRESS + return pattern.matcher(email).matches() + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/FabFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/FabFragment.kt new file mode 100644 index 0000000..e935178 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/FabFragment.kt @@ -0,0 +1,69 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.google.android.material.snackbar.Snackbar +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentFabBinding + +class FabFragment : Fragment(R.layout.fragment_fab) { + + private lateinit var binding: FragmentFabBinding + private var visible: Boolean = false + private lateinit var snackBar: Snackbar + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentFabBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.fabAdd.setOnClickListener { + changeVisibility() + if (visible) snackBar.show() + } + setupExpandableFab() + setupSnackBar() + } + + private fun changeVisibility() { + visible = visible.not() + binding.fabAdd.setImageResource( + if (visible) R.drawable.ic_cross + else R.drawable.ic_add + ) + binding.fabEdit.visibility = if (visible) View.VISIBLE else View.INVISIBLE + binding.fabImage.visibility = if (visible) View.VISIBLE else View.INVISIBLE + } + + private fun setupExpandableFab() { + binding.fabExtended1.isChecked = true + binding.fabExtended2.isChecked = true + + binding.fabExtended1.addOnCheckedChangeListener { _, isChecked -> + binding.fabExtended1.isExtended = isChecked + } + + binding.fabExtended2.addOnCheckedChangeListener { _, isChecked -> + binding.fabExtended2.isExtended = isChecked + } + + binding.fabExtended3.setOnClickListener { + binding.fabExtended3.isExtended = binding.fabExtended3.isExtended.not() + } + } + + private fun setupSnackBar() { + snackBar = Snackbar.make(binding.root, "Fab button opened", Snackbar.LENGTH_SHORT) + snackBar.anchorView = binding.fabImage + snackBar.setAction("Close") { + changeVisibility() + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/ProgressBarFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ProgressBarFragment.kt new file mode 100644 index 0000000..3c6a3cd --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ProgressBarFragment.kt @@ -0,0 +1,41 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.krunal.demo.databinding.FragmentProgressBarBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +class ProgressBarFragment : Fragment() { + + private lateinit var binding: FragmentProgressBarBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentProgressBarBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupProgress() + } + + private fun setupProgress() { + CoroutineScope(Dispatchers.Main).launch { + repeat(10) { + delay(1000) + binding.pbCircularDeterminate.incrementProgressBy(5) + binding.pbCircularIndeterminate1.incrementProgressBy(5) + binding.pbCircularIndeterminate2.incrementProgressBy(5) + binding.pbHorizontalDeterminate.incrementProgressBy(-5) + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/RadioFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/RadioFragment.kt new file mode 100644 index 0000000..2b1e07c --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/RadioFragment.kt @@ -0,0 +1,52 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import com.google.android.material.radiobutton.MaterialRadioButton +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentRadioBinding + +class RadioFragment : Fragment(R.layout.fragment_radio) { + + private lateinit var binding: FragmentRadioBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentRadioBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + showSelected() + } + + private fun showSelected() { + val toast = Toast(requireContext()) + toast.duration = Toast.LENGTH_SHORT + toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0) + + binding.rgGender.setOnCheckedChangeListener { _, id -> + toast.setText( + when (id) { + binding.radioMale.id -> "Male" + binding.radioFemale.id -> "Female" + else -> "Other" + } + ) + toast.show() + } + binding.rgHouse.setOnCheckedChangeListener { _, id -> + toast.setText( + binding.root.findViewById(id).text + ) + toast.show() + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/SliderFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/SliderFragment.kt new file mode 100644 index 0000000..e79bbf7 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/SliderFragment.kt @@ -0,0 +1,50 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentSliderBinding + +class SliderFragment : Fragment(R.layout.fragment_slider) { + + private lateinit var binding: FragmentSliderBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentSliderBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupBars() + } + + private fun setupBars() { + binding.sliderNormal.addOnChangeListener { _, value, _ -> + binding.tvSliderNormal.text = getString(R.string.slider_value, value) + } + + binding.sliderDiscrete.addOnChangeListener { _, value, _ -> + binding.tvSliderDiscrete.text = getString(R.string.slider_value, value) + } + + binding.sliderRange.addOnChangeListener { _, _, _ -> + val values = binding.sliderRange.values + binding.tvSliderRange.text = getString( + R.string.slider_range_value, values[0], values[1] + ) + } + + binding.sliderRangeDiscrete.addOnChangeListener { _, _, _ -> + val values = binding.sliderRangeDiscrete.values + binding.tvSliderRangeDiscrete.text = getString( + R.string.slider_range_value, values[0], values[1] + ) + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/SnackBarFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/SnackBarFragment.kt new file mode 100644 index 0000000..269a034 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/SnackBarFragment.kt @@ -0,0 +1,69 @@ +package com.krunal.demo.uicomponents + +import android.graphics.Color +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import androidx.fragment.app.Fragment +import com.google.android.material.behavior.SwipeDismissBehavior +import com.google.android.material.snackbar.BaseTransientBottomBar.ANIMATION_MODE_SLIDE +import com.google.android.material.snackbar.BaseTransientBottomBar.Behavior +import com.google.android.material.snackbar.Snackbar +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentSnackbarBinding + +class SnackBarFragment : Fragment(R.layout.fragment_snackbar) { + + private lateinit var binding: FragmentSnackbarBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentSnackbarBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupSnackBars() + } + + private fun setupSnackBars() { + binding.btnShortSnackBar.setOnClickListener { + Snackbar.make(requireView(), (it as Button).text, Snackbar.LENGTH_SHORT).show() + } + + binding.btnLongSnackBar.setOnClickListener { + Snackbar.make(requireView(), (it as Button).text, Snackbar.LENGTH_LONG).show() + } + + binding.btnMultiLineSnackBar.setOnClickListener { + Snackbar.make(requireView(), (it as Button).text, Snackbar.LENGTH_SHORT).show() + } + + binding.btnActionSnackBar.setOnClickListener { + val snackBar = Snackbar.make(requireView(), (it as Button).text, Snackbar.LENGTH_LONG) + .setBackgroundTint(Color.GRAY).setActionTextColor(Color.GREEN) + snackBar.setAction("Red") { + snackBar.setBackgroundTint(Color.RED) + } + snackBar.show() + } + + binding.btnAnchorSnackBar.setOnClickListener { + Snackbar.make(requireView(), (it as Button).text, Snackbar.LENGTH_LONG) + .setAnchorView(binding.btnShortSnackBar).show() + } + + binding.btnSwipeableSnackBar.setOnClickListener { + Snackbar.make(requireView(), (it as Button).text, Snackbar.LENGTH_LONG).apply { + behavior = Behavior() + animationMode = ANIMATION_MODE_SLIDE + behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_ANY) + show() + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/SpanFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/SpanFragment.kt new file mode 100644 index 0000000..c392e46 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/SpanFragment.kt @@ -0,0 +1,103 @@ +package com.krunal.demo.uicomponents + +import android.graphics.Color +import android.os.Bundle +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.method.LinkMovementMethod +import android.text.style.ForegroundColorSpan +import android.text.style.StrikethroughSpan +import android.text.style.URLSpan +import android.text.style.UnderlineSpan +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.text.bold +import androidx.core.text.italic +import androidx.core.text.toSpannable +import androidx.fragment.app.Fragment +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentSpanBinding +import com.krunal.demo.uicomponents.extentions.addTextView +import com.krunal.demo.uicomponents.views.MyClickableSpan + +class SpanFragment : Fragment() { + + private lateinit var binding: FragmentSpanBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentSpanBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupSpans() + } + + private fun setupSpans() { + // Normal foreground color span + val textNormal = binding.tvColorSpan.text + val spanNormal = textNormal.toSpannable().apply { + setSpan( + ForegroundColorSpan(Color.RED), 8, 11, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + setSpan( + ForegroundColorSpan(Color.GREEN), 16, 21, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + binding.tvColorSpan.text = spanNormal + + // Underlined and LineThrough + val textUnderlineLineThrough = binding.tvUnderlineLineThroughSpan.text + val spanUnderlineLineThrough = textUnderlineLineThrough.toSpannable().apply { + setSpan( + UnderlineSpan(), 8, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + setSpan( + StrikethroughSpan(), 23, 34, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + binding.tvUnderlineLineThroughSpan.text = spanUnderlineLineThrough + + // Clickable Link + val spanLink = SpannableStringBuilder("This is my ").bold { + italic { + append( + "Portfolio", + URLSpan("https://krunalpatel.me"), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + }.append(" or you can visit my ").bold { + italic { + append( + "Blog", + URLSpan("https://blog.krunalpatel.me"), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + } + binding.tvLink.text = spanLink + binding.tvLink.movementMethod = LinkMovementMethod.getInstance() + + // Clickable span + val clickableText = "You can click on any word" + + SpannableStringBuilder().also { spanBuilder -> + clickableText.split(" ").forEach { word -> + spanBuilder.append("$word ", MyClickableSpan { + Toast.makeText( + requireContext(), + getString(R.string.click_toast_text, word), + Toast.LENGTH_SHORT + ).show() + }, Spannable.SPAN_INCLUSIVE_EXCLUSIVE) + } + binding.root.addTextView(spanBuilder) + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/SpinnerFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/SpinnerFragment.kt new file mode 100644 index 0000000..3f4b669 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/SpinnerFragment.kt @@ -0,0 +1,64 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Toast +import androidx.fragment.app.Fragment +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentSpinnerBinding +import com.krunal.demo.uicomponents.adapters.TimezoneAdapter + +class SpinnerFragment : Fragment(), AdapterView.OnItemSelectedListener { + + private lateinit var binding: FragmentSpinnerBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentSpinnerBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupSpinners() + } + + private fun setupSpinners() { + ArrayAdapter.createFromResource( + requireContext(), R.array.timezones, android.R.layout.simple_spinner_item + ).also { adapter -> + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + binding.spinnerNormal.adapter = adapter + binding.spinnerDialog.adapter = adapter + binding.spinnerUnderlined.adapter = adapter + binding.spinnerBordered.adapter = adapter + binding.autoCompleteTimezone.setAdapter(adapter) + binding.spinnerDisabled.adapter = adapter + } + + binding.spinnerBordered.setSelection(4, true) + binding.spinnerNormal.onItemSelectedListener = this + binding.spinnerDisabled.isEnabled = false + + val adapter = TimezoneAdapter(requireContext(), resources.getStringArray(R.array.timezones)) + binding.spinnerCustom.adapter = adapter + } + + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + Toast.makeText( + requireContext(), + binding.spinnerNormal.selectedItem.toString(), + Toast.LENGTH_SHORT + ).show() + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + Toast.makeText(requireContext(), getString(R.string.nothing_selected), Toast.LENGTH_SHORT) + .show() + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/ThemeFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ThemeFragment.kt new file mode 100644 index 0000000..7fc27ed --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ThemeFragment.kt @@ -0,0 +1,57 @@ +package com.krunal.demo.uicomponents + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import com.krunal.demo.databinding.FragmentThemeBinding +import com.krunal.demo.uicomponents.adapters.ThemeAdapter +import com.krunal.demo.uicomponents.extentions.isDarkMode +import com.krunal.demo.uicomponents.helpers.ThemeHelper +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +class ThemeFragment : Fragment() { + + private lateinit var binding: FragmentThemeBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentThemeBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupTheme() + } + + private fun setupTheme() { + val themes = ThemeHelper.getThemes(requireContext(), requireActivity().isDarkMode) + val themeAdapter = ThemeAdapter( + requireContext(), themes + ) + + binding.gvTheme.adapter = themeAdapter + binding.gvTheme.setItemChecked(ThemeHelper.getThemeAccent().ordinal, true) + lifecycleScope.launch { + delay(100) + + binding.gvTheme.setItemChecked(ThemeHelper.getThemeAccent().ordinal, true) + binding.gvTheme.setSelection(ThemeHelper.getThemeAccent().ordinal) + Log.d("Tag", ThemeHelper.getThemeAccent().ordinal.toString()) + Log.d("Tag", binding.gvTheme.selectedItemPosition.toString()) + } + // binding.gvTheme.selectedView.performClick() // selectedView is null + + binding.gvTheme.setOnItemClickListener { _, _, position, _ -> + ThemeHelper.setThemeAccent(themes[position].accentColor) + // TODO: Restart activity +// activity?.recreate() + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/ToastFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ToastFragment.kt new file mode 100644 index 0000000..b6822f5 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/ToastFragment.kt @@ -0,0 +1,74 @@ +package com.krunal.demo.uicomponents + +import android.graphics.Color +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import android.widget.Toast +import androidx.core.content.res.ResourcesCompat +import androidx.fragment.app.Fragment +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentToastBinding + +class ToastFragment : Fragment(R.layout.fragment_toast) { + private lateinit var binding: FragmentToastBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentToastBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupButtons() + } + + // Set click listeners for buttons to show toast + private fun setupButtons() { + // Short toast + binding.btnShortToast.setOnClickListener { + Toast.makeText(requireContext(), "This is short Toast", Toast.LENGTH_SHORT).show() + } + + // Long toast + binding.btnLongToast.setOnClickListener { + Toast.makeText(requireContext(), "This is long Toast", Toast.LENGTH_LONG).show() + } + + // Custom short success toast + binding.btnSuccessToast.setOnClickListener { + showToast(R.drawable.ic_check, "Custom success toast", backgroundColor = Color.GREEN) + } + + // Custom long error toast + binding.btnErrorToast.setOnClickListener { + showToast(R.drawable.ic_cross, "Custom error toast", Toast.LENGTH_LONG, Color.RED) + } + } + + // Make and show custom toast + private fun showToast(iconId: Int, text: String, length: Int = Toast.LENGTH_SHORT, backgroundColor: Int = Color.GRAY) { + val toastView = layoutInflater.inflate(R.layout.custom_toast, requireActivity().findViewById(R.id.customToast)) + val imgViewStatus = toastView.findViewById(R.id.imgViewStatus) + val tvMessage = toastView.findViewById(R.id.tvMessage) + + toastView.setBackgroundColor(backgroundColor) + imgViewStatus.setImageResource(iconId) + tvMessage.text = text + + Toast(requireContext()).apply { + setGravity(Gravity.CENTER, 0, 0) + duration = length + view = toastView + show() + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/sheets/OperationsBottomSheetFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/sheets/OperationsBottomSheetFragment.kt new file mode 100644 index 0000000..693b651 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/sheets/OperationsBottomSheetFragment.kt @@ -0,0 +1,35 @@ +package com.krunal.demo.uicomponents.sheets + +import android.annotation.SuppressLint +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.krunal.demo.databinding.FragmentOperationsBottomSheetBinding + +class OperationsBottomSheetFragment : BottomSheetDialogFragment() { + + private lateinit var binding: FragmentOperationsBottomSheetBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentOperationsBottomSheetBinding.inflate(layoutInflater) + return binding.root + } + + @SuppressLint("RestrictedApi", "VisibleForTests") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = super.onCreateDialog(savedInstanceState) + + (dialog as BottomSheetDialog).behavior.apply { + isDraggable = true + state = BottomSheetBehavior.STATE_EXPANDED + } + return dialog + } +} \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/fragment_app_bar.xml b/Demo/app/src/main/res/layout/fragment_app_bar.xml new file mode 100644 index 0000000..12ac20d --- /dev/null +++ b/Demo/app/src/main/res/layout/fragment_app_bar.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/fragment_button.xml b/Demo/app/src/main/res/layout/fragment_button.xml new file mode 100644 index 0000000..d333081 --- /dev/null +++ b/Demo/app/src/main/res/layout/fragment_button.xml @@ -0,0 +1,149 @@ + + + +