aboutsummaryrefslogtreecommitdiff
path: root/library/src/main/kotlin
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-07-04 16:08:03 -0700
committerAllan Wang <me@allanwang.ca>2017-07-04 16:08:03 -0700
commitcf2a7fcd0880a8d276970124cdb5d5845d5631fe (patch)
treecc38ead7853ddb85c9c988e94a4af605e1e676f8 /library/src/main/kotlin
parentfe4632c34a1d671503e0242a269865b884545e13 (diff)
downloadkau-cf2a7fcd0880a8d276970124cdb5d5845d5631fe.tar.gz
kau-cf2a7fcd0880a8d276970124cdb5d5845d5631fe.tar.bz2
kau-cf2a7fcd0880a8d276970124cdb5d5845d5631fe.zip
Separate core components in its own module
Diffstat (limited to 'library/src/main/kotlin')
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt236
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt85
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt189
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/adapters/SectionAdapter.kt13
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/animators/BaseDelayAnimator.kt45
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java764
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/animators/BaseSlideAlphaAnimator.kt52
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt63
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt51
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt41
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/animators/SlideUpExitRightAnimator.kt23
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt105
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/dialogs/color/CircleView.kt228
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPalette.kt349
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt82
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt309
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt92
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt127
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/iitems/CutoutIItem.kt48
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt49
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt23
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/iitems/LibraryIItem.kt100
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt50
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyResettable.kt51
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kotlin/NonReadablePropertyException.kt12
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt35
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt137
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt120
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt89
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt33
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt73
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt25
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt85
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt126
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt29
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt43
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt62
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/logging/KL.kt6
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/logging/TimberLogger.kt19
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt58
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt26
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt68
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt80
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt412
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt71
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/AnimHolder.kt15
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/AnimUtils.kt153
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt235
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/Const.kt6
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt165
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/Either.kt32
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/FontUtils.kt29
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt12
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/IIconUtils.kt20
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt166
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt48
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/StringHolder.kt22
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/TransitionUtils.kt19
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt111
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt126
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/views/BoundedCardView.kt50
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/views/CutoutView.kt183
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt140
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/views/SimpleRippleDrawable.kt19
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt234
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java859
-rw-r--r--library/src/main/kotlin/ca/allanwang/kau/widgets/TextSlider.kt125
67 files changed, 0 insertions, 7553 deletions
diff --git a/library/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt b/library/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
deleted file mode 100644
index 32e8745..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
+++ /dev/null
@@ -1,236 +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
- * 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<*, *>>
-
- 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)
- }
- 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)
- 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(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/library/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt b/library/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt
deleted file mode 100644
index e1c5c18..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt b/library/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt
deleted file mode 100644
index 66fec4b..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/adapters/SectionAdapter.kt b/library/src/main/kotlin/ca/allanwang/kau/adapters/SectionAdapter.kt
deleted file mode 100644
index cf7205a..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/animators/BaseDelayAnimator.kt b/library/src/main/kotlin/ca/allanwang/kau/animators/BaseDelayAnimator.kt
deleted file mode 100644
index c649376..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java b/library/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java
deleted file mode 100644
index 69c2cf3..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/animators/BaseSlideAlphaAnimator.kt b/library/src/main/kotlin/ca/allanwang/kau/animators/BaseSlideAlphaAnimator.kt
deleted file mode 100644
index a963358..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt b/library/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt
deleted file mode 100644
index 9aeafde..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt b/library/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt
deleted file mode 100644
index e968cda..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt b/library/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt
deleted file mode 100644
index 244287b..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/animators/SlideUpExitRightAnimator.kt b/library/src/main/kotlin/ca/allanwang/kau/animators/SlideUpExitRightAnimator.kt
deleted file mode 100644
index 8670493..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt b/library/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt
deleted file mode 100644
index 302d9dc..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/changelog/Changelog.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-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
-import android.support.v7.widget.RecyclerView
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import ca.allanwang.kau.R
-import ca.allanwang.kau.utils.bindOptionalView
-import ca.allanwang.kau.utils.bindView
-import ca.allanwang.kau.utils.materialDialog
-import ca.allanwang.kau.utils.use
-import com.afollestad.materialdialogs.MaterialDialog
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.uiThread
-import org.xmlpull.v1.XmlPullParser
-import java.util.*
-
-
-/**
- * Created by Allan Wang on 2017-05-28.
- */
-
-fun Context.showChangelog(@XmlRes xmlRes: Int, @ColorInt textColor: Int? = null, customize: MaterialDialog.Builder.() -> Unit = {}) {
- doAsync {
- val items = parse(this@showChangelog, xmlRes)
- uiThread {
- materialDialog {
- title(R.string.kau_changelog)
- positiveText(R.string.kau_great)
- adapter(ChangelogAdapter(items, textColor), null)
- customize()
- }
- }
- }
-}
-
-/**
- * Internals of the changelog dialog
- * Contains an mainAdapter for each item, as well as the tags to parse
- */
-internal class ChangelogAdapter(val items: List<Pair<String, ChangelogType>>, @ColorInt val textColor: Int? = null) : RecyclerView.Adapter<ChangelogAdapter.ChangelogVH>() {
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ChangelogVH(LayoutInflater.from(parent.context)
- .inflate(items[viewType].second.layout, parent, false))
-
- override fun onBindViewHolder(holder: ChangelogVH, position: Int) {
- holder.text.text = items[position].first
- if (textColor != null) {
- holder.text.setTextColor(textColor)
- holder.bullet?.setTextColor(textColor)
- }
- }
-
- override fun getItemId(position: Int) = position.toLong()
-
- override fun getItemViewType(position: Int) = position
-
- override fun getItemCount() = items.size
-
- internal class ChangelogVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
- val text: TextView by bindView(R.id.kau_changelog_text)
- val bullet: TextView? by bindOptionalView(R.id.kau_changelog_bullet)
- }
-}
-
-internal fun parse(context: Context, @XmlRes xmlRes: Int): List<Pair<String, ChangelogType>> {
- val items = mutableListOf<Pair<String, ChangelogType>>()
- context.resources.getXml(xmlRes).use {
- parser: XmlResourceParser ->
- var eventType = parser.eventType
- while (eventType != XmlPullParser.END_DOCUMENT) {
- if (eventType == XmlPullParser.START_TAG)
- ChangelogType.values.any { it.add(parser, items) }
- eventType = parser.next()
- }
- }
- return items
-}
-
-internal enum class ChangelogType(val tag: String, val attr: String, @LayoutRes val layout: Int) {
- TITLE("version", "title", R.layout.kau_changelog_title),
- ITEM("item", "text", R.layout.kau_changelog_content);
-
- companion object {
- @JvmStatic val values = values()
- }
-
- /**
- * Returns true if tag matches; false otherwise
- */
- fun add(parser: XmlResourceParser, list: MutableList<Pair<String, ChangelogType>>): Boolean {
- if (parser.name != tag) return false
- if (parser.getAttributeValue(null, attr).isNotBlank())
- list.add(Pair(parser.getAttributeValue(null, attr), this))
- return true
- }
-}
-
diff --git a/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/CircleView.kt b/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/CircleView.kt
deleted file mode 100644
index 3430b42..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPalette.kt b/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPalette.kt
deleted file mode 100644
index 22bd0d4..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt b/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerDialog.kt
deleted file mode 100644
index 7c57c26..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt b/library/src/main/kotlin/ca/allanwang/kau/dialogs/color/ColorPickerView.kt
deleted file mode 100644
index da864c9..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt b/library/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt
deleted file mode 100644
index b03a620..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-package ca.allanwang.kau.email
-
-import android.app.Activity
-import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Build
-import android.support.annotation.StringRes
-import android.util.DisplayMetrics
-import ca.allanwang.kau.R
-import ca.allanwang.kau.logging.KL
-import ca.allanwang.kau.utils.installerPackageName
-import ca.allanwang.kau.utils.isAppInstalled
-import ca.allanwang.kau.utils.string
-
-
-/**
- * Created by Allan Wang on 2017-06-20.
- */
-class EmailBuilder(val email: String, val subject: String) {
- var message: String = "Write here."
- var deviceDetails: Boolean = true
- var appInfo: Boolean = true
- var footer: String? = null
- private val pairs: MutableMap<String, String> = mutableMapOf()
- private val packages: MutableList<Package> = mutableListOf()
-
- fun checkPackage(packageName: String, appName: String) = packages.add(Package(packageName, appName))
-
- fun addItem(key: String, value: String) = pairs.put(key, value)
-
- data class Package(val packageName: String, val appName: String)
-
- fun execute(context: Context) {
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse("mailto:$email"))
- intent.putExtra(Intent.EXTRA_SUBJECT, subject)
- val emailBuilder = StringBuilder()
- emailBuilder.append(message).append("\n\n")
- if (deviceDetails) {
- val deviceItems = mutableMapOf(
- "OS Version" to "${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})",
- "OS API Level" to Build.DEVICE,
- "Manufacturer" to Build.MANUFACTURER,
- "Model (and Product)" to "${Build.MODEL} (${Build.PRODUCT})",
- "Package Installer" to (context.installerPackageName ?: "None")
- )
- if (context is Activity) {
- val metric = DisplayMetrics()
- context.windowManager.defaultDisplay.getMetrics(metric)
- deviceItems.put("Screen Dimensions", "${metric.widthPixels} x ${metric.heightPixels}")
- }
- deviceItems.forEach { (k, v) -> emailBuilder.append("$k: $v\n") }
- }
- if (appInfo) {
- try {
- val appInfo = context.packageManager.getPackageInfo(context.packageName, 0)
- emailBuilder.append("\nApp: ").append(context.packageName)
- .append("\nApp Version Name: ").append(appInfo.versionName)
- .append("\nApp Version Code: ").append(appInfo.versionCode).append("\n")
- } catch (e: PackageManager.NameNotFoundException) {
- KL.e("EmailBuilder packageInfo not found")
- }
- }
-
- if (packages.isNotEmpty()) emailBuilder.append("\n")
- packages.forEach {
- if (context.isAppInstalled(it.packageName))
- emailBuilder.append(String.format("\n%s is installed", it.appName))
- }
-
- if (pairs.isNotEmpty()) emailBuilder.append("\n")
- pairs.forEach { (k, v) -> emailBuilder.append("$k: $v\n") }
-
- if (footer != null)
- emailBuilder.append("\n").append(footer)
-
- intent.putExtra(Intent.EXTRA_TEXT, emailBuilder.toString())
- context.startActivity(Intent.createChooser(intent, context.resources.getString(R.string.kau_send_via)))
- }
-}
-
-fun Context.sendEmail(@StringRes emailId: Int, @StringRes subjectId: Int, builder: EmailBuilder.() -> Unit = {})
- = sendEmail(string(emailId), string(subjectId), builder)
-
-
-fun Context.sendEmail(email: String, subject: String, builder: EmailBuilder.() -> Unit = {}) {
- EmailBuilder(email, subject).apply {
- builder()
- execute(this@sendEmail)
- }
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt b/library/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt
deleted file mode 100644
index 3380ade..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/iitems/CutoutIItem.kt b/library/src/main/kotlin/ca/allanwang/kau/iitems/CutoutIItem.kt
deleted file mode 100644
index 627e1df..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt b/library/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt
deleted file mode 100644
index e994781..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt b/library/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt
deleted file mode 100644
index 00b165c..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt
+++ /dev/null
@@ -1,23 +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
- */
-open class KauIItem<Item, VH : RecyclerView.ViewHolder>(
- private val type: Int,
- @param:LayoutRes private val layoutRes: Int,
- private val viewHolder: (v: View) -> VH
-) : 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/library/src/main/kotlin/ca/allanwang/kau/iitems/LibraryIItem.kt b/library/src/main/kotlin/ca/allanwang/kau/iitems/LibraryIItem.kt
deleted file mode 100644
index aabd9e3..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt b/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt
deleted file mode 100644
index 8b59539..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package ca.allanwang.kau.kotlin
-
-import android.content.Context
-import android.support.annotation.AnimatorRes
-import android.support.annotation.InterpolatorRes
-import android.view.animation.Animation
-import android.view.animation.AnimationUtils
-import android.view.animation.Interpolator
-
-/**
- * Created by Allan Wang on 2017-05-30.
- *
- * Lazy retrieval of context based items
- * Items are retrieved using delegateName[context]
- *
- */
-
-fun lazyInterpolator(@InterpolatorRes id: Int) = lazyContext<Interpolator> { AnimationUtils.loadInterpolator(it, id) }
-fun lazyAnimation(@AnimatorRes id: Int) = lazyContext<Animation> { AnimationUtils.loadAnimation(it, id) }
-
-fun <T : Any> lazyContext(initializer: (context: Context) -> T): LazyContext<T> = LazyContext<T>(initializer)
-
-class LazyContext<out T : Any>(private val initializer: (context: Context) -> T, lock: Any? = null) {
- @Volatile private var _value: Any = UNINITIALIZED
- private val lock = lock ?: this
-
- fun invalidate() {
- _value = UNINITIALIZED
- }
-
- operator fun invoke(context: Context): T {
- val _v1 = _value
- if (_v1 !== UNINITIALIZED)
- @Suppress("UNCHECKED_CAST")
- return _v1 as T
-
- return synchronized(lock) {
- val _v2 = _value
- if (_v2 !== UNINITIALIZED) {
- @Suppress("UNCHECKED_CAST")
- _v2 as T
- } else {
- val typedValue = initializer(context)
- _value = typedValue
- typedValue
- }
- }
- }
-
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyResettable.kt b/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyResettable.kt
deleted file mode 100644
index f8947f3..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/kotlin/LazyResettable.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package ca.allanwang.kau.kotlin
-
-import java.io.Serializable
-import kotlin.reflect.KProperty
-
-/**
- * Created by Allan Wang on 2017-05-30.
- *
- * Lazy delegate that can be invalidated if needed
- * https://stackoverflow.com/a/37294840/4407321
- */
-internal object UNINITIALIZED
-
-fun <T : Any> lazyResettable(initializer: () -> T): LazyResettable<T> = LazyResettable<T>(initializer)
-
-class LazyResettable<T : Any>(private val initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
- @Volatile private var _value: Any = UNINITIALIZED
- private val lock = lock ?: this
-
- fun invalidate() {
- _value = UNINITIALIZED
- }
-
- override val value: T
- get() {
- val _v1 = _value
- if (_v1 !== UNINITIALIZED)
- @Suppress("UNCHECKED_CAST")
- return _v1 as T
-
- return synchronized(lock) {
- val _v2 = _value
- if (_v2 !== UNINITIALIZED) {
- @Suppress("UNCHECKED_CAST")
- _v2 as T
- } else {
- val typedValue = initializer()
- _value = typedValue
- typedValue
- }
- }
- }
-
- override fun isInitialized(): Boolean = _value !== UNINITIALIZED
-
- override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
-
- operator fun setValue(any: Any, property: KProperty<*>, t: T) {
- _value = t
- }
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/kotlin/NonReadablePropertyException.kt b/library/src/main/kotlin/ca/allanwang/kau/kotlin/NonReadablePropertyException.kt
deleted file mode 100644
index f3add48..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/kotlin/NonReadablePropertyException.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package ca.allanwang.kau.kotlin
-
-/**
- * Created by Allan Wang on 2017-06-24.
- *
- * Credits to @zsmb13
- *
- * https://github.com/zsmb13/MaterialDrawerKt/blob/master/library/src/main/java/co/zsmb/materialdrawerkt/NonReadablePropertyException.kt
- */
-class NonReadablePropertyException : Exception()
-
-fun nonReadable(): Nothing = throw NonReadablePropertyException() \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt
deleted file mode 100644
index 7fd8955..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package ca.allanwang.kau.kpref
-
-import android.content.Context
-import android.content.SharedPreferences
-
-/**
- * Created by Allan Wang on 2017-06-07.
- */
-open class KPref {
-
- lateinit private var c: Context
- lateinit internal var PREFERENCE_NAME: String
- private var initialized = false
-
- fun initialize(c: Context, preferenceName: String) {
- if (initialized) throw KPrefException("KPref object $preferenceName has already been initialized; please only do so once")
- initialized = true
- this.c = c.applicationContext
- PREFERENCE_NAME = preferenceName
- }
-
- internal val sp: SharedPreferences by lazy {
- if (!initialized) throw KPrefException("KPref object has not yet been initialized; please initialize it with a context and preference name")
- c.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)
- }
-
- internal val prefMap: MutableMap<String, KPrefDelegate<*>> = mutableMapOf()
-
- fun reset() {
- prefMap.values.forEach { it.invalidate() }
- }
-
- operator fun get(key: String): KPrefDelegate<*>? = prefMap[key]
-
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefActivity.kt
deleted file mode 100644
index 9a9f7d4..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt
deleted file mode 100644
index 7f42d2a..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBinder.kt
+++ /dev/null
@@ -1,120 +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(internal 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
- internal val list: MutableList<KPrefItemCore> = mutableListOf()
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt
deleted file mode 100644
index 4d57ff1..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-package ca.allanwang.kau.kpref
-
-/**
- * Created by Allan Wang on 2017-06-07.
- */
-private object UNINITIALIZED
-
-fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit = {}): KPrefDelegate<Boolean> = KPrefDelegate(key, fallback, this, postSetter)
-fun KPref.kpref(key: String, fallback: Double, postSetter: (value: Float) -> Unit = {}): KPrefDelegate<Float> = KPrefDelegate(key, fallback.toFloat(), this, postSetter)
-fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit = {}): KPrefDelegate<Float> = KPrefDelegate(key, fallback, this, postSetter)
-fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit = {}): KPrefDelegate<Int> = KPrefDelegate(key, fallback, this, postSetter)
-fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit = {}): KPrefDelegate<Long> = KPrefDelegate(key, fallback, this, postSetter)
-fun KPref.kpref(key: String, fallback: Set<String>, postSetter: (value: Set<String>) -> Unit = {}): KPrefDelegate<StringSet> = KPrefDelegate(key, StringSet(fallback), this, postSetter)
-fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit = {}): KPrefDelegate<String> = KPrefDelegate(key, fallback, this, postSetter)
-
-class StringSet(set: Collection<String>) : LinkedHashSet<String>(set)
-
-/**
- * Implementation of a kpref data item
- * Contains a unique key for the shared preference as well as a nonnull fallback item
- * Also contains an optional mutable postSetter that will be called every time a new value is given
- */
-class KPrefDelegate<T : Any> internal constructor(private val key: String, private val fallback: T, private val pref: KPref, var postSetter: (value: T) -> Unit = {}, lock: Any? = null) : Lazy<T>, java.io.Serializable {
-
- @Volatile private var _value: Any = UNINITIALIZED
- private val lock = lock ?: this
-
- init {
- if (pref.prefMap.containsKey(key))
- throw KPrefException("$key is already used elsewhere in preference ${pref.PREFERENCE_NAME}")
- pref.prefMap.put(key, this@KPrefDelegate)
- }
-
- fun invalidate() {
- _value = UNINITIALIZED
- }
-
- override val value: T
- get() {
- val _v1 = _value
- if (_v1 !== UNINITIALIZED)
- @Suppress("UNCHECKED_CAST")
- return _v1 as T
-
- return synchronized(lock) {
- val _v2 = _value
- if (_v2 !== UNINITIALIZED) {
- @Suppress("UNCHECKED_CAST")
- _v2 as T
- } else {
- _value = when (fallback) {
- is Boolean -> pref.sp.getBoolean(key, fallback)
- is Float -> pref.sp.getFloat(key, fallback)
- is Int -> pref.sp.getInt(key, fallback)
- is Long -> pref.sp.getLong(key, fallback)
- is StringSet -> StringSet(pref.sp.getStringSet(key, fallback))
- is String -> pref.sp.getString(key, fallback)
- else -> throw KPrefException(fallback)
- }
- @Suppress("UNCHECKED_CAST")
- _value as T
- }
- }
- }
-
- override fun isInitialized(): Boolean = _value !== UNINITIALIZED
-
- override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet."
-
- operator fun setValue(any: Any, property: kotlin.reflect.KProperty<*>, t: T) {
- _value = t
- val editor = pref.sp.edit()
- when (t) {
- is Boolean -> editor.putBoolean(key, t)
- is Float -> editor.putFloat(key, t)
- is Int -> editor.putInt(key, t)
- is Long -> editor.putLong(key, t)
- is StringSet -> editor.putStringSet(key, t)
- is String -> editor.putString(key, t)
- else -> throw KPrefException(t)
- }
- editor.apply()
- postSetter.invoke(t)
- }
-}
-
-class KPrefException(message: String) : IllegalAccessException(message) {
- constructor(element: Any?) : this("Invalid type in pref cache: ${element?.javaClass?.simpleName ?: "null"}")
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefCheckbox.kt
deleted file mode 100644
index 22cc927..0000000
--- a/library/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
- */
-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/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefColorPicker.kt
deleted file mode 100644
index c573939..0000000
--- a/library/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
- */
-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/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefHeader.kt
deleted file mode 100644
index fa8efff..0000000
--- a/library/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
- */
-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/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemBase.kt
deleted file mode 100644
index bb0f0a3..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt
deleted file mode 100644
index 5f684ba..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefItemCore.kt
+++ /dev/null
@@ -1,126 +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()
- }
- }
-
- /**
- * 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 innerContent: View?
- get() = itemView.findViewById(R.id.kau_pref_inner_content)
-
- inline fun <reified T : View> bindInnerView(@LayoutRes id: Int): 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)
- }
- return innerContent as T
- }
-
- inline fun <reified T : View> getInnerView() = innerContent as T
-
- operator fun get(@IdRes id: Int): View = itemView.findViewById(id)
- }
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefPlainText.kt
deleted file mode 100644
index a782430..0000000
--- a/library/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
- *
- */
-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/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefSubItems.kt
deleted file mode 100644
index 51625ab..0000000
--- a/library/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
- *
- */
-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/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt b/library/src/main/kotlin/ca/allanwang/kau/kpref/items/KPrefText.kt
deleted file mode 100644
index 8662b6a..0000000
--- a/library/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
- *
- */
-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/library/src/main/kotlin/ca/allanwang/kau/logging/KL.kt b/library/src/main/kotlin/ca/allanwang/kau/logging/KL.kt
deleted file mode 100644
index 4fa3360..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/logging/KL.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package ca.allanwang.kau.logging
-
-/**
- * Created by Allan Wang on 2017-06-19.
- */
-object KL : TimberLogger("KAU") \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/logging/TimberLogger.kt b/library/src/main/kotlin/ca/allanwang/kau/logging/TimberLogger.kt
deleted file mode 100644
index 5969fd5..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/logging/TimberLogger.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package ca.allanwang.kau.logging
-
-import timber.log.Timber
-
-
-/**
- * Created by Allan Wang on 2017-05-28.
- *
- * Timber extension that will embed the tag as part of the message for each log item
- */
-open class TimberLogger(tag: String) {
- internal val TAG = "$tag: %s"
- fun e(s: String) = Timber.e(TAG, s)
- fun e(t: Throwable, s: String = "error") = Timber.e(t, TAG, s)
- fun d(s: String) = Timber.d(TAG, s)
- fun i(s: String) = Timber.i(TAG, s)
- fun v(s: String) = Timber.v(TAG, s)
- fun eThrow(s: String) = e(Throwable(s))
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt b/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt
deleted file mode 100644
index 6f93c9f..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package ca.allanwang.kau.permissions
-
-import android.app.Activity
-import android.content.Context
-import android.support.v4.app.ActivityCompat
-import ca.allanwang.kau.logging.KL
-import ca.allanwang.kau.utils.KauException
-import ca.allanwang.kau.utils.buildIsMarshmallowAndUp
-import ca.allanwang.kau.utils.hasPermission
-import java.lang.ref.WeakReference
-
-/**
- * Created by Allan Wang on 2017-07-03.
- */
-internal object PermissionManager {
-
- var requestInProgress = false
- val pendingResults: MutableList<WeakReference<PermissionResult>> by lazy { mutableListOf<WeakReference<PermissionResult>>() }
-
- operator fun invoke(context: Context, permissions: Array<out String>, callback: (granted: Boolean, deniedPerm: String?) -> Unit) {
- KL.d("Requesting permissions: ${permissions.contentToString()}")
- if (!buildIsMarshmallowAndUp) return callback(true, null)
- val missingPermissions = permissions.filter { !context.hasPermission(it) }
- if (missingPermissions.isEmpty()) return callback(true, null)
- pendingResults.add(WeakReference(PermissionResult(permissions, callback = callback)))
- if (!requestInProgress) {
- requestInProgress = true
- requestPermissions(context, missingPermissions.toTypedArray())
- } else KL.d("Request is postponed since another one is still in progress; did you remember to override onRequestPermissionsResult?")
- }
-
- @Synchronized internal fun requestPermissions(context: Context, permissions: Array<out String>) {
- val activity = (context as? Activity) ?: throw KauException("Context is not an instance of an activity; cannot request permissions")
- ActivityCompat.requestPermissions(activity, permissions, 1)
- }
-
- fun onRequestPermissionsResult(context: Context, permissions: Array<out String>, grantResults: IntArray) {
- val count = Math.min(permissions.size, grantResults.size)
- val iter = pendingResults.iterator()
- while (iter.hasNext()) {
- val action = iter.next().get()
- if ((0 until count).any { action?.onResult(permissions[it], grantResults[it]) ?: true })
- iter.remove()
- }
- if (pendingResults.isEmpty())
- requestInProgress = false
- else {
- val action = pendingResults.map { it.get() }.firstOrNull { it != null }
- if (action == null) { //actions have been unlinked from their weak references
- pendingResults.clear()
- requestInProgress = false
- return
- }
- requestPermissions(context, action.permissions.toTypedArray())
- }
- }
-
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt b/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt
deleted file mode 100644
index 14bfdff..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package ca.allanwang.kau.permissions
-
-import android.content.pm.PackageManager
-
-/**
- * Created by Allan Wang on 2017-07-03.
- */
-class PermissionResult(permissions: Array<out String>, val callback: (granted: Boolean, deniedPerm: String?) -> Unit) {
- val permissions = mutableSetOf(*permissions)
-
- /**
- * Called from the manager whenever a permission has changed
- * Returns true if result is completed, false otherwise
- */
- fun onResult(permission: String, result: Int): Boolean {
- if (result != PackageManager.PERMISSION_GRANTED) {
- callback(false, permission)
- permissions.clear()
- return true
- }
- permissions.remove(permission)
- if (permissions.isNotEmpty()) return false
- callback(true, null)
- return true
- }
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt b/library/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt
deleted file mode 100644
index fd43102..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package ca.allanwang.kau.permissions
-
-import android.Manifest
-import android.app.Activity
-import android.content.Context
-
-
-/**
- * Created by Allan Wang on 2017-07-02.
- *
- * Bindings for the permission manager
- */
-
-/**
- * Hook that should be added inside all [Activity.onRequestPermissionsResult] so that the Permission manager can handle the responses
- */
-fun Activity.kauOnRequestPermissionsResult(permissions: Array<out String>, grantResults: IntArray)
- = PermissionManager.onRequestPermissionsResult(this, permissions, grantResults)
-
-/**
- * Request a permission with a callback
- * In reality, an activity is needed to fulfill the request, but a context is enough if those permissions are already granted
- * To be safe, you may want to check that the context can be casted successfully first
- * The [callback] returns [granted], which is true if all permissions are granted
- * [deniedPerm] is the first denied permission, if granted is false
- */
-fun Context.kauRequestPermissions(vararg permissions: String, callback: (granted: Boolean, deniedPerm: String?) -> Unit)
- = PermissionManager(this, permissions, callback)
-
-/**
- * See http://developer.android.com/guide/topics/security/permissions.html#normal-dangerous for a
- * list of 'dangerous' permissions that require a permission request on API 23.
- */
-const val PERMISSION_READ_CALENDAR = Manifest.permission.READ_CALENDAR
-
-const val PERMISSION_WRITE_CALENDAR = Manifest.permission.WRITE_CALENDAR
-
-const val PERMISSION_CAMERA = Manifest.permission.CAMERA
-
-const val PERMISSION_READ_CONTACTS = Manifest.permission.READ_CONTACTS
-const val PERMISSION_WRITE_CONTACTS = Manifest.permission.WRITE_CONTACTS
-const val PERMISSION_GET_ACCOUNTS = Manifest.permission.GET_ACCOUNTS
-
-const val PERMISSION_ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION
-const val PERMISSION_ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION
-
-const val PERMISSION_RECORD_AUDIO = Manifest.permission.RECORD_AUDIO
-
-const val PERMISSION_READ_PHONE_STATE = Manifest.permission.READ_PHONE_STATE
-const val PERMISSION_CALL_PHONE = Manifest.permission.CALL_PHONE
-const val PERMISSION_READ_CALL_LOG = Manifest.permission.READ_CALL_LOG
-const val PERMISSION_WRITE_CALL_LOG = Manifest.permission.WRITE_CALL_LOG
-const val PERMISSION_ADD_VOICEMAIL = Manifest.permission.ADD_VOICEMAIL
-const val PERMISSION_USE_SIP = Manifest.permission.USE_SIP
-const val PERMISSION_PROCESS_OUTGOING_CALLS = Manifest.permission.PROCESS_OUTGOING_CALLS
-
-const val PERMISSION_BODY_SENSORS = Manifest.permission.BODY_SENSORS
-
-const val PERMISSION_SEND_SMS = Manifest.permission.SEND_SMS
-const val PERMISSION_RECEIVE_SMS = Manifest.permission.RECEIVE_SMS
-const val PERMISSION_READ_SMS = Manifest.permission.READ_SMS
-const val PERMISSION_RECEIVE_WAP_PUSH = Manifest.permission.RECEIVE_WAP_PUSH
-const val PERMISSION_RECEIVE_MMS = Manifest.permission.RECEIVE_MMS
-
-const val PERMISSION_READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE
-const val PERMISSION_WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE
-
-const val PERMISSION_SYSTEM_ALERT_WINDOW = Manifest.permission.SYSTEM_ALERT_WINDOW \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt b/library/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt
deleted file mode 100644
index ac8ec2e..0000000
--- a/library/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.id.kau_item_search,
- R.layout.kau_search_iitem,
- {ViewHolder(it)}
-) {
-
- 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/library/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt b/library/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt
deleted file mode 100644
index c077a06..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt
deleted file mode 100644
index ec51bfd..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.app.Activity
-import android.content.Intent
-import android.graphics.Color
-import android.support.annotation.ColorInt
-import android.support.annotation.StringRes
-import android.support.design.widget.Snackbar
-import android.support.v4.app.Fragment
-import android.view.Menu
-import ca.allanwang.kau.R
-import com.mikepenz.iconics.typeface.IIcon
-import org.jetbrains.anko.contentView
-import org.jetbrains.anko.withArguments
-
-/**
- * Created by Allan Wang on 2017-06-21.
- */
-
-/**
- * Restarts an activity from itself without animations
- * Keeps its existing extra bundles and has a builder to accept other parameters
- */
-fun Activity.restart(builder: Intent.() -> Unit = {}) {
- val i = Intent(this, this::class.java)
- i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
- i.putExtras(intent.extras)
- i.builder()
- startActivity(i)
- overridePendingTransition(0, 0) //No transitions
- finish()
- overridePendingTransition(0, 0)
-}
-
-fun Activity.finishSlideOut() {
- finish()
- overridePendingTransition(R.anim.kau_fade_in, R.anim.kau_slide_out_right_top)
-}
-
-var Activity.navigationBarColor: Int
- get() = if (buildIsLollipopAndUp) window.navigationBarColor else Color.BLACK
- set(value) {
- if (buildIsLollipopAndUp) window.navigationBarColor = value
- }
-
-var Activity.statusBarColor: Int
- get() = if (buildIsLollipopAndUp) window.statusBarColor else Color.BLACK
- set(value) {
- if (buildIsLollipopAndUp) window.statusBarColor = value
- }
-
-/**
- * Themes the base menu icons and adds iicons programmatically based on ids
- *
- * Call in [Activity.onCreateOptionsMenu]
- */
-fun Activity.setMenuIcons(menu: Menu, @ColorInt color: Int = Color.WHITE, vararg iicons: Pair<Int, IIcon>) {
- iicons.forEach { (id, iicon) ->
- menu.findItem(id).icon = iicon.toDrawable(this, sizeDp = 18, color = color)
- }
-}
-
-fun Activity.hideKeyboard() = currentFocus.hideKeyboard()
-
-fun Activity.showKeyboard() = currentFocus.showKeyboard()
-
-fun Activity.snackbar(text: String, duration: Int = Snackbar.LENGTH_LONG, builder: Snackbar.() -> Unit = {})
- = contentView!!.snackbar(text, duration, builder)
-
-fun Activity.snackbar(@StringRes textId: Int, duration: Int = Snackbar.LENGTH_LONG, builder: Snackbar.() -> Unit = {})
- = contentView!!.snackbar(textId, duration, builder) \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/AnimHolder.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/AnimHolder.kt
deleted file mode 100644
index 3db8b9c..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/AnimHolder.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package ca.allanwang.kau.utils
-
-import ca.allanwang.kau.kotlin.lazyInterpolator
-
-/**
- * Created by Allan Wang on 2017-06-28.
- *
- * Holder for a bunch of common animators/interpolators used throughout this library
- */
-object AnimHolder {
-
- val fastOutSlowInInterpolator = lazyInterpolator(android.R.interpolator.fast_out_linear_in)
- val decelerateInterpolator = lazyInterpolator(android.R.interpolator.decelerate_cubic)
-
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/AnimUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/AnimUtils.kt
deleted file mode 100644
index 86b049e..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/AnimUtils.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.content.Context
-import android.support.annotation.StringRes
-import android.view.View
-import android.view.ViewAnimationUtils
-import android.view.animation.Animation
-import android.view.animation.AnimationUtils
-import android.view.animation.DecelerateInterpolator
-import android.view.animation.Interpolator
-import android.widget.TextView
-import ca.allanwang.kau.kotlin.lazyContext
-
-/**
- * Created by Allan Wang on 2017-06-01.
- *
- * Animation extension @KauUtils functions for Views
- */
-@KauUtils fun View.rootCircularReveal(x: Int = 0, y: Int = 0, duration: Long = 500L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
- this.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
- override @KauUtils fun onLayoutChange(v: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int,
- oldRight: Int, oldBottom: Int) {
- v.removeOnLayoutChangeListener(this)
- var x2 = x
- var y2 = y
- if (x2 > right) x2 = 0
- if (y2 > bottom) y2 = 0
- val radius = Math.hypot(Math.max(x2, right - x2).toDouble(), Math.max(y2, bottom - y2).toDouble()).toInt()
- val reveal = ViewAnimationUtils.createCircularReveal(v, x2, y2, 0f, radius.toFloat())
- reveal.interpolator = DecelerateInterpolator(1f)
- reveal.duration = duration
- reveal.addListener(object : AnimatorListenerAdapter() {
- override @KauUtils fun onAnimationStart(animation: Animator?) {
- visible()
- onStart?.invoke()
- }
-
- override @KauUtils fun onAnimationEnd(animation: Animator?) = onFinish?.invoke() ?: Unit
- override @KauUtils fun onAnimationCancel(animation: Animator?) = onFinish?.invoke() ?: Unit
- })
- reveal.start()
- }
- })
-}
-
-@KauUtils fun View.circularReveal(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float = -1.0f, duration: Long = 500L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
- if (!isAttachedToWindow) {
- onStart?.invoke()
- visible()
- onFinish?.invoke()
- return
- }
- var r = radius
- if (r < 0.0f) {
- r = Math.max(Math.hypot(x.toDouble(), y.toDouble()), Math.hypot((width - x.toDouble()), (height - y.toDouble()))).toFloat()
- }
- val anim = ViewAnimationUtils.createCircularReveal(this, x, y, 0f, r).setDuration(duration)
- anim.startDelay = offset
- anim.addListener(object : AnimatorListenerAdapter() {
- override @KauUtils fun onAnimationStart(animation: Animator?) {
- visible()
- onStart?.invoke()
- }
-
- override @KauUtils fun onAnimationEnd(animation: Animator?) = onFinish?.invoke() ?: Unit
- override @KauUtils fun onAnimationCancel(animation: Animator?) = onFinish?.invoke() ?: Unit
- })
- anim.start()
-}
-
-@KauUtils fun View.circularHide(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float = -1.0f, duration: Long = 500L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
- if (!isAttachedToWindow) {
- onStart?.invoke()
- invisible()
- onFinish?.invoke()
- return
- }
- var r = radius
- if (r < 0.0f) {
- r = Math.max(Math.hypot(x.toDouble(), y.toDouble()), Math.hypot((width - x.toDouble()), (height - y.toDouble()))).toFloat()
- }
- val anim = ViewAnimationUtils.createCircularReveal(this, x, y, r, 0f).setDuration(duration)
- anim.startDelay = offset
- anim.addListener(object : AnimatorListenerAdapter() {
- override @KauUtils fun onAnimationStart(animation: Animator?) = onStart?.invoke() ?: Unit
-
- override @KauUtils fun onAnimationEnd(animation: Animator?) {
- invisible()
- onFinish?.invoke() ?: Unit
- }
-
- override @KauUtils fun onAnimationCancel(animation: Animator?) = onFinish?.invoke() ?: Unit
- })
- anim.start()
-}
-
-@KauUtils fun View.fadeIn(offset: Long = 0L, duration: Long = 200L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
- if (!isAttachedToWindow) {
- onStart?.invoke()
- visible()
- onFinish?.invoke()
- return
- }
- if (isAttachedToWindow) {
- val anim = AnimationUtils.loadAnimation(context, android.R.anim.fade_in)
- anim.startOffset = offset
- anim.duration = duration
- anim.setAnimationListener(object : Animation.AnimationListener {
- override @KauUtils fun onAnimationRepeat(animation: Animation?) {}
- override @KauUtils fun onAnimationEnd(animation: Animation?) = onFinish?.invoke() ?: Unit
- override @KauUtils fun onAnimationStart(animation: Animation?) {
- visible()
- onStart?.invoke()
- }
- })
- startAnimation(anim)
- }
-}
-
-@KauUtils fun View.fadeOut(offset: Long = 0L, duration: Long = 200L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
- if (!isAttachedToWindow) {
- onStart?.invoke()
- invisible()
- onFinish?.invoke()
- return
- }
- val anim = AnimationUtils.loadAnimation(context, android.R.anim.fade_out)
- anim.startOffset = offset
- anim.duration = duration
- anim.setAnimationListener(object : Animation.AnimationListener {
- override @KauUtils fun onAnimationRepeat(animation: Animation?) {}
- override @KauUtils fun onAnimationEnd(animation: Animation?) {
- invisible()
- onFinish?.invoke()
- }
-
- override @KauUtils fun onAnimationStart(animation: Animation?) {
- onStart?.invoke()
- }
- })
- startAnimation(anim)
-}
-
-@KauUtils fun TextView.setTextWithFade(text: String, duration: Long = 200, onFinish: (() -> Unit)? = null) {
- fadeOut(duration = duration, onFinish = {
- setText(text)
- fadeIn(duration = duration, onFinish = onFinish)
- })
-}
-
-@KauUtils fun TextView.setTextWithFade(@StringRes textId: Int, duration: Long = 200, onFinish: (() -> Unit)? = null) = setTextWithFade(context.getString(textId), duration, onFinish) \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt
deleted file mode 100644
index 8590d6f..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt
+++ /dev/null
@@ -1,235 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.content.Context
-import android.content.res.ColorStateList
-import android.graphics.Color
-import android.graphics.PorterDuff
-import android.graphics.drawable.Drawable
-import android.os.Build
-import android.support.annotation.ColorInt
-import android.support.annotation.FloatRange
-import android.support.annotation.IntRange
-import android.support.v4.content.ContextCompat
-import android.support.v4.graphics.drawable.DrawableCompat
-import android.support.v7.widget.AppCompatEditText
-import android.support.v7.widget.Toolbar
-import android.widget.*
-import com.afollestad.materialdialogs.R
-
-/**
- * Created by Allan Wang on 2017-06-08.
- */
-fun Int.isColorDark(): Boolean
- = (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255.0 < 0.5
-
-fun Int.toHexString(withAlpha: Boolean = false, withHexPrefix: Boolean = true): String {
- val hex = if (withAlpha) String.format("#%08X", this)
- else String.format("#%06X", 0xFFFFFF and this)
- return if (withHexPrefix) hex else hex.substring(1)
-}
-
-fun Int.toRgbaString(): String = "rgba(${Color.red(this)}, ${Color.green(this)}, ${Color.blue(this)}, ${(Color.alpha(this) / 255f).round(3)})"
-
-fun Int.toHSV(): FloatArray {
- val hsv = FloatArray(3)
- Color.colorToHSV(this, hsv)
- return hsv
-}
-
-fun FloatArray.toColor(): Int = Color.HSVToColor(this)
-
-fun Int.isColorVisibleOn(@ColorInt color: Int, @IntRange(from = 0L, to = 255L) delta: Int = 25,
- @IntRange(from = 0L, to = 255L) minAlpha: Int = 50): Boolean =
- if (Color.alpha(this) < minAlpha) false
- else !(Math.abs(Color.red(this) - Color.red(color)) < delta
- && Math.abs(Color.green(this) - Color.green(color)) < delta
- && Math.abs(Color.blue(this) - Color.blue(color)) < delta)
-
-
-@ColorInt
-fun Context.getDisabledColor(): Int {
- val primaryColor = resolveColor(android.R.attr.textColorPrimary)
- val disabledColor = if (primaryColor.isColorDark()) Color.BLACK else Color.WHITE
- return disabledColor.adjustAlpha(0.3f)
-}
-
-@ColorInt
-fun Int.adjustAlpha(factor: Float): Int {
- val alpha = Math.round(Color.alpha(this) * factor)
- return Color.argb(alpha, Color.red(this), Color.green(this), Color.blue(this))
-}
-
-val Int.isColorTransparent: Boolean
- get() = Color.alpha(this) != 255
-
-@ColorInt
-fun Int.withAlpha(@IntRange(from = 0L, to = 255L) alpha: Int): Int
- = Color.argb(alpha, Color.red(this), Color.green(this), Color.blue(this))
-
-@ColorInt
-fun Int.withMinAlpha(@IntRange(from = 0L, to = 255L) alpha: Int): Int
- = Color.argb(Math.max(alpha, Color.alpha(this)), Color.red(this), Color.green(this), Color.blue(this))
-
-@ColorInt
-fun Int.lighten(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int {
- val (red, green, blue) = intArrayOf(Color.red(this), Color.green(this), Color.blue(this))
- .map { (it * (1f - factor) + 255f * factor).toInt() }
- return Color.argb(Color.alpha(this), red, green, blue)
-}
-
-@ColorInt
-fun Int.darken(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int {
- val (red, green, blue) = intArrayOf(Color.red(this), Color.green(this), Color.blue(this))
- .map { (it * (1f - factor)).toInt() }
- return Color.argb(Color.alpha(this), red, green, blue)
-}
-
-@ColorInt
-fun Int.colorToBackground(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int
- = if (isColorDark()) darken(factor) else lighten(factor)
-
-@ColorInt
-fun Int.colorToForeground(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int
- = if (isColorDark()) lighten(factor) else darken(factor)
-
-@Throws(IllegalArgumentException::class)
-fun String.toColor(): Int {
- val toParse: String
- if (startsWith("#") && length == 4)
- toParse = "#${this[1]}${this[1]}${this[2]}${this[2]}${this[3]}${this[3]}"
- else
- toParse = this
- return Color.parseColor(toParse)
-}
-
-//Get ColorStateList
-fun Context.colorStateList(@ColorInt color: Int): ColorStateList {
- val disabledColor = color.adjustAlpha(0.3f)
- return ColorStateList(arrayOf(intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked),
- intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked),
- intArrayOf(-android.R.attr.state_enabled, -android.R.attr.state_checked),
- intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked)),
- intArrayOf(color.adjustAlpha(0.8f), color, disabledColor, disabledColor))
-}
-
-/*
- * Tint Helpers
- * Kotlin tint bindings that start with 'tint' so it doesn't conflict with existing methods
- * Largely based on MDTintHelper
- * https://github.com/afollestad/material-dialogs/blob/master/core/src/main/java/com/afollestad/materialdialogs/internal/MDTintHelper.java
- */
-fun RadioButton.tint(colors: ColorStateList) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- buttonTintList = colors
- } else {
- val radioDrawable = context.drawable(R.drawable.abc_btn_radio_material)
- val d = DrawableCompat.wrap(radioDrawable)
- DrawableCompat.setTintList(d, colors)
- buttonDrawable = d
- }
-}
-
-fun RadioButton.tint(@ColorInt color: Int) = tint(context.colorStateList(color))
-
-fun CheckBox.tint(colors: ColorStateList) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- buttonTintList = colors
- } else {
- val checkDrawable = context.drawable(R.drawable.abc_btn_check_material)
- val drawable = DrawableCompat.wrap(checkDrawable)
- DrawableCompat.setTintList(drawable, colors)
- buttonDrawable = drawable
- }
-}
-
-fun CheckBox.tint(@ColorInt color: Int) = tint(context.colorStateList(color))
-
-fun SeekBar.tint(@ColorInt color: Int) {
- val s1 = ColorStateList.valueOf(color)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- thumbTintList = s1
- progressTintList = s1
- } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
- val progressDrawable = DrawableCompat.wrap(progressDrawable)
- this.progressDrawable = progressDrawable
- DrawableCompat.setTintList(progressDrawable, s1)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- val thumbDrawable = DrawableCompat.wrap(thumb)
- DrawableCompat.setTintList(thumbDrawable, s1)
- thumb = thumbDrawable
- }
- } else {
- val mode: PorterDuff.Mode = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)
- PorterDuff.Mode.MULTIPLY else PorterDuff.Mode.SRC_IN
- indeterminateDrawable?.setColorFilter(color, mode)
- progressDrawable?.setColorFilter(color, mode)
- }
-}
-
-fun ProgressBar.tint(@ColorInt color: Int, skipIndeterminate: Boolean = false) {
- val sl = ColorStateList.valueOf(color)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- progressTintList = sl
- secondaryProgressTintList = sl
- if (!skipIndeterminate) indeterminateTintList = sl
- } else {
- val mode: PorterDuff.Mode = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)
- PorterDuff.Mode.MULTIPLY else PorterDuff.Mode.SRC_IN
- indeterminateDrawable?.setColorFilter(color, mode)
- progressDrawable?.setColorFilter(color, mode)
- }
-}
-
-fun Context.textColorStateList(@ColorInt color: Int): ColorStateList {
- val states = arrayOf(
- intArrayOf(-android.R.attr.state_enabled),
- intArrayOf(-android.R.attr.state_pressed, -android.R.attr.state_focused),
- intArrayOf()
- )
- val colors = intArrayOf(
- resolveColor(R.attr.colorControlNormal),
- resolveColor(R.attr.colorControlNormal),
- color
- )
- return ColorStateList(states, colors)
-}
-
-fun EditText.tint(@ColorInt color: Int) {
- val editTextColorStateList = context.textColorStateList(color)
- if (this is AppCompatEditText) {
- supportBackgroundTintList = editTextColorStateList
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- backgroundTintList = editTextColorStateList
- }
- tintCursor(color)
-}
-
-fun EditText.tintCursor(@ColorInt color: Int) {
- try {
- val fCursorDrawableRes = TextView::class.java.getDeclaredField("mCursorDrawableRes")
- fCursorDrawableRes.isAccessible = true
- val mCursorDrawableRes = fCursorDrawableRes.getInt(this)
- val fEditor = TextView::class.java.getDeclaredField("mEditor")
- fEditor.isAccessible = true
- val editor = fEditor.get(this)
- val clazz = editor.javaClass
- val fCursorDrawable = clazz.getDeclaredField("mCursorDrawable")
- fCursorDrawable.isAccessible = true
- val drawables: Array<Drawable> = Array(2, {
- val drawable = ContextCompat.getDrawable(context, mCursorDrawableRes)
- drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN)
- drawable
- })
- fCursorDrawable.set(editor, drawables)
- } catch (e: Exception) {
- e.printStackTrace()
- }
-}
-
-fun Toolbar.tint(@ColorInt color: Int, tintTitle: Boolean = true) {
- if (tintTitle) {
- setTitleTextColor(color)
- setSubtitleTextColor(color)
- }
- (0 until childCount).asSequence().forEach { (getChildAt(it) as? ImageButton)?.setColorFilter(color) }
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/Const.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/Const.kt
deleted file mode 100644
index 944caa4..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/Const.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package ca.allanwang.kau.utils
-
-/**
- * Created by Allan Wang on 2017-06-08.
- */
-const val ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android" \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt
deleted file mode 100644
index 21021e2..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt
+++ /dev/null
@@ -1,165 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.app.Activity
-import android.app.ActivityOptions
-import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
-import android.net.ConnectivityManager
-import android.net.Uri
-import android.os.Bundle
-import android.support.annotation.*
-import android.support.v4.app.ActivityOptionsCompat
-import android.support.v4.content.ContextCompat
-import android.util.TypedValue
-import android.view.View
-import android.widget.Toast
-import ca.allanwang.kau.R
-import ca.allanwang.kau.logging.KL
-import com.afollestad.materialdialogs.MaterialDialog
-
-
-/**
- * Created by Allan Wang on 2017-06-03.
- */
-fun Context.startActivity(
- clazz: Class<out Activity>,
- clearStack: Boolean = false,
- transition: Boolean = false,
- bundle: Bundle? = null,
- intentBuilder: Intent.() -> Unit = {}) {
- val intent = (Intent(this, clazz))
- if (clearStack) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
- intent.intentBuilder()
- val fullBundle = if (transition && this is Activity) ActivityOptions.makeSceneTransitionAnimation(this).toBundle() else Bundle()
- if (transition && this !is Activity) KL.d("Cannot make scene transition when context is not an instance of an Activity")
- if (bundle != null) fullBundle.putAll(bundle)
- ContextCompat.startActivity(this, intent, if (fullBundle.isEmpty) null else fullBundle)
- if (this is Activity && clearStack) finish()
-}
-
-/**
- * Bring in activity from the right
- */
-fun Context.startActivitySlideIn(clazz: Class<out Activity>, clearStack: Boolean = false, intentBuilder: Intent.() -> Unit = {}, bundleBuilder: Bundle.() -> Unit = {}) {
- val bundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.kau_slide_in_right, R.anim.kau_fade_out).toBundle()
- bundle.bundleBuilder()
- startActivity(clazz, clearStack, intentBuilder = intentBuilder, bundle = bundle)
-}
-
-/**
- * Bring in activity from behind while pushing the current activity to the right
- * This replicates the exit animation of a sliding activity, but is a forward creation
- * For the animation to work, the previous activity should not be in the stack (otherwise you wouldn't need this in the first place)
- * Consequently, the stack will be cleared by default
- */
-fun Context.startActivitySlideOut(clazz: Class<out Activity>, clearStack: Boolean = true, intentBuilder: Intent.() -> Unit = {}, bundleBuilder: Bundle.() -> Unit = {}) {
- val bundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.kau_fade_in, R.anim.kau_slide_out_right_top).toBundle()
- bundle.bundleBuilder()
- startActivity(clazz, clearStack, intentBuilder = intentBuilder, bundle = bundle)
-}
-
-fun Context.startPlayStoreLink(@StringRes packageIdRes: Int) = startPlayStoreLink(string(packageIdRes))
-
-fun Context.startPlayStoreLink(packageId: String) {
- startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$packageId")))
-}
-
-/**
- * Starts a url
- * If given a series of links, will open the first one that isn't null
- */
-fun Context.startLink(vararg url: String?) {
- val link = url.firstOrNull { !it.isNullOrBlank() } ?: return
- val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
- startActivity(browserIntent)
-}
-
-//Toast helpers
-fun Context.toast(@StringRes id: Int, duration: Int = Toast.LENGTH_LONG) = toast(this.string(id), duration)
-
-fun Context.toast(text: String, duration: Int = Toast.LENGTH_LONG) {
- Toast.makeText(this, text, duration).show()
-}
-
-//Resource retrievers
-fun Context.string(@StringRes id: Int): String = getString(id)
-
-fun Context.string(@StringRes id: Int, fallback: String?): String? = if (id > 0) string(id) else fallback
-fun Context.string(holder: StringHolder?): String? = holder?.getString(this)
-fun Context.color(@ColorRes id: Int): Int = ContextCompat.getColor(this, id)
-fun Context.integer(@IntegerRes id: Int): Int = resources.getInteger(id)
-fun Context.dimen(@DimenRes id: Int): Float = resources.getDimension(id)
-fun Context.dimenPixelSize(@DimenRes id: Int): Int = resources.getDimensionPixelSize(id)
-fun Context.drawable(@DrawableRes id: Int): Drawable = ContextCompat.getDrawable(this, id)
-fun Context.drawable(@DrawableRes id: Int, fallback: Drawable?): Drawable? = if (id > 0) drawable(id) else fallback
-
-//Attr retrievers
-fun Context.resolveColor(@AttrRes attr: Int, fallback: Int = 0): Int {
- val a = theme.obtainStyledAttributes(intArrayOf(attr))
- try {
- return a.getColor(0, fallback)
- } finally {
- a.recycle()
- }
-}
-
-fun Context.resolveDrawable(@AttrRes attr: Int): Drawable? {
- val a = theme.obtainStyledAttributes(intArrayOf(attr))
- try {
- return a.getDrawable(0)
- } finally {
- a.recycle()
- }
-}
-
-fun Context.resolveBoolean(@AttrRes attr: Int, fallback: Boolean = false): Boolean {
- val a = theme.obtainStyledAttributes(intArrayOf(attr))
- try {
- return a.getBoolean(0, fallback)
- } finally {
- a.recycle()
- }
-}
-
-fun Context.resolveString(@AttrRes attr: Int, fallback: String = ""): String {
- val v = TypedValue()
- return if (theme.resolveAttribute(attr, v, true)) v.string.toString() else fallback
-}
-
-/**
- * Wrapper function for the MaterialDialog adapterBuilder
- * There is no need to call build() or show() as those are done by default
- */
-inline fun Context.materialDialog(action: MaterialDialog.Builder.() -> Unit): MaterialDialog {
- val builder = MaterialDialog.Builder(this)
- builder.action()
- return builder.show()
-}
-
-inline val Context.isNetworkAvailable: Boolean
- get() {
- val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
- val activeNetworkInfo = connectivityManager.activeNetworkInfo
- return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting
- }
-
-fun Context.getDip(value: Float): Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics)
-
-inline val Context.isRtl: Boolean
- get() = resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
-
-/**
- * Determine if the navigation bar will be on the bottom of the screen, based on logic in
- * PhoneWindowManager.
- */
-inline val Context.isNavBarOnBottom: Boolean
- get() {
- val cfg = resources.configuration
- val dm = resources.displayMetrics
- val canMove = dm.widthPixels != dm.heightPixels && cfg.smallestScreenWidthDp < 600
- return !canMove || dm.widthPixels < dm.heightPixels
- }
-
-fun Context.hasPermission(permissions: String) = !buildIsMarshmallowAndUp || ContextCompat.checkSelfPermission(this, permissions) == PackageManager.PERMISSION_GRANTED \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/Either.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/Either.kt
deleted file mode 100644
index dab5810..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/Either.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package ca.allanwang.kau.utils
-
-/**
- * Created by Allan Wang on 2017-06-17.
- *
- * Courtesy of adelnizamutdinov
- *
- * https://github.com/adelnizamutdinov/kotlin-either
- */
-@Suppress("unused")
-sealed class Either<out L, out R>
-
-data class Left<out T>(val value: T) : Either<T, Nothing>()
-data class Right<out T>(val value: T) : Either<Nothing, T>()
-
-inline fun <L, R, T> Either<L, R>.fold(left: (L) -> T, right: (R) -> T): T =
- when (this) {
- is Left -> left(value)
- is Right -> right(value)
- }
-
-inline fun <L, R, T> Either<L, R>.flatMap(f: (R) -> Either<L, T>): Either<L, T> =
- fold({ this as Left }, f)
-
-inline fun <L, R, T> Either<L, R>.map(f: (R) -> T): Either<L, T> =
- flatMap { Right(f(it)) }
-
-val <T> Either<T, *>.isLeft: Boolean
- get() = this is Left<T>
-
-val <T> Either<*, T>.isRight: Boolean
- get() = this is Right<T> \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/FontUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/FontUtils.kt
deleted file mode 100644
index 3fc509d..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/FontUtils.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.content.Context
-import android.graphics.Typeface
-
-/**
- * Created by Allan Wang on 2017-06-28.
- */
-object FontUtils {
-
- val sTypefaceCache: MutableMap<String, Typeface> = mutableMapOf()
-
- fun get(context: Context, font: String): Typeface {
- synchronized(sTypefaceCache) {
- if (!sTypefaceCache.containsKey(font)) {
- val tf = Typeface.createFromAsset(
- context.applicationContext.assets, "fonts/$font.ttf")
- sTypefaceCache.put(font, tf)
- }
- return sTypefaceCache.get(font) ?: throw IllegalArgumentException("Font error; typeface does not exist at assets/fonts$font.ttf")
- }
- }
-
- fun getName(typeface: Typeface): String? = sTypefaceCache.entries.firstOrNull { it.value == typeface }?.key
-
-}
-
-fun Context.getFont(font: String) = FontUtils.get(this, font)
-fun Context.getFontName(typeface: Typeface) = FontUtils.getName(typeface) \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt
deleted file mode 100644
index acc71f2..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.support.v4.app.Fragment
-import org.jetbrains.anko.bundleOf
-
-/**
- * Created by Allan Wang on 2017-07-02.
- */
-fun <T : Fragment> T.withArguments(vararg params: Pair<String, Any>): T {
- arguments = bundleOf(*params)
- return this
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/IIconUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/IIconUtils.kt
deleted file mode 100644
index 03a1605..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/IIconUtils.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.content.Context
-import android.content.res.ColorStateList
-import android.graphics.Color
-import android.graphics.drawable.Drawable
-import android.support.annotation.ColorInt
-import com.mikepenz.iconics.IconicsDrawable
-import com.mikepenz.iconics.typeface.IIcon
-
-/**
- * Created by Allan Wang on 2017-05-29.
- */
-@KauUtils fun IIcon.toDrawable(c: Context, sizeDp: Int = 24, @ColorInt color: Int = Color.WHITE, builder: IconicsDrawable.() -> Unit = {}): Drawable {
- val state = ColorStateList.valueOf(color)
- val icon = IconicsDrawable(c).icon(this).sizeDp(sizeDp)
- icon.setTintList(state)
- icon.builder()
- return icon
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt
deleted file mode 100644
index 247bbc7..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt
+++ /dev/null
@@ -1,166 +0,0 @@
-package ca.allanwang.kau.utils
-
-/**
- * Created by Allan Wang on 2017-05-29.
- *
- * Courtesy of Jake Wharton
- *
- * https://github.com/JakeWharton/kotterknife/blob/master/src/main/kotlin/kotterknife/ButterKnife.kt
- */
-import android.app.Activity
-import android.app.Dialog
-import android.app.DialogFragment
-import android.app.Fragment
-import android.support.v7.widget.RecyclerView.ViewHolder
-import android.view.View
-import kotlin.properties.ReadOnlyProperty
-import kotlin.reflect.KProperty
-import android.support.v4.app.DialogFragment as SupportDialogFragment
-import android.support.v4.app.Fragment as SupportFragment
-
-fun <V : View> View.bindView(id: Int)
- : ReadOnlyProperty<View, V> = required(id, viewFinder)
-
-fun <V : View> Activity.bindView(id: Int)
- : ReadOnlyProperty<Activity, V> = required(id, viewFinder)
-
-fun <V : View> Dialog.bindView(id: Int)
- : ReadOnlyProperty<Dialog, V> = required(id, viewFinder)
-
-fun <V : View> DialogFragment.bindView(id: Int)
- : ReadOnlyProperty<DialogFragment, V> = required(id, viewFinder)
-
-fun <V : View> android.support.v4.app.DialogFragment.bindView(id: Int)
- : ReadOnlyProperty<android.support.v4.app.DialogFragment, V> = required(id, viewFinder)
-
-fun <V : View> Fragment.bindView(id: Int)
- : ReadOnlyProperty<Fragment, V> = required(id, viewFinder)
-
-fun <V : View> android.support.v4.app.Fragment.bindView(id: Int)
- : ReadOnlyProperty<android.support.v4.app.Fragment, V> = required(id, viewFinder)
-
-fun <V : View> ViewHolder.bindView(id: Int)
- : ReadOnlyProperty<ViewHolder, V> = required(id, viewFinder)
-
-fun <V : View> View.bindOptionalView(id: Int)
- : ReadOnlyProperty<View, V?> = optional(id, viewFinder)
-
-fun <V : View> Activity.bindOptionalView(id: Int)
- : ReadOnlyProperty<Activity, V?> = optional(id, viewFinder)
-
-fun <V : View> Dialog.bindOptionalView(id: Int)
- : ReadOnlyProperty<Dialog, V?> = optional(id, viewFinder)
-
-fun <V : View> DialogFragment.bindOptionalView(id: Int)
- : ReadOnlyProperty<DialogFragment, V?> = optional(id, viewFinder)
-
-fun <V : View> android.support.v4.app.DialogFragment.bindOptionalView(id: Int)
- : ReadOnlyProperty<android.support.v4.app.DialogFragment, V?> = optional(id, viewFinder)
-
-fun <V : View> Fragment.bindOptionalView(id: Int)
- : ReadOnlyProperty<Fragment, V?> = optional(id, viewFinder)
-
-fun <V : View> android.support.v4.app.Fragment.bindOptionalView(id: Int)
- : ReadOnlyProperty<android.support.v4.app.Fragment, V?> = optional(id, viewFinder)
-
-fun <V : View> ViewHolder.bindOptionalView(id: Int)
- : ReadOnlyProperty<ViewHolder, V?> = optional(id, viewFinder)
-
-fun <V : View> View.bindViews(vararg ids: Int)
- : ReadOnlyProperty<View, List<V>> = required(ids, viewFinder)
-
-fun <V : View> Activity.bindViews(vararg ids: Int)
- : ReadOnlyProperty<Activity, List<V>> = required(ids, viewFinder)
-
-fun <V : View> Dialog.bindViews(vararg ids: Int)
- : ReadOnlyProperty<Dialog, List<V>> = required(ids, viewFinder)
-
-fun <V : View> DialogFragment.bindViews(vararg ids: Int)
- : ReadOnlyProperty<DialogFragment, List<V>> = required(ids, viewFinder)
-
-fun <V : View> android.support.v4.app.DialogFragment.bindViews(vararg ids: Int)
- : ReadOnlyProperty<android.support.v4.app.DialogFragment, List<V>> = required(ids, viewFinder)
-
-fun <V : View> Fragment.bindViews(vararg ids: Int)
- : ReadOnlyProperty<Fragment, List<V>> = required(ids, viewFinder)
-
-fun <V : View> android.support.v4.app.Fragment.bindViews(vararg ids: Int)
- : ReadOnlyProperty<android.support.v4.app.Fragment, List<V>> = required(ids, viewFinder)
-
-fun <V : View> ViewHolder.bindViews(vararg ids: Int)
- : ReadOnlyProperty<ViewHolder, List<V>> = required(ids, viewFinder)
-
-fun <V : View> View.bindOptionalViews(vararg ids: Int)
- : ReadOnlyProperty<View, List<V>> = optional(ids, viewFinder)
-
-fun <V : View> Activity.bindOptionalViews(vararg ids: Int)
- : ReadOnlyProperty<Activity, List<V>> = optional(ids, viewFinder)
-
-fun <V : View> Dialog.bindOptionalViews(vararg ids: Int)
- : ReadOnlyProperty<Dialog, List<V>> = optional(ids, viewFinder)
-
-fun <V : View> DialogFragment.bindOptionalViews(vararg ids: Int)
- : ReadOnlyProperty<DialogFragment, List<V>> = optional(ids, viewFinder)
-
-fun <V : View> android.support.v4.app.DialogFragment.bindOptionalViews(vararg ids: Int)
- : ReadOnlyProperty<android.support.v4.app.DialogFragment, List<V>> = optional(ids, viewFinder)
-
-fun <V : View> Fragment.bindOptionalViews(vararg ids: Int)
- : ReadOnlyProperty<Fragment, List<V>> = optional(ids, viewFinder)
-
-fun <V : View> android.support.v4.app.Fragment.bindOptionalViews(vararg ids: Int)
- : ReadOnlyProperty<android.support.v4.app.Fragment, List<V>> = optional(ids, viewFinder)
-
-fun <V : View> ViewHolder.bindOptionalViews(vararg ids: Int)
- : ReadOnlyProperty<ViewHolder, List<V>> = optional(ids, viewFinder)
-
-private val View.viewFinder: View.(Int) -> View?
- get() = { findViewById(it) }
-private val Activity.viewFinder: Activity.(Int) -> View?
- get() = { findViewById(it) }
-private val Dialog.viewFinder: Dialog.(Int) -> View?
- get() = { findViewById(it) }
-private val DialogFragment.viewFinder: DialogFragment.(Int) -> View?
- get() = { dialog.findViewById(it) }
-private val android.support.v4.app.DialogFragment.viewFinder: android.support.v4.app.DialogFragment.(Int) -> View?
- get() = { dialog.findViewById(it) }
-private val Fragment.viewFinder: Fragment.(Int) -> View?
- get() = { view.findViewById(it) }
-private val android.support.v4.app.Fragment.viewFinder: android.support.v4.app.Fragment.(Int) -> View?
- get() = { view!!.findViewById(it) }
-private val ViewHolder.viewFinder: ViewHolder.(Int) -> View?
- get() = { itemView.findViewById(it) }
-
-private fun viewNotFound(id: Int, desc: KProperty<*>): Nothing =
- throw IllegalStateException("View ID $id for '${desc.name}' not found.")
-
-@Suppress("UNCHECKED_CAST")
-private fun <T, V : View> required(id: Int, finder: T.(Int) -> View?)
- = Lazy { t: T, desc -> (t.finder(id) as V?)?.apply { } ?: viewNotFound(id, desc) }
-
-@Suppress("UNCHECKED_CAST")
-private fun <T, V : View> optional(id: Int, finder: T.(Int) -> View?)
- = Lazy { t: T, _ -> t.finder(id) as V? }
-
-@Suppress("UNCHECKED_CAST")
-private fun <T, V : View> required(ids: IntArray, finder: T.(Int) -> View?)
- = Lazy { t: T, desc -> ids.map { t.finder(it) as V? ?: viewNotFound(it, desc) } }
-
-@Suppress("UNCHECKED_CAST")
-private fun <T, V : View> optional(ids: IntArray, finder: T.(Int) -> View?)
- = Lazy { t: T, _ -> ids.map { t.finder(it) as V? }.filterNotNull() }
-
-// Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it
-private class Lazy<T, V>(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> {
- private object EMPTY
-
- private var value: Any? = EMPTY
-
- override fun getValue(thisRef: T, property: KProperty<*>): V {
- if (value == EMPTY) {
- value = initializer(thisRef, property)
- }
- @Suppress("UNCHECKED_CAST")
- return value as V
- }
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt
deleted file mode 100644
index 837c209..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.Build
-import android.support.annotation.RequiresApi
-
-/**
- * Created by Allan Wang on 2017-06-23.
- */
-
-/**
- * Checks if a given package is installed
- * @param packageName packageId
- * @return true if installed with activity, false otherwise
- */
-@KauUtils fun Context.isAppInstalled(packageName: String): Boolean {
- val pm = packageManager
- var installed: Boolean
- try {
- pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES)
- installed = true
- } catch (e: PackageManager.NameNotFoundException) {
- installed = false
- }
- return installed
-}
-
-val buildIsLollipopAndUp: Boolean
- get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
-
-val buildIsMarshmallowAndUp: Boolean
- get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
-
-val buildIsNougatAndUp: Boolean
- get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
-
-const val INSTALLER_GOOGLE_PLAY_VENDING = "com.android.vending"
-const val INSTALLER_GOOGLE_PLAY_FEEDBACK = "com.google.android.feedback"
-
-val Context.installerPackageName: String?
- get() = packageManager.getInstallerPackageName(packageName)
-
-val Context.isFromGooglePlay: Boolean
- get() {
- val installer = installerPackageName
- return arrayOf(INSTALLER_GOOGLE_PLAY_FEEDBACK, INSTALLER_GOOGLE_PLAY_VENDING).any { it == installer }
- } \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/StringHolder.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/StringHolder.kt
deleted file mode 100644
index e70a2d1..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/StringHolder.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.content.Context
-import android.support.annotation.StringRes
-
-/**
- * Created by Allan Wang on 2017-06-08.
- */
-class StringHolder {
- var text: String? = null
- var textRes: Int = 0
-
- constructor(@StringRes textRes: Int) {
- this.textRes = textRes
- }
-
- constructor(text: String) {
- this.text = text
- }
-
- fun getString(context: Context) = context.string(textRes, text)
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/TransitionUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/TransitionUtils.kt
deleted file mode 100644
index 9e668d0..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/TransitionUtils.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.support.transition.Transition
-import android.support.transition.TransitionSet
-
-/**
- * Created by Allan Wang on 2017-06-24.
- */
-class TransitionEndListener(val onEnd: (transition: Transition) -> Unit) : Transition.TransitionListener {
- override fun onTransitionEnd(transition: Transition) = onEnd(transition)
- override fun onTransitionResume(transition: Transition) {}
- override fun onTransitionPause(transition: Transition) {}
- override fun onTransitionCancel(transition: Transition) {}
- override fun onTransitionStart(transition: Transition) {}
-}
-
-@KauUtils fun TransitionSet.addEndListener(onEnd: (transition: Transition) -> Unit) {
- addListener(TransitionEndListener(onEnd))
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt
deleted file mode 100644
index 84794f9..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.content.Context
-import android.content.res.Resources
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.drawable.BitmapDrawable
-import android.graphics.drawable.Drawable
-import android.os.Handler
-import android.os.Looper
-import android.support.annotation.IntRange
-import ca.allanwang.kau.R
-import ca.allanwang.kau.logging.KL
-import java.math.RoundingMode
-import java.text.DecimalFormat
-
-
-/**
- * Created by Allan Wang on 2017-05-28.
- */
-
-/**
- * Markers to isolate respective extension @KauUtils functions to their extended class
- * Avoids having a whole bunch of methods for nested calls
- */
-@DslMarker
-annotation class KauUtils
-
-@KauUtils val Int.dpToPx: Int
- get() = (this * Resources.getSystem().displayMetrics.density).toInt()
-
-@KauUtils val Int.pxToDp: Int
- get() = (this / Resources.getSystem().displayMetrics.density).toInt()
-
-/**
- * Log whether current state is in the main thread
- */
-@KauUtils fun checkThread(id: Int) {
- val status = if (Looper.myLooper() == Looper.getMainLooper()) "is" else "is not"
- KL.d("$id $status in the main thread")
-}
-
-/**
- * Converts minute value to string
- * Whole hours and days will be converted as such, otherwise it will default to x minutes
- */
-@KauUtils fun Context.minuteToText(minutes: Long): String = with(minutes) {
- if (this < 0L) string(R.string.kau_none)
- else if (this == 60L) string(R.string.kau_one_hour)
- else if (this == 1440L) string(R.string.kau_one_day)
- else if (this % 1440L == 0L) String.format(string(R.string.kau_x_days), this / 1440L)
- else if (this % 60L == 0L) String.format(string(R.string.kau_x_hours), this / 60L)
- else String.format(string(R.string.kau_x_minutes), this)
-}
-
-@KauUtils fun Number.round(@IntRange(from = 1L) decimalCount: Int): String {
- val expression = StringBuilder().append("#.")
- (1..decimalCount).forEach { expression.append("#") }
- val formatter = DecimalFormat(expression.toString())
- formatter.roundingMode = RoundingMode.HALF_UP
- return formatter.format(this)
-}
-
-/**
- * Extracts the bitmap of a drawable, and applies a scale if given
- * For solid colors, a 1 x 1 pixel will be generated
- */
-@KauUtils fun Drawable.toBitmap(scaling: Float = 1f, config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap {
- if (this is BitmapDrawable && bitmap != null) {
- if (scaling == 1f) return bitmap
- val width = (bitmap.width * scaling).toInt()
- val height = (bitmap.height * scaling).toInt()
- return Bitmap.createScaledBitmap(bitmap, width, height, false)
- }
- val bitmap = if (intrinsicWidth <= 0 || intrinsicHeight <= 0)
- Bitmap.createBitmap(1, 1, config)
- else
- Bitmap.createBitmap((intrinsicWidth * scaling).toInt(), (intrinsicHeight * scaling).toInt(), config)
- val canvas = Canvas(bitmap)
- setBounds(0, 0, canvas.width, canvas.height)
- draw(canvas)
- return bitmap
-}
-
-/**
- * Use block for autocloseables
- */
-inline fun <T : AutoCloseable, R> T.use(block: (T) -> R): R {
- var closed = false
- try {
- return block(this)
- } catch (e: Exception) {
- closed = true
- try {
- close()
- } catch (closeException: Exception) {
- e.addSuppressed(closeException)
- }
- throw e
- } finally {
- if (!closed) {
- close()
- }
- }
-}
-
-fun postDelayed(delay: Long, action: () -> Unit) {
- Handler().postDelayed(action, delay)
-}
-
-class KauException(message: String) : RuntimeException(message) \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt
deleted file mode 100644
index b4752a5..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-package ca.allanwang.kau.utils
-
-import android.content.Context
-import android.graphics.Color
-import android.graphics.Outline
-import android.graphics.Rect
-import android.support.annotation.ColorInt
-import android.support.annotation.StringRes
-import android.support.annotation.TransitionRes
-import android.support.design.widget.Snackbar
-import android.support.transition.AutoTransition
-import android.support.transition.Transition
-import android.support.transition.TransitionInflater
-import android.support.transition.TransitionManager
-import android.support.v7.widget.LinearLayoutManager
-import android.support.v7.widget.RecyclerView
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewOutlineProvider
-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 com.mikepenz.iconics.IconicsDrawable
-import com.mikepenz.iconics.typeface.IIcon
-
-
-/**
- * Created by Allan Wang on 2017-05-31.
- */
-@KauUtils fun <T : View> T.visible(): T {
- visibility = View.VISIBLE
- return this
-}
-
-@KauUtils fun <T : View> T.invisible(): T {
- visibility = View.INVISIBLE
- return this
-}
-
-@KauUtils fun <T : View> T.gone(): T {
- visibility = View.GONE
- return this
-}
-
-@KauUtils fun View.isVisible(): Boolean = visibility == View.VISIBLE
-@KauUtils fun View.isInvisible(): Boolean = visibility == View.INVISIBLE
-@KauUtils fun View.isGone(): Boolean = visibility == View.GONE
-
-fun View.snackbar(text: String, duration: Int = Snackbar.LENGTH_LONG, builder: Snackbar.() -> Unit = {}): Snackbar {
- val snackbar = Snackbar.make(this, text, duration)
- snackbar.builder()
- snackbar.show()
- return snackbar
-}
-
-fun View.snackbar(@StringRes textId: Int, duration: Int = Snackbar.LENGTH_LONG, builder: Snackbar.() -> Unit = {})
- = snackbar(context.string(textId), duration, builder)
-
-@KauUtils fun TextView.setTextIfValid(@StringRes id: Int) {
- if (id > 0) text = context.string(id)
-}
-
-@KauUtils fun ImageView.setIcon(icon: IIcon?, sizeDp: Int = 24, @ColorInt color: Int = Color.WHITE, builder: IconicsDrawable.() -> Unit = {}) {
- if (icon == null) return
- setImageDrawable(icon.toDrawable(context, sizeDp = sizeDp, color = color, builder = builder))
-}
-
-@KauUtils fun View.hideKeyboard() {
- clearFocus()
- (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(windowToken, 0)
-}
-
-@KauUtils fun View.showKeyboard() {
- requestFocus()
- (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
-}
-
-@KauUtils fun ViewGroup.transitionAuto(builder: AutoTransition.() -> Unit = {}) {
- val transition = AutoTransition()
- transition.builder()
- TransitionManager.beginDelayedTransition(this, transition)
-}
-
-@KauUtils fun ViewGroup.transitionDelayed(@TransitionRes id: Int, builder: Transition.() -> Unit = {}) {
- val transition = TransitionInflater.from(context).inflateTransition(id)
- transition.builder()
- TransitionManager.beginDelayedTransition(this, transition)
-}
-
-@KauUtils fun View.setRippleBackground(@ColorInt foregroundColor: Int, @ColorInt backgroundColor: Int) {
- background = createSimpleRippleDrawable(foregroundColor, backgroundColor)
-}
-
-@KauUtils val View.parentViewGroup: ViewGroup
- get() = parent as ViewGroup
-
-@KauUtils val View.parentVisibleHeight: Int
- get() {
- val r = Rect()
- parentViewGroup.getWindowVisibleDisplayFrame(r)
- return r.height()
- }
-
-val CIRCULAR_OUTLINE: ViewOutlineProvider = object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- KL.d("CIRCULAR OUTLINE")
- outline.setOval(view.paddingLeft,
- view.paddingTop,
- view.width - view.paddingRight,
- view.height - view.paddingBottom)
- }
-}
-
-/**
- * Generates a recycler view with match parent and a linearlayoutmanager, since it's so commonly used
- */
-fun Context.fullLinearRecycler(rvAdapter: RecyclerView.Adapter<*>? = null, configs: RecyclerView.() -> Unit = {}): RecyclerView {
- return RecyclerView(this).apply {
- layoutManager = LinearLayoutManager(this@fullLinearRecycler)
- layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.MATCH_PARENT)
- if (rvAdapter != null) adapter = rvAdapter
- configs()
- }
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/views/BoundedCardView.kt b/library/src/main/kotlin/ca/allanwang/kau/views/BoundedCardView.kt
deleted file mode 100644
index 0cb65d0..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/views/CutoutView.kt b/library/src/main/kotlin/ca/allanwang/kau/views/CutoutView.kt
deleted file mode 100644
index 023bdb4..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt b/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt
deleted file mode 100644
index 805fb21..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/views/RippleCanvas.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-package ca.allanwang.kau.views
-
-import android.animation.ArgbEvaluator
-import android.animation.ValueAnimator
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.util.AttributeSet
-import android.view.View
-import ca.allanwang.kau.utils.adjustAlpha
-
-/**
- * Created by Allan Wang on 2016-11-17.
- *
- *
- * Canvas drawn ripples that keep the previous color
- * Extends to view dimensions
- * Supports multiple ripples from varying locations
- */
-class RippleCanvas @JvmOverloads constructor(
- context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
-) : View(context, attrs, defStyleAttr) {
- private val paint: Paint = Paint()
- private var baseColor = Color.TRANSPARENT
- private val ripples: MutableList<Ripple> = mutableListOf()
-
- init {
- paint.isAntiAlias = true
- paint.style = Paint.Style.FILL
- }
-
- /**
- * Drawing the ripples involves having access to the next layer if it exists,
- * and using its values to decide on the current color.
- * If the next layer requests a fade, we will adjust the alpha of our current layer before drawing.
- * Otherwise we will just draw the color as intended
- */
- override fun onDraw(canvas: Canvas) {
- val itr = ripples.listIterator()
- if (!itr.hasNext()) return canvas.drawColor(baseColor)
- var next = itr.next()
- canvas.drawColor(colorToDraw(baseColor, next.fade, next.radius, next.maxRadius))
- var last = false
- while (!last) {
- val current = next
- if (itr.hasNext()) next = itr.next()
- else last = true
- //We may fade any layer except for the last one
- paint.color = colorToDraw(current.color, next.fade && !last, next.radius, next.maxRadius)
- canvas.drawCircle(current.x, current.y, current.radius, paint)
- if (current.radius == current.maxRadius) {
- if (!last) {
- itr.previous()
- itr.remove()
- itr.next()
- } else {
- itr.remove()
- }
- baseColor = current.color
- }
- }
- }
-
- /**
- * Given our current color and next layer's radius & max,
- * we will decide on the alpha of our current layer
- */
- internal fun colorToDraw(color: Int, fade: Boolean, current: Float, goal: Float): Int {
- if (!fade || (current / goal <= FADE_PIVOT)) return color
- val factor = (goal - current) / (goal - FADE_PIVOT * goal)
- return color.adjustAlpha(factor)
- }
-
- /**
- * Creates a ripple effect from the given starting values
- * [fade] will gradually transition previous ripples to a transparent color so the resulting background is what we want
- * this is typically only necessary if the ripple color has transparency
- */
- fun ripple(color: Int, startX: Float = 0f, startY: Float = 0f, duration: Long = 600L, fade: Boolean = Color.alpha(color) != 255) {
- val w = width.toFloat()
- val h = height.toFloat()
- val x = when (startX) {
- MIDDLE -> w / 2
- END -> w
- else -> startX
- }
- val y = when (startY) {
- MIDDLE -> h / 2
- END -> h
- else -> startY
- }
- val maxRadius = Math.hypot(Math.max(x, w - x).toDouble(), Math.max(y, h - y).toDouble()).toFloat()
- val ripple = Ripple(color, x, y, 0f, maxRadius, fade)
- ripples.add(ripple)
- val animator = ValueAnimator.ofFloat(0f, maxRadius)
- animator.duration = duration
- animator.addUpdateListener { animation ->
- ripple.radius = animation.animatedValue as Float
- invalidate()
- }
- animator.start()
- }
-
- /**
- * Sets a color directly; clears ripple queue if it exists
- */
- fun set(color: Int) {
- baseColor = color
- ripples.clear()
- invalidate()
- }
-
- /**
- * Sets a color directly but with a transition
- */
- fun fade(color: Int, duration: Long = 300L) {
- ripples.clear()
- val animator = ValueAnimator.ofObject(ArgbEvaluator(), baseColor, color)
- animator.duration = duration
- animator.addUpdateListener { animation ->
- baseColor = animation.animatedValue as Int
- invalidate()
- }
- animator.start()
- }
-
- internal class Ripple(val color: Int,
- val x: Float,
- val y: Float,
- var radius: Float,
- val maxRadius: Float,
- val fade: Boolean)
-
- companion object {
- const val MIDDLE = -1.0f
- const val END = -2.0f
- const val FADE_PIVOT = 0.5f
- }
-}
diff --git a/library/src/main/kotlin/ca/allanwang/kau/views/SimpleRippleDrawable.kt b/library/src/main/kotlin/ca/allanwang/kau/views/SimpleRippleDrawable.kt
deleted file mode 100644
index df842f6..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/views/SimpleRippleDrawable.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package ca.allanwang.kau.views
-
-import android.content.res.ColorStateList
-import android.graphics.drawable.ColorDrawable
-import android.graphics.drawable.RippleDrawable
-import android.support.annotation.ColorInt
-import ca.allanwang.kau.utils.adjustAlpha
-
-/**
- * Created by Allan Wang on 2017-06-24.
- *
- * Tries to mimic a standard ripple, given the foreground and background colors
- */
-fun createSimpleRippleDrawable(@ColorInt foregroundColor: Int, @ColorInt backgroundColor: Int): RippleDrawable {
- val states = ColorStateList(arrayOf(intArrayOf()), intArrayOf(foregroundColor))
- val content = ColorDrawable(backgroundColor)
- val mask = ColorDrawable(foregroundColor.adjustAlpha(0.16f))
- return RippleDrawable(states, content, mask)
-} \ No newline at end of file
diff --git a/library/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt b/library/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt
deleted file mode 100644
index 38c99c3..0000000
--- a/library/src/main/kotlin/ca/allanwang/kau/widgets/ElasticDragDismissFrameLayout.kt
+++ /dev/null
@@ -1,234 +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
- private var dragDismissDistance = Float.MAX_VALUE
- private var dragDismissFraction = -1f
- private var dragDismissScale = 1f
- private var shouldScale = false
- private 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)
- shouldScale = dragDismissScale != 1f
- 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/library/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java b/library/src/main/kotlin/ca/allanwang/kau/widgets/InkPageIndicator.java
deleted file mode 100644
index 78e915d..0000000
--- a/library/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/library/src/main/kotlin/ca/allanwang/kau/widgets/TextSlider.kt b/library/src/main/kotlin/ca/allanwang/kau/widgets/TextSlider.kt
deleted file mode 100644
index 528dabc..0000000
--- a/library/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