Skip to content

Commit

Permalink
Merge pull request #123 from natura-cosmeticos/app-bar-top
Browse files Browse the repository at this point in the history
DSY-1202 - App Bar Top
  • Loading branch information
mlcsouza authored Jul 24, 2020
2 parents 5c653b8 + ec66b36 commit f8c1080
Show file tree
Hide file tree
Showing 13 changed files with 226 additions and 189 deletions.
149 changes: 86 additions & 63 deletions designsystem/src/main/kotlin/com/natura/android/appbar/AppBar.kt
Original file line number Diff line number Diff line change
@@ -1,101 +1,124 @@
package com.natura.android.appbar

import android.app.Activity
import android.content.Context
import android.graphics.Color
import android.os.Build
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import androidx.appcompat.widget.Toolbar
import com.natura.android.R
import kotlin.math.sqrt
import com.natura.android.ext.setVisibilityFromBoolean
import com.natura.android.badge.BadgeDrawable

// WIP
class AppBar(context: Context, attrs: AttributeSet) : Toolbar(context, attrs) {
var color: Int

private lateinit var badgeDrawable: BadgeDrawable
private var showLogo: Boolean
private val logo: ImageView

init {
context
.theme
.obtainStyledAttributes(attrs, R.styleable.AppBar, 0, 0)
.apply {
try {
color = this.getInt(R.styleable.AppBar_appBarType, 0)
} finally {
recycle()
}
}
}
logo = createLogo(context, attrs)

override fun onAttachedToWindow() {
super.onAttachedToWindow()
setStatusBarStyle()
val typedValue = context.obtainStyledAttributes(attrs, R.styleable.AppBar)
showLogo = typedValue.getBoolean(R.styleable.AppBar_showLogo, false)
setLogoVisibility()

this.setBackgroundColor(getThemeColor(getMainColorFromAttrs()))
this.setTitleTextColor(getThemeColor(getMainOnColorFromAttrs()))
addView(logo)

if (isOnColorTooLight(getThemeColor(getMainOnColorFromAttrs()))) {
setStatusBarIconsLighter()
} else {
setStatusBarIconsDarker()
contentInsetStartWithNavigation = 0
elevation = getElevationFromTheme(context)

typedValue.recycle()
}

private fun setLogoVisibility() {
logo.setVisibilityFromBoolean(showLogo)

if (showLogo) {
title = ""
}
}

private fun isOnColorTooLight(color: Int): Boolean {
val rgb = intArrayOf(Color.red(color), Color.green(color), Color.blue(color))
val brightness = sqrt(rgb[0] * rgb[0] * .241 + (rgb[1] * rgb[1] * .691) + rgb[2] * rgb[2] * .068).toInt()
return brightness >= 200
fun showLogo() {
showLogo = true
setLogoVisibility()
}

private fun setStatusBarStyle() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
fun hideLogo() {
showLogo = false
setLogoVisibility()
}

fun addMenuIconBadge(menuIcon: Drawable, initBadgeValue: Int) {
badgeDrawable = BadgeDrawable(context, initBadgeValue, menuIcon)
}

val context = context as Activity
val window = context.window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = getThemeColor(getMainColorFromAttrs())
fun updateBadgeValue(value: Int) {
if (this::badgeDrawable.isInitialized) {
badgeDrawable.updateBadgeDrawable(value)
}
}

private fun getMainColorFromAttrs(): Int {
return when (color) {
1 -> R.attr.colorPrimary
2 -> R.attr.colorSecondary
3 -> android.R.color.transparent
else -> R.attr.colorSurface
}
private fun createLogo(context: Context, attrs: AttributeSet): ImageView {
val imageView = ImageView(context)
imageView.setImageResource(getLogoResId(context, attrs))
val logoWidth = getLogoWidthFromTheme(context)
imageView.layoutParams =
LayoutParams(logoWidth, ViewGroup.LayoutParams.WRAP_CONTENT, getLogoAlign(context))
imageView.visibility = View.GONE
return imageView
}

private fun getMainOnColorFromAttrs(): Int {
return when (color) {
1 -> R.attr.colorOnPrimary
2 -> R.attr.colorOnSecondary
3 -> R.attr.colorOnSurface
else -> R.attr.colorOnSurface
private fun getLogoAlign(context: Context): Int {
return if (getWindowWidthInPx(context) < MINIMUM_SCREEN_SIZE_FOR_CENTRALIZED_LOGO) {
Gravity.START
} else {
Gravity.CENTER
}
}

private fun getThemeColor(colorAttr: Int): Int {
val outValue = TypedValue()
context.theme.resolveAttribute(colorAttr, outValue, true)
return outValue.data
private fun getLogoWidthFromTheme(context: Context): Int {
val typedValue = TypedValue()
if (context.theme.resolveAttribute(R.attr.sizeHugeX, typedValue, true)) {
return TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)
}

return ViewGroup.LayoutParams.WRAP_CONTENT
}

private fun setStatusBarIconsDarker() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val context = context as Activity
val window = context.window
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
private fun getElevationFromTheme(context: Context): Float {
val typedValue = TypedValue()
if (context.theme.resolveAttribute(R.attr.elevation02, typedValue, true)) {
return TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics).toFloat()
}

return 0f
}

private fun setStatusBarIconsLighter() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val context = context as Activity
val window = context.window
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
private fun getLogoResId(context: Context, attrs: AttributeSet): Int {
val typedValue = context.theme
.obtainStyledAttributes(attrs, intArrayOf(R.attr.logoHorizontal), 0, 0)
return typedValue.getResourceId(0, 0)
}

private fun getWindowWidthInPx(context: Context): Int {
return try {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val metrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(metrics)

metrics.widthPixels
} catch (ex: Exception) {
MINIMUM_SCREEN_SIZE_FOR_CENTRALIZED_LOGO
}
}

companion object {
private const val MINIMUM_SCREEN_SIZE_FOR_CENTRALIZED_LOGO = 361
}
}
20 changes: 0 additions & 20 deletions designsystem/src/main/res/layout/custom_app_bar.xml

This file was deleted.

1 change: 1 addition & 0 deletions designsystem/src/main/res/menu/custom_menu.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
android:icon="@drawable/icon_base_badge"
android:title="notification"
app:showAsAction="always" />

</menu>
7 changes: 1 addition & 6 deletions designsystem/src/main/res/values/ds_attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@
</declare-styleable>

<declare-styleable name="AppBar" >
<attr name="appBarType" format="enum">
<enum name="appBarDefault" value="0"/>
<enum name="appBarPrimary" value="1"/>
<enum name="appBarSecondary" value="2"/>
<enum name="appBarInherit" value="3"/>
</attr>
<attr name="showLogo" format="boolean"/>
</declare-styleable>

<declare-styleable name="ds_menu">
Expand Down
7 changes: 7 additions & 0 deletions designsystem/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,15 @@
<item name="android:layout_height">@dimen/ds_toolbar_height</item>
<item name="navigationIcon">@drawable/outlined_navigation_directionright</item>
<item name="collapseIcon">@drawable/outlined_navigation_directionright</item>
<item name="actionButtonStyle">@style/ActionButton</item>
</style>

<style name="Theme.DS.Toolbar" parent="ThemeOverlay.MaterialComponents.Toolbar.Primary">
<item name="colorOnPrimary">?colorOnSurface</item>
<item name="colorControlNormal">?colorOnSurface</item>
<item name="navigationIcon">@drawable/outlined_navigation_directionright</item>
<item name="collapseIcon">@drawable/outlined_navigation_directionright</item>
<item name="actionButtonStyle">@style/ActionButton</item>
</style>

<style name="Widget.DS.Toolbar.Primary" parent="Widget.DS.AppBarTop">
Expand All @@ -179,6 +181,11 @@
<item name="titleTextColor">?colorOnSecondary</item>
</style>

<style name="ActionButton" parent="@android:style/Widget.ActionButton">
<item name="paddingEnd">?spacingTiny</item>
<item name="paddingStart">?spacingTiny</item>
</style>

<!-- SELECTION CONTROL STYLES -->
<style name="Widget.DS.RadioButton.V23" parent="Widget.AppCompat.CompoundButton.RadioButton">
<item name="buttonTint">@color/color_selector_primary_v23</item>
Expand Down
57 changes: 48 additions & 9 deletions doc/app-bar-top.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
# App Bar Top Component - Default variant

### What is it?
App Bar Top is the Android native Action bar with some styles to match Nat DS appereance.
App Bar Top is the Android native Action bar with some customizations to match Nat DS appearance and integrate with other components, such as badge and logo.

### Why should I use it?
This component can help you to show some infos and/or actions on the top of the screen
This component can help you to show some info and/or actions on the top of the screen

### When should I use it?
Every time you want to configure an quick acess to some basic infos or actions
Every time you want to configure an quick acess to some basic info or actions

### How to use it?
If you need to configure just a basic App Bar, with title and some simple actions, just configure at your view the base theme. You can check how to do it [here](getting-started.md).
Add the app bar component in your xml layout file
Important! Do not use the default theme, use the NoActionBar variant. You can check more info [Getting Started guide](getting-started.md).
```android
<com.natura.android.appbar.AppBar
android:id="@+id/appBar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?toolbarDefaultTheme"/>
```
After that, the App Bar will be displayed at the top of the screen and in the root view code, you can add some basic actions:

```android
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -28,11 +37,41 @@ To configure the back button action:
onBackPressed()
return true
}
```

```
#### AppBar with centralized brand logo
To use the app bar with logo component just add the attribute showLogo in the xml file like below:
```android
<com.natura.android.appbar.AppBar
android:id="@+id/appBar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?toolbarDefaultTheme"
app:showLogo="true"/>
```
If you prefer you can do it programmatically using the methods below in the root view code:
```android
appBar.showLogo()
appBar.hideLogo()
```
#### AppBar with badge in action button
The AppBar component abstract the logic to configure the badge component to work in the action button.
Example:
```android
notificationMenuItem = menu?.findItem(R.id.ic_notification)
notificationMenuItem?.let {
appBar.addMenuIconBadge(it.icon, mCount)
}
```
To update badge value:
```android
appBar.updateBadgeValue(mCount)
```

However if you need an more complex App Bar Top, you can use Android Toolbar component with the DS theme applied.
Important! If you are adding the toolbar component at you xml layou file, DO NOT use the default theme, use the NoActionBar variant. You can check more infos [here](getting-started.md).
### How create a customized App Bar
If you need an more customize App Bar Top, you can use Android Toolbar component with the DS theme applied.
Important! If you are adding the toolbar component at you xml layout file, DO NOT use the default theme, use the NoActionBar variant. You can check more info [Getting Started guide](getting-started.md).

```android
<com.google.android.material.appbar.AppBarLayout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class MainActivityFunctionalTests {
onView(withId(R.id.btnAppbar)).perform(scrollTo())
onView(withId(R.id.btnAppbar)).perform(click())

onView(withText("App Bar Top")).check(matches(isDisplayed()))
onView(withId(R.id.appBar)).check(matches(isDisplayed()))
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.natura.android.sample.components

import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.natura.android.sample.R
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class AppBarActivityFunctionalTest {

private lateinit var appBarActivityTest: ActivityScenario<AppBarActivity>

@Before
fun setup() {
appBarActivityTest = ActivityScenario.launch(AppBarActivity::class.java)
}

@Test
fun addAndIncrementBadge() {
onView(withId(R.id.btnIncrement)).perform(click())
onView(withId(R.id.btnIncrement)).perform(click())

onView(withId(R.id.appBar)).check(matches(isDisplayed()))
}

@Test
fun showSearchBar() {
onView(withId(R.id.searchMenuBtn)).perform(click())

onView(withId(R.id.appBar)).check(matches(isDisplayed()))
}
}
Loading

0 comments on commit f8c1080

Please sign in to comment.