aboutsummaryrefslogtreecommitdiff
path: root/about/src
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-07-08 20:25:23 -0700
committerAllan Wang <me@allanwang.ca>2017-07-08 20:25:23 -0700
commit81996038462de1be86643e95d262933c4b96c551 (patch)
tree39423b28217251e7051f86e9e4f80c47bcba20b2 /about/src
parent880d433e475e5be4e5d91afac145b490f9a959b7 (diff)
downloadkau-81996038462de1be86643e95d262933c4b96c551.tar.gz
kau-81996038462de1be86643e95d262933c4b96c551.tar.bz2
kau-81996038462de1be86643e95d262933c4b96c551.zip
Move components to separate modules
Diffstat (limited to 'about/src')
-rw-r--r--about/src/androidTest/java/ca/allanwang/kau/ExampleInstrumentedTest.java26
-rw-r--r--about/src/main/AndroidManifest.xml1
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt248
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt48
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt99
-rw-r--r--about/src/main/res/layout/kau_about_section_libraries.xml27
-rw-r--r--about/src/main/res/layout/kau_about_section_main.xml46
-rw-r--r--about/src/main/res/layout/kau_activity_about.xml26
-rw-r--r--about/src/main/res/layout/kau_iitem_cutout.xml9
-rw-r--r--about/src/main/res/layout/kau_iitem_library.xml124
-rw-r--r--about/src/main/res/transition/kau_about_return_downward.xml49
-rw-r--r--about/src/main/res/transition/kau_about_return_upwards.xml50
-rw-r--r--about/src/main/res/values/styles.xml9
-rw-r--r--about/src/test/java/ca/allanwang/kau/ExampleUnitTest.java17
14 files changed, 779 insertions, 0 deletions
diff --git a/about/src/androidTest/java/ca/allanwang/kau/ExampleInstrumentedTest.java b/about/src/androidTest/java/ca/allanwang/kau/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..7b079b2
--- /dev/null
+++ b/about/src/androidTest/java/ca/allanwang/kau/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package ca.allanwang.kau;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("ca.allanwang.kau.test", appContext.getPackageName());
+ }
+}
diff --git a/about/src/main/AndroidManifest.xml b/about/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..9c9b9e6
--- /dev/null
+++ b/about/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest package="ca.allanwang.kau.about" />
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt b/about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
new file mode 100644
index 0000000..4751a09
--- /dev/null
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/AboutActivityBase.kt
@@ -0,0 +1,248 @@
+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.ui.widgets.ElasticDragDismissFrameLayout
+import ca.allanwang.kau.ui.widgets.InkPageIndicator
+import com.mikepenz.aboutlibraries.Libs
+import com.mikepenz.aboutlibraries.entity.Library
+import com.mikepenz.fastadapter.IItem
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
+import java.security.InvalidParameterException
+
+/**
+ * Created by Allan Wang on 2017-06-28.
+ *
+ * Floating About Activity Panel for your app
+ * This contains all the necessary layouts, and can be extended and configured using the [configBuilder]
+ * The [rClass] is necessary to generate the list of libraries used in your app, and should point to your app's
+ * R.string::class.java
+ * If you don't need auto detect, you can pass null instead
+ * Note that for the auto detection to work, the R fields must be excluded from Proguard
+ * Manual lib listings and other extra modifications can be done so by overriding the open functions
+ */
+abstract class AboutActivityBase(val rClass: Class<*>?, val configBuilder: Configs.() -> Unit = {}) : AppCompatActivity(), ViewPager.OnPageChangeListener {
+
+ val draggableFrame: ElasticDragDismissFrameLayout by bindView(R.id.about_draggable_frame)
+ val pager: ViewPager by bindView(R.id.about_pager)
+ val indicator: InkPageIndicator by bindView(R.id.about_indicator)
+ /**
+ * Holds some common configurations that may be added directly from the constructor
+ * Applied lazily since it needs the context to fetch resources
+ */
+ val configs: Configs by lazy { Configs().apply { configBuilder() } }
+ /**
+ * Number of pages in the adapter
+ * Defaults to just the main view and lib view
+ */
+ open val pageCount: Int = 2
+ /**
+ * Page position for the libs
+ * This is generated automatically if [inflateLibPage] is called
+ */
+ private var libPage: Int = -2
+ /**
+ * Holds that status of each page
+ * 0 means nothing has happened
+ * 1 means this page has been in view at least once
+ * The rest is up to you
+ */
+ lateinit var pageStatus: IntArray
+ /**
+ * Holds the lib items once they are fetched asynchronously
+ */
+ var libItems: List<LibraryIItem>? = null
+ /**
+ * Holds the adapter for the library page; this is generated later because it uses the config colors
+ */
+ lateinit var libAdapter: FastItemThemedAdapter<IItem<*, *>>
+ /**
+ * Global reference of the library recycler
+ * This is set by default through [inflateLibPage] and is used to stop scrolling
+ * When the draggable frame exits
+ * It is not required, hence its nullability
+ */
+ private var libRecycler: RecyclerView? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.kau_activity_about)
+ pageStatus = IntArray(pageCount)
+ libAdapter = FastItemThemedAdapter(configs)
+ LibraryIItem.bindClickEvents(libAdapter)
+ if (configs.textColor != null) indicator.setColour(configs.textColor!!)
+ with(pager) {
+ adapter = AboutPagerAdapter()
+ pageMargin = dimenPixelSize(R.dimen.kau_spacing_normal)
+ addOnPageChangeListener(this@AboutActivityBase)
+ }
+ indicator.setViewPager(pager)
+ draggableFrame.addListener(object : ElasticDragDismissFrameLayout.SystemChromeFader(this) {
+ override fun onDragDismissed() {
+ // if we drag dismiss downward then the default reversal of the enter
+ // transition would slide content upward which looks weird. So reverse it.
+ if (draggableFrame.translationY > 0) {
+ window.returnTransition = TransitionInflater.from(this@AboutActivityBase)
+ .inflateTransition(configs.transitionExitReversed)
+ }
+ libRecycler?.stopScroll()
+ finishAfterTransition()
+ }
+ })
+ }
+
+ inner class Configs : ThemableIItemColors by ThemableIItemColorsDelegate() {
+ var cutoutTextRes: Int = -1
+ var cutoutText: String? = null
+ var cutoutDrawableRes: Int = -1
+ var cutoutDrawable: Drawable? = null
+ var cutoutForeground: Int? = null
+ var libPageTitleRes: Int = -1
+ var libPageTitle: String? = string(R.string.kau_about_libraries_intro) //This is in the string by default since it's lower priority
+ /**
+ * Transition to be called if the view is dragged down
+ */
+ var transitionExitReversed: Int = R.transition.kau_about_return_downward
+ }
+
+ /**
+ * Method to fetch the library list
+ * This is fetched asynchronously and you may override it to customize the list
+ */
+ open fun getLibraries(libs: Libs): List<Library> = libs.prepareLibraries(this, null, null, true, true)
+
+ /**
+ * Gets the view associated with the given page position
+ * Keep in mind that when inflating, do NOT add the view to the viewgroup
+ * Use layoutInflater.inflate(id, parent, false)
+ */
+ open fun getPage(position: Int, layoutInflater: LayoutInflater, parent: ViewGroup): View {
+ return when (position) {
+ 0 -> inflateMainPage(layoutInflater, parent, position)
+ pageCount - 1 -> inflateLibPage(layoutInflater, parent, position)
+ else -> throw InvalidParameterException()
+ }
+ }
+
+ /**
+ * Create the main view with the cutout
+ */
+ open fun inflateMainPage(layoutInflater: LayoutInflater, parent: ViewGroup, position: Int): View {
+ val fastAdapter = FastItemThemedAdapter<IItem<*, *>>(configs)
+ val recycler = fullLinearRecycler(fastAdapter)
+ fastAdapter.add(CutoutIItem {
+ with(configs) {
+ text = string(cutoutTextRes, cutoutText)
+ drawable = drawable(cutoutDrawableRes, cutoutDrawable)
+ if (configs.cutoutForeground != null) foregroundColor = configs.cutoutForeground!!
+ }
+ }.apply {
+ themeEnabled = configs.cutoutForeground == null
+ })
+ postInflateMainPage(fastAdapter)
+ return recycler
+ }
+
+ /**
+ * Open hook called just before the main page view is returned
+ * Feel free to add your own items to the adapter in here
+ */
+ open fun postInflateMainPage(adapter: FastItemThemedAdapter<IItem<*, *>>) {
+
+ }
+
+ /**
+ * Create the lib view with the list of libraries
+ */
+ open fun inflateLibPage(layoutInflater: LayoutInflater, parent: ViewGroup, position: Int): View {
+ libPage = position
+ val v = layoutInflater.inflate(R.layout.kau_recycler_detached_background, parent, false)
+ val recycler = v.findViewById<RecyclerView>(R.id.kau_recycler_detached)
+ libRecycler = recycler
+ recycler.adapter = libAdapter
+ recycler.itemAnimator = FadeScaleAnimator(itemDelayFactor = 0.2f).apply { addDuration = 300; interpolator = AnimHolder.decelerateInterpolator(this@AboutActivityBase) }
+ val background = v.findViewById<View>(R.id.kau_recycler_detached_background)
+ if (configs.backgroundColor != null) background.setBackgroundColor(configs.backgroundColor!!.colorToForeground())
+ doAsync {
+ libItems = getLibraries(
+ if (rClass == null) Libs(this@AboutActivityBase) else Libs(this@AboutActivityBase, Libs.toStringArray(rClass.fields))
+ ).map { LibraryIItem(it) }
+ if (libPage >= 0 && pageStatus[libPage] == 1)
+ uiThread { addLibItems() }
+ }
+ return v
+ }
+
+ inner class AboutPagerAdapter : PagerAdapter() {
+
+ private val layoutInflater: LayoutInflater = LayoutInflater.from(this@AboutActivityBase)
+ private val views = Array<View?>(pageCount) { null }
+
+ override fun instantiateItem(collection: ViewGroup, position: Int): Any {
+ val layout = getPage(position, collection)
+ collection.addView(layout)
+ return layout
+ }
+
+ override fun destroyItem(collection: ViewGroup, position: Int, view: Any) {
+ collection.removeView(view as View)
+ views[position] = null
+ }
+
+ override fun getCount(): Int = pageCount
+
+ override fun isViewFromObject(view: View, `object`: Any): Boolean = view === `object`
+
+ /**
+ * Only get page if view does not exist
+ */
+ private fun getPage(position: Int, parent: ViewGroup): View {
+ if (views[position] == null) views[position] = getPage(position, layoutInflater, parent)
+ return views[position]!!
+ }
+ }
+
+ override fun onPageScrollStateChanged(state: Int) {}
+
+ override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
+
+ override fun onPageSelected(position: Int) {
+ if (pageStatus[position] == 0) pageStatus[position] = 1 // mark as seen if previously null
+ if (position == libPage && libItems != null && pageStatus[position] == 1) {
+ pageStatus[position] = 2 //add libs and mark as such
+ postDelayed(300) { addLibItems() } //delay so that the animations occur once the page is fully switched
+ }
+ }
+
+ /**
+ * Function that is called when the view is ready to add the lib items
+ * Feel free to add your own items here
+ */
+ open fun addLibItems() {
+ libAdapter.add(HeaderIItem(text = configs.libPageTitle, textRes = configs.libPageTitleRes))
+ .add(libItems)
+ }
+
+ override fun onDestroy() {
+ AnimHolder.decelerateInterpolator.invalidate() //clear the reference to the interpolators we've used
+ super.onDestroy()
+ }
+} \ No newline at end of file
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt b/about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt
new file mode 100644
index 0000000..34e8641
--- /dev/null
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/CutoutIItem.kt
@@ -0,0 +1,48 @@
+package ca.allanwang.kau.about
+
+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.ui.views.CutoutView
+import ca.allanwang.kau.utils.bindView
+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/about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt b/about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt
new file mode 100644
index 0000000..1b832a2
--- /dev/null
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/LibraryIItem.kt
@@ -0,0 +1,99 @@
+package ca.allanwang.kau.about
+
+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 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/about/src/main/res/layout/kau_about_section_libraries.xml b/about/src/main/res/layout/kau_about_section_libraries.xml
new file mode 100644
index 0000000..c14225e
--- /dev/null
+++ b/about/src/main/res/layout/kau_about_section_libraries.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/about_library_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/about_library_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/kau_activity_horizontal_margin"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/about_library_recycler"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/about_library_title" />
+
+</android.support.constraint.ConstraintLayout>
diff --git a/about/src/main/res/layout/kau_about_section_main.xml b/about/src/main/res/layout/kau_about_section_main.xml
new file mode 100644
index 0000000..40d8dfb
--- /dev/null
+++ b/about/src/main/res/layout/kau_about_section_main.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.NestedScrollView 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"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:showIn="@layout/kau_activity_about">
+
+ <android.support.constraint.ConstraintLayout
+ android:id="@+id/about_main_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ca.allanwang.kau.views.CutoutView
+ android:id="@+id/about_main_cutout"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/kau_about_header_height"
+ android:minHeight="@dimen/kau_about_header_height"
+ app:foregroundColor="?android:colorAccent"
+ app:heightPercentageToScreen="0.5"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <FrameLayout
+ android:id="@+id/about_main_bottom_container"
+ android:layout_width="0dp"
+ android:layout_height="2000dp"
+ android:background="?android:attr/colorBackground"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/about_main_cutout">
+
+ <TextView
+ android:id="@+id/about_main_bottom_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_margin="@dimen/activity_horizontal_margin" />
+
+ </FrameLayout>
+
+ </android.support.constraint.ConstraintLayout>
+
+</android.support.v4.widget.NestedScrollView>
diff --git a/about/src/main/res/layout/kau_activity_about.xml b/about/src/main/res/layout/kau_activity_about.xml
new file mode 100644
index 0000000..3d1f9ab
--- /dev/null
+++ b/about/src/main/res/layout/kau_activity_about.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ca.allanwang.kau.ui.widgets.ElasticDragDismissFrameLayout 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"
+ android:id="@+id/about_draggable_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:dragDismissDistance="@dimen/kau_drag_dismiss_distance"
+ app:dragDismissScale="0.95"
+ tools:context=".ui.about.AboutActivityBase">
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/about_pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <ca.allanwang.kau.ui.widgets.InkPageIndicator
+ android:id="@+id/about_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_marginTop="@dimen/kau_padding_normal"
+ app:currentPageIndicatorColor="@color/kau_about_page_indicator_dark_selected"
+ app:pageIndicatorColor="@color/kau_about_page_indicator_dark" />
+
+</ca.allanwang.kau.ui.widgets.ElasticDragDismissFrameLayout>
diff --git a/about/src/main/res/layout/kau_iitem_cutout.xml b/about/src/main/res/layout/kau_iitem_cutout.xml
new file mode 100644
index 0000000..b3a841e
--- /dev/null
+++ b/about/src/main/res/layout/kau_iitem_cutout.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ca.allanwang.kau.ui.views.CutoutView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/kau_cutout"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/kau_about_header_height"
+ android:minHeight="@dimen/kau_about_header_height"
+ app:foregroundColor="?android:colorAccent"
+ app:heightPercentageToScreen="0.5" /> \ No newline at end of file
diff --git a/about/src/main/res/layout/kau_iitem_library.xml b/about/src/main/res/layout/kau_iitem_library.xml
new file mode 100644
index 0000000..1c3de5c
--- /dev/null
+++ b/about/src/main/res/layout/kau_iitem_library.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.CardView 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"
+ android:id="@+id/lib_item_card"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/kau_padding_normal"
+ android:background="?android:selectableItemBackground"
+ android:clickable="true">
+
+ <android.support.constraint.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/kau_padding_normal"
+ android:paddingEnd="@dimen/kau_padding_normal"
+ android:paddingStart="@dimen/kau_padding_normal"
+ android:paddingTop="@dimen/kau_padding_normal">
+
+ <android.support.constraint.Guideline
+ android:id="@+id/lib_g_m_v"
+ android:layout_width="1dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.5" />
+
+ <TextView
+ android:id="@+id/lib_item_name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:gravity="start"
+ android:maxLines="1"
+ android:textSize="@dimen/textSizeLarge_openSource"
+ android:textStyle="normal"
+ app:layout_constraintEnd_toStartOf="@id/lib_g_m_v"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Library name" />
+
+ <TextView
+ android:id="@+id/lib_item_author"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="2dp"
+ android:gravity="end"
+ android:maxLines="2"
+ android:textSize="@dimen/textSizeSmall_openSource"
+ android:textStyle="normal"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/lib_g_m_v"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Author" />
+
+ <android.support.constraint.Barrier
+ android:id="@+id/lib_item_top_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="1px"
+ android:layout_marginTop="@dimen/kau_spacing_micro"
+ app:barrierDirection="bottom"
+ app:constraint_referenced_ids="lib_item_name,lib_item_author"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/lib_item_description"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:maxLines="20"
+ android:paddingBottom="@dimen/kau_spacing_normal"
+ android:paddingTop="@dimen/kau_spacing_normal"
+ android:textSize="@dimen/textSizeSmall_openSource"
+ android:textStyle="normal"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/lib_item_top_divider"
+ tools:text="Description" />
+
+ <View
+ android:id="@+id/lib_item_bottom_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:layout_marginTop="@dimen/kau_spacing_micro"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/lib_item_description" />
+
+ <TextView
+ android:id="@+id/lib_item_version"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/kau_spacing_normal"
+ android:layout_marginTop="@dimen/kau_spacing_micro"
+ android:layout_weight="1"
+ android:gravity="start"
+ android:maxLines="1"
+ android:textSize="@dimen/textSizeSmall_openSource"
+ android:textStyle="normal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/lib_g_m_v"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/lib_item_bottom_divider"
+ tools:text="Version" />
+
+ <TextView
+ android:id="@+id/lib_item_license"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/kau_spacing_normal"
+ android:layout_marginTop="@dimen/kau_spacing_micro"
+ android:layout_weight="1"
+ android:gravity="end"
+ android:maxLines="1"
+ android:textSize="@dimen/textSizeSmall_openSource"
+ android:textStyle="normal"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/lib_g_m_v"
+ app:layout_constraintTop_toBottomOf="@id/lib_item_bottom_divider"
+ tools:text="License" />
+
+ </android.support.constraint.ConstraintLayout>
+
+</android.support.v7.widget.CardView> \ No newline at end of file
diff --git a/about/src/main/res/transition/kau_about_return_downward.xml b/about/src/main/res/transition/kau_about_return_downward.xml
new file mode 100644
index 0000000..b040b1b
--- /dev/null
+++ b/about/src/main/res/transition/kau_about_return_downward.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<transitionSet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionOrdering="together"
+ android:interpolator="@android:interpolator/fast_out_linear_in">
+
+ <slide
+ android:slideEdge="bottom"
+ android:duration="400">
+ <targets>
+ <target android:excludeId="@android:id/navigationBarBackground" />
+ <target android:excludeId="@android:id/statusBarBackground" />
+ </targets>
+ </slide>
+
+ <fade
+ android:startDelay="200"
+ android:duration="200">
+ <targets>
+ <target android:targetId="@id/about_indicator" />
+ <!--<target android:targetId="@id/libraries_intro" />-->
+ <!--<target android:targetId="@id/libs_list_background" />-->
+ </targets>
+ </fade>
+
+ <fade android:duration="400">
+ <targets>
+ <target android:targetId="@android:id/navigationBarBackground" />
+ <target android:targetId="@android:id/statusBarBackground" />
+ </targets>
+ </fade>
+
+</transitionSet>
diff --git a/about/src/main/res/transition/kau_about_return_upwards.xml b/about/src/main/res/transition/kau_about_return_upwards.xml
new file mode 100644
index 0000000..64b3f5e
--- /dev/null
+++ b/about/src/main/res/transition/kau_about_return_upwards.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<transitionSet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionOrdering="together"
+ android:interpolator="@android:interpolator/fast_out_linear_in">
+
+ <slide
+ android:slideEdge="top"
+ android:duration="400">
+ <targets>
+ <target android:excludeId="@android:id/navigationBarBackground" />
+ <target android:excludeId="@android:id/statusBarBackground" />
+ </targets>
+ </slide>
+
+ <fade
+ android:startDelay="200"
+ android:duration="200">
+ <targets>
+ <target android:targetId="@id/about_indicator" />
+ <!--<target android:targetId="@id/libraries_intro" />-->
+ <!--<target android:targetId="@id/libs_list_background" />-->
+
+ </targets>
+ </fade>
+
+ <fade android:duration="400">
+ <targets>
+ <target android:targetId="@android:id/navigationBarBackground" />
+ <target android:targetId="@android:id/statusBarBackground" />
+ </targets>
+ </fade>
+
+</transitionSet>
diff --git a/about/src/main/res/values/styles.xml b/about/src/main/res/values/styles.xml
new file mode 100644
index 0000000..59b2470
--- /dev/null
+++ b/about/src/main/res/values/styles.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="Kau.Translucent.About">
+ <item name="android:windowEnterTransition">@transition/kau_enter_slide_top</item>
+ <item name="android:windowReturnTransition">@transition/kau_about_return_upwards</item>
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/about/src/test/java/ca/allanwang/kau/ExampleUnitTest.java b/about/src/test/java/ca/allanwang/kau/ExampleUnitTest.java
new file mode 100644
index 0000000..a29b447
--- /dev/null
+++ b/about/src/test/java/ca/allanwang/kau/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package ca.allanwang.kau;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+} \ No newline at end of file