aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/ca/allanwang/kau
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-07-08 20:25:23 -0700
committerAllan Wang <me@allanwang.ca>2017-07-08 20:25:23 -0700
commit81996038462de1be86643e95d262933c4b96c551 (patch)
tree39423b28217251e7051f86e9e4f80c47bcba20b2 /core/src/main/kotlin/ca/allanwang/kau
parent880d433e475e5be4e5d91afac145b490f9a959b7 (diff)
downloadkau-81996038462de1be86643e95d262933c4b96c551.tar.gz
kau-81996038462de1be86643e95d262933c4b96c551.tar.bz2
kau-81996038462de1be86643e95d262933c4b96c551.zip
Move components to separate modules
Diffstat (limited to 'core/src/main/kotlin/ca/allanwang/kau')
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt248
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt85
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt189
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/adapters/SectionAdapter.kt13
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/animators/BaseDelayAnimator.kt45
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java764
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/animators/BaseSlideAlphaAnimator.kt52
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt63
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt51
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt41
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/animators/SlideUpExitRightAnimator.kt23
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt2
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/dialogs/color/CircleView.kt228
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPalette.kt349
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt82
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt309
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt127
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/iitems/CutoutIItem.kt48
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt49
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt24
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/iitems/LibraryIItem.kt100
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt137
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt128
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt33
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt73
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt25
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt85
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt143
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt29
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSeekbar.kt109
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt43
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt62
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt80
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt412
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/ui/SimpleRippleDrawable.kt (renamed from core/src/main/kotlin/ca/allanwang/kau/views/SimpleRippleDrawable.kt)2
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/ui/views/RippleCanvas.kt (renamed from core/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt)2
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt2
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/views/BoundedCardView.kt50
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/views/CutoutView.kt183
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt237
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java859
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/widgets/TextSlider.kt125
42 files changed, 3 insertions, 5708 deletions
diff --git a/core/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt b/core/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
deleted file mode 100644
index 77dbef1..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
+++ /dev/null
@@ -1,248 +0,0 @@
-package ca.allanwang.kau.about
-
-import android.graphics.drawable.Drawable
-import android.os.Bundle
-import android.support.v4.view.PagerAdapter
-import android.support.v4.view.ViewPager
-import android.support.v7.app.AppCompatActivity
-import android.support.v7.widget.RecyclerView
-import android.transition.TransitionInflater
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import ca.allanwang.kau.R
-import ca.allanwang.kau.adapters.FastItemThemedAdapter
-import ca.allanwang.kau.adapters.ThemableIItemColors
-import ca.allanwang.kau.adapters.ThemableIItemColorsDelegate
-import ca.allanwang.kau.animators.FadeScaleAnimator
-import ca.allanwang.kau.iitems.CutoutIItem
-import ca.allanwang.kau.iitems.HeaderIItem
-import ca.allanwang.kau.iitems.LibraryIItem
-import ca.allanwang.kau.utils.*
-import ca.allanwang.kau.widgets.ElasticDragDismissFrameLayout
-import ca.allanwang.kau.widgets.InkPageIndicator
-import com.mikepenz.aboutlibraries.Libs
-import com.mikepenz.aboutlibraries.entity.Library
-import com.mikepenz.fastadapter.IItem
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.uiThread
-import java.security.InvalidParameterException
-
-/**
- * Created by Allan Wang on 2017-06-28.
- *
- * Floating About Activity Panel for your app
- * This contains all the necessary layouts, and can be extended and configured using the [configBuilder]
- * The [rClass] is necessary to generate the list of libraries used in your app, and should point to your app's
- * R.string::class.java
- * If you don't need auto detect, you can pass null instead
- * Note that for the auto detection to work, the R fields must be excluded from Proguard
- * Manual lib listings and other extra modifications can be done so by overriding the open functions
- */
-abstract class AboutActivityBase(val rClass: Class<*>?, val configBuilder: Configs.() -> Unit = {}) : AppCompatActivity(), ViewPager.OnPageChangeListener {
-
- val draggableFrame: ElasticDragDismissFrameLayout by bindView(R.id.about_draggable_frame)
- val pager: ViewPager by bindView(R.id.about_pager)
- val indicator: InkPageIndicator by bindView(R.id.about_indicator)
- /**
- * Holds some common configurations that may be added directly from the constructor
- * Applied lazily since it needs the context to fetch resources
- */
- val configs: Configs by lazy { Configs().apply { configBuilder() } }
- /**
- * Number of pages in the adapter
- * Defaults to just the main view and lib view
- */
- open val pageCount: Int = 2
- /**
- * Page position for the libs
- * This is generated automatically if [inflateLibPage] is called
- */
- private var libPage: Int = -2
- /**
- * Holds that status of each page
- * 0 means nothing has happened
- * 1 means this page has been in view at least once
- * The rest is up to you
- */
- lateinit var pageStatus: IntArray
- /**
- * Holds the lib items once they are fetched asynchronously
- */
- var libItems: List<LibraryIItem>? = null
- /**
- * Holds the adapter for the library page; this is generated later because it uses the config colors
- */
- lateinit var libAdapter: FastItemThemedAdapter<IItem<*, *>>
- /**
- * Global reference of the library recycler
- * This is set by default through [inflateLibPage] and is used to stop scrolling
- * When the draggable frame exits
- * It is not required, hence its nullability
- */
- private var libRecycler: RecyclerView? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.kau_activity_about)
- pageStatus = IntArray(pageCount)
- libAdapter = FastItemThemedAdapter(configs)
- LibraryIItem.bindClickEvents(libAdapter)
- if (configs.textColor != null) indicator.setColour(configs.textColor!!)
- with(pager) {
- adapter = AboutPagerAdapter()
- pageMargin = dimenPixelSize(R.dimen.kau_spacing_normal)
- addOnPageChangeListener(this@AboutActivityBase)
- }
- indicator.setViewPager(pager)
- draggableFrame.addListener(object : ElasticDragDismissFrameLayout.SystemChromeFader(this) {
- override fun onDragDismissed() {
- // if we drag dismiss downward then the default reversal of the enter
- // transition would slide content upward which looks weird. So reverse it.
- if (draggableFrame.translationY > 0) {
- window.returnTransition = TransitionInflater.from(this@AboutActivityBase)
- .inflateTransition(configs.transitionExitReversed)
- }
- libRecycler?.stopScroll()
- finishAfterTransition()
- }
- })
- }
-
- inner class Configs : ThemableIItemColors by ThemableIItemColorsDelegate() {
- var cutoutTextRes: Int = -1
- var cutoutText: String? = null
- var cutoutDrawableRes: Int = -1
- var cutoutDrawable: Drawable? = null
- var cutoutForeground: Int? = null
- var libPageTitleRes: Int = -1
- var libPageTitle: String? = string(R.string.kau_about_libraries_intro) //This is in the string by default since it's lower priority
- /**
- * Transition to be called if the view is dragged down
- */
- var transitionExitReversed: Int = R.transition.kau_about_return_downward
- }
-
- /**
- * Method to fetch the library list
- * This is fetched asynchronously and you may override it to customize the list
- */
- open fun getLibraries(libs: Libs): List<Library> = libs.prepareLibraries(this, null, null, true, true)
-
- /**
- * Gets the view associated with the given page position
- * Keep in mind that when inflating, do NOT add the view to the viewgroup
- * Use layoutInflater.inflate(id, parent, false)
- */
- open fun getPage(position: Int, layoutInflater: LayoutInflater, parent: ViewGroup): View {
- return when (position) {
- 0 -> inflateMainPage(layoutInflater, parent, position)
- pageCount - 1 -> inflateLibPage(layoutInflater, parent, position)
- else -> throw InvalidParameterException()
- }
- }
-
- /**
- * Create the main view with the cutout
- */
- open fun inflateMainPage(layoutInflater: LayoutInflater, parent: ViewGroup, position: Int): View {
- val fastAdapter = FastItemThemedAdapter<IItem<*, *>>(configs)
- val recycler = fullLinearRecycler(fastAdapter)
- fastAdapter.add(CutoutIItem {
- with(configs) {
- text = string(cutoutTextRes, cutoutText)
- drawable = drawable(cutoutDrawableRes, cutoutDrawable)
- if (configs.cutoutForeground != null) foregroundColor = configs.cutoutForeground!!
- }
- }.apply {
- themeEnabled = configs.cutoutForeground == null
- })
- postInflateMainPage(fastAdapter)
- return recycler
- }
-
- /**
- * Open hook called just before the main page view is returned
- * Feel free to add your own items to the adapter in here
- */
- open fun postInflateMainPage(adapter: FastItemThemedAdapter<IItem<*, *>>) {
-
- }
-
- /**
- * Create the lib view with the list of libraries
- */
- open fun inflateLibPage(layoutInflater: LayoutInflater, parent: ViewGroup, position: Int): View {
- libPage = position
- val v = layoutInflater.inflate(R.layout.kau_recycler_detached_background, parent, false)
- val recycler = v.findViewById<RecyclerView>(R.id.kau_recycler_detached)
- libRecycler = recycler
- recycler.adapter = libAdapter
- recycler.itemAnimator = FadeScaleAnimator(itemDelayFactor = 0.2f).apply { addDuration = 300; interpolator = AnimHolder.decelerateInterpolator(this@AboutActivityBase) }
- val background = v.findViewById<View>(R.id.kau_recycler_detached_background)
- if (configs.backgroundColor != null) background.setBackgroundColor(configs.backgroundColor!!.colorToForeground())
- doAsync {
- libItems = getLibraries(
- if (rClass == null) Libs(this@AboutActivityBase) else Libs(this@AboutActivityBase, Libs.toStringArray(rClass.fields))
- ).map { LibraryIItem(it) }
- if (libPage >= 0 && pageStatus[libPage] == 1)
- uiThread { addLibItems() }
- }
- return v
- }
-
- inner class AboutPagerAdapter : PagerAdapter() {
-
- private val layoutInflater: LayoutInflater = LayoutInflater.from(this@AboutActivityBase)
- private val views = Array<View?>(pageCount) { null }
-
- override fun instantiateItem(collection: ViewGroup, position: Int): Any {
- val layout = getPage(position, collection)
- collection.addView(layout)
- return layout
- }
-
- override fun destroyItem(collection: ViewGroup, position: Int, view: Any) {
- collection.removeView(view as View)
- views[position] = null
- }
-
- override fun getCount(): Int = pageCount
-
- override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object`
-
- /**
- * Only get page if view does not exist
- */
- private fun getPage(position: Int, parent: ViewGroup): View {
- if (views[position] == null) views[position] = getPage(position, layoutInflater, parent)
- return views[position]!!
- }
- }
-
- override fun onPageScrollStateChanged(state: Int) {}
-
- override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
-
- override fun onPageSelected(position: Int) {
- if (pageStatus[position] == 0) pageStatus[position] = 1 // mark as seen if previously null
- if (position == libPage && libItems != null && pageStatus[position] == 1) {
- pageStatus[position] = 2 //add libs and mark as such
- postDelayed(300) { addLibItems() } //delay so that the animations occur once the page is fully switched
- }
- }
-
- /**
- * Function that is called when the view is ready to add the lib items
- * Feel free to add your own items here
- */
- open fun addLibItems() {
- libAdapter.add(HeaderIItem(text = configs.libPageTitle, textRes = configs.libPageTitleRes))
- .add(libItems)
- }
-
- override fun onDestroy() {
- AnimHolder.decelerateInterpolator.invalidate() //clear the reference to the interpolators we've used
- super.onDestroy()
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt b/core/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt
deleted file mode 100644
index e1c5c18..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-package ca.allanwang.kau.adapters
-
-import android.support.v7.widget.LinearLayoutManager
-import android.support.v7.widget.RecyclerView
-import com.mikepenz.fastadapter.IItem
-import com.mikepenz.fastadapter.adapters.HeaderAdapter
-import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
-import org.jetbrains.anko.collections.forEachReversedWithIndex
-import java.util.*
-
-/**
- * Created by Allan Wang on 2017-06-27.
- *
- * Once bounded to a [RecyclerView], this will
- * - Chain together a list of [HeaderAdapter]s, backed by a generic [FastItemAdapter]
- * - Add a [LinearLayoutManager] to the recycler
- * - Add a listener for when a new adapter segment is being used
- */
-class ChainedAdapters<T>(vararg items: Pair<T, SectionAdapter<*>>) {
- private val chain: MutableList<Pair<T, SectionAdapter<*>>> = mutableListOf(*items)
- val baseAdapter: FastItemAdapter<IItem<*, *>> = FastItemAdapter()
- private val indexStack = Stack<Int>()
- var recycler: RecyclerView? = null
- val firstVisibleItemPosition: Int
- get() = (recycler?.layoutManager as LinearLayoutManager?)?.findFirstVisibleItemPosition() ?: throw IllegalArgumentException("No recyclerview was bounded to the chain adapters")
-
- fun add(vararg items: Pair<T, SectionAdapter<*>>) = add(items.toList())
-
- fun add(items: Collection<Pair<T, SectionAdapter<*>>>): ChainedAdapters<T> {
- if (recycler != null) throw IllegalAccessException("Chain adapter is already bounded to a recycler; cannot add directly.")
- items.map { it.second }.forEachIndexed { index, sectionAdapter -> sectionAdapter.sectionOrder = chain.size + 1 + index }
- chain.addAll(items)
- return this
- }
-
- operator fun get(index: Int) = chain[index]
-
- /**
- * Attaches the chain to a recycler
- * After this stage, any modifications to the adapters must be done through external references
- * You may still get the generic header adapters through the get operator
- * Binding the recycler also involves supplying a callback, which returns
- * the item (T) associated with the adapter,
- * the index (Int) of the current adapter
- * and the dy (Int) as given by the scroll listener
- */
- fun bindRecyclerView(recyclerView: RecyclerView, onAdapterSectionChanged: (item: T, index: Int, dy: Int) -> Unit) {
- if (recycler != null) throw IllegalStateException("Chain adapter is already bounded")
- if (chain.isEmpty()) throw IllegalArgumentException("No adapters have been added to the adapters list")
- //wrap adapters
- chain.map { it.second }.forEachReversedWithIndex { i, headerAdapter ->
- if (i == chain.size - 1) headerAdapter.wrap(baseAdapter)
- else headerAdapter.wrap(chain[i + 1].second)
- }
- recycler = recyclerView
- indexStack.push(0)
- with(recyclerView) {
- layoutManager = LinearLayoutManager(context)
- adapter = chain.first().second
- addOnScrollListener(object : RecyclerView.OnScrollListener() {
- override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) {
- super.onScrolled(rv, dx, dy)
- val topPosition = firstVisibleItemPosition
- val currentAdapterIndex = indexStack.peek()
- if (dy > 0) {
- //look ahead from current adapter
- val nextAdapterIndex = (currentAdapterIndex until chain.size).asSequence()
- .firstOrNull {
- val adapter = chain[it].second
- adapter.adapterItemCount > 0 && adapter.getGlobalPosition(adapter.adapterItemCount - 1) >= topPosition
- } ?: currentAdapterIndex
- if (nextAdapterIndex == currentAdapterIndex) return
- indexStack.push(nextAdapterIndex)
- onAdapterSectionChanged(chain[indexStack.peek()].first, indexStack.peek(), dy)
- } else if (currentAdapterIndex == 0) {
- return //All adapters may be empty; in this case, if we are already at the beginning, don't bother checking
- } else if (chain[currentAdapterIndex].second.getGlobalPosition(0) > topPosition) {
- indexStack.pop()
- onAdapterSectionChanged(chain[indexStack.peek()].first, indexStack.peek(), dy)
- }
- }
- })
- }
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt b/core/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt
deleted file mode 100644
index 66fec4b..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt
+++ /dev/null
@@ -1,189 +0,0 @@
-package ca.allanwang.kau.adapters
-
-import android.content.res.ColorStateList
-import android.view.View
-import android.widget.ImageView
-import android.widget.TextView
-import ca.allanwang.kau.utils.adjustAlpha
-import ca.allanwang.kau.views.createSimpleRippleDrawable
-import com.mikepenz.fastadapter.IExpandable
-import com.mikepenz.fastadapter.IItem
-import com.mikepenz.fastadapter.ISubItem
-import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
-
-/**
- * Created by Allan Wang on 2017-06-29.
- *
- * Adapter with a set of colors that will be added to all subsequent items
- * Changing a color while the adapter is not empty will reload all items
- *
- * This adapter overrides every method where an item is added
- * If that item extends [ThemableIItem], then the colors will be set
- */
-class FastItemThemedAdapter<Item : IItem<*, *>>(
- textColor: Int? = null,
- backgroundColor: Int? = null,
- accentColor: Int? = null
-) : FastItemAdapter<Item>() {
- constructor(colors: ThemableIItemColors) : this(colors.textColor, colors.backgroundColor, colors.accentColor)
-
- var textColor: Int? = textColor
- set(value) {
- if (field == value) return
- field = value
- themeChanged()
- }
- var backgroundColor: Int? = backgroundColor
- set(value) {
- if (field == value) return
- field = value
- themeChanged()
- }
- var accentColor: Int? = accentColor
- set(value) {
- if (field == value) return
- field = value
- themeChanged()
- }
-
- fun setColors(colors: ThemableIItemColors) {
- this.textColor = colors.textColor
- this.backgroundColor = colors.backgroundColor
- this.accentColor = colors.accentColor
- }
-
- fun themeChanged() {
- if (adapterItemCount == 0) return
- injectTheme(adapterItems)
- notifyAdapterDataSetChanged()
- }
-
- override fun add(position: Int, items: MutableList<Item>): FastItemAdapter<Item> {
- injectTheme(items)
- return super.add(position, items)
- }
-
- override fun add(position: Int, item: Item): FastItemAdapter<Item> {
- injectTheme(item)
- return super.add(position, item)
- }
-
- override fun add(item: Item): FastItemAdapter<Item> {
- injectTheme(item)
- return super.add(item)
- }
-
- override fun add(items: MutableList<Item>): FastItemAdapter<Item> {
- injectTheme(items)
- injectTheme(items)
- return super.add(items)
- }
-
- override fun set(items: MutableList<Item>?): FastItemAdapter<Item> {
- injectTheme(items)
- return super.set(items)
- }
-
- override fun set(position: Int, item: Item): FastItemAdapter<Item> {
- injectTheme(item)
- return super.set(position, item)
- }
-
- override fun setNewList(items: MutableList<Item>?, retainFilter: Boolean): FastItemAdapter<Item> {
- injectTheme(items)
- return super.setNewList(items, retainFilter)
- }
-
- override fun setNewList(items: MutableList<Item>?): FastItemAdapter<Item> {
- injectTheme(items)
- return super.setNewList(items)
- }
-
- override fun <T, S> setSubItems(collapsible: T, subItems: MutableList<S>?): T where S : IItem<*, *>?, T : IItem<*, *>?, T : IExpandable<T, S>?, S : ISubItem<Item, T>? {
- injectTheme(subItems)
- return super.setSubItems(collapsible, subItems)
- }
-
- internal fun injectTheme(items: Collection<IItem<*, *>?>?) {
- items?.forEach { injectTheme(it) }
- }
-
- internal fun injectTheme(item: IItem<*, *>?) {
- if (item is ThemableIItem && item.themeEnabled) {
- item.textColor = textColor
- item.backgroundColor = backgroundColor
- item.accentColor = accentColor
- }
- }
-}
-
-interface ThemableIItemColors {
- var textColor: Int?
- var backgroundColor: Int?
- var accentColor: Int?
-}
-
-class ThemableIItemColorsDelegate : ThemableIItemColors {
- override var textColor: Int? = null
- override var backgroundColor: Int? = null
- override var accentColor: Int? = null
-}
-
-/**
- * Interface that needs to be implemented by every iitem
- * Holds the color values and has helper methods to inject the colors
- */
-interface ThemableIItem : ThemableIItemColors {
- var themeEnabled: Boolean
- fun bindTextColor(vararg views: TextView?)
- fun bindTextColorSecondary(vararg views: TextView?)
- fun bindDividerColor(vararg views: View?)
- fun bindAccentColor(vararg views: TextView?)
- fun bindBackgroundColor(vararg views: View?)
- fun bindBackgroundRipple(vararg views: View?)
- fun bindIconColor(vararg views: ImageView?)
-}
-
-/**
- * The delegate for [ThemableIItem]
- */
-class ThemableIItemDelegate : ThemableIItem, ThemableIItemColors by ThemableIItemColorsDelegate() {
- override var themeEnabled: Boolean = true
-
- override fun bindTextColor(vararg views: TextView?) {
- val color = textColor ?: return
- views.forEach { it?.setTextColor(color) }
- }
-
- override fun bindTextColorSecondary(vararg views: TextView?) {
- val color = textColor?.adjustAlpha(0.8f) ?: return
- views.forEach { it?.setTextColor(color) }
- }
-
- override fun bindAccentColor(vararg views: TextView?) {
- val color = accentColor ?: textColor ?: return
- views.forEach { it?.setTextColor(color) }
- }
-
- override fun bindDividerColor(vararg views: View?) {
- val color = (textColor ?: accentColor)?.adjustAlpha(0.1f) ?: return
- views.forEach { it?.setBackgroundColor(color) }
- }
-
- override fun bindBackgroundColor(vararg views: View?) {
- val color = backgroundColor ?: return
- views.forEach { it?.setBackgroundColor(color) }
- }
-
- override fun bindBackgroundRipple(vararg views: View?) {
- val foreground = accentColor ?: textColor ?: return
- val background = backgroundColor ?: return
- val ripple = createSimpleRippleDrawable(foreground, background)
- views.forEach { it?.background = ripple }
- }
-
- override fun bindIconColor(vararg views: ImageView?) {
- val color = accentColor ?: textColor ?: return
- views.forEach { it?.drawable?.setTintList(ColorStateList.valueOf(color)) }
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/adapters/SectionAdapter.kt b/core/src/main/kotlin/ca/allanwang/kau/adapters/SectionAdapter.kt
deleted file mode 100644
index cf7205a..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/adapters/SectionAdapter.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package ca.allanwang.kau.adapters
-
-import com.mikepenz.fastadapter.IItem
-import com.mikepenz.fastadapter.adapters.HeaderAdapter
-
-/**
- * Created by Allan Wang on 2017-06-27.
- *
- * Extension of [HeaderAdapter] where we can define the order
- */
-class SectionAdapter<Item : IItem<*, *>>(var sectionOrder: Int = 100) : HeaderAdapter<Item>() {
- override fun getOrder(): Int = sectionOrder
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/animators/BaseDelayAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/animators/BaseDelayAnimator.kt
deleted file mode 100644
index c649376..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/animators/BaseDelayAnimator.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package ca.allanwang.kau.animators
-
-import android.support.v7.widget.RecyclerView
-import android.view.ViewPropertyAnimator
-
-/**
- * Created by Allan Wang on 2017-06-27.
- *
- * Base for delayed animators
- * item delay factor by default can be 0.125f
- */
-abstract class BaseDelayAnimator(val itemDelayFactor: Float) : DefaultAnimator() {
-
- override abstract fun addAnimationPrepare(holder: RecyclerView.ViewHolder)
-
- override fun addAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return holder.itemView.animate().apply {
- startDelay = Math.max(0L, (holder.adapterPosition * addDuration * itemDelayFactor).toLong())
- duration = this@BaseDelayAnimator.addDuration
- interpolator = this@BaseDelayAnimator.interpolator
- }
- }
-
-
- override abstract fun addAnimationCleanup(holder: RecyclerView.ViewHolder)
-
- override fun getAddDelay(remove: Long, move: Long, change: Long): Long = 0
-
- override fun getRemoveDelay(remove: Long, move: Long, change: Long): Long = 0
-
- /**
- * Partial removal animation
- * As of now, all it does it change the alpha
- * To have it slide, add onto it in a sub class
- */
- override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return holder.itemView.animate().apply {
- duration = this@BaseDelayAnimator.removeDuration
- startDelay = Math.max(0L, (holder.adapterPosition * removeDuration * itemDelayFactor).toLong())
- interpolator = this@BaseDelayAnimator.interpolator
- }
- }
-
- override abstract fun removeAnimationCleanup(holder: RecyclerView.ViewHolder)
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java b/core/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java
deleted file mode 100644
index 69c2cf3..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java
+++ /dev/null
@@ -1,764 +0,0 @@
-package ca.allanwang.kau.animators;
-
-/*
- * Created by Allan Wang on 2017-06-27.
- *
- * Based on Item Animator by {@author Mike Penz}
- * Rewritten to match with the updated compat dependencies
- */
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.support.annotation.NonNull;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.support.v7.widget.SimpleItemAnimator;
-import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This implementation of {@link android.support.v7.widget.RecyclerView.ItemAnimator} provides basic
- * animations on remove, add, and move events that happen to the items in
- * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.
- *
- * @see android.support.v7.widget.RecyclerView#setItemAnimator(android.support.v7.widget.RecyclerView.ItemAnimator)
- */
-public abstract class BaseItemAnimator extends SimpleItemAnimator {
- private static final boolean DEBUG = false;
-
- private static TimeInterpolator sDefaultInterpolator;
-
- private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
- private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
- private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
- private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
-
- ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
- ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
- ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
-
- ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
- ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
- ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
- ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
-
- public Interpolator interpolator;
-
- private static class MoveInfo {
- public ViewHolder holder;
- public int fromX, fromY, toX, toY;
-
- MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
- this.holder = holder;
- this.fromX = fromX;
- this.fromY = fromY;
- this.toX = toX;
- this.toY = toY;
- }
- }
-
- public static class ChangeInfo {
- public ViewHolder oldHolder, newHolder;
- public int fromX, fromY, toX, toY;
-
- private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
- this.oldHolder = oldHolder;
- this.newHolder = newHolder;
- }
-
- ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
- int fromX, int fromY, int toX, int toY) {
- this(oldHolder, newHolder);
- this.fromX = fromX;
- this.fromY = fromY;
- this.toX = toX;
- this.toY = toY;
- }
-
- @Override
- public String toString() {
- return "ChangeInfo{"
- + "oldHolder=" + oldHolder
- + ", newHolder=" + newHolder
- + ", fromX=" + fromX
- + ", fromY=" + fromY
- + ", toX=" + toX
- + ", toY=" + toY
- + '}';
- }
- }
-
- @Override
- public void runPendingAnimations() {
- boolean removalsPending = !mPendingRemovals.isEmpty();
- boolean movesPending = !mPendingMoves.isEmpty();
- boolean changesPending = !mPendingChanges.isEmpty();
- boolean additionsPending = !mPendingAdditions.isEmpty();
- if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
- // nothing to animate
- return;
- }
- // First, remove stuff
- for (ViewHolder holder : mPendingRemovals) {
- animateRemoveImpl(holder);
- }
- mPendingRemovals.clear();
- // Next, move stuff
- if (movesPending) {
- final ArrayList<MoveInfo> moves = new ArrayList<>();
- moves.addAll(mPendingMoves);
- mMovesList.add(moves);
- mPendingMoves.clear();
- Runnable mover = new Runnable() {
- @Override
- public void run() {
- for (MoveInfo moveInfo : moves) {
- animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
- moveInfo.toX, moveInfo.toY);
- }
- moves.clear();
- mMovesList.remove(moves);
- }
- };
- if (removalsPending) {
- View view = moves.get(0).holder.itemView;
- ViewCompat.postOnAnimationDelayed(view, mover, 0);
- } else {
- mover.run();
- }
- }
- // Next, change stuff, to run in parallel with move animations
- if (changesPending) {
- final ArrayList<ChangeInfo> changes = new ArrayList<>();
- changes.addAll(mPendingChanges);
- mChangesList.add(changes);
- mPendingChanges.clear();
- Runnable changer = new Runnable() {
- @Override
- public void run() {
- for (ChangeInfo change : changes) {
- animateChangeImpl(change);
- }
- changes.clear();
- mChangesList.remove(changes);
- }
- };
- if (removalsPending) {
- ViewHolder holder = changes.get(0).oldHolder;
- long moveDuration = movesPending ? getMoveDuration() : 0;
- ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDelay(getRemoveDuration(), moveDuration, getChangeDuration()));
- } else {
- changer.run();
- }
- }
- // Next, add stuff
- if (additionsPending) {
- final ArrayList<ViewHolder> additions = new ArrayList<>();
- additions.addAll(mPendingAdditions);
- mAdditionsList.add(additions);
- mPendingAdditions.clear();
- Runnable adder = new Runnable() {
- @Override
- public void run() {
- for (ViewHolder holder : additions) {
- animateAddImpl(holder);
- }
- additions.clear();
- mAdditionsList.remove(additions);
- }
- };
- if (removalsPending || movesPending || changesPending) {
- long removeDuration = removalsPending ? getRemoveDuration() : 0;
- long moveDuration = movesPending ? getMoveDuration() : 0;
- long changeDuration = changesPending ? getChangeDuration() : 0;
- View view = additions.get(0).itemView;
- ViewCompat.postOnAnimationDelayed(view, adder, getAddDelay(removeDuration, moveDuration, changeDuration));
- } else {
- adder.run();
- }
- }
- }
-
- /**
- * used to calculated the delay until the remove animation should start
- *
- * @param remove the remove duration
- * @param move the move duration
- * @param change the change duration
- * @return the calculated delay for the remove items animation
- */
- public long getRemoveDelay(long remove, long move, long change) {
- return remove + Math.max(move, change);
- }
-
- /**
- * used to calculated the delay until the add animation should start
- *
- * @param remove the remove duration
- * @param move the move duration
- * @param change the change duration
- * @return the calculated delay for the add items animation
- */
- public long getAddDelay(long remove, long move, long change) {
- return remove + Math.max(move, change);
- }
-
- @Override
- public boolean animateRemove(final ViewHolder holder) {
- resetAnimation(holder);
- mPendingRemovals.add(holder);
- return true;
- }
-
- private void animateRemoveImpl(final ViewHolder holder) {
- final ViewPropertyAnimator animation = removeAnimation(holder);
- mRemoveAnimations.add(holder);
- animation.setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- dispatchRemoveStarting(holder);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- animation.setListener(null);
- removeAnimationCleanup(holder);
- dispatchRemoveFinished(holder);
- mRemoveAnimations.remove(holder);
- dispatchFinishedWhenDone();
- }
- }).start();
- }
-
- abstract public ViewPropertyAnimator removeAnimation(ViewHolder holder);
-
- abstract public void removeAnimationCleanup(ViewHolder holder);
-
- @Override
- public boolean animateAdd(final ViewHolder holder) {
- resetAnimation(holder);
- addAnimationPrepare(holder);
- mPendingAdditions.add(holder);
- return true;
- }
-
- void animateAddImpl(final ViewHolder holder) {
- final ViewPropertyAnimator animation = addAnimation(holder);
- mAddAnimations.add(holder);
- animation.setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- dispatchAddStarting(holder);
- }
-
- @Override
- public void onAnimationCancel(Animator animator) {
- addAnimationCleanup(holder);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- animation.setListener(null);
- dispatchAddFinished(holder);
- mAddAnimations.remove(holder);
- dispatchFinishedWhenDone();
- addAnimationCleanup(holder);
- }
- }).start();
- }
-
- /**
- * the animation to prepare the view before the add animation is run
- *
- * @param holder
- */
- public abstract void addAnimationPrepare(ViewHolder holder);
-
- /**
- * the animation for adding a view
- *
- * @param holder
- * @return
- */
- public abstract ViewPropertyAnimator addAnimation(ViewHolder holder);
-
- /**
- * the cleanup method if the animation needs to be stopped. and tro prepare for the next view
- *
- * @param holder
- */
- abstract void addAnimationCleanup(ViewHolder holder);
-
- @Override
- public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
- int toX, int toY) {
- final View view = holder.itemView;
- fromX += (int) holder.itemView.getTranslationX();
- fromY += (int) holder.itemView.getTranslationY();
- resetAnimation(holder);
- int deltaX = toX - fromX;
- int deltaY = toY - fromY;
- if (deltaX == 0 && deltaY == 0) {
- dispatchMoveFinished(holder);
- return false;
- }
- if (deltaX != 0) {
- view.setTranslationX(-deltaX);
- }
- if (deltaY != 0) {
- view.setTranslationY(-deltaY);
- }
- mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
- return true;
- }
-
- void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
- final View view = holder.itemView;
- final int deltaX = toX - fromX;
- final int deltaY = toY - fromY;
- if (deltaX != 0) {
- view.animate().translationX(0);
- }
- if (deltaY != 0) {
- view.animate().translationY(0);
- }
- // TODO: make EndActions end listeners instead, since end actions aren't called when
- // vpas are canceled (and can't end them. why?)
- // need listener functionality in VPACompat for this. Ick.
- final ViewPropertyAnimator animation = view.animate();
- mMoveAnimations.add(holder);
- animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- dispatchMoveStarting(holder);
- }
-
- @Override
- public void onAnimationCancel(Animator animator) {
- if (deltaX != 0) {
- view.setTranslationX(0);
- }
- if (deltaY != 0) {
- view.setTranslationY(0);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- animation.setListener(null);
- dispatchMoveFinished(holder);
- mMoveAnimations.remove(holder);
- dispatchFinishedWhenDone();
- }
- }).start();
- }
-
- @Override
- public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
- int fromX, int fromY, int toX, int toY) {
- if (oldHolder == newHolder) {
- // Don't know how to run change animations when the same view holder is re-used.
- // run a move animation to handle position changes.
- return animateMove(oldHolder, fromX, fromY, toX, toY);
- }
- changeAnimation(oldHolder, newHolder,
- fromX, fromY, toX, toY);
- mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
- return true;
- }
-
- void animateChangeImpl(final ChangeInfo changeInfo) {
- final ViewHolder holder = changeInfo.oldHolder;
- final View view = holder == null ? null : holder.itemView;
- final ViewHolder newHolder = changeInfo.newHolder;
- final View newView = newHolder != null ? newHolder.itemView : null;
- if (view != null) {
- final ViewPropertyAnimator oldViewAnim = changeOldAnimation(holder, changeInfo);
- mChangeAnimations.add(changeInfo.oldHolder);
- oldViewAnim.setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- dispatchChangeStarting(changeInfo.oldHolder, true);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- oldViewAnim.setListener(null);
- changeAnimationCleanup(holder);
- view.setTranslationX(0);
- view.setTranslationY(0);
- dispatchChangeFinished(changeInfo.oldHolder, true);
- mChangeAnimations.remove(changeInfo.oldHolder);
- dispatchFinishedWhenDone();
- }
- }).start();
- }
- if (newView != null) {
- final ViewPropertyAnimator newViewAnimation = changeNewAnimation(newHolder);
- mChangeAnimations.add(changeInfo.newHolder);
- newViewAnimation.setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- dispatchChangeStarting(changeInfo.newHolder, false);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- newViewAnimation.setListener(null);
- changeAnimationCleanup(newHolder);
- newView.setTranslationX(0);
- newView.setTranslationY(0);
- dispatchChangeFinished(changeInfo.newHolder, false);
- mChangeAnimations.remove(changeInfo.newHolder);
- dispatchFinishedWhenDone();
- }
- }).start();
- }
- }
-
- /**
- * the whole change animation if we have to cross animate two views
- *
- * @param oldHolder
- * @param newHolder
- * @param fromX
- * @param fromY
- * @param toX
- * @param toY
- */
- public void changeAnimation(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
- final float prevTranslationX = oldHolder.itemView.getTranslationX();
- final float prevTranslationY = oldHolder.itemView.getTranslationY();
- final float prevValue = oldHolder.itemView.getAlpha();
- resetAnimation(oldHolder);
- int deltaX = (int) (toX - fromX - prevTranslationX);
- int deltaY = (int) (toY - fromY - prevTranslationY);
- // recover prev translation state after ending animation
- oldHolder.itemView.setTranslationX(prevTranslationX);
- oldHolder.itemView.setTranslationY(prevTranslationY);
-
- oldHolder.itemView.setAlpha(prevValue);
- if (newHolder != null) {
- // carry over translation values
- resetAnimation(newHolder);
- newHolder.itemView.setTranslationX(-deltaX);
- newHolder.itemView.setTranslationY(-deltaY);
- newHolder.itemView.setAlpha(0);
- }
- }
-
- /**
- * the animation for removing the old view
- *
- * @param holder
- * @return
- */
- public abstract ViewPropertyAnimator changeOldAnimation(ViewHolder holder, ChangeInfo changeInfo);
-
- /**
- * the animation for changing the new view
- *
- * @param holder
- * @return
- */
- public abstract ViewPropertyAnimator changeNewAnimation(ViewHolder holder);
-
- /**
- * the cleanup method if the animation needs to be stopped. and tro prepare for the next view
- *
- * @param holder
- */
- public abstract void changeAnimationCleanup(ViewHolder holder);
-
- private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {
- for (int i = infoList.size() - 1; i >= 0; i--) {
- ChangeInfo changeInfo = infoList.get(i);
- if (endChangeAnimationIfNecessary(changeInfo, item)) {
- if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
- infoList.remove(changeInfo);
- }
- }
- }
- }
-
- private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
- if (changeInfo.oldHolder != null) {
- endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
- }
- if (changeInfo.newHolder != null) {
- endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
- }
- }
-
- private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
- boolean oldItem = false;
- if (changeInfo.newHolder == item) {
- changeInfo.newHolder = null;
- } else if (changeInfo.oldHolder == item) {
- changeInfo.oldHolder = null;
- oldItem = true;
- } else {
- return false;
- }
- changeAnimationCleanup(item);
- item.itemView.setTranslationX(0);
- item.itemView.setTranslationY(0);
- dispatchChangeFinished(item, oldItem);
- return true;
- }
-
- @Override
- public void endAnimation(ViewHolder item) {
- final View view = item.itemView;
- // this will trigger end callback which should set properties to their target values.
- view.animate().cancel();
- // TODO if some other animations are chained to end, how do we cancel them as well?
- for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
- MoveInfo moveInfo = mPendingMoves.get(i);
- if (moveInfo.holder == item) {
- view.setTranslationY(0);
- view.setTranslationX(0);
- dispatchMoveFinished(item);
- mPendingMoves.remove(i);
- }
- }
- endChangeAnimation(mPendingChanges, item);
- if (mPendingRemovals.remove(item)) {
- removeAnimationCleanup(item);
- dispatchRemoveFinished(item);
- }
- if (mPendingAdditions.remove(item)) {
- addAnimationCleanup(item);
- dispatchAddFinished(item);
- }
-
- for (int i = mChangesList.size() - 1; i >= 0; i--) {
- ArrayList<ChangeInfo> changes = mChangesList.get(i);
- endChangeAnimation(changes, item);
- if (changes.isEmpty()) {
- mChangesList.remove(i);
- }
- }
- for (int i = mMovesList.size() - 1; i >= 0; i--) {
- ArrayList<MoveInfo> moves = mMovesList.get(i);
- for (int j = moves.size() - 1; j >= 0; j--) {
- MoveInfo moveInfo = moves.get(j);
- if (moveInfo.holder == item) {
- view.setTranslationY(0);
- view.setTranslationX(0);
- dispatchMoveFinished(item);
- moves.remove(j);
- if (moves.isEmpty()) {
- mMovesList.remove(i);
- }
- break;
- }
- }
- }
- for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
- ArrayList<ViewHolder> additions = mAdditionsList.get(i);
- if (additions.remove(item)) {
- addAnimationCleanup(item);
- dispatchAddFinished(item);
- if (additions.isEmpty()) {
- mAdditionsList.remove(i);
- }
- }
- }
-
- // animations should be ended by the cancel above.
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (mRemoveAnimations.remove(item) && DEBUG) {
- throw new IllegalStateException("after animation is cancelled, item should not be in "
- + "mRemoveAnimations list");
- }
-
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (mAddAnimations.remove(item) && DEBUG) {
- throw new IllegalStateException("after animation is cancelled, item should not be in "
- + "mAddAnimations list");
- }
-
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (mChangeAnimations.remove(item) && DEBUG) {
- throw new IllegalStateException("after animation is cancelled, item should not be in "
- + "mChangeAnimations list");
- }
-
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (mMoveAnimations.remove(item) && DEBUG) {
- throw new IllegalStateException("after animation is cancelled, item should not be in "
- + "mMoveAnimations list");
- }
- dispatchFinishedWhenDone();
- }
-
- private void resetAnimation(ViewHolder holder) {
-
- if (sDefaultInterpolator == null) {
- sDefaultInterpolator = new ValueAnimator().getInterpolator();
- }
- holder.itemView.animate().setInterpolator(sDefaultInterpolator);
- endAnimation(holder);
- }
-
- @Override
- public boolean isRunning() {
- return (!mPendingAdditions.isEmpty()
- || !mPendingChanges.isEmpty()
- || !mPendingMoves.isEmpty()
- || !mPendingRemovals.isEmpty()
- || !mMoveAnimations.isEmpty()
- || !mRemoveAnimations.isEmpty()
- || !mAddAnimations.isEmpty()
- || !mChangeAnimations.isEmpty()
- || !mMovesList.isEmpty()
- || !mAdditionsList.isEmpty()
- || !mChangesList.isEmpty());
- }
-
- /**
- * Check the state of currently pending and running animations. If there are none
- * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
- * listeners.
- */
- void dispatchFinishedWhenDone() {
- if (!isRunning()) {
- dispatchAnimationsFinished();
- }
- }
-
- @Override
- public void endAnimations() {
- int count = mPendingMoves.size();
- for (int i = count - 1; i >= 0; i--) {
- MoveInfo item = mPendingMoves.get(i);
- View view = item.holder.itemView;
- view.setTranslationY(0);
- view.setTranslationX(0);
- dispatchMoveFinished(item.holder);
- mPendingMoves.remove(i);
- }
- count = mPendingRemovals.size();
- for (int i = count - 1; i >= 0; i--) {
- ViewHolder item = mPendingRemovals.get(i);
- dispatchRemoveFinished(item);
- mPendingRemovals.remove(i);
- }
- count = mPendingAdditions.size();
- for (int i = count - 1; i >= 0; i--) {
- ViewHolder item = mPendingAdditions.get(i);
- addAnimationCleanup(item);
- dispatchAddFinished(item);
- mPendingAdditions.remove(i);
- }
- count = mPendingChanges.size();
- for (int i = count - 1; i >= 0; i--) {
- endChangeAnimationIfNecessary(mPendingChanges.get(i));
- }
- mPendingChanges.clear();
- if (!isRunning()) {
- return;
- }
-
- int listCount = mMovesList.size();
- for (int i = listCount - 1; i >= 0; i--) {
- ArrayList<MoveInfo> moves = mMovesList.get(i);
- count = moves.size();
- for (int j = count - 1; j >= 0; j--) {
- MoveInfo moveInfo = moves.get(j);
- ViewHolder item = moveInfo.holder;
- View view = item.itemView;
- view.setTranslationY(0);
- view.setTranslationX(0);
- dispatchMoveFinished(moveInfo.holder);
- moves.remove(j);
- if (moves.isEmpty()) {
- mMovesList.remove(moves);
- }
- }
- }
- listCount = mAdditionsList.size();
- for (int i = listCount - 1; i >= 0; i--) {
- ArrayList<ViewHolder> additions = mAdditionsList.get(i);
- count = additions.size();
- for (int j = count - 1; j >= 0; j--) {
- ViewHolder item = additions.get(j);
- View view = item.itemView;
- addAnimationCleanup(item);
- dispatchAddFinished(item);
- additions.remove(j);
- if (additions.isEmpty()) {
- mAdditionsList.remove(additions);
- }
- }
- }
- listCount = mChangesList.size();
- for (int i = listCount - 1; i >= 0; i--) {
- ArrayList<ChangeInfo> changes = mChangesList.get(i);
- count = changes.size();
- for (int j = count - 1; j >= 0; j--) {
- endChangeAnimationIfNecessary(changes.get(j));
- if (changes.isEmpty()) {
- mChangesList.remove(changes);
- }
- }
- }
-
- cancelAll(mRemoveAnimations);
- cancelAll(mMoveAnimations);
- cancelAll(mAddAnimations);
- cancelAll(mChangeAnimations);
-
- dispatchAnimationsFinished();
- }
-
- void cancelAll(List<ViewHolder> viewHolders) {
- for (int i = viewHolders.size() - 1; i >= 0; i--) {
- viewHolders.get(i).itemView.animate().cancel();
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
- * When this is the case:
- * <ul>
- * <li>If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both
- * ViewHolder arguments will be the same instance.
- * </li>
- * <li>
- * If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)},
- * then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and
- * run a move animation instead.
- * </li>
- * </ul>
- */
- @Override
- public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
- @NonNull List<Object> payloads) {
- return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
- }
-}
diff --git a/core/src/main/kotlin/ca/allanwang/kau/animators/BaseSlideAlphaAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/animators/BaseSlideAlphaAnimator.kt
deleted file mode 100644
index a963358..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/animators/BaseSlideAlphaAnimator.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package ca.allanwang.kau.animators
-
-import android.support.v7.widget.RecyclerView
-import android.view.ViewPropertyAnimator
-
-/**
- * Created by Allan Wang on 2017-06-27.
- *
- * Base for sliding animators
- * item delay factor by default can be 0.125f
- */
-abstract class BaseSlideAlphaAnimator(itemDelayFactor: Float) : BaseDelayAnimator(itemDelayFactor) {
-
- override fun addAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return super.addAnimation(holder).apply {
- translationY(0f)
- translationX(0f)
- alpha(1f)
- }
- }
-
- final override fun addAnimationCleanup(holder: RecyclerView.ViewHolder) {
- with(holder.itemView) {
- translationY = 0f
- translationX = 0f
- alpha = 1f
- }
- }
-
- override fun getAddDelay(remove: Long, move: Long, change: Long): Long = 0
-
- override fun getRemoveDelay(remove: Long, move: Long, change: Long): Long = 0
-
- /**
- * Partial removal animation
- * As of now, all it does it change the alpha
- * To have it slide, add onto it in a sub class
- */
- override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return super.addAnimation(holder).apply {
- alpha(0f)
- }
- }
-
- override final fun removeAnimationCleanup(holder: RecyclerView.ViewHolder) {
- with(holder.itemView) {
- translationY = 0f
- translationX = 0f
- alpha = 1f
- }
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt
deleted file mode 100644
index 9aeafde..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-package ca.allanwang.kau.animators
-
-import android.support.v7.widget.RecyclerView
-import android.view.ViewPropertyAnimator
-
-/**
- * Created by Allan Wang on 2017-06-27.
- */
-open class DefaultAnimator : BaseItemAnimator() {
-
- override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return holder.itemView.animate().apply {
- alpha(0f)
- duration = this@DefaultAnimator.removeDuration
- interpolator = this@DefaultAnimator.interpolator
- }
- }
-
- override fun removeAnimationCleanup(holder: RecyclerView.ViewHolder) {
- holder.itemView.alpha = 1f
- }
-
- override fun addAnimationPrepare(holder: RecyclerView.ViewHolder) {
- holder.itemView.alpha = 0f
- }
-
- override fun addAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return holder.itemView.animate().apply {
- alpha(1f)
- duration = this@DefaultAnimator.addDuration
- interpolator = this@DefaultAnimator.interpolator
- }
- }
-
- override fun addAnimationCleanup(holder: RecyclerView.ViewHolder) {
- holder.itemView.alpha = 1f
- }
-
- override fun changeOldAnimation(holder: RecyclerView.ViewHolder, changeInfo: ChangeInfo): ViewPropertyAnimator {
- return holder.itemView.animate().apply {
- alpha(0f)
- translationX(changeInfo.toX.toFloat() - changeInfo.fromX)
- translationY(changeInfo.toY.toFloat() - changeInfo.fromY)
- duration = this@DefaultAnimator.changeDuration
- interpolator = this@DefaultAnimator.interpolator
- }
- }
-
- override fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return holder.itemView.animate().apply {
- alpha(1f)
- translationX(0f)
- translationY(0f)
- duration = this@DefaultAnimator.changeDuration
- interpolator = this@DefaultAnimator.interpolator
- }
- }
-
- override fun changeAnimationCleanup(holder: RecyclerView.ViewHolder) {
- holder.itemView.alpha = 1f
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt
deleted file mode 100644
index e968cda..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package ca.allanwang.kau.animators
-
-import android.support.v7.widget.RecyclerView
-import android.view.ViewPropertyAnimator
-
-/**
- * Created by Allan Wang on 2017-06-29.
- */
-open class FadeScaleAnimator(val scaleFactor: Float = 0.7f, itemDelayFactor: Float = 0.125f) : BaseDelayAnimator(itemDelayFactor) {
-
- override fun addAnimationPrepare(holder: RecyclerView.ViewHolder) {
- with(holder.itemView) {
- scaleX = scaleFactor
- scaleY = scaleFactor
- alpha = 0f
- }
- }
-
- override final fun addAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return super.addAnimation(holder).apply {
- scaleX(1f)
- scaleY(1f)
- alpha(1f)
- }
- }
-
-
- final override fun addAnimationCleanup(holder: RecyclerView.ViewHolder) {
- with(holder.itemView) {
- scaleX = 1f
- scaleY = 1f
- alpha = 1f
- }
- }
-
- override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return super.removeAnimation(holder).apply {
- scaleX(scaleFactor)
- scaleY(scaleFactor)
- alpha(0f)
- }
- }
-
- override final fun removeAnimationCleanup(holder: RecyclerView.ViewHolder) {
- with(holder.itemView) {
- translationY = 0f
- translationX = 0f
- alpha = 1f
- }
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt
deleted file mode 100644
index 244287b..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package ca.allanwang.kau.animators
-
-import android.support.v7.widget.RecyclerView
-import android.view.ViewPropertyAnimator
-
-/**
- * Created by Allan Wang on 2017-06-27.
- *
- * Truly have no animation
- */
-class NoAnimator : DefaultAnimator() {
- override fun addAnimationPrepare(holder: RecyclerView.ViewHolder) {}
-
- override fun addAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator = holder.itemView.animate().apply { duration = 0 }
-
- override fun addAnimationCleanup(holder: RecyclerView.ViewHolder) {}
-
- override fun changeOldAnimation(holder: RecyclerView.ViewHolder, changeInfo: ChangeInfo): ViewPropertyAnimator = holder.itemView.animate().apply { duration = 0 }
-
- override fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator = holder.itemView.animate().apply { duration = 0 }
-
- override fun changeAnimationCleanup(holder: RecyclerView.ViewHolder) {}
-
- override fun changeAnimation(oldHolder: RecyclerView.ViewHolder, newHolder: RecyclerView.ViewHolder?, fromX: Int, fromY: Int, toX: Int, toY: Int) {}
-
- override fun getAddDelay(remove: Long, move: Long, change: Long): Long = 0
-
- override fun getAddDuration(): Long = 0
-
- override fun getMoveDuration(): Long = 0
-
- override fun getRemoveDuration(): Long = 0
-
- override fun getChangeDuration(): Long = 0
-
- override fun getRemoveDelay(remove: Long, move: Long, change: Long): Long = 0
-
- override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator = holder.itemView.animate().apply { duration = 0 }
-
- override fun removeAnimationCleanup(holder: RecyclerView.ViewHolder) {}
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/animators/SlideUpExitRightAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/animators/SlideUpExitRightAnimator.kt
deleted file mode 100644
index 8670493..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/animators/SlideUpExitRightAnimator.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package ca.allanwang.kau.animators
-
-import android.support.v7.widget.RecyclerView
-import android.view.ViewPropertyAnimator
-
-/**
- * Created by Allan Wang on 2017-06-27.
- */
-class SlideUpExitRightAnimator(itemDelayFactor: Float = 0.125f) : BaseSlideAlphaAnimator(itemDelayFactor) {
-
- override fun addAnimationPrepare(holder: RecyclerView.ViewHolder) {
- with(holder.itemView) {
- translationY = height.toFloat()
- alpha = 0f
- }
- }
-
- override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator {
- return super.removeAnimation(holder).apply {
- translationX(holder.itemView.width.toFloat())
- }
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt b/core/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt
index 302d9dc..43546ae 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt
@@ -2,7 +2,6 @@ package ca.allanwang.kau.changelog
import android.content.Context
import android.content.res.XmlResourceParser
-import android.os.Handler
import android.support.annotation.ColorInt
import android.support.annotation.LayoutRes
import android.support.annotation.XmlRes
@@ -20,7 +19,6 @@ import com.afollestad.materialdialogs.MaterialDialog
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread
import org.xmlpull.v1.XmlPullParser
-import java.util.*
/**
diff --git a/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/CircleView.kt b/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/CircleView.kt
deleted file mode 100644
index 3430b42..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/CircleView.kt
+++ /dev/null
@@ -1,228 +0,0 @@
-package ca.allanwang.kau.dialogs.color
-
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.ColorStateList
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.Rect
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.RippleDrawable
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.StateListDrawable
-import android.graphics.drawable.shapes.OvalShape
-import android.os.Build
-import android.support.annotation.ColorInt
-import android.support.annotation.ColorRes
-import android.support.annotation.FloatRange
-import android.support.v4.view.GravityCompat
-import android.support.v4.view.ViewCompat
-import android.util.AttributeSet
-import android.view.Gravity
-import android.widget.FrameLayout
-import android.widget.Toast
-import ca.allanwang.kau.utils.color
-import ca.allanwang.kau.utils.getDip
-import ca.allanwang.kau.utils.toColor
-import ca.allanwang.kau.utils.toHSV
-
-/**
- * Created by Allan Wang on 2017-06-10.
- *
- * An extension of MaterialDialog's CircleView with animation selection
- * [https://github.com/afollestad/material-dialogs/blob/master/commons/src/main/java/com/afollestad/materialdialogs/color/CircleView.java]
- */
-class CircleView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr) {
-
- private val borderWidthMicro: Float = context.getDip(1f)
- private val borderWidthSmall: Float = context.getDip(3f)
- private val borderWidthLarge: Float = context.getDip(5f)
- private var whiteOuterBound: Float = borderWidthLarge
-
- private val outerPaint: Paint = Paint().apply { isAntiAlias = true }
- private val whitePaint: Paint = Paint().apply { isAntiAlias = true; color = Color.WHITE }
- private val innerPaint: Paint = Paint().apply { isAntiAlias = true }
- private var selected: Boolean = false
- var withBorder: Boolean = false
- get() = field
- set(value) {
- if (field != value) {
- field = value
- invalidate()
- }
- }
-
- init {
- update(Color.DKGRAY)
- setWillNotDraw(false)
- }
-
- private fun update(@ColorInt color: Int) {
- innerPaint.color = color
- outerPaint.color = shiftColorDown(color)
-
- val selector = createSelector(color)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- val states = arrayOf(intArrayOf(android.R.attr.state_pressed))
- val colors = intArrayOf(shiftColorUp(color))
- val rippleColors = ColorStateList(states, colors)
- foreground = RippleDrawable(rippleColors, selector, null)
- } else {
- foreground = selector
- }
- }
-
- override fun setBackgroundColor(@ColorInt color: Int) {
- update(color)
- requestLayout()
- invalidate()
- }
-
- override fun setBackgroundResource(@ColorRes color: Int) {
- setBackgroundColor(context.color(color))
- }
-
-
- @Deprecated("")
- override fun setBackground(background: Drawable) {
- throw IllegalStateException("Cannot use setBackground() on CircleView.")
- }
-
-
- @Deprecated("")
- override fun setBackgroundDrawable(background: Drawable) {
- throw IllegalStateException("Cannot use setBackgroundDrawable() on CircleView.")
- }
-
-
- @Deprecated("")
- override fun setActivated(activated: Boolean) {
- throw IllegalStateException("Cannot use setActivated() on CircleView.")
- }
-
- override fun setSelected(selected: Boolean) {
- this.selected = selected
- whiteOuterBound = borderWidthLarge
- invalidate()
- }
-
- fun animateSelected(selected: Boolean) {
- if (this.selected == selected) return
- this.selected = true // We need to draw the other bands
- val range = if (selected) Pair(-borderWidthSmall, borderWidthLarge) else Pair(borderWidthLarge, -borderWidthSmall)
- val animator = ValueAnimator.ofFloat(range.first, range.second)
- with(animator) {
- reverse()
- duration = 150L
- addUpdateListener { animation ->
- whiteOuterBound = animation.animatedValue as Float
- invalidate()
- }
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- this@CircleView.selected = selected
- }
-
- override fun onAnimationCancel(animation: Animator) {
- this@CircleView.selected = selected
- }
- })
- start()
- }
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, widthMeasureSpec)
- setMeasuredDimension(measuredWidth, measuredWidth)
- }
-
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
- val centerWidth = (measuredWidth / 2).toFloat()
- val centerHeight = (measuredHeight / 2).toFloat()
- if (withBorder) canvas.drawCircle(centerWidth, centerHeight, centerWidth, whitePaint)
- if (selected) {
- val whiteRadius = centerWidth - whiteOuterBound
- val innerRadius = whiteRadius - borderWidthSmall
- if (whiteRadius >= centerWidth) {
- canvas.drawCircle(centerWidth, centerHeight, centerWidth, whitePaint)
- } else {
- canvas.drawCircle(centerWidth, centerHeight, if (withBorder) centerWidth - borderWidthMicro else centerWidth, outerPaint)
- canvas.drawCircle(centerWidth, centerHeight, whiteRadius, whitePaint)
- }
- canvas.drawCircle(centerWidth, centerHeight, innerRadius, innerPaint)
- } else {
- canvas.drawCircle(centerWidth, centerHeight, if (withBorder) centerWidth - borderWidthMicro else centerWidth, innerPaint)
- }
- }
-
- private fun createSelector(color: Int): Drawable {
- val darkerCircle = ShapeDrawable(OvalShape())
- darkerCircle.paint.color = translucentColor(shiftColorUp(color))
- val stateListDrawable = StateListDrawable()
- stateListDrawable.addState(intArrayOf(android.R.attr.state_pressed), darkerCircle)
- return stateListDrawable
- }
-
- fun showHint(color: Int) {
- val screenPos = IntArray(2)
- val displayFrame = Rect()
- getLocationOnScreen(screenPos)
- getWindowVisibleDisplayFrame(displayFrame)
- val context = context
- val width = width
- val height = height
- val midy = screenPos[1] + height / 2
- var referenceX = screenPos[0] + width / 2
- if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR) {
- val screenWidth = context.resources.displayMetrics.widthPixels
- referenceX = screenWidth - referenceX // mirror
- }
- val cheatSheet = Toast
- .makeText(context, String.format("#%06X", 0xFFFFFF and color), Toast.LENGTH_SHORT)
- if (midy < displayFrame.height()) {
- // Show along the top; follow action buttons
- cheatSheet.setGravity(Gravity.TOP or GravityCompat.END, referenceX,
- screenPos[1] + height - displayFrame.top)
- } else {
- // Show along the bottom center
- cheatSheet.setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL, 0, height)
- }
- cheatSheet.show()
- }
-
- companion object {
-
- @ColorInt
- @JvmStatic
- private fun translucentColor(color: Int): Int {
- val factor = 0.7f
- val alpha = Math.round(Color.alpha(color) * factor)
- val red = Color.red(color)
- val green = Color.green(color)
- val blue = Color.blue(color)
- return Color.argb(alpha, red, green, blue)
- }
-
- @ColorInt
- @JvmStatic
- fun shiftColor(@ColorInt color: Int,
- @FloatRange(from = 0.0, to = 2.0) by: Float): Int {
- if (by == 1f) return color
- val hsv = color.toHSV()
- hsv[2] *= by // value component
- return hsv.toColor()
- }
-
- @ColorInt
- @JvmStatic
- fun shiftColorDown(@ColorInt color: Int): Int = shiftColor(color, 0.9f)
-
- @ColorInt
- @JvmStatic
- fun shiftColorUp(@ColorInt color: Int): Int = shiftColor(color, 1.1f)
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPalette.kt b/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPalette.kt
deleted file mode 100644
index 22bd0d4..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPalette.kt
+++ /dev/null
@@ -1,349 +0,0 @@
-package ca.allanwang.kau.dialogs.color
-
-import android.graphics.Color
-
-/**
- * @author Aidan Follestad (afollestad)
- */
-internal object ColorPalette {
-
- val PRIMARY_COLORS: IntArray by lazy {
- colorArrayOf(
- "#F44336",
- "#E91E63",
- "#9C27B0",
- "#673AB7",
- "#3F51B5",
- "#2196F3",
- "#03A9F4",
- "#00BCD4",
- "#009688",
- "#4CAF50",
- "#8BC34A",
- "#CDDC39",
- "#FFEB3B",
- "#FFC107",
- "#FF9800",
- "#FF5722",
- "#795548",
- "#9E9E9E",
- "#607D8B")
- }
-
- val PRIMARY_COLORS_SUB: Array<IntArray> by lazy {
- arrayOf(colorArrayOf(
- "#FFEBEE",
- "#FFCDD2",
- "#EF9A9A",
- "#E57373",
- "#EF5350",
- "#F44336",
- "#E53935",
- "#D32F2F",
- "#C62828",
- "#B71C1C"
- ), colorArrayOf(
- "#FCE4EC",
- "#F8BBD0",
- "#F48FB1",
- "#F06292",
- "#EC407A",
- "#E91E63",
- "#D81B60",
- "#C2185B",
- "#AD1457",
- "#880E4F"
- ), colorArrayOf(
- "#F3E5F5",
- "#E1BEE7",
- "#CE93D8",
- "#BA68C8",
- "#AB47BC",
- "#9C27B0",
- "#8E24AA",
- "#7B1FA2",
- "#6A1B9A",
- "#4A148C"
- ), colorArrayOf(
- "#EDE7F6",
- "#D1C4E9",
- "#B39DDB",
- "#9575CD",
- "#7E57C2",
- "#673AB7",
- "#5E35B1",
- "#512DA8",
- "#4527A0",
- "#311B92"
- ), colorArrayOf(
- "#E8EAF6",
- "#C5CAE9",
- "#9FA8DA",
- "#7986CB",
- "#5C6BC0",
- "#3F51B5",
- "#3949AB",
- "#303F9F",
- "#283593",
- "#1A237E"
- ), colorArrayOf(
- "#E3F2FD",
- "#BBDEFB",
- "#90CAF9",
- "#64B5F6",
- "#42A5F5",
- "#2196F3",
- "#1E88E5",
- "#1976D2",
- "#1565C0",
- "#0D47A1"
- ), colorArrayOf(
- "#E1F5FE",
- "#B3E5FC",
- "#81D4FA",
- "#4FC3F7",
- "#29B6F6",
- "#03A9F4",
- "#039BE5",
- "#0288D1",
- "#0277BD",
- "#01579B"
- ), colorArrayOf(
- "#E0F7FA",
- "#B2EBF2",
- "#80DEEA",
- "#4DD0E1",
- "#26C6DA",
- "#00BCD4",
- "#00ACC1",
- "#0097A7",
- "#00838F",
- "#006064"
- ), colorArrayOf(
- "#E0F2F1",
- "#B2DFDB",
- "#80CBC4",
- "#4DB6AC",
- "#26A69A",
- "#009688",
- "#00897B",
- "#00796B",
- "#00695C",
- "#004D40"
- ), colorArrayOf(
- "#E8F5E9",
- "#C8E6C9",
- "#A5D6A7",
- "#81C784",
- "#66BB6A",
- "#4CAF50",
- "#43A047",
- "#388E3C",
- "#2E7D32",
- "#1B5E20"
- ), colorArrayOf(
- "#F1F8E9",
- "#DCEDC8",
- "#C5E1A5",
- "#AED581",
- "#9CCC65",
- "#8BC34A",
- "#7CB342",
- "#689F38",
- "#558B2F",
- "#33691E"
- ), colorArrayOf(
- "#F9FBE7",
- "#F0F4C3",
- "#E6EE9C",
- "#DCE775",
- "#D4E157",
- "#CDDC39",
- "#C0CA33",
- "#AFB42B",
- "#9E9D24",
- "#827717"
- ), colorArrayOf(
- "#FFFDE7",
- "#FFF9C4",
- "#FFF59D",
- "#FFF176",
- "#FFEE58",
- "#FFEB3B",
- "#FDD835",
- "#FBC02D",
- "#F9A825",
- "#F57F17"
- ), colorArrayOf(
- "#FFF8E1",
- "#FFECB3",
- "#FFE082",
- "#FFD54F",
- "#FFCA28",
- "#FFC107",
- "#FFB300",
- "#FFA000",
- "#FF8F00",
- "#FF6F00"
- ), colorArrayOf(
- "#FFF3E0",
- "#FFE0B2",
- "#FFCC80",
- "#FFB74D",
- "#FFA726",
- "#FF9800",
- "#FB8C00",
- "#F57C00",
- "#EF6C00",
- "#E65100"
- ), colorArrayOf(
- "#FBE9E7",
- "#FFCCBC",
- "#FFAB91",
- "#FF8A65",
- "#FF7043",
- "#FF5722",
- "#F4511E",
- "#E64A19",
- "#D84315",
- "#BF360C"
- ), colorArrayOf(
- "#EFEBE9",
- "#D7CCC8",
- "#BCAAA4",
- "#A1887F",
- "#8D6E63",
- "#795548",
- "#6D4C41",
- "#5D4037",
- "#4E342E",
- "#3E2723"
- ), colorArrayOf(
- "#FAFAFA",
- "#F5F5F5",
- "#EEEEEE",
- "#E0E0E0",
- "#BDBDBD",
- "#9E9E9E",
- "#757575",
- "#616161",
- "#424242",
- "#212121"
- ), colorArrayOf(
- "#ECEFF1",
- "#CFD8DC",
- "#B0BEC5",
- "#90A4AE",
- "#78909C",
- "#607D8B",
- "#546E7A",
- "#455A64",
- "#37474F",
- "#263238"))
- }
-
- val ACCENT_COLORS: IntArray by lazy {
- colorArrayOf(
- "#FF1744",
- "#F50057",
- "#D500F9",
- "#651FFF",
- "#3D5AFE",
- "#2979FF",
- "#00B0FF",
- "#00E5FF",
- "#1DE9B6",
- "#00E676",
- "#76FF03",
- "#C6FF00",
- "#FFEA00",
- "#FFC400",
- "#FF9100",
- "#FF3D00")
- }
-
- val ACCENT_COLORS_SUB: Array<IntArray> by lazy {
- arrayOf(colorArrayOf("#FF8A80",
- "#FF5252",
- "#FF1744",
- "#D50000"
- ), colorArrayOf(
- "#FF80AB",
- "#FF4081",
- "#F50057",
- "#C51162"
- ), colorArrayOf(
- "#EA80FC",
- "#E040FB",
- "#D500F9",
- "#AA00FF"
- ), colorArrayOf(
- "#B388FF",
- "#7C4DFF",
- "#651FFF",
- "#6200EA"
- ), colorArrayOf(
- "#8C9EFF",
- "#536DFE",
- "#3D5AFE",
- "#304FFE"
- ), colorArrayOf(
- "#82B1FF",
- "#448AFF",
- "#2979FF",
- "#2962FF"
- ), colorArrayOf(
- "#80D8FF",
- "#40C4FF",
- "#00B0FF",
- "#0091EA"
- ), colorArrayOf(
- "#84FFFF",
- "#18FFFF",
- "#00E5FF",
- "#00B8D4"
- ), colorArrayOf(
- "#A7FFEB",
- "#64FFDA",
- "#1DE9B6",
- "#00BFA5"
- ), colorArrayOf(
- "#B9F6CA",
- "#69F0AE",
- "#00E676",
- "#00C853"
- ), colorArrayOf(
- "#CCFF90",
- "#B2FF59",
- "#76FF03",
- "#64DD17"
- ), colorArrayOf(
- "#F4FF81",
- "#EEFF41",
- "#C6FF00",
- "#AEEA00"
- ), colorArrayOf(
- "#FFFF8D",
- "#FFFF00",
- "#FFEA00",
- "#FFD600"
- ), colorArrayOf(
- "#FFE57F",
- "#FFD740",
- "#FFC400",
- "#FFAB00"
- ), colorArrayOf(
- "#FFD180",
- "#FFAB40",
- "#FF9100",
- "#FF6D00"
- ), colorArrayOf(
- "#FF9E80",
- "#FF6E40",
- "#FF3D00",
- "#DD2C00"))
- }
-
- fun colorArrayOf(vararg colors: String) = colors.map { Color.parseColor(it) }.toIntArray()
-}
-
diff --git a/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt b/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt
deleted file mode 100644
index 7c57c26..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-package ca.allanwang.kau.dialogs.color
-
-import android.content.Context
-import android.graphics.Color
-import android.support.annotation.DimenRes
-import android.support.annotation.StringRes
-import ca.allanwang.kau.R
-import ca.allanwang.kau.utils.string
-import com.afollestad.materialdialogs.MaterialDialog
-import com.afollestad.materialdialogs.Theme
-
-class ColorBuilder : ColorContract {
- override var title: String? = null
- override var titleRes: Int = -1
- override var allowCustom: Boolean = true
- override var allowCustomAlpha: Boolean = false
- override var isAccent: Boolean = false
- override var defaultColor: Int = Color.BLACK
- override var doneText: Int = R.string.kau_done
- override var backText: Int = R.string.kau_back
- override var cancelText: Int = R.string.kau_cancel
- override var presetText: Int = R.string.kau_md_presets
- override var customText: Int = R.string.kau_md_custom
- get() = if (allowCustom) field else 0
- override var dynamicButtonColors: Boolean = true
- override var circleSizeRes: Int = R.dimen.kau_color_circle_size
- override var colorCallback: ((selectedColor: Int) -> Unit)? = null
- override var colorsTop: IntArray? = null
- override var colorsSub: Array<IntArray>? = null
- override var theme: Theme? = null
-}
-
-interface ColorContract {
- var title: String?
- var titleRes: Int @StringRes set
- var allowCustom: Boolean
- var allowCustomAlpha: Boolean
- var isAccent: Boolean
- var defaultColor: Int @StringRes set
- var doneText: Int @StringRes set
- var backText: Int @StringRes set
- var cancelText: Int @StringRes set
- var presetText: Int
- @StringRes set
- var customText: Int @StringRes set
- var dynamicButtonColors: Boolean
- var circleSizeRes: Int @DimenRes set
- var colorCallback: ((selectedColor: Int) -> Unit)?
- var colorsTop: IntArray?
- var colorsSub: Array<IntArray>?
- var theme: Theme?
-}
-
-/**
- * This is the extension that allows us to initialize the dialog
- * Note that this returns just the dialog; you still need to call .show() to show it
- */
-fun Context.colorPickerDialog(action: ColorContract.() -> Unit): MaterialDialog {
- val b = ColorBuilder()
- b.action()
- return colorPickerDialog(b)
-}
-
-fun Context.colorPickerDialog(contract: ColorContract): MaterialDialog {
- val view = ColorPickerView(this)
- val dialog = with(MaterialDialog.Builder(this)) {
- title(string(contract.titleRes, contract.title) ?: string(R.string.kau_md_color_palette))
- customView(view, false)
- autoDismiss(false)
- positiveText(contract.doneText)
- negativeText(contract.cancelText)
- if (contract.allowCustom) neutralText(contract.presetText)
- onPositive { dialog, _ -> contract.colorCallback?.invoke(view.selectedColor); dialog.dismiss() }
- onNegative { _, _ -> view.backOrCancel() }
- if (contract.allowCustom) onNeutral { _, _ -> view.toggleCustom() }
- showListener { view.refreshColors() }
- if (contract.theme != null) theme(contract.theme!!)
- build()
- }
- view.bind(contract, dialog)
- return dialog
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt b/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt
deleted file mode 100644
index 20e0259..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt
+++ /dev/null
@@ -1,309 +0,0 @@
-package ca.allanwang.kau.dialogs.color
-
-import android.content.Context
-import android.graphics.Color
-import android.support.annotation.ColorInt
-import android.support.v4.content.res.ResourcesCompat
-import android.text.Editable
-import android.text.InputFilter
-import android.text.TextWatcher
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewGroup
-import android.widget.*
-import ca.allanwang.kau.R
-import ca.allanwang.kau.utils.*
-import com.afollestad.materialdialogs.DialogAction
-import com.afollestad.materialdialogs.MaterialDialog
-import com.afollestad.materialdialogs.color.FillGridView
-import java.util.*
-
-/**
- * Created by Allan Wang on 2017-06-08.
- *
- * ColorPicker component of the ColorPickerDialog
- */
-internal class ColorPickerView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : ScrollView(context, attrs, defStyleAttr) {
- var selectedColor: Int = -1
- var isInSub: Boolean = false
- var isInCustom: Boolean = false
- var circleSize: Int = context.dimen(R.dimen.kau_color_circle_size).toInt()
- val backgroundColor = context.resolveColor(R.attr.md_background_color,
- if (context.resolveColor(android.R.attr.textColorPrimary).isColorDark) Color.WHITE else 0xff424242.toInt())
- val backgroundColorTint = backgroundColor.colorToForeground()
- lateinit var dialog: MaterialDialog
- lateinit var builder: ColorContract
- lateinit var colorsTop: IntArray
- var colorsSub: Array<IntArray>? = null
- var topIndex: Int = -1
- var subIndex: Int = -1
- var colorIndex: Int
- get() = if (isInSub) subIndex else topIndex
- set(value) {
- if (isInSub) subIndex = value
- else {
- topIndex = value
- if (colorsSub != null && colorsSub!!.size > value) {
- dialog.setActionButton(DialogAction.NEGATIVE, builder.backText)
- isInSub = true
- invalidateGrid()
- }
- }
- }
-
-
- val gridView: FillGridView by bindView(R.id.md_grid)
- val customFrame: LinearLayout by bindView(R.id.md_colorChooserCustomFrame)
- val customColorIndicator: View by bindView(R.id.md_colorIndicator)
- val hexInput: EditText by bindView(R.id.md_hexInput)
- val alphaLabel: TextView by bindView(R.id.md_colorALabel)
- val alphaSeekbar: SeekBar by bindView(R.id.md_colorA)
- val alphaValue: TextView by bindView(R.id.md_colorAValue)
- val redSeekbar: SeekBar by bindView(R.id.md_colorR)
- val redValue: TextView by bindView(R.id.md_colorRValue)
- val greenSeekbar: SeekBar by bindView(R.id.md_colorG)
- val greenValue: TextView by bindView(R.id.md_colorGValue)
- val blueSeekbar: SeekBar by bindView(R.id.md_colorB)
- val blueValue: TextView by bindView(R.id.md_colorBValue)
-
- var customHexTextWatcher: TextWatcher? = null
- var customRgbListener: SeekBar.OnSeekBarChangeListener? = null
-
- init {
- View.inflate(context, R.layout.md_dialog_colorchooser, this)
- }
-
- fun bind(builder: ColorContract, dialog: MaterialDialog) {
- this.builder = builder
- this.dialog = dialog
- this.colorsTop = with(builder) {
- if (colorsTop != null) colorsTop!!
- else if (isAccent) ColorPalette.ACCENT_COLORS
- else ColorPalette.PRIMARY_COLORS
- }
- this.colorsSub = with(builder) {
- if (colorsTop != null) colorsSub
- else if (isAccent) ColorPalette.ACCENT_COLORS_SUB
- else ColorPalette.PRIMARY_COLORS_SUB
- }
- this.selectedColor = builder.defaultColor
- if (builder.allowCustom) {
- if (!builder.allowCustomAlpha) {
- alphaLabel.gone()
- alphaSeekbar.gone()
- alphaValue.gone()
- hexInput.hint = String.format("%06X", selectedColor)
- hexInput.filters = arrayOf(InputFilter.LengthFilter(6))
- } else {
- hexInput.hint = String.format("%08X", selectedColor)
- hexInput.filters = arrayOf(InputFilter.LengthFilter(8))
- }
- }
- if (findColor(builder.defaultColor) || !builder.allowCustom) isInCustom = true //when toggled this will be false
- toggleCustom()
- }
-
- fun backOrCancel() {
- if (isInSub) {
- dialog.setActionButton(DialogAction.NEGATIVE, builder.cancelText)
- //to top
- isInSub = false
- subIndex = -1
- invalidateGrid()
- } else {
- dialog.cancel()
- }
- }
-
- fun toggleCustom() {
- isInCustom = !isInCustom
- if (isInCustom) {
- isInSub = false
- if (builder.allowCustom) dialog.setActionButton(DialogAction.NEUTRAL, builder.presetText)
- dialog.setActionButton(DialogAction.NEGATIVE, builder.cancelText)
- customHexTextWatcher = object : TextWatcher {
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
-
- override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
- try {
- selectedColor = Color.parseColor("#" + s.toString())
- } catch (e: IllegalArgumentException) {
- selectedColor = Color.BLACK
- }
-
- customColorIndicator.setBackgroundColor(selectedColor)
- if (alphaSeekbar.isVisible()) {
- val alpha = Color.alpha(selectedColor)
- alphaSeekbar.progress = alpha
- alphaValue.text = String.format(Locale.CANADA, "%d", alpha)
- }
- redSeekbar.progress = Color.red(selectedColor)
- greenSeekbar.progress = Color.green(selectedColor)
- blueSeekbar.progress = Color.blue(selectedColor)
- isInSub = false
- topIndex = -1
- subIndex = -1
- refreshColors()
- }
-
- override fun afterTextChanged(s: Editable?) {}
- }
- hexInput.setText(selectedColor.toHexString(builder.allowCustomAlpha, false))
- hexInput.addTextChangedListener(customHexTextWatcher)
- customRgbListener = object : SeekBar.OnSeekBarChangeListener {
- override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
- if (fromUser) {
- val color = if (builder.allowCustomAlpha)
- Color.argb(alphaSeekbar.progress,
- redSeekbar.progress,
- greenSeekbar.progress,
- blueSeekbar.progress)
- else Color.rgb(redSeekbar.progress,
- greenSeekbar.progress,
- blueSeekbar.progress)
-
- hexInput.setText(color.toHexString(builder.allowCustomAlpha, false))
- }
- if (builder.allowCustomAlpha) alphaValue.text = alphaSeekbar.progress.toString()
- redValue.text = redSeekbar.progress.toString()
- greenValue.text = greenSeekbar.progress.toString()
- blueValue.text = blueSeekbar.progress.toString()
- }
-
- override fun onStartTrackingTouch(seekBar: SeekBar) {}
-
- override fun onStopTrackingTouch(seekBar: SeekBar) {}
- }
- redSeekbar.setOnSeekBarChangeListener(customRgbListener)
- greenSeekbar.setOnSeekBarChangeListener(customRgbListener)
- blueSeekbar.setOnSeekBarChangeListener(customRgbListener)
- if (alphaSeekbar.isVisible())
- alphaSeekbar.setOnSeekBarChangeListener(customRgbListener)
- hexInput.setText(selectedColor.toHexString(alphaSeekbar.isVisible(), false))
- gridView.fadeOut(onFinish = { gridView.gone() })
- customFrame.fadeIn()
- } else {
- findColor(selectedColor)
- if (builder.allowCustom) dialog.setActionButton(DialogAction.NEUTRAL, builder.customText)
- dialog.setActionButton(DialogAction.NEGATIVE, if (isInSub) builder.backText else builder.cancelText)
- gridView.fadeIn(onStart = { invalidateGrid() })
- customFrame.fadeOut(onFinish = { customFrame.gone() })
- hexInput.removeTextChangedListener(customHexTextWatcher)
- customHexTextWatcher = null
- alphaSeekbar.setOnSeekBarChangeListener(null)
- redSeekbar.setOnSeekBarChangeListener(null)
- greenSeekbar.setOnSeekBarChangeListener(null)
- blueSeekbar.setOnSeekBarChangeListener(null)
- customRgbListener = null
- }
- }
-
- fun refreshColors() {
- if (!isInCustom) findColor(selectedColor)
- //Ensure that our tinted color is still visible against the background
- val visibleColor = if (selectedColor.isColorVisibleOn(backgroundColor)) selectedColor else backgroundColorTint
- if (builder.dynamicButtonColors) {
- dialog.getActionButton(DialogAction.POSITIVE).setTextColor(visibleColor)
- dialog.getActionButton(DialogAction.NEGATIVE).setTextColor(visibleColor)
- dialog.getActionButton(DialogAction.NEUTRAL).setTextColor(visibleColor)
- }
- if (!builder.allowCustom || !isInCustom) return
- if (builder.allowCustomAlpha)
- alphaSeekbar.visible().tint(visibleColor)
- redSeekbar.tint(visibleColor)
- greenSeekbar.tint(visibleColor)
- blueSeekbar.tint(visibleColor)
- hexInput.tint(visibleColor)
- }
-
- fun findColor(@ColorInt color: Int): Boolean {
- topIndex = -1
- subIndex = -1
- colorsTop.forEachIndexed {
- index, topColor ->
- if (findSubColor(color, index)) {
- topIndex = index
- return true
- }
- if (topColor == color) { // If no sub colors exists and top color matches
- topIndex = index
- return true
- }
- }
- return false
- }
-
- fun findSubColor(@ColorInt color: Int, topIndex: Int): Boolean {
- if (colorsSub == null || colorsSub!!.size <= topIndex) return false
- colorsSub!![topIndex].forEachIndexed {
- index, subColor ->
- if (subColor == color) {
- subIndex = index
- return true
- }
- }
- return false
- }
-
- fun invalidateGrid() {
- if (gridView.adapter == null) {
- gridView.adapter = ColorGridAdapter()
- gridView.selector = ResourcesCompat.getDrawable(resources, R.drawable.kau_transparent, null)
- } else {
- (gridView.adapter as BaseAdapter).notifyDataSetChanged()
- }
- }
-
- inner class ColorGridAdapter : BaseAdapter(), OnClickListener, OnLongClickListener {
- override fun onClick(v: View) {
- if (v.tag != null && v.tag is String) {
- val tags = (v.tag as String).split(":")
- if (colorIndex == tags[0].toInt()) {
- colorIndex = tags[0].toInt() //Go to sub list if exists
- return
- }
- if (colorIndex != -1) (gridView.getChildAt(colorIndex) as CircleView).animateSelected(false)
- selectedColor = tags[1].toInt()
- refreshColors()
- val currentSub = isInSub
- colorIndex = tags[0].toInt()
- if (currentSub == isInSub) (gridView.getChildAt(colorIndex) as CircleView).animateSelected(true)
- //Otherwise we are invalidating our grid, so there is no point in animating
- }
- }
-
- override fun onLongClick(v: View): Boolean {
- if (v.tag != null && v.tag is String) {
- val tag = (v.tag as String).split(":")
- val color = tag[1].toInt()
- (v as CircleView).showHint(color)
- return true
- }
- return false
- }
-
- override fun getItem(position: Int): Any = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position]
-
- override fun getCount(): Int = if (isInSub) colorsSub!![topIndex].size else colorsTop.size
-
- override fun getItemId(position: Int): Long = position.toLong()
-
- override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
- val view: CircleView = if (convertView == null)
- CircleView(context).apply { layoutParams = AbsListView.LayoutParams(circleSize, circleSize) }
- else
- convertView as CircleView
- val color: Int = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position]
- return view.apply {
- setBackgroundColor(color)
- isSelected = colorIndex == position
- tag = "$position:$color"
- setOnClickListener(this@ColorGridAdapter)
- setOnLongClickListener(this@ColorGridAdapter)
- }
- }
-
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt b/core/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt
deleted file mode 100644
index 3380ade..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-package ca.allanwang.kau.iitems
-
-import android.graphics.Color
-import android.graphics.drawable.Drawable
-import android.support.v7.widget.CardView
-import android.support.v7.widget.RecyclerView
-import android.view.View
-import android.widget.Button
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.TextView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.adapters.ThemableIItem
-import ca.allanwang.kau.adapters.ThemableIItemDelegate
-import ca.allanwang.kau.utils.*
-import com.mikepenz.fastadapter.FastAdapter
-import com.mikepenz.fastadapter.IItem
-import com.mikepenz.fastadapter.items.AbstractItem
-import com.mikepenz.fastadapter.listeners.ClickEventHook
-import com.mikepenz.iconics.typeface.IIcon
-
-/**
- * Created by Allan Wang on 2017-06-28.
- *
- * Simple generic card item with an icon, title, description and button
- * The icon and button are hidden by default unless values are given
- */
-class CardIItem(val builder: Config.() -> Unit = {}
-) : AbstractItem<CardIItem, CardIItem.ViewHolder>(), ThemableIItem by ThemableIItemDelegate() {
-
- companion object {
- @JvmStatic fun bindClickEvents(fastAdapter: FastAdapter<IItem<*,*>>) {
- fastAdapter.withEventHook(object : ClickEventHook<IItem<*,*>>() {
- override fun onBindMany(viewHolder: RecyclerView.ViewHolder): List<View>? {
- return if (viewHolder is ViewHolder) listOf(viewHolder.card, viewHolder.button) else null
- }
-
- override fun onClick(v: View, position: Int, adapter: FastAdapter<IItem<*,*>>, item: IItem<*,*>) {
- if (item !is CardIItem) return
- with(item.configs) {
- when (v.id) {
- R.id.kau_card_container -> cardClick?.onClick(v)
- R.id.kau_card_button -> buttonClick?.onClick(v)
- else -> {
- }
- }
- }
- }
- })
- }
- }
-
- val configs = Config().apply { builder() }
-
- class Config {
- var title: String? = null
- var titleRes: Int = -1
- var desc: String? = null
- var descRes: Int = -1
- var button: String? = null
- var buttonRes: Int = -1
- var buttonClick: View.OnClickListener? = null
- var cardClick: View.OnClickListener? = null
- var image: Drawable? = null
- var imageIIcon: IIcon? = null
- var imageIIconColor: Int = Color.WHITE
- var imageRes: Int = -1
- }
-
-
- override fun getType(): Int = R.id.kau_item_card
-
- override fun getLayoutRes(): Int = R.layout.kau_iitem_card
-
- override fun bindView(holder: ViewHolder, payloads: MutableList<Any>?) {
- super.bindView(holder, payloads)
- with(holder.itemView.context) context@ {
- with(configs) {
- holder.title.text = string(titleRes, title)
- holder.description.text = string(descRes, desc)
- val buttonText = string(buttonRes, button)
- if (buttonText != null) {
- holder.bottomRow.visible()
- holder.button.text = buttonText
- holder.button.setOnClickListener(buttonClick)
- }
- holder.icon.setImageDrawable(
- if (imageRes > 0) drawable(imageRes)
- else if (imageIIcon != null) imageIIcon!!.toDrawable(this@context, sizeDp = 40, color = imageIIconColor)
- else image
- )
- holder.card.setOnClickListener(cardClick)
- }
- with(holder) {
- bindTextColor(title)
- bindTextColorSecondary(description)
- bindAccentColor(button)
- if (configs.imageIIcon != null) bindIconColor(icon)
- bindBackgroundRipple(card)
- }
- }
- }
-
- override fun unbindView(holder: ViewHolder) {
- super.unbindView(holder)
- with(holder) {
- icon.gone().setImageDrawable(null)
- title.text = null
- description.text = null
- bottomRow.gone()
- button.setOnClickListener(null)
- card.setOnClickListener(null)
- }
- }
-
- override fun getViewHolder(v: View): ViewHolder = ViewHolder(v)
-
- class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
- val card: CardView by bindView(R.id.kau_card_container)
- val icon: ImageView by bindView(R.id.kau_card_image)
- val title: TextView by bindView(R.id.kau_card_title)
- val description: TextView by bindView(R.id.kau_card_description)
- val bottomRow: LinearLayout by bindView(R.id.kau_card_bottom_row)
- val button: Button by bindView(R.id.kau_card_button)
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/iitems/CutoutIItem.kt b/core/src/main/kotlin/ca/allanwang/kau/iitems/CutoutIItem.kt
deleted file mode 100644
index 627e1df..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/iitems/CutoutIItem.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package ca.allanwang.kau.iitems
-
-import android.support.v7.widget.RecyclerView
-import android.view.View
-import ca.allanwang.kau.R
-import ca.allanwang.kau.adapters.ThemableIItem
-import ca.allanwang.kau.adapters.ThemableIItemDelegate
-import ca.allanwang.kau.utils.bindView
-import ca.allanwang.kau.views.CutoutView
-import com.mikepenz.fastadapter.items.AbstractItem
-
-/**
- * Created by Allan Wang on 2017-06-28.
- *
- * Just a cutout item with some defaults in [R.layout.kau_iitem_cutout]
- */
-class CutoutIItem(val config: CutoutView.() -> Unit = {}
-) : AbstractItem<CutoutIItem, CutoutIItem.ViewHolder>(), ThemableIItem by ThemableIItemDelegate() {
-
- override fun getType(): Int = R.id.kau_item_cutout
-
- override fun getLayoutRes(): Int = R.layout.kau_iitem_cutout
-
- override fun isSelectable(): Boolean = false
-
- override fun bindView(holder: ViewHolder, payloads: MutableList<Any>?) {
- super.bindView(holder, payloads)
- with(holder) {
- if (accentColor != null && themeEnabled) cutout.foregroundColor = accentColor!!
- cutout.config()
- }
- }
-
- override fun unbindView(holder: ViewHolder) {
- super.unbindView(holder)
- with(holder) {
- cutout.drawable = null
- cutout.text = "Text" //back to default
- }
- }
-
- override fun getViewHolder(v: View): ViewHolder = ViewHolder(v)
-
- class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
- val cutout: CutoutView by bindView(R.id.kau_cutout)
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt b/core/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt
deleted file mode 100644
index e994781..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package ca.allanwang.kau.iitems
-
-import android.support.v7.widget.CardView
-import android.support.v7.widget.RecyclerView
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.adapters.ThemableIItem
-import ca.allanwang.kau.adapters.ThemableIItemDelegate
-import ca.allanwang.kau.utils.bindView
-import ca.allanwang.kau.utils.string
-import com.mikepenz.fastadapter.items.AbstractItem
-
-/**
- * Created by Allan Wang on 2017-06-28.
- *
- * Simple Header with lots of padding on the top
- * Contains only one text view
- */
-class HeaderIItem(text: String? = null, var textRes: Int = -1
-) : AbstractItem<HeaderIItem, HeaderIItem.ViewHolder>(), ThemableIItem by ThemableIItemDelegate() {
-
- var text: String = text ?: "Header Placeholder"
-
- override fun getType(): Int = R.id.kau_item_header_big_margin_top
-
- override fun getLayoutRes(): Int = R.layout.kau_iitem_header
-
- override fun bindView(holder: ViewHolder, payloads: MutableList<Any>?) {
- super.bindView(holder, payloads)
- holder.text.text = holder.itemView.context.string(textRes, text)
- bindTextColor(holder.text)
- bindBackgroundColor(holder.container)
- }
-
- override fun unbindView(holder: ViewHolder) {
- super.unbindView(holder)
- holder.text.text = null
- }
-
- override fun getViewHolder(v: View): ViewHolder = ViewHolder(v)
-
- class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
- val text: TextView by bindView(R.id.kau_header_text)
- val container: CardView by bindView(R.id.kau_header_container)
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt b/core/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt
deleted file mode 100644
index d8567c4..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package ca.allanwang.kau.iitems
-
-import android.support.annotation.LayoutRes
-import android.support.v7.widget.RecyclerView
-import android.view.View
-import com.mikepenz.fastadapter.IClickable
-import com.mikepenz.fastadapter.IItem
-import com.mikepenz.fastadapter.items.AbstractItem
-
-/**
- * Created by Allan Wang on 2017-07-03.
- *
- * Kotlin implementation of the [AbstractItem] to make things shorter
- * If only one iitem type extends the given [layoutRes], you may use it as the type and not worry about another id
- */
-open class KauIItem<Item, VH : RecyclerView.ViewHolder>(
- @param:LayoutRes private val layoutRes: Int,
- private val viewHolder: (v: View) -> VH,
- private val type: Int = layoutRes
-) : AbstractItem<Item, VH>() where Item : IItem<*, *>, Item : IClickable<*> {
- override final fun getType(): Int = type
- override final fun getViewHolder(v: View): VH = viewHolder(v)
- override final fun getLayoutRes(): Int = layoutRes
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/iitems/LibraryIItem.kt b/core/src/main/kotlin/ca/allanwang/kau/iitems/LibraryIItem.kt
deleted file mode 100644
index aabd9e3..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/iitems/LibraryIItem.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-package ca.allanwang.kau.iitems
-
-import android.os.Build
-import android.support.v7.widget.CardView
-import android.support.v7.widget.RecyclerView
-import android.text.Html
-import android.view.View
-import android.widget.TextView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.adapters.ThemableIItem
-import ca.allanwang.kau.adapters.ThemableIItemDelegate
-import ca.allanwang.kau.utils.bindView
-import ca.allanwang.kau.utils.gone
-import ca.allanwang.kau.utils.startLink
-import ca.allanwang.kau.utils.visible
-import ca.allanwang.kau.views.createSimpleRippleDrawable
-import com.mikepenz.aboutlibraries.entity.Library
-import com.mikepenz.fastadapter.FastAdapter
-import com.mikepenz.fastadapter.IItem
-import com.mikepenz.fastadapter.items.AbstractItem
-
-/**
- * Created by Allan Wang on 2017-06-27.
- */
-class LibraryIItem(val lib: Library
-) : AbstractItem<LibraryIItem, LibraryIItem.ViewHolder>(), ThemableIItem by ThemableIItemDelegate() {
-
- companion object {
- @JvmStatic fun bindClickEvents(fastAdapter: FastAdapter<IItem<*, *>>) {
- fastAdapter.withOnClickListener { v, _, item, _ ->
- if (item !is LibraryIItem) false
- else {
- val c = v.context
- with(item.lib) {
- c.startLink(libraryWebsite, repositoryLink, authorWebsite)
- }
- true
- }
- }
- }
- }
-
- override fun getType(): Int = R.id.kau_item_library
-
- override fun getLayoutRes(): Int = R.layout.kau_iitem_library
-
- override fun isSelectable(): Boolean = false
-
- override fun bindView(holder: ViewHolder, payloads: MutableList<Any>?) {
- super.bindView(holder, payloads)
- with(holder) {
- name.text = lib.libraryName
- creator.text = lib.author
- description.text = if (lib.libraryDescription.isBlank()) lib.libraryDescription
- else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
- Html.fromHtml(lib.libraryDescription, Html.FROM_HTML_MODE_LEGACY)
- else Html.fromHtml(lib.libraryDescription)
- bottomDivider.gone()
- if (lib.libraryVersion?.isNotBlank() ?: false) {
- bottomDivider.visible()
- version.visible().text = lib.libraryVersion
- }
- if (lib.license?.licenseName?.isNotBlank() ?: false) {
- bottomDivider.visible()
- license.visible().text = lib.license?.licenseName
- }
- bindTextColor(name, creator)
- bindTextColorSecondary(description)
- bindAccentColor(license, version)
- bindDividerColor(divider, bottomDivider)
- bindBackgroundRipple(card)
- }
- }
-
- override fun unbindView(holder: ViewHolder) {
- super.unbindView(holder)
- with(holder) {
- name.text = null
- creator.text = null
- description.text = null
- bottomDivider.gone()
- version.gone().text = null
- license.gone().text = null
- }
- }
-
- override fun getViewHolder(v: View): ViewHolder = ViewHolder(v)
-
- class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
- val card: CardView by bindView(R.id.lib_item_card)
- val name: TextView by bindView(R.id.lib_item_name)
- val creator: TextView by bindView(R.id.lib_item_author)
- val description: TextView by bindView(R.id.lib_item_description)
- val version: TextView by bindView(R.id.lib_item_version)
- val license: TextView by bindView(R.id.lib_item_license)
- val divider: View by bindView(R.id.lib_item_top_divider)
- val bottomDivider: View by bindView(R.id.lib_item_bottom_divider)
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt
deleted file mode 100644
index 9a9f7d4..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt
+++ /dev/null
@@ -1,137 +0,0 @@
-package ca.allanwang.kau.kpref
-
-import android.os.Bundle
-import android.support.annotation.StringRes
-import android.support.constraint.ConstraintLayout
-import android.support.v7.app.AppCompatActivity
-import android.support.v7.widget.RecyclerView
-import android.support.v7.widget.Toolbar
-import android.view.View
-import android.view.animation.Animation
-import android.view.animation.AnimationUtils
-import android.widget.FrameLayout
-import android.widget.ViewAnimator
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.items.KPrefItemCore
-import ca.allanwang.kau.utils.bindView
-import ca.allanwang.kau.utils.resolveColor
-import ca.allanwang.kau.utils.statusBarColor
-import ca.allanwang.kau.utils.string
-import ca.allanwang.kau.widgets.TextSlider
-import ca.allanwang.kau.views.RippleCanvas
-import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
-
-abstract class KPrefActivity : AppCompatActivity(), KPrefActivityContract {
-
- val adapter: FastItemAdapter<KPrefItemCore>
- @Suppress("UNCHECKED_CAST")
- get() = recycler.adapter as FastItemAdapter<KPrefItemCore>
- val recycler: RecyclerView
- get() = prefHolder.currentView as RecyclerView
- val container: ConstraintLayout by bindView(R.id.kau_container)
- val bgCanvas: RippleCanvas by bindView(R.id.kau_ripple)
- val toolbarCanvas: RippleCanvas by bindView(R.id.kau_toolbar_ripple)
- val toolbar: Toolbar by bindView(R.id.kau_toolbar)
- val toolbarTitle: TextSlider by bindView(R.id.kau_toolbar_text)
- val prefHolder: ViewAnimator by bindView(R.id.kau_holder)
- private lateinit var globalOptions: GlobalOptions
- var animate: Boolean = true
- set(value) {
- field = value
- toolbarTitle.animationType = if (value) TextSlider.ANIMATION_SLIDE_HORIZONTAL else TextSlider.ANIMATION_NONE
- }
-
- private val SLIDE_IN_LEFT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_in_left) }
- private val SLIDE_IN_RIGHT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_in_right) }
- private val SLIDE_OUT_LEFT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_out_left) }
- private val SLIDE_OUT_RIGHT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_out_right) }
-
- /**
- * Core attribute builder that is consistent throughout all items
- * Leave blank to use defaults
- */
- abstract fun kPrefCoreAttributes(): CoreAttributeContract.() -> Unit
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- //setup layout
- setContentView(R.layout.kau_activity_kpref)
- setSupportActionBar(toolbar)
- if (supportActionBar != null)
- with(supportActionBar!!) {
- setDisplayHomeAsUpEnabled(true)
- setDisplayShowHomeEnabled(true)
- toolbar.setNavigationOnClickListener { onBackPressed() }
- setDisplayShowTitleEnabled(false)
- }
- window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- statusBarColor = 0x30000000
- toolbarCanvas.set(resolveColor(R.attr.colorPrimary))
- bgCanvas.set(resolveColor(android.R.attr.colorBackground))
- prefHolder.animateFirstView = false
- //setup prefs
- val core = CoreAttributeBuilder()
- val builder = kPrefCoreAttributes()
- core.builder()
- globalOptions = GlobalOptions(core, this)
- showNextPrefs(R.string.kau_settings, onCreateKPrefs(savedInstanceState))
- }
-
- override fun onPostCreate(savedInstanceState: Bundle?) {
- super.onPostCreate(savedInstanceState)
- }
-
- override fun showNextPrefs(@StringRes toolbarTitleRes: Int, builder: KPrefAdapterBuilder.() -> Unit) {
- val rv = RecyclerView(this).apply {
- layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
- setKPrefAdapter(globalOptions, builder)
- }
- with(prefHolder) {
- inAnimation = if (animate) SLIDE_IN_RIGHT_ITEMS else null
- outAnimation = if (animate) SLIDE_OUT_LEFT_ITEMS else null
- addView(rv)
- showNext()
- }
- toolbarTitle.setNextText(string(toolbarTitleRes))
- }
-
- override fun showPrevPrefs() {
- val current = prefHolder.currentView
- with(prefHolder) {
- inAnimation = if (animate) SLIDE_IN_LEFT_ITEMS else null
- outAnimation = if (animate) SLIDE_OUT_RIGHT_ITEMS else null
- showPrevious()
- removeView(current)
- adapter.notifyAdapterDataSetChanged()
- }
- toolbarTitle.setPrevText()
- }
-
- fun reload(vararg index: Int) {
- if (index.isEmpty()) adapter.notifyAdapterDataSetChanged()
- else index.forEach { adapter.notifyItemChanged(it) }
- }
-
- override fun reloadByTitle(@StringRes vararg title: Int) {
- if (title.isEmpty()) return
- adapter.adapterItems.forEachIndexed { index, item ->
- if (title.any { item.core.titleRes == it })
- adapter.notifyItemChanged(index)
- }
- }
-
- abstract fun onCreateKPrefs(savedInstanceState: Bundle?): KPrefAdapterBuilder.() -> Unit
-
- override fun onBackPressed() {
- if (!backPress()) super.onBackPressed()
- }
-
- fun backPress(): Boolean {
- if (!toolbarTitle.isRoot) {
- showPrevPrefs()
- return true
- }
- return false
- }
-}
-
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt
deleted file mode 100644
index a424839..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-package ca.allanwang.kau.kpref
-
-import android.support.annotation.StringRes
-import android.support.v7.widget.LinearLayoutManager
-import android.support.v7.widget.RecyclerView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.items.*
-import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.uiThread
-
-/**
- * Created by Allan Wang on 2017-06-08.
- *
- * Houses all the components that can be called externally to setup the kpref mainAdapter
- */
-
-/**
- * Base extension that will register the layout manager and mainAdapter with the given items
- * Returns FastAdapter
- */
-fun RecyclerView.setKPrefAdapter(globalOptions: GlobalOptions, builder: KPrefAdapterBuilder.() -> Unit): FastItemAdapter<KPrefItemCore> {
- layoutManager = LinearLayoutManager(context)
- val adapter = FastItemAdapter<KPrefItemCore>()
- adapter.withOnClickListener { v, _, item, _ -> item.onClick(v, v.findViewById(R.id.kau_pref_inner_content)) }
- this.adapter = adapter
- doAsync {
- val items = KPrefAdapterBuilder(globalOptions)
- builder.invoke(items)
- uiThread {
- adapter.add(items.list)
- }
- }
- return adapter
-}
-
-@DslMarker
-annotation class KPrefMarker
-
-/**
- * Contains attributes shared amongst all kpref items
- */
-@KPrefMarker
-interface CoreAttributeContract {
- var textColor: (() -> Int)?
- var accentColor: (() -> Int)?
-}
-
-/**
- * Implementation of [CoreAttributeContract]
- */
-class CoreAttributeBuilder : CoreAttributeContract {
- override var textColor: (() -> Int)? = null
- override var accentColor: (() -> Int)? = textColor
-}
-
-interface KPrefActivityContract {
- fun showNextPrefs(@StringRes toolbarTitleRes: Int, builder: KPrefAdapterBuilder.() -> Unit)
- fun showPrevPrefs()
- fun reloadByTitle(@StringRes vararg title: Int)
-}
-
-
-class GlobalOptions(core: CoreAttributeContract, activity: KPrefActivityContract
-) : CoreAttributeContract by core, KPrefActivityContract by activity
-
-
-/**
- * Builder for kpref items
- * Contains DSLs for every possible item
- * The arguments are all the mandatory values plus an optional builder housing all the possible configurations
- * The mandatory values are final so they cannot be edited in the builder
- */
-@KPrefMarker
-class KPrefAdapterBuilder(val globalOptions: GlobalOptions) {
-
- @KPrefMarker
- fun header(@StringRes title: Int)
- = list.add(KPrefHeader(KPrefItemCore.CoreBuilder(globalOptions, title)))
-
- @KPrefMarker
- fun checkbox(@StringRes title: Int,
- getter: (() -> Boolean),
- setter: ((value: Boolean) -> Unit),
- builder: KPrefItemBase.BaseContract<Boolean>.() -> Unit = {})
- = list.add(KPrefCheckbox(KPrefItemBase.BaseBuilder(globalOptions, title, getter, setter)
- .apply { builder() }))
-
- @KPrefMarker
- fun colorPicker(@StringRes title: Int,
- getter: (() -> Int),
- setter: ((value: Int) -> Unit),
- builder: KPrefColorPicker.KPrefColorContract.() -> Unit = {})
- = list.add(KPrefColorPicker(KPrefColorPicker.KPrefColorBuilder(globalOptions, title, getter, setter)
- .apply { builder() }))
-
- @KPrefMarker
- fun <T> text(@StringRes title: Int,
- getter: (() -> T),
- setter: ((value: T) -> Unit),
- builder: KPrefText.KPrefTextContract<T>.() -> Unit = {})
- = list.add(KPrefText<T>(KPrefText.KPrefTextBuilder<T>(globalOptions, title, getter, setter)
- .apply { builder() }))
-
- @KPrefMarker
- fun subItems(@StringRes title: Int,
- itemBuilder: KPrefAdapterBuilder.() -> Unit,
- builder: KPrefSubItems.KPrefSubItemsContract.() -> Unit)
- = list.add(KPrefSubItems(KPrefSubItems.KPrefSubItemsBuilder(globalOptions, title, itemBuilder)
- .apply { builder() }))
-
- @KPrefMarker
- fun plainText(@StringRes title: Int,
- builder: KPrefItemBase.BaseContract<Unit>.() -> Unit = {})
- = list.add(KPrefPlainText(KPrefPlainText.KPrefPlainTextBuilder(globalOptions, title)
- .apply { builder() }))
-
- @KPrefMarker
- fun seekbar(@StringRes title: Int,
- getter: (() -> Int),
- setter: ((value: Int) -> Unit),
- builder: KPrefSeekbar.KPrefSeekbarContract.() -> Unit = {})
- = list.add(KPrefSeekbar(KPrefSeekbar.KPrefSeekbarBuilder(globalOptions, title, getter, setter)
- .apply { builder() }))
-
- @KPrefMarker
- val list: MutableList<KPrefItemCore> = mutableListOf()
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt
deleted file mode 100644
index 999a718..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.view.View
-import android.widget.CheckBox
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.KPrefMarker
-import ca.allanwang.kau.utils.tint
-
-/**
- * Created by Allan Wang on 2017-06-07.
- *
- * Checkbox preference
- * When clicked, will toggle the preference and the apply the result to the checkbox
- */
-open class KPrefCheckbox(builder: BaseContract<Boolean>) : KPrefItemBase<Boolean>(builder) {
-
- override fun defaultOnClick(itemView: View, innerContent: View?): Boolean {
- pref = !pref
- (innerContent as CheckBox).isChecked = pref
- return true
- }
-
- override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
- super.onPostBindView(viewHolder, textColor, accentColor)
- val checkbox = viewHolder.bindInnerView<CheckBox>(R.layout.kau_preference_checkbox)
- if (accentColor != null) checkbox.tint(accentColor)
- checkbox.isChecked = pref
- checkbox.jumpDrawablesToCurrentState() //Cancel the animation
- }
-
- override fun getType(): Int = R.id.kau_item_pref_checkbox
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt
deleted file mode 100644
index 762bcf4..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.view.View
-import ca.allanwang.kau.R
-import ca.allanwang.kau.dialogs.color.CircleView
-import ca.allanwang.kau.dialogs.color.ColorBuilder
-import ca.allanwang.kau.dialogs.color.ColorContract
-import ca.allanwang.kau.dialogs.color.colorPickerDialog
-import ca.allanwang.kau.kpref.CoreAttributeContract
-import ca.allanwang.kau.kpref.GlobalOptions
-import ca.allanwang.kau.kpref.KPrefMarker
-
-/**
- * Created by Allan Wang on 2017-06-07.
- *
- * ColorPicker preference
- * When a color is successfully selected in the dialog, it will be saved as an int
- */
-open class KPrefColorPicker(val builder: KPrefColorContract) : KPrefItemBase<Int>(builder) {
-
- override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
- super.onPostBindView(viewHolder, textColor, accentColor)
- builder.apply {
- titleRes = core.titleRes
- colorCallback = {
- pref = it
- }
- }
- if (builder.showPreview) {
- val preview = viewHolder.bindInnerView<CircleView>(R.layout.kau_preference_color_preview)
- preview.setBackgroundColor(pref)
- preview.withBorder = true
- builder.apply {
- colorCallback = {
- pref = it
- if (builder.showPreview)
- preview.setBackgroundColor(it)
- }
- }
- }
- }
-
-
- override fun defaultOnClick(itemView: View, innerContent: View?): Boolean {
- builder.apply {
- defaultColor = pref //update color
- }
- itemView.context.colorPickerDialog(builder).show()
- return true
- }
-
- /**
- * Extension of the base contract and [ColorContract] along with a showPreview option
- */
- interface KPrefColorContract : BaseContract<Int>, ColorContract {
- var showPreview: Boolean
- }
-
- /**
- * Default implementation of [KPrefColorContract]
- */
- class KPrefColorBuilder(globalOptions: GlobalOptions,
- override var titleRes: Int,
- getter: () -> Int,
- setter: (value: Int) -> Unit
- ) : KPrefColorContract, BaseContract<Int> by BaseBuilder<Int>(globalOptions, titleRes, getter, setter),
- ColorContract by ColorBuilder() {
- override var showPreview: Boolean = true
- }
-
- override fun getType(): Int = R.id.kau_item_pref_color_picker
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt
deleted file mode 100644
index 4068496..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.view.View
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.KPrefMarker
-
-/**
- * Created by Allan Wang on 2017-06-07.
- *
- * Header preference
- * This view just holds a title and is not clickable. It is styled using the accent color
- */
-open class KPrefHeader(builder: CoreContract) : KPrefItemCore(builder) {
-
- override fun getLayoutRes(): Int = R.layout.kau_preference_header
-
- override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
- if (accentColor != null) viewHolder.title.setTextColor(accentColor)
- }
-
- override fun onClick(itemView: View, innerContent: View?): Boolean = true
-
- override fun getType() = R.id.kau_item_pref_header
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt
deleted file mode 100644
index bb0f0a3..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.support.annotation.CallSuper
-import android.view.View
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.CoreAttributeContract
-import ca.allanwang.kau.kpref.GlobalOptions
-import ca.allanwang.kau.utils.resolveDrawable
-
-/**
- * Created by Allan Wang on 2017-06-05.
- *
- * Base class for pref setters that include the Shared Preference hooks
- */
-abstract class KPrefItemBase<T>(val base: BaseContract<T>) : KPrefItemCore(base) {
-
- open var pref: T
- get() = base.getter.invoke()
- set(value) {
- base.setter.invoke(value)
- }
-
- var enabled: Boolean = true
-
- init {
- if (base.onClick == null) base.onClick = {
- itemView, innerContent, _ ->
- defaultOnClick(itemView, innerContent)
- }
- }
-
- abstract fun defaultOnClick(itemView: View, innerContent: View?): Boolean
-
- @CallSuper
- override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
- enabled = base.enabler.invoke()
- with(viewHolder) {
- if (!enabled) container?.background = null
- container?.alpha = if (enabled) 1.0f else 0.3f
- }
- }
-
- override final fun onClick(itemView: View, innerContent: View?): Boolean {
- return if (enabled) base.onClick?.invoke(itemView, innerContent, this) ?: false
- else base.onDisabledClick?.invoke(itemView, innerContent, this) ?: false
- }
-
- override fun unbindView(holder: ViewHolder) {
- super.unbindView(holder)
- with(holder) {
- container?.isEnabled = true
- container?.background = itemView.context.resolveDrawable(android.R.attr.selectableItemBackground)
- container?.alpha = 1.0f
- }
- }
-
- override final fun getLayoutRes(): Int = R.layout.kau_preference
-
- /**
- * Extension of the core contract
- * Since everything that extends the base is an actual preference, there must be a getter and setter
- * The rest are optional and will have their defaults
- */
- interface BaseContract<T> : CoreContract {
- var enabler: () -> Boolean
- var onClick: ((itemView: View, innerContent: View?, item: KPrefItemBase<T>) -> Boolean)?
- var onDisabledClick: ((itemView: View, innerContent: View?, item: KPrefItemBase<T>) -> Boolean)?
- val getter: () -> T
- val setter: (value: T) -> Unit
- }
-
- /**
- * Default implementation of [BaseContract]
- */
- class BaseBuilder<T>(globalOptions: GlobalOptions,
- titleRes: Int,
- override val getter: () -> T,
- override val setter: (value: T) -> Unit
- ) : CoreContract by CoreBuilder(globalOptions, titleRes), BaseContract<T> {
- override var enabler: () -> Boolean = { true }
- override var onClick: ((itemView: View, innerContent: View?, item: KPrefItemBase<T>) -> Boolean)? = null
- override var onDisabledClick: ((itemView: View, innerContent: View?, item: KPrefItemBase<T>) -> Boolean)? = null
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt
deleted file mode 100644
index 18d1bae..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.support.annotation.CallSuper
-import android.support.annotation.IdRes
-import android.support.annotation.LayoutRes
-import android.support.annotation.StringRes
-import android.support.v7.widget.RecyclerView
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.TextView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.adapters.ThemableIItem
-import ca.allanwang.kau.adapters.ThemableIItemDelegate
-import ca.allanwang.kau.kpref.GlobalOptions
-import ca.allanwang.kau.kpref.KPrefMarker
-import ca.allanwang.kau.utils.*
-import com.mikepenz.fastadapter.items.AbstractItem
-import com.mikepenz.iconics.typeface.IIcon
-
-/**
- * Created by Allan Wang on 2017-06-05.
- *
- * Core class containing nothing but the view items
- */
-
-abstract class KPrefItemCore(val core: CoreContract) : AbstractItem<KPrefItemCore, KPrefItemCore.ViewHolder>(),
- ThemableIItem by ThemableIItemDelegate() {
-
- override final fun getViewHolder(v: View) = ViewHolder(v)
-
- @CallSuper
- override fun bindView(viewHolder: ViewHolder, payloads: List<Any>) {
- super.bindView(viewHolder, payloads)
- with(viewHolder) {
- val context = itemView.context
- title.text = context.string(core.titleRes)
- if (core.descRes > 0)
- desc?.visible()?.setText(core.descRes)
- else
- desc?.gone()
- if (core.iicon != null) icon?.visible()?.setIcon(core.iicon, 24)
- else icon?.gone()
- innerFrame?.removeAllViews()
- val textColor = core.globalOptions.textColor?.invoke()
- if (textColor != null) {
- title.setTextColor(textColor)
- desc?.setTextColor(textColor)
- }
- val accentColor = core.globalOptions.accentColor?.invoke()
- if (accentColor != null) {
- icon?.drawable?.setTint(accentColor)
- }
- onPostBindView(this, textColor, accentColor)
- }
- }
-
- abstract fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?)
-
- abstract fun onClick(itemView: View, innerContent: View?): Boolean
-
- override fun unbindView(holder: ViewHolder) {
- super.unbindView(holder)
- with(holder) {
- title.text = null
- desc?.text = null
- icon?.setImageDrawable(null)
- innerFrame?.removeAllViews()
- lowerFrame?.removeAllViews()
- }
- }
-
- /**
- * Core values for all kpref items
- */
- @KPrefMarker
- interface CoreContract {
- val globalOptions: GlobalOptions
- @get:StringRes val titleRes: Int
- var descRes: Int
- @StringRes get
- var iicon: IIcon?
-
- /**
- * Attempts to reload current item by identifying it with its [titleRes]
- */
- fun reloadSelf()
- }
-
- /**
- * Default implementation of [CoreContract]
- */
- class CoreBuilder(override val globalOptions: GlobalOptions,
- override @param:StringRes val titleRes: Int) : CoreContract {
- override var descRes: Int = -1
- override var iicon: IIcon? = null
-
- override fun reloadSelf() {
- globalOptions.reloadByTitle(titleRes)
- }
- }
-
- class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
- val title: TextView by bindView(R.id.kau_pref_title)
- val container: ViewGroup? by bindOptionalView(R.id.kau_pref_container)
- val desc: TextView? by bindOptionalView(R.id.kau_pref_desc)
- val icon: ImageView? by bindOptionalView(R.id.kau_pref_icon)
- val innerFrame: LinearLayout? by bindOptionalView(R.id.kau_pref_inner_frame)
- val lowerFrame: LinearLayout? by bindOptionalView(R.id.kau_pref_lower_frame)
- val innerContent: View?
- get() = itemView.findViewById(R.id.kau_pref_inner_content)
- val lowerContent: View?
- get() = itemView.findViewById(R.id.kau_pref_lower_content)
-
- inline fun <reified T : View> bindInnerView(@LayoutRes id: Int) = bindInnerView(id) { _: T -> }
-
- inline fun <reified T : View> bindInnerView(@LayoutRes id: Int, onFirstBind: (T) -> Unit): T {
- if (innerFrame == null) throw IllegalStateException("Cannot bind inner view when innerFrame does not exist")
- if (innerContent !is T) {
- innerFrame!!.removeAllViews()
- LayoutInflater.from(innerFrame!!.context).inflate(id, innerFrame)
- onFirstBind(innerContent as T)
- }
- return innerContent as T
- }
-
- inline fun <reified T : View> bindLowerView(@LayoutRes id: Int) = bindLowerView(id) { _: T -> }
-
- inline fun <reified T : View> bindLowerView(@LayoutRes id: Int, onFirstBind: (T) -> Unit): T {
- if (lowerFrame == null) throw IllegalStateException("Cannot bind inner view when lowerContent does not exist")
- if (lowerContent !is T) {
- lowerFrame!!.removeAllViews()
- LayoutInflater.from(lowerFrame!!.context).inflate(id, lowerFrame)
- onFirstBind(lowerContent as T)
- }
- return lowerContent as T
- }
-
- operator fun get(@IdRes id: Int): View = itemView.findViewById(id)
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt
deleted file mode 100644
index 6f7adf8..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.view.View
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.GlobalOptions
-
-/**
- * Created by Allan Wang on 2017-06-14.
- *
- * Just text with the core options. Extends base preference but has an empty getter and setter
- * Useful replacement of [KPrefText] when nothing is displayed on the right side,
- * and when the preference is completely handled by the click
- *
- */
-open class KPrefPlainText(val builder: KPrefPlainTextBuilder) : KPrefItemBase<Unit>(builder) {
-
- override fun defaultOnClick(itemView: View, innerContent: View?): Boolean {
- //nothing
- return true
- }
-
- class KPrefPlainTextBuilder(
- globalOptions: GlobalOptions,
- titleRes: Int
- ) : BaseContract<Unit> by BaseBuilder(globalOptions, titleRes, {}, {})
-
- override fun getType(): Int = R.id.kau_item_pref_plain_text
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSeekbar.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSeekbar.kt
deleted file mode 100644
index 407018f..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSeekbar.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.view.View
-import android.widget.SeekBar
-import android.widget.TextView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.GlobalOptions
-import ca.allanwang.kau.kpref.KPrefException
-import ca.allanwang.kau.logging.KL
-import ca.allanwang.kau.utils.tint
-
-/**
- * Created by Allan Wang on 2017-06-07.
- *
- * Checkbox preference
- * When clicked, will toggle the preference and the apply the result to the checkbox
- */
-open class KPrefSeekbar(val builder: KPrefSeekbarContract) : KPrefItemBase<Int>(builder) {
-
-
- override fun defaultOnClick(itemView: View, innerContent: View?): Boolean = false
-
- override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
- super.onPostBindView(viewHolder, textColor, accentColor)
- val text = viewHolder.bindInnerView<TextView>(R.layout.kau_preference_seekbar_text)
- if (textColor != null) text.setTextColor(textColor)
-
- val tvc = builder.textViewConfigs
-
- text.tvc()
- val seekbar = viewHolder.bindLowerView<SeekBar>(R.layout.kau_preference_seekbar) {
- it.max = builder.range
- it.incrementProgressBy(builder.increments)
- it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
- override fun onProgressChanged(sb: SeekBar, progress: Int, fromUser: Boolean) {
- text.text = builder.toText(progress.fromProgress)
- }
-
- override fun onStartTrackingTouch(sb: SeekBar) {}
-
- override fun onStopTrackingTouch(sb: SeekBar) {
- val trueProgress = sb.progress.fromProgress
- pref = trueProgress
- }
- })
- }
- if (accentColor != null) seekbar.tint(accentColor)
- text.text = builder.toText(seekbar.progress.fromProgress) //set initial text in case no change occurs
- seekbar.progress = pref.toProgress
- }
-
- /**
- * Extension of the base contract
- */
- interface KPrefSeekbarContract : BaseContract<Int> {
- var min: Int
- var max: Int
- var increments: Int
- var range: Int
- /**
- * Once a seekbar is let go, calculates what text to show in the text view
- */
- var toText: (Int) -> String
- var textViewConfigs: TextView.() -> Unit
- }
-
- /**
- * Default implementation of [KPrefSeekbarContract]
- */
- class KPrefSeekbarBuilder(
- globalOptions: GlobalOptions,
- titleRes: Int,
- getter: () -> Int,
- setter: (value: Int) -> Unit
- ) : KPrefSeekbarContract, BaseContract<Int> by BaseBuilder(globalOptions, titleRes, getter, setter) {
-
- override var min: Int = 0
- set(value) {
- field = value
- range = -1
- }
- override var max: Int = 100
- set(value) {
- field = value
- range = -1
- }
- override var increments: Int = 1
-
- override var range: Int = max - min
- //value doesn't matter; setting will prompt the check
- set(value) {
- if (max <= min) throw KPrefException("Range min ($min) must be smaller than max ($max)")
- field = max - min
- }
-
- override var toText: (Int) -> String = { it.toString() }
-
- override var textViewConfigs: TextView.() -> Unit = {}
- }
-
- val Int.toProgress: Int
- get() = this - builder.min
-
- val Int.fromProgress: Int
- get() = this + builder.min
-
- override fun getType(): Int = R.id.kau_item_pref_seekbar
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt
deleted file mode 100644
index f373ec4..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.view.View
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.GlobalOptions
-import ca.allanwang.kau.kpref.KPrefAdapterBuilder
-
-/**
- * Created by Allan Wang on 2017-06-14.
- *
- * Sub item preference
- * When clicked, will navigate to a new set of preferences and add the old list to a stack
- *
- */
-open class KPrefSubItems(val builder: KPrefSubItemsContract) : KPrefItemCore(builder) {
-
- override fun onClick(itemView: View, innerContent: View?): Boolean {
- builder.globalOptions.showNextPrefs(builder.titleRes, builder.itemBuilder)
- return true
- }
-
- override fun getLayoutRes(): Int = R.layout.kau_preference
-
- override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {}
- /**
- * Extension of the base contract with an optional text getter
- */
- interface KPrefSubItemsContract : CoreContract {
- val itemBuilder: KPrefAdapterBuilder.() -> Unit
- }
-
- /**
- * Default implementation of [KPrefTextContract]
- */
- class KPrefSubItemsBuilder(
- globalOptions: GlobalOptions,
- titleRes: Int,
- override val itemBuilder: KPrefAdapterBuilder.() -> Unit
- ) : KPrefSubItemsContract, CoreContract by CoreBuilder(globalOptions, titleRes)
-
- override fun getType(): Int = R.id.kau_item_pref_sub_item
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt
deleted file mode 100644
index 9c44cd4..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package ca.allanwang.kau.kpref.items
-
-import android.view.View
-import android.widget.TextView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.kpref.GlobalOptions
-import ca.allanwang.kau.utils.toast
-
-/**
- * Created by Allan Wang on 2017-06-14.
- *
- * Text preference
- * Holds a textview to display data on the right
- * This is still a generic preference
- *
- */
-open class KPrefText<T>(val builder: KPrefTextContract<T>) : KPrefItemBase<T>(builder) {
-
- /**
- * Automatically reload on set
- */
- override var pref: T
- get() = base.getter.invoke()
- set(value) {
- base.setter.invoke(value)
- builder.reloadSelf()
- }
-
- override fun defaultOnClick(itemView: View, innerContent: View?): Boolean {
- itemView.context.toast("No click function set")
- return true
- }
-
- override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) {
- super.onPostBindView(viewHolder, textColor, accentColor)
- val textview = viewHolder.bindInnerView<TextView>(R.layout.kau_preference_text)
- if (textColor != null) textview.setTextColor(textColor)
- textview.text = builder.textGetter.invoke(pref)
- }
-
- /**
- * Extension of the base contract with an optional text getter
- */
- interface KPrefTextContract<T> : BaseContract<T> {
- var textGetter: (T) -> String?
- }
-
- /**
- * Default implementation of [KPrefTextContract]
- */
- class KPrefTextBuilder<T>(
- globalOptions: GlobalOptions,
- titleRes: Int,
- getter: () -> T,
- setter: (value: T) -> Unit
- ) : KPrefTextContract<T>, BaseContract<T> by BaseBuilder<T>(globalOptions, titleRes, getter, setter) {
- override var textGetter: (T) -> String? = { it?.toString() }
- }
-
- override fun getType(): Int = R.id.kau_item_pref_text
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt b/core/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt
deleted file mode 100644
index 96dc789..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package ca.allanwang.kau.searchview
-
-import android.graphics.Typeface
-import android.graphics.drawable.Drawable
-import android.support.constraint.ConstraintLayout
-import android.support.v7.widget.RecyclerView
-import android.text.Spannable
-import android.text.SpannableStringBuilder
-import android.text.style.StyleSpan
-import android.view.View
-import android.widget.ImageView
-import android.widget.TextView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.iitems.KauIItem
-import ca.allanwang.kau.utils.*
-import com.mikepenz.google_material_typeface_library.GoogleMaterial
-import com.mikepenz.iconics.typeface.IIcon
-
-/**
- * Created by Allan Wang on 2017-06-23.
- *
- * A holder for each individual search item
- * Contains a [key] which acts as a unique identifier (eg url)
- * and a [content] which is displayed in the item
- */
-class SearchItem(val key: String,
- val content: String = key,
- val description: String? = null,
- val iicon: IIcon? = GoogleMaterial.Icon.gmd_search,
- val image: Drawable? = null
-) : KauIItem<SearchItem, SearchItem.ViewHolder>(
- R.layout.kau_search_iitem,
- { ViewHolder(it) },
- R.id.kau_item_search
-) {
-
- companion object {
- @JvmStatic var foregroundColor: Int = 0xdd000000.toInt()
- @JvmStatic var backgroundColor: Int = 0xfffafafa.toInt()
- }
-
- var styledContent: SpannableStringBuilder? = null
-
- /**
- * Highlight the subText if it is present in the content
- */
- fun withHighlights(subText: String) {
- val index = content.indexOf(subText)
- if (index == -1) return
- styledContent = SpannableStringBuilder(content)
- styledContent!!.setSpan(StyleSpan(Typeface.BOLD), index, index + subText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
- }
-
- override fun bindView(holder: ViewHolder, payloads: MutableList<Any>?) {
- super.bindView(holder, payloads)
- holder.title.setTextColor(foregroundColor)
- holder.desc.setTextColor(foregroundColor.adjustAlpha(0.6f))
-
- if (image != null) holder.icon.setImageDrawable(image)
- else holder.icon.setIcon(iicon, sizeDp = 18, color = foregroundColor)
-
- holder.container.setRippleBackground(foregroundColor, backgroundColor)
- holder.title.text = styledContent ?: content
- if (description?.isNotBlank() ?: false) holder.desc.visible().text = description
- }
-
- override fun unbindView(holder: ViewHolder) {
- super.unbindView(holder)
- holder.title.text = null
- holder.desc.gone().text = null
- holder.icon.setImageDrawable(null)
- }
-
- class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
- val icon: ImageView by bindView(R.id.search_icon)
- val title: TextView by bindView(R.id.search_title)
- val desc: TextView by bindView(R.id.search_desc)
- val container: ConstraintLayout by bindView(R.id.search_item_frame)
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt b/core/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt
deleted file mode 100644
index c077a06..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt
+++ /dev/null
@@ -1,412 +0,0 @@
-package ca.allanwang.kau.searchview
-
-import android.app.Activity
-import android.content.Context
-import android.content.res.ColorStateList
-import android.graphics.Color
-import android.support.annotation.ColorInt
-import android.support.annotation.IdRes
-import android.support.annotation.StringRes
-import android.support.transition.AutoTransition
-import android.support.v7.widget.AppCompatEditText
-import android.support.v7.widget.LinearLayoutManager
-import android.support.v7.widget.RecyclerView
-import android.util.AttributeSet
-import android.view.*
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.ProgressBar
-import ca.allanwang.kau.R
-import ca.allanwang.kau.animators.NoAnimator
-import ca.allanwang.kau.kotlin.nonReadable
-import ca.allanwang.kau.searchview.SearchView.Configs
-import ca.allanwang.kau.utils.*
-import ca.allanwang.kau.views.BoundedCardView
-import com.jakewharton.rxbinding2.widget.RxTextView
-import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
-import com.mikepenz.google_material_typeface_library.GoogleMaterial
-import com.mikepenz.iconics.typeface.IIcon
-import io.reactivex.Observable
-import io.reactivex.schedulers.Schedulers
-import org.jetbrains.anko.runOnUiThread
-
-
-/**
- * Created by Allan Wang on 2017-06-23.
- *
- * A materialized SearchView with complete theming and observables
- * This view can be added programmatically and configured using the [Configs] DSL
- * It is preferred to add the view through an activity, but it can be attached to any ViewGroup
- * Beware of where specifically this is added, as its view or the keyboard may affect positioning
- *
- * Huge thanks to @lapism for his base
- * https://github.com/lapism/SearchView
- */
-class SearchView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr) {
-
- /**
- * Collection of all possible arguments when building the SearchView
- * Everything is made as opened as possible so other components may be found in the [SearchView]
- * However, these are the notable options put together an an inner class for better visibility
- */
- inner class Configs {
- /**
- * In the searchview, foreground color accounts for all text colors and icon colors
- * Various alpha levels may be used for sub texts/dividers etc
- */
- var foregroundColor: Int
- get() = SearchItem.foregroundColor
- set(value) {
- if (SearchItem.foregroundColor == value) return
- SearchItem.foregroundColor = value
- tintForeground(value)
- }
- /**
- * Namely the background for the card and recycler view
- */
- var backgroundColor: Int
- get() = SearchItem.backgroundColor
- set(value) {
- if (SearchItem.backgroundColor == value) return
- SearchItem.backgroundColor = value
- tintBackground(value)
- }
- /**
- * Icon for the leftmost ImageView, which typically contains the hamburger menu/back arror
- */
- var navIcon: IIcon? = GoogleMaterial.Icon.gmd_arrow_back
- set(value) {
- field = value
- iconNav.setSearchIcon(value)
- if (value == null) iconNav.gone()
- }
-
- /**
- * Optional icon just to the left of the clear icon
- * This is not implemented by default, but can be used for anything, such as mic or redirects
- * Returns the extra imageview
- * Set the iicon as null to hide the extra icon
- */
- fun setExtraIcon(iicon: IIcon?, onClick: OnClickListener?): ImageView {
- iconExtra.setSearchIcon(iicon)
- if (iicon == null) iconClear.gone()
- iconExtra.setOnClickListener(onClick)
- return iconExtra
- }
-
- /**
- * Icon for the rightmost ImageView, which typically contains a close icon
- */
- var clearIcon: IIcon? = GoogleMaterial.Icon.gmd_clear
- set(value) {
- field = value
- iconClear.setSearchIcon(value)
- if (value == null) iconClear.gone()
- }
- /**
- * Duration for the circular reveal animation
- */
- var revealDuration: Long = 300L
- /**
- * Duration for the auto transition, which is namely used to resize the recycler view
- */
- var transitionDuration: Long = 100L
- /**
- * Defines whether the edit text and mainAdapter should clear themselves when the searchView is closed
- */
- var shouldClearOnClose: Boolean = false
- /**
- * Callback that will be called every time the searchView opens
- */
- var openListener: ((searchView: SearchView) -> Unit)? = null
- /**
- * Callback that will be called every time the searchView closes
- */
- var closeListener: ((searchView: SearchView) -> Unit)? = null
- /**
- * Draw a divider between the search bar and the suggestion items
- * The divider is colored based on the [foregroundColor]
- */
- var withDivider: Boolean = true
- set(value) {
- field = value
- if (value) divider.visible() else divider.invisible()
- }
- /**
- * Hint string to be set in the searchView
- */
- var hintText: String?
- get() = editText.hint?.toString()
- set(value) {
- editText.hint = value
- }
- /**
- * Hint string res to be set in the searchView
- */
- var hintTextRes: Int
- @Deprecated(level = DeprecationLevel.ERROR, message = "Non readable property")
- get() = nonReadable()
- @StringRes set(value) {
- hintText = context.string(value)
- }
- /**
- * StringRes for a "no results found" item
- * If [results] is ever set to an empty list, it will default to
- * a list with one item with this string
- *
- * For simplicity, kau contains [R.string.kau_no_results_found]
- * which you may use
- */
- var noResultsFound: Int = -1
- /**
- * Text watcher configurations on init
- * By default, the observable is on a separate thread, so you may directly execute background processes
- * This builder acts on an observable, so you may switch threads, debounce, and do anything else that you require
- */
- var textObserver: (observable: Observable<String>, searchView: SearchView) -> Unit = { _, _ -> }
- /**
- * Click event for suggestion items
- * This event is only triggered when [key] is not blank (like in [noResultsFound]
- */
- var onItemClick: (position: Int, key: String, content: String, searchView: SearchView) -> Unit = { _, _, _, _ -> }
- /**
- * Long click event for suggestion items
- * This event is only triggered when [key] is not blank (like in [noResultsFound]
- */
- var onItemLongClick: (position: Int, key: String, content: String, searchView: SearchView) -> Unit = { _, _, _, _ -> }
- /**
- * If a [SearchItem]'s title contains the submitted query, make that portion bold
- * See [SearchItem.withHighlights]
- */
- var highlightQueryText: Boolean = true
- }
-
- /**
- * Contract for mainAdapter items
- * Setting results will ensure that the values are sent on the UI thread
- */
- var results: List<SearchItem>
- get() = adapter.adapterItems
- set(value) = context.runOnUiThread {
- val list = if (value.isEmpty() && configs.noResultsFound > 0)
- listOf(SearchItem("", context.string(configs.noResultsFound), iicon = null))
- else value
- if (configs.highlightQueryText && value.isNotEmpty()) list.forEach { it.withHighlights(editText.text.toString()) }
- cardTransition()
- adapter.setNewList(list)
- }
-
- /**
- * Empties the list on the UI thread
- * The noResults item will not be added
- */
- internal fun clearResults() = context.runOnUiThread { cardTransition(); adapter.clear() }
-
- val configs = Configs()
- //views
- private val shadow: View by bindView(R.id.search_shadow)
- private val card: BoundedCardView by bindView(R.id.search_cardview)
- private val iconNav: ImageView by bindView(R.id.search_nav)
- private val editText: AppCompatEditText by bindView(R.id.search_edit_text)
- val textEvents: Observable<String>
- private val progress: ProgressBar by bindView(R.id.search_progress)
- val iconExtra: ImageView by bindView(R.id.search_extra)
- private val iconClear: ImageView by bindView(R.id.search_clear)
- private val divider: View by bindView(R.id.search_divider)
- private val recycler: RecyclerView by bindView(R.id.search_recycler)
- val adapter = FastItemAdapter<SearchItem>()
- var menuItem: MenuItem? = null
- val isOpen: Boolean
- get() = card.isVisible()
-
- /*
- * Ripple start points and search view offset
- * These are calculated every time the search view is opened,
- * and can be overridden with the open listener if necessary
- */
- var menuX: Int = -1 //starting x for circular reveal
- var menuY: Int = -1 //reference for cardview's marginTop
- var menuHalfHeight: Int = -1 //starting y for circular reveal (relative to the cardview)
-
- init {
- View.inflate(context, R.layout.kau_search_view, this)
- z = 99f
- iconNav.setSearchIcon(configs.navIcon).setOnClickListener { revealClose() }
- iconClear.setSearchIcon(configs.clearIcon).setOnClickListener { editText.text.clear() }
- tintForeground(configs.foregroundColor)
- tintBackground(configs.backgroundColor)
- with(recycler) {
- isNestedScrollingEnabled = false
- layoutManager = LinearLayoutManager(context)
- addOnScrollListener(object : RecyclerView.OnScrollListener() {
- override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
- super.onScrollStateChanged(recyclerView, newState)
- if (newState == RecyclerView.SCROLL_STATE_DRAGGING) hideKeyboard()
- }
- })
- adapter = this@SearchView.adapter
- itemAnimator = NoAnimator()
- }
- with(adapter) {
- withSelectable(true)
- withOnClickListener { _, _, item, position ->
- if (item.key.isNotBlank()) configs.onItemClick(position, item.key, item.content, this@SearchView); true
- }
- withOnLongClickListener { _, _, item, position ->
- if (item.key.isNotBlank()) configs.onItemLongClick(position, item.key, item.content, this@SearchView); true
- }
- }
- textEvents = RxTextView.textChangeEvents(editText)
- .skipInitialValue()
- .observeOn(Schedulers.newThread())
- .map { it.text().toString().trim() }
- textEvents.filter { it.isBlank() }
- .subscribe { clearResults() }
- }
-
- internal fun ImageView.setSearchIcon(iicon: IIcon?): ImageView {
- setIcon(iicon, sizeDp = 18, color = configs.foregroundColor)
- return this
- }
-
- internal fun cardTransition(builder: AutoTransition.() -> Unit = {}) {
- card.transitionAuto { duration = configs.transitionDuration; builder() }
- }
-
- fun config(config: Configs.() -> Unit) {
- configs.config()
- }
-
- /**
- * Binds the SearchView to a menu item and handles everything internally
- * This is assuming that SearchView has already been added to a ViewGroup
- * If not, see the extension function [bindSearchView]
- */
- fun bind(menu: Menu, @IdRes id: Int, @ColorInt menuIconColor: Int = Color.WHITE, config: Configs.() -> Unit = {}): SearchView {
- config(config)
- configs.textObserver(textEvents.filter { it.isNotBlank() }, this)
- menuItem = menu.findItem(id)
- if (menuItem!!.icon == null) menuItem!!.icon = GoogleMaterial.Icon.gmd_search.toDrawable(context, 18, menuIconColor)
- card.gone()
- menuItem!!.setOnMenuItemClickListener { configureCoords(it); revealOpen(); true }
- shadow.setOnClickListener { revealClose() }
- return this
- }
-
- fun unBind(replacementMenuItemClickListener: MenuItem.OnMenuItemClickListener? = null) {
- parentViewGroup.removeView(this)
- menuItem?.setOnMenuItemClickListener(replacementMenuItemClickListener)
- }
-
- fun configureCoords(item: MenuItem) {
- val view = parentViewGroup.findViewById<View>(item.itemId) ?: return
- val locations = IntArray(2)
- view.getLocationOnScreen(locations)
- menuX = (locations[0] + view.width / 2)
- menuHalfHeight = view.height / 2
- menuY = (locations[1] + menuHalfHeight)
- card.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- view.viewTreeObserver.removeOnPreDrawListener(this)
- val topAlignment = menuY - card.height / 2
- val params = (card.layoutParams as MarginLayoutParams).apply {
- topMargin = topAlignment
- }
- card.layoutParams = params
- return false
- }
- })
- }
-
- /**
- * Handle a back press event
- * Returns true if back press is consumed, false otherwise
- */
- fun onBackPressed(): Boolean {
- if (isOpen && menuItem != null) {
- revealClose()
- return true
- }
- return false
- }
-
- /**
- * Tint foreground attributes
- * This can be done publicly through [configs], which will also save the color
- */
- internal fun tintForeground(@ColorInt color: Int) {
- iconNav.drawable.setTint(color)
- iconClear.drawable.setTint(color)
- divider.setBackgroundColor(color.adjustAlpha(0.1f))
- editText.tint(color)
- editText.setTextColor(ColorStateList.valueOf(color))
- }
-
- /**
- * Tint background attributes
- * This can be done publicly through [configs], which will also save the color
- */
- internal fun tintBackground(@ColorInt color: Int) {
- card.setCardBackgroundColor(color)
- }
-
- fun revealOpen() {
- if (isOpen) return
- /**
- * The y component is relative to the cardView, but it hasn't been drawn yet so its own height is 0
- * We therefore use half the menuItem height, which is a close approximation to our intended value
- * The cardView matches the parent's width, so menuX is correct
- */
- configs.openListener?.invoke(this)
- shadow.fadeIn()
- editText.showKeyboard()
- card.circularReveal(menuX, menuHalfHeight, duration = configs.revealDuration) {
- cardTransition()
- recycler.visible()
- }
- }
-
- fun revealClose() {
- if (!isOpen) return
- shadow.fadeOut(duration = configs.transitionDuration)
- cardTransition {
- addEndListener {
- card.circularHide(menuX, menuHalfHeight, duration = configs.revealDuration,
- onFinish = {
- configs.closeListener?.invoke(this@SearchView)
- if (configs.shouldClearOnClose) editText.text.clear()
- recycler.gone()
- })
- }
- }
- recycler.gone()
- editText.hideKeyboard()
- }
-}
-
-@DslMarker
-annotation class KauSearch
-
-/**
- * Helper function that binds to an activity's main view
- */
-@KauSearch
-fun Activity.bindSearchView(menu: Menu, @IdRes id: Int, @ColorInt menuIconColor: Int = Color.WHITE, config: SearchView.Configs.() -> Unit = {}): SearchView
- = findViewById<ViewGroup>(android.R.id.content).bindSearchView(menu, id, menuIconColor, config)
-
-/**
- * Bind searchView to a menu item; call this in [Activity.onCreateOptionsMenu]
- * Be wary that if you may reinflate the menu many times (eg through [Activity.invalidateOptionsMenu]),
- * it may be worthwhile to hold a reference to the searchview and only bind it if it hasn't been bound before
- */
-@KauSearch
-fun ViewGroup.bindSearchView(menu: Menu, @IdRes id: Int, @ColorInt menuIconColor: Int = Color.WHITE, config: SearchView.Configs.() -> Unit = {}): SearchView {
- val searchView = SearchView(context)
- searchView.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
- addView(searchView)
- searchView.bind(menu, id, menuIconColor, config)
- return searchView
-}
-
diff --git a/core/src/main/kotlin/ca/allanwang/kau/views/SimpleRippleDrawable.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/SimpleRippleDrawable.kt
index df842f6..30c8edd 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/views/SimpleRippleDrawable.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/ui/SimpleRippleDrawable.kt
@@ -1,4 +1,4 @@
-package ca.allanwang.kau.views
+package ca.allanwang.kau.ui
import android.content.res.ColorStateList
import android.graphics.drawable.ColorDrawable
diff --git a/core/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/views/RippleCanvas.kt
index 805fb21..773490c 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/ui/views/RippleCanvas.kt
@@ -1,4 +1,4 @@
-package ca.allanwang.kau.views
+package ca.allanwang.kau.ui.views
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt
index b4752a5..48a048b 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt
@@ -21,7 +21,7 @@ import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.TextView
import ca.allanwang.kau.logging.KL
-import ca.allanwang.kau.views.createSimpleRippleDrawable
+import ca.allanwang.kau.ui.createSimpleRippleDrawable
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
diff --git a/core/src/main/kotlin/ca/allanwang/kau/views/BoundedCardView.kt b/core/src/main/kotlin/ca/allanwang/kau/views/BoundedCardView.kt
deleted file mode 100644
index 0cb65d0..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/views/BoundedCardView.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package ca.allanwang.kau.views
-
-import android.content.Context
-import android.graphics.Rect
-import android.support.v7.widget.CardView
-import android.util.AttributeSet
-import ca.allanwang.kau.R
-import ca.allanwang.kau.utils.parentViewGroup
-import ca.allanwang.kau.utils.parentVisibleHeight
-
-
-/**
- * Created by Allan Wang on 2017-06-26.
- *
- * CardView with a limited height
- * This view should be used with wrap_content as its height
- * Defaults to at most the parent's visible height
- */
-class BoundedCardView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : CardView(context, attrs, defStyleAttr) {
-
- /**
- * Maximum height possible, defined in dp (will be converted to px)
- * Defaults to parent's visible height
- */
- var maxHeight: Int = -1
- /**
- * Percentage of resulting max height to fill
- * Negative value = fill all of maxHeight
- */
- var maxHeightPercent: Float = -1.0f
-
- init {
- if (attrs != null) {
- val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.BoundedCardView)
- maxHeight = styledAttrs.getDimensionPixelSize(R.styleable.BoundedCardView_maxHeight, -1)
- maxHeightPercent = styledAttrs.getFloat(R.styleable.BoundedCardView_maxHeightPercent, -1.0f)
- styledAttrs.recycle()
- }
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- var maxMeasureHeight = if (maxHeight > 0) maxHeight else parentVisibleHeight
- if (maxHeightPercent > 0f) maxMeasureHeight = (maxMeasureHeight * maxHeightPercent).toInt()
- val trueHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxMeasureHeight, MeasureSpec.AT_MOST)
- super.onMeasure(widthMeasureSpec, trueHeightMeasureSpec)
- }
-
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/views/CutoutView.kt b/core/src/main/kotlin/ca/allanwang/kau/views/CutoutView.kt
deleted file mode 100644
index 023bdb4..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/views/CutoutView.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ca.allanwang.kau.views
-
-import android.content.Context
-import android.graphics.*
-import android.graphics.drawable.Drawable
-import android.text.TextPaint
-import android.util.AttributeSet
-import android.util.DisplayMetrics
-import android.util.TypedValue
-import android.view.View
-import ca.allanwang.kau.R
-import ca.allanwang.kau.utils.dimenPixelSize
-import ca.allanwang.kau.utils.getFont
-import ca.allanwang.kau.utils.parentVisibleHeight
-import ca.allanwang.kau.utils.toBitmap
-
-/**
- * A view which punches out some text from an opaque color block, allowing you to see through it.
- */
-class CutoutView @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : View(context, attrs, defStyleAttr) {
-
- companion object {
- const val PHI = 1.6182f
- const val TYPE_TEXT = 100
- const val TYPE_DRAWABLE = 101
- }
-
- private val paint: TextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
- private var bitmapScaling: Float = 1f
- private var cutout: Bitmap? = null
- var foregroundColor = Color.MAGENTA
- var text: String? = "Text"
- set(value) {
- field = value
- if (value != null) cutoutType = TYPE_TEXT
- else if (drawable != null) cutoutType = TYPE_DRAWABLE
- }
- var cutoutType: Int = TYPE_TEXT
- private var textSize: Float = 0f
- private var cutoutY: Float = 0f
- private var cutoutX: Float = 0f
- var drawable: Drawable? = null
- set(value) {
- field = value
- if (value != null) cutoutType = TYPE_DRAWABLE
- else if (text != null) cutoutType = TYPE_TEXT
- }
- private var heightPercentage: Float = 0f
- private var minHeight: Float = 0f
- private val maxTextSize: Float
-
- init {
- if (attrs != null) {
- val a = context.obtainStyledAttributes(attrs, R.styleable.CutoutView, 0, 0)
- if (a.hasValue(R.styleable.CutoutView_font))
- paint.typeface = context.getFont(a.getString(R.styleable.CutoutView_font))
- foregroundColor = a.getColor(R.styleable.CutoutView_foregroundColor, foregroundColor)
- text = a.getString(R.styleable.CutoutView_android_text) ?: text
- minHeight = a.getDimension(R.styleable.CutoutView_android_minHeight, minHeight)
- heightPercentage = a.getFloat(R.styleable.CutoutView_heightPercentageToScreen, heightPercentage)
- a.recycle()
- }
- maxTextSize = context.dimenPixelSize(R.dimen.kau_display_4_text_size).toFloat()
- }
-
- override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
- super.onSizeChanged(w, h, oldw, oldh)
- calculatePosition()
- createBitmap()
- }
-
- private fun calculatePosition() {
- when (cutoutType) {
- TYPE_TEXT -> calculateTextPosition()
- TYPE_DRAWABLE -> calculateImagePosition()
- }
- }
-
- private fun calculateTextPosition() {
- val targetWidth = width / PHI
- textSize = getSingleLineTextSize(text!!, paint, targetWidth, 0f, maxTextSize,
- 0.5f, resources.displayMetrics)
- paint.textSize = textSize
-
- // measuring text is fun :] see: https://chris.banes.me/2014/03/27/measuring-text/
- cutoutX = (width - paint.measureText(text)) / 2
- val textBounds = Rect()
- paint.getTextBounds(text, 0, text!!.length, textBounds)
- val textHeight = textBounds.height().toFloat()
- cutoutY = (height + textHeight) / 2
- }
-
- private fun calculateImagePosition() {
- if (drawable!!.intrinsicHeight <= 0 || drawable!!.intrinsicWidth <= 0) throw IllegalArgumentException("Drawable's intrinsic size cannot be less than 0")
- val targetWidth = width / PHI
- val targetHeight = height / PHI
- bitmapScaling = Math.min(targetHeight / drawable!!.intrinsicHeight, targetWidth / drawable!!.intrinsicWidth)
- cutoutX = (width - drawable!!.intrinsicWidth * bitmapScaling) / 2
- cutoutY = (height - drawable!!.intrinsicHeight * bitmapScaling) / 2
- }
-
- /**
- * If height percent is specified, ensure it is met
- */
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- val minHeight = Math.max(minHeight, heightPercentage * parentVisibleHeight)
- val trueHeightMeasureSpec = if (minHeight > 0)
- MeasureSpec.makeMeasureSpec(Math.max(minHeight.toInt(), measuredHeight), MeasureSpec.EXACTLY)
- else heightMeasureSpec
- super.onMeasure(widthMeasureSpec, trueHeightMeasureSpec)
- }
-
- /**
- * Recursive binary search to find the best size for the text.
-
- * Adapted from https://github.com/grantland/android-autofittextview
- */
- fun getSingleLineTextSize(text: String,
- paint: TextPaint,
- targetWidth: Float,
- low: Float,
- high: Float,
- precision: Float,
- metrics: DisplayMetrics): Float {
- val mid = (low + high) / 2.0f
-
- paint.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid, metrics)
- val maxLineWidth = paint.measureText(text)
-
- return if (high - low < precision) low
- else if (maxLineWidth > targetWidth) getSingleLineTextSize(text, paint, targetWidth, low, mid, precision, metrics)
- else if (maxLineWidth < targetWidth) getSingleLineTextSize(text, paint, targetWidth, mid, high, precision, metrics)
- else mid
- }
-
- private fun createBitmap() {
- if (!(cutout?.isRecycled ?: true))
- cutout?.recycle()
- if (width == 0 || height == 0) return
- cutout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
- cutout!!.setHasAlpha(true)
- val cutoutCanvas = Canvas(cutout!!)
- cutoutCanvas.drawColor(foregroundColor)
- paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
-
- when (cutoutType) {
- TYPE_TEXT -> {
- // this is the magic – Clear mode punches out the bitmap
- paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
- cutoutCanvas.drawText(text, cutoutX, cutoutY, paint)
- }
- TYPE_DRAWABLE -> {
- cutoutCanvas.drawBitmap(drawable!!.toBitmap(bitmapScaling, Bitmap.Config.ALPHA_8), cutoutX, cutoutY, paint)
- }
-
- }
- }
-
- override fun onDraw(canvas: Canvas) {
- canvas.drawBitmap(cutout!!, 0f, 0f, null)
- }
-
- override fun hasOverlappingRendering(): Boolean = true
-
-}
diff --git a/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt b/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt
deleted file mode 100644
index c39a278..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ca.allanwang.kau.widgets
-
-import android.app.Activity
-import android.content.Context
-import android.graphics.Color
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
-import ca.allanwang.kau.R
-import ca.allanwang.kau.utils.*
-
-/**
- * A [FrameLayout] which responds to nested scrolls to create drag-dismissable layouts.
- * Applies an elasticity factor to reduce movement as you approach the given dismiss distance.
- * Optionally also scales down content during drag.
- */
-class ElasticDragDismissFrameLayout @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0
-) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
-
- // configurable attribs
- var dragDismissDistance = context.dimen(R.dimen.kau_drag_dismiss_distance).dpToPx
- var dragDismissFraction = -1f
- var dragDismissScale = 1f
- set(value) {
- field = value
- shouldScale = value != 1f
- }
- private var shouldScale = false
- var dragElacticity = 0.8f
-
- // state
- private var totalDrag: Float = 0f
- private var draggingDown = false
- private var draggingUp = false
-
- private var callbacks: MutableList<ElasticDragDismissCallback> = mutableListOf()
-
- init {
- if (attrs != null) {
- val a = getContext().obtainStyledAttributes(attrs, R.styleable.ElasticDragDismissFrameLayout, 0, 0)
- dragDismissDistance = a.getDimensionPixelSize(R.styleable.ElasticDragDismissFrameLayout_dragDismissDistance, Int.MAX_VALUE).toFloat()
- dragDismissFraction = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragDismissFraction, dragDismissFraction)
- dragDismissScale = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragDismissScale, dragDismissScale)
- dragElacticity = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragElasticity, dragElacticity)
- a.recycle()
- }
- }
-
- abstract class ElasticDragDismissCallback {
-
- /**
- * Called for each drag event.
-
- * @param elasticOffset Indicating the drag offset with elasticity applied i.e. may
- * * exceed 1.
- * *
- * @param elasticOffsetPixels The elastically scaled drag distance in pixels.
- * *
- * @param rawOffset Value from [0, 1] indicating the raw drag offset i.e.
- * * without elasticity applied. A value of 1 indicates that the
- * * dismiss distance has been reached.
- * *
- * @param rawOffsetPixels The raw distance the user has dragged
- */
- internal open fun onDrag(elasticOffset: Float, elasticOffsetPixels: Float,
- rawOffset: Float, rawOffsetPixels: Float) {
- }
-
- /**
- * Called when dragging is released and has exceeded the threshold dismiss distance.
- */
- internal open fun onDragDismissed() {}
-
- }
-
- override fun onStartNestedScroll(child: View, target: View, nestedScrollAxes: Int): Boolean {
- return nestedScrollAxes and View.SCROLL_AXIS_VERTICAL != 0
- }
-
- override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray) {
- // if we're in a drag gesture and the user reverses up the we should take those events
- if (draggingDown && dy > 0 || draggingUp && dy < 0) {
- dragScale(dy)
- consumed[1] = dy
- }
- }
-
- override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int,
- dxUnconsumed: Int, dyUnconsumed: Int) {
- dragScale(dyUnconsumed)
- }
-
- override fun onStopNestedScroll(child: View) {
- if (Math.abs(totalDrag) >= dragDismissDistance) {
- dispatchDismissCallback()
- } else { // settle back to natural position
- animate()
- .translationY(0f)
- .scaleX(1f)
- .scaleY(1f)
- .setDuration(200L)
- .setInterpolator(AnimHolder.fastOutSlowInInterpolator(context))
- .setListener(null)
- .start()
- totalDrag = 0f
- draggingUp = false
- draggingDown = draggingUp
- dispatchDragCallback(0f, 0f, 0f, 0f)
- }
- }
-
- override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
- super.onSizeChanged(w, h, oldw, oldh)
- if (dragDismissFraction > 0f) {
- dragDismissDistance = h * dragDismissFraction
- }
- }
-
- fun addListener(listener: ElasticDragDismissCallback) {
- callbacks.add(listener)
- }
-
- fun removeListener(listener: ElasticDragDismissCallback) {
- callbacks.remove(listener)
- }
-
- private fun dragScale(scroll: Int) {
- if (scroll == 0) return
-
- totalDrag += scroll.toFloat()
-
- // track the direction & set the pivot point for scaling
- // don't double track i.e. if start dragging down and then reverse, keep tracking as
- // dragging down until they reach the 'natural' position
- if (scroll < 0 && !draggingUp && !draggingDown) {
- draggingDown = true
- if (shouldScale) pivotY = height.toFloat()
- } else if (scroll > 0 && !draggingDown && !draggingUp) {
- draggingUp = true
- if (shouldScale) pivotY = 0f
- }
- // how far have we dragged relative to the distance to perform a dismiss
- // (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit
- var dragFraction = Math.log10((1 + Math.abs(totalDrag) / dragDismissDistance).toDouble()).toFloat()
-
- // calculate the desired translation given the drag fraction
- var dragTo = dragFraction * dragDismissDistance * dragElacticity
-
- if (draggingUp) {
- // as we use the absolute magnitude when calculating the drag fraction, need to
- // re-apply the drag direction
- dragTo *= -1f
- }
- translationY = dragTo
-
- if (shouldScale) {
- val scale = 1 - (1 - dragDismissScale) * dragFraction
- scaleX = scale
- scaleY = scale
- }
-
- // if we've reversed direction and gone past the settle point then clear the flags to
- // allow the list to get the scroll events & reset any transforms
- if (draggingDown && totalDrag >= 0 || draggingUp && totalDrag <= 0) {
- dragFraction = 0f
- dragTo = dragFraction
- totalDrag = dragTo
- draggingUp = false
- draggingDown = draggingUp
- translationY = 0f
- scaleX = 1f
- scaleY = 1f
- }
- dispatchDragCallback(dragFraction, dragTo,
- Math.min(1f, Math.abs(totalDrag) / dragDismissDistance), totalDrag)
- }
-
- private fun dispatchDragCallback(elasticOffset: Float, elasticOffsetPixels: Float,
- rawOffset: Float, rawOffsetPixels: Float) {
- callbacks.forEach {
- it.onDrag(elasticOffset, elasticOffsetPixels,
- rawOffset, rawOffsetPixels)
- }
- }
-
- private fun dispatchDismissCallback() {
- callbacks.forEach { it.onDragDismissed() }
- }
-
- /**
- * An [ElasticDragDismissCallback] which fades system chrome (i.e. status bar and
- * navigation bar) whilst elastic drags are performed and
- * [finishes][Activity.finishAfterTransition] the activity when drag dismissed.
- */
- open class SystemChromeFader(private val activity: Activity) : ElasticDragDismissCallback() {
- private val statusBarAlpha: Int = Color.alpha(activity.statusBarColor)
- private val navBarAlpha: Int = Color.alpha(activity.navigationBarColor)
- private val fadeNavBar: Boolean = activity.isNavBarOnBottom
-
- public override fun onDrag(elasticOffset: Float, elasticOffsetPixels: Float,
- rawOffset: Float, rawOffsetPixels: Float) {
- if (elasticOffsetPixels > 0) {
- // dragging downward, fade the status bar in proportion
- activity.statusBarColor = activity.statusBarColor.withAlpha(((1f - rawOffset) * statusBarAlpha).toInt())
- } else if (elasticOffsetPixels == 0f) {
- // reset
- activity.statusBarColor = activity.statusBarColor.withAlpha(statusBarAlpha)
- activity.navigationBarColor = activity.navigationBarColor.withAlpha(navBarAlpha)
- } else if (fadeNavBar) {
- // dragging upward, fade the navigation bar in proportion
- activity.navigationBarColor = activity.navigationBarColor.withAlpha(((1f - rawOffset) * navBarAlpha).toInt())
- }
- }
-
- public override fun onDragDismissed() {
- activity.finishAfterTransition()
- }
- }
-
-}
diff --git a/core/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java b/core/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java
deleted file mode 100644
index 78e915d..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java
+++ /dev/null
@@ -1,859 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ca.allanwang.kau.widgets;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.support.annotation.ColorInt;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import java.util.Arrays;
-
-import ca.allanwang.kau.R;
-import ca.allanwang.kau.utils.AnimHolder;
-import ca.allanwang.kau.utils.ColorUtilsKt;
-
-/**
- * An ink inspired widget for indicating pages in a {@link ViewPager}.
- */
-public class InkPageIndicator extends View implements ViewPager.OnPageChangeListener,
- View.OnAttachStateChangeListener {
-
- // defaults
- private static final int DEFAULT_DOT_SIZE = 8; // dp
- private static final int DEFAULT_GAP = 12; // dp
- private static final int DEFAULT_ANIM_DURATION = 400; // ms
- private static final int DEFAULT_UNSELECTED_COLOUR = 0x80ffffff; // 50% white
- private static final int DEFAULT_SELECTED_COLOUR = 0xffffffff; // 100% white
-
- // constants
- private static final float INVALID_FRACTION = -1f;
- private static final float MINIMAL_REVEAL = 0.00001f;
-
- // configurable attributes
- private int dotDiameter;
- private int gap;
- private long animDuration;
- private int unselectedColour;
- private int selectedColour;
-
- public void setColour(@ColorInt int color) {
- selectedColour = color;
- unselectedColour = ColorUtilsKt.adjustAlpha(color, 0.5f);
- selectedPaint.setColor(selectedColour);
- unselectedPaint.setColor(unselectedColour);
- }
-
- // derived from attributes
- private float dotRadius;
- private float halfDotRadius;
- private long animHalfDuration;
- private float dotTopY;
- private float dotCenterY;
- private float dotBottomY;
-
- // ViewPager
- private ViewPager viewPager;
-
- // state
- private int pageCount;
- private int currentPage;
- private int previousPage;
- private float selectedDotX;
- private boolean selectedDotInPosition;
- private float[] dotCenterX;
- private float[] joiningFractions;
- private float retreatingJoinX1;
- private float retreatingJoinX2;
- private float[] dotRevealFractions;
- private boolean isAttachedToWindow;
- private boolean pageChanging;
-
- // drawing
- private final Paint unselectedPaint;
- private final Paint selectedPaint;
- private final Path combinedUnselectedPath;
- private final Path unselectedDotPath;
- private final Path unselectedDotLeftPath;
- private final Path unselectedDotRightPath;
- private final RectF rectF;
-
- // animation
- private ValueAnimator moveAnimation;
- private AnimatorSet joiningAnimationSet;
- private PendingRetreatAnimator retreatAnimation;
- private PendingRevealAnimator[] revealAnimations;
- private final Interpolator interpolator;
-
- // working values for beziers
- float endX1;
- float endY1;
- float endX2;
- float endY2;
- float controlX1;
- float controlY1;
- float controlX2;
- float controlY2;
-
- public InkPageIndicator(Context context) {
- this(context, null, 0);
- }
-
- public InkPageIndicator(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public InkPageIndicator(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- final int density = (int) context.getResources().getDisplayMetrics().density;
-
- // Load attributes
- final TypedArray a = getContext().obtainStyledAttributes(
- attrs, R.styleable.InkPageIndicator, defStyle, 0);
-
- dotDiameter = a.getDimensionPixelSize(R.styleable.InkPageIndicator_dotDiameter,
- DEFAULT_DOT_SIZE * density);
- dotRadius = dotDiameter / 2;
- halfDotRadius = dotRadius / 2;
- gap = a.getDimensionPixelSize(R.styleable.InkPageIndicator_dotGap,
- DEFAULT_GAP * density);
- animDuration = (long) a.getInteger(R.styleable.InkPageIndicator_animationDuration,
- DEFAULT_ANIM_DURATION);
- animHalfDuration = animDuration / 2;
- unselectedColour = a.getColor(R.styleable.InkPageIndicator_pageIndicatorColor,
- DEFAULT_UNSELECTED_COLOUR);
- selectedColour = a.getColor(R.styleable.InkPageIndicator_currentPageIndicatorColor,
- DEFAULT_SELECTED_COLOUR);
-
- a.recycle();
-
- unselectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- unselectedPaint.setColor(unselectedColour);
- selectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- selectedPaint.setColor(selectedColour);
- interpolator = AnimHolder.INSTANCE.getFastOutSlowInInterpolator().invoke(context);
-
- // create paths & rect now – reuse & rewind later
- combinedUnselectedPath = new Path();
- unselectedDotPath = new Path();
- unselectedDotLeftPath = new Path();
- unselectedDotRightPath = new Path();
- rectF = new RectF();
-
- addOnAttachStateChangeListener(this);
- }
-
- public void setViewPager(ViewPager viewPager) {
- this.viewPager = viewPager;
- viewPager.addOnPageChangeListener(this);
- setPageCount(viewPager.getAdapter().getCount());
- viewPager.getAdapter().registerDataSetObserver(new DataSetObserver() {
- @Override
- public void onChanged() {
- setPageCount(InkPageIndicator.this.viewPager.getAdapter().getCount());
- }
- });
- setCurrentPageImmediate();
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- if (isAttachedToWindow) {
- float fraction = positionOffset;
- int currentPosition = pageChanging ? previousPage : currentPage;
- int leftDotPosition = position;
- // when swiping from #2 to #1 ViewPager reports position as 1 and a descending offset
- // need to convert this into our left-dot-based 'coordinate space'
- if (currentPosition != position) {
- fraction = 1f - positionOffset;
-
- // if user scrolls completely to next page then the position param updates to that
- // new page but we're not ready to switch our 'current' page yet so adjust for that
- if (fraction == 1f) {
- leftDotPosition = Math.min(currentPosition, position);
- }
- }
- setJoiningFraction(leftDotPosition, fraction);
- }
- }
-
- @Override
- public void onPageSelected(int position) {
- if (isAttachedToWindow) {
- // this is the main event we're interested in!
- setSelectedPage(position);
- } else {
- // when not attached, don't animate the move, just store immediately
- setCurrentPageImmediate();
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- // nothing to do
- }
-
- private void setPageCount(int pages) {
- pageCount = pages;
- resetState();
- requestLayout();
- }
-
- private void calculateDotPositions(int width, int height) {
- int left = getPaddingLeft();
- int top = getPaddingTop();
- int right = width - getPaddingRight();
- int bottom = height - getPaddingBottom();
-
- int requiredWidth = getRequiredWidth();
- float startLeft = left + ((right - left - requiredWidth) / 2) + dotRadius;
-
- dotCenterX = new float[pageCount];
- for (int i = 0; i < pageCount; i++) {
- dotCenterX[i] = startLeft + i * (dotDiameter + gap);
- }
- // todo just top aligning for now… should make this smarter
- dotTopY = top;
- dotCenterY = top + dotRadius;
- dotBottomY = top + dotDiameter;
-
- setCurrentPageImmediate();
- }
-
- private void setCurrentPageImmediate() {
- if (viewPager != null) {
- currentPage = viewPager.getCurrentItem();
- } else {
- currentPage = 0;
- }
- if (dotCenterX != null) {
- selectedDotX = dotCenterX[currentPage];
- }
- }
-
- private void resetState() {
- joiningFractions = new float[pageCount - 1];
- Arrays.fill(joiningFractions, 0f);
- dotRevealFractions = new float[pageCount];
- Arrays.fill(dotRevealFractions, 0f);
- retreatingJoinX1 = INVALID_FRACTION;
- retreatingJoinX2 = INVALID_FRACTION;
- selectedDotInPosition = true;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
- int desiredHeight = getDesiredHeight();
- int height;
- switch (MeasureSpec.getMode(heightMeasureSpec)) {
- case MeasureSpec.EXACTLY:
- height = MeasureSpec.getSize(heightMeasureSpec);
- break;
- case MeasureSpec.AT_MOST:
- height = Math.min(desiredHeight, MeasureSpec.getSize(heightMeasureSpec));
- break;
- case MeasureSpec.UNSPECIFIED:
- default:
- height = desiredHeight;
- break;
- }
-
- int desiredWidth = getDesiredWidth();
- int width;
- switch (MeasureSpec.getMode(widthMeasureSpec)) {
- case MeasureSpec.EXACTLY:
- width = MeasureSpec.getSize(widthMeasureSpec);
- break;
- case MeasureSpec.AT_MOST:
- width = Math.min(desiredWidth, MeasureSpec.getSize(widthMeasureSpec));
- break;
- case MeasureSpec.UNSPECIFIED:
- default:
- width = desiredWidth;
- break;
- }
- setMeasuredDimension(width, height);
- calculateDotPositions(width, height);
- }
-
- private int getDesiredHeight() {
- return getPaddingTop() + dotDiameter + getPaddingBottom();
- }
-
- private int getRequiredWidth() {
- return pageCount * dotDiameter + (pageCount - 1) * gap;
- }
-
- private int getDesiredWidth() {
- return getPaddingLeft() + getRequiredWidth() + getPaddingRight();
- }
-
- @Override
- public void onViewAttachedToWindow(View view) {
- isAttachedToWindow = true;
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- isAttachedToWindow = false;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (viewPager == null || pageCount == 0) return;
- drawUnselected(canvas);
- drawSelected(canvas);
- }
-
- private void drawUnselected(Canvas canvas) {
-
- combinedUnselectedPath.rewind();
-
- // draw any settled, revealing or joining dots
- for (int page = 0; page < pageCount; page++) {
- int nextXIndex = page == pageCount - 1 ? page : page + 1;
- combinedUnselectedPath.op(getUnselectedPath(page,
- dotCenterX[page],
- dotCenterX[nextXIndex],
- page == pageCount - 1 ? INVALID_FRACTION : joiningFractions[page],
- dotRevealFractions[page]), Path.Op.UNION);
- }
- // draw any retreating joins
- if (retreatingJoinX1 != INVALID_FRACTION) {
- combinedUnselectedPath.op(getRetreatingJoinPath(), Path.Op.UNION);
- }
- canvas.drawPath(combinedUnselectedPath, unselectedPaint);
- }
-
- /**
- * Unselected dots can be in 6 states:
- * <p>
- * #1 At rest
- * #2 Joining neighbour, still separate
- * #3 Joining neighbour, combined curved
- * #4 Joining neighbour, combined straight
- * #5 Join retreating
- * #6 Dot re-showing / revealing
- * <p>
- * It can also be in a combination of these states e.g. joining one neighbour while
- * retreating from another. We therefore create a Path so that we can examine each
- * dot pair separately and later take the union for these cases.
- * <p>
- * This function returns a path for the given dot **and any action to it's right** e.g. joining
- * or retreating from it's neighbour
- *
- * @param page
- * @return
- */
- private Path getUnselectedPath(int page,
- float centerX,
- float nextCenterX,
- float joiningFraction,
- float dotRevealFraction) {
-
- unselectedDotPath.rewind();
-
- if ((joiningFraction == 0f || joiningFraction == INVALID_FRACTION)
- && dotRevealFraction == 0f
- && !(page == currentPage && selectedDotInPosition == true)) {
-
- // case #1 – At rest
- unselectedDotPath.addCircle(dotCenterX[page], dotCenterY, dotRadius, Path.Direction.CW);
- }
-
- if (joiningFraction > 0f && joiningFraction <= 0.5f
- && retreatingJoinX1 == INVALID_FRACTION) {
-
- // case #2 – Joining neighbour, still separate
-
- // start with the left dot
- unselectedDotLeftPath.rewind();
-
- // start at the bottom center
- unselectedDotLeftPath.moveTo(centerX, dotBottomY);
-
- // semi circle to the top center
- rectF.set(centerX - dotRadius, dotTopY, centerX + dotRadius, dotBottomY);
- unselectedDotLeftPath.arcTo(rectF, 90, 180, true);
-
- // cubic to the right middle
- endX1 = centerX + dotRadius + (joiningFraction * gap);
- endY1 = dotCenterY;
- controlX1 = centerX + halfDotRadius;
- controlY1 = dotTopY;
- controlX2 = endX1;
- controlY2 = endY1 - halfDotRadius;
- unselectedDotLeftPath.cubicTo(controlX1, controlY1,
- controlX2, controlY2,
- endX1, endY1);
-
- // cubic back to the bottom center
- endX2 = centerX;
- endY2 = dotBottomY;
- controlX1 = endX1;
- controlY1 = endY1 + halfDotRadius;
- controlX2 = centerX + halfDotRadius;
- controlY2 = dotBottomY;
- unselectedDotLeftPath.cubicTo(controlX1, controlY1,
- controlX2, controlY2,
- endX2, endY2);
-
- unselectedDotPath.op(unselectedDotLeftPath, Path.Op.UNION);
-
- // now do the next dot to the right
- unselectedDotRightPath.rewind();
-
- // start at the bottom center
- unselectedDotRightPath.moveTo(nextCenterX, dotBottomY);
-
- // semi circle to the top center
- rectF.set(nextCenterX - dotRadius, dotTopY, nextCenterX + dotRadius, dotBottomY);
- unselectedDotRightPath.arcTo(rectF, 90, -180, true);
-
- // cubic to the left middle
- endX1 = nextCenterX - dotRadius - (joiningFraction * gap);
- endY1 = dotCenterY;
- controlX1 = nextCenterX - halfDotRadius;
- controlY1 = dotTopY;
- controlX2 = endX1;
- controlY2 = endY1 - halfDotRadius;
- unselectedDotRightPath.cubicTo(controlX1, controlY1,
- controlX2, controlY2,
- endX1, endY1);
-
- // cubic back to the bottom center
- endX2 = nextCenterX;
- endY2 = dotBottomY;
- controlX1 = endX1;
- controlY1 = endY1 + halfDotRadius;
- controlX2 = endX2 - halfDotRadius;
- controlY2 = dotBottomY;
- unselectedDotRightPath.cubicTo(controlX1, controlY1,
- controlX2, controlY2,
- endX2, endY2);
- unselectedDotPath.op(unselectedDotRightPath, Path.Op.UNION);
- }
-
- if (joiningFraction > 0.5f && joiningFraction < 1f
- && retreatingJoinX1 == INVALID_FRACTION) {
-
- // case #3 – Joining neighbour, combined curved
-
- // adjust the fraction so that it goes from 0.3 -> 1 to produce a more realistic 'join'
- float adjustedFraction = (joiningFraction - 0.2f) * 1.25f;
-
- // start in the bottom left
- unselectedDotPath.moveTo(centerX, dotBottomY);
-
- // semi-circle to the top left
- rectF.set(centerX - dotRadius, dotTopY, centerX + dotRadius, dotBottomY);
- unselectedDotPath.arcTo(rectF, 90, 180, true);
-
- // bezier to the middle top of the join
- endX1 = centerX + dotRadius + (gap / 2);
- endY1 = dotCenterY - (adjustedFraction * dotRadius);
- controlX1 = endX1 - (adjustedFraction * dotRadius);
- controlY1 = dotTopY;
- controlX2 = endX1 - ((1 - adjustedFraction) * dotRadius);
- controlY2 = endY1;
- unselectedDotPath.cubicTo(controlX1, controlY1,
- controlX2, controlY2,
- endX1, endY1);
-
- // bezier to the top right of the join
- endX2 = nextCenterX;
- endY2 = dotTopY;
- controlX1 = endX1 + ((1 - adjustedFraction) * dotRadius);
- controlY1 = endY1;
- controlX2 = endX1 + (adjustedFraction * dotRadius);
- controlY2 = dotTopY;
- unselectedDotPath.cubicTo(controlX1, controlY1,
- controlX2, controlY2,
- endX2, endY2);
-
- // semi-circle to the bottom right
- rectF.set(nextCenterX - dotRadius, dotTopY, nextCenterX + dotRadius, dotBottomY);
- unselectedDotPath.arcTo(rectF, 270, 180, true);
-
- // bezier to the middle bottom of the join
- // endX1 stays the same
- endY1 = dotCenterY + (adjustedFraction * dotRadius);
- controlX1 = endX1 + (adjustedFraction * dotRadius);
- controlY1 = dotBottomY;
- controlX2 = endX1 + ((1 - adjustedFraction) * dotRadius);
- controlY2 = endY1;
- unselectedDotPath.cubicTo(controlX1, controlY1,
- controlX2, controlY2,
- endX1, endY1);
-
- // bezier back to the start point in the bottom left
- endX2 = centerX;
- endY2 = dotBottomY;
- controlX1 = endX1 - ((1 - adjustedFraction) * dotRadius);
- controlY1 = endY1;
- controlX2 = endX1 - (adjustedFraction * dotRadius);
- controlY2 = endY2;
- unselectedDotPath.cubicTo(controlX1, controlY1,
- controlX2, controlY2,
- endX2, endY2);
- }
- if (joiningFraction == 1 && retreatingJoinX1 == INVALID_FRACTION) {
-
- // case #4 Joining neighbour, combined straight technically we could use case 3 for this
- // situation as well but assume that this is an optimization rather than faffing around
- // with beziers just to draw a rounded rect
- rectF.set(centerX - dotRadius, dotTopY, nextCenterX + dotRadius, dotBottomY);
- unselectedDotPath.addRoundRect(rectF, dotRadius, dotRadius, Path.Direction.CW);
- }
-
- // case #5 is handled by #getRetreatingJoinPath()
- // this is done separately so that we can have a single retreating path spanning
- // multiple dots and therefore animate it's movement smoothly
-
- if (dotRevealFraction > MINIMAL_REVEAL) {
-
- // case #6 – previously hidden dot revealing
- unselectedDotPath.addCircle(centerX, dotCenterY, dotRevealFraction * dotRadius,
- Path.Direction.CW);
- }
-
- return unselectedDotPath;
- }
-
- private Path getRetreatingJoinPath() {
- unselectedDotPath.rewind();
- rectF.set(retreatingJoinX1, dotTopY, retreatingJoinX2, dotBottomY);
- unselectedDotPath.addRoundRect(rectF, dotRadius, dotRadius, Path.Direction.CW);
- return unselectedDotPath;
- }
-
- private void drawSelected(Canvas canvas) {
- canvas.drawCircle(selectedDotX, dotCenterY, dotRadius, selectedPaint);
- }
-
- private void setSelectedPage(int now) {
- if (now == currentPage) return;
-
- pageChanging = true;
- previousPage = currentPage;
- currentPage = now;
- final int steps = Math.abs(now - previousPage);
-
- if (steps > 1) {
- if (now > previousPage) {
- for (int i = 0; i < steps; i++) {
- setJoiningFraction(previousPage + i, 1f);
- }
- } else {
- for (int i = -1; i > -steps; i--) {
- setJoiningFraction(previousPage + i, 1f);
- }
- }
- }
-
- // create the anim to move the selected dot – this animator will kick off
- // retreat animations when it has moved 75% of the way.
- // The retreat animation in turn will kick of reveal anims when the
- // retreat has passed any dots to be revealed
- moveAnimation = createMoveSelectedAnimator(dotCenterX[now], previousPage, now, steps);
- moveAnimation.start();
- }
-
- private ValueAnimator createMoveSelectedAnimator(
- final float moveTo, int was, int now, int steps) {
-
- // create the actual move animator
- ValueAnimator moveSelected = ValueAnimator.ofFloat(selectedDotX, moveTo);
-
- // also set up a pending retreat anim – this starts when the move is 75% complete
- retreatAnimation = new PendingRetreatAnimator(was, now, steps,
- now > was ?
- new RightwardStartPredicate(moveTo - ((moveTo - selectedDotX) * 0.25f)) :
- new LeftwardStartPredicate(moveTo + ((selectedDotX - moveTo) * 0.25f)));
- retreatAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- resetState();
- pageChanging = false;
- }
- });
- moveSelected.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- // todo avoid autoboxing
- selectedDotX = (Float) valueAnimator.getAnimatedValue();
- retreatAnimation.startIfNecessary(selectedDotX);
- postInvalidateOnAnimation();
- }
- });
- moveSelected.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- // set a flag so that we continue to draw the unselected dot in the target position
- // until the selected dot has finished moving into place
- selectedDotInPosition = false;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- // set a flag when anim finishes so that we don't draw both selected & unselected
- // page dots
- selectedDotInPosition = true;
- }
- });
- // slightly delay the start to give the joins a chance to run
- // unless dot isn't in position yet – then don't delay!
- moveSelected.setStartDelay(selectedDotInPosition ? animDuration / 4l : 0l);
- moveSelected.setDuration(animDuration * 3l / 4l);
- moveSelected.setInterpolator(interpolator);
- return moveSelected;
- }
-
- private void setJoiningFraction(int leftDot, float fraction) {
- if (leftDot < joiningFractions.length) {
-
- if (leftDot == 1) {
- Log.d("PageIndicator", "dot 1 fraction:\t" + fraction);
- }
-
- joiningFractions[leftDot] = fraction;
- postInvalidateOnAnimation();
- }
- }
-
- private void clearJoiningFractions() {
- Arrays.fill(joiningFractions, 0f);
- postInvalidateOnAnimation();
- }
-
- private void setDotRevealFraction(int dot, float fraction) {
- dotRevealFractions[dot] = fraction;
- postInvalidateOnAnimation();
- }
-
- private void cancelJoiningAnimations() {
- if (joiningAnimationSet != null && joiningAnimationSet.isRunning()) {
- joiningAnimationSet.cancel();
- }
- }
-
- /**
- * A {@link ValueAnimator} that starts once a given predicate returns true.
- */
- public abstract class PendingStartAnimator extends ValueAnimator {
-
- protected boolean hasStarted;
- protected StartPredicate predicate;
-
- public PendingStartAnimator(StartPredicate predicate) {
- super();
- this.predicate = predicate;
- hasStarted = false;
- }
-
- public void startIfNecessary(float currentValue) {
- if (!hasStarted && predicate.shouldStart(currentValue)) {
- start();
- hasStarted = true;
- }
- }
- }
-
- /**
- * An Animator that shows and then shrinks a retreating join between the previous and newly
- * selected pages. This also sets up some pending dot reveals – to be started when the retreat
- * has passed the dot to be revealed.
- */
- public class PendingRetreatAnimator extends PendingStartAnimator {
-
- public PendingRetreatAnimator(int was, int now, int steps, StartPredicate predicate) {
- super(predicate);
- setDuration(animHalfDuration);
- setInterpolator(interpolator);
-
- // work out the start/end values of the retreating join from the direction we're
- // travelling in. Also look at the current selected dot position, i.e. we're moving on
- // before a prior anim has finished.
- final float initialX1 = now > was ? Math.min(dotCenterX[was], selectedDotX) - dotRadius
- : dotCenterX[now] - dotRadius;
- final float finalX1 = now > was ? dotCenterX[now] - dotRadius
- : dotCenterX[now] - dotRadius;
- final float initialX2 = now > was ? dotCenterX[now] + dotRadius
- : Math.max(dotCenterX[was], selectedDotX) + dotRadius;
- final float finalX2 = now > was ? dotCenterX[now] + dotRadius
- : dotCenterX[now] + dotRadius;
-
- revealAnimations = new PendingRevealAnimator[steps];
- // hold on to the indexes of the dots that will be hidden by the retreat so that
- // we can initialize their revealFraction's i.e. make sure they're hidden while the
- // reveal animation runs
- final int[] dotsToHide = new int[steps];
- if (initialX1 != finalX1) { // rightward retreat
- setFloatValues(initialX1, finalX1);
- // create the reveal animations that will run when the retreat passes them
- for (int i = 0; i < steps; i++) {
- revealAnimations[i] = new PendingRevealAnimator(was + i,
- new RightwardStartPredicate(dotCenterX[was + i]));
- dotsToHide[i] = was + i;
- }
- addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- // todo avoid autoboxing
- retreatingJoinX1 = (Float) valueAnimator.getAnimatedValue();
- postInvalidateOnAnimation();
- // start any reveal animations if we've passed them
- for (PendingRevealAnimator pendingReveal : revealAnimations) {
- pendingReveal.startIfNecessary(retreatingJoinX1);
- }
- }
- });
- } else { // (initialX2 != finalX2) leftward retreat
- setFloatValues(initialX2, finalX2);
- // create the reveal animations that will run when the retreat passes them
- for (int i = 0; i < steps; i++) {
- revealAnimations[i] = new PendingRevealAnimator(was - i,
- new LeftwardStartPredicate(dotCenterX[was - i]));
- dotsToHide[i] = was - i;
- }
- addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- // todo avoid autoboxing
- retreatingJoinX2 = (Float) valueAnimator.getAnimatedValue();
- postInvalidateOnAnimation();
- // start any reveal animations if we've passed them
- for (PendingRevealAnimator pendingReveal : revealAnimations) {
- pendingReveal.startIfNecessary(retreatingJoinX2);
- }
- }
- });
- }
-
- addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- cancelJoiningAnimations();
- clearJoiningFractions();
- // we need to set this so that the dots are hidden until the reveal anim runs
- for (int dot : dotsToHide) {
- setDotRevealFraction(dot, MINIMAL_REVEAL);
- }
- retreatingJoinX1 = initialX1;
- retreatingJoinX2 = initialX2;
- postInvalidateOnAnimation();
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- retreatingJoinX1 = INVALID_FRACTION;
- retreatingJoinX2 = INVALID_FRACTION;
- postInvalidateOnAnimation();
- }
- });
- }
- }
-
- /**
- * An Animator that animates a given dot's revealFraction i.e. scales it up
- */
- public class PendingRevealAnimator extends PendingStartAnimator {
-
- private int dot;
-
- public PendingRevealAnimator(int dot, StartPredicate predicate) {
- super(predicate);
- setFloatValues(MINIMAL_REVEAL, 1f);
- this.dot = dot;
- setDuration(animHalfDuration);
- setInterpolator(interpolator);
- addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- // todo avoid autoboxing
- setDotRevealFraction(PendingRevealAnimator.this.dot,
- (Float) valueAnimator.getAnimatedValue());
- }
- });
- addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- setDotRevealFraction(PendingRevealAnimator.this.dot, 0f);
- postInvalidateOnAnimation();
- }
- });
- }
- }
-
- /**
- * A predicate used to start an animation when a test passes
- */
- public abstract class StartPredicate {
-
- protected float thresholdValue;
-
- public StartPredicate(float thresholdValue) {
- this.thresholdValue = thresholdValue;
- }
-
- abstract boolean shouldStart(float currentValue);
-
- }
-
- /**
- * A predicate used to start an animation when a given value is greater than a threshold
- */
- public class RightwardStartPredicate extends StartPredicate {
-
- public RightwardStartPredicate(float thresholdValue) {
- super(thresholdValue);
- }
-
- boolean shouldStart(float currentValue) {
- return currentValue > thresholdValue;
- }
- }
-
- /**
- * A predicate used to start an animation then a given value is less than a threshold
- */
- public class LeftwardStartPredicate extends StartPredicate {
-
- public LeftwardStartPredicate(float thresholdValue) {
- super(thresholdValue);
- }
-
- boolean shouldStart(float currentValue) {
- return currentValue < thresholdValue;
- }
- }
-}
diff --git a/core/src/main/kotlin/ca/allanwang/kau/widgets/TextSlider.kt b/core/src/main/kotlin/ca/allanwang/kau/widgets/TextSlider.kt
deleted file mode 100644
index 528dabc..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/widgets/TextSlider.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-package ca.allanwang.kau.widgets
-
-import android.content.Context
-import android.graphics.Color
-import android.support.v4.widget.TextViewCompat
-import android.text.TextUtils
-import android.util.AttributeSet
-import android.view.Gravity
-import android.view.animation.Animation
-import android.view.animation.AnimationUtils
-import android.widget.TextSwitcher
-import android.widget.TextView
-import ca.allanwang.kau.R
-import java.util.*
-
-/**
- * Created by Allan Wang on 2017-06-21.
- *
- * Text switcher with global text color and embedded sliding animations
- * Also has a stack to keep track of title changes
- */
-class TextSlider @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
-) : TextSwitcher(context, attrs) {
-
- val titleStack: Stack<CharSequence?> = Stack()
-
- /**
- * Holds a mapping of animation types to their respective animations
- */
- val animationMap = mapOf(
- ANIMATION_NONE to null,
- ANIMATION_SLIDE_HORIZONTAL to AnimationBundle(
- R.anim.kau_slide_in_right, R.anim.kau_slide_out_left,
- R.anim.kau_slide_in_left, R.anim.kau_slide_out_right),
- ANIMATION_SLIDE_VERTICAL to AnimationBundle(
- R.anim.kau_slide_in_bottom, R.anim.kau_slide_out_top,
- R.anim.kau_slide_in_top, R.anim.kau_slide_out_bottom
- )
- )
-
- /**
- * Holds lazy instances of the animations
- */
- inner class AnimationBundle(private val nextIn: Int, private val nextOut: Int, private val prevIn: Int, private val prevOut: Int) {
- val NEXT_IN: Animation by lazy { AnimationUtils.loadAnimation(context, nextIn) }
- val NEXT_OUT: Animation by lazy { AnimationUtils.loadAnimation(context, nextOut) }
- val PREV_IN: Animation by lazy { AnimationUtils.loadAnimation(context, prevIn) }
- val PREV_OUT: Animation by lazy { AnimationUtils.loadAnimation(context, prevOut) }
- }
-
- companion object {
- const val ANIMATION_NONE = 1000
- const val ANIMATION_SLIDE_HORIZONTAL = 1001
- const val ANIMATION_SLIDE_VERTICAL = 1002
- }
-
- var animationType: Int = ANIMATION_SLIDE_HORIZONTAL
-
- var textColor: Int = Color.WHITE
- get() = field
- set(value) {
- field = value
- (getChildAt(0) as TextView).setTextColor(value)
- (getChildAt(1) as TextView).setTextColor(value)
- }
- val isRoot: Boolean
- get() = titleStack.size <= 1
-
- init {
- if (attrs != null) {
- val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.TextSlider)
- animationType = styledAttrs.getInteger(R.styleable.TextSlider_animation_type, ANIMATION_SLIDE_HORIZONTAL)
- styledAttrs.recycle()
- }
- }
-
- override fun setText(text: CharSequence?) {
- if ((currentView as TextView).text == text) return
- super.setText(text)
- }
-
- override fun setCurrentText(text: CharSequence?) {
- if (titleStack.isNotEmpty()) titleStack.pop()
- titleStack.push(text)
- super.setCurrentText(text)
- }
-
- fun setNextText(text: CharSequence?) {
- if (titleStack.isEmpty()) {
- setCurrentText(text)
- return
- }
- titleStack.push(text)
- val anim = animationMap[animationType]
- inAnimation = anim?.NEXT_IN
- outAnimation = anim?.NEXT_OUT
- setText(text)
- }
-
- /**
- * Sets the text as the previous title
- * No further checks are done, so be sure to verify with [isRoot]
- */
- @Throws(EmptyStackException::class)
- fun setPrevText() {
- titleStack.pop()
- val anim = animationMap[animationType]
- inAnimation = anim?.PREV_IN
- outAnimation = anim?.PREV_OUT
- val text = titleStack.peek()
- setText(text)
- }
-
- init {
- setFactory {
- TextView(context).apply {
- //replica of toolbar title
- gravity = Gravity.START
- setSingleLine()
- ellipsize = TextUtils.TruncateAt.END
- TextViewCompat.setTextAppearance(this, R.style.TextAppearance_AppCompat_Title)
- }
- }
- }
-} \ No newline at end of file