Feature/styling (#36)

* Working with material styles

* Replaced deprecated ViewModelProviders.of and applied base styles with material components

* Various code style fixes and styles.xml improvements
This commit is contained in:
Melih Aksoy
2019-09-13 14:09:28 +02:00
committed by GitHub
parent 5a10edce17
commit 99a2abf050
58 changed files with 523 additions and 464 deletions

View File

@@ -14,7 +14,7 @@ import javax.inject.Inject
* Parent of fragments which has injections. Aim is to seperate [BaseFragment] functionality for fragments which
* won't need any injection.
*
* Note that fragments that extends from [BaseDaggerFragment] should contribute android injector.
* Note that fragments that extends from [BaseDaggerFragment] should contribute their injector.
*
* This class provides [viewModelFactory] which serves as factory for view models
* in the project. It's injected by map of view models that this app is serving. Check [ViewModelFactory]
@@ -22,16 +22,16 @@ import javax.inject.Inject
*/
abstract class BaseDaggerFragment<T : ViewDataBinding> : BaseFragment<T>(), HasAndroidInjector {
// region Properties
//region Properties
@get:Inject
internal var androidInjector: DispatchingAndroidInjector<Any>? = null
@Inject
lateinit var viewModelFactory: ViewModelFactory
// endregion
//endregion
// region Functions
//region Functions
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
@@ -39,5 +39,5 @@ abstract class BaseDaggerFragment<T : ViewDataBinding> : BaseFragment<T>(), HasA
}
override fun androidInjector(): AndroidInjector<Any>? = androidInjector
// endregion
//endregion
}

View File

@@ -20,12 +20,18 @@ import com.melih.repository.interactors.base.Reason
*/
abstract class BaseFragment<T : ViewDataBinding> : Fragment() {
// region Properties
//region Abstractions
@LayoutRes
abstract fun getLayoutId(): Int
//endregion
//region Properties
protected lateinit var binding: T
// endregion
//endregion
// region Functions
//region Functions
override fun onCreateView(
inflater: LayoutInflater,
@@ -46,8 +52,5 @@ abstract class BaseFragment<T : ViewDataBinding> : Fragment() {
block()
}.show()
}
@LayoutRes
abstract fun getLayoutId(): Int
// endregion
//endregion
}

View File

@@ -37,12 +37,12 @@ const val INITIAL_PAGE = 0
@UseExperimental(ExperimentalCoroutinesApi::class)
abstract class BasePagingDataSource<T> : PageKeyedDataSource<Int, T>() {
// region Abstractions
//region Abstractions
abstract fun loadDataForPage(page: Int): Flow<Result<List<T>>> // Load next page(s)
// endregion
//endregion
// region Properties
//region Properties
private val _stateData = MutableLiveData<State>()
private val _reasonData = MutableLiveData<Reason>()
@@ -59,9 +59,9 @@ abstract class BasePagingDataSource<T> : PageKeyedDataSource<Int, T>() {
*/
val reasonData: LiveData<Reason>
get() = _reasonData
// endregion
//endregion
// region Functions
//region Functions
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, T>) {
// Looping through channel as we'll receive any state, error or data here
@@ -136,5 +136,5 @@ abstract class BasePagingDataSource<T> : PageKeyedDataSource<Int, T>() {
coroutineScope.cancel()
super.invalidate()
}
// endregion
//endregion
}

View File

@@ -16,20 +16,20 @@ import androidx.paging.DataSource
*/
abstract class BasePagingFactory<T> : DataSource.Factory<Int, T>() {
// region Abstractions
//region Abstractions
abstract fun createSource(): BasePagingDataSource<T>
// endregion
//endregion
// region Properties
//region Properties
private val _currentSource = MutableLiveData<BasePagingDataSource<T>>()
val currentSource: LiveData<BasePagingDataSource<T>>
get() = _currentSource
// endregion
//endregion
// region Functions
//region Functions
override fun create(): DataSource<Int, T> = createSource().apply { _currentSource.postValue(this) }
@@ -38,5 +38,5 @@ abstract class BasePagingFactory<T> : DataSource.Factory<Int, T>() {
* by calling [BasePagingDataSource.invalidate]
*/
fun invalidateDataSource() = currentSource.value?.invalidate()
// endregion
//endregion
}

View File

@@ -15,6 +15,8 @@ abstract class BasePagingListAdapter<T>(
private val clickListener: (T) -> Unit
) : PagedListAdapter<T, BaseViewHolder<T>>(callback) {
//region Abstractions
/**
* This method will be called to create view holder to obfuscate layout inflation creation / process
*
@@ -27,6 +29,9 @@ abstract class BasePagingListAdapter<T>(
parent: ViewGroup,
viewType: Int
): BaseViewHolder<T>
//endregion
//region Functions
/**
* [createViewHolder] will provide holders, no need to override this
@@ -51,6 +56,7 @@ abstract class BasePagingListAdapter<T>(
holder.bind(item)
}
}
//endregion
}
/**
@@ -58,6 +64,8 @@ abstract class BasePagingListAdapter<T>(
*/
abstract class BaseViewHolder<T>(binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
//region Functions
/**
* Items are delivered to [bind] via [BaseListAdapter.onBindViewHolder]
*
@@ -65,4 +73,5 @@ abstract class BaseViewHolder<T>(binding: ViewDataBinding) : RecyclerView.ViewHo
* @param position position from adapter
*/
abstract fun bind(item: T)
//endregion
}

View File

@@ -1,7 +1,7 @@
package com.melih.core.base.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.Transformations.switchMap
import androidx.lifecycle.ViewModel
import androidx.paging.PagedList
import androidx.paging.toLiveData
@@ -20,19 +20,19 @@ import com.melih.repository.interactors.base.State
*/
abstract class BasePagingViewModel<T> : ViewModel() {
// region Abstractions
//region Abstractions
abstract val factory: BasePagingFactory<T>
abstract val config: PagedList.Config
// endregion
//endregion
// region Properties
//region Properties
/**
* Observe [stateData] to get notified of state of data
*/
val stateData: LiveData<State> by lazy {
Transformations.switchMap(factory.currentSource) {
switchMap(factory.currentSource) {
it.stateData
}
}
@@ -41,7 +41,7 @@ abstract class BasePagingViewModel<T> : ViewModel() {
* Observe [errorData] to get notified if an error occurs
*/
val errorData: LiveData<Reason> by lazy {
Transformations.switchMap(factory.currentSource) {
switchMap(factory.currentSource) {
it.reasonData
}
}
@@ -52,9 +52,9 @@ abstract class BasePagingViewModel<T> : ViewModel() {
val pagedList: LiveData<PagedList<T>> by lazy {
factory.toLiveData(config)
}
// endregion
//endregion
// region Functions
//region Functions
fun refresh() {
factory.currentSource.value?.invalidate()
@@ -66,5 +66,5 @@ abstract class BasePagingViewModel<T> : ViewModel() {
fun retry() {
factory.currentSource.value?.invalidate()
}
// endregion
//endregion
}

View File

@@ -15,10 +15,10 @@ import kotlinx.coroutines.launch
*/
abstract class BaseViewModel<T> : ViewModel() {
// region Abstractions
//region Abstractions
abstract suspend fun loadData()
// endregion
//endregion
init {
viewModelScope.launch {
@@ -26,7 +26,7 @@ abstract class BaseViewModel<T> : ViewModel() {
}
}
// region Properties
//region Properties
private val _successData = MutableLiveData<T>()
private val _stateData = MutableLiveData<State>()
@@ -49,9 +49,9 @@ abstract class BaseViewModel<T> : ViewModel() {
*/
val errorData: LiveData<Reason>
get() = _errorData
// endregion
//endregion
// region Functions
//region Functions
/**
* Default success handler which assigns given [data] to [successData]
@@ -97,5 +97,5 @@ abstract class BaseViewModel<T> : ViewModel() {
loadData()
}
}
// endregion
//endregion
}

View File

@@ -0,0 +1,27 @@
package com.melih.core.extensions
import androidx.recyclerview.widget.DiffUtil
/**
* Get [diff callback][DiffUtil.ItemCallback] for given type based on provided checker.
* It uses [itemCheck] for both [DiffUtil.ItemCallback.areItemsTheSame] and [DiffUtil.ItemCallback.areContentsTheSame].
*/
inline fun <T> createDiffCallback(crossinline itemCheck: (oldItem: T, newItem: T) -> Boolean) = createDiffCallback(itemCheck, itemCheck)
/**
* Get [diff callback][DiffUtil.ItemCallback] for given type based on provided checker
*/
inline fun <T> createDiffCallback(
crossinline itemCheck: (oldItem: T, newItem: T) -> Boolean,
crossinline contentCheck: (oldItem: T, newItem: T) -> Boolean
) = object : DiffUtil.ItemCallback<T>() {
//region Functions
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean =
itemCheck(oldItem, newItem)
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean =
contentCheck(oldItem, newItem)
//endregion
}

View File

@@ -3,9 +3,6 @@ package com.melih.core.extensions
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
/**
* Reduces required boilerplate code to observe a live data
@@ -16,13 +13,3 @@ import androidx.lifecycle.ViewModelProviders
fun <T> Fragment.observe(data: LiveData<T>, block: (T) -> Unit) {
data.observe(this, Observer(block))
}
/**
* Method for getting viewModel from factory and run a block over it if required for easy access
*
* crossinline for unwanted returns
*/
inline fun <reified T : ViewModel> ViewModelProvider.Factory.createFor(
fragment: Fragment,
crossinline block: T.() -> Unit = {}
): T = ViewModelProviders.of(fragment, this)[T::class.java].apply(block)

View File

@@ -1,14 +0,0 @@
package com.melih.core.extensions
import androidx.recyclerview.widget.DiffUtil
/**
* Get [diff callback][DiffUtil.ItemCallback] for given type based on provided checker
*/
inline fun <T> getDiffCallbackForType(crossinline itemCheck: (oldItem: T, newItem: T) -> Boolean) = object : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean =
itemCheck(oldItem, newItem)
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean =
itemCheck(oldItem, newItem)
}

View File

@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
<color name="lightGray">#8F8F8F</color>
<color name="primaryColor">#f9a825</color>
<color name="primaryLightColor">#ffd95a</color>
<color name="primaryDarkColor">#c17900</color>
<color name="secondaryColor">#757575</color>
<color name="secondaryLightColor">#a4a4a4</color>
<color name="secondaryDarkColor">#494949</color>
<color name="primaryTextColor">#000000</color>
<color name="secondaryTextColor">#686868</color>
</resources>

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="padding_standard">8dp</dimen>
<dimen name="padding_large">16dp</dimen>
<dimen name="corner_radius_light">4dp</dimen>
<dimen name="corner_radius_standard">11dp</dimen>
</resources>

View File

@@ -1,35 +1,40 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/primaryColor</item>
<item name="colorPrimaryDark">@color/primaryDarkColor</item>
<item name="colorSecondary">@color/secondaryColor</item>
<item name="colorAccent">@color/secondaryDarkColor</item>
<!--Styles-->
<item name="android:textColorPrimary">@color/primaryTextColor</item>
<item name="android:textColorSecondary">@color/secondaryTextColor</item>
<style name="TitleTextStyle">
<item name="android:singleLine">true</item>
</style>
<item name="toolbarStyle">@style/AppTheme.ToolbarStyle</item>
<item name="materialCardViewStyle">@style/AppTheme.CardViewStyle</item>
</style>
<style name="ShortDescriptionTextStyle">
<item name="android:ellipsize">end</item>
<item name="android:maxLines">@integer/common_max_lines</item>
<item name="android:gravity">center|left</item>
</style>
<!-- Widgets -->
<style name="DescriptionTextStyle">
<style name="AppTheme.ToolbarStyle" parent="Widget.MaterialComponents.Toolbar">
<item name="android:background">@color/primaryColor</item>
</style>
</style>
<style name="AppTheme.CardViewStyle" parent="Widget.MaterialComponents.CardView">
<item name="android:background">@null</item>
</style>
<!--Text appearances-->
<!--Styles-->
<style name="AppTheme.BaseTextViewStyle" parent="Widget.MaterialComponents.TextView" />
<style name="TitleTextAppearance" parent="TextAppearance.AppCompat.Title" />
<style name="DescriptionTextAppearance" parent="TextAppearance.AppCompat.Body1">
<item name="android:textColor">@color/lightGray</item>
</style>
<style name="AppTheme.TextViewStyle.Title" parent="AppTheme.BaseTextViewStyle">
<item name="android:textAppearance">@style/TextAppearance.MaterialComponents.Headline5</item>
<item name="android:singleLine">true</item>
</style>
<style name="AppTheme.TextViewStyle.Description" parent="AppTheme.BaseTextViewStyle">
<item name="android:ellipsize">end</item>
<item name="android:maxLines">@integer/common_max_lines</item>
<item name="android:textAppearance">@style/TextAppearance.MaterialComponents.Caption</item>
</style>
</resources>