Move from java/ to kotlin/ directory
Android 12 dynamic color usage on login screen
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package com.meloda.fast.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
abstract class BaseActivity : AppCompatActivity, LifecycleOwner {
|
||||
|
||||
constructor() : super()
|
||||
|
||||
constructor(@LayoutRes resId: Int) : super(resId)
|
||||
|
||||
protected lateinit var lifecycleRegistry: LifecycleRegistry
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
lifecycleRegistry = LifecycleRegistry(this)
|
||||
lifecycleRegistry.currentState = Lifecycle.State.CREATED
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
lifecycleRegistry.currentState = Lifecycle.State.STARTED
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
lifecycleRegistry.currentState = Lifecycle.State.RESUMED
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
|
||||
}
|
||||
|
||||
val rootView: View? get() = findViewById(android.R.id.content)
|
||||
|
||||
fun requireRootView() = rootView!!
|
||||
|
||||
var errorSnackbar: Snackbar? = null
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.meloda.fast.base
|
||||
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
abstract class BaseFragment : Fragment {
|
||||
|
||||
constructor() : super()
|
||||
|
||||
constructor(@LayoutRes resId: Int) : super(resId)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.meloda.fast.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.meloda.fast.R
|
||||
|
||||
abstract class BaseFullscreenDialog : DialogFragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setStyle(STYLE_NORMAL, R.style.AppTheme_FullScreenDialog)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
dialog?.let { dialog ->
|
||||
val width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
val height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
|
||||
dialog.window?.let {
|
||||
it.setLayout(width, height)
|
||||
it.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
|
||||
it.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
|
||||
it.setWindowAnimations(R.style.AppTheme_Slide)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.meloda.fast.base
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.meloda.fast.base.viewmodel.BaseVM
|
||||
import com.meloda.fast.base.viewmodel.VKEvent
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
abstract class BaseVMFragment<VM : BaseVM> : BaseFragment {
|
||||
|
||||
constructor() : super()
|
||||
|
||||
constructor(@LayoutRes resId: Int) : super(resId)
|
||||
|
||||
protected abstract val viewModel: VM
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
|
||||
viewModel.tasksEvent.onEach { onEvent(it) }.collect()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onEvent(event: VKEvent) {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.meloda.fast.base.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "unused", "MemberVisibilityCanBePrivate", "CanBeParameter")
|
||||
abstract class BaseAdapter<Item : BaseItem, VH : BaseHolder>(
|
||||
var context: Context,
|
||||
values: ArrayList<Item>,
|
||||
diffUtil: DiffUtil.ItemCallback<Item>
|
||||
) : ListAdapter<Item, VH>(diffUtil) {
|
||||
|
||||
val cleanValues = arrayListOf<Item>()
|
||||
val values = arrayListOf<Item>()
|
||||
|
||||
init {
|
||||
addAll(values)
|
||||
}
|
||||
|
||||
protected var inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
|
||||
var itemClickListener: OnItemClickListener? = null
|
||||
var itemLongClickListener: OnItemLongClickListener? = null
|
||||
|
||||
open fun destroy() {
|
||||
itemClickListener = null
|
||||
itemLongClickListener = null
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Item {
|
||||
return values[position]
|
||||
}
|
||||
|
||||
fun add(position: Int, item: Item) {
|
||||
values.add(position, item)
|
||||
cleanValues.add(position, item)
|
||||
}
|
||||
|
||||
fun add(item: Item) {
|
||||
values += item
|
||||
cleanValues.add(item)
|
||||
}
|
||||
|
||||
fun addAll(items: List<Item>) {
|
||||
values += items
|
||||
cleanValues.addAll(items)
|
||||
}
|
||||
|
||||
fun addAll(position: Int, items: List<Item>) {
|
||||
values.addAll(position, items)
|
||||
cleanValues.addAll(position, items)
|
||||
}
|
||||
|
||||
fun removeAll(items: List<Item>) {
|
||||
values.removeAll(items)
|
||||
cleanValues.removeAll(items)
|
||||
}
|
||||
|
||||
fun removeAt(index: Int) {
|
||||
values.removeAt(index)
|
||||
cleanValues.removeAt(index)
|
||||
}
|
||||
|
||||
fun remove(item: Item) {
|
||||
values.remove(item)
|
||||
cleanValues.remove(item)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
values.clear()
|
||||
cleanValues.clear()
|
||||
}
|
||||
|
||||
operator fun get(position: Int): Item {
|
||||
return values[position]
|
||||
}
|
||||
|
||||
operator fun set(position: Int, item: Item) {
|
||||
values[position] = item
|
||||
cleanValues[position] = item
|
||||
}
|
||||
|
||||
open fun notifyChanges(oldList: List<Item>, newList: List<Item>) {}
|
||||
|
||||
fun isEmpty() = values.isEmpty()
|
||||
fun isNotEmpty() = values.isNotEmpty()
|
||||
|
||||
fun view(resId: Int, viewGroup: ViewGroup, attachToRoot: Boolean = false): View {
|
||||
return inflater.inflate(resId, viewGroup, attachToRoot)
|
||||
}
|
||||
|
||||
fun updateValues(arrayList: ArrayList<Item>) {
|
||||
values.clear()
|
||||
values += arrayList
|
||||
}
|
||||
|
||||
fun updateValues(list: List<Item>) = updateValues(ArrayList(list))
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int) {
|
||||
onBindItemViewHolder(holder, position)
|
||||
}
|
||||
|
||||
protected fun initListeners(itemView: View, position: Int) {
|
||||
if (itemView is AdapterView<*>) return
|
||||
|
||||
itemView.setOnClickListener {
|
||||
itemClickListener?.onItemClick(position)
|
||||
}
|
||||
|
||||
itemView.setOnLongClickListener {
|
||||
itemLongClickListener?.onItemLongClick(position)
|
||||
return@setOnLongClickListener itemClickListener == null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return values.size
|
||||
}
|
||||
|
||||
val size get() = itemCount
|
||||
|
||||
private fun onBindItemViewHolder(holder: VH, position: Int) {
|
||||
initListeners(holder.itemView, position)
|
||||
holder.bind(position)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.meloda.fast.base.adapter
|
||||
|
||||
abstract class BaseItem
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.meloda.fast.base.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.meloda.fast.util.AndroidUtils
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class EmptyHeaderAdapter(
|
||||
var context: Context
|
||||
) : RecyclerView.Adapter<EmptyHeaderAdapter.Holder>() {
|
||||
|
||||
inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = Holder(generateHeaderView())
|
||||
|
||||
override fun onBindViewHolder(holder: Holder, position: Int) {
|
||||
}
|
||||
|
||||
override fun getItemCount() = 1
|
||||
|
||||
private fun generateHeaderView() = View(context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
AndroidUtils.px(56).roundToInt()
|
||||
)
|
||||
isClickable = false
|
||||
isEnabled = false
|
||||
isFocusable = false
|
||||
isInvisible = true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.meloda.fast.base.adapter
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
|
||||
abstract class BaseHolder(v: View) : RecyclerView.ViewHolder(v) {
|
||||
|
||||
open fun bind(position: Int) {}
|
||||
|
||||
open fun bind(position: Int, payloads: MutableList<Any>?) {}
|
||||
|
||||
}
|
||||
|
||||
abstract class BindingHolder<B : ViewBinding>(protected val binding: B) : BaseHolder(binding.root)
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.meloda.fast.base.adapter
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(position: Int)
|
||||
}
|
||||
|
||||
interface OnItemLongClickListener {
|
||||
fun onItemLongClick(position: Int)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.meloda.fast.base.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.meloda.fast.api.Answer
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseVM : ViewModel() {
|
||||
|
||||
protected val tasksEventChannel = Channel<VKEvent>()
|
||||
val tasksEvent = tasksEventChannel.receiveAsFlow()
|
||||
|
||||
protected fun <T> makeJob(
|
||||
job: suspend () -> Answer<T>,
|
||||
onAnswer: suspend (T) -> Unit = {},
|
||||
onStart: (suspend () -> Unit)? = null,
|
||||
onEnd: (suspend () -> Unit)? = null,
|
||||
onError: (suspend (String) -> Unit)? = null
|
||||
) = viewModelScope.launch {
|
||||
onStart?.invoke()
|
||||
when (val response = job()) {
|
||||
is Answer.Success -> onAnswer(response.data)
|
||||
is Answer.Error -> onError?.invoke(response.errorString) ?: tasksEventChannel.send(
|
||||
ShowDialogInfoEvent(null, response.errorString)
|
||||
)
|
||||
}
|
||||
}.also { it.invokeOnCompletion { viewModelScope.launch { onEnd?.invoke() } } }
|
||||
|
||||
protected suspend fun <T : VKEvent> sendEvent(event: T) = tasksEventChannel.send(event)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.meloda.fast.base.viewmodel
|
||||
|
||||
data class ShowDialogInfoEvent(
|
||||
val title: String? = null,
|
||||
val message: String,
|
||||
val positiveBtn: String? = null,
|
||||
val negativeBtn: String? = null
|
||||
) : VKEvent()
|
||||
|
||||
object StartProgressEvent : VKEvent()
|
||||
object StopProgressEvent : VKEvent()
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.meloda.fast.base.viewmodel
|
||||
|
||||
abstract class VKEvent
|
||||
Reference in New Issue
Block a user