aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml14
-rw-r--r--README.md15
-rw-r--r--about/README.md9
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt184
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt198
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/CollapsibleTextView.kt33
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt12
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/FaqIItem.kt86
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt34
-rw-r--r--about/src/main/res/layout/kau_iitem_faq.xml37
-rw-r--r--about/src/main/res/values/strings.xml1
-rw-r--r--about/src/main/res/values/strings_about.xml2
-rw-r--r--adapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt36
-rw-r--r--core/README.md10
-rw-r--r--core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt28
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt105
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt7
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt9
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt75
-rw-r--r--core/src/main/res/values/ids.xml2
-rw-r--r--docs/Changelog.md8
-rw-r--r--gradle.properties4
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt13
-rw-r--r--sample/src/main/kotlin/ca/allanwang/kau/sample/AboutActivity.kt2
-rw-r--r--sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt1
-rw-r--r--sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt24
-rw-r--r--sample/src/main/res/menu/menu_main.xml16
-rw-r--r--sample/src/main/res/xml/kau_changelog.xml13
-rw-r--r--sample/src/main/res/xml/kau_faq.xml14
29 files changed, 733 insertions, 259 deletions
diff --git a/.travis.yml b/.travis.yml
index 6b134ff..c2b02bf 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,9 @@ jdk:
android:
components:
- tools
+# - android-21
- platform-tools
+ - tools
- build-tools-26.0.1
- android-26
- extra-android-support
@@ -12,7 +14,13 @@ android:
- extra-google-m2repository
licenses:
- ".+"
+#before_script:
+#- echo no | android create avd --force -n test -t android-21 --abi armeabi-v7a
+#- emulator -avd test -no-audio -no-window &
+#- android-wait-for-emulator
+#- adb shell input keyevent 82 &
script:
+- chmod +x gradlew
- ./gradlew --quiet androidGitVersion
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then ./gradlew lintRelease publishRelease; else ./gradlew lintRelease test; fi
branches:
@@ -25,12 +33,12 @@ notifications:
- pitchedapps:G5OB9U1vsDxy9mxt0Nt6gbFu#kau
on_success: always
on_failure: always
-sudo: false
cache:
directories:
- "$HOME/.m2"
-before_script:
-- chmod +x gradlew
+ - $HOME/.gradle/caches/
+ - $HOME/.gradle/wrapper/
+ - $HOME/.android/build-cache
before_install:
- openssl aes-256-cbc -K $encrypted_12e8842891a3_key -iv $encrypted_12e8842891a3_iv
-in files/kau.tar.enc -out kau.tar -d
diff --git a/README.md b/README.md
index 9641ae2..54c3f7e 100644
--- a/README.md
+++ b/README.md
@@ -43,8 +43,8 @@ dependencies {
compile "ca.allanwang.kau:about:$KAU"
compile "ca.allanwang.kau:colorpicker:$KAU"
- compile "ca.allanwang.kau:mediapicker:$KAU"
compile "ca.allanwang.kau:kpref-activity:$KAU"
+ compile "ca.allanwang.kau:mediapicker:$KAU"
compile "ca.allanwang.kau:searchview:$KAU"
}
@@ -53,9 +53,10 @@ dependencies {
-----------
# Submodules
-(linked to their respective Docs)
+> linked to their respective Docs
+> included dependencies are only those with exposed APIs. See [new dependency configurations](https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#new_configurations)
-## [Core](core#readme)****
+## [Core](core#readme)
* Collection of extension functions and small helper methods applicable in almost any application.
* Notable features: KPrefs, Changelog XML, Kotterknife, Ripple Canvas, Delegates, Lazy Resettables, Extensions, Email Builder
* Includes
@@ -89,16 +90,16 @@ dependencies {
* Includes `:core`,
[`Material Dialogs (commons)`](https://github.com/afollestad/material-dialogs)
+## [Kpref Activity](kpref-activity#readme)
+* Fully programmatic implementation of a Preference Activity, backed by RecyclerViews
+* Includes `:core-ui`, `:adapter`, `colorpicker`
+
## [Media Picker](mediapicker#readme)
* Fully functional image and video pickers, both as an overlay and as a requested activity.
* Includes `:core-ui`,
[`Glide`](https://github.com/bumptech/glide),
[`Blurry`](https://github.com/wasabeef/Blurry)
-## [Kpref Activity](kpref-activity#readme)
-* Fully programmatic implementation of a Preference Activity, backed by RecyclerViews
-* Includes `:core-ui`, `:adapter`, `colorpicker`
-
## [SearchView](searchview#readme)
* Material searchview with kotlin bindings
* Includes `:core-ui`, `:adapter`
diff --git a/about/README.md b/about/README.md
index e7012b5..77a329d 100644
--- a/about/README.md
+++ b/about/README.md
@@ -11,9 +11,12 @@ and adds on the power of [About Libraries](https://github.com/mikepenz/AboutLibr
This activity can be easily added by extending `AboutActivityBase`.
Everything is already prepared, but you can modify the theme or other components through the config DSL or through the open functions.
-There are also numerous iitem cards already prepared (in this submodule and from `:adapter`)if you wish to add that in your main view.
-You may also easily launch the activity through the simple binder:
+If you wish to add custom panels, you will need to implement `AboutPanelContract`.
+The most common usage is with a recyclerview, so there is a simplified class `AboutPanelRecycler` that you may extend as well.
+Note that the viewpager by default will keep all panels in memory, so it's best to not have too many pages with expensive interactions.
+
+You may easily launch the activity through the binder:
```
Activity.kauLaunchAbout(YourClass::class.java)
```
@@ -22,7 +25,7 @@ Be sure to include the activity in your Manifest and have it extend `Kau.About`,
## Proguard
-Without auto detect, KAU about will retain the classes containing the lib strings by default.
+Without auto detection, KAU about will retain the classes containing the lib strings by default.
If you use proguard with auto detect, make sure to retain all R classes to make it possible
```
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt b/about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
index 61a3bb2..442821f 100644
--- a/about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
@@ -4,8 +4,6 @@ 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
@@ -13,19 +11,15 @@ import android.view.ViewGroup
import ca.allanwang.kau.adapters.FastItemThemedAdapter
import ca.allanwang.kau.adapters.ThemableIItemColors
import ca.allanwang.kau.adapters.ThemableIItemColorsDelegate
-import ca.allanwang.kau.animators.FadeScaleAnimatorAdd
-import ca.allanwang.kau.animators.KauAnimator
-import ca.allanwang.kau.iitems.HeaderIItem
import ca.allanwang.kau.internal.KauBaseActivity
import ca.allanwang.kau.ui.widgets.ElasticDragDismissFrameLayout
import ca.allanwang.kau.ui.widgets.InkPageIndicator
-import ca.allanwang.kau.utils.*
+import ca.allanwang.kau.utils.AnimHolder
+import ca.allanwang.kau.utils.bindView
+import ca.allanwang.kau.utils.dimenPixelSize
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.
@@ -38,26 +32,21 @@ import java.security.InvalidParameterException
* 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 = {}) : KauBaseActivity(), ViewPager.OnPageChangeListener {
+abstract class AboutActivityBase(val rClass: Class<*>?, private val configBuilder: Configs.() -> Unit = {}) : KauBaseActivity(), ViewPager.OnPageChangeListener {
+
+ private val draggableFrame: ElasticDragDismissFrameLayout by bindView(R.id.about_draggable_frame)
+ private val pager: ViewPager by bindView(R.id.about_pager)
+ private val indicator: InkPageIndicator by bindView(R.id.about_indicator)
+
+ val currentPage
+ get() = pager.currentItem
- 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
@@ -65,98 +54,65 @@ abstract class AboutActivityBase(val rClass: Class<*>?, val configBuilder: Confi
* The rest is up to you
*/
lateinit var pageStatus: IntArray
- /**
- * Holds the lib items once they are fetched asynchronously
- */
- var libItems: List<LibraryIItem>? = null
- /**
- * Holds the adapter for the library page; this is generated later because it uses the config colors
- */
- lateinit var libAdapter: FastItemThemedAdapter<IItem<*, *>>
- /**
- * Global reference of the library recycler
- * This is set by default through [inflateLibPage] and is used to stop scrolling
- * When the draggable frame exits
- * It is not required, hence its nullability
- */
- private var libRecycler: RecyclerView? = null
+
+ val panels: List<AboutPanelContract> by lazy {
+ val defaultPanels = mutableListOf(AboutPanelMain(), AboutPanelLibs())
+ if (configs.faqXmlRes != -1) defaultPanels.add(AboutPanelFaqs())
+ defaultPanels
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.kau_activity_about)
- pageStatus = IntArray(pageCount)
- libAdapter = FastItemThemedAdapter(configs)
- LibraryIItem.bindClickEvents(libAdapter)
+ pageStatus = IntArray(panels.size)
+ pageStatus[0] = 2 //the first page is instantly visible
if (configs.textColor != null) indicator.setColour(configs.textColor!!)
with(pager) {
adapter = AboutPagerAdapter()
pageMargin = dimenPixelSize(R.dimen.kau_spacing_normal)
+ offscreenPageLimit = panels.size - 1
addOnPageChangeListener(this@AboutActivityBase)
}
indicator.setViewPager(pager)
draggableFrame.addListener(object : ElasticDragDismissFrameLayout.SystemChromeFader(this) {
override fun onDragDismissed() {
window.returnTransition = TransitionInflater.from(this@AboutActivityBase)
- .inflateTransition(if (draggableFrame.translationY > 0) configs.transitionExitBottom else configs.transitionExitTop)
-
- libRecycler?.stopScroll()
+ .inflateTransition(if (draggableFrame.translationY > 0) R.transition.kau_exit_slide_bottom else R.transition.kau_exit_slide_top)
+ panels[currentPage].recycler?.stopScroll()
finishAfterTransition()
}
})
+ panels.forEachIndexed { index, contract -> contract.loadItems(this, index) }
}
- inner class Configs : ThemableIItemColors by ThemableIItemColorsDelegate() {
+ 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
-
- var transitionExitTop: Int = R.transition.kau_exit_slide_top
- var transitionExitBottom: Int = R.transition.kau_exit_slide_bottom
- }
-
- /**
- * 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!!
+ var libPageTitleRes: Int = R.string.kau_about_libraries_intro
+ var libPageTitle: String? = null
+ set(value) {
+ field = value
+ libPageTitleRes = -1 //reset res so we don't use our default
}
- }.apply {
- themeEnabled = configs.cutoutForeground == null
- })
- postInflateMainPage(fastAdapter)
- return recycler
+ var faqXmlRes: Int = -1
+ var faqPageTitleRes: Int = R.string.kau_about_faq_intro
+ var faqPageTitle: String? = null
+ set(value) {
+ field = value
+ faqPageTitleRes = -1 //reset res so we don't use our default
+ }
+ /**
+ * Whether new lines should be included
+ */
+ var faqParseNewLine: Boolean = true
}
/**
+ * For [mainPanel]
+ *
* Open hook called just before the main page view is returned
* Feel free to add your own items to the adapter in here
*/
@@ -165,32 +121,23 @@ abstract class AboutActivityBase(val rClass: Class<*>?, val configBuilder: Confi
}
/**
- * Create the lib view with the list of libraries
+ * For [libPanel]
+ *
+ * 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)!!
+
+ /*
+ * -------------------------------------------------------------------
+ * Page 3: FAQ
+ * -------------------------------------------------------------------
*/
- open fun inflateLibPage(layoutInflater: LayoutInflater, parent: ViewGroup, position: Int): View {
- libPage = position
- val v = layoutInflater.inflate(R.layout.kau_recycler_detached_background, parent, false)
- val recycler = v.findViewById<RecyclerView>(R.id.kau_recycler_detached)
- libRecycler = recycler
- recycler.withMarginDecoration(16, KAU_BOTTOM)
- recycler.adapter = libAdapter
- recycler.itemAnimator = KauAnimator(addAnimator = FadeScaleAnimatorAdd(scaleFactor = 0.7f, itemDelayFactor = 0.2f)).apply { addDuration = 300; interpolator = AnimHolder.decelerateInterpolator(this@AboutActivityBase) }
- val background = v.findViewById<View>(R.id.kau_recycler_detached_background)
- if (configs.backgroundColor != null) background.setBackgroundColor(configs.backgroundColor!!.colorToForeground())
- doAsync {
- libItems = getLibraries(
- if (rClass == null) Libs(this@AboutActivityBase) else Libs(this@AboutActivityBase, Libs.toStringArray(rClass.fields))
- ).map { LibraryIItem(it) }
- if (libPage >= 0 && pageStatus[libPage] == 1)
- uiThread { addLibItems() }
- }
- return v
- }
- inner class AboutPagerAdapter : PagerAdapter() {
+ private inner class AboutPagerAdapter : PagerAdapter() {
private val layoutInflater: LayoutInflater = LayoutInflater.from(this@AboutActivityBase)
- private val views = Array<View?>(pageCount) { null }
+ private val views = Array<View?>(panels.size) { null }
override fun instantiateItem(collection: ViewGroup, position: Int): Any {
val layout = getPage(position, collection)
@@ -203,7 +150,7 @@ abstract class AboutActivityBase(val rClass: Class<*>?, val configBuilder: Confi
views[position] = null
}
- override fun getCount(): Int = pageCount
+ override fun getCount(): Int = panels.size
override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object`
@@ -211,7 +158,8 @@ abstract class AboutActivityBase(val rClass: Class<*>?, val configBuilder: Confi
* 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)
+ if (views[position] == null) views[position] = panels[position]
+ .inflatePage(this@AboutActivityBase, parent, position)
return views[position]!!
}
}
@@ -222,19 +170,7 @@ abstract class AboutActivityBase(val rClass: Class<*>?, val configBuilder: Confi
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)
+ if (pageStatus[position] == 1) panels[position].addItems(this, position)
}
override fun onDestroy() {
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt b/about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt
new file mode 100644
index 0000000..47b9ac4
--- /dev/null
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt
@@ -0,0 +1,198 @@
+package ca.allanwang.kau.about
+
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import ca.allanwang.kau.adapters.FastItemThemedAdapter
+import ca.allanwang.kau.animators.FadeScaleAnimatorAdd
+import ca.allanwang.kau.animators.KauAnimator
+import ca.allanwang.kau.animators.NoAnimatorChange
+import ca.allanwang.kau.iitems.HeaderIItem
+import ca.allanwang.kau.utils.*
+import ca.allanwang.kau.xml.kauParseFaq
+import com.mikepenz.aboutlibraries.Libs
+import com.mikepenz.fastadapter.IItem
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
+
+/**
+ * Created by Allan Wang on 2017-08-02.
+ *
+ * The core logic for pages used in [AboutActivityBase]
+ */
+interface AboutPanelContract {
+ /**
+ * Model list to be added to [adapter]
+ */
+ var items: List<IItem<*, *>>?
+ /**
+ * The adapter, will be late initialized as it depends on configs
+ */
+ var adapter: FastItemThemedAdapter<IItem<*, *>>
+ /**
+ * Reference to the recyclerview, will be used to stop scrolling upon exit
+ */
+ var recycler: RecyclerView?
+
+ /**
+ * The base inflation method that will be called for new pages from the page adapter
+ * Keep in mind that when inflating, do NOT add the view to the viewgroup
+ * Use layoutInflater.inflate(id, parent, false)
+ */
+ fun inflatePage(activity: AboutActivityBase, parent: ViewGroup, position: Int): View
+
+ /**
+ * Convenience method called during [inflatePage]
+ * No return value necessary
+ */
+ fun onInflatingPage(activity: AboutActivityBase, recycler: RecyclerView, position: Int)
+
+ /**
+ * Triggers start of item loading
+ * Typically called with [inflatePage]
+ */
+ fun loadItems(activity: AboutActivityBase, position: Int)
+
+ /**
+ * Called when the [adapter] should take in the items
+ * This typically happens once the user has scroll to the page,
+ * so they may see a transition
+ *
+ * [AboutActivityBase.pageStatus] should be updated accordingly,
+ * as triggering this does not necessarily mean that the items are added
+ */
+ fun addItems(activity: AboutActivityBase, position: Int)
+}
+
+abstract class AboutPanelRecycler : AboutPanelContract {
+
+ override var items: List<IItem<*, *>>? = null
+
+ override lateinit var adapter: FastItemThemedAdapter<IItem<*, *>>
+
+ override var recycler: RecyclerView? = null
+
+ override fun onInflatingPage(activity: AboutActivityBase, recycler: RecyclerView, position: Int) {
+ recycler.adapter = adapter
+ recycler.itemAnimator = KauAnimator(
+ addAnimator = FadeScaleAnimatorAdd(scaleFactor = 0.7f, itemDelayFactor = 0.2f),
+ changeAnimator = NoAnimatorChange()
+ ).apply { addDuration = 300; interpolator = AnimHolder.decelerateInterpolator(recycler.context) }
+ }
+
+ override fun inflatePage(activity: AboutActivityBase, parent: ViewGroup, position: Int): View {
+ val v = LayoutInflater.from(activity).inflate(R.layout.kau_recycler_detached_background, parent, false)
+ adapter = FastItemThemedAdapter(activity.configs)
+ recycler = v.findViewById(R.id.kau_recycler_detached)
+ onInflatingPage(activity, recycler!!, position)
+ val background = v.findViewById<View>(R.id.kau_recycler_detached_background)
+ if (activity.configs.backgroundColor != null) background.setBackgroundColor(activity.configs.backgroundColor!!.colorToForeground())
+ loadItems(activity, position)
+ return v
+ }
+
+ override fun addItems(activity: AboutActivityBase, position: Int) {
+ if (items == null) return
+ activity.pageStatus[position] = 2
+ postDelayed(300) { addItemsImpl(activity, position) }
+ }
+
+ abstract fun addItemsImpl(activity: AboutActivityBase, position: Int)
+}
+
+/**
+ * Panel delegate for the main page
+ * The loading is synchronous, so it is done on inflation
+ * All other loading and adding methods are overridden to do nothing
+ * There is a [AboutActivityBase.postInflateMainPage] hook that can be overridden
+ * to update this panel without extending the whole delegate
+ */
+open class AboutPanelMain : AboutPanelRecycler() {
+
+ override fun onInflatingPage(activity: AboutActivityBase, recycler: RecyclerView, position: Int) {}
+
+ override fun inflatePage(activity: AboutActivityBase, parent: ViewGroup, position: Int): View {
+ with(activity) {
+ adapter = FastItemThemedAdapter<IItem<*, *>>(configs)
+ recycler = fullLinearRecycler(adapter)
+ adapter.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(adapter)
+ return recycler!!
+ }
+ }
+
+ override fun loadItems(activity: AboutActivityBase, position: Int) {}
+ override fun addItems(activity: AboutActivityBase, position: Int) {
+ activity.pageStatus[position] = 2
+ }
+
+ override fun addItemsImpl(activity: AboutActivityBase, position: Int) {}
+
+}
+
+/**
+ * Panel for loading libraries
+ * There is a [AboutActivityBase.getLibraries] hook that can be overridden
+ * to customize the libraries listed
+ */
+open class AboutPanelLibs : AboutPanelRecycler() {
+
+ override fun onInflatingPage(activity: AboutActivityBase, recycler: RecyclerView, position: Int) {
+ super.onInflatingPage(activity, recycler, position)
+ recycler.withMarginDecoration(16, KAU_BOTTOM)
+ LibraryIItem.bindEvents(adapter)
+ }
+
+ override fun loadItems(activity: AboutActivityBase, position: Int) {
+ doAsync {
+ with(activity) {
+ items = getLibraries(if (rClass == null) Libs(activity) else Libs(this, Libs.toStringArray(rClass.fields)))
+ .map { LibraryIItem(it) }
+ if (pageStatus[position] == 1)
+ uiThread { addItems(activity, position) }
+ }
+ }
+ }
+
+ override fun addItemsImpl(activity: AboutActivityBase, position: Int) {
+ with(activity.configs) {
+ adapter.add(HeaderIItem(text = libPageTitle, textRes = libPageTitleRes))
+ .add(items)
+ }
+ }
+}
+
+open class AboutPanelFaqs : AboutPanelRecycler() {
+
+ override fun onInflatingPage(activity: AboutActivityBase, recycler: RecyclerView, position: Int) {
+ super.onInflatingPage(activity, recycler, position)
+ FaqIItem.bindEvents(adapter)
+ }
+
+ override fun loadItems(activity: AboutActivityBase, position: Int) {
+ with(activity) {
+ kauParseFaq(configs.faqXmlRes, configs.faqParseNewLine) {
+ items = it.map { FaqIItem(it) }
+ if (pageStatus[position] == 1)
+ addItems(activity, position)
+ }
+ }
+ }
+
+ override fun addItemsImpl(activity: AboutActivityBase, position: Int) {
+ with(activity.configs) {
+ adapter.add(HeaderIItem(text = faqPageTitle, textRes = faqPageTitleRes))
+ .add(items)
+ }
+ }
+
+} \ No newline at end of file
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/CollapsibleTextView.kt b/about/src/main/kotlin/ca/allanwang/kau/about/CollapsibleTextView.kt
new file mode 100644
index 0000000..32573bc
--- /dev/null
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/CollapsibleTextView.kt
@@ -0,0 +1,33 @@
+package ca.allanwang.kau.about
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import android.widget.TextView
+import ca.allanwang.kau.ui.views.CollapsibleView
+import ca.allanwang.kau.ui.views.CollapsibleViewDelegate
+
+/**
+ * Created by Allan Wang on 2017-08-02.
+ *
+ */
+class CollapsibleTextView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
+) : TextView(context, attrs, defStyleAttr), CollapsibleView by CollapsibleViewDelegate() {
+
+ init {
+ initCollapsible(this)
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ resetCollapsibleAnimation()
+ super.onConfigurationChanged(newConfig)
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ val result = getCollapsibleDimension()
+ setMeasuredDimension(result.first, result.second)
+ }
+} \ No newline at end of file
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt b/about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt
index 1ba5815..3f764d3 100644
--- a/about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt
@@ -4,6 +4,7 @@ import android.support.v7.widget.RecyclerView
import android.view.View
import ca.allanwang.kau.adapters.ThemableIItem
import ca.allanwang.kau.adapters.ThemableIItemDelegate
+import ca.allanwang.kau.iitems.KauIItem
import ca.allanwang.kau.ui.views.CutoutView
import ca.allanwang.kau.utils.bindView
import com.mikepenz.fastadapter.items.AbstractItem
@@ -13,12 +14,9 @@ import com.mikepenz.fastadapter.items.AbstractItem
*
* 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
+class CutoutIItem(val config: CutoutView.() -> Unit = {}) : KauIItem<CutoutIItem, CutoutIItem.ViewHolder>(
+ R.layout.kau_iitem_cutout, {ViewHolder(it)}, R.id.kau_item_cutout
+), ThemableIItem by ThemableIItemDelegate() {
override fun isSelectable(): Boolean = false
@@ -38,8 +36,6 @@ class CutoutIItem(val config: CutoutView.() -> Unit = {}
}
}
- override fun getViewHolder(v: View): ViewHolder = ViewHolder(v)
-
class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
val cutout: CutoutView by bindView(R.id.kau_cutout)
}
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/FaqIItem.kt b/about/src/main/kotlin/ca/allanwang/kau/about/FaqIItem.kt
new file mode 100644
index 0000000..5e00447
--- /dev/null
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/FaqIItem.kt
@@ -0,0 +1,86 @@
+package ca.allanwang.kau.about
+
+import android.support.v7.widget.RecyclerView
+import android.text.method.LinkMovementMethod
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import ca.allanwang.kau.adapters.ThemableIItem
+import ca.allanwang.kau.adapters.ThemableIItemDelegate
+import ca.allanwang.kau.iitems.KauIItem
+import ca.allanwang.kau.utils.bindView
+import ca.allanwang.kau.utils.dpToPx
+import ca.allanwang.kau.utils.parentViewGroup
+import ca.allanwang.kau.utils.setPaddingLeft
+import ca.allanwang.kau.xml.FaqItem
+import com.mikepenz.fastadapter.FastAdapter
+import com.mikepenz.fastadapter.IItem
+import com.mikepenz.fastadapter.listeners.ClickEventHook
+
+/**
+ * Created by Allan Wang on 2017-08-02.
+ */
+class FaqIItem(val content: FaqItem) : KauIItem<LibraryIItem, FaqIItem.ViewHolder>(
+ R.layout.kau_iitem_faq, { ViewHolder(it) }, R.id.kau_item_faq
+), ThemableIItem by ThemableIItemDelegate() {
+
+ companion object {
+
+
+ @JvmStatic fun bindEvents(fastAdapter: FastAdapter<IItem<*, *>>) {
+ fastAdapter.withSelectable(false)
+ .withEventHook(object : ClickEventHook<IItem<*, *>>() {
+
+ override fun onBind(viewHolder: RecyclerView.ViewHolder): View?
+ = (viewHolder as? ViewHolder)?.questionContainer
+
+ override fun onClick(v: View, position: Int, adapter: FastAdapter<IItem<*, *>>, item: IItem<*, *>) {
+ if (item !is FaqIItem) return
+ item.isExpanded = !item.isExpanded
+ v.parentViewGroup.findViewById<CollapsibleTextView>(R.id.faq_item_answer).setExpanded(item.isExpanded)
+ }
+
+ })
+ }
+ }
+
+ private var isExpanded = false
+
+ override fun bindView(holder: ViewHolder, payloads: MutableList<Any>?) {
+ super.bindView(holder, payloads)
+ with(holder) {
+ number.text = "${content.number}."
+ question.text = content.question
+ answer.setExpanded(isExpanded, false)
+ if (accentColor != null) answer.setLinkTextColor(accentColor!!)
+ answer.text = content.answer
+ answer.post { answer.setPaddingLeft(16.dpToPx + number.width) }
+ bindTextColor(number, question)
+ bindTextColorSecondary(answer)
+ bindDividerColor(answer)
+ bindBackgroundRipple(questionContainer)
+ }
+ }
+
+ override fun unbindView(holder: ViewHolder) {
+ super.unbindView(holder)
+ with(holder) {
+ number.text = null
+ question.text = null
+ answer.text = null
+ }
+ }
+
+ class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
+ val container: ViewGroup by bindView(R.id.faq_item)
+ val questionContainer: ViewGroup by bindView(R.id.faq_item_question_container)
+ val number: TextView by bindView(R.id.faq_item_number)
+ val question: TextView by bindView(R.id.faq_item_question)
+ val answer: CollapsibleTextView by bindView(R.id.faq_item_answer)
+
+ init {
+ answer.movementMethod = LinkMovementMethod.getInstance()
+ }
+ }
+
+} \ No newline at end of file
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt b/about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt
index 518cd54..e50460e 100644
--- a/about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt
@@ -8,6 +8,7 @@ import android.view.View
import android.widget.TextView
import ca.allanwang.kau.adapters.ThemableIItem
import ca.allanwang.kau.adapters.ThemableIItemDelegate
+import ca.allanwang.kau.iitems.KauIItem
import ca.allanwang.kau.utils.bindView
import ca.allanwang.kau.utils.gone
import ca.allanwang.kau.utils.startLink
@@ -15,33 +16,30 @@ import ca.allanwang.kau.utils.visible
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() {
+class LibraryIItem(val lib: Library) : KauIItem<LibraryIItem, LibraryIItem.ViewHolder>(
+ R.layout.kau_iitem_library, { ViewHolder(it) }, R.id.kau_item_library
+), 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)
+ @JvmStatic fun bindEvents(fastAdapter: FastAdapter<IItem<*, *>>) {
+ fastAdapter.withSelectable(false)
+ .withOnClickListener { v, _, item, _ ->
+ if (item !is LibraryIItem) false
+ else {
+ val c = v.context
+ with(item.lib) {
+ c.startLink(libraryWebsite, repositoryLink, authorWebsite)
+ }
+ true
+ }
}
- 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>?) {
@@ -83,8 +81,6 @@ class LibraryIItem(val lib: Library
}
}
- 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)
diff --git a/about/src/main/res/layout/kau_iitem_faq.xml b/about/src/main/res/layout/kau_iitem_faq.xml
new file mode 100644
index 0000000..4a0f7fc
--- /dev/null
+++ b/about/src/main/res/layout/kau_iitem_faq.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/faq_item"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/faq_item_question_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="@dimen/kau_padding_normal">
+
+ <TextView
+ android:id="@+id/faq_item_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingEnd="@dimen/kau_padding_small"
+ android:paddingStart="0dp" />
+
+ <TextView
+ android:id="@+id/faq_item_question"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ <ca.allanwang.kau.about.CollapsibleTextView
+ android:id="@+id/faq_item_answer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="@dimen/kau_padding_normal" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/about/src/main/res/values/strings.xml b/about/src/main/res/values/strings.xml
index 509b56c..164c0c8 100644
--- a/about/src/main/res/values/strings.xml
+++ b/about/src/main/res/values/strings.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="kau_about_libraries_intro">This app would not be possible without the following great libraries.</string>
+ <string name="kau_about_faq_intro">FAQ</string>
</resources> \ No newline at end of file
diff --git a/about/src/main/res/values/strings_about.xml b/about/src/main/res/values/strings_about.xml
index 8f10d31..8c3dc59 100644
--- a/about/src/main/res/values/strings_about.xml
+++ b/about/src/main/res/values/strings_about.xml
@@ -1,5 +1,5 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="ResourceName">
- <string name="library_kau_libraryVersion">3.0</string>
+ <string name="library_kau_libraryVersion">3.2.3</string>
<string name="define_kau"/>
<string name="library_kau_author">Allan Wang</string>
<string name="library_kau_authorWebsite">https://www.allanwang.ca/dev/</string>
diff --git a/adapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt b/adapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt
index 244287b..5ecc937 100644
--- a/adapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt
+++ b/adapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt
@@ -1,41 +1,39 @@
package ca.allanwang.kau.animators
import android.support.v7.widget.RecyclerView
+import android.view.View
import android.view.ViewPropertyAnimator
/**
- * Created by Allan Wang on 2017-06-27.
- *
- * Truly have no animation
+ * Created by Allan Wang on 2017-08-02.
*/
-class NoAnimator : DefaultAnimator() {
- override fun addAnimationPrepare(holder: RecyclerView.ViewHolder) {}
+class NoAnimatorAdd(override var itemDelayFactor: Float = 0f) : KauAnimatorAdd {
- override fun addAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator = holder.itemView.animate().apply { duration = 0 }
+ override fun animationPrepare(holder: RecyclerView.ViewHolder): View.() -> Unit = {}
- override fun addAnimationCleanup(holder: RecyclerView.ViewHolder) {}
+ override fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = {}
- override fun changeOldAnimation(holder: RecyclerView.ViewHolder, changeInfo: ChangeInfo): ViewPropertyAnimator = holder.itemView.animate().apply { duration = 0 }
+ override fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { }
- override fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator = holder.itemView.animate().apply { duration = 0 }
+ override fun getDelay(remove: Long, move: Long, change: Long): Long = 0L
- override fun changeAnimationCleanup(holder: RecyclerView.ViewHolder) {}
+}
- override fun changeAnimation(oldHolder: RecyclerView.ViewHolder, newHolder: RecyclerView.ViewHolder?, fromX: Int, fromY: Int, toX: Int, toY: Int) {}
+class NoAnimatorRemove(override var itemDelayFactor: Float = 0f) : KauAnimatorRemove {
- override fun getAddDelay(remove: Long, move: Long, change: Long): Long = 0
+ override fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { }
- override fun getAddDuration(): Long = 0
+ override fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = {}
- override fun getMoveDuration(): Long = 0
+ override fun getDelay(remove: Long, move: Long, change: Long): Long = 0L
+}
- override fun getRemoveDuration(): Long = 0
+class NoAnimatorChange : KauAnimatorChange {
- override fun getChangeDuration(): Long = 0
+ override fun changeOldAnimation(holder: RecyclerView.ViewHolder, changeInfo: BaseItemAnimator.ChangeInfo): ViewPropertyAnimator.() -> Unit = { }
- override fun getRemoveDelay(remove: Long, move: Long, change: Long): Long = 0
+ override fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { }
- override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator = holder.itemView.animate().apply { duration = 0 }
+ override fun changeAnimationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { alpha = 1f }
- override fun removeAnimationCleanup(holder: RecyclerView.ViewHolder) {}
} \ No newline at end of file
diff --git a/core/README.md b/core/README.md
index b952797..385c7ed 100644
--- a/core/README.md
+++ b/core/README.md
@@ -10,6 +10,7 @@
* [Kotterknife](#kotterknife)
* [Ripple Canvas](#ripple-canvas)
* [MeasureSpecDelegate](#measure-spec-delegate)
+* [CollapsibleViewDelegate](#collapsible-view-delegate)
* [Timber Logger](#timber-logger)
* [Email Builder](#email-builder)
* [Extensions](#extensions)
@@ -117,7 +118,7 @@ There is another parser for a FAQ list with the following format:
<answer>This is an answer</answer>
```
-Calling `kauParseFaq` will give you a `List<Pair<Spanned, Spanned>` that you can work with.
+Call `kauParseFaq` and pass a callback taking in a `List<Pair<Spanned, Spanned>` that you can work with.
By default, the questions are numbered, and the content is formatted with HTML.
You may still need to add your own methods to allow interaction with certain elements such as links.
@@ -150,6 +151,13 @@ If you ever have a view needing exact aspect ratios with its parent and/or itsel
Implementing this in any view class unlocks its attributes, giving you three layers of view measuring to ensure exact sizing.
More information can be found in the [klass file](https://github.com/AllanWang/KAU/blob/master/core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt)
+< a name="collapsible-view-delegate"></a>
+## Collapsible View Delegate
+
+A common animation is having a view that can smoothly enter and exit by changing its height.
+This delegate will implement everything for you and give you the methods `expand`, `collapse`, etc.
+See the [kclass file](https://github.com/AllanWang/KAU/blob/master/core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt) for more details.
+
<a name="timber-logger"></a>
## Timber Logger
diff --git a/core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt b/core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt
index 94d1330..1b185f3 100644
--- a/core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt
+++ b/core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt
@@ -17,22 +17,26 @@ class FaqTest {
@Test
fun simpleTest() {
- val data = InstrumentationRegistry.getTargetContext().kauParseFaq(R.xml.test_faq)
- assertEquals(2, data.size, "FAQ size is incorrect")
- assertEquals("1. This is a question", data.first().first.toString(), "First question does not match")
- assertEquals("This is an answer", data.first().second.toString(), "First answer does not match")
- assertEquals("2. This is another question", data.last().first.toString(), "Second question does not match")
- assertEquals("This is another answer", data.last().second.toString(), "Second answer does not match")
+ InstrumentationRegistry.getTargetContext().kauParseFaq(R.xml.test_faq) {
+ data ->
+ assertEquals(2, data.size, "FAQ size is incorrect")
+ assertEquals("1. This is a question", data.first().first.toString(), "First question does not match")
+ assertEquals("This is an answer", data.first().second.toString(), "First answer does not match")
+ assertEquals("2. This is another question", data.last().first.toString(), "Second question does not match")
+ assertEquals("This is another answer", data.last().second.toString(), "Second answer does not match")
+ }
}
@Test
fun withoutNumbering() {
- val data = InstrumentationRegistry.getTargetContext().kauParseFaq(R.xml.test_faq, false)
- assertEquals(2, data.size, "FAQ size is incorrect")
- assertEquals("This is a question", data.first().first.toString(), "First question does not match")
- assertEquals("This is an answer", data.first().second.toString(), "First answer does not match")
- assertEquals("This is another question", data.last().first.toString(), "Second question does not match")
- assertEquals("This is another answer", data.last().second.toString(), "Second answer does not match")
+ InstrumentationRegistry.getTargetContext().kauParseFaq(R.xml.test_faq, false) {
+ data ->
+ assertEquals(2, data.size, "FAQ size is incorrect")
+ assertEquals("This is a question", data.first().first.toString(), "First question does not match")
+ assertEquals("This is an answer", data.first().second.toString(), "First answer does not match")
+ assertEquals("This is another question", data.last().first.toString(), "Second question does not match")
+ assertEquals("This is another answer", data.last().second.toString(), "Second answer does not match")
+ }
}
} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt
new file mode 100644
index 0000000..6994ca2
--- /dev/null
+++ b/core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt
@@ -0,0 +1,105 @@
+package ca.allanwang.kau.ui.views
+
+import android.animation.ValueAnimator
+import android.view.View
+import ca.allanwang.kau.utils.*
+import java.lang.ref.WeakReference
+
+/**
+ * Created by Allan Wang on 2017-08-03.
+ *
+ * Delegation class for collapsible views
+ *
+ * Views that implement this MUST call [initCollapsible] before using any of the methods
+ * Additionally, you will need to call [getCollapsibleDimension] and use the response for
+ * [View.setMeasuredDimension] during [View.onMeasure]
+ * (That method is protected so we cannot access it here)
+ *
+ * With reference to <a href="https://github.com/cachapa/ExpandableLayout">ExpandableLayout</a>
+ */
+interface CollapsibleView {
+ var expansion: Float
+ val state: Int
+ val expanded: Boolean
+ fun initCollapsible(view: View)
+ fun resetCollapsibleAnimation()
+ fun getCollapsibleDimension(): Pair<Int, Int>
+ fun toggleExpansion()
+ fun toggleExpansion(animate: Boolean)
+ fun expand()
+ fun expand(animate: Boolean)
+ fun collapse()
+ fun collapse(animate: Boolean)
+ fun setExpanded(expand: Boolean)
+ fun setExpanded(expand: Boolean, animate: Boolean)
+}
+
+class CollapsibleViewDelegate : CollapsibleView {
+
+ private lateinit var viewRef: WeakReference<View>
+ private val view
+ get() = viewRef.get()
+ private var animator: ValueAnimator? = null
+
+ override var expansion = 0f
+ set(value) {
+ if (value == field) return
+ var v = value
+ if (v > 1) v = 1f
+ else if (v < 0) v = 0f
+ stateHolder =
+ if (v == 0f) KAU_COLLAPSED
+ else if (v == 1f) KAU_EXPANDED
+ else if (v - field < 0) KAU_COLLAPSING
+ else KAU_EXPANDING
+ field = v
+ view?.goneIf(state == KAU_COLLAPSED)
+ view?.requestLayout()
+ }
+
+ private var stateHolder = KAU_COLLAPSED
+ override val state
+ get() = stateHolder
+ override val expanded
+ get() = stateHolder == KAU_EXPANDED || stateHolder == KAU_EXPANDING
+
+ override fun initCollapsible(view: View) {
+ this.viewRef = WeakReference(view)
+ }
+
+ override fun resetCollapsibleAnimation() {
+ animator?.cancel()
+ animator = null
+ if (stateHolder == KAU_COLLAPSING) stateHolder = KAU_COLLAPSED
+ else if (stateHolder == KAU_EXPANDING) stateHolder = KAU_EXPANDED
+ }
+
+ override fun getCollapsibleDimension(): Pair<Int, Int> {
+ val v = view ?: return Pair(0, 0)
+ val size = v.measuredHeight
+ v.goneIf(expansion == 0f && size == 0)
+ return Pair(v.measuredWidth, Math.round(size * expansion))
+ }
+
+ private fun animateSize(target: Float) {
+ resetCollapsibleAnimation()
+ animator = ValueAnimator.ofFloat(expansion, target).apply {
+ addUpdateListener { expansion = it.animatedValue as Float }
+ start()
+ }
+ }
+
+ override fun toggleExpansion() = toggleExpansion(true)
+ override fun toggleExpansion(animate: Boolean) = setExpanded(!expanded, animate)
+ override fun expand() = expand(true)
+ override fun expand(animate: Boolean) = setExpanded(true, animate)
+ override fun collapse() = collapse(true)
+ override fun collapse(animate: Boolean) = setExpanded(false, animate)
+ override fun setExpanded(expand: Boolean) = setExpanded(expand, true)
+ override fun setExpanded(expand: Boolean, animate: Boolean) {
+ if (expand == expanded) return //state already matches
+ val target = if (expand) 1f else 0f
+ if (animate) animateSize(target) else expansion = target
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt
index 3e90926..dad01f1 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt
@@ -11,4 +11,9 @@ const val KAU_RIGHT = 4
const val KAU_BOTTOM = 8
const val KAU_HORIZONTAL = KAU_LEFT or KAU_RIGHT
const val KAU_VERTICAL = KAU_TOP or KAU_BOTTOM
-const val KAU_ALL = KAU_HORIZONTAL or KAU_VERTICAL \ No newline at end of file
+const val KAU_ALL = KAU_HORIZONTAL or KAU_VERTICAL
+
+const val KAU_COLLAPSED = 0
+const val KAU_COLLAPSING = 1
+const val KAU_EXPANDING = 2
+const val KAU_EXPANDED = 3 \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt
index 2219b5d..20cec73 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt
@@ -1,3 +1,5 @@
+@file:Suppress("NOTHING_TO_INLINE")
+
package ca.allanwang.kau.utils
import android.annotation.SuppressLint
@@ -89,9 +91,12 @@ fun Context.startLink(vararg url: String?) {
}
//Toast helpers
-fun Context.toast(@StringRes id: Int, duration: Int = Toast.LENGTH_LONG) = toast(this.string(id), duration)
+inline fun View.toast(@StringRes id: Int, duration: Int = Toast.LENGTH_LONG) = context.toast(id, duration)
+
+inline 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) {
+inline fun View.toast(text: String, duration: Int = Toast.LENGTH_LONG) = context.toast(text, duration)
+inline fun Context.toast(text: String, duration: Int = Toast.LENGTH_LONG) {
Toast.makeText(this, text, duration).show()
}
diff --git a/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt b/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt
index b39540c..07a0287 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt
@@ -6,6 +6,8 @@ import android.support.annotation.XmlRes
import android.text.Html
import android.text.Spanned
import ca.allanwang.kau.utils.use
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
import org.xmlpull.v1.XmlPullParser
/**
@@ -13,42 +15,51 @@ import org.xmlpull.v1.XmlPullParser
*/
/**
- * Parse an xml with two tags, <question>Text</question> and <answer>Text</answer>,
- * and return them as a list of string pairs
+ * Parse an xml asynchronously with two tags, <question>Text</question> and <answer>Text</answer>,
+ * and invoke the [callback] on the ui thread
*/
-fun Context.kauParseFaq(@XmlRes xmlRes: Int, withNumbering: Boolean = true): List<Pair<Spanned, Spanned>> {
- val items = mutableListOf<Pair<Spanned, Spanned>>()
- resources.getXml(xmlRes).use {
- parser: XmlResourceParser ->
- var eventType = parser.eventType
- var question: Spanned? = null
- var flag = -1 //-1, 0, 1 -> invalid, question, answer
- while (eventType != XmlPullParser.END_DOCUMENT) {
- if (eventType == XmlPullParser.START_TAG) {
- flag = when (parser.name) {
- "question" -> 0
- "answer" -> 1
- else -> -1
- }
- } else if (eventType == XmlPullParser.TEXT) {
- when (flag) {
- 0 -> {
- var q = parser.text.replace("\n", "<br/>")
- if (withNumbering) q = "${items.size + 1}. $q"
- question = Html.fromHtml(q)
- flag = -1
+@Suppress("DEPRECATION")
+fun Context.kauParseFaq(
+ @XmlRes xmlRes: Int,
+ /**
+ * If \n is used, it will automatically be converted to </br>
+ */
+ parseNewLine: Boolean = true,
+ callback: (items: List<FaqItem>) -> Unit) {
+ doAsync {
+ val items = mutableListOf<FaqItem>()
+ resources.getXml(xmlRes).use {
+ parser: XmlResourceParser ->
+ var eventType = parser.eventType
+ var question: Spanned? = null
+ var flag = -1 //-1, 0, 1 -> invalid, question, answer
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ flag = when (parser.name) {
+ "question" -> 0
+ "answer" -> 1
+ else -> -1
}
- 1 -> {
- items.add(Pair(question ?: throw IllegalArgumentException("KAU FAQ answer found without a question"),
- Html.fromHtml(parser.text.replace("\n", "<br/>"))))
- question = null
- flag = -1
+ } else if (eventType == XmlPullParser.TEXT) {
+ when (flag) {
+ 0 -> {
+ question = Html.fromHtml(parser.text.replace("\n", if (parseNewLine) "<br/>" else ""))
+ flag = -1
+ }
+ 1 -> {
+ items.add(FaqItem(items.size + 1,
+ question ?: throw IllegalArgumentException("KAU FAQ answer found without a question"),
+ Html.fromHtml(parser.text.replace("\n", if (parseNewLine) "<br/>" else ""))))
+ question = null
+ flag = -1
+ }
}
}
+ eventType = parser.next()
}
-
- eventType = parser.next()
}
+ uiThread { callback(items) }
}
- return items
-} \ No newline at end of file
+}
+
+data class FaqItem(val number: Int, val question: Spanned, val answer: Spanned) \ No newline at end of file
diff --git a/core/src/main/res/values/ids.xml b/core/src/main/res/values/ids.xml
index 003e8a7..c4912e2 100644
--- a/core/src/main/res/values/ids.xml
+++ b/core/src/main/res/values/ids.xml
@@ -6,6 +6,8 @@
<item name="kau_item_cutout" type="id"/>
<item name="kau_item_header_big_margin_top" type="id"/>
<item name="kau_item_library" type="id"/>
+ <item name="kau_item_faq" type="id"/>
+
<item name="kau_item_pref_checkbox" type="id"/>
<item name="kau_item_pref_color_picker" type="id"/>
<item name="kau_item_pref_header" type="id"/>
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 64d4644..e27936d 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -1,9 +1,15 @@
# Changelog
+## v3.2.3
+* :about: Modularize everything
+* :about: Create faq panel
+* :core: Create faq parser
+* :core: Create collapsible view delegate
+* :mediapicker: Allow for prefetching by default for videos
+
## v3.2.2
* :core: Add simple KauBaseActivity so that activities extending AppCompatActivity can have some default kau helpers implemented
* :core: The permission manager will now notify you if you try to request a permission that isn\'t added to your manifest
-* :core: Create faq parser
* Begin writing android tests
## v3.2.1
diff --git a/gradle.properties b/gradle.properties
index 5fa8125..004ae9b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -28,8 +28,8 @@ ABOUT_LIBRARIES=5.9.7
ANKO=0.10.1
BLURRY=2.1.1
CONSTRAINT_LAYOUT=1.1.0-beta1
-FAST_ADAPTER=2.6.2
-FAST_ADAPTER_COMMONS=2.6.0
+FAST_ADAPTER=2.6.3
+FAST_ADAPTER_COMMONS=2.6.3
GLIDE=4.0.0
ICONICS=2.9.0
IICON_GOOGLE=3.0.1.1
diff --git a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt
index 4324fd3..3e1596f 100644
--- a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt
+++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt
@@ -59,13 +59,12 @@ abstract class KPrefActivity : KauBaseActivity(), KPrefActivityContract {
//setup layout
setContentView(R.layout.kau_pref_activity)
setSupportActionBar(toolbar)
- if (supportActionBar != null)
- with(supportActionBar!!) {
- setDisplayHomeAsUpEnabled(true)
- setDisplayShowHomeEnabled(true)
- toolbar.setNavigationOnClickListener { onBackPressed() }
- setDisplayShowTitleEnabled(false)
- }
+ supportActionBar?.apply {
+ 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))
diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/AboutActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/AboutActivity.kt
index 983544d..78f31ae 100644
--- a/sample/src/main/kotlin/ca/allanwang/kau/sample/AboutActivity.kt
+++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/AboutActivity.kt
@@ -14,6 +14,8 @@ class AboutActivity : AboutActivityBase(R.string::class.java, {
backgroundColor = 0xfffafafa.toInt()
accentColor = 0xff00838F.toInt()
cutoutForeground = 0xff18FFFF.toInt()
+ faqXmlRes = R.xml.kau_faq
+ faqParseNewLine = false
}) {
override fun postInflateMainPage(adapter: FastItemThemedAdapter<IItem<*, *>>) {
diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt
index fc4277f..9adabed 100644
--- a/sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt
+++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt
@@ -8,6 +8,7 @@ import ca.allanwang.kau.kpref.kpref
* Created by Allan Wang on 2017-06-07.
*/
object KPrefSample : KPref() {
+ var version: Int by kpref("version", -1)
var textColor: Int by kpref("TEXT_COLOR", Color.WHITE)
var accentColor: Int by kpref("ACCENT_COLOR", 0xffff8900.toInt())
var bgColor: Int by kpref("BG_COLOR", 0xff303030.toInt())
diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt
index 4c9107c..62ca8a3 100644
--- a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt
+++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt
@@ -6,12 +6,11 @@ import android.view.Menu
import android.view.MenuItem
import ca.allanwang.kau.about.kauLaunchAbout
import ca.allanwang.kau.email.sendEmail
-
-import ca.allanwang.kau.mediapicker.kauLaunchMediaPicker
-import ca.allanwang.kau.mediapicker.kauOnMediaPickerResult
import ca.allanwang.kau.kpref.activity.CoreAttributeContract
import ca.allanwang.kau.kpref.activity.KPrefActivity
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
+import ca.allanwang.kau.mediapicker.kauLaunchMediaPicker
+import ca.allanwang.kau.mediapicker.kauOnMediaPickerResult
import ca.allanwang.kau.searchview.SearchItem
import ca.allanwang.kau.searchview.SearchView
import ca.allanwang.kau.searchview.bindSearchView
@@ -20,6 +19,7 @@ import ca.allanwang.kau.utils.materialDialog
import ca.allanwang.kau.utils.navigationBarColor
import ca.allanwang.kau.utils.startActivity
import ca.allanwang.kau.utils.toast
+import ca.allanwang.kau.xml.showChangelog
import com.mikepenz.google_material_typeface_library.GoogleMaterial
@@ -206,7 +206,18 @@ class MainActivity : KPrefActivity() {
bgCanvas.set(KPrefSample.bgColor)
toolbarCanvas.set(KPrefSample.accentColor)
this.navigationBarColor = KPrefSample.accentColor
-
+ if (KPrefSample.version < BuildConfig.VERSION_CODE) {
+ KPrefSample.version = BuildConfig.VERSION_CODE
+ showChangelog(R.xml.kau_changelog, KPrefSample.textColor) {
+ titleColor(KPrefSample.textColor)
+ backgroundColor(KPrefSample.bgColor)
+ positiveColor(KPrefSample.accentColor)
+ }
+ }
+ supportActionBar?.apply {
+ setDisplayHomeAsUpEnabled(false)
+ setDisplayShowHomeEnabled(false)
+ }
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -237,6 +248,11 @@ class MainActivity : KPrefActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
+ R.id.action_changelog -> showChangelog(R.xml.kau_changelog, KPrefSample.textColor) {
+ titleColor(KPrefSample.textColor)
+ backgroundColor(KPrefSample.bgColor)
+ positiveColor(KPrefSample.accentColor)
+ }
R.id.action_settings -> startActivity(AnimActivity::class.java)
R.id.action_email -> sendEmail(R.string.your_email, R.string.your_subject)
else -> return super.onOptionsItemSelected(item)
diff --git a/sample/src/main/res/menu/menu_main.xml b/sample/src/main/res/menu/menu_main.xml
index 039c2c9..f8933f3 100644
--- a/sample/src/main/res/menu/menu_main.xml
+++ b/sample/src/main/res/menu/menu_main.xml
@@ -1,13 +1,19 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
- tools:context="com.pitchedapps.myapplication.MainActivity">
+ tools:context="ca.allanwang.kau.sample.MainActivity">
<item
android:id="@+id/action_search"
android:orderInCategory="90"
android:title="@string/kau_search"
- app:showAsAction="always|collapseActionView" />
+ app:showAsAction="always" />
+
+ <item
+ android:id="@+id/action_changelog"
+ android:orderInCategory="95"
+ android:title="@string/kau_changelog"
+ app:showAsAction="never" />
<item
android:id="@+id/action_settings"
@@ -21,11 +27,5 @@
android:title="@string/kau_send_feedback"
app:showAsAction="never" />
- <item
- android:id="@+id/test"
- android:orderInCategory="300"
- android:title="Test"
- app:showAsAction="never" />
-
</menu>
diff --git a/sample/src/main/res/xml/kau_changelog.xml b/sample/src/main/res/xml/kau_changelog.xml
index db6a504..df6ec2b 100644
--- a/sample/src/main/res/xml/kau_changelog.xml
+++ b/sample/src/main/res/xml/kau_changelog.xml
@@ -6,14 +6,17 @@
<item text="" />
-->
- <version title="v3.2.2"/>
- <item text=":core: Add simple KauBaseActivity so that activities extending AppCompatActivity can have some default kau helpers implemented" />
- <item text=":core: The permission manager will now notify you if you try to request a permission that isn\'t added to your manifest" />
+ <version title="v3.2.3"/>
+ <item text=":about: Modularize everything" />
+ <item text=":about: Create faq panel" />
<item text=":core: Create faq parser" />
+ <item text=":core: Create collapsible view delegate" />
<item text=":mediapicker: Allow for prefetching by default for videos" />
+
+ <version title="v3.2.2"/>
+ <item text=":core: Add simple KauBaseActivity so that activities extending AppCompatActivity can have some default kau helpers implemented" />
+ <item text=":core: The permission manager will now notify you if you try to request a permission that isn\'t added to your manifest" />
<item text="Begin writing android tests" />
- <item text="" />
- <item text="" />
<version title="v3.2.1"/>
<item text=":core: Remove requestLayout call from setMargin and setPadding" />
diff --git a/sample/src/main/res/xml/kau_faq.xml b/sample/src/main/res/xml/kau_faq.xml
new file mode 100644
index 0000000..5230a31
--- /dev/null
+++ b/sample/src/main/res/xml/kau_faq.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <question>This is a FAQ question</question>
+ <answer>This is a FAQ answer</answer>
+ <question><![CDATA[Supports <b>HTML</b> <i>tags</i>]]></question>
+ <answer><![CDATA[Both <u>questions</u> and <u>answers</u> are automatically parsed with <b>HTML</b>]]></answer>
+ <question>Links</question>
+ <answer><![CDATA[<ul>
+ <li><a href="https://github.com/AllanWang/KAU">Github</a><li>
+ <li><a href="https://allanwang.github.io/KAU">Page</a><li>
+ <li><a href="https://play.google.com/store/apps/details?id=ca.allanwang.kau.sample&utm_source=sample">Play Store</a><li>
+ </ul>]]></answer>
+</resources> \ No newline at end of file