From 8f2b5ac043f47cc44f43c3788d1377083fb339a2 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 18 Jul 2017 20:16:23 -0700 Subject: Dev 2.1 (#8) * Rewrite animation interfaces * Update changelog * Add scale factor for slide * Remove margins in iitems and replace with decorators * Remove mutable list * Switch cardiitem to use lambdas for click * status * Utils update and imagepicker fixes * Remove stringholder * Add fade in fade out * Increment about version * Rename fromedge to direction in javadocs * More logging * Add logging and docs * Make card icons visible * Update email builder and icon padding * Create elastic recycler activity * Fix card iitem * Add lint check and plurals * Inline all the things * Format and sort xml * Update dependencies and increment version --- .../kotlin/ca/allanwang/kau/email/EmailBuilder.kt | 6 +- .../allanwang/kau/permissions/PermissionManager.kt | 5 +- .../ca/allanwang/kau/permissions/Permissions.kt | 6 ++ .../allanwang/kau/ui/views/MeasureSpecDelegate.kt | 107 +++++++++++++++++++++ .../kotlin/ca/allanwang/kau/utils/ActivityUtils.kt | 6 +- .../kotlin/ca/allanwang/kau/utils/ColorUtils.kt | 2 +- .../kotlin/ca/allanwang/kau/utils/ContextUtils.kt | 23 +++-- .../main/kotlin/ca/allanwang/kau/utils/Either.kt | 32 ------ .../kotlin/ca/allanwang/kau/utils/Kotterknife.kt | 16 +-- .../kotlin/ca/allanwang/kau/utils/PackageUtils.kt | 18 +++- .../kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt | 25 +++++ .../kotlin/ca/allanwang/kau/utils/StringHolder.kt | 22 ----- .../main/kotlin/ca/allanwang/kau/utils/Utils.kt | 16 ++- .../kotlin/ca/allanwang/kau/utils/ViewUtils.kt | 61 +++++++++--- 14 files changed, 240 insertions(+), 105 deletions(-) create mode 100644 core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt delete mode 100644 core/src/main/kotlin/ca/allanwang/kau/utils/Either.kt create mode 100644 core/src/main/kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt delete mode 100644 core/src/main/kotlin/ca/allanwang/kau/utils/StringHolder.kt (limited to 'core/src/main/kotlin') diff --git a/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt b/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt index b03a620..88a0945 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt @@ -40,9 +40,9 @@ class EmailBuilder(val email: String, val subject: String) { if (deviceDetails) { val deviceItems = mutableMapOf( "OS Version" to "${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})", - "OS API Level" to Build.DEVICE, - "Manufacturer" to Build.MANUFACTURER, - "Model (and Product)" to "${Build.MODEL} (${Build.PRODUCT})", + "OS SDK" to Build.VERSION.SDK_INT, + "Device (Manufacturer)" to "${Build.DEVICE} (${Build.MANUFACTURER})", + "Model (Product)" to "${Build.MODEL} (${Build.PRODUCT})", "Package Installer" to (context.installerPackageName ?: "None") ) if (context is Activity) { diff --git a/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt b/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt index 6f93c9f..d6e17db 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt @@ -18,7 +18,7 @@ internal object PermissionManager { val pendingResults: MutableList> by lazy { mutableListOf>() } operator fun invoke(context: Context, permissions: Array, callback: (granted: Boolean, deniedPerm: String?) -> Unit) { - KL.d("Requesting permissions: ${permissions.contentToString()}") + KL.d("Permission manager for: ${permissions.contentToString()}") if (!buildIsMarshmallowAndUp) return callback(true, null) val missingPermissions = permissions.filter { !context.hasPermission(it) } if (missingPermissions.isEmpty()) return callback(true, null) @@ -31,10 +31,12 @@ internal object PermissionManager { @Synchronized internal fun requestPermissions(context: Context, permissions: Array) { val activity = (context as? Activity) ?: throw KauException("Context is not an instance of an activity; cannot request permissions") + KL.d("Requesting permissions ${permissions.contentToString()}") ActivityCompat.requestPermissions(activity, permissions, 1) } fun onRequestPermissionsResult(context: Context, permissions: Array, grantResults: IntArray) { + KL.d("On permission result: pending ${pendingResults.size}") val count = Math.min(permissions.size, grantResults.size) val iter = pendingResults.iterator() while (iter.hasNext()) { @@ -53,6 +55,7 @@ internal object PermissionManager { } requestPermissions(context, action.permissions.toTypedArray()) } + KL.d("Post on permission result: pending ${pendingResults.size}") } } \ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt b/core/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt index fd43102..36ad52f 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt @@ -9,6 +9,12 @@ import android.content.Context * Created by Allan Wang on 2017-07-02. * * Bindings for the permission manager + * This is the only class you need to worry about when using KAU's manager + * + * MAKE SURE [kauOnRequestPermissionsResult] is added to your activities, + * and don't forget to request the permissions in your manifest. + * A collection of constants redirecting to the [Manifest.permission] counterparts + * are added for your convenience */ /** diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt new file mode 100644 index 0000000..edc1536 --- /dev/null +++ b/core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt @@ -0,0 +1,107 @@ +package ca.allanwang.kau.ui.views + +import android.content.Context +import android.graphics.Rect +import android.util.AttributeSet +import android.view.View +import ca.allanwang.kau.R +import ca.allanwang.kau.utils.parentViewGroup + +/** + * Created by Allan Wang on 2017-07-14. + * + * Handles relative sizes for any view + */ +interface MeasureSpecContract { + + /** + * Width will be calculated as a percentage of the parent + * This takes precedence over relativeWidth + */ + var relativeWidthToParent: Float + /** + * Height will be calculated as a percentage of the parent + * This takes precedence over relativeHeight + */ + var relativeHeightToParent: Float + /** + * Width will be calculated based on the measured height + */ + var relativeWidth: Float + /** + * Height will be calculated based on the measure width + */ + var relativeHeight: Float + /** + * Width will be once again calculated from the current measured height + * This is the last step + */ + var postRelativeWidth: Float + /** + * Height will be once again calculated from the current measured width + * This is the last step + */ + var postRelativeHeight: Float + + /** + * Retrieves relative values from the [AttributeSet] + * Call this on init + */ + fun initAttrs(context: Context, attrs: AttributeSet?) + + /** + * Calculates the final measure specs + * Call this from [View.onMeasure] and send the Pair result as the specs + * The pair is of the format (width, height) + */ + fun onMeasure(view: View, widthMeasureSpec: Int, heightMeasureSpec: Int): Pair +} + +class MeasureSpecDelegate : MeasureSpecContract { + + override var relativeWidth = -1f + override var relativeHeight = -1f + override var relativeWidthToParent = -1f + override var relativeHeightToParent = -1f + override var postRelativeWidth: Float = -1f + override var postRelativeHeight: Float = -1f + private val parentFrame = Rect() + + override fun initAttrs(context: Context, attrs: AttributeSet?) { + if (attrs == null) return + val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MeasureSpecDelegate) + relativeWidth = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeWidth, relativeWidth) + relativeHeight = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeHeight, relativeHeight) + relativeWidthToParent = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeWidthToParent, relativeWidthToParent) + relativeHeightToParent = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeHeightToParent, relativeHeightToParent) + postRelativeWidth = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_postRelativeWidth, postRelativeWidth) + postRelativeHeight = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_postRelativeHeight, postRelativeHeight) + styledAttrs.recycle() + } + + override fun onMeasure(view: View, widthMeasureSpec: Int, heightMeasureSpec: Int): Pair { + view.parentViewGroup.getWindowVisibleDisplayFrame(parentFrame) + var width = View.MeasureSpec.getSize(widthMeasureSpec).toFloat() + var height = View.MeasureSpec.getSize(heightMeasureSpec).toFloat() + //first cycle - relative to parent + if (relativeHeightToParent > 0) + height = relativeHeightToParent * parentFrame.height() + if (relativeWidthToParent > 0) + width = relativeWidthToParent * parentFrame.width() + //second cycle - relative to each other + if (relativeHeight > 0) + height = relativeHeight * width + else if (relativeWidth > 0) + width = relativeWidth * height + //third cycle - relative to each other + if (postRelativeHeight > 0) + height = postRelativeHeight * width + else if (postRelativeWidth > 0) + width = postRelativeWidth * height + return Pair(width.measureSpec, height.measureSpec) + } + + private val Float.measureSpec: Int + get() = View.MeasureSpec.makeMeasureSpec(this.toInt(), View.MeasureSpec.EXACTLY) + +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt index 3b99c46..cd6e089 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt @@ -38,19 +38,19 @@ fun Activity.finishSlideOut() { overridePendingTransition(R.anim.kau_fade_in, R.anim.kau_slide_out_right_top) } -var Activity.navigationBarColor: Int +inline var Activity.navigationBarColor: Int get() = if (buildIsLollipopAndUp) window.navigationBarColor else Color.BLACK set(value) { if (buildIsLollipopAndUp) window.navigationBarColor = value } -var Activity.statusBarColor: Int +inline var Activity.statusBarColor: Int get() = if (buildIsLollipopAndUp) window.statusBarColor else Color.BLACK set(value) { if (buildIsLollipopAndUp) window.statusBarColor = value } -var Activity.statusBarLight: Boolean +inline var Activity.statusBarLight: Boolean @SuppressLint("InlinedApi") get() = if (buildIsMarshmallowAndUp) window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR > 0 else false @SuppressLint("InlinedApi") diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt index 81bf0d9..50d117c 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt @@ -65,7 +65,7 @@ fun Int.adjustAlpha(factor: Float): Int { return Color.argb(alpha, Color.red(this), Color.green(this), Color.blue(this)) } -val Int.isColorTransparent: Boolean +inline val Int.isColorTransparent: Boolean get() = Color.alpha(this) != 255 @ColorInt 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 3759c75..a8e0715 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt @@ -15,6 +15,7 @@ import android.support.v4.app.ActivityOptionsCompat import android.support.v4.content.ContextCompat import android.util.TypedValue import android.view.View +import android.view.animation.AnimationUtils import android.widget.Toast import ca.allanwang.kau.R import ca.allanwang.kau.logging.KL @@ -85,16 +86,18 @@ fun Context.toast(text: String, duration: Int = Toast.LENGTH_LONG) { } //Resource retrievers -fun Context.string(@StringRes id: Int): String = getString(id) - -fun Context.string(@StringRes id: Int, fallback: String?): String? = if (id > 0) string(id) else fallback -fun Context.string(holder: StringHolder?): String? = holder?.getString(this) -fun Context.color(@ColorRes id: Int): Int = ContextCompat.getColor(this, id) -fun Context.integer(@IntegerRes id: Int): Int = resources.getInteger(id) -fun Context.dimen(@DimenRes id: Int): Float = resources.getDimension(id) -fun Context.dimenPixelSize(@DimenRes id: Int): Int = resources.getDimensionPixelSize(id) -fun Context.drawable(@DrawableRes id: Int): Drawable = ContextCompat.getDrawable(this, id) -fun Context.drawable(@DrawableRes id: Int, fallback: Drawable?): Drawable? = if (id > 0) drawable(id) else fallback +inline fun Context.string(@StringRes id: Int): String = getString(id) + +inline fun Context.string(@StringRes id: Int, fallback: String?): String? = if (id > 0) string(id) else fallback +inline fun Context.color(@ColorRes id: Int): Int = ContextCompat.getColor(this, id) +inline fun Context.integer(@IntegerRes id: Int): Int = resources.getInteger(id) +inline fun Context.dimen(@DimenRes id: Int): Float = resources.getDimension(id) +inline fun Context.dimenPixelSize(@DimenRes id: Int): Int = resources.getDimensionPixelSize(id) +inline fun Context.drawable(@DrawableRes id: Int): Drawable = ContextCompat.getDrawable(this, id) +inline fun Context.drawable(@DrawableRes id: Int, fallback: Drawable?): Drawable? = if (id > 0) drawable(id) else fallback +inline fun Context.interpolator(@InterpolatorRes id: Int) = AnimationUtils.loadInterpolator(this, id) +inline fun Context.animation(@AnimRes id: Int) = AnimationUtils.loadAnimation(this, id) +inline fun Context.plural(@PluralsRes id: Int, quantity: Number) = resources.getQuantityString(id, quantity.toInt()) //Attr retrievers fun Context.resolveColor(@AttrRes attr: Int, fallback: Int = 0): Int { diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/Either.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/Either.kt deleted file mode 100644 index dab5810..0000000 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/Either.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ca.allanwang.kau.utils - -/** - * Created by Allan Wang on 2017-06-17. - * - * Courtesy of adelnizamutdinov - * - * https://github.com/adelnizamutdinov/kotlin-either - */ -@Suppress("unused") -sealed class Either - -data class Left(val value: T) : Either() -data class Right(val value: T) : Either() - -inline fun Either.fold(left: (L) -> T, right: (R) -> T): T = - when (this) { - is Left -> left(value) - is Right -> right(value) - } - -inline fun Either.flatMap(f: (R) -> Either): Either = - fold({ this as Left }, f) - -inline fun Either.map(f: (R) -> T): Either = - flatMap { Right(f(it)) } - -val Either.isLeft: Boolean - get() = this is Left - -val Either<*, T>.isRight: Boolean - get() = this is Right \ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt index 247bbc7..3783931 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt @@ -114,21 +114,21 @@ fun android.support.v4.app.Fragment.bindOptionalViews(vararg ids: Int fun ViewHolder.bindOptionalViews(vararg ids: Int) : ReadOnlyProperty> = optional(ids, viewFinder) -private val View.viewFinder: View.(Int) -> View? +private inline val View.viewFinder: View.(Int) -> View? get() = { findViewById(it) } -private val Activity.viewFinder: Activity.(Int) -> View? +private inline val Activity.viewFinder: Activity.(Int) -> View? get() = { findViewById(it) } -private val Dialog.viewFinder: Dialog.(Int) -> View? +private inline val Dialog.viewFinder: Dialog.(Int) -> View? get() = { findViewById(it) } -private val DialogFragment.viewFinder: DialogFragment.(Int) -> View? +private inline val DialogFragment.viewFinder: DialogFragment.(Int) -> View? get() = { dialog.findViewById(it) } -private val android.support.v4.app.DialogFragment.viewFinder: android.support.v4.app.DialogFragment.(Int) -> View? +private inline val android.support.v4.app.DialogFragment.viewFinder: android.support.v4.app.DialogFragment.(Int) -> View? get() = { dialog.findViewById(it) } -private val Fragment.viewFinder: Fragment.(Int) -> View? +private inline val Fragment.viewFinder: Fragment.(Int) -> View? get() = { view.findViewById(it) } -private val android.support.v4.app.Fragment.viewFinder: android.support.v4.app.Fragment.(Int) -> View? +private inline val android.support.v4.app.Fragment.viewFinder: android.support.v4.app.Fragment.(Int) -> View? get() = { view!!.findViewById(it) } -private val ViewHolder.viewFinder: ViewHolder.(Int) -> View? +private inline val ViewHolder.viewFinder: ViewHolder.(Int) -> View? get() = { itemView.findViewById(it) } private fun viewNotFound(id: Int, desc: KProperty<*>): Nothing = diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt index d04538c..89d64e5 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt @@ -22,22 +22,30 @@ import android.os.Build } } -val buildIsLollipopAndUp: Boolean +@KauUtils fun Context.isAppEnabled(packageName: String): Boolean { + try { + return packageManager.getApplicationInfo(packageName, 0).enabled + } catch (e: Exception) { + return false + } +} + +inline val buildIsLollipopAndUp: Boolean get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -val buildIsMarshmallowAndUp: Boolean +inline val buildIsMarshmallowAndUp: Boolean get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -val buildIsNougatAndUp: Boolean +inline val buildIsNougatAndUp: Boolean get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N const val INSTALLER_GOOGLE_PLAY_VENDING = "com.android.vending" const val INSTALLER_GOOGLE_PLAY_FEEDBACK = "com.google.android.feedback" -val Context.installerPackageName: String? +inline val Context.installerPackageName: String? get() = packageManager.getInstallerPackageName(packageName) -val Context.isFromGooglePlay: Boolean +inline val Context.isFromGooglePlay: Boolean get() { val installer = installerPackageName return arrayOf(INSTALLER_GOOGLE_PLAY_FEEDBACK, INSTALLER_GOOGLE_PLAY_VENDING).any { it == installer } diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt new file mode 100644 index 0000000..f80c85e --- /dev/null +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt @@ -0,0 +1,25 @@ +package ca.allanwang.kau.utils + +import android.graphics.Rect +import android.support.v7.widget.RecyclerView +import android.view.View + +/** + * Created by Allan Wang on 2017-07-11. + */ +fun RecyclerView.withMarginDecoration(sizeDp: Int, edgeFlags: Int) { + addItemDecoration(MarginItemDecoration(sizeDp, edgeFlags)) +} + +class MarginItemDecoration(sizeDp: Int, val edgeFlags: Int) : RecyclerView.ItemDecoration() { + + val sizePx = sizeDp.dpToPx + + override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { + super.getItemOffsets(outRect, view, parent, state) + if (edgeFlags and KAU_LEFT > 0) outRect.left += sizePx + if (edgeFlags and KAU_TOP > 0) outRect.top += sizePx + if (edgeFlags and KAU_RIGHT > 0) outRect.right += sizePx + if (edgeFlags and KAU_BOTTOM > 0) outRect.bottom += sizePx + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/StringHolder.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/StringHolder.kt deleted file mode 100644 index e70a2d1..0000000 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/StringHolder.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ca.allanwang.kau.utils - -import android.content.Context -import android.support.annotation.StringRes - -/** - * Created by Allan Wang on 2017-06-08. - */ -class StringHolder { - var text: String? = null - var textRes: Int = 0 - - constructor(@StringRes textRes: Int) { - this.textRes = textRes - } - - constructor(text: String) { - this.text = text - } - - fun getString(context: Context) = context.string(textRes, text) -} \ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt index a2043db..e8f385a 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt @@ -26,16 +26,16 @@ import java.text.DecimalFormat @DslMarker annotation class KauUtils -@KauUtils val Float.dpToPx: Float +@KauUtils inline val Float.dpToPx: Float get() = this * Resources.getSystem().displayMetrics.density -@KauUtils val Int.dpToPx: Int +@KauUtils inline val Int.dpToPx: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt() -@KauUtils val Float.pxToDp: Float +@KauUtils inline val Float.pxToDp: Float get() = this / Resources.getSystem().displayMetrics.density -@KauUtils val Int.pxToDp: Int +@KauUtils inline val Int.pxToDp: Int get() = (this / Resources.getSystem().displayMetrics.density).toInt() /** @@ -52,11 +52,9 @@ annotation class KauUtils */ @KauUtils fun Context.minuteToText(minutes: Long): String = with(minutes) { if (this < 0L) string(R.string.kau_none) - else if (this == 60L) string(R.string.kau_one_hour) - else if (this == 1440L) string(R.string.kau_one_day) - else if (this % 1440L == 0L) String.format(string(R.string.kau_x_days), this / 1440L) - else if (this % 60L == 0L) String.format(string(R.string.kau_x_hours), this / 60L) - else String.format(string(R.string.kau_x_minutes), this) + else if (this % 1440L == 0L) plural(R.plurals.kau_x_days, this / 1440L) + else if (this % 60L == 0L) plural(R.plurals.kau_x_hours, this / 60L) + else plural(R.plurals.kau_x_minutes, this) } @KauUtils fun Number.round(@IntRange(from = 1L) decimalCount: Int): String { diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt index 53d6d05..59ae204 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt @@ -1,9 +1,12 @@ +@file:Suppress("NOTHING_TO_INLINE") + package ca.allanwang.kau.utils +import android.animation.ValueAnimator import android.content.Context import android.graphics.Color -import android.graphics.Rect import android.support.annotation.ColorInt +import android.support.annotation.ColorRes import android.support.annotation.StringRes import android.support.annotation.TransitionRes import android.support.design.widget.FloatingActionButton @@ -58,6 +61,8 @@ import com.mikepenz.iconics.typeface.IIcon @KauUtils inline val View.isGone: Boolean get() = visibility == View.GONE +@KauUtils inline fun View.setBackgroundColorRes(@ColorRes color: Int) = setBackgroundColor(context.color(color)) + fun View.snackbar(text: String, duration: Int = Snackbar.LENGTH_LONG, builder: Snackbar.() -> Unit = {}): Snackbar { val snackbar = Snackbar.make(this, text, duration) snackbar.builder() @@ -85,12 +90,24 @@ fun FloatingActionButton.hideIf(hide: Boolean) = if (hide) hide() else show() @KauUtils fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): View = LayoutInflater.from(context).inflate(layoutId, this, attachToRoot) +/** + * Set left margin to a value in px + */ @KauUtils fun View.updateLeftMargin(margin: Int) = updateMargins(margin, KAU_LEFT) +/** + * Set top margin to a value in px + */ @KauUtils fun View.updateTopMargin(margin: Int) = updateMargins(margin, KAU_TOP) +/** + * Set right margin to a value in px + */ @KauUtils fun View.updateRightMargin(margin: Int) = updateMargins(margin, KAU_RIGHT) +/** + * Set bottom margin to a value in px + */ @KauUtils fun View.updateBottomMargin(margin: Int) = updateMargins(margin, KAU_BOTTOM) @KauUtils private fun View.updateMargins(margin: Int, flag: Int) { @@ -130,18 +147,11 @@ fun FloatingActionButton.hideIf(hide: Boolean) = if (hide) hide() else show() background = createSimpleRippleDrawable(foregroundColor, backgroundColor) } -@KauUtils val View.parentViewGroup: ViewGroup get() = parent as ViewGroup - -@KauUtils val View.parentVisibleHeight: Int - get() { - val r = Rect() - parentViewGroup.getWindowVisibleDisplayFrame(r) - return r.height() - } +@KauUtils inline val View.parentViewGroup: ViewGroup get() = parent as ViewGroup -val EditText.value: String get() = text.toString().trim() +inline val EditText.value: String get() = text.toString().trim() -val TextInputEditText.value: String get() = text.toString().trim() +inline val TextInputEditText.value: String get() = text.toString().trim() /** * Generates a recycler view with match parent and a linearlayoutmanager, since it's so commonly used @@ -153,4 +163,33 @@ fun Context.fullLinearRecycler(rvAdapter: RecyclerView.Adapter<*>? = null, confi if (rvAdapter != null) adapter = rvAdapter configs() } +} + +/** + * Animate a transition for a FloatinActionButton + * If it is not shown, the action will be invoked directly and the fab will be shown + * If it is already shown, scaling and alpha animations will be added to the action + */ +inline fun FloatingActionButton.transition(crossinline action: FloatingActionButton.() -> Unit) { + if (isHidden) { + action() + show() + } else { + var transitioned = false + ValueAnimator.ofFloat(1.0f, 0.0f, 1.0f).apply { + duration = 500L + addUpdateListener { + val x = it.animatedValue as Float + val scale = x * 0.3f + 0.7f + scaleX = scale + scaleY = scale + imageAlpha = (x * 255).toInt() + if (it.animatedFraction > 0.5f && !transitioned) { + transitioned = true + action() + } + } + start() + } + } } \ No newline at end of file -- cgit v1.2.3