Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP ANDDEP-1045 Added utility class for switching status bar color automa… #6

Draft
wants to merge 1 commit into
base: dev/G-0.5.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions buildSrc/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -2105,5 +2105,34 @@
"samples": [],
"has_mirror": false,
"mirror_repo": ""
},
{
"id": "common-tools",
"version": "0.5.0",
"unstable_version": 0,
"stable": false,
"dir": "common-tools",
"libs": [
{
"name": "status-bar-switcher",
"dir": "lib-status-bar-switcher",
"artifact_name": "status-bar-switcher",
"third_party_dependencies": [
{
"name": "androidx.palette:palette-ktx",
"type": "implementation"
}
],
"android_standard_dependencies": []
}
],
"samples": [
{
"name": "common-tools-sample",
"dir": "sample"
}
],
"has_mirror": false,
"mirror_repo": ""
}
]
1 change: 1 addition & 0 deletions buildSrc/config.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ ext {
"androidx.core:core" : "1.2.0", //https://bit.ly/2yYY82h
"androidx.core:core-ktx" : "1.2.0", //http://bit.ly/2qY7ffW
"androidx.constraintlayout:constraintlayout" : "2.0.0-beta4", //http://bit.ly/2S1WoNg
"androidx.palette:palette-ktx" : "1.0.0", //https://bit.ly/30afhnJ
"androidx.appcompat:appcompat" : "1.1.0", //http://bit.ly/2zjueqh
"androidx.lifecycle:lifecycle-runtime-ktx" : "2.2.0", //https://bit.ly/3anEaR2
"androidx.multidex:multidex" : "2.0.1", //http://bit.ly/2r6uX9G
Expand Down
6 changes: 6 additions & 0 deletions common-tools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Common tools

## Модули

- [Status bar switcher](lib-status-bar-switcher)
- [Пример](sample)
7 changes: 7 additions & 0 deletions common-tools/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Common tools Release Notes

- [0.5.0-alpha](#050-alpha)

## 0.5.0-alpha
##### Common tools
* TODO
1 change: 1 addition & 0 deletions common-tools/lib-status-bar-switcher/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
23 changes: 23 additions & 0 deletions common-tools/lib-status-bar-switcher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Status bar switcher

Сущность для автоматического переключения цвета статус-бара в зависимости от цвета контента, находящегося под ним.

# Использование
[Пример использования для определенного экрана](../sample)

Для того, чтобы цвет статус-бара переключался автоматически для каждого экрана, нужно воспользоваться ActivityLifecycleCallbacks:

```kotlin
object : Application.ActivityLifecycleCallbacks() {

//...

override fun onActivityResumed(activity: Activity) {
statusBarSwitcher.attach(activity)
}

override fun onActivityStopped(activity: Activity) {
statusBarSwitcher.detach()
}
}
```
1 change: 1 addition & 0 deletions common-tools/lib-status-bar-switcher/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apply from: "$rootDir/buildSrc/baseBuild.gradle"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="ru.surfstudio.android.common.tools.statusbarswitcher" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
Copyright (c) 2020-present, SurfStudio LLC.

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.
*/
package ru.surfstudio.android.common.tools.statusbarswitcher

import android.app.Activity
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Rect
import android.os.AsyncTask
import android.os.Build
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewTreeObserver
import androidx.annotation.ColorInt
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.palette.graphics.Palette
import kotlin.math.sqrt

/**
* Helper class for switching status bar color automatically.
*
* Status bar color calculation based on the underlying content brightness.
*/
class StatusBarSwitcher(private val displayMetrics: DisplayMetrics) {

private val colorFilter = Palette.Filter { _, _ -> true }

private val width: Int
get() = displayMetrics.widthPixels

private var attachedActivity: Activity? = null

private val decorView: View?
get() = attachedActivity?.window?.decorView

private var statusBarHeight: Int = 0

private val statusBarHeightListener = OnApplyWindowInsetsListener { _, insets ->
statusBarHeight = insets.stableInsetTop
insets
}

private var hasStatusBarListenerAttached: Boolean = false

private val onPreDrawListener = ViewTreeObserver.OnPreDrawListener {
if (statusBarHeight != 0) {
val bitmap = Bitmap.createBitmap(width, statusBarHeight, Bitmap.Config.RGB_565)
val canvas = Canvas(bitmap)
val statusBarRect = Rect(0, 0, width, statusBarHeight)
canvas.clipRect(statusBarRect)
decorView?.draw(canvas)

Palette.from(bitmap)
.clearFilters()
.addFilter(colorFilter)
.tryGenerate { palette ->
val dominantColor = palette.getDominantColor(Color.WHITE)
val isLightStatusBar = isBrightColor(dominantColor)
attachedActivity?.let { setLightStatusBar(it, isLightStatusBar) }
}
}
true
}

/**
* Sets status bar height manually.
*/
fun setStatusBarHeight(statusBarHeight: Int) {
this.statusBarHeight = statusBarHeight
}

/**
* Attaches [StatusBarSwitcher] to the [activity].
*
* @param shouldDetectStatusBarHeight defines whether status bar height should be calculated
* automatically.
*/
fun attach(
activity: Activity,
shouldDetectStatusBarHeight: Boolean = true
) {
val decorView = activity.window.decorView
decorView.viewTreeObserver.addOnPreDrawListener(onPreDrawListener)
if (shouldDetectStatusBarHeight) {
ViewCompat.setOnApplyWindowInsetsListener(decorView, statusBarHeightListener)
hasStatusBarListenerAttached = true
}
attachedActivity = activity
}

/**
* Detaches [StatusBarSwitcher] from the latest activity.
*/
fun detach() {
val activity = attachedActivity ?: return
attachedActivity = null

val decorView = activity.window.decorView ?: return
if (hasStatusBarListenerAttached) {
ViewCompat.setOnApplyWindowInsetsListener(decorView, null)
hasStatusBarListenerAttached = false
}
decorView.viewTreeObserver.removeOnPreDrawListener(onPreDrawListener)
}

/**
* Defines brightness of color.
*
* @return true, if color is bright; false otherwise.
*/
private fun isBrightColor(@ColorInt 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
)
return brightness >= 200
}

private fun Palette.Builder.tryGenerate(onGeneratedAction: (Palette) -> Unit): AsyncTask<Bitmap, Void, Palette> {
return this.generate { palette: Palette? ->
palette ?: return@generate
onGeneratedAction(palette)
}
}

companion object {

/**
* Toggles status bar color for the defined [activity].
*/
fun setLightStatusBar(activity: Activity, isLightStatusBar: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val window = activity.window
val decorView = window?.decorView ?: return
val systemUiVisibilityFlags = decorView.systemUiVisibility
decorView.systemUiVisibility = when {
isLightStatusBar -> systemUiVisibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
else -> systemUiVisibilityFlags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
}
}
}
}
1 change: 1 addition & 0 deletions common-tools/sample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
18 changes: 18 additions & 0 deletions common-tools/sample/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import ru.surfstudio.android.build.DependencyConfigurator

apply from: "$rootDir/buildSrc/androidSample.gradle"

android {
defaultConfig {
applicationId "ru.surfstudio.android.common.tools.sample"
}
}

dependencies {
DependencyConfigurator.projectImplementation(project, "status-bar-switcher")
DependencyConfigurator.projectImplementation(project, "sample-common")
DependencyConfigurator.projectImplementation(project, "imageloader")
DependencyConfigurator.projectImplementation(project, "easyadapter")

DependencyConfigurator.kapt(project, "com.google.dagger:dagger-compiler")
}
27 changes: 27 additions & 0 deletions common-tools/sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ru.surfstudio.android.common.tools.sample">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name="ru.surfstudio.android.sample.dagger.app.DefaultApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".screen.main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".screen.status_bar_switcher.StatusBarSwitcherActivityView"
android:theme="@style/AppTheme.NoActionBar" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ru.surfstudio.android.common.tools.sample.screen.main

import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import ru.surfstudio.android.common.tools.sample.R
import ru.surfstudio.android.common.tools.sample.screen.status_bar_switcher.StatusBarSwitcherActivityView

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setOnClickListener(open_status_bar_switcher_btn, StatusBarSwitcherActivityView::class.java)
}

private fun setOnClickListener(btn: Button, activityClass: Class<*>) {
btn.setOnClickListener {
startActivity(Intent(this, activityClass))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.surfstudio.android.common.tools.sample.screen.status_bar_switcher

import android.content.Context
import android.content.Intent
import ru.surfstudio.android.core.ui.navigation.activity.route.ActivityRoute

class StatusBarSwitcherActivityRoute : ActivityRoute() {

override fun prepareIntent(context: Context) =
Intent(context, StatusBarSwitcherActivityView::class.java)
}
Loading