diff --git a/Demo/app/build.gradle b/Demo/app/build.gradle index 806d1aa..8382565 100644 --- a/Demo/app/build.gradle +++ b/Demo/app/build.gradle @@ -1,6 +1,7 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' } android { @@ -24,26 +25,30 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } buildFeatures { viewBinding true + dataBinding true } } dependencies { - - implementation 'androidx.core:core-ktx:1.9.0' + def lifecycle_version = "2.6.1" + implementation 'androidx.core:core-ktx:1.10.1' implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' + implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" - implementation 'androidx.fragment:fragment-ktx:1.5.5' + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + implementation 'androidx.fragment:fragment-ktx:1.5.7' + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" + implementation "androidx.core:core-splashscreen:1.0.1" + implementation "androidx.preference:preference-ktx:1.2.0" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/Demo/app/src/main/AndroidManifest.xml b/Demo/app/src/main/AndroidManifest.xml index 3410a78..4858a5c 100644 --- a/Demo/app/src/main/AndroidManifest.xml +++ b/Demo/app/src/main/AndroidManifest.xml @@ -3,14 +3,34 @@ xmlns:tools="http://schemas.android.com/tools"> + + + + + + + + + + + + + + diff --git a/Demo/app/src/main/java/com/krunal/demo/DemoApplication.kt b/Demo/app/src/main/java/com/krunal/demo/DemoApplication.kt new file mode 100644 index 0000000..45e7964 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/DemoApplication.kt @@ -0,0 +1,22 @@ +package com.krunal.demo + +import android.app.Application +import com.krunal.demo.uicomponents.helpers.PreferenceHelper + +class DemoApplication: Application() { + + override fun onCreate() { + super.onCreate() + + instance = this + + /** + * Initialize [PreferenceHelper] + */ + PreferenceHelper.initialize(applicationContext) + } + + companion object { + lateinit var instance: Application + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/MainActivity.kt b/Demo/app/src/main/java/com/krunal/demo/MainActivity.kt index bd7b820..56439e4 100644 --- a/Demo/app/src/main/java/com/krunal/demo/MainActivity.kt +++ b/Demo/app/src/main/java/com/krunal/demo/MainActivity.kt @@ -12,15 +12,18 @@ import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { - lateinit var binding: ActivityMainBinding - lateinit var mPlayer: MediaPlayer + private lateinit var binding: ActivityMainBinding + private lateinit var mPlayer: MediaPlayer private val viewModel: MainActivityViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + + binding.mainViewModel = viewModel + + // Update time without data binding CoroutineScope(Dispatchers.IO).launch { viewModel.timeFlow.collectLatest { time -> runOnUiThread { diff --git a/Demo/app/src/main/java/com/krunal/demo/MainActivityViewModel.kt b/Demo/app/src/main/java/com/krunal/demo/MainActivityViewModel.kt index 31952be..2730fe0 100644 --- a/Demo/app/src/main/java/com/krunal/demo/MainActivityViewModel.kt +++ b/Demo/app/src/main/java/com/krunal/demo/MainActivityViewModel.kt @@ -2,15 +2,16 @@ package com.krunal.demo import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import java.text.SimpleDateFormat -import java.util.* +import java.util.Date class MainActivityViewModel : ViewModel() { - private val _timeFlow = MutableSharedFlow() - val timeFlow: MutableSharedFlow = _timeFlow + private val _timeFlow = MutableStateFlow(null) + val timeFlow: StateFlow = _timeFlow init { start() diff --git a/Demo/app/src/main/java/com/krunal/demo/UIComponentsActivity.kt b/Demo/app/src/main/java/com/krunal/demo/UIComponentsActivity.kt new file mode 100644 index 0000000..d03fa42 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/UIComponentsActivity.kt @@ -0,0 +1,31 @@ +package com.krunal.demo + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import com.krunal.demo.uicomponents.ButtonFragment +import com.krunal.demo.uicomponents.CoordinatorLayoutFragment +import com.krunal.demo.uicomponents.ThemeFragment +import com.krunal.demo.uicomponents.cardscreen.CardFragment +import com.krunal.demo.uicomponents.helpers.ThemeHelper + +class UIComponentsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + installSplashScreen() + setupTheme() + setContentView(R.layout.activity_uicomponents) + setupFragment() + } + + private fun setupTheme() { + setTheme(ThemeHelper.getThemeResource(ThemeHelper.getThemeAccent())) + } + + private fun setupFragment() { + supportFragmentManager.beginTransaction() + .replace(R.id.uiComponentsFragment, CoordinatorLayoutFragment()) + .commit() + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/StackExchangeActivity.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/StackExchangeActivity.kt new file mode 100644 index 0000000..9e483a5 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/StackExchangeActivity.kt @@ -0,0 +1,95 @@ +package com.krunal.demo.stackexchange + +import android.graphics.Color +import android.os.Bundle +import android.view.ViewTreeObserver.OnGlobalLayoutListener +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import com.google.android.material.badge.BadgeDrawable +import com.google.android.material.badge.BadgeUtils +import com.google.android.material.badge.ExperimentalBadgeUtils +import com.krunal.demo.R +import com.krunal.demo.databinding.ActivityStackExchangeBinding +import com.krunal.demo.stackexchange.exchange.ExchangeFragment +import com.krunal.demo.stackexchange.market.MarketFragment +import com.krunal.demo.stackexchange.wallet.WalletFragment +import com.krunal.demo.uicomponents.extentions.getThemeColor +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch + + +class StackExchangeActivity : AppCompatActivity() { + + private lateinit var binding: ActivityStackExchangeBinding + private val viewModel: StackExchangeActivityViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityStackExchangeBinding.inflate(layoutInflater) + setContentView(binding.root) + binding.viewModel = viewModel + setupUI() + setupBottomNavigation() + } + + private fun setupUI() { + + setupAppBar() + + binding.apply { + fabExchange.setOnClickListener { + bottomNavigation.selectedItemId = R.id.actionExchange + } + } + } + + @androidx.annotation.OptIn(ExperimentalBadgeUtils::class) + private fun setupAppBar() { + setSupportActionBar(binding.toolbar) + val badge = BadgeDrawable.create(this).apply { + badgeTextColor = getThemeColor(com.google.android.material.R.attr.colorOnSecondary) + backgroundColor = getThemeColor(com.google.android.material.R.attr.colorSecondary) + } + + binding.notificationView.apply { + clipToOutline = false + bringToFront() + viewTreeObserver.addOnGlobalLayoutListener { + BadgeUtils.attachBadgeDrawable( + badge, + binding.notificationView + ) + } + } + + lifecycleScope.launch { + viewModel.notificationCount.collectLatest { + if (it == 0) { + badge.isVisible = false + } else { + badge.number = it + } + } + } + } + + private fun setupBottomNavigation() { + changeFragment(MarketFragment()) + + binding.bottomNavigation.setOnItemSelectedListener { menuItem -> + val fragment = when (menuItem.itemId) { + R.id.actionMarket -> MarketFragment() + R.id.actionExchange -> ExchangeFragment() + else -> WalletFragment() + } + changeFragment(fragment) + return@setOnItemSelectedListener true + } + } + + private fun changeFragment(fragment: Fragment) { + supportFragmentManager.beginTransaction().replace(R.id.fragmentContainer, fragment).commit() + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/StackExchangeActivityViewModel.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/StackExchangeActivityViewModel.kt new file mode 100644 index 0000000..fc60938 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/StackExchangeActivityViewModel.kt @@ -0,0 +1,11 @@ +package com.krunal.demo.stackexchange + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class StackExchangeActivityViewModel : ViewModel() { + + private val _notificationCount: MutableStateFlow = MutableStateFlow(6) + val notificationCount: StateFlow = _notificationCount +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/adapters/PortfolioAdapter.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/adapters/PortfolioAdapter.kt new file mode 100644 index 0000000..fdcc311 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/adapters/PortfolioAdapter.kt @@ -0,0 +1,40 @@ +package com.krunal.demo.stackexchange.adapters + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.krunal.demo.databinding.ItemPortfolioBinding +import com.krunal.demo.stackexchange.models.ShareDetails + +class PortfolioAdapter : RecyclerView.Adapter() { + + private val shareDetailsList: MutableList = mutableListOf() + + class PortfolioViewHolder(private val binding: ItemPortfolioBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bind(shareDetails: ShareDetails) { + binding.shareDetails = shareDetails + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PortfolioViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val binding = ItemPortfolioBinding.inflate(layoutInflater, parent, false) + return PortfolioViewHolder(binding) + } + + override fun getItemCount(): Int = shareDetailsList.count() + + override fun onBindViewHolder(holder: PortfolioViewHolder, position: Int) { + holder.bind(shareDetailsList[position]) + } + + @SuppressLint("NotifyDataSetChanged") + fun submitList(list: List) { + shareDetailsList.clear() + shareDetailsList.addAll(list) + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/adapters/ShareDetailsAdapter.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/adapters/ShareDetailsAdapter.kt new file mode 100644 index 0000000..b474aa7 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/adapters/ShareDetailsAdapter.kt @@ -0,0 +1,40 @@ +package com.krunal.demo.stackexchange.adapters + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.krunal.demo.databinding.ItemShareDetailsBinding +import com.krunal.demo.stackexchange.models.ShareDetails + +class ShareDetailsAdapter : RecyclerView.Adapter() { + + private val shareDetailsList: MutableList = mutableListOf() + + class ShareDetailsViewHolder(private val binding: ItemShareDetailsBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bind(shareDetails: ShareDetails) { + binding.shareDetails = shareDetails + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShareDetailsViewHolder { + val layoutInflater = LayoutInflater.from(parent.context) + val binding = ItemShareDetailsBinding.inflate(layoutInflater, parent, false) + return ShareDetailsViewHolder(binding) + } + + override fun getItemCount(): Int = shareDetailsList.count() + + override fun onBindViewHolder(holder: ShareDetailsViewHolder, position: Int) { + holder.bind(shareDetailsList[position]) + } + + @SuppressLint("NotifyDataSetChanged") + fun submitList(list: List) { + shareDetailsList.clear() + shareDetailsList.addAll(list) + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/exchange/ExchangeFragment.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/exchange/ExchangeFragment.kt new file mode 100644 index 0000000..d62a663 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/exchange/ExchangeFragment.kt @@ -0,0 +1,72 @@ +package com.krunal.demo.stackexchange.exchange + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import com.krunal.demo.databinding.FragmentExchangeBinding + +class ExchangeFragment : Fragment() { + + private lateinit var binding: FragmentExchangeBinding + private val viewModel: ExchangeFragmentViewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentExchangeBinding.inflate(layoutInflater) + binding.lifecycleOwner = viewLifecycleOwner + binding.viewModel = viewModel + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupListener() + + } + + private fun setupListener() { + binding.cardExchange.exchangeView.cardAmount.apply { + imgBtnAdd.setOnClickListener { + viewModel.changeExchangeAmount(0.1F) + } + + imgBtnMinus.setOnClickListener { + viewModel.changeExchangeAmount(-0.1F) + } + + imgBtnAdd.setOnLongClickListener { + viewModel.changeExchangeAmount(1F) + true + } + + imgBtnMinus.setOnLongClickListener { + viewModel.changeExchangeAmount(-1F) + true + } + } + + binding.cardExchange.receiverView.cardAmount.apply { + imgBtnAdd.setOnClickListener { + viewModel.changeReceiveAmount(0.1F) + } + + imgBtnMinus.setOnClickListener { + viewModel.changeReceiveAmount(-0.1F) + } + + imgBtnAdd.setOnLongClickListener { + viewModel.changeReceiveAmount(1F) + true + } + + imgBtnMinus.setOnLongClickListener { + viewModel.changeReceiveAmount(-1F) + true + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/exchange/ExchangeFragmentViewModel.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/exchange/ExchangeFragmentViewModel.kt new file mode 100644 index 0000000..f548197 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/exchange/ExchangeFragmentViewModel.kt @@ -0,0 +1,75 @@ +package com.krunal.demo.stackexchange.exchange + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.krunal.demo.stackexchange.models.ExchangeModel +import com.krunal.demo.stackexchange.models.ShareDetails +import com.krunal.demo.stackexchange.models.TitleCardModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch + +class ExchangeFragmentViewModel : ViewModel() { + + private val _dateTime: MutableStateFlow = MutableStateFlow("") + val dateTime: StateFlow = _dateTime + + private val _titleCardModel: MutableStateFlow = MutableStateFlow(null) + val titleCardModel: StateFlow = _titleCardModel + + private val _exchangeModel: MutableStateFlow = MutableStateFlow(null) + val exchangeModel: StateFlow = _exchangeModel + + init { + setupInitialValues() + } + + private fun setupInitialValues() { + viewModelScope.launch { + _dateTime.emit("OCTOBER 18, TUESDAY 3:19:23 PM") + + _exchangeModel.emit( + ExchangeModel( + ShareDetails.dummyData[0], ShareDetails.dummyData[2], 32.80F + ) + ) + + exchangeModel.collectLatest { + it?.receive?.let { share -> + _titleCardModel.emit( + TitleCardModel("EXCHANGE FOR", share.value, share.name) + ) + } + } + } + } + + fun changeExchangeAmount(number: Float) { + viewModelScope.launch { + exchangeModel.value?.exchange?.let { exchange -> + _exchangeModel.emit( + exchangeModel.value?.copy( + exchange = exchange.copy( + value = exchange.value + number + ) + ) + ) + } + } + } + + fun changeReceiveAmount(number: Float) { + viewModelScope.launch { + exchangeModel.value?.receive?.let { receive -> + _exchangeModel.emit( + exchangeModel.value?.copy( + receive = receive.copy( + value = receive.value + number + ) + ) + ) + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/market/MarketFragment.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/market/MarketFragment.kt new file mode 100644 index 0000000..abb9553 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/market/MarketFragment.kt @@ -0,0 +1,64 @@ +package com.krunal.demo.stackexchange.market + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import com.krunal.demo.databinding.FragmentMarketBinding +import com.krunal.demo.stackexchange.adapters.ShareDetailsAdapter +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch + +class MarketFragment : Fragment() { + + private lateinit var binding: FragmentMarketBinding + private val viewModel: MarketFragmentViewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentMarketBinding.inflate(layoutInflater) + binding.lifecycleOwner = viewLifecycleOwner + binding.viewModel = viewModel + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupUI() + } + + private fun setupUI() { + binding.priceLayout1.imgBtnAdd.setOnClickListener { + viewModel.changeYourPriceValue(true) + } + + binding.priceLayout1.imgBtnMinus.setOnClickListener { + viewModel.changeYourPriceValue(false) + } + + binding.priceLayout2.imgBtnAdd.setOnClickListener { + viewModel.changeAmountValue(true) + } + + binding.priceLayout2.imgBtnMinus.setOnClickListener { + viewModel.changeAmountValue(false) + } + + setupShareDetails() + } + + private fun setupShareDetails() { + val shareDetailsAdapter = ShareDetailsAdapter() + binding.rvShareDetails.adapter = shareDetailsAdapter + + lifecycleScope.launch { + viewModel.shareDetails.collectLatest { list -> + list?.let { shareDetailsAdapter.submitList(it) } + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/market/MarketFragmentViewModel.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/market/MarketFragmentViewModel.kt new file mode 100644 index 0000000..db70d0a --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/market/MarketFragmentViewModel.kt @@ -0,0 +1,67 @@ +package com.krunal.demo.stackexchange.market + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.krunal.demo.stackexchange.models.ShareDetails +import com.krunal.demo.stackexchange.models.TitleCardModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +class MarketFragmentViewModel : ViewModel() { + + private val _titleCardModel: MutableStateFlow = MutableStateFlow(null) + val titleCardModel: StateFlow = _titleCardModel + + private val _shareDetails: MutableStateFlow?> = MutableStateFlow(null) + val shareDetails: StateFlow?> = _shareDetails + + private val _yourPrice: MutableStateFlow = MutableStateFlow(0F) + val yourPrice: StateFlow = _yourPrice + + private val _amount: MutableStateFlow = MutableStateFlow(0F) + val amount: StateFlow = _amount + + + init { + setupInitialValues() + } + + private fun setupInitialValues() { + viewModelScope.launch { + _titleCardModel.emit( + TitleCardModel("BUY", 50000F, "SHIB") + ) + + _shareDetails.emit(ShareDetails.dummyData) + + _yourPrice.emit(0.268800F) + _amount.emit(50000F) + } + } + + fun changeYourPriceValue(increase: Boolean = false) { + viewModelScope.launch { + _yourPrice.emit( + if (increase) { + yourPrice.value + 0.1F + } else { + yourPrice.value - 0.1F + } + ) + } + } + + fun changeAmountValue(increase: Boolean = false) { + viewModelScope.launch { + _amount.emit( + if (increase) { + amount.value + 1 + } else { + amount.value - 1 + } + ) + } + } + +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/ExchangeModel.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/ExchangeModel.kt new file mode 100644 index 0000000..4b61854 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/ExchangeModel.kt @@ -0,0 +1,5 @@ +package com.krunal.demo.stackexchange.models + +data class ExchangeModel( + val exchange: ShareDetails, val receive: ShareDetails, val transactionCost: Float +) diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/ShareDetails.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/ShareDetails.kt new file mode 100644 index 0000000..e70fe15 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/ShareDetails.kt @@ -0,0 +1,52 @@ +package com.krunal.demo.stackexchange.models + +import androidx.annotation.DrawableRes +import com.krunal.demo.R + +data class ShareDetails( + val name: String, + val parentName: String, + @DrawableRes val logo: Int, + val amountDiff: Float, + val isPositive: Boolean = true, + val value: Float, + val price: Float, + val percentage: Int, + val dateTime: String +) { + companion object { + val dummyData: List = listOf( + ShareDetails( + "Avax", + "AVALANCHE", + R.drawable.ic_avax_logo, + 0.187200F, + true, + 47.38662F, + 6022.84F, + 37, + "2021-10-19 8:51" + ), ShareDetails( + "Rose", + "OASIS NETWORK", + R.drawable.ic_rose_logo, + 0.187200F, + false, + 47.38662F, + 6022.84F, + 72, + "2021-10-19 16:13" + ), ShareDetails( + "Link", + "CHAINLINK", + R.drawable.ic_link_logo, + 127.10F, + false, + 50.45872F, + 6022.84F, + 29, + "2021-10-19 10:00" + ) + ) + } +} diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/TitleCardModel.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/TitleCardModel.kt new file mode 100644 index 0000000..6c6c0f5 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/TitleCardModel.kt @@ -0,0 +1,5 @@ +package com.krunal.demo.stackexchange.models + +data class TitleCardModel( + val textBeforeAmount: String, val amount: Float, val textAfterAmount: String +) \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/Wallet.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/Wallet.kt new file mode 100644 index 0000000..86b7ee5 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/models/Wallet.kt @@ -0,0 +1,19 @@ +package com.krunal.demo.stackexchange.models + +data class Wallet( + val amount: String, + val amountDiff: String, + val diffPercentage: String, + val maxValue: String, + val isPositive: Boolean = true +) { + companion object { + val dummyWallet: Wallet + get() = Wallet( + "182.057,83", + "31.800,52", + "21,1", + "185.701,18" + ) + } +} diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/utils/Formatter.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/utils/Formatter.kt new file mode 100644 index 0000000..5e47231 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/utils/Formatter.kt @@ -0,0 +1,13 @@ +package com.krunal.demo.stackexchange.utils + +import java.text.NumberFormat +import java.util.Locale + +object Formatter { + private val formatter = NumberFormat.getCurrencyInstance(Locale.GERMANY).apply { + minimumFractionDigits = 0 + } + + @JvmStatic + fun euroFormat(number: Float): String = formatter.format(number).removeSuffix("€") +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/Divider.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/Divider.kt new file mode 100644 index 0000000..6638ed5 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/Divider.kt @@ -0,0 +1,83 @@ +package com.krunal.demo.stackexchange.views + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View +import com.krunal.demo.R +import com.krunal.demo.uicomponents.extentions.getThemeColor + +class Divider @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = R.attr.dividerStyle, +) : View(context, attrs, defStyleAttr) { + + private val progressPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) + private val backgroundPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG) + private var lineHeight = 5F + private var progressColor = + Color.RED + private var backgroundColor = + context.getThemeColor(com.google.android.material.R.attr.colorSurfaceDim) + private var progress = 6F + private var centerPositionY: Float = 5F + + + init { + setupAttributes(attrs, defStyleAttr) + setupPaint() + } + + private fun setupAttributes(attrs: AttributeSet?, defStyle: Int) { + context.theme.obtainStyledAttributes(attrs, R.styleable.Divider, defStyle, 0).apply { + backgroundColor = getColor(R.styleable.Divider_backgroundColor, backgroundColor) + progressColor = getColor(R.styleable.Divider_progressColor, progressColor) + progress = getDimension(R.styleable.Divider_progress, progress) + lineHeight = getDimension(R.styleable.Divider_lineHeight, lineHeight) + recycle() + } + } + + private fun setupPaint() { + progressPaint.apply { + color = progressColor + strokeWidth = lineHeight + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + } + + backgroundPaint.apply { + color = backgroundColor + strokeWidth = lineHeight + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + } + } + + override fun onDraw(canvas: Canvas) { + centerPositionY = height / 2F + drawBackground(canvas) + drawProgress(canvas) + + super.onDraw(canvas) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(widthMeasureSpec, lineHeight.toInt()) + } + + private fun drawBackground(canvas: Canvas) { + canvas.drawLine(0f, centerPositionY, width.toFloat(), centerPositionY, backgroundPaint) + } + + private fun drawProgress(canvas: Canvas) { + // Draw left progress + canvas.drawLine(0f, centerPositionY, progress, centerPositionY, progressPaint) + + // Draw right progress + canvas.drawLine(width - progress, centerPositionY, width.toFloat(), centerPositionY, progressPaint) + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/DonutChart.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/DonutChart.kt new file mode 100644 index 0000000..88230bc --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/DonutChart.kt @@ -0,0 +1,123 @@ +package com.krunal.demo.stackexchange.views + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.View +import com.krunal.demo.R +import com.krunal.demo.uicomponents.extentions.toFloat +import com.krunal.demo.uicomponents.extentions.toInt +import kotlin.math.min + +class DonutChart @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : View(context, attrs, defStyleAttr) { + + private var colors: List = + listOf(Color.YELLOW, Color.RED, Color.GREEN, Color.CYAN)//emptyList() + private var values: List = listOf(25F, 30F, 10F, 35F)//emptyList() + private var donutSpacing = 5F + private val angles by lazy { values.map { it.toAngle() - donutSpacing } } + private var lineWidth = 15F + private var totalValue = 100 + private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG) + private val rectF = RectF() + private var currentAngle = 270f // Make initial angle top + private var radius = -1F + + init { + setupAttributes(attrs, defStyleAttr) + setupPaint() + } + + private fun setupAttributes(attrs: AttributeSet?, defStyleAttr: Int) { + context.theme.obtainStyledAttributes(attrs, R.styleable.DonutChart, defStyleAttr, 0).apply { + donutSpacing = getDimension(R.styleable.DonutChart_donutSpacing, donutSpacing) + lineWidth = getDimension(R.styleable.DonutChart_strokeWidth, lineWidth) + totalValue = getInt(R.styleable.DonutChart_totalValue, totalValue) + radius = getDimension(R.styleable.DonutChart_radius, radius) + + if (hasValue(R.styleable.DonutChart_values)) { + val valuesId = getResourceId(R.styleable.DonutChart_values, 0) + resources.obtainTypedArray(valuesId).also { typedArray -> + values = typedArray.toFloat() + typedArray.recycle() + } + } + + if (hasValue(R.styleable.DonutChart_colors)) { + val colorId = getResourceId(R.styleable.DonutChart_colors, 0) + resources.obtainTypedArray(colorId).also { typedArray -> + colors = typedArray.toInt() + typedArray.recycle() + } + } + recycle() + } + } + + private fun setupPaint() { + linePaint.apply { + strokeWidth = lineWidth + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val desiredWidth = suggestedMinimumWidth + paddingLeft + paddingRight + val desiredHeight = suggestedMinimumHeight + paddingTop + paddingBottom + + setMeasuredDimension( + resolveSize(desiredWidth, widthMeasureSpec), + resolveSize(desiredHeight, heightMeasureSpec) + ) + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + radius = if (radius == DEFAULT_RADIUS) { + val minSize = min(height, width) + minSize / 2 - (minSize/10F) + } else { + radius + } + super.onLayout(changed, left, top, right, bottom) + } + + override fun onDraw(canvas: Canvas) { + setupRectF() + drawGraph(canvas) + super.onDraw(canvas) + } + + private fun setupRectF() { + val centerX = width / 2F + val centerY = height / 2F + rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius) + } + + private fun drawGraph(canvas: Canvas) { + angles.forEachIndexed { index, value -> + linePaint.color = colors[index] + drawDonut(canvas, linePaint, currentAngle, value) + currentAngle += value + donutSpacing + } + } + + private fun drawDonut(canvas: Canvas, paint: Paint, startAngle: Float, sweepAngle: Float) { + canvas.drawArc(rectF, startAngle, sweepAngle, false, paint) + } + + private fun Float.toAngle(): Float = ((this * 360F) / totalValue) + + companion object { + private const val TAG = "DonutChart" + private const val DEFAULT_RADIUS = -1F + private const val CHART_PADDING = 100F + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/HexagonFloatingActionButton.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/HexagonFloatingActionButton.kt new file mode 100644 index 0000000..f13d4ad --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/views/HexagonFloatingActionButton.kt @@ -0,0 +1,39 @@ +package com.krunal.demo.stackexchange.views + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Outline +import android.graphics.Path +import android.util.AttributeSet +import android.view.View +import android.view.ViewOutlineProvider +import com.google.android.material.floatingactionbutton.FloatingActionButton + +class HexagonFloatingActionButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0): FloatingActionButton(context, attrs, defStyleAttr) { + + private val hexagonPath = Path() + + override fun onDraw(canvas: Canvas?) { +// clipHexagonShape(canvas) + super.onDraw(canvas) + } + + private fun clipHexagonShape(canvas: Canvas?) { + hexagonPath.apply { + moveTo(size / 2f, 0f) + lineTo(size.toFloat(), height / 3f) + lineTo(size.toFloat(), height * 2 / 3f) + lineTo(size / 2f, height.toFloat()) + lineTo(0f, height * 2 / 3f) + lineTo(0f, height / 3f) + close() + } + canvas?.clipPath(hexagonPath) + clipToOutline = true + outlineProvider = object : ViewOutlineProvider() { + override fun getOutline(view: View, outline: Outline) { + outline.setConvexPath(hexagonPath) + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/wallet/WalletFragment.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/wallet/WalletFragment.kt new file mode 100644 index 0000000..cf5f174 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/wallet/WalletFragment.kt @@ -0,0 +1,64 @@ +package com.krunal.demo.stackexchange.wallet + +import android.graphics.Rect +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.RecyclerView +import com.krunal.demo.databinding.FragmentWalletBinding +import com.krunal.demo.stackexchange.adapters.PortfolioAdapter +import com.krunal.demo.stackexchange.models.ShareDetails +import kotlinx.coroutines.launch + +class WalletFragment : Fragment() { + + private lateinit var binding: FragmentWalletBinding + private val viewModel: WalletFragmentViewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentWalletBinding.inflate(layoutInflater) + binding.lifecycleOwner = viewLifecycleOwner + binding.viewModel = viewModel + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupPortfolio() + } + + private fun setupPortfolio() { + val adapter = PortfolioAdapter() + binding.rvPortfolio.adapter = adapter + binding.rvPortfolio.addItemDecoration(object: DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL) { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + outRect.top += 16 + outRect.bottom += 16 + } + }) + + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + viewModel.portfolios.collect { list -> + list?.let { adapter.submitList(list) } + } + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/stackexchange/wallet/WalletFragmentViewModel.kt b/Demo/app/src/main/java/com/krunal/demo/stackexchange/wallet/WalletFragmentViewModel.kt new file mode 100644 index 0000000..fb08632 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/stackexchange/wallet/WalletFragmentViewModel.kt @@ -0,0 +1,34 @@ +package com.krunal.demo.stackexchange.wallet + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.krunal.demo.stackexchange.models.ShareDetails +import com.krunal.demo.stackexchange.models.Wallet +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +class WalletFragmentViewModel : ViewModel() { + + private val _portfolios: MutableStateFlow?> = MutableStateFlow(ShareDetails.dummyData) + val portfolios: StateFlow?> = _portfolios + + private val _wallet: MutableStateFlow = MutableStateFlow(null) + val wallet: StateFlow = _wallet + + init { + setupInitialValues() + } + + private fun setupInitialValues() { + viewModelScope.launch { + _portfolios.emit( + ShareDetails.dummyData + ) + + _wallet.emit( + Wallet.dummyWallet + ) + } + } +} \ No newline at end of file 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/CoordinatorLayoutFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/CoordinatorLayoutFragment.kt new file mode 100644 index 0000000..246e9ba --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/CoordinatorLayoutFragment.kt @@ -0,0 +1,45 @@ +package com.krunal.demo.uicomponents + +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 +import com.google.android.material.snackbar.Snackbar +import com.krunal.demo.databinding.FragmentCoordinatorLayoutBinding + +class CoordinatorLayoutFragment : Fragment() { + + private lateinit var binding: FragmentCoordinatorLayoutBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentCoordinatorLayoutBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupUI() + } + + private fun setupUI() { + setupSnackBar() + } + + private fun setupSnackBar() { + binding.btnSwipeableSnackBar.setOnClickListener { + Snackbar.make(requireView(), (it as Button).text, Snackbar.LENGTH_LONG).apply { + behavior = BaseTransientBottomBar.Behavior() + animationMode = BaseTransientBottomBar.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/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/FrameLayoutFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/FrameLayoutFragment.kt new file mode 100644 index 0000000..c833914 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/FrameLayoutFragment.kt @@ -0,0 +1,8 @@ +package com.krunal.demo.uicomponents + +import androidx.fragment.app.Fragment +import com.krunal.demo.R + +class FrameLayoutFragment : Fragment(R.layout.fragment_frame_layout) { + +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/LinearLayoutFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/LinearLayoutFragment.kt new file mode 100644 index 0000000..00d1a6b --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/LinearLayoutFragment.kt @@ -0,0 +1,8 @@ +package com.krunal.demo.uicomponents + +import androidx.fragment.app.Fragment +import com.krunal.demo.R + +class LinearLayoutFragment : Fragment(R.layout.fragment_linear_layout) { + +} \ 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/RelativeLayoutFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/RelativeLayoutFragment.kt new file mode 100644 index 0000000..871b5cf --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/RelativeLayoutFragment.kt @@ -0,0 +1,8 @@ +package com.krunal.demo.uicomponents + +import androidx.fragment.app.Fragment +import com.krunal.demo.R + +class RelativeLayoutFragment : Fragment(R.layout.fragment_relative_layout) { + +} \ 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/TabLayoutFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/TabLayoutFragment.kt new file mode 100644 index 0000000..838a944 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/TabLayoutFragment.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.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentTabLayoutBinding +import com.krunal.demo.uicomponents.adapters.ViewPagerAdapter + +class TabLayoutFragment : Fragment(R.layout.fragment_tab_layout) { + + private lateinit var binding: FragmentTabLayoutBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentTabLayoutBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupTabsWithPager() + } + + private fun setupTabsWithPager() { + val viewPagerAdapter = ViewPagerAdapter(requireActivity()) + binding.viewPager.adapter = viewPagerAdapter + TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position -> + tab.text = when (position) { + 0 -> "Home" + 1 -> "Chat" + else -> "Call" + } + }.attach() + } +} \ 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/adapters/ThemeAdapter.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/ThemeAdapter.kt new file mode 100644 index 0000000..81716d3 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/ThemeAdapter.kt @@ -0,0 +1,33 @@ +package com.krunal.demo.uicomponents.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView +import com.google.android.material.card.MaterialCardView +import com.krunal.demo.R +import com.krunal.demo.uicomponents.models.Theme + +class ThemeAdapter( + private val context: Context, + private val themes: List +) : BaseAdapter() { + override fun getCount(): Int = themes.size + + override fun getItem(position: Int): Any? = null + + override fun getItemId(position: Int): Long = 0 + + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.theme_layout, null) + val tvName = view.findViewById(R.id.tvThemeName) + val viewTheme = view.findViewById(R.id.cardThemeColor) + + tvName.text = themes[position].name + viewTheme.setCardBackgroundColor(themes[position].color) + return view + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/TimezoneAdapter.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/TimezoneAdapter.kt new file mode 100644 index 0000000..4f445da --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/TimezoneAdapter.kt @@ -0,0 +1,29 @@ +package com.krunal.demo.uicomponents.adapters + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView +import com.krunal.demo.R + +class TimezoneAdapter(private val context: Context, private val timezones: Array): BaseAdapter() { + + override fun getCount() = timezones.size + + override fun getItem(position: Int) = null + + override fun getItemId(position: Int): Long = 0 + + @SuppressLint("ViewHolder") + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.timezone_spinner, null) + val tvName = view.findViewById(R.id.tvTimezone) + tvName.text = timezones[position] + + return view + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/ViewBindingAdapter.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/ViewBindingAdapter.kt new file mode 100644 index 0000000..548048c --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/ViewBindingAdapter.kt @@ -0,0 +1,61 @@ +package com.krunal.demo.uicomponents.adapters + +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import androidx.appcompat.content.res.AppCompatResources +import androidx.core.graphics.ColorUtils +import androidx.databinding.BindingAdapter + +@BindingAdapter("drawableResource") +fun ImageView.setDrawableResource(@DrawableRes drawableResource: Int) { + setImageResource(drawableResource) +} + +@BindingAdapter("drawableResource") +fun TextView.setStartDrawableResource(@DrawableRes drawableResource: Int) { + setCompoundDrawablesRelativeWithIntrinsicBounds( + AppCompatResources.getDrawable(context, drawableResource), null, null, null + ) +} + +@BindingAdapter("drawableColorResource") +fun TextView.setStartColorResource(@ColorRes colorResource: Int) { + compoundDrawablesRelative.filterNotNull().forEach { drawable -> + drawable.mutate().setTint(resources.getColor(colorResource, context.theme)) + } +} + +@BindingAdapter("textColorResource") +fun TextView.setTextColorResource(@ColorRes colorResource: Int) { + setTextColor(resources.getColor(colorResource, context.theme)) +} + +@BindingAdapter("textColor") +fun TextView.setTextColor(color: Int) { + setTextColor(color) +} + +@BindingAdapter("backgroundColor") +fun View.setViewBackgroundColor(color: Int) { + setBackgroundColor(color) +} + +@BindingAdapter("gradientStartColor", "gradientEndColor", "angle", requireAll = false) +fun View.setGradientBackground(gradientStartColor: Int?, gradientEndColor: Int?, angle: Int?) { + gradientStartColor?.let { it -> + ColorUtils.setAlphaComponent(it, 0x4f).let { color -> + GradientDrawable( + GradientDrawable.Orientation.TR_BL, + intArrayOf(color, gradientEndColor ?: Color.TRANSPARENT) + ).also { + it.cornerRadius = 0f + background = it + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/ViewPagerAdapter.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/ViewPagerAdapter.kt new file mode 100644 index 0000000..b88918c --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/adapters/ViewPagerAdapter.kt @@ -0,0 +1,16 @@ +package com.krunal.demo.uicomponents.adapters + +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.krunal.demo.uicomponents.tablayoutfragments.CallsFragment +import com.krunal.demo.uicomponents.tablayoutfragments.ChatFragment +import com.krunal.demo.uicomponents.tablayoutfragments.HomeFragment + +class ViewPagerAdapter(fa: FragmentActivity): FragmentStateAdapter(fa) { + + private val fragments = listOf(HomeFragment(), ChatFragment(), CallsFragment()) + + override fun getItemCount() = fragments.count() + + override fun createFragment(position: Int) = fragments[position] +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/binding/DataBindingFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/binding/DataBindingFragment.kt new file mode 100644 index 0000000..c89d361 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/binding/DataBindingFragment.kt @@ -0,0 +1,26 @@ +package com.krunal.demo.uicomponents.binding + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import com.krunal.demo.databinding.FragmentDataBindingBinding + +class DataBindingFragment : Fragment() { + + private lateinit var binding: FragmentDataBindingBinding + private val viewModel: DataBingingViewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentDataBindingBinding.inflate(inflater) + binding.viewModel = viewModel + binding.lifecycleOwner = viewLifecycleOwner + return binding.root + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/binding/DataBingingViewModel.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/binding/DataBingingViewModel.kt new file mode 100644 index 0000000..ced1218 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/binding/DataBingingViewModel.kt @@ -0,0 +1,34 @@ +package com.krunal.demo.uicomponents.binding + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.krunal.demo.uicomponents.models.Name +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +class DataBingingViewModel : ViewModel() { + + private val _nameFlow = MutableStateFlow(null) + val nameFlow: StateFlow = _nameFlow + + init { + randomName() + } + + /** Generate and emmit random names to [nameFlow] */ + private fun randomName() { + // List of random names + val names = listOf( + Name("Harry", "Potter") + ) + + viewModelScope.launch { + while (true) { + delay(3000) + _nameFlow.emit(names.first()) + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/cardscreen/CardFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/cardscreen/CardFragment.kt new file mode 100644 index 0000000..f296686 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/cardscreen/CardFragment.kt @@ -0,0 +1,117 @@ +package com.krunal.demo.uicomponents.cardscreen + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import com.google.android.material.chip.Chip +import com.google.android.material.navigation.NavigationBarView +import com.krunal.demo.R +import com.krunal.demo.UIComponentsActivity +import com.krunal.demo.databinding.CardLayoutBinding +import com.krunal.demo.databinding.FragmentCardBinding +import com.krunal.demo.uicomponents.dialogs.MyDatePickerDialog +import com.krunal.demo.uicomponents.extentions.dpFormat +import com.krunal.demo.uicomponents.models.CardDetail +import com.krunal.demo.uicomponents.sheets.OperationsBottomSheetFragment +import kotlinx.coroutines.launch + +class CardFragment : Fragment() { + + private lateinit var binding: FragmentCardBinding + private val viewModel: CardFragmentViewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentCardBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupUI() + } + + private fun setupUI() { + setupBottomBar() + (activity as? UIComponentsActivity)?.supportActionBar?.apply { + title = getString(R.string.my_cards) + setBackgroundDrawable(ColorDrawable(Color.parseColor("#2F7CEF"))) + elevation = 0f + // TODO: Make title center + } + + setupCardChips() + + setupCard() + + binding.cardOperations.btnDetails.setOnClickListener { + OperationsBottomSheetFragment().show(childFragmentManager, null) + } + + binding.cardOperations.btnCalendar.setOnClickListener { + MyDatePickerDialog().show(childFragmentManager, null) + } + } + + private fun setupCardChips() { + viewModel.dummyCardDetails.forEach { card -> + (layoutInflater.inflate(R.layout.card_chip, null) as? Chip)?.apply { + text = getString(R.string.card_name, card.type, card.number) + isChecked = card.isSelected + setOnCheckedChangeListener { _, isChecked -> + viewModel.updateCardSelection( + card, + isChecked + ) + } + }.also { cardChip -> + binding.cgCard.addView(cardChip) + } + } + } + + private fun setupCard() { + addSelectedCards(viewModel.dummyCardDetails) + lifecycleScope.launch { + viewModel.selectedCards.collect { cardList -> + binding.llCards.removeViews(1, binding.llCards.childCount - 1) + addSelectedCards(cardList) + } + } + } + + private fun addSelectedCards(cardList: List) { + cardList + .filter { it.isSelected } + .forEach { card -> + val cardBinding = CardLayoutBinding.inflate(layoutInflater) + cardBinding.card = card + + val layoutParams = LinearLayout.LayoutParams( + 300.dpFormat(requireContext()), + 200.dpFormat(requireContext()), + ).apply { + marginEnd = 18.dpFormat(requireContext()) + } + + cardBinding.root.layoutParams = layoutParams + binding.llCards.addView(cardBinding.root) + } + } + + private fun setupBottomBar() { + binding.bottomNavigation.apply { + selectedItemId = menu.getItem(1).itemId + labelVisibilityMode = NavigationBarView.LABEL_VISIBILITY_SELECTED + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/cardscreen/CardFragmentViewModel.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/cardscreen/CardFragmentViewModel.kt new file mode 100644 index 0000000..2c76f8e --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/cardscreen/CardFragmentViewModel.kt @@ -0,0 +1,29 @@ +package com.krunal.demo.uicomponents.cardscreen + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.krunal.demo.uicomponents.models.CardDetail +import com.krunal.demo.uicomponents.models.enums.CardType +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch + +class CardFragmentViewModel : ViewModel() { + + private val _selectedCards = MutableSharedFlow>() + val selectedCards: SharedFlow> = _selectedCards + + var dummyCardDetails: List = buildList { + add(CardDetail(CardType.DEBIT, 1963, "$2,983.78", true)) + add(CardDetail(CardType.DEBIT, 1822, "$1,002.02")) + add(CardDetail(CardType.CREDIT, 2291, "$540.00")) + } + + fun updateCardSelection(card: CardDetail, isSelected: Boolean) { + dummyCardDetails.find { it.number == card.number }?.isSelected = + isSelected + viewModelScope.launch { + _selectedCards.emit(dummyCardDetails) + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/ChainBiasFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/ChainBiasFragment.kt new file mode 100644 index 0000000..dfb8893 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/ChainBiasFragment.kt @@ -0,0 +1,5 @@ +package com.krunal.demo.uicomponents.constraintLayouts + +import androidx.fragment.app.Fragment + +class ChainBiasFragment : Fragment() \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/CircularFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/CircularFragment.kt new file mode 100644 index 0000000..2f8fbe6 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/CircularFragment.kt @@ -0,0 +1,32 @@ +package com.krunal.demo.uicomponents.constraintLayouts + +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.FragmentClCircularBinding + +class CircularFragment : Fragment(R.layout.fragment_cl_circular) { + + private lateinit var binding: FragmentClCircularBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentClCircularBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupUI() + } + + private fun setupUI() { + binding.imgBtnMain.setOnCheckedChangeListener { _, isChecked -> + binding.groupEmojis.visibility = if (isChecked) View.VISIBLE else View.INVISIBLE + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/GuidelineBarrierFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/GuidelineBarrierFragment.kt new file mode 100644 index 0000000..64d7c05 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/GuidelineBarrierFragment.kt @@ -0,0 +1,5 @@ +package com.krunal.demo.uicomponents.constraintLayouts + +import androidx.fragment.app.Fragment + +class GuidelineBarrierFragment : Fragment() \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/RelativeFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/RelativeFragment.kt new file mode 100644 index 0000000..18dac3a --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/constraintLayouts/RelativeFragment.kt @@ -0,0 +1,6 @@ +package com.krunal.demo.uicomponents.constraintLayouts + +import androidx.fragment.app.Fragment +import com.krunal.demo.R + +class RelativeFragment : Fragment(R.layout.fragment_cl_relative) \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/dialogs/MyDatePickerDialog.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/dialogs/MyDatePickerDialog.kt new file mode 100644 index 0000000..369e675 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/dialogs/MyDatePickerDialog.kt @@ -0,0 +1,21 @@ +package com.krunal.demo.uicomponents.dialogs + +import android.app.DatePickerDialog +import android.app.Dialog +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import java.util.Calendar + +class MyDatePickerDialog : DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val mCalendar: Calendar = Calendar.getInstance() + val year = mCalendar.get(Calendar.YEAR) + val month = mCalendar.get(Calendar.MONTH) + val dayOfMonth = mCalendar.get(Calendar.DAY_OF_MONTH) + + return DatePickerDialog(requireActivity(), null, year, month, dayOfMonth).apply { + datePicker.maxDate = Calendar.getInstance().timeInMillis + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/ActivityExtentions.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/ActivityExtentions.kt new file mode 100644 index 0000000..36b32d9 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/ActivityExtentions.kt @@ -0,0 +1,21 @@ +package com.krunal.demo.uicomponents.extentions + +import android.app.Activity +import android.content.Context +import android.content.res.Configuration +import android.util.TypedValue + + +fun Context.getThemeColor(resId: Int): Int { + val typedValue = TypedValue() + theme.resolveAttribute(resId, typedValue, true) + return typedValue.data +} + +val Activity.isDarkMode: Boolean + get() = when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { + Configuration.UI_MODE_NIGHT_YES -> true + Configuration.UI_MODE_NIGHT_NO -> false + Configuration.UI_MODE_NIGHT_UNDEFINED -> false + else -> false + } \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/LinearLayoutExtentions.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/LinearLayoutExtentions.kt new file mode 100644 index 0000000..4ee254f --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/LinearLayoutExtentions.kt @@ -0,0 +1,29 @@ +package com.krunal.demo.uicomponents.extentions + +import android.text.Spanned +import android.text.method.LinkMovementMethod +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.text.toSpanned + +// Add text to the LinearLayout by creating new TextView +fun LinearLayout.addTextView(text: String) { + addTextView(text.toSpanned()) +} + +// Add spanned text to the LinearLayout by creating new TextView +fun LinearLayout.addTextView(spanText: Spanned) { + TextView(context).apply { + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT, + ).apply { + topMargin = 24.dpFormat(context) + } + text = spanText + textSize = 18f + movementMethod = LinkMovementMethod.getInstance() + }.also { textView -> + addView(textView) + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/NumberExtentions.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/NumberExtentions.kt new file mode 100644 index 0000000..43bc8c3 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/NumberExtentions.kt @@ -0,0 +1,10 @@ +package com.krunal.demo.uicomponents.extentions + +import android.content.Context +import android.util.DisplayMetrics +import kotlin.math.roundToInt + +fun Int.dpFormat(context: Context): Int { + val displayMetrics = context.resources.displayMetrics + return (this * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)).roundToInt() +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/TypedArrayExtentions.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/TypedArrayExtentions.kt new file mode 100644 index 0000000..9e6af51 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/TypedArrayExtentions.kt @@ -0,0 +1,15 @@ +package com.krunal.demo.uicomponents.extentions + +import android.content.res.TypedArray + +fun TypedArray.toFloat(): List = buildList { + for (i in 0 until length()) { + add(getFloat(i, -1f)) + } +} + +fun TypedArray.toInt(): List = buildList { + for (i in 0 until length()) { + add(getInt(i, -1)) + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/ViewExtentions.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/ViewExtentions.kt new file mode 100644 index 0000000..856ac34 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/extentions/ViewExtentions.kt @@ -0,0 +1,10 @@ +package com.krunal.demo.uicomponents.extentions + +import android.app.Activity +import android.view.View +import android.view.inputmethod.InputMethodManager + +fun View.hideKeyboard() { + val inputMethodManager = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.hideSoftInputFromWindow(windowToken, 0) +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/helpers/PreferenceHelper.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/helpers/PreferenceHelper.kt new file mode 100644 index 0000000..4c7cca8 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/helpers/PreferenceHelper.kt @@ -0,0 +1,69 @@ +package com.krunal.demo.uicomponents.helpers + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceManager + +object PreferenceHelper { + + private lateinit var preferences: SharedPreferences + private lateinit var editor: SharedPreferences.Editor + + /** + * set the context that is being used to access the shared preferences + */ + fun initialize(context: Context) { + preferences = getDefaultSharedPreferences(context) + editor = preferences.edit() + } + + fun putString(key: String, value: String) { + editor.putString(key, value).commit() + } + + fun putBoolean(key: String, value: Boolean) { + editor.putBoolean(key, value).commit() + } + + fun putInt(key: String, value: Int) { + editor.putInt(key, value).commit() + } + + fun putFloat(key: String, value: Float) { + editor.putFloat(key, value).commit() + } + + fun putLong(key: String, value: Long) { + editor.putLong(key, value).commit() + } + + fun getString(key: String?, defValue: String): String { + return preferences.getString(key, defValue) ?: defValue + } + + fun getBoolean(key: String?, defValue: Boolean): Boolean { + return preferences.getBoolean(key, defValue) + } + + fun getInt(key: String?, defValue: Int): Int { + return runCatching { + preferences.getInt(key, defValue) + }.getOrElse { preferences.getLong(key, defValue.toLong()).toInt() } + } + + fun getLong(key: String?, defValue: Long): Long { + return preferences.getLong(key, defValue) + } + + fun getFloat(key: String?, defValue: Float): Float { + return preferences.getFloat(key, defValue) + } + + fun clearPreferences() { + editor.clear().apply() + } + + private fun getDefaultSharedPreferences(context: Context): SharedPreferences { + return PreferenceManager.getDefaultSharedPreferences(context) + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/helpers/ThemeHelper.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/helpers/ThemeHelper.kt new file mode 100644 index 0000000..2bedaae --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/helpers/ThemeHelper.kt @@ -0,0 +1,79 @@ +package com.krunal.demo.uicomponents.helpers + +import android.content.Context +import com.krunal.demo.R +import com.krunal.demo.uicomponents.models.Theme +import com.krunal.demo.uicomponents.models.enums.AccentColor +import com.krunal.demo.uicomponents.models.enums.ThemeMode +import com.krunal.demo.uicomponents.utils.PreferenceKeys + +object ThemeHelper { + fun getThemes(context: Context, isDark: Boolean = false): List { + val accentValues = AccentColor.values() + val accent = context.resources.getStringArray(R.array.accents) + + return buildList { + for (i in accentValues.indices) { + add( + Theme( + accent[i], accentValues[i], getAccentColor(context, accentValues[i], isDark) + ) + ) + } + } + } + + fun getThemeResource(accentColor: AccentColor): Int { + return when (accentColor) { + AccentColor.RED -> R.style.Theme_Red + AccentColor.BLUE -> R.style.Theme_Blue + AccentColor.YELLOW -> R.style.Theme_Yellow + AccentColor.GREEN -> R.style.Theme_Green + AccentColor.PURPLE -> R.style.Theme_Purple + AccentColor.VIOLET -> R.style.Theme_Violet + } + } + + fun getThemeMode(): ThemeMode { + return ThemeMode.valueOf( + PreferenceHelper.getString( + PreferenceKeys.THEME_MODE, + ThemeMode.AUTO.name + ) + ) + } + + fun setThemeMode(mode: ThemeMode) { + PreferenceHelper.putString(PreferenceKeys.THEME_MODE, mode.name) + } + + fun getThemeAccent(): AccentColor { + return AccentColor.valueOf( + PreferenceHelper.getString( + PreferenceKeys.ACCENT_COLOR, + AccentColor.VIOLET.name + ) + ) + } + + fun setThemeAccent(color: AccentColor) { + PreferenceHelper.putString(PreferenceKeys.ACCENT_COLOR, color.name) + } + + private fun getAccentColor( + context: Context, + accentColor: AccentColor, + isDarkMode: Boolean + ): Int { + return context.getColor( + when (accentColor) { + AccentColor.RED -> if (isDarkMode) R.color.red_md_theme_dark_primary else R.color.red_md_theme_light_primary + AccentColor.BLUE -> if (isDarkMode) R.color.blue_md_theme_dark_primary else R.color.blue_md_theme_light_primary + AccentColor.YELLOW -> if (isDarkMode) R.color.yellow_md_theme_dark_primary else R.color.yellow_md_theme_light_primary + AccentColor.GREEN -> if (isDarkMode) R.color.green_md_theme_dark_primary else R.color.green_md_theme_light_primary + AccentColor.PURPLE -> if (isDarkMode) R.color.purple_md_theme_dark_primary else R.color.purple_md_theme_light_primary + AccentColor.VIOLET -> if (isDarkMode) R.color.violet_theme_dark_primary else R.color.violet_theme_light_primary + } + ) + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/CardDetail.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/CardDetail.kt new file mode 100644 index 0000000..d910dbf --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/CardDetail.kt @@ -0,0 +1,18 @@ +package com.krunal.demo.uicomponents.models + +import com.krunal.demo.uicomponents.models.enums.CardType + +data class CardDetail( + val type: CardType, val number: Int, val amount: String = "$0" +) { + + var isSelected: Boolean = false + + constructor(type: CardType, number: Int, amount: String, isSelected: Boolean) : this( + type, + number, + amount + ) { + this.isSelected = isSelected + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/DrawableResource.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/DrawableResource.kt new file mode 100644 index 0000000..9f3f4fb --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/DrawableResource.kt @@ -0,0 +1,8 @@ +package com.krunal.demo.uicomponents.models + +import android.graphics.Bitmap as AndroidBitmap + +sealed interface DrawableResource { + data class Drawable(val id: Int): DrawableResource + data class Bitmap(val bitmap: AndroidBitmap): DrawableResource +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/Name.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/Name.kt new file mode 100644 index 0000000..cb56627 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/Name.kt @@ -0,0 +1,3 @@ +package com.krunal.demo.uicomponents.models + +data class Name(val firstName: String, val lastName: String) diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/Theme.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/Theme.kt new file mode 100644 index 0000000..400d7fa --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/Theme.kt @@ -0,0 +1,11 @@ +package com.krunal.demo.uicomponents.models + +import androidx.annotation.ColorRes +import com.krunal.demo.uicomponents.models.enums.AccentColor + +data class Theme( + val name: String, + val accentColor: AccentColor, + val color: Int, + val isDarkMode: Boolean = false +) diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/AccentColor.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/AccentColor.kt new file mode 100644 index 0000000..944286c --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/AccentColor.kt @@ -0,0 +1,5 @@ +package com.krunal.demo.uicomponents.models.enums + +enum class AccentColor { + RED, BLUE, YELLOW, GREEN, PURPLE, VIOLET +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/CardType.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/CardType.kt new file mode 100644 index 0000000..d3ac2a3 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/CardType.kt @@ -0,0 +1,11 @@ +package com.krunal.demo.uicomponents.models.enums + +import java.util.Locale + +enum class CardType { + DEBIT, CREDIT; + + override fun toString(): String { + return name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/ThemeMode.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/ThemeMode.kt new file mode 100644 index 0000000..4b7b2f5 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/models/enums/ThemeMode.kt @@ -0,0 +1,5 @@ +package com.krunal.demo.uicomponents.models.enums + +enum class ThemeMode { + AUTO, DARK, LIGHT +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/picker/DatePickerFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/picker/DatePickerFragment.kt new file mode 100644 index 0000000..0c78ada --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/picker/DatePickerFragment.kt @@ -0,0 +1,31 @@ +package com.krunal.demo.uicomponents.picker + +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.FragmentDatePickerBinding +import java.util.Date + +class DatePickerFragment : Fragment() { + + private lateinit var binding: FragmentDatePickerBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentDatePickerBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupPicker() + } + + private fun setupPicker() { + binding.dpCalender.maxDate = Date().time + binding.dpCalender.updateDate(2023, 1, 1) + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/picker/TimePickerFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/picker/TimePickerFragment.kt new file mode 100644 index 0000000..8f99181 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/picker/TimePickerFragment.kt @@ -0,0 +1,62 @@ +package com.krunal.demo.uicomponents.picker + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TimePicker +import android.widget.TimePicker.OnTimeChangedListener +import androidx.fragment.app.Fragment +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentTimePickerBinding + +class TimePickerFragment : Fragment(), OnTimeChangedListener { + + private lateinit var binding: FragmentTimePickerBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentTimePickerBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupPicker() + } + + private fun setupPicker() { + // Set is 24 hour format + binding.switch24hour.setOnCheckedChangeListener { _, isChecked -> + binding.tpClock.setIs24HourView(isChecked) + binding.tpSpinner.setIs24HourView(isChecked) + } + + binding.tpClock.setOnTimeChangedListener(this) + binding.tpSpinner.setOnTimeChangedListener(this) + } + + override fun onTimeChanged(timePicker: TimePicker, hourOfDay: Int, minute: Int) { + // Sync both time pickers + if (timePicker == binding.tpClock) { + binding.tpSpinner.hour = hourOfDay + binding.tpSpinner.minute = minute + } else { + binding.tpClock.hour = hourOfDay + binding.tpClock.minute = minute + } + + // Update time text view + binding.tvTime.text = if (timePicker.is24HourView) { + getString(R.string.time, hourOfDay, minute, "") + } else { + getString( + R.string.time, + if (hourOfDay % 12 == 0) 12 else hourOfDay, + minute, + if (hourOfDay < 12) "AM" else "PM" + ) + } + } +} \ 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/java/com/krunal/demo/uicomponents/tablayoutfragments/CallsFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/tablayoutfragments/CallsFragment.kt new file mode 100644 index 0000000..9c9e82c --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/tablayoutfragments/CallsFragment.kt @@ -0,0 +1,6 @@ +package com.krunal.demo.uicomponents.tablayoutfragments + +import androidx.fragment.app.Fragment +import com.krunal.demo.R + +class CallsFragment : Fragment(R.layout.fragment_calls) \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/tablayoutfragments/ChatFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/tablayoutfragments/ChatFragment.kt new file mode 100644 index 0000000..9d9f346 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/tablayoutfragments/ChatFragment.kt @@ -0,0 +1,11 @@ +package com.krunal.demo.uicomponents.tablayoutfragments + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.krunal.demo.R + +class ChatFragment : Fragment(R.layout.fragment_chat) { +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/tablayoutfragments/HomeFragment.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/tablayoutfragments/HomeFragment.kt new file mode 100644 index 0000000..8d124ab --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/tablayoutfragments/HomeFragment.kt @@ -0,0 +1,66 @@ +package com.krunal.demo.uicomponents.tablayoutfragments + +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.tabs.TabLayout +import com.google.android.material.tabs.TabLayout.OnTabSelectedListener +import com.krunal.demo.R +import com.krunal.demo.databinding.FragmentHomeBinding + +class HomeFragment : Fragment(R.layout.fragment_home) { + + private lateinit var binding: FragmentHomeBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + binding = FragmentHomeBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupTabs() + } + + private fun setupTabs() { + // Text tabs programmatically + listOf("Tab1", "Tab2", "Tab3", "Tab4").forEach { + binding.tabLayoutText.apply { + addTab(newTab().setText(it)) + } + } + + // Show/Hide lable + binding.tabLayoutSelectedText.apply { + for (i in 0..tabCount) { + if (i != selectedTabPosition) { + getTabAt(i)?.tabLabelVisibility = TabLayout.TAB_LABEL_VISIBILITY_UNLABELED + } + } + } + binding.tabLayoutSelectedText.addOnTabSelectedListener(object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab?) { + tab?.tabLabelVisibility = TabLayout.TAB_LABEL_VISIBILITY_LABELED + } + + override fun onTabUnselected(tab: TabLayout.Tab?) { + tab?.tabLabelVisibility = TabLayout.TAB_LABEL_VISIBILITY_UNLABELED + } + + override fun onTabReselected(tab: TabLayout.Tab?) { + + } + }) + + // Scrollable tabs + listOf("Tab1", "Tab2", "Tab3").forEach { + binding.tabLayoutScrollable.apply { + addTab(newTab().setText(it)) + } + } + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/utils/PreferenceKeys.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/utils/PreferenceKeys.kt new file mode 100644 index 0000000..70f4061 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/utils/PreferenceKeys.kt @@ -0,0 +1,11 @@ +package com.krunal.demo.uicomponents.utils + +object PreferenceKeys { + + /** + * Appearance + */ + const val THEME_MODE = "theme_mode" + const val ACCENT_COLOR = "accent_color" + +} diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/CustomView.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/CustomView.kt new file mode 100644 index 0000000..02e24ce --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/CustomView.kt @@ -0,0 +1,41 @@ +package com.krunal.demo.uicomponents.views + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View +import com.krunal.demo.R + +class CustomView(context: Context, attrs: AttributeSet): View(context, attrs) { + + private val paint = Paint() + private var centerOfX = 340F + private var centerOfY = 340F + private val radiusOfCircleView = 140F + private var isCenter = false + + init { + val attributeArray: TypedArray = context.theme.obtainStyledAttributes( + attrs, R.styleable.CustomButton, 0, 0 + ) + + paint.apply { + color = attributeArray.getColor(R.styleable.CustomButton_circleColor, Color.RED) + strokeWidth = attributeArray.getFloat(R.styleable.CustomButton_strokeSize, 20f) + style = Paint.Style.STROKE + } + isCenter = attributeArray.getBoolean(R.styleable.CustomButton_onCenter, isCenter) + } + + override fun onDraw(canvas: Canvas?) { + if (isCenter) { + centerOfX = (width / 2).toFloat() + centerOfY = (height / 2).toFloat() + } + canvas?.drawCircle(centerOfX, centerOfY, radiusOfCircleView, paint) + super.onDraw(canvas) + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/HistoryLineView.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/HistoryLineView.kt new file mode 100644 index 0000000..3fd28d1 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/HistoryLineView.kt @@ -0,0 +1,70 @@ +package com.krunal.demo.uicomponents.views + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View +import com.krunal.demo.R + +class HistoryLineView(context: Context, attrs: AttributeSet) : View(context, attrs) { + + private val thumbPaint = Paint(Paint.ANTI_ALIAS_FLAG) + private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG) + private var thumbColor = Color.BLUE + private var lineColor = thumbColor + private var thumbRadius = 10F + private var lineWidth = 2F + + init { + + setupAttributes(attrs) + setupPaint() + } + + private fun setupAttributes(attrs: AttributeSet) { + val typedArray = + context.theme.obtainStyledAttributes(attrs, R.styleable.HistoryLineView, 0, 0) + thumbColor = typedArray.getColor(R.styleable.HistoryLineView_thumbColor, thumbColor) + lineColor = typedArray.getColor(R.styleable.HistoryLineView_lineColor, lineColor) + thumbRadius = typedArray.getDimension(R.styleable.HistoryLineView_thumbRadius, thumbRadius) + lineWidth = typedArray.getDimension(R.styleable.HistoryLineView_lineWidth, lineWidth) + +// typedArray.recycle() + } + + private fun setupPaint() { + thumbPaint.apply { + color = thumbColor + style = Paint.Style.FILL + } + + linePaint.apply { + color = lineColor + strokeWidth = lineWidth + style = Paint.Style.STROKE + strokeCap = Paint.Cap.ROUND + } + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + drawThumb(canvas) + drawLine(canvas) + } + + private fun drawThumb(canvas: Canvas) { + canvas.drawCircle((width / 2).toFloat(), thumbRadius, thumbRadius, thumbPaint) + } + + private fun drawLine(canvas: Canvas) { + val xPosition = (width / 2).toFloat() + val yStartPosition = (thumbRadius * 2) + lineWidth + val yEndPosition = height - yStartPosition + canvas.drawLine( + xPosition, yStartPosition, xPosition, yEndPosition, linePaint + ) + } +} \ No newline at end of file diff --git a/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/MyClickableSpan.kt b/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/MyClickableSpan.kt new file mode 100644 index 0000000..81d9176 --- /dev/null +++ b/Demo/app/src/main/java/com/krunal/demo/uicomponents/views/MyClickableSpan.kt @@ -0,0 +1,18 @@ +package com.krunal.demo.uicomponents.views + +import android.text.TextPaint +import android.text.style.ClickableSpan +import android.view.View + +class MyClickableSpan( + private val onTextClick: () -> Unit +) : ClickableSpan() { + override fun onClick(view: View) { + onTextClick() + } + + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.isUnderlineText = false + } +} \ No newline at end of file diff --git a/Demo/app/src/main/res/color/chip_card.xml b/Demo/app/src/main/res/color/chip_card.xml new file mode 100644 index 0000000..ed1c356 --- /dev/null +++ b/Demo/app/src/main/res/color/chip_card.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/color/chip_card_text.xml b/Demo/app/src/main/res/color/chip_card_text.xml new file mode 100644 index 0000000..447acf5 --- /dev/null +++ b/Demo/app/src/main/res/color/chip_card_text.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/color/chip_operations_time.xml b/Demo/app/src/main/res/color/chip_operations_time.xml new file mode 100644 index 0000000..cd4dbcd --- /dev/null +++ b/Demo/app/src/main/res/color/chip_operations_time.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/color/stack_exchange_chip_background.xml b/Demo/app/src/main/res/color/stack_exchange_chip_background.xml new file mode 100644 index 0000000..b4b0d3b --- /dev/null +++ b/Demo/app/src/main/res/color/stack_exchange_chip_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/calendar_banner.jpeg b/Demo/app/src/main/res/drawable/calendar_banner.jpeg new file mode 100644 index 0000000..6b784d4 Binary files /dev/null and b/Demo/app/src/main/res/drawable/calendar_banner.jpeg differ diff --git a/Demo/app/src/main/res/drawable/circle_image.xml b/Demo/app/src/main/res/drawable/circle_image.xml new file mode 100644 index 0000000..b9d6c3d --- /dev/null +++ b/Demo/app/src/main/res/drawable/circle_image.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/circular_progress.xml b/Demo/app/src/main/res/drawable/circular_progress.xml new file mode 100644 index 0000000..9bf5a8f --- /dev/null +++ b/Demo/app/src/main/res/drawable/circular_progress.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/dark_forest.jpeg b/Demo/app/src/main/res/drawable/dark_forest.jpeg new file mode 100644 index 0000000..bd25b0d Binary files /dev/null and b/Demo/app/src/main/res/drawable/dark_forest.jpeg differ diff --git a/Demo/app/src/main/res/drawable/gradient_button.xml b/Demo/app/src/main/res/drawable/gradient_button.xml new file mode 100644 index 0000000..30ecd05 --- /dev/null +++ b/Demo/app/src/main/res/drawable/gradient_button.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/Demo/app/src/main/res/drawable/gradient_progress.xml b/Demo/app/src/main/res/drawable/gradient_progress.xml new file mode 100644 index 0000000..ab16dd6 --- /dev/null +++ b/Demo/app/src/main/res/drawable/gradient_progress.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/highlighted_background.xml b/Demo/app/src/main/res/drawable/highlighted_background.xml new file mode 100644 index 0000000..af6d70b --- /dev/null +++ b/Demo/app/src/main/res/drawable/highlighted_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/ic_add.xml b/Demo/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..70046c4 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_add_filled.xml b/Demo/app/src/main/res/drawable/ic_add_filled.xml new file mode 100644 index 0000000..a14875c --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_add_filled.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_android.xml b/Demo/app/src/main/res/drawable/ic_android.xml new file mode 100644 index 0000000..1168496 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_android.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_avax_logo.xml b/Demo/app/src/main/res/drawable/ic_avax_logo.xml new file mode 100644 index 0000000..c36bcab --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_avax_logo.xml @@ -0,0 +1,9 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_back.xml b/Demo/app/src/main/res/drawable/ic_back.xml new file mode 100644 index 0000000..6c3197a --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_back.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_calendar.xml b/Demo/app/src/main/res/drawable/ic_calendar.xml new file mode 100644 index 0000000..b6caf72 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_calendar.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_call.xml b/Demo/app/src/main/res/drawable/ic_call.xml new file mode 100644 index 0000000..2aba8ad --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_call.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_card.xml b/Demo/app/src/main/res/drawable/ic_card.xml new file mode 100644 index 0000000..838fb42 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_card.xml @@ -0,0 +1,5 @@ + + + + diff --git a/Demo/app/src/main/res/drawable/ic_chat.xml b/Demo/app/src/main/res/drawable/ic_chat.xml new file mode 100644 index 0000000..9e7de07 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_chat.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_check.xml b/Demo/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 0000000..fbc59ec --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,10 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_circle.xml b/Demo/app/src/main/res/drawable/ic_circle.xml new file mode 100644 index 0000000..7c795ea --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_circle.xml @@ -0,0 +1,10 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_cross.xml b/Demo/app/src/main/res/drawable/ic_cross.xml new file mode 100644 index 0000000..70db409 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_cross.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_down_arrow.xml b/Demo/app/src/main/res/drawable/ic_down_arrow.xml new file mode 100644 index 0000000..3dbfedb --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_down_arrow.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_down_arrow_outlined.xml b/Demo/app/src/main/res/drawable/ic_down_arrow_outlined.xml new file mode 100644 index 0000000..1aeaa99 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_down_arrow_outlined.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_email.xml b/Demo/app/src/main/res/drawable/ic_email.xml new file mode 100644 index 0000000..80111c8 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_email.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_exchange.xml b/Demo/app/src/main/res/drawable/ic_exchange.xml new file mode 100644 index 0000000..6f7c7f3 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_exchange.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/Demo/app/src/main/res/drawable/ic_feed.xml b/Demo/app/src/main/res/drawable/ic_feed.xml new file mode 100644 index 0000000..4a4e158 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_feed.xml @@ -0,0 +1,5 @@ + + + + diff --git a/Demo/app/src/main/res/drawable/ic_female.xml b/Demo/app/src/main/res/drawable/ic_female.xml new file mode 100644 index 0000000..ea62387 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_female.xml @@ -0,0 +1,9 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_food.xml b/Demo/app/src/main/res/drawable/ic_food.xml new file mode 100644 index 0000000..45861e1 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_food.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/Demo/app/src/main/res/drawable/ic_gasoline.xml b/Demo/app/src/main/res/drawable/ic_gasoline.xml new file mode 100644 index 0000000..bc05081 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_gasoline.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/Demo/app/src/main/res/drawable/ic_heart_outlined.xml b/Demo/app/src/main/res/drawable/ic_heart_outlined.xml new file mode 100644 index 0000000..58c0403 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_heart_outlined.xml @@ -0,0 +1,4 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_home.xml b/Demo/app/src/main/res/drawable/ic_home.xml new file mode 100644 index 0000000..4c5e854 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_home.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_image.xml b/Demo/app/src/main/res/drawable/ic_image.xml new file mode 100644 index 0000000..d35859d --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_image.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_info.xml b/Demo/app/src/main/res/drawable/ic_info.xml new file mode 100644 index 0000000..e0ecb40 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_info.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_line_shader.xml b/Demo/app/src/main/res/drawable/ic_line_shader.xml new file mode 100644 index 0000000..c775e03 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_line_shader.xml @@ -0,0 +1,4 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_link_logo.xml b/Demo/app/src/main/res/drawable/ic_link_logo.xml new file mode 100644 index 0000000..5eb2e0c --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_link_logo.xml @@ -0,0 +1,4 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_male.xml b/Demo/app/src/main/res/drawable/ic_male.xml new file mode 100644 index 0000000..97af074 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_male.xml @@ -0,0 +1,12 @@ + + + + diff --git a/Demo/app/src/main/res/drawable/ic_market.xml b/Demo/app/src/main/res/drawable/ic_market.xml new file mode 100644 index 0000000..9dd36e5 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_market.xml @@ -0,0 +1,4 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_mastercard.xml b/Demo/app/src/main/res/drawable/ic_mastercard.xml new file mode 100644 index 0000000..3405fd8 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_mastercard.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/Demo/app/src/main/res/drawable/ic_menu.xml b/Demo/app/src/main/res/drawable/ic_menu.xml new file mode 100644 index 0000000..470db52 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_menu.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_next.xml b/Demo/app/src/main/res/drawable/ic_next.xml new file mode 100644 index 0000000..4680420 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_next.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_notification.xml b/Demo/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..45d15da --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,4 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_other_gender.xml b/Demo/app/src/main/res/drawable/ic_other_gender.xml new file mode 100644 index 0000000..856139c --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_other_gender.xml @@ -0,0 +1,9 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_paperplane.xml b/Demo/app/src/main/res/drawable/ic_paperplane.xml new file mode 100644 index 0000000..bb0e194 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_paperplane.xml @@ -0,0 +1,5 @@ + + + + diff --git a/Demo/app/src/main/res/drawable/ic_pause.xml b/Demo/app/src/main/res/drawable/ic_pause.xml new file mode 100644 index 0000000..f701d6f --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_pause.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_pause_outlined.xml b/Demo/app/src/main/res/drawable/ic_pause_outlined.xml new file mode 100644 index 0000000..dbf5f42 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_pause_outlined.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_pen.xml b/Demo/app/src/main/res/drawable/ic_pen.xml new file mode 100644 index 0000000..faddfce --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_pen.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_play.xml b/Demo/app/src/main/res/drawable/ic_play.xml new file mode 100644 index 0000000..0870be8 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_play.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_play_outlined.xml b/Demo/app/src/main/res/drawable/ic_play_outlined.xml new file mode 100644 index 0000000..c4f8875 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_play_outlined.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_previous.xml b/Demo/app/src/main/res/drawable/ic_previous.xml new file mode 100644 index 0000000..544bf5b --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_previous.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_profile.xml b/Demo/app/src/main/res/drawable/ic_profile.xml new file mode 100644 index 0000000..ea4bd5c --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_profile.xml @@ -0,0 +1,4 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_remove.xml b/Demo/app/src/main/res/drawable/ic_remove.xml new file mode 100644 index 0000000..128a743 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_remove.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_right_arrow.xml b/Demo/app/src/main/res/drawable/ic_right_arrow.xml new file mode 100644 index 0000000..a749bde --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_right_arrow.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_rose_logo.xml b/Demo/app/src/main/res/drawable/ic_rose_logo.xml new file mode 100644 index 0000000..f418b9e --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_rose_logo.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/Demo/app/src/main/res/drawable/ic_search.xml b/Demo/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..6031484 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,10 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_splash.xml b/Demo/app/src/main/res/drawable/ic_splash.xml new file mode 100644 index 0000000..ce0d04e --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_splash.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/Demo/app/src/main/res/drawable/ic_swap.xml b/Demo/app/src/main/res/drawable/ic_swap.xml new file mode 100644 index 0000000..d113e11 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_swap.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_timezone.xml b/Demo/app/src/main/res/drawable/ic_timezone.xml new file mode 100644 index 0000000..3476385 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_timezone.xml @@ -0,0 +1,5 @@ + + + + diff --git a/Demo/app/src/main/res/drawable/ic_two_circle.xml b/Demo/app/src/main/res/drawable/ic_two_circle.xml new file mode 100644 index 0000000..87fc77a --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_two_circle.xml @@ -0,0 +1,4 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_up_arrow.xml b/Demo/app/src/main/res/drawable/ic_up_arrow.xml new file mode 100644 index 0000000..ba9e503 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_up_arrow.xml @@ -0,0 +1,5 @@ + + + diff --git a/Demo/app/src/main/res/drawable/ic_wallet.xml b/Demo/app/src/main/res/drawable/ic_wallet.xml new file mode 100644 index 0000000..fc12537 --- /dev/null +++ b/Demo/app/src/main/res/drawable/ic_wallet.xml @@ -0,0 +1,5 @@ + + + + diff --git a/Demo/app/src/main/res/drawable/image_toggle.xml b/Demo/app/src/main/res/drawable/image_toggle.xml new file mode 100644 index 0000000..56adc24 --- /dev/null +++ b/Demo/app/src/main/res/drawable/image_toggle.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/img.png b/Demo/app/src/main/res/drawable/img.png new file mode 100644 index 0000000..bc5121c Binary files /dev/null and b/Demo/app/src/main/res/drawable/img.png differ diff --git a/Demo/app/src/main/res/drawable/profile.jpeg b/Demo/app/src/main/res/drawable/profile.jpeg new file mode 100644 index 0000000..74d27a6 Binary files /dev/null and b/Demo/app/src/main/res/drawable/profile.jpeg differ diff --git a/Demo/app/src/main/res/drawable/progress_vertical.xml b/Demo/app/src/main/res/drawable/progress_vertical.xml new file mode 100644 index 0000000..c4350b1 --- /dev/null +++ b/Demo/app/src/main/res/drawable/progress_vertical.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/progress_vertical_graph.xml b/Demo/app/src/main/res/drawable/progress_vertical_graph.xml new file mode 100644 index 0000000..39bdca0 --- /dev/null +++ b/Demo/app/src/main/res/drawable/progress_vertical_graph.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/rounded_bottom_bar.xml b/Demo/app/src/main/res/drawable/rounded_bottom_bar.xml new file mode 100644 index 0000000..a012cd1 --- /dev/null +++ b/Demo/app/src/main/res/drawable/rounded_bottom_bar.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/rounded_bottom_sheet.xml b/Demo/app/src/main/res/drawable/rounded_bottom_sheet.xml new file mode 100644 index 0000000..1d5669c --- /dev/null +++ b/Demo/app/src/main/res/drawable/rounded_bottom_sheet.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/rounded_image.xml b/Demo/app/src/main/res/drawable/rounded_image.xml new file mode 100644 index 0000000..b93d8ec --- /dev/null +++ b/Demo/app/src/main/res/drawable/rounded_image.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/rounded_tab_layout.xml b/Demo/app/src/main/res/drawable/rounded_tab_layout.xml new file mode 100644 index 0000000..b576933 --- /dev/null +++ b/Demo/app/src/main/res/drawable/rounded_tab_layout.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/rounded_toast.xml b/Demo/app/src/main/res/drawable/rounded_toast.xml new file mode 100644 index 0000000..77f703a --- /dev/null +++ b/Demo/app/src/main/res/drawable/rounded_toast.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/rounded_transparent_background.xml b/Demo/app/src/main/res/drawable/rounded_transparent_background.xml new file mode 100644 index 0000000..2526f09 --- /dev/null +++ b/Demo/app/src/main/res/drawable/rounded_transparent_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/running_up_that_hill.png b/Demo/app/src/main/res/drawable/running_up_that_hill.png new file mode 100644 index 0000000..82e0545 Binary files /dev/null and b/Demo/app/src/main/res/drawable/running_up_that_hill.png differ diff --git a/Demo/app/src/main/res/drawable/selected_theme.xml b/Demo/app/src/main/res/drawable/selected_theme.xml new file mode 100644 index 0000000..2537c85 --- /dev/null +++ b/Demo/app/src/main/res/drawable/selected_theme.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/tab_selected_background.xml b/Demo/app/src/main/res/drawable/tab_selected_background.xml new file mode 100644 index 0000000..a596220 --- /dev/null +++ b/Demo/app/src/main/res/drawable/tab_selected_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/drawable/time_banner.jpeg b/Demo/app/src/main/res/drawable/time_banner.jpeg new file mode 100644 index 0000000..147a3f1 Binary files /dev/null and b/Demo/app/src/main/res/drawable/time_banner.jpeg differ diff --git a/Demo/app/src/main/res/drawable/trade_graph.png b/Demo/app/src/main/res/drawable/trade_graph.png new file mode 100644 index 0000000..034cbc8 Binary files /dev/null and b/Demo/app/src/main/res/drawable/trade_graph.png differ diff --git a/Demo/app/src/main/res/layout/activity_main.xml b/Demo/app/src/main/res/layout/activity_main.xml index 6656eb1..6fc97e1 100644 --- a/Demo/app/src/main/res/layout/activity_main.xml +++ b/Demo/app/src/main/res/layout/activity_main.xml @@ -1,21 +1,31 @@ + - + - + + - \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/activity_stack_exchange.xml b/Demo/app/src/main/res/layout/activity_stack_exchange.xml new file mode 100644 index 0000000..5f02973 --- /dev/null +++ b/Demo/app/src/main/res/layout/activity_stack_exchange.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/activity_uicomponents.xml b/Demo/app/src/main/res/layout/activity_uicomponents.xml new file mode 100644 index 0000000..5851e1c --- /dev/null +++ b/Demo/app/src/main/res/layout/activity_uicomponents.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/card_chip.xml b/Demo/app/src/main/res/layout/card_chip.xml new file mode 100644 index 0000000..8d95f42 --- /dev/null +++ b/Demo/app/src/main/res/layout/card_chip.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/card_layout.xml b/Demo/app/src/main/res/layout/card_layout.xml new file mode 100644 index 0000000..85b8a39 --- /dev/null +++ b/Demo/app/src/main/res/layout/card_layout.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/company_amount_card.xml b/Demo/app/src/main/res/layout/company_amount_card.xml new file mode 100644 index 0000000..d52099b --- /dev/null +++ b/Demo/app/src/main/res/layout/company_amount_card.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/custom_toast.xml b/Demo/app/src/main/res/layout/custom_toast.xml new file mode 100644 index 0000000..44d0ada --- /dev/null +++ b/Demo/app/src/main/res/layout/custom_toast.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/exchange_card.xml b/Demo/app/src/main/res/layout/exchange_card.xml new file mode 100644 index 0000000..b38af4c --- /dev/null +++ b/Demo/app/src/main/res/layout/exchange_card.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Demo/app/src/main/res/layout/exchange_layout.xml b/Demo/app/src/main/res/layout/exchange_layout.xml new file mode 100644 index 0000000..4274a50 --- /dev/null +++ b/Demo/app/src/main/res/layout/exchange_layout.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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..271d6ef --- /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..21bad22 --- /dev/null +++ b/Demo/app/src/main/res/layout/fragment_button.xml @@ -0,0 +1,149 @@ + + + +