Skip to content

Commit

Permalink
Merge pull request #150 from natura-cosmeticos/icon-button-component
Browse files Browse the repository at this point in the history
[DSY-1332] - Icon button component
  • Loading branch information
mlcsouza authored Dec 11, 2020
2 parents c1164b1 + 8c609c2 commit 0ad7a18
Show file tree
Hide file tree
Showing 28 changed files with 811 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import android.view.WindowManager
import android.widget.ImageView
import androidx.appcompat.widget.Toolbar
import com.natura.android.R
import com.natura.android.ext.setVisibilityFromBoolean
import com.natura.android.extensions.setVisibilityFromBoolean
import com.natura.android.badge.BadgeDrawable

class AppBar(context: Context, attrs: AttributeSet) : Toolbar(context, attrs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class BadgeDrawable(
private fun setBadge() {
val icon = parent as LayerDrawable
icon.mutate()
icon.setDrawableByLayerId(R.id.ic_badge_placeholder, this)
icon.setDrawableByLayerId(R.id.badge_placeholder, this)
}

override fun draw(canvas: Canvas) {
Expand Down Expand Up @@ -57,9 +57,7 @@ class BadgeDrawable(
}
}

private fun drawBadgeWithText(
canvas: Canvas
) {
private fun drawBadgeWithText(canvas: Canvas) {
defineTextBounds(count.toString())
definePositionToDrawBadge(canvas)
}
Expand All @@ -68,24 +66,16 @@ class BadgeDrawable(
mTextPaint.getTextBounds(text, 0, count.toString().length, mTxtRect)
}

private fun definePositionToDrawBadge(
canvas: Canvas
) {
private fun definePositionToDrawBadge(canvas: Canvas) {
val bounds = bounds

val badgeWith = when {
count > 99 -> getDimenFromTheme(R.attr.sizeSemi)
count > 9 -> getDimenFromTheme(R.attr.sizeStandard)
else -> getDimenFromTheme(R.attr.sizeSmall)
}
val badgeWith = mTxtRect.width() + getDimenFromTheme(R.attr.spacingTiny).toInt()

context.resources.getDrawable(R.drawable.badge_rounded_rectangle, context.theme).apply {
setBounds(
bounds.right - badgeWith.toInt(),
bounds.right - badgeWith,
bounds.top,
bounds.right,
getDimenFromTheme(R.attr.sizeSmall).toInt()
)
mTxtRect.height() + getDimenFromTheme(R.attr.spacingMicro).toInt())
draw(canvas)

drawText(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.natura.android.extensions

import android.util.TypedValue

/**
* Get an alpha value converted to base 255. Its in interesting use
* this method when you want to access a opacity token defined at
* theme and use it programmatically to set alpha on a component
* that receives a value between 0 and 255 inclusive, with 0 being
* transparent and 255 being opaque
* @return an int with the corresponding alpha value
*/
fun TypedValue.getAlphaAsBase255(): Int {
return (this.float * 255).toInt()
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.natura.android.ext
package com.natura.android.extensions

import android.view.View

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package com.natura.android.iconButton

import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.content.res.getIntOrThrow
import androidx.core.content.res.getResourceIdOrThrow
import androidx.core.content.res.getStringOrThrow
import com.natura.android.R
import com.natura.android.badge.BadgeDrawable
import com.natura.android.exceptions.MissingThemeException
import com.natura.android.resources.getColorTokenFromTheme
import com.natura.android.resources.getIconResourceIdFromName

class IconButton @JvmOverloads constructor(
context: Context,
private val attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

private var iconButtonAttributesArray: TypedArray

private var iconColorResourceAttribute = 0
private var rippleDrawableResourceAttribute = 0

private var iconNameAttribute: String? = null
private var colorAttribute: Int? = null
private var notifyAttribute: Int = 0
private var enabledAttribute: Boolean = false

private val iconButton by lazy { findViewById<ImageView>(R.id.iconButtonIcon) }
private val iconButtonContainer by lazy { findViewById<ConstraintLayout>(R.id.iconButtonContainer) }
private val badgeContainer by lazy { findViewById<ImageView>(R.id.iconButtonBadgeContainer) }

init {
try {
View.inflate(context, R.layout.icon_button, this)
} catch (e: Exception) {
throw (MissingThemeException())
}

iconButtonAttributesArray = context.obtainStyledAttributes(attrs, R.styleable.IconButton)

getAttributes()
getAppereanceAttributesFromTheme()

configureAppearance()
configureNotification()
configureEnabled()

iconButtonAttributesArray.recycle()
}

private fun configureEnabled() {
isEnabled = enabledAttribute
}

private fun configureNotification() {
if (notifyAttribute > 0) {
badgeContainer.visibility = View.VISIBLE
BadgeDrawable(context, notifyAttribute, badgeContainer.drawable)
}
}

override fun setEnabled(enabled: Boolean) {
iconButton.isEnabled = enabled
if (!enabled) {
setDisabledColor()
}
super.setEnabled(enabled)
}

private fun setDisabledColor() {
iconButton.setColorFilter(getColorTokenFromTheme(context, R.attr.colorMediumEmphasis), android.graphics.PorterDuff.Mode.SRC_IN)
}

fun setIcon(icon: String?) {
icon?.apply {
val iconDrawableId = getIconResourceIdFromName(context, icon)
iconButton.setImageResource(iconDrawableId)
}
}

fun getIcon(): ImageView {
return iconButton
}

fun getBadge(): ImageView {
return badgeContainer
}

fun getColor(): Int? {
return colorAttribute
}

private fun getAttributes() {
getIconName()
getColorAttribute()
getEnabledAttribute()
getNotify()
}

private fun getNotify() {
notifyAttribute = iconButtonAttributesArray.getInteger(R.styleable.IconButton_notify, 0)
}

private fun getIconName() {
try {
iconNameAttribute = iconButtonAttributesArray.getStringOrThrow(R.styleable.IconButton_iconName)
} catch (e: Exception) {
throw (IllegalArgumentException("⚠️ ⚠️ Missing iconName required argument. You MUST set the icon name.", e))
}
}

private fun getColorAttribute() {
try {
colorAttribute = iconButtonAttributesArray.getIntOrThrow(R.styleable.IconButton_buttonColor)
} catch (e: Exception) {
throw (IllegalArgumentException("⚠️ ⚠️ Missing iconButton required argument. You MUST set the iconButton color.", e))
}
}

private fun getEnabledAttribute() {
enabledAttribute = iconButtonAttributesArray.getBoolean(R.styleable.IconButton_android_enabled, true)
}

private fun getAppereanceAttributesFromTheme() {
try {
when (colorAttribute) {
PRIMARY -> {
setColorAttribute(R.attr.iconButtonPrimary)
setDrawableRippleAttribute(R.attr.iconButtonPrimary)
}
DEFAULT -> {
setColorAttribute(R.attr.iconButtonDefault)
setDrawableRippleAttribute(R.attr.iconButtonDefault)
}
}
} catch (e: Exception) {
throw (MissingThemeException())
}
}

private fun setDrawableRippleAttribute(iconButtonStyleFromTheme: Int) {
context
.theme
.obtainStyledAttributes(
attrs,
R.styleable.IconButton,
iconButtonStyleFromTheme,
0
)
.apply {
rippleDrawableResourceAttribute = this.getResourceIdOrThrow(R.styleable.IconButton_rippleDrawable)
}
}

private fun setColorAttribute(attribute: Int) {
context
.theme
.obtainStyledAttributes(
attrs,
R.styleable.IconButton,
attribute,
0
)
.apply {
iconColorResourceAttribute = this.getResourceIdOrThrow(R.styleable.IconButton_iconColor)
}
}

private fun configureAppearance() {
setIcon(iconNameAttribute)
iconButton.setColorFilter(ContextCompat.getColor(context, iconColorResourceAttribute), android.graphics.PorterDuff.Mode.SRC_IN)
iconButtonContainer.background = resources.getDrawable(rippleDrawableResourceAttribute, context.theme)
}

companion object {
const val DEFAULT = 0
const val PRIMARY = 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import androidx.appcompat.widget.AppCompatTextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.natura.android.R
import com.natura.android.ext.setVisibilityFromBoolean
import com.natura.android.extensions.setVisibilityFromBoolean
import com.natura.android.tag.Tag

@SuppressLint("CustomViewStyleable")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.natura.android.resources

import android.content.Context
import com.natura.android.R
import com.natura.android.resources.ResourcesConstants.DRAWABLE_NOT_FOUND

fun getIconResourceIdFromName(context: Context, iconName: String): Int {
var drawableId = context.resources.getIdentifier(iconName.replace("-", "_"), "drawable", context.packageName)

if (drawableId == DRAWABLE_NOT_FOUND) {
drawableId = R.drawable.default_icon_outlined_default_mockup
}
return drawableId
}

object ResourcesConstants {
const val DRAWABLE_NOT_FOUND = 0
}
16 changes: 16 additions & 0 deletions designsystem/src/main/kotlin/com/natura/android/resources/Theme.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.natura.android.resources

import android.content.Context
import android.util.TypedValue

fun getColorTokenFromTheme(context: Context, attrColorId: Int): Int {
val value = TypedValue()
context.theme.resolveAttribute(attrColorId, value, true)
return value.data
}

fun getDimenFromTheme(context: Context, attributeName: Int): Float {
val typedValue = TypedValue()
context.theme.resolveAttribute(attributeName, typedValue, true)
return typedValue.getDimension(context.resources.displayMetrics)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.core.content.res.getStringOrThrow
import androidx.core.graphics.drawable.DrawableCompat
import com.natura.android.R
import com.natura.android.exceptions.MissingThemeException
import com.natura.android.resources.getIconResourceIdFromName
import com.natura.android.extensions.setAppearance

class Shortcut @JvmOverloads constructor(
Expand Down Expand Up @@ -65,22 +66,13 @@ class Shortcut @JvmOverloads constructor(

fun setIcon(icon: String?) {
icon?.apply {
val drawableId = context.resources.getIdentifier(icon.replace("-", "_"), "drawable", context.packageName)

if (drawableId == ICON_NOT_FOUND) {
configDefaultIconIfEmpty()
} else {
iconContainer.setImageResource(drawableId)
}
val drawableId = getIconResourceIdFromName(context, icon)

iconContainer.setImageResource(drawableId)
iconContainer.setColorFilter(ContextCompat.getColor(context, iconColorResourceAttribute), android.graphics.PorterDuff.Mode.SRC_IN)
}
}

private fun configDefaultIconIfEmpty() {
iconContainer.setImageResource(R.drawable.default_icon_outlined_default_mockup)
}

fun getIcon(): ImageView {
return iconContainer
}
Expand Down Expand Up @@ -184,7 +176,6 @@ class Shortcut @JvmOverloads constructor(
}

companion object {
const val ICON_NOT_FOUND = 0
const val OUTLINED = 0
const val CONTAINED = 1
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M9.65 17.25A0.46 0.46 0 0 1 9.3 17.1a0.48 0.48 0 0 1 0-0.7l4.35-4.41l-4.35-4.4A0.5 0.5 0 1 1 10 6.9l4.72 4.72c0.09 0.1 0.14 0.22 0.14 0.35a0.64 0.64 0 0 1-0.14 0.4L10 17.09a0.48 0.48 0 0 1-0.35 0.17z"/>
</vector>
3 changes: 1 addition & 2 deletions designsystem/src/main/res/drawable/icon_base_badge.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
android:gravity="right" />

<item
android:id="@+id/ic_badge_placeholder"
android:id="@+id/badge_placeholder"
android:drawable="@android:color/transparent" />

</layer-list>
6 changes: 6 additions & 0 deletions designsystem/src/main/res/drawable/icon_button_base_badge.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/badge_placeholder"
android:drawable="@android:color/transparent" />
</layer-list>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorHighlightOpacity05">
<item
android:id="@android:id/mask"
android:gravity="center">
<shape android:shape="oval">
<solid android:color="@android:color/white"/>
</shape>
</item>
</ripple>
Loading

0 comments on commit 0ad7a18

Please sign in to comment.