mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-17 20:36:44 -06:00
[Gradle] Add NavLib 0.8.0 as local module
This commit is contained in:
parent
cefb0deba8
commit
fadf1d7754
@ -181,6 +181,7 @@ dependencies {
|
|||||||
implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
|
implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
|
||||||
|
|
||||||
// Szkolny.eu libraries/forks
|
// Szkolny.eu libraries/forks
|
||||||
|
implementation project(":navlib")
|
||||||
implementation "eu.szkolny:android-snowfall:1ca9ea2da3"
|
implementation "eu.szkolny:android-snowfall:1ca9ea2da3"
|
||||||
implementation "eu.szkolny:agendacalendarview:1.0.4"
|
implementation "eu.szkolny:agendacalendarview:1.0.4"
|
||||||
implementation "eu.szkolny:cafebar:5bf0c618de"
|
implementation "eu.szkolny:cafebar:5bf0c618de"
|
||||||
@ -191,7 +192,6 @@ dependencies {
|
|||||||
implementation "eu.szkolny.selective-dao:annotation:27f8f3f194"
|
implementation "eu.szkolny.selective-dao:annotation:27f8f3f194"
|
||||||
officialImplementation "eu.szkolny:ssl-provider:1.0.0"
|
officialImplementation "eu.szkolny:ssl-provider:1.0.0"
|
||||||
unofficialImplementation "eu.szkolny:ssl-provider:1.0.0"
|
unofficialImplementation "eu.szkolny:ssl-provider:1.0.0"
|
||||||
implementation "pl.szczodrzynski:navlib:0.8.0"
|
|
||||||
implementation "pl.szczodrzynski:numberslidingpicker:2921225f76"
|
implementation "pl.szczodrzynski:numberslidingpicker:2921225f76"
|
||||||
implementation "pl.szczodrzynski:recyclertablayout:700f980584"
|
implementation "pl.szczodrzynski:recyclertablayout:700f980584"
|
||||||
implementation "pl.szczodrzynski:tachyon:551943a6b5"
|
implementation "pl.szczodrzynski:tachyon:551943a6b5"
|
||||||
|
44
navlib-font/build.gradle
Normal file
44
navlib-font/build.gradle
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Mike Penz
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion setup.compileSdk
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion setup.minSdk
|
||||||
|
targetSdkVersion setup.targetSdk
|
||||||
|
consumerProguardFiles 'consumer-proguard-rules.pro'
|
||||||
|
versionCode 10
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
resValue "string", "NavLibFont_version", "${versionName}"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "com.mikepenz:iconics-typeface-api:5.3.0-b01"
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
|
}
|
1
navlib-font/consumer-proguard-rules.pro
Normal file
1
navlib-font/consumer-proguard-rules.pro
Normal file
@ -0,0 +1 @@
|
|||||||
|
-keep class com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont { *; }
|
18
navlib-font/gradle.properties
Normal file
18
navlib-font/gradle.properties
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2019 Mike Penz
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
POM_NAME=Android-Iconics NavLibFont Typeface Library
|
||||||
|
POM_ARTIFACT_ID=navlibfont-typeface
|
||||||
|
POM_PACKAGING=aar
|
18
navlib-font/src/main/AndroidManifest.xml
Normal file
18
navlib-font/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2019 Mike Penz
|
||||||
|
~
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="com.mikepenz.iconics.typeface.library.navlibfont" />
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Mike Penz
|
||||||
|
*
|
||||||
|
* 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 com.mikepenz.iconics.typeface.library.navlibfont
|
||||||
|
|
||||||
|
import com.mikepenz.iconics.typeface.IIcon
|
||||||
|
import com.mikepenz.iconics.typeface.ITypeface
|
||||||
|
import java.util.LinkedList
|
||||||
|
|
||||||
|
@Suppress("EnumEntryName")
|
||||||
|
object NavLibFont : ITypeface {
|
||||||
|
|
||||||
|
override val fontRes: Int
|
||||||
|
get() = R.font.navlibfont_font_v1_0
|
||||||
|
|
||||||
|
override val characters: Map<String, Char> by lazy {
|
||||||
|
Icon.values().associate { it.name to it.character }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val mappingPrefix: String
|
||||||
|
get() = "nav"
|
||||||
|
|
||||||
|
override val fontName: String
|
||||||
|
get() = "NavLibFont"
|
||||||
|
|
||||||
|
override val version: String
|
||||||
|
get() = "1.0"
|
||||||
|
|
||||||
|
override val iconCount: Int
|
||||||
|
get() = characters.size
|
||||||
|
|
||||||
|
override val icons: List<String>
|
||||||
|
get() = characters.keys.toCollection(LinkedList())
|
||||||
|
|
||||||
|
override val author: String
|
||||||
|
get() = "Kuba Szczodrzyński"
|
||||||
|
|
||||||
|
override val url: String
|
||||||
|
get() = "https://github.com/kuba2k2/NavLib"
|
||||||
|
|
||||||
|
override val description: String
|
||||||
|
get() = ""
|
||||||
|
|
||||||
|
override val license: String
|
||||||
|
get() = ""
|
||||||
|
|
||||||
|
override val licenseUrl: String
|
||||||
|
get() = ""
|
||||||
|
|
||||||
|
override fun getIcon(key: String): IIcon = Icon.valueOf(key)
|
||||||
|
|
||||||
|
enum class Icon constructor(override val character: Char) : IIcon {
|
||||||
|
nav_dots_vertical('\ue801'),
|
||||||
|
nav_menu('\ue800'),
|
||||||
|
nav_sort_ascending('\ue803'),
|
||||||
|
nav_sort_descending('\ue802');
|
||||||
|
|
||||||
|
override val typeface: ITypeface by lazy { NavLibFont }
|
||||||
|
}
|
||||||
|
}
|
BIN
navlib-font/src/main/res/font/navlibfont_font_v1_0.ttf
Normal file
BIN
navlib-font/src/main/res/font/navlibfont_font_v1_0.ttf
Normal file
Binary file not shown.
20
navlib-font/src/main/res/values/font_addon.xml
Normal file
20
navlib-font/src/main/res/values/font_addon.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2019 Mike Penz
|
||||||
|
~
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<string name="define_font_NavLibFont">com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont</string>
|
||||||
|
</resources>
|
31
navlib-font/src/main/res/values/font_description.xml
Normal file
31
navlib-font/src/main/res/values/font_description.xml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2019 Mike Penz
|
||||||
|
~
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<string name="define_FontAwesome">year;author;libraryName;libraryWebsite</string>
|
||||||
|
<string name="library_FontAwesome_author">Kuba Szczodrzyński</string>
|
||||||
|
<string name="library_FontAwesome_authorWebsite">https://github.com/kuba2k2/NavLib</string>
|
||||||
|
<string name="library_FontAwesome_libraryName">NavLibFont</string>
|
||||||
|
<string name="library_FontAwesome_libraryDescription"></string>
|
||||||
|
<string name="library_FontAwesome_libraryWebsite">https://github.com/kuba2k2/NavLib</string>
|
||||||
|
<string name="library_FontAwesome_libraryVersion">@string/NavLibFont_version</string>
|
||||||
|
<string name="library_FontAwesome_licenseId"></string>
|
||||||
|
<string name="library_FontAwesome_isOpenSource">true</string>
|
||||||
|
<string name="library_FontAwesome_repositoryLink">https://github.com/kuba2k2/NavLib</string>
|
||||||
|
<!-- Custom variables section -->
|
||||||
|
<string name="library_FontAwesome_year">2018</string>
|
||||||
|
</resources>
|
55
navlib/build.gradle
Normal file
55
navlib/build.gradle
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion setup.compileSdk
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion setup.minSdk
|
||||||
|
targetSdkVersion setup.targetSdk
|
||||||
|
versionCode release.versionCode
|
||||||
|
versionName release.versionName
|
||||||
|
|
||||||
|
consumerProguardFiles 'consumer-rules.pro'
|
||||||
|
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
dataBinding = true
|
||||||
|
}
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
exclude 'META-INF/library-core_release.kotlin_module'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||||
|
implementation "androidx.core:core-ktx:1.3.2"
|
||||||
|
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||||
|
implementation "com.google.android.material:material:1.3.0"
|
||||||
|
|
||||||
|
api "com.mikepenz:materialize:1.2.1"
|
||||||
|
api "com.mikepenz:materialdrawer:8.3.3"
|
||||||
|
api "com.mikepenz:iconics-core:5.3.0-b01"
|
||||||
|
api "com.mikepenz:itemanimators:1.1.0"
|
||||||
|
|
||||||
|
compileOnly "pl.droidsonroids.gif:android-gif-drawable:1.2.15"
|
||||||
|
|
||||||
|
implementation "com.balysv:material-ripple:1.0.2"
|
||||||
|
|
||||||
|
implementation project(":navlib-font")
|
||||||
|
}
|
1
navlib/consumer-rules.pro
Normal file
1
navlib/consumer-rules.pro
Normal file
@ -0,0 +1 @@
|
|||||||
|
-keep class androidx.drawerlayout.widget.DrawerLayout { *; }
|
23
navlib/proguard-rules.pro
vendored
Normal file
23
navlib/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in init.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
|
-keep class androidx.drawerlayout.widget.DrawerLayout { *; }
|
2
navlib/src/main/AndroidManifest.xml
Normal file
2
navlib/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="pl.szczodrzynski.navlib" />
|
170
navlib/src/main/java/pl/szczodrzynski/navlib/Anim.kt
Normal file
170
navlib/src/main/java/pl/szczodrzynski/navlib/Anim.kt
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.AccelerateInterpolator
|
||||||
|
import android.view.animation.AlphaAnimation
|
||||||
|
import android.view.animation.Animation
|
||||||
|
import android.view.animation.DecelerateInterpolator
|
||||||
|
import android.view.animation.ScaleAnimation
|
||||||
|
import android.view.animation.Transformation
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
|
||||||
|
object Anim {
|
||||||
|
fun expand(v: View, duration: Int?, animationListener: Animation.AnimationListener?) {
|
||||||
|
v.measure(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||||
|
val targetHeight = v.measuredHeight
|
||||||
|
//Log.d("Anim", "targetHeight="+targetHeight);
|
||||||
|
v.visibility = View.VISIBLE
|
||||||
|
v.layoutParams.height = 0
|
||||||
|
val a = object : Animation() {
|
||||||
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
||||||
|
v.layoutParams.height = if (interpolatedTime == 1.0f)
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT//(int)(targetHeight * interpolatedTime)
|
||||||
|
else
|
||||||
|
(targetHeight * interpolatedTime).toInt()
|
||||||
|
v.requestLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun willChangeBounds(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (duration == null) {
|
||||||
|
a.duration = (targetHeight.toFloat() / v.context.resources.displayMetrics.density).toInt().toLong()
|
||||||
|
} else {
|
||||||
|
a.duration = duration as Long
|
||||||
|
}
|
||||||
|
if (animationListener != null) {
|
||||||
|
a.setAnimationListener(animationListener)
|
||||||
|
}
|
||||||
|
v.startAnimation(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collapse(v: View, duration: Int?, animationListener: Animation.AnimationListener?) {
|
||||||
|
val initialHeight = v.measuredHeight
|
||||||
|
val a = object : Animation() {
|
||||||
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
||||||
|
if (interpolatedTime == 1.0f) {
|
||||||
|
v.visibility = View.GONE
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.layoutParams.height = initialHeight - (initialHeight.toFloat() * interpolatedTime).toInt()
|
||||||
|
v.requestLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun willChangeBounds(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (duration == null) {
|
||||||
|
a.duration = (initialHeight.toFloat() / v.context.resources.displayMetrics.density).toInt().toLong()
|
||||||
|
} else {
|
||||||
|
a.duration = duration as Long
|
||||||
|
}
|
||||||
|
if (animationListener != null) {
|
||||||
|
a.setAnimationListener(animationListener)
|
||||||
|
}
|
||||||
|
v.startAnimation(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fadeIn(v: View, duration: Int?, animationListener: Animation.AnimationListener?) {
|
||||||
|
val fadeIn = AlphaAnimation(0f, 1f)
|
||||||
|
fadeIn.interpolator = DecelerateInterpolator() //add this
|
||||||
|
fadeIn.duration = duration!!.toLong()
|
||||||
|
fadeIn.setAnimationListener(object : Animation.AnimationListener {
|
||||||
|
override fun onAnimationStart(animation: Animation) {
|
||||||
|
v.visibility = View.VISIBLE
|
||||||
|
animationListener?.onAnimationStart(animation)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: Animation) {
|
||||||
|
animationListener?.onAnimationEnd(animation)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationRepeat(animation: Animation) {
|
||||||
|
animationListener?.onAnimationRepeat(animation)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
v.startAnimation(fadeIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fadeOut(v: View, duration: Int?, animationListener: Animation.AnimationListener?) {
|
||||||
|
val fadeOut = AlphaAnimation(1f, 0f)
|
||||||
|
fadeOut.interpolator = AccelerateInterpolator() //and this
|
||||||
|
fadeOut.duration = duration!!.toLong()
|
||||||
|
fadeOut.setAnimationListener(object : Animation.AnimationListener {
|
||||||
|
override fun onAnimationStart(animation: Animation) {
|
||||||
|
animationListener?.onAnimationStart(animation)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: Animation) {
|
||||||
|
v.visibility = View.INVISIBLE
|
||||||
|
animationListener?.onAnimationEnd(animation)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationRepeat(animation: Animation) {
|
||||||
|
animationListener?.onAnimationRepeat(animation)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
v.startAnimation(fadeOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scaleView(
|
||||||
|
v: View,
|
||||||
|
duration: Int?,
|
||||||
|
animationListener: Animation.AnimationListener?,
|
||||||
|
startScale: Float,
|
||||||
|
endScale: Float
|
||||||
|
) {
|
||||||
|
val anim = ScaleAnimation(
|
||||||
|
1f, 1f, // Start and end values for the X axis scaling
|
||||||
|
startScale, endScale, // Start and end values for the Y axis scaling
|
||||||
|
Animation.RELATIVE_TO_SELF, 0f, // Pivot point of X scaling
|
||||||
|
Animation.RELATIVE_TO_SELF, 0f
|
||||||
|
) // Pivot point of Y scaling
|
||||||
|
anim.fillAfter = true // Needed to keep the result of the animation
|
||||||
|
anim.duration = duration!!.toLong()
|
||||||
|
anim.setAnimationListener(object : Animation.AnimationListener {
|
||||||
|
override fun onAnimationStart(animation: Animation) {
|
||||||
|
animationListener?.onAnimationStart(animation)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: Animation) {
|
||||||
|
animationListener?.onAnimationEnd(animation)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAnimationRepeat(animation: Animation) {
|
||||||
|
animationListener?.onAnimationRepeat(animation)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
v.startAnimation(anim)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResizeAnimation(
|
||||||
|
private val mView: View,
|
||||||
|
private val mFromWidth: Float,
|
||||||
|
private val mFromHeight: Float,
|
||||||
|
private val mToWidth: Float,
|
||||||
|
private val mToHeight: Float
|
||||||
|
) : Animation() {
|
||||||
|
|
||||||
|
private val width: Float
|
||||||
|
private val height: Float
|
||||||
|
|
||||||
|
init {
|
||||||
|
width = mView.width.toFloat()
|
||||||
|
height = mView.height.toFloat()
|
||||||
|
duration = 300
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
||||||
|
val height = (mToHeight - mFromHeight) * interpolatedTime + mFromHeight
|
||||||
|
val width = (mToWidth - mFromWidth) * interpolatedTime + mFromWidth
|
||||||
|
val p = mView.layoutParams
|
||||||
|
p.width = (width * this.width).toInt()
|
||||||
|
p.height = (height * this.height).toInt()
|
||||||
|
mView.requestLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
109
navlib/src/main/java/pl/szczodrzynski/navlib/BadgeDrawable.java
Normal file
109
navlib/src/main/java/pl/szczodrzynski/navlib/BadgeDrawable.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package pl.szczodrzynski.navlib;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
public class BadgeDrawable extends Drawable {
|
||||||
|
|
||||||
|
private Paint mBadgePaint;
|
||||||
|
private Paint mBadgePaint1;
|
||||||
|
private Paint mTextPaint;
|
||||||
|
private Rect mTxtRect = new Rect();
|
||||||
|
|
||||||
|
private String mCount = "";
|
||||||
|
private boolean mWillDraw = false;
|
||||||
|
|
||||||
|
public BadgeDrawable(Context context) {
|
||||||
|
float mTextSize = context.getResources().getDimension(R.dimen.badge_text_size);
|
||||||
|
|
||||||
|
mBadgePaint = new Paint();
|
||||||
|
mBadgePaint.setColor(0xffff3d00);
|
||||||
|
mBadgePaint.setAntiAlias(true);
|
||||||
|
mBadgePaint.setStyle(Paint.Style.FILL);
|
||||||
|
/*mBadgePaint1 = new Paint();
|
||||||
|
mBadgePaint1.setColor(ContextCompat.getColor(context.getApplicationContext(), R.color.grey_ivory5));
|
||||||
|
mBadgePaint1.setAntiAlias(true);
|
||||||
|
mBadgePaint1.setStyle(Paint.Style.FILL);*/
|
||||||
|
|
||||||
|
mTextPaint = new Paint();
|
||||||
|
mTextPaint.setColor(Color.WHITE);
|
||||||
|
mTextPaint.setTypeface(Typeface.DEFAULT);
|
||||||
|
mTextPaint.setTextSize(mTextSize);
|
||||||
|
mTextPaint.setAntiAlias(true);
|
||||||
|
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!mWillDraw) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Rect bounds = getBounds();
|
||||||
|
float width = bounds.right - bounds.left;
|
||||||
|
float height = bounds.bottom - bounds.top;
|
||||||
|
|
||||||
|
// Position the badge in the top-right quadrant of the icon.
|
||||||
|
|
||||||
|
/*Using Math.max rather than Math.min */
|
||||||
|
|
||||||
|
float radius = ((Math.max(width, height) / 2)) / 2;
|
||||||
|
float centerX = (width - radius - 1) +5;
|
||||||
|
float centerY = radius -5;
|
||||||
|
if(mCount.length() <= 2){
|
||||||
|
// Draw badge circle.
|
||||||
|
//canvas.drawCircle(centerX, centerY, (int)(radius+7.5), mBadgePaint1);
|
||||||
|
canvas.drawCircle(centerX, centerY, (int)(radius+5.5), mBadgePaint);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//canvas.drawCircle(centerX, centerY, (int)(radius+8.5), mBadgePaint1);
|
||||||
|
canvas.drawCircle(centerX, centerY, (int)(radius+6.5), mBadgePaint);
|
||||||
|
// canvas.drawRoundRect(radius, radius, radius, radius, 10, 10, mBadgePaint);
|
||||||
|
}
|
||||||
|
// Draw badge count text inside the circle.
|
||||||
|
mTextPaint.getTextBounds(mCount, 0, mCount.length(), mTxtRect);
|
||||||
|
float textHeight = mTxtRect.bottom - mTxtRect.top;
|
||||||
|
float textY = centerY + (textHeight / 2f);
|
||||||
|
if(mCount.length() > 2)
|
||||||
|
canvas.drawText("99+", centerX, textY, mTextPaint);
|
||||||
|
else
|
||||||
|
canvas.drawText(mCount, centerX, textY, mTextPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sets the count (i.e notifications) to display.
|
||||||
|
*/
|
||||||
|
public void setCount(String count) {
|
||||||
|
mCount = count;
|
||||||
|
|
||||||
|
// Only draw a badge if there are notifications.
|
||||||
|
mWillDraw = !count.equalsIgnoreCase("0");
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(ColorFilter cf) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
return PixelFormat.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,278 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.*
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewOutlineProvider
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import com.mikepenz.materialdrawer.R
|
||||||
|
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||||
|
import pl.droidsonroids.gif.GifImageView
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [android.widget.ImageView] that draws its contents inside a mask and draws a border
|
||||||
|
* drawable on top. This is useful for applying a beveled look to image contents, but is also
|
||||||
|
* flexible enough for use with other desired aesthetics.
|
||||||
|
*/
|
||||||
|
open class BezelImageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : GifImageView(context, attrs, defStyle) {
|
||||||
|
private val mBlackPaint: Paint
|
||||||
|
private val mMaskedPaint: Paint
|
||||||
|
|
||||||
|
private var mBounds: Rect? = null
|
||||||
|
private var mBoundsF: RectF? = null
|
||||||
|
|
||||||
|
private val mMaskDrawable: Drawable?
|
||||||
|
private var mDrawCircularShadow = true
|
||||||
|
|
||||||
|
private var mDesaturateColorFilter: ColorMatrixColorFilter? = null
|
||||||
|
|
||||||
|
private val mSelectorAlpha = 150
|
||||||
|
private var mSelectorColor: Int = 0
|
||||||
|
private var mSelectorFilter: ColorFilter? = null
|
||||||
|
|
||||||
|
private var mCacheValid = false
|
||||||
|
private var mCacheBitmap: Bitmap
|
||||||
|
private var mCachedWidth: Int = 0
|
||||||
|
private var mCachedHeight: Int = 0
|
||||||
|
|
||||||
|
private var mIsPressed = false
|
||||||
|
private var mIsSelected: Boolean = false
|
||||||
|
|
||||||
|
private var mTempDesaturateColorFilter: ColorMatrixColorFilter? = null
|
||||||
|
private var mTempSelectorFilter: ColorFilter? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
// Attribute initialization
|
||||||
|
val a = context.obtainStyledAttributes(attrs, R.styleable.BezelImageView, defStyle, R.style.BezelImageView)
|
||||||
|
|
||||||
|
mMaskDrawable = a.getDrawable(R.styleable.BezelImageView_materialDrawerMaskDrawable)
|
||||||
|
if (mMaskDrawable != null) {
|
||||||
|
mMaskDrawable.callback = this
|
||||||
|
}
|
||||||
|
|
||||||
|
mDrawCircularShadow = a.getBoolean(R.styleable.BezelImageView_materialDrawerDrawCircularShadow, true)
|
||||||
|
|
||||||
|
mSelectorColor = a.getColor(R.styleable.BezelImageView_materialDrawerSelectorOnPress, 0)
|
||||||
|
|
||||||
|
a.recycle()
|
||||||
|
|
||||||
|
// Other initialization
|
||||||
|
mBlackPaint = Paint()
|
||||||
|
mBlackPaint.color = -0x1000000
|
||||||
|
|
||||||
|
mMaskedPaint = Paint()
|
||||||
|
mMaskedPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
|
||||||
|
|
||||||
|
// Always want a cache allocated.
|
||||||
|
mCacheBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
||||||
|
|
||||||
|
// Create a desaturate color filter for pressed state.
|
||||||
|
val cm = ColorMatrix()
|
||||||
|
cm.setSaturation(0f)
|
||||||
|
mDesaturateColorFilter = ColorMatrixColorFilter(cm)
|
||||||
|
|
||||||
|
//create a selectorFilter if we already have a color
|
||||||
|
if (mSelectorColor != 0) {
|
||||||
|
this.mSelectorFilter = PorterDuffColorFilter(Color.argb(mSelectorAlpha, Color.red(mSelectorColor), Color.green(mSelectorColor), Color.blue(mSelectorColor)), PorterDuff.Mode.SRC_ATOP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSizeChanged(w: Int, h: Int, old_w: Int, old_h: Int) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
if (mDrawCircularShadow) {
|
||||||
|
outlineProvider = CustomOutline(w, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(21)
|
||||||
|
private inner class CustomOutline internal constructor(internal var width: Int, internal var height: Int) : ViewOutlineProvider() {
|
||||||
|
|
||||||
|
override fun getOutline(view: View, outline: Outline) {
|
||||||
|
outline.setOval(0, 0, width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFrame(l: Int, t: Int, r: Int, b: Int): Boolean {
|
||||||
|
val changed = super.setFrame(l, t, r, b)
|
||||||
|
mBounds = Rect(0, 0, r - l, b - t).also {
|
||||||
|
mBoundsF = RectF(it)
|
||||||
|
|
||||||
|
if (mMaskDrawable != null) {
|
||||||
|
mMaskDrawable.bounds = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
mCacheValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
val bounds = mBounds ?: return
|
||||||
|
|
||||||
|
val width = bounds.width()
|
||||||
|
val height = bounds.height()
|
||||||
|
|
||||||
|
if (width == 0 || height == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mCacheValid || width != mCachedWidth || height != mCachedHeight || mIsSelected != mIsPressed) {
|
||||||
|
// Need to redraw the cache
|
||||||
|
if (width == mCachedWidth && height == mCachedHeight) {
|
||||||
|
// Have a correct-sized bitmap cache already allocated. Just erase it.
|
||||||
|
mCacheBitmap.eraseColor(0)
|
||||||
|
} else {
|
||||||
|
// Allocate a new bitmap with the correct dimensions.
|
||||||
|
mCacheBitmap.recycle()
|
||||||
|
|
||||||
|
mCacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
mCachedWidth = width
|
||||||
|
mCachedHeight = height
|
||||||
|
}
|
||||||
|
|
||||||
|
val cacheCanvas = Canvas(mCacheBitmap)
|
||||||
|
|
||||||
|
when {
|
||||||
|
mMaskDrawable != null -> {
|
||||||
|
val sc = cacheCanvas.save()
|
||||||
|
mMaskDrawable.draw(cacheCanvas)
|
||||||
|
if (mIsSelected) {
|
||||||
|
if (mSelectorFilter != null) {
|
||||||
|
mMaskedPaint.colorFilter = mSelectorFilter
|
||||||
|
} else {
|
||||||
|
mMaskedPaint.colorFilter = mDesaturateColorFilter
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mMaskedPaint.colorFilter = null
|
||||||
|
}
|
||||||
|
cacheCanvas.saveLayer(mBoundsF, mMaskedPaint, Canvas.ALL_SAVE_FLAG)
|
||||||
|
super.onDraw(cacheCanvas)
|
||||||
|
cacheCanvas.restoreToCount(sc)
|
||||||
|
}
|
||||||
|
mIsSelected -> {
|
||||||
|
val sc = cacheCanvas.save()
|
||||||
|
cacheCanvas.drawRect(0f, 0f, mCachedWidth.toFloat(), mCachedHeight.toFloat(), mBlackPaint)
|
||||||
|
if (mSelectorFilter != null) {
|
||||||
|
mMaskedPaint.colorFilter = mSelectorFilter
|
||||||
|
} else {
|
||||||
|
mMaskedPaint.colorFilter = mDesaturateColorFilter
|
||||||
|
}
|
||||||
|
cacheCanvas.saveLayer(mBoundsF, mMaskedPaint, Canvas.ALL_SAVE_FLAG)
|
||||||
|
super.onDraw(cacheCanvas)
|
||||||
|
cacheCanvas.restoreToCount(sc)
|
||||||
|
}
|
||||||
|
else -> super.onDraw(cacheCanvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw from cache
|
||||||
|
canvas.drawBitmap(mCacheBitmap, bounds.left.toFloat(), bounds.top.toFloat(), null)
|
||||||
|
|
||||||
|
//remember the previous press state
|
||||||
|
mIsPressed = isPressed
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
||||||
|
// Check for clickable state and do nothing if disabled
|
||||||
|
if (!this.isClickable) {
|
||||||
|
this.mIsSelected = false
|
||||||
|
return super.onTouchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set selected state based on Motion Event
|
||||||
|
when (event.action) {
|
||||||
|
MotionEvent.ACTION_DOWN -> this.mIsSelected = true
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_OUTSIDE, MotionEvent.ACTION_CANCEL -> this.mIsSelected = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redraw image and return super type
|
||||||
|
this.invalidate()
|
||||||
|
return super.dispatchTouchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun drawableStateChanged() {
|
||||||
|
super.drawableStateChanged()
|
||||||
|
if (mMaskDrawable != null && mMaskDrawable.isStateful) {
|
||||||
|
mMaskDrawable.state = drawableState
|
||||||
|
}
|
||||||
|
if (isDuplicateParentStateEnabled) {
|
||||||
|
ViewCompat.postInvalidateOnAnimation(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidateDrawable(who: Drawable) {
|
||||||
|
if (who === mMaskDrawable) {
|
||||||
|
invalidate()
|
||||||
|
} else {
|
||||||
|
super.invalidateDrawable(who)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun verifyDrawable(who: Drawable): Boolean {
|
||||||
|
return who === mMaskDrawable || super.verifyDrawable(who)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the color of the selector to be draw over the
|
||||||
|
* CircularImageView. Be sure to provide some opacity.
|
||||||
|
*
|
||||||
|
* @param selectorColor The color (including alpha) to set for the selector overlay.
|
||||||
|
*/
|
||||||
|
fun setSelectorColor(selectorColor: Int) {
|
||||||
|
this.mSelectorColor = selectorColor
|
||||||
|
this.mSelectorFilter = PorterDuffColorFilter(Color.argb(mSelectorAlpha, Color.red(mSelectorColor), Color.green(mSelectorColor), Color.blue(mSelectorColor)), PorterDuff.Mode.SRC_ATOP)
|
||||||
|
this.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun setImageDrawable(drawable: Drawable?) {
|
||||||
|
super.setImageDrawable(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setImageResource(resId: Int) {
|
||||||
|
super.setImageResource(resId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setImageBitmap(bm: Bitmap) {
|
||||||
|
super.setImageBitmap(bm)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setImageURI(uri: Uri?) {
|
||||||
|
if ("http" == uri?.scheme || "https" == uri?.scheme) {
|
||||||
|
DrawerImageLoader.instance.setImage(this, uri, null)
|
||||||
|
} else {
|
||||||
|
super.setImageURI(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disableTouchFeedback(disable: Boolean) {
|
||||||
|
if (disable) {
|
||||||
|
mTempDesaturateColorFilter = this.mDesaturateColorFilter
|
||||||
|
mTempSelectorFilter = this.mSelectorFilter
|
||||||
|
this.mSelectorFilter = null
|
||||||
|
this.mDesaturateColorFilter = null
|
||||||
|
} else {
|
||||||
|
if (mTempDesaturateColorFilter != null) {
|
||||||
|
this.mDesaturateColorFilter = mTempDesaturateColorFilter
|
||||||
|
}
|
||||||
|
if (mTempSelectorFilter != null) {
|
||||||
|
this.mSelectorFilter = mTempSelectorFilter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.mikepenz.iconics.typeface.IIcon
|
||||||
|
import com.mikepenz.materialdrawer.*
|
||||||
|
import com.mikepenz.materialdrawer.holder.StringHolder
|
||||||
|
import com.mikepenz.materialdrawer.model.AbstractDrawerItem
|
||||||
|
import com.mikepenz.materialdrawer.model.BaseDrawerItem
|
||||||
|
import com.mikepenz.materialdrawer.model.interfaces.*
|
||||||
|
import com.mikepenz.materialdrawer.util.getDrawerItem
|
||||||
|
import com.mikepenz.materialdrawer.util.updateItem
|
||||||
|
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
|
||||||
|
|
||||||
|
/*inline fun DrawerBuilder.withOnDrawerItemClickListener(crossinline listener: (view: View?, position: Int, drawerItem: IDrawerItem<*>) -> Boolean): DrawerBuilder {
|
||||||
|
return this.withOnDrawerItemClickListener(object : Drawer.OnDrawerItemClickListener {
|
||||||
|
override fun onItemClick(view: View?, position: Int, drawerItem: IDrawerItem<*>): Boolean {
|
||||||
|
return listener(view, position, drawerItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun DrawerBuilder.withOnDrawerItemLongClickListener(crossinline listener: (view: View, position: Int, drawerItem: IDrawerItem<*>) -> Boolean): DrawerBuilder {
|
||||||
|
return this.withOnDrawerItemLongClickListener(object : Drawer.OnDrawerItemLongClickListener {
|
||||||
|
override fun onItemLongClick(view: View, position: Int, drawerItem: IDrawerItem<*>): Boolean {
|
||||||
|
return listener(view, position, drawerItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun AccountHeaderBuilder.withOnAccountHeaderListener(crossinline listener: (view: View?, profile: IProfile<*>, current: Boolean) -> Boolean): AccountHeaderBuilder {
|
||||||
|
return this.withOnAccountHeaderListener(object : AccountHeader.OnAccountHeaderListener {
|
||||||
|
override fun onProfileChanged(view: View?, profile: IProfile<*>, current: Boolean): Boolean {
|
||||||
|
return listener(view, profile, current)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun AccountHeaderBuilder.withOnAccountHeaderItemLongClickListener(crossinline listener: (view: View, profile: IProfile<*>, current: Boolean) -> Boolean): AccountHeaderBuilder {
|
||||||
|
return this.withOnAccountHeaderItemLongClickListener(object : AccountHeader.OnAccountHeaderItemLongClickListener {
|
||||||
|
override fun onProfileLongClick(view: View, profile: IProfile<*>, current: Boolean): Boolean {
|
||||||
|
return listener(view, profile, current)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun AccountHeaderBuilder.withOnAccountHeaderProfileImageListener(
|
||||||
|
crossinline onClick: (
|
||||||
|
view: View,
|
||||||
|
profile: IProfile<*>,
|
||||||
|
current: Boolean
|
||||||
|
) -> Boolean,
|
||||||
|
crossinline onLongClick: (
|
||||||
|
view: View,
|
||||||
|
profile: IProfile<*>,
|
||||||
|
current: Boolean
|
||||||
|
) -> Boolean
|
||||||
|
): AccountHeaderBuilder {
|
||||||
|
return this.withOnAccountHeaderProfileImageListener(object : AccountHeader.OnAccountHeaderProfileImageListener {
|
||||||
|
override fun onProfileImageClick(view: View, profile: IProfile<*>, current: Boolean): Boolean {
|
||||||
|
return onClick(view, profile, current)
|
||||||
|
}
|
||||||
|
override fun onProfileImageLongClick(view: View, profile: IProfile<*>, current: Boolean): Boolean {
|
||||||
|
return onLongClick(view, profile, current)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun MiniDrawer.withOnMiniDrawerItemClickListener(crossinline listener: (view: View?, position: Int, drawerItem: IDrawerItem<*>, type: Int) -> Boolean): MiniDrawer {
|
||||||
|
return this.withOnMiniDrawerItemClickListener(object : MiniDrawer.OnMiniDrawerItemClickListener {
|
||||||
|
override fun onItemClick(view: View?, position: Int, drawerItem: IDrawerItem<*>, type: Int): Boolean {
|
||||||
|
return listener(view, position, drawerItem, type)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}*/
|
||||||
|
|
||||||
|
fun MaterialDrawerSliderView.updateBadge(identifier: Long, badge: StringHolder?) {
|
||||||
|
val drawerItem = getDrawerItem(identifier)
|
||||||
|
if (drawerItem is Badgeable) {
|
||||||
|
drawerItem.withBadge(badge)
|
||||||
|
updateItem(drawerItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Iconable> T.withIcon(icon: IIcon) = withIcon(pl.szczodrzynski.navlib.ImageHolder(icon))
|
121
navlib/src/main/java/pl/szczodrzynski/navlib/ImageHolder.kt
Normal file
121
navlib/src/main/java/pl/szczodrzynski/navlib/ImageHolder.kt
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.graphics.PorterDuffColorFilter
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.net.Uri
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
|
import com.mikepenz.iconics.typeface.IIcon
|
||||||
|
import com.mikepenz.iconics.utils.actionBar
|
||||||
|
import com.mikepenz.iconics.utils.sizeDp
|
||||||
|
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||||
|
import pl.droidsonroids.gif.GifDrawable
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mikepenz on 13.07.15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
open class ImageHolder : com.mikepenz.materialdrawer.holder.ImageHolder {
|
||||||
|
|
||||||
|
constructor(@DrawableRes iconRes: Int, colorFilter: Int?) : super(iconRes) {
|
||||||
|
this.colorFilter = colorFilter
|
||||||
|
}
|
||||||
|
constructor(iicon: IIcon) : super(null as Drawable?) {
|
||||||
|
this.iicon = iicon
|
||||||
|
}
|
||||||
|
constructor() : super()
|
||||||
|
constructor(url: String) : super(url)
|
||||||
|
constructor(uri: Uri) : super(uri)
|
||||||
|
constructor(icon: Drawable?) : super(icon)
|
||||||
|
constructor(bitmap: Bitmap) : super(bitmap)
|
||||||
|
constructor(iconRes: Int) : super(iconRes)
|
||||||
|
|
||||||
|
var iicon: IIcon? = null
|
||||||
|
@ColorInt
|
||||||
|
var colorFilter: Int? = null
|
||||||
|
var colorFilterMode: PorterDuff.Mode = PorterDuff.Mode.DST_OVER
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets an existing image to the imageView
|
||||||
|
*
|
||||||
|
* @param imageView
|
||||||
|
* @param tag used to identify imageViews and define different placeholders
|
||||||
|
* @return true if an image was set
|
||||||
|
*/
|
||||||
|
override fun applyTo(imageView: ImageView, tag: String?): Boolean {
|
||||||
|
val ii = iicon
|
||||||
|
|
||||||
|
if (uri != null) {
|
||||||
|
if (uri.toString().endsWith(".gif", true)) {
|
||||||
|
imageView.setImageDrawable(GifDrawable(uri.toString()))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val consumed = DrawerImageLoader.instance.setImage(imageView, uri!!, tag)
|
||||||
|
if (!consumed) {
|
||||||
|
imageView.setImageURI(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (icon != null) {
|
||||||
|
imageView.setImageDrawable(icon)
|
||||||
|
} else if (bitmap != null) {
|
||||||
|
imageView.setImageBitmap(bitmap)
|
||||||
|
} else if (iconRes != -1) {
|
||||||
|
imageView.setImageResource(iconRes)
|
||||||
|
} else if (ii != null) {
|
||||||
|
imageView.setImageDrawable(IconicsDrawable(imageView.context, ii).actionBar())
|
||||||
|
} else {
|
||||||
|
imageView.setImageBitmap(null)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorFilter != null) {
|
||||||
|
imageView.colorFilter = PorterDuffColorFilter(colorFilter!!, colorFilterMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this only handles Drawables
|
||||||
|
*
|
||||||
|
* @param ctx
|
||||||
|
* @param iconColor
|
||||||
|
* @param tint
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
override fun decideIcon(ctx: Context, iconColor: ColorStateList, tint: Boolean, paddingDp: Int): Drawable? {
|
||||||
|
var icon: Drawable? = icon
|
||||||
|
val ii = iicon
|
||||||
|
val uri = uri
|
||||||
|
|
||||||
|
when {
|
||||||
|
ii != null -> icon = IconicsDrawable(ctx).apply {
|
||||||
|
this.icon = ii
|
||||||
|
colorList = iconColor
|
||||||
|
sizeDp = 24
|
||||||
|
}
|
||||||
|
iconRes != -1 -> icon = AppCompatResources.getDrawable(ctx, iconRes)
|
||||||
|
uri != null -> try {
|
||||||
|
val inputStream = ctx.contentResolver.openInputStream(uri)
|
||||||
|
icon = Drawable.createFromStream(inputStream, uri.toString())
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
//no need to handle this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if we got an icon AND we have auto tinting enabled AND it is no IIcon, tint it ;)
|
||||||
|
if (icon != null && tint && iicon == null) {
|
||||||
|
icon = icon.mutate()
|
||||||
|
icon.setColorFilter(iconColor.defaultColor, PorterDuff.Mode.SRC_IN)
|
||||||
|
}
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
}
|
211
navlib/src/main/java/pl/szczodrzynski/navlib/NavBottomBar.kt
Normal file
211
navlib/src/main/java/pl/szczodrzynski/navlib/NavBottomBar.kt
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.google.android.material.bottomappbar.BottomAppBar
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
|
import com.mikepenz.iconics.typeface.IIcon
|
||||||
|
import com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont
|
||||||
|
import com.mikepenz.iconics.utils.sizeDp
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet
|
||||||
|
import pl.szczodrzynski.navlib.drawer.NavDrawer
|
||||||
|
|
||||||
|
class NavBottomBar : BottomAppBar {
|
||||||
|
constructor(context: Context) : super(context) {
|
||||||
|
create(null, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||||
|
create(attrs, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
|
||||||
|
create(attrs, defStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
var drawer: NavDrawer? = null
|
||||||
|
var bottomSheet: NavBottomSheet? = null
|
||||||
|
var fabView: FloatingActionButton? = null
|
||||||
|
var fabExtendedView: ExtendedFloatingActionButton? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the BottomAppBar and sets the contentView's margin to be
|
||||||
|
* above the BottomAppBar.
|
||||||
|
*/
|
||||||
|
var enable = true
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
visibility = if (value) View.VISIBLE else View.GONE
|
||||||
|
setFabParams()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Whether the FAB should be visible.
|
||||||
|
*/
|
||||||
|
var fabEnable = true
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
setFabVisibility()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Whether an ExtendableFloatingActionButton should be used
|
||||||
|
* instead of a normal FloatingActionButton.
|
||||||
|
* Note that the extendable button does not support end alignment/gravity
|
||||||
|
* when used together with the bottom app bar.
|
||||||
|
*/
|
||||||
|
var fabExtendable = true
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
setFabParams()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* If BottomAppBar is enabled, sets its fabAlignmentMode.
|
||||||
|
* Else, sets the actual FAB's gravity.
|
||||||
|
*/
|
||||||
|
var fabGravity = Gravity.CENTER
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
setFabParams()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Whether the FAB should be extended and its text visible.
|
||||||
|
*/
|
||||||
|
var fabExtended = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
if (fabExtended)
|
||||||
|
fabExtendedView?.extend()
|
||||||
|
else
|
||||||
|
fabExtendedView?.shrink()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the FAB's icon.
|
||||||
|
*/
|
||||||
|
var fabIcon: IIcon? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
fabView?.setImageDrawable(IconicsDrawable(context).apply {
|
||||||
|
icon = value
|
||||||
|
colorAttr(context, R.attr.colorFabIcon)
|
||||||
|
sizeDp = 24
|
||||||
|
})
|
||||||
|
fabExtendedView?.icon = IconicsDrawable(context).apply {
|
||||||
|
icon = value
|
||||||
|
colorAttr(context, R.attr.colorFabIcon)
|
||||||
|
sizeDp = 24
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set the ExtendedFAB's text.
|
||||||
|
*/
|
||||||
|
var fabExtendedText
|
||||||
|
get() = fabExtendedView?.text
|
||||||
|
set(value) {
|
||||||
|
fabExtendedView?.text = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the FAB's on click listener
|
||||||
|
*/
|
||||||
|
fun setFabOnClickListener(onClickListener: OnClickListener?) {
|
||||||
|
fabView?.setOnClickListener(onClickListener)
|
||||||
|
fabExtendedView?.setOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
private fun create(attrs: AttributeSet?, defStyle: Int) {
|
||||||
|
setOnTouchListener { _, event ->
|
||||||
|
if (bottomSheet?.enable != true || bottomSheet?.enableDragToOpen != true)
|
||||||
|
return@setOnTouchListener false
|
||||||
|
bottomSheet?.dispatchBottomBarEvent(event)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
elevation = 0f
|
||||||
|
|
||||||
|
val icon = ContextCompat.getDrawable(context, R.drawable.ic_menu_badge) as LayerDrawable?
|
||||||
|
icon?.apply {
|
||||||
|
mutate()
|
||||||
|
setDrawableByLayerId(R.id.ic_menu, IconicsDrawable(context).apply {
|
||||||
|
this.icon = NavLibFont.Icon.nav_menu
|
||||||
|
sizeDp = 24
|
||||||
|
colorAttr(context, R.attr.colorOnPrimary)
|
||||||
|
})
|
||||||
|
setDrawableByLayerId(R.id.ic_badge, BadgeDrawable(context))
|
||||||
|
}
|
||||||
|
navigationIcon = icon
|
||||||
|
|
||||||
|
menu.add(0, -1, 0, "Menu")
|
||||||
|
.setIcon(IconicsDrawable(context).apply {
|
||||||
|
this.icon = NavLibFont.Icon.nav_dots_vertical
|
||||||
|
sizeDp = 24
|
||||||
|
colorAttr(context, R.attr.colorOnPrimary)
|
||||||
|
})
|
||||||
|
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
|
||||||
|
|
||||||
|
setNavigationOnClickListener {
|
||||||
|
drawer?.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setOnMenuItemClickListener {
|
||||||
|
if (it.itemId == -1 && bottomSheet?.enable == true) {
|
||||||
|
bottomSheet?.toggle()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
onMenuItemClickListener?.onMenuItemClick(it)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setFabParams() {
|
||||||
|
val layoutParams =
|
||||||
|
((if (fabExtendable) fabExtendedView?.layoutParams else fabView?.layoutParams) ?: return) as CoordinatorLayout.LayoutParams
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
layoutParams.anchorId = this.id
|
||||||
|
if (fabExtendable)
|
||||||
|
layoutParams.anchorGravity = if (fabExtendable) fabGravity or Gravity.TOP else Gravity.NO_GRAVITY
|
||||||
|
layoutParams.gravity = Gravity.NO_GRAVITY
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
layoutParams.anchorId = View.NO_ID
|
||||||
|
if (fabExtendable)
|
||||||
|
layoutParams.anchorGravity = Gravity.NO_GRAVITY
|
||||||
|
layoutParams.gravity = fabGravity or Gravity.BOTTOM
|
||||||
|
}
|
||||||
|
fabAlignmentMode = if (fabGravity == Gravity.END) FAB_ALIGNMENT_MODE_END else FAB_ALIGNMENT_MODE_CENTER
|
||||||
|
if (fabExtendable)
|
||||||
|
fabExtendedView?.layoutParams = layoutParams
|
||||||
|
else
|
||||||
|
fabView?.layoutParams = layoutParams
|
||||||
|
setFabVisibility()
|
||||||
|
}
|
||||||
|
private fun setFabVisibility() {
|
||||||
|
if (fabEnable && fabExtendable) {
|
||||||
|
fabView?.hide()
|
||||||
|
fabExtendedView?.show()
|
||||||
|
}
|
||||||
|
else if (fabEnable) {
|
||||||
|
fabView?.show()
|
||||||
|
fabExtendedView?.hide()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fabView?.hide()
|
||||||
|
fabExtendedView?.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var onMenuItemClickListener: OnMenuItemClickListener? = null
|
||||||
|
override fun setOnMenuItemClickListener(listener: OnMenuItemClickListener?) {
|
||||||
|
onMenuItemClickListener = listener
|
||||||
|
}
|
||||||
|
}
|
55
navlib/src/main/java/pl/szczodrzynski/navlib/NavToolbar.kt
Normal file
55
navlib/src/main/java/pl/szczodrzynski/navlib/NavToolbar.kt
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.ImageView
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
|
|
||||||
|
class NavToolbar : MaterialToolbar {
|
||||||
|
|
||||||
|
constructor(context: Context) : super(context) {
|
||||||
|
create(null, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||||
|
create(attrs, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
|
||||||
|
create(attrs, defStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
var toolbarImage: ImageView? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
toolbarImage?.setOnClickListener {
|
||||||
|
profileImageClickListener?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSubtitle(subtitle: CharSequence?) {
|
||||||
|
if(subtitle.isNullOrEmpty()) {
|
||||||
|
setPadding(0, 0, 0, 0)
|
||||||
|
toolbarImage?.translationY = 0f
|
||||||
|
} else {
|
||||||
|
setPadding(0, -1, 0, 5)
|
||||||
|
toolbarImage?.translationY = 6f
|
||||||
|
}
|
||||||
|
super.setSubtitle(subtitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun create(attrs: AttributeSet?, defStyle: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var subtitleFormat: Int? = null
|
||||||
|
var subtitleFormatWithUnread: Int? = null
|
||||||
|
|
||||||
|
var profileImageClickListener: (() -> Unit)? = null
|
||||||
|
|
||||||
|
var profileImage
|
||||||
|
get() = toolbarImage?.drawable
|
||||||
|
set(value) {
|
||||||
|
toolbarImage?.setImageDrawable(value)
|
||||||
|
}
|
||||||
|
}
|
213
navlib/src/main/java/pl/szczodrzynski/navlib/NavView.kt
Normal file
213
navlib/src/main/java/pl/szczodrzynski/navlib/NavView.kt
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.content.res.Configuration.ORIENTATION_PORTRAIT
|
||||||
|
import android.graphics.Point
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import androidx.core.view.children
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
import kotlinx.android.synthetic.main.nav_view.view.*
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet
|
||||||
|
import pl.szczodrzynski.navlib.drawer.NavDrawer
|
||||||
|
|
||||||
|
|
||||||
|
class NavView : FrameLayout {
|
||||||
|
companion object {
|
||||||
|
const val SOURCE_OTHER = 0
|
||||||
|
const val SOURCE_DRAWER = 1
|
||||||
|
const val SOURCE_BOTTOM_SHEET = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private var contentView: LinearLayout? = null
|
||||||
|
|
||||||
|
private lateinit var statusBarBackground: View
|
||||||
|
private lateinit var navigationBarBackground: View
|
||||||
|
private lateinit var mainView: LinearLayout
|
||||||
|
private lateinit var floatingActionButton: FloatingActionButton
|
||||||
|
private lateinit var extendedFloatingActionButton: ExtendedFloatingActionButton
|
||||||
|
|
||||||
|
lateinit var drawer: NavDrawer
|
||||||
|
lateinit var toolbar: NavToolbar
|
||||||
|
lateinit var bottomBar: NavBottomBar
|
||||||
|
lateinit var bottomSheet: NavBottomSheet
|
||||||
|
val coordinator by lazy {
|
||||||
|
findViewById<CoordinatorLayout>(R.id.nv_coordinator)
|
||||||
|
}
|
||||||
|
|
||||||
|
var navigationLoader: NavigationLoader? = null
|
||||||
|
|
||||||
|
constructor(context: Context) : super(context) {
|
||||||
|
create(null, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||||
|
create(attrs, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
|
||||||
|
create(attrs, defStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun create(attrs: AttributeSet?, defStyle: Int) {
|
||||||
|
// Load attributes
|
||||||
|
val a = context.obtainStyledAttributes(attrs, R.styleable.NavView, defStyle, 0)
|
||||||
|
/*_exampleString = a.getString(
|
||||||
|
R.styleable.NavView_exampleString
|
||||||
|
)*/
|
||||||
|
a.recycle()
|
||||||
|
|
||||||
|
val layoutInflater = LayoutInflater.from(context)
|
||||||
|
layoutInflater.inflate(R.layout.nav_view, this)
|
||||||
|
|
||||||
|
contentView = findViewById<LinearLayout>(R.id.nv_content)
|
||||||
|
|
||||||
|
statusBarBackground = findViewById(R.id.nv_statusBarBackground)
|
||||||
|
navigationBarBackground = findViewById(R.id.nv_navigationBarBackground)
|
||||||
|
mainView = findViewById(R.id.nv_main)
|
||||||
|
floatingActionButton = findViewById(R.id.nv_floatingActionButton)
|
||||||
|
extendedFloatingActionButton = findViewById(R.id.nv_extendedFloatingActionButton)
|
||||||
|
|
||||||
|
drawer = NavDrawer(
|
||||||
|
context,
|
||||||
|
findViewById(R.id.nv_drawerLayout),
|
||||||
|
findViewById(R.id.nv_drawerContainerLandscape),
|
||||||
|
findViewById(R.id.nv_miniDrawerContainerPortrait),
|
||||||
|
findViewById(R.id.nv_miniDrawerElevation)
|
||||||
|
)
|
||||||
|
toolbar = findViewById(R.id.nv_toolbar)
|
||||||
|
bottomBar = findViewById(R.id.nv_bottomBar)
|
||||||
|
bottomSheet = findViewById(R.id.nv_bottomSheet)
|
||||||
|
|
||||||
|
drawer.toolbar = toolbar
|
||||||
|
drawer.bottomBar = bottomBar
|
||||||
|
|
||||||
|
toolbar.toolbarImage = findViewById(R.id.nv_toolbar_image)
|
||||||
|
|
||||||
|
bottomBar.drawer = drawer
|
||||||
|
bottomBar.bottomSheet = bottomSheet
|
||||||
|
bottomBar.fabView = floatingActionButton
|
||||||
|
bottomBar.fabExtendedView = extendedFloatingActionButton
|
||||||
|
|
||||||
|
ripple.isEnabled = false
|
||||||
|
ripple.children.forEach { it.isEnabled = false }
|
||||||
|
|
||||||
|
//bottomSheetBehavior.peekHeight = displayHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertDpToPixel(dp: Float): Float {
|
||||||
|
val resources = context.resources
|
||||||
|
val metrics = resources.displayMetrics
|
||||||
|
return dp * (metrics.densityDpi / 160f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gainAttentionOnBottomBar() {
|
||||||
|
var x = ripple.width.toFloat()
|
||||||
|
var y = ripple.height.toFloat()
|
||||||
|
x -= convertDpToPixel(56f) / 2
|
||||||
|
y -= convertDpToPixel(56f) / 2
|
||||||
|
ripple.performRipple(Point(x.toInt(), y.toInt()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun configSystemBarsUtil(systemBarsUtil: SystemBarsUtil) {
|
||||||
|
this.systemBarsUtil = systemBarsUtil.apply {
|
||||||
|
this.statusBarBgView = statusBarBackground
|
||||||
|
this.navigationBarBgView = navigationBarBackground
|
||||||
|
this.statusBarDarkView = nv_statusBarDarker
|
||||||
|
//this.navigationBarDarkView = navigationBarBackground
|
||||||
|
this.insetsListener = nv_drawerLayout
|
||||||
|
this.marginBySystemBars = mainView
|
||||||
|
this.paddingByNavigationBar = bottomSheet.getContentView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var enableBottomSheet = true
|
||||||
|
var enableBottomSheetDrag = true
|
||||||
|
|
||||||
|
var bottomBarEnable = true
|
||||||
|
get() = bottomBar.enable
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
bottomBar.enable = value
|
||||||
|
setContentMargins() // TODO combine bottomBarEnable and bottomBar.enable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the toolbar and sets the contentView's margin to be
|
||||||
|
* below the toolbar.
|
||||||
|
*/
|
||||||
|
var showToolbar = true; set(value) {
|
||||||
|
toolbar.visibility = if (value) View.VISIBLE else View.GONE
|
||||||
|
field = value
|
||||||
|
setContentMargins()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the FAB's on click listener
|
||||||
|
*/
|
||||||
|
fun setFabOnClickListener(onClickListener: OnClickListener?) {
|
||||||
|
bottomBar.setFabOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var systemBarsUtil: SystemBarsUtil? = null
|
||||||
|
|
||||||
|
private fun setContentMargins() {
|
||||||
|
val layoutParams = CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||||
|
val actionBarSize = 56 * context.resources.displayMetrics.density
|
||||||
|
layoutParams.topMargin = if (showToolbar) actionBarSize.toInt() else 0
|
||||||
|
layoutParams.bottomMargin = if (bottomBarEnable) actionBarSize.toInt() else 0
|
||||||
|
contentView?.layoutParams = layoutParams
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
|
||||||
|
Log.d(
|
||||||
|
"NavLib",
|
||||||
|
"CONFIGURATION CHANGED: ${newConfig?.screenWidthDp}x${newConfig?.screenHeightDp} "+if (newConfig?.orientation == ORIENTATION_PORTRAIT) "portrait" else "landscape"
|
||||||
|
)
|
||||||
|
|
||||||
|
systemBarsUtil?.commit()
|
||||||
|
|
||||||
|
drawer.decideDrawerMode(
|
||||||
|
newConfig?.orientation ?: ORIENTATION_PORTRAIT,
|
||||||
|
newConfig?.screenWidthDp ?: 0,
|
||||||
|
newConfig?.screenHeightDp ?: 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBackPressed(): Boolean {
|
||||||
|
if (drawer.isOpen && !drawer.fixedDrawerEnabled()) {
|
||||||
|
if (drawer.profileSelectionIsOpen) {
|
||||||
|
drawer.profileSelectionClose()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
drawer.close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (bottomSheet.isOpen) {
|
||||||
|
bottomSheet.close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
|
||||||
|
if (contentView == null) {
|
||||||
|
super.addView(child, index, params)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
contentView!!.addView(child, index, params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
interface NavigationLoader {
|
||||||
|
fun load(itemId: Int, callerId: Int, source: Int, args: Map<String, Any?>)
|
||||||
|
}
|
376
navlib/src/main/java/pl/szczodrzynski/navlib/SystemBarsUtil.kt
Normal file
376
navlib/src/main/java/pl/szczodrzynski/navlib/SystemBarsUtil.kt
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.res.Configuration.ORIENTATION_PORTRAIT
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Build.VERSION.SDK_INT
|
||||||
|
import android.os.Build.VERSION_CODES
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.*
|
||||||
|
import android.view.Window
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import com.mikepenz.materialize.util.KeyboardUtil
|
||||||
|
|
||||||
|
|
||||||
|
class SystemBarsUtil(private val activity: Activity) {
|
||||||
|
companion object {
|
||||||
|
private const val COLOR_TRANSPARENT = Color.TRANSPARENT
|
||||||
|
/**
|
||||||
|
* A fallback color.
|
||||||
|
* Tells to apply a #22000000 overlay over the status/nav bar color.
|
||||||
|
* This has the same effect as [statusBarDarker].
|
||||||
|
*/
|
||||||
|
const val COLOR_HALF_TRANSPARENT = -1
|
||||||
|
/**
|
||||||
|
* Use ?colorPrimaryDark as a fallback or status bar color.
|
||||||
|
*/
|
||||||
|
const val COLOR_PRIMARY_DARK = -2
|
||||||
|
/**
|
||||||
|
* A fallback color.
|
||||||
|
* Not recommended to use as [statusBarFallbackLight] because it will make status bar
|
||||||
|
* icons almost invisible.
|
||||||
|
*/
|
||||||
|
const val COLOR_DO_NOT_CHANGE = -3
|
||||||
|
|
||||||
|
private const val TARGET_MODE_NORMAL = 0
|
||||||
|
private const val TARGET_MODE_LIGHT = 1
|
||||||
|
private const val TARGET_MODE_GRADIENT = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
val window: Window by lazy {
|
||||||
|
activity.window
|
||||||
|
}
|
||||||
|
val resources: Resources by lazy {
|
||||||
|
activity.resources
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view which will have the padding added when the soft input keyboard appears.
|
||||||
|
*/
|
||||||
|
var paddingByKeyboard: View? = null
|
||||||
|
/**
|
||||||
|
* Whether the app should be fullscreen.
|
||||||
|
*
|
||||||
|
* This means it will display under the system bars
|
||||||
|
* and you should probably provide [statusBarBgView],
|
||||||
|
* [navigationBarBgView] and [marginBySystemBars].
|
||||||
|
*/
|
||||||
|
var appFullscreen = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the color used to tint the status bar background.
|
||||||
|
*
|
||||||
|
* Valid values are [COLOR_PRIMARY_DARK] or a color integer.
|
||||||
|
*
|
||||||
|
* You cannot use neither [COLOR_HALF_TRANSPARENT] nor [COLOR_DO_NOT_CHANGE] here.
|
||||||
|
* See [statusBarDarker].
|
||||||
|
*/
|
||||||
|
var statusBarColor = COLOR_PRIMARY_DARK
|
||||||
|
/**
|
||||||
|
* Whether the status bar should have a dark overlay (#22000000).
|
||||||
|
*
|
||||||
|
* Useful if the [statusBarColor] is set to a bright color and is the same as an action bar.
|
||||||
|
* Not useful if [statusBarColor] is [COLOR_PRIMARY_DARK].
|
||||||
|
*/
|
||||||
|
var statusBarDarker = false
|
||||||
|
/**
|
||||||
|
* A fallback status bar color used on Android Lollipop
|
||||||
|
* when the [statusBarColor] combined with [statusBarDarker] is
|
||||||
|
* too bright not to blend with status bar icons (they cannot be
|
||||||
|
* set to dark).
|
||||||
|
*
|
||||||
|
* This will (most likely) not be used when [statusBarDarker] is true.
|
||||||
|
*
|
||||||
|
* Valid values are [COLOR_HALF_TRANSPARENT], [COLOR_PRIMARY_DARK], [COLOR_DO_NOT_CHANGE].
|
||||||
|
*/
|
||||||
|
var statusBarFallbackLight = COLOR_HALF_TRANSPARENT
|
||||||
|
/**
|
||||||
|
* A fallback status bar color used on Android KitKat and older.
|
||||||
|
* On these systems there is a black-to-transparent gradient as
|
||||||
|
* the status bar background.
|
||||||
|
*
|
||||||
|
* Valid values are [COLOR_HALF_TRANSPARENT], [COLOR_PRIMARY_DARK], [COLOR_DO_NOT_CHANGE].
|
||||||
|
*/
|
||||||
|
var statusBarFallbackGradient = COLOR_DO_NOT_CHANGE
|
||||||
|
|
||||||
|
// TODO remove - test for huawei
|
||||||
|
var statusBarTranslucent = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If false, the nav bar is mostly translucent but not completely transparent.
|
||||||
|
*/
|
||||||
|
var navigationBarTransparent = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A background view to be resized in order to fit under the status bar.
|
||||||
|
*/
|
||||||
|
var statusBarBgView: View? = null
|
||||||
|
/**
|
||||||
|
* A background view to be resized in order to fit under the nav bar.
|
||||||
|
*/
|
||||||
|
var navigationBarBgView: View? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dark, half-transparent view to be resized in order to fit under the status bar.
|
||||||
|
*/
|
||||||
|
var statusBarDarkView: View? = null
|
||||||
|
/**
|
||||||
|
* A dark, half-transparent view to be resized in order to fit under the nav bar.
|
||||||
|
*/
|
||||||
|
var navigationBarDarkView: View? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view which will have the margin added not to overlap with the status/nav bar.
|
||||||
|
*/
|
||||||
|
var marginBySystemBars: View? = null
|
||||||
|
/**
|
||||||
|
* A view which will listen to the inset applying.
|
||||||
|
*/
|
||||||
|
var insetsListener: View? = null
|
||||||
|
/**
|
||||||
|
* A view which will have the padding added not to overlap with the nav bar.
|
||||||
|
* Useful for persistent bottom sheets.
|
||||||
|
* Requires [marginBySystemBars].
|
||||||
|
*/
|
||||||
|
var paddingByNavigationBar: View? = null
|
||||||
|
|
||||||
|
private var keyboardUtil: KeyboardUtil? = null
|
||||||
|
private var insetsApplied = false
|
||||||
|
|
||||||
|
fun commit() {
|
||||||
|
Log.d("NavLib", "SystemBarsUtil applying")
|
||||||
|
insetsApplied = false
|
||||||
|
if (paddingByKeyboard != null) {
|
||||||
|
// thanks mikepenz for this life-saving class
|
||||||
|
keyboardUtil = KeyboardUtil(activity, paddingByKeyboard)
|
||||||
|
keyboardUtil?.enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the correct target SB color
|
||||||
|
var targetStatusBarColor = statusBarColor
|
||||||
|
if (targetStatusBarColor == COLOR_PRIMARY_DARK)
|
||||||
|
targetStatusBarColor = getColorFromAttr(activity, R.attr.colorPrimaryDark)
|
||||||
|
|
||||||
|
var targetStatusBarDarker = statusBarDarker
|
||||||
|
|
||||||
|
// fallback if the SB color is too light for the icons to be visible
|
||||||
|
// applicable on Lollipop 5.0 and TouchWiz 4.1-4.3
|
||||||
|
var targetStatusBarFallbackLight = statusBarFallbackLight
|
||||||
|
if (targetStatusBarFallbackLight == COLOR_PRIMARY_DARK)
|
||||||
|
targetStatusBarFallbackLight = getColorFromAttr(activity, R.attr.colorPrimaryDark)
|
||||||
|
|
||||||
|
// fallback if there is a gradient under the status bar
|
||||||
|
// applicable on AOSP/similar 4.4 and Huawei EMUI Lollipop
|
||||||
|
// TODO check huawei 6.0+ for gradient bars, check huawei 4.4
|
||||||
|
var targetStatusBarFallbackGradient = statusBarFallbackGradient
|
||||||
|
if (targetStatusBarFallbackGradient == COLOR_PRIMARY_DARK)
|
||||||
|
targetStatusBarFallbackGradient = getColorFromAttr(activity, R.attr.colorPrimaryDark)
|
||||||
|
|
||||||
|
// determines the target mode that will be applied
|
||||||
|
var targetStatusBarMode = TARGET_MODE_NORMAL
|
||||||
|
|
||||||
|
val targetStatusBarLight = ColorUtils.calculateLuminance(targetStatusBarColor) > 0.75 && !targetStatusBarDarker
|
||||||
|
|
||||||
|
if (appFullscreen) {
|
||||||
|
window.decorView.systemUiVisibility = 0
|
||||||
|
// API 19+ (KitKat 4.4+) - make the app fullscreen.
|
||||||
|
// On lower APIs this is useless because
|
||||||
|
// #1 the status/nav bar cannot be transparent (except Samsung TouchWiz)
|
||||||
|
// #2 tablets do not report status/nav bar height correctly
|
||||||
|
// #3 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the activity not resize when keyboard is open
|
||||||
|
// Samsung TouchWiz - app will go fullscreen. There is a problem though, see #3.
|
||||||
|
var targetAppFullscreen = false
|
||||||
|
if (SDK_INT >= VERSION_CODES.KITKAT) {
|
||||||
|
targetAppFullscreen = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (SDK_INT in VERSION_CODES.KITKAT until VERSION_CODES.LOLLIPOP) {
|
||||||
|
// API 19-20 (KitKat 4.4) - set gradient status bar
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
|
// take FallbackGradient color
|
||||||
|
targetStatusBarMode = TARGET_MODE_GRADIENT
|
||||||
|
// disable darker even if [statusBarDarker] == true BUT gradient fallback is not COLOR_HALF_TRANSPARENT
|
||||||
|
//targetStatusBarDarker = targetStatusBarDarker && targetStatusBarFallbackGradient == COLOR_HALF_TRANSPARENT
|
||||||
|
}
|
||||||
|
else if (SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||||
|
// API 21+ (Lollipop 5.0+) - set transparent status bar
|
||||||
|
if (statusBarTranslucent) {
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
window.statusBarColor = Color.TRANSPARENT
|
||||||
|
}
|
||||||
|
if (SDK_INT < VERSION_CODES.M && targetStatusBarLight) {
|
||||||
|
// take FallbackLight color
|
||||||
|
targetStatusBarMode = TARGET_MODE_LIGHT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SDK_INT >= VERSION_CODES.M && targetStatusBarLight) {
|
||||||
|
// API 23+ (Marshmallow 6.0+) - set the status bar icons to dark color if [statusBarLight] is true
|
||||||
|
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||||
|
}
|
||||||
|
// FOR SAMSUNG/SONY DEVICES (TouchWiz 4.1-4.3)
|
||||||
|
if (SDK_INT < VERSION_CODES.KITKAT) {
|
||||||
|
val libs = activity.packageManager.systemSharedLibraryNames
|
||||||
|
var reflect: String? = null
|
||||||
|
// TODO galaxy s3 - opening keyboard does not resize activity if fullscreen
|
||||||
|
if (libs != null) {
|
||||||
|
for (lib in libs) {
|
||||||
|
Log.d("SBU", lib)
|
||||||
|
if (lib == "touchwiz")
|
||||||
|
// SYSTEM_UI_FLAG_TRANSPARENT_BACKGROUND = 0x00001000
|
||||||
|
reflect = "SYSTEM_UI_FLAG_TRANSPARENT_BACKGROUND"
|
||||||
|
else if (lib.startsWith("com.sonyericsson.navigationbar"))
|
||||||
|
reflect = "SYSTEM_UI_FLAG_TRANSPARENT"
|
||||||
|
}
|
||||||
|
if (reflect != null) {
|
||||||
|
try {
|
||||||
|
val field = View::class.java.getField(reflect)
|
||||||
|
var flag = 0
|
||||||
|
if (field.type === Integer.TYPE)
|
||||||
|
flag = field.getInt(null)
|
||||||
|
if (flag != 0) {
|
||||||
|
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or flag
|
||||||
|
targetStatusBarMode = TARGET_MODE_LIGHT /* or TARGET_MODE_GRADIENT */
|
||||||
|
targetAppFullscreen = true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO huawei detection for 5.0+
|
||||||
|
|
||||||
|
targetStatusBarColor = when (targetStatusBarMode) {
|
||||||
|
TARGET_MODE_LIGHT -> when (targetStatusBarFallbackLight) {
|
||||||
|
COLOR_DO_NOT_CHANGE -> targetStatusBarColor
|
||||||
|
COLOR_HALF_TRANSPARENT -> {
|
||||||
|
targetStatusBarDarker = true
|
||||||
|
targetStatusBarColor
|
||||||
|
}
|
||||||
|
else -> targetStatusBarFallbackLight
|
||||||
|
}
|
||||||
|
TARGET_MODE_GRADIENT -> when (targetStatusBarFallbackGradient) {
|
||||||
|
COLOR_DO_NOT_CHANGE -> {
|
||||||
|
targetStatusBarDarker = false
|
||||||
|
targetStatusBarColor
|
||||||
|
}
|
||||||
|
COLOR_HALF_TRANSPARENT -> {
|
||||||
|
targetStatusBarDarker = true
|
||||||
|
targetStatusBarColor
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
targetStatusBarDarker = false
|
||||||
|
targetStatusBarFallbackGradient
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> targetStatusBarColor
|
||||||
|
}
|
||||||
|
|
||||||
|
statusBarBgView?.setBackgroundColor(targetStatusBarColor)
|
||||||
|
statusBarDarkView?.visibility = if (targetStatusBarDarker) VISIBLE else GONE
|
||||||
|
|
||||||
|
if (targetAppFullscreen) {
|
||||||
|
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO navigation bar options like status bar
|
||||||
|
// NAVIGATION BAR
|
||||||
|
if (SDK_INT >= VERSION_CODES.KITKAT && (SDK_INT < VERSION_CODES.LOLLIPOP || !navigationBarTransparent)) {
|
||||||
|
// API 19-20 (KitKat 4.4) - set gradient navigation bar
|
||||||
|
// API 21+ (Lollipop 5.0+) - set half-transparent navigation bar if [navigationBarTransparent] is false
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SDK_INT >= VERSION_CODES.LOLLIPOP && navigationBarTransparent) {
|
||||||
|
// API 21+ (Lollipop 5.0+) - set fully transparent navigation bar if [navigationBarTransparent] is true
|
||||||
|
window.navigationBarColor = Color.TRANSPARENT
|
||||||
|
}
|
||||||
|
|
||||||
|
// PADDING
|
||||||
|
if (insetsListener != null) {
|
||||||
|
if (SDK_INT >= VERSION_CODES.LOLLIPOP && false) {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(insetsListener!!) { _, insets ->
|
||||||
|
Log.d("NavLib", "Got insets left = ${insets.systemWindowInsetLeft}, top = ${insets.systemWindowInsetTop}, right = ${insets.systemWindowInsetRight}, bottom = ${insets.systemWindowInsetBottom}")
|
||||||
|
if (insetsApplied)
|
||||||
|
return@setOnApplyWindowInsetsListener insets.consumeSystemWindowInsets()
|
||||||
|
Log.d("NavLib", "Applied insets left = ${insets.systemWindowInsetLeft}, top = ${insets.systemWindowInsetTop}, right = ${insets.systemWindowInsetRight}, bottom = ${insets.systemWindowInsetBottom}")
|
||||||
|
insetsApplied = true
|
||||||
|
applyPadding(
|
||||||
|
insets.systemWindowInsetLeft,
|
||||||
|
insets.systemWindowInsetTop,
|
||||||
|
insets.systemWindowInsetRight,
|
||||||
|
insets.systemWindowInsetBottom
|
||||||
|
)
|
||||||
|
insets.consumeSystemWindowInsets()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var statusBarSize = 0
|
||||||
|
val statusBarRes = resources.getIdentifier("status_bar_height", "dimen", "android")
|
||||||
|
if (statusBarRes > 0 && targetAppFullscreen) {
|
||||||
|
statusBarSize = resources.getDimensionPixelSize(statusBarRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var navigationBarSize = 0
|
||||||
|
if (hasNavigationBar(activity) && targetAppFullscreen) {
|
||||||
|
val orientation = resources.configuration.orientation
|
||||||
|
|
||||||
|
val navigationBarRes = when {
|
||||||
|
orientation == ORIENTATION_PORTRAIT ->
|
||||||
|
resources.getIdentifier("navigation_bar_height", "dimen", "android")
|
||||||
|
isTablet(activity) ->
|
||||||
|
resources.getIdentifier("navigation_bar_height_landscape", "dimen", "android")
|
||||||
|
else ->
|
||||||
|
resources.getIdentifier("navigation_bar_width", "dimen", "android")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigationBarRes > 0) {
|
||||||
|
navigationBarSize = resources.getDimensionPixelSize(navigationBarRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPadding(
|
||||||
|
0,
|
||||||
|
statusBarSize,
|
||||||
|
0,
|
||||||
|
navigationBarSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// app not fullscreen
|
||||||
|
// TODO statusBarColor & navigationBarColor if not fullscreen (it's possible)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyPadding(left: Int, top: Int, right: Int, bottom: Int) {
|
||||||
|
marginBySystemBars?.setPadding(left, top, right, bottom)
|
||||||
|
|
||||||
|
statusBarBgView?.layoutParams?.height = top
|
||||||
|
navigationBarBgView?.layoutParams?.height = bottom
|
||||||
|
|
||||||
|
statusBarDarkView?.layoutParams?.height = top
|
||||||
|
navigationBarDarkView?.layoutParams?.height = bottom
|
||||||
|
|
||||||
|
paddingByNavigationBar?.setPadding(
|
||||||
|
(8 * resources.displayMetrics.density).toInt(),
|
||||||
|
0,
|
||||||
|
(8 * resources.displayMetrics.density).toInt(),
|
||||||
|
bottom
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun destroy() {
|
||||||
|
if (paddingByKeyboard != null) {
|
||||||
|
keyboardUtil?.disable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
164
navlib/src/main/java/pl/szczodrzynski/navlib/Utils.kt
Normal file
164
navlib/src/main/java/pl/szczodrzynski/navlib/Utils.kt
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package pl.szczodrzynski.navlib
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.DisplayMetrics
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.annotation.AttrRes
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import com.google.android.material.elevation.ElevationOverlayProvider
|
||||||
|
import com.mikepenz.iconics.IconicsColor
|
||||||
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
|
import com.mikepenz.iconics.utils.colorInt
|
||||||
|
|
||||||
|
|
||||||
|
/*private val displayMetrics by lazy {
|
||||||
|
context.resources.displayMetrics
|
||||||
|
}*/
|
||||||
|
/*private val configuration by lazy { context.resources.configuration }
|
||||||
|
private val displayWidth: Int by lazy { configuration.screenWidthDp }
|
||||||
|
private val displayHeight: Int by lazy { configuration.screenHeightDp }*/
|
||||||
|
|
||||||
|
fun getTopInset(context: Context, view: View): Float {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
(view.rootWindowInsets?.systemWindowInsetTop ?: 24)
|
||||||
|
} else {
|
||||||
|
24
|
||||||
|
} * context.resources.displayMetrics.density
|
||||||
|
}
|
||||||
|
fun getLeftInset(context: Context, view: View): Float {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
(view.rootWindowInsets?.systemWindowInsetLeft ?: 0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} * context.resources.displayMetrics.density
|
||||||
|
}
|
||||||
|
fun getRightInset(context: Context, view: View): Float {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
(view.rootWindowInsets?.systemWindowInsetRight ?: 0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
} * context.resources.displayMetrics.density
|
||||||
|
}
|
||||||
|
fun getBottomInset(context: Context, view: View): Float {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
(view.rootWindowInsets?.systemWindowInsetBottom ?: 48)
|
||||||
|
} else {
|
||||||
|
48
|
||||||
|
} * context.resources.displayMetrics.density
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.getActivity(): Activity {
|
||||||
|
return findViewById<View>(android.R.id.content).context as Activity
|
||||||
|
}
|
||||||
|
|
||||||
|
fun blendColors(background: Int, foreground: Int): Int {
|
||||||
|
val r1 = (background shr 16 and 0xff)
|
||||||
|
val g1 = (background shr 8 and 0xff)
|
||||||
|
val b1 = (background and 0xff)
|
||||||
|
|
||||||
|
val r2 = (foreground shr 16 and 0xff)
|
||||||
|
val g2 = (foreground shr 8 and 0xff)
|
||||||
|
val b2 = (foreground and 0xff)
|
||||||
|
val a2 = (foreground shr 24 and 0xff)
|
||||||
|
//ColorUtils.compositeColors()
|
||||||
|
|
||||||
|
val factor = a2.toFloat() / 255f
|
||||||
|
val red = (r1 * (1 - factor) + r2 * factor)
|
||||||
|
val green = (g1 * (1 - factor) + g2 * factor)
|
||||||
|
val blue = (b1 * (1 - factor) + b2 * factor)
|
||||||
|
|
||||||
|
return (0xff000000 or (red.toLong() shl 16) or (green.toLong() shl 8) or (blue.toLong())).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun elevateSurface(context: Context, dp: Int): Int {
|
||||||
|
ElevationOverlayProvider(context).apply {
|
||||||
|
return compositeOverlay(themeSurfaceColor, dp * context.resources.displayMetrics.density)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isTablet(c: Context): Boolean {
|
||||||
|
return (c.resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasNavigationBar(context: Context): Boolean {
|
||||||
|
val id = context.resources.getIdentifier("config_showNavigationBar", "bool", "android")
|
||||||
|
var hasNavigationBar = id > 0 && context.resources.getBoolean(id)
|
||||||
|
|
||||||
|
if (!hasNavigationBar && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
val d = (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
|
||||||
|
|
||||||
|
val realDisplayMetrics = DisplayMetrics()
|
||||||
|
d.getRealMetrics(realDisplayMetrics)
|
||||||
|
|
||||||
|
val realHeight = realDisplayMetrics.heightPixels
|
||||||
|
val realWidth = realDisplayMetrics.widthPixels
|
||||||
|
|
||||||
|
val displayMetrics = DisplayMetrics()
|
||||||
|
d.getMetrics(displayMetrics)
|
||||||
|
|
||||||
|
val displayHeight = displayMetrics.heightPixels
|
||||||
|
val displayWidth = displayMetrics.widthPixels
|
||||||
|
|
||||||
|
hasNavigationBar = realWidth - displayWidth > 0 || realHeight - displayHeight > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow a system property to override this. Used by the emulator.
|
||||||
|
// See also hasNavigationBar().
|
||||||
|
val navBarOverride = System.getProperty("qemu.hw.mainkeys")
|
||||||
|
if (navBarOverride == "1")
|
||||||
|
hasNavigationBar = true
|
||||||
|
else if (navBarOverride == "0") hasNavigationBar = false
|
||||||
|
|
||||||
|
return hasNavigationBar
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IconicsDrawable.colorAttr(context: Context, @AttrRes attrRes: Int) {
|
||||||
|
colorInt = getColorFromAttr(context, attrRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getColorFromAttr(context: Context, @AttrRes color: Int): Int {
|
||||||
|
val typedValue = TypedValue()
|
||||||
|
context.theme.resolveAttribute(color, typedValue, true)
|
||||||
|
return typedValue.data
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getDrawableFromRes(@DrawableRes id: Int): Drawable {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
resources.getDrawable(id, theme)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resources.getDrawable(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
fun Context.getColorFromRes(@ColorRes id: Int): Int {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
resources.getColor(id, theme)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resources.getColor(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun crc16(buffer: String): Int {
|
||||||
|
/* Note the change here */
|
||||||
|
var crc = 0x1D0F
|
||||||
|
for (j in buffer) {
|
||||||
|
crc = crc.ushr(8) or (crc shl 8) and 0xffff
|
||||||
|
crc = crc xor (j.toInt() and 0xff)//byte to int, trunc sign
|
||||||
|
crc = crc xor (crc and 0xff shr 4)
|
||||||
|
crc = crc xor (crc shl 12 and 0xffff)
|
||||||
|
crc = crc xor (crc and 0xFF shl 5 and 0xffff)
|
||||||
|
}
|
||||||
|
crc = crc and 0xffff
|
||||||
|
return crc
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package pl.szczodrzynski.navlib.bottomsheet
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import pl.szczodrzynski.navlib.R
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.items.IBottomSheetItem
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||||
|
|
||||||
|
class BottomSheetAdapter(val items: List<IBottomSheetItem<*>>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
private val viewHolderProvider = ViewHolderProvider()
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewHolderProvider.registerViewHolderFactory(1, R.layout.nav_bs_item_primary) { itemView ->
|
||||||
|
BottomSheetPrimaryItem.ViewHolder(itemView)
|
||||||
|
}
|
||||||
|
viewHolderProvider.registerViewHolderFactory(2, R.layout.nav_bs_item_separator) { itemView ->
|
||||||
|
BottomSheetSeparatorItem.ViewHolder(itemView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return viewHolderProvider.provideViewHolder(viewGroup = parent, viewType = viewType)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return items[position].viewType
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return items.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
items[position].bindViewHolder(viewHolder = holder)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,438 @@
|
|||||||
|
package pl.szczodrzynski.navlib.bottomsheet
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.graphics.PorterDuffColorFilter
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import androidx.core.widget.NestedScrollView
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import com.google.android.material.button.MaterialButtonToggleGroup
|
||||||
|
import com.google.android.material.textfield.TextInputEditText
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
|
import com.mikepenz.iconics.typeface.IIcon
|
||||||
|
import com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont
|
||||||
|
import com.mikepenz.iconics.utils.paddingDp
|
||||||
|
import com.mikepenz.iconics.utils.sizeDp
|
||||||
|
import pl.szczodrzynski.navlib.*
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||||
|
import pl.szczodrzynski.navlib.bottomsheet.items.IBottomSheetItem
|
||||||
|
|
||||||
|
|
||||||
|
class NavBottomSheet : CoordinatorLayout {
|
||||||
|
companion object {
|
||||||
|
const val TOGGLE_GROUP_SINGLE_SELECTION = 0
|
||||||
|
const val TOGGLE_GROUP_MULTIPLE_SELECTION = 1
|
||||||
|
const val TOGGLE_GROUP_SORTING_ORDER = 2
|
||||||
|
|
||||||
|
const val SORT_MODE_ASCENDING = 0
|
||||||
|
const val SORT_MODE_DESCENDING = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context) : super(context) {
|
||||||
|
create(null, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||||
|
create(attrs, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
|
||||||
|
create(attrs, defStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var scrimView: View
|
||||||
|
private lateinit var bottomSheet: NestedScrollView
|
||||||
|
private lateinit var content: LinearLayout
|
||||||
|
private lateinit var dragBar: View
|
||||||
|
private lateinit var textInputLayout: TextInputLayout
|
||||||
|
private lateinit var textInputEditText: TextInputEditText
|
||||||
|
private lateinit var toggleGroupContainer: LinearLayout
|
||||||
|
private lateinit var toggleGroup: MaterialButtonToggleGroup
|
||||||
|
private lateinit var toggleGroupTitleView: TextView
|
||||||
|
private lateinit var list: RecyclerView
|
||||||
|
|
||||||
|
private lateinit var bottomSheetBehavior: BottomSheetBehavior<View>
|
||||||
|
private var bottomSheetVisible = false
|
||||||
|
|
||||||
|
private val items = ArrayList<IBottomSheetItem<*>>()
|
||||||
|
private val adapter = BottomSheetAdapter(items)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the bottom sheet.
|
||||||
|
* This value is mostly relevant to the [pl.szczodrzynski.navlib.NavBottomBar].
|
||||||
|
*/
|
||||||
|
var enable = true
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
if (!value && bottomSheetVisible)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Whether the [pl.szczodrzynski.navlib.NavBottomBar] should open this BottomSheet
|
||||||
|
* when the user drags the bottom bar.
|
||||||
|
*/
|
||||||
|
var enableDragToOpen = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control the scrim view visibility, shown when BottomSheet
|
||||||
|
* is expanded.
|
||||||
|
*/
|
||||||
|
var scrimViewEnabled = true
|
||||||
|
set(value) {
|
||||||
|
scrimView.visibility = if (value) View.INVISIBLE else View.GONE // INVISIBLE
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Whether tapping the Scrim view should hide the BottomSheet.
|
||||||
|
*/
|
||||||
|
var scrimViewTapToClose = true
|
||||||
|
|
||||||
|
|
||||||
|
fun hideKeyboard() {
|
||||||
|
val imm = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(rootView.windowToken, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun create(attrs: AttributeSet?, defStyle: Int) {
|
||||||
|
val layoutInflater = LayoutInflater.from(context)
|
||||||
|
layoutInflater.inflate(R.layout.nav_bottom_sheet, this)
|
||||||
|
|
||||||
|
scrimView = findViewById(R.id.bs_scrim)
|
||||||
|
bottomSheet = findViewById(R.id.bs_view)
|
||||||
|
content = findViewById(R.id.bs_content)
|
||||||
|
dragBar = findViewById(R.id.bs_dragBar)
|
||||||
|
textInputLayout = findViewById(R.id.bs_textInputLayout)
|
||||||
|
textInputEditText = findViewById(R.id.bs_textInputEditText)
|
||||||
|
toggleGroupContainer = findViewById(R.id.bs_toggleGroupContainer)
|
||||||
|
toggleGroup = findViewById(R.id.bs_toggleGroup)
|
||||||
|
toggleGroupTitleView = findViewById(R.id.bs_toggleGroupTitle)
|
||||||
|
list = findViewById(R.id.bs_list)
|
||||||
|
|
||||||
|
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
|
||||||
|
|
||||||
|
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||||
|
|
||||||
|
scrimView.setOnTouchListener { _, event ->
|
||||||
|
if (!scrimViewTapToClose)
|
||||||
|
return@setOnTouchListener true
|
||||||
|
if (event.action == MotionEvent.ACTION_UP && bottomSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN) {
|
||||||
|
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomSheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||||
|
override fun onSlide(v: View, p1: Float) {}
|
||||||
|
override fun onStateChanged(v: View, newState: Int) {
|
||||||
|
if (newState == BottomSheetBehavior.STATE_HIDDEN && bottomSheetVisible) {
|
||||||
|
bottomSheetVisible = false
|
||||||
|
bottomSheet.scrollTo(0, 0)
|
||||||
|
if (scrimViewEnabled)
|
||||||
|
Anim.fadeOut(scrimView, 300, null)
|
||||||
|
// steal the focus from any EditTexts
|
||||||
|
dragBar.requestFocus()
|
||||||
|
hideKeyboard()
|
||||||
|
onCloseListener?.invoke()
|
||||||
|
}
|
||||||
|
else if (!bottomSheetVisible) {
|
||||||
|
bottomSheetVisible = true
|
||||||
|
if (scrimViewEnabled)
|
||||||
|
Anim.fadeIn(scrimView, 300, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
content.background.colorFilter = PorterDuffColorFilter(
|
||||||
|
elevateSurface(context, dp = 8),
|
||||||
|
PorterDuff.Mode.SRC_ATOP
|
||||||
|
)
|
||||||
|
|
||||||
|
// steal the focus from any EditTexts
|
||||||
|
dragBar.requestFocus()
|
||||||
|
|
||||||
|
list.apply {
|
||||||
|
setHasFixedSize(true)
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
adapter = this@NavBottomSheet.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleGroup.addOnButtonCheckedListener(toggleGroupCheckedListener)
|
||||||
|
textInputEditText.addTextChangedListener(textInputWatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
var onCloseListener: (() -> Unit)? = null
|
||||||
|
|
||||||
|
/* _____ _
|
||||||
|
|_ _| |
|
||||||
|
| | | |_ ___ _ __ ___ ___
|
||||||
|
| | | __/ _ \ '_ ` _ \/ __|
|
||||||
|
_| |_| || __/ | | | | \__ \
|
||||||
|
|_____|\__\___|_| |_| |_|__*/
|
||||||
|
operator fun plusAssign(item: IBottomSheetItem<*>) {
|
||||||
|
appendItem(item)
|
||||||
|
}
|
||||||
|
fun appendItem(item: IBottomSheetItem<*>) {
|
||||||
|
items.add(item)
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
//adapter.notifyItemInserted(items.size - 1)
|
||||||
|
}
|
||||||
|
fun appendItems(vararg items: IBottomSheetItem<*>) {
|
||||||
|
this.items.addAll(items)
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
//adapter.notifyItemRangeInserted(this.items.size - items.size, items.size)
|
||||||
|
}
|
||||||
|
fun prependItem(item: IBottomSheetItem<*>) {
|
||||||
|
items.add(0, item)
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
//adapter.notifyItemInserted(0)
|
||||||
|
}
|
||||||
|
fun prependItems(vararg items: IBottomSheetItem<*>) {
|
||||||
|
this.items.addAll(0, items.toList())
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
//adapter.notifyItemRangeInserted(0, items.size)
|
||||||
|
}
|
||||||
|
fun addItemAt(index: Int, item: IBottomSheetItem<*>) {
|
||||||
|
items.add(index, item)
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
//adapter.notifyItemInserted(index)
|
||||||
|
}
|
||||||
|
fun removeItemById(id: Int) {
|
||||||
|
items.filterNot { it.id == id }
|
||||||
|
}
|
||||||
|
fun removeItemAt(index: Int) {
|
||||||
|
items.removeAt(index)
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
//adapter.notifyItemRemoved(index)
|
||||||
|
}
|
||||||
|
fun removeAllItems() {
|
||||||
|
items.clear()
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
fun removeAllStatic() {
|
||||||
|
items.removeAll { !it.isContextual }
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
fun removeAllContextual() {
|
||||||
|
items.removeAll { it.isContextual }
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
fun removeSeparators() {
|
||||||
|
items.removeAll { it is BottomSheetSeparatorItem }
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getItemById(id: Int, run: (it: IBottomSheetItem<*>?) -> Unit) {
|
||||||
|
items.singleOrNull { it.id == id }.also {
|
||||||
|
run(it)
|
||||||
|
if (it != null)
|
||||||
|
adapter.notifyItemChanged(items.indexOf(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun getItemByIndex(index: Int, run: (it: IBottomSheetItem<*>?) -> Unit) {
|
||||||
|
items.getOrNull(index).also {
|
||||||
|
run(it)
|
||||||
|
if (it != null)
|
||||||
|
adapter.notifyItemChanged(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* _______ _
|
||||||
|
|__ __| | |
|
||||||
|
| | ___ __ _ __ _| | ___ __ _ _ __ ___ _ _ _ __
|
||||||
|
| |/ _ \ / _` |/ _` | |/ _ \ / _` | '__/ _ \| | | | '_ \
|
||||||
|
| | (_) | (_| | (_| | | __/ | (_| | | | (_) | |_| | |_) |
|
||||||
|
|_|\___/ \__, |\__, |_|\___| \__, |_| \___/ \__,_| .__/
|
||||||
|
__/ | __/ | __/ | | |
|
||||||
|
|___/ |___/ |___/ |*/
|
||||||
|
var toggleGroupEnabled
|
||||||
|
get() = toggleGroupContainer.visibility == View.VISIBLE
|
||||||
|
set(value) { toggleGroupContainer.visibility = if (value) View.VISIBLE else View.GONE }
|
||||||
|
var toggleGroupTitle
|
||||||
|
get() = toggleGroupTitleView.text.toString()
|
||||||
|
set(value) { toggleGroupTitleView.text = value }
|
||||||
|
var toggleGroupSelectionMode: Int = TOGGLE_GROUP_SORTING_ORDER
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
toggleGroup.isSingleSelection = value != TOGGLE_GROUP_MULTIPLE_SELECTION
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleGroupGetIconicsDrawable(context: Context, icon: IIcon?): Drawable? {
|
||||||
|
if (icon == null)
|
||||||
|
return null
|
||||||
|
return IconicsDrawable(context, icon).apply {
|
||||||
|
sizeDp = 24
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleGroupAddItem(id: Int, text: String, @DrawableRes icon: Int, defaultSortOrder: Int = SORT_MODE_ASCENDING) {
|
||||||
|
toggleGroupAddItem(id, text, context.getDrawableFromRes(icon), defaultSortOrder)
|
||||||
|
}
|
||||||
|
fun toggleGroupAddItem(id: Int, text: String, icon: IIcon, defaultSortOrder: Int = SORT_MODE_ASCENDING) {
|
||||||
|
toggleGroupAddItem(id, text, toggleGroupGetIconicsDrawable(context, icon), defaultSortOrder)
|
||||||
|
}
|
||||||
|
fun toggleGroupAddItem(id: Int, text: String, icon: Drawable?, defaultSortOrder: Int = SORT_MODE_ASCENDING) {
|
||||||
|
if (id < 0)
|
||||||
|
throw IllegalArgumentException("ID cannot be less than 0")
|
||||||
|
toggleGroup.addView(MaterialButton(context, null, R.attr.materialButtonOutlinedStyle).apply {
|
||||||
|
this.id = id + 1
|
||||||
|
this.tag = defaultSortOrder
|
||||||
|
this.text = text
|
||||||
|
this.icon = icon
|
||||||
|
}, WRAP_CONTENT, WRAP_CONTENT)
|
||||||
|
}
|
||||||
|
fun toggleGroupCheck(id: Int) {
|
||||||
|
toggleGroup.check(id)
|
||||||
|
}
|
||||||
|
fun toggleGroupRemoveItems() {
|
||||||
|
toggleGroup.removeAllViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val toggleGroupCheckedListener = MaterialButtonToggleGroup.OnButtonCheckedListener { group, checkedId, isChecked ->
|
||||||
|
if (group.checkedButtonId == View.NO_ID) {
|
||||||
|
group.check(checkedId)
|
||||||
|
return@OnButtonCheckedListener
|
||||||
|
}
|
||||||
|
/* TAG bit order
|
||||||
|
* bit 0 = default sorting mode
|
||||||
|
* bit 1 = is checked
|
||||||
|
* bit 2 = current sorting mode
|
||||||
|
*/
|
||||||
|
if (toggleGroupSelectionMode == TOGGLE_GROUP_SORTING_ORDER) {
|
||||||
|
val button = group.findViewById<MaterialButton>(checkedId) ?: return@OnButtonCheckedListener
|
||||||
|
var tag = button.tag as Int
|
||||||
|
var sortingMode: Int? = null
|
||||||
|
if (isChecked) {
|
||||||
|
sortingMode = if (tag and 0b010 == 1 shl 1) {
|
||||||
|
/* the view is checked and clicked once again */
|
||||||
|
if (tag and 0b100 == SORT_MODE_ASCENDING shl 2) SORT_MODE_DESCENDING else SORT_MODE_ASCENDING
|
||||||
|
} else {
|
||||||
|
/* the view is first clicked so use the default sorting mode */
|
||||||
|
if (tag and 0b001 == SORT_MODE_ASCENDING) SORT_MODE_ASCENDING else SORT_MODE_DESCENDING
|
||||||
|
}
|
||||||
|
tag = tag and 0b001 /* retain only default sorting mode */
|
||||||
|
tag = tag or 0b010 /* set as checked */
|
||||||
|
tag = tag or (sortingMode shl 2) /* set new sorting mode */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tag = tag and 0b001 /* retain only default sorting mode */
|
||||||
|
}
|
||||||
|
button.tag = tag
|
||||||
|
button.icon = toggleGroupGetIconicsDrawable(context, when (sortingMode) {
|
||||||
|
SORT_MODE_ASCENDING -> NavLibFont.Icon.nav_sort_ascending
|
||||||
|
SORT_MODE_DESCENDING -> NavLibFont.Icon.nav_sort_descending
|
||||||
|
else -> null
|
||||||
|
})
|
||||||
|
if (sortingMode != null) {
|
||||||
|
toggleGroupSortingOrderListener?.invoke(checkedId, sortingMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (toggleGroup.isSingleSelection && isChecked) {
|
||||||
|
toggleGroupSingleSelectionListener?.invoke(checkedId - 1)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toggleGroupMultipleSelectionListener?.invoke(checkedId - 1, isChecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var toggleGroupSingleSelectionListener: ((id: Int) -> Unit)? = null
|
||||||
|
var toggleGroupMultipleSelectionListener: ((id: Int, checked: Boolean) -> Unit)? = null
|
||||||
|
var toggleGroupSortingOrderListener: ((id: Int, sortMode: Int) -> Unit)? = null
|
||||||
|
|
||||||
|
|
||||||
|
/* _______ _ _ _
|
||||||
|
|__ __| | | (_) | |
|
||||||
|
| | _____ _| |_ _ _ __ _ __ _ _| |_
|
||||||
|
| |/ _ \ \/ / __| | | '_ \| '_ \| | | | __|
|
||||||
|
| | __/> <| |_ | | | | | |_) | |_| | |_
|
||||||
|
|_|\___/_/\_\\__| |_|_| |_| .__/ \__,_|\__|
|
||||||
|
| |
|
||||||
|
|*/
|
||||||
|
var textInputEnabled
|
||||||
|
get() = textInputLayout.visibility == View.VISIBLE
|
||||||
|
set(value) { textInputLayout.visibility = if (value) View.VISIBLE else View.GONE }
|
||||||
|
var textInputText
|
||||||
|
get() = textInputEditText.text.toString()
|
||||||
|
set(value) { textInputEditText.setText(value) }
|
||||||
|
var textInputHint
|
||||||
|
get() = textInputLayout.hint.toString()
|
||||||
|
set(value) { textInputLayout.hint = value }
|
||||||
|
var textInputHelperText
|
||||||
|
get() = textInputLayout.helperText.toString()
|
||||||
|
set(value) { textInputLayout.helperText = value }
|
||||||
|
var textInputError
|
||||||
|
get() = textInputLayout.error
|
||||||
|
set(value) { textInputLayout.error = value }
|
||||||
|
var textInputIcon: Any?
|
||||||
|
get() = textInputLayout.startIconDrawable
|
||||||
|
set(value) {
|
||||||
|
textInputLayout.startIconDrawable = when (value) {
|
||||||
|
is Drawable -> value
|
||||||
|
is IIcon -> IconicsDrawable(context).apply {
|
||||||
|
icon = value
|
||||||
|
sizeDp = 24
|
||||||
|
// colorInt = Color.BLACK
|
||||||
|
}
|
||||||
|
is Int -> context.getDrawableFromRes(value)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var textInputWatcher = object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable?) {}
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
textInputChangedListener?.onTextChanged(s?.toString() ?: "", start, before, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OnTextInputChangedListener {
|
||||||
|
fun onTextChanged(s: String, start: Int, before: Int, count: Int)
|
||||||
|
}
|
||||||
|
var textInputChangedListener: OnTextInputChangedListener? = null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun dispatchBottomBarEvent(event: MotionEvent) {
|
||||||
|
val location = IntArray(2)
|
||||||
|
bottomSheet.getLocationOnScreen(location)
|
||||||
|
event.setLocation(event.rawX - location[0], event.rawY - location[1])
|
||||||
|
bottomSheet.dispatchTouchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setContentPadding(left: Int, top: Int, right: Int, bottom: Int) {
|
||||||
|
content.setPadding(left, top, right, bottom)
|
||||||
|
}
|
||||||
|
fun getContentView() = content
|
||||||
|
|
||||||
|
var isOpen
|
||||||
|
get() = bottomSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN
|
||||||
|
set(value) {
|
||||||
|
bottomSheetBehavior.state = if (value) BottomSheetBehavior.STATE_EXPANDED else BottomSheetBehavior.STATE_HIDDEN
|
||||||
|
}
|
||||||
|
fun open() { isOpen = true }
|
||||||
|
fun close() { isOpen = false }
|
||||||
|
fun toggle() {
|
||||||
|
if (!enable)
|
||||||
|
return
|
||||||
|
isOpen = !isOpen
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package pl.szczodrzynski.navlib.bottomsheet
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
class ViewHolderProvider {
|
||||||
|
private val viewHolderFactories = hashMapOf<Int, Pair<Int, (View) -> RecyclerView.ViewHolder>>()
|
||||||
|
|
||||||
|
fun provideViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
val (layoutId: Int, f: (View) -> RecyclerView.ViewHolder) = viewHolderFactories[viewType]!!
|
||||||
|
val view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, viewGroup, false)
|
||||||
|
return f(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerViewHolderFactory(viewType: Int, layoutId: Int, viewHolderFactory: (View) -> RecyclerView.ViewHolder) {
|
||||||
|
viewHolderFactories[viewType] = Pair(layoutId, viewHolderFactory)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
package pl.szczodrzynski.navlib.bottomsheet.items
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
|
import com.mikepenz.iconics.typeface.IIcon
|
||||||
|
import com.mikepenz.iconics.utils.sizeDp
|
||||||
|
import pl.szczodrzynski.navlib.ImageHolder
|
||||||
|
import pl.szczodrzynski.navlib.R
|
||||||
|
import pl.szczodrzynski.navlib.colorAttr
|
||||||
|
import pl.szczodrzynski.navlib.getColorFromAttr
|
||||||
|
|
||||||
|
data class BottomSheetPrimaryItem(override val isContextual: Boolean = true) : IBottomSheetItem<BottomSheetPrimaryItem.ViewHolder> {
|
||||||
|
|
||||||
|
/*_ _
|
||||||
|
| | | |
|
||||||
|
| | __ _ _ _ ___ _ _| |_
|
||||||
|
| | / _` | | | |/ _ \| | | | __|
|
||||||
|
| |___| (_| | |_| | (_) | |_| | |_
|
||||||
|
|______\__,_|\__, |\___/ \__,_|\__|
|
||||||
|
__/ |
|
||||||
|
|__*/
|
||||||
|
override var id: Int = -1
|
||||||
|
override val viewType: Int
|
||||||
|
get() = 1
|
||||||
|
override val layoutId: Int
|
||||||
|
get() = R.layout.nav_bs_item_primary
|
||||||
|
|
||||||
|
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val root = itemView.findViewById<View>(R.id.item_root)
|
||||||
|
val image = itemView.findViewById<ImageView>(R.id.item_icon)
|
||||||
|
val text = itemView.findViewById<TextView>(R.id.item_text)
|
||||||
|
val description = itemView.findViewById<TextView>(R.id.item_description)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindViewHolder(viewHolder: ViewHolder) {
|
||||||
|
viewHolder.root.setOnClickListener(onClickListener)
|
||||||
|
|
||||||
|
viewHolder.image.setImageDrawable(IconicsDrawable(viewHolder.text.context).apply {
|
||||||
|
icon = iconicsIcon
|
||||||
|
colorAttr(viewHolder.text.context, android.R.attr.textColorSecondary)
|
||||||
|
sizeDp = 24
|
||||||
|
})
|
||||||
|
|
||||||
|
viewHolder.description.visibility = View.VISIBLE
|
||||||
|
when {
|
||||||
|
descriptionRes != null -> viewHolder.description.setText(descriptionRes!!)
|
||||||
|
description != null -> viewHolder.description.text = description
|
||||||
|
else -> viewHolder.description.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
titleRes != null -> viewHolder.text.setText(titleRes!!)
|
||||||
|
else -> viewHolder.text.text = title
|
||||||
|
}
|
||||||
|
viewHolder.text.setTextColor(getColorFromAttr(viewHolder.text.context, android.R.attr.textColorPrimary))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*_____ _
|
||||||
|
| __ \ | |
|
||||||
|
| | | | __ _| |_ __ _
|
||||||
|
| | | |/ _` | __/ _` |
|
||||||
|
| |__| | (_| | || (_| |
|
||||||
|
|_____/ \__,_|\__\__,*/
|
||||||
|
var title: CharSequence? = null
|
||||||
|
@StringRes
|
||||||
|
var titleRes: Int? = null
|
||||||
|
var description: CharSequence? = null
|
||||||
|
@StringRes
|
||||||
|
var descriptionRes: Int? = null
|
||||||
|
var icon: ImageHolder? = null
|
||||||
|
var iconicsIcon: IIcon? = null
|
||||||
|
var onClickListener: View.OnClickListener? = null
|
||||||
|
|
||||||
|
fun withId(id: Int): BottomSheetPrimaryItem {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withTitle(title: CharSequence): BottomSheetPrimaryItem {
|
||||||
|
this.title = title
|
||||||
|
this.titleRes = null
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
fun withTitle(@StringRes title: Int): BottomSheetPrimaryItem {
|
||||||
|
this.title = null
|
||||||
|
this.titleRes = title
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withDescription(description: CharSequence): BottomSheetPrimaryItem {
|
||||||
|
this.description = description
|
||||||
|
this.descriptionRes = null
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
fun withDescription(@StringRes description: Int): BottomSheetPrimaryItem {
|
||||||
|
this.description = null
|
||||||
|
this.descriptionRes = description
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withIcon(icon: Drawable): BottomSheetPrimaryItem {
|
||||||
|
this.icon = ImageHolder(icon)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
fun withIcon(@DrawableRes icon: Int): BottomSheetPrimaryItem {
|
||||||
|
this.icon = ImageHolder(icon)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
fun withIcon(icon: IIcon): BottomSheetPrimaryItem {
|
||||||
|
this.iconicsIcon = icon
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withOnClickListener(onClickListener: View.OnClickListener): BottomSheetPrimaryItem {
|
||||||
|
this.onClickListener = onClickListener
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package pl.szczodrzynski.navlib.bottomsheet.items
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import pl.szczodrzynski.navlib.R
|
||||||
|
|
||||||
|
data class BottomSheetSeparatorItem(override val isContextual: Boolean = true) : IBottomSheetItem<BottomSheetSeparatorItem.ViewHolder> {
|
||||||
|
|
||||||
|
/*_ _
|
||||||
|
| | | |
|
||||||
|
| | __ _ _ _ ___ _ _| |_
|
||||||
|
| | / _` | | | |/ _ \| | | | __|
|
||||||
|
| |___| (_| | |_| | (_) | |_| | |_
|
||||||
|
|______\__,_|\__, |\___/ \__,_|\__|
|
||||||
|
__/ |
|
||||||
|
|__*/
|
||||||
|
override var id: Int = -1
|
||||||
|
override val viewType: Int
|
||||||
|
get() = 2
|
||||||
|
override val layoutId: Int
|
||||||
|
get() = R.layout.nav_bs_item_separator
|
||||||
|
|
||||||
|
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||||
|
|
||||||
|
override fun bindViewHolder(viewHolder: ViewHolder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package pl.szczodrzynski.navlib.bottomsheet.items
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
interface IBottomSheetItem<T> {
|
||||||
|
|
||||||
|
val isContextual: Boolean
|
||||||
|
var id: Int
|
||||||
|
val viewType: Int
|
||||||
|
val layoutId: Int
|
||||||
|
|
||||||
|
fun bindViewHolder(viewHolder: T)
|
||||||
|
fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) {
|
||||||
|
bindViewHolder(viewHolder as T)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package pl.szczodrzynski.navlib.drawer
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.widget.ImageView
|
||||||
|
import pl.szczodrzynski.navlib.ImageHolder
|
||||||
|
|
||||||
|
interface IDrawerProfile {
|
||||||
|
val id: Int
|
||||||
|
var name: String
|
||||||
|
var subname: String?
|
||||||
|
var image: String?
|
||||||
|
|
||||||
|
fun getImageDrawable(context: Context): Drawable?
|
||||||
|
fun getImageHolder(context: Context): ImageHolder?
|
||||||
|
fun applyImageTo(imageView: ImageView)
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package pl.szczodrzynski.navlib.drawer
|
||||||
|
|
||||||
|
interface IUnreadCounter {
|
||||||
|
var profileId: Int
|
||||||
|
var type: Int
|
||||||
|
var drawerItemId: Int?
|
||||||
|
var count: Int
|
||||||
|
}
|
730
navlib/src/main/java/pl/szczodrzynski/navlib/drawer/NavDrawer.kt
Normal file
730
navlib/src/main/java/pl/szczodrzynski/navlib/drawer/NavDrawer.kt
Normal file
@ -0,0 +1,730 @@
|
|||||||
|
package pl.szczodrzynski.navlib.drawer
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.customview.widget.ViewDragHelper
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
import com.mikepenz.fastadapter.IAdapter
|
||||||
|
import com.mikepenz.itemanimators.AlphaCrossFadeAnimator
|
||||||
|
import com.mikepenz.materialdrawer.*
|
||||||
|
import com.mikepenz.materialdrawer.holder.BadgeStyle
|
||||||
|
import com.mikepenz.materialdrawer.holder.ColorHolder
|
||||||
|
import com.mikepenz.materialdrawer.holder.StringHolder
|
||||||
|
import com.mikepenz.materialdrawer.model.BaseDrawerItem
|
||||||
|
import com.mikepenz.materialdrawer.model.MiniProfileDrawerItem
|
||||||
|
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||||
|
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
||||||
|
import com.mikepenz.materialdrawer.model.interfaces.*
|
||||||
|
import com.mikepenz.materialdrawer.util.*
|
||||||
|
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
||||||
|
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
|
||||||
|
import com.mikepenz.materialdrawer.widget.MiniDrawerSliderView
|
||||||
|
import com.mikepenz.materialize.util.UIUtils
|
||||||
|
import pl.szczodrzynski.navlib.*
|
||||||
|
import pl.szczodrzynski.navlib.R
|
||||||
|
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
|
||||||
|
|
||||||
|
class NavDrawer(
|
||||||
|
val context: Context,
|
||||||
|
val drawerLayout: DrawerLayout,
|
||||||
|
val drawerContainerLandscape: FrameLayout,
|
||||||
|
val drawerContainerPortrait: FrameLayout,
|
||||||
|
val miniDrawerElevation: View
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val DRAWER_MODE_NORMAL = 0
|
||||||
|
private const val DRAWER_MODE_MINI = 1
|
||||||
|
private const val DRAWER_MODE_FIXED = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var activity: Activity
|
||||||
|
private val resources: Resources
|
||||||
|
get() = context.resources
|
||||||
|
|
||||||
|
internal lateinit var toolbar: NavToolbar
|
||||||
|
internal lateinit var bottomBar: NavBottomBar
|
||||||
|
|
||||||
|
private lateinit var drawer: MaterialDrawerSliderView
|
||||||
|
private lateinit var accountHeader: AccountHeaderView
|
||||||
|
private lateinit var miniDrawer: MiniDrawerSliderView
|
||||||
|
|
||||||
|
private var drawerMode: Int = DRAWER_MODE_NORMAL
|
||||||
|
private var selection: Int = -1
|
||||||
|
|
||||||
|
lateinit var badgeStyle: BadgeStyle
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
fun init(activity: Activity) {
|
||||||
|
this.activity = activity
|
||||||
|
|
||||||
|
/*badgeStyle = BadgeStyle(
|
||||||
|
R.drawable.material_drawer_badge,
|
||||||
|
getColorFromAttr(context, R.attr.colorError),
|
||||||
|
getColorFromAttr(context, R.attr.colorError),
|
||||||
|
getColorFromAttr(context, R.attr.colorOnError)
|
||||||
|
)*/
|
||||||
|
|
||||||
|
badgeStyle = BadgeStyle().apply {
|
||||||
|
textColor = ColorHolder.fromColor(Color.WHITE)
|
||||||
|
color = ColorHolder.fromColor(0xffd32f2f.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
|
||||||
|
override fun onDrawerStateChanged(newState: Int) {}
|
||||||
|
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {}
|
||||||
|
override fun onDrawerClosed(drawerView: View) {
|
||||||
|
drawerClosedListener?.invoke()
|
||||||
|
profileSelectionClose()
|
||||||
|
}
|
||||||
|
override fun onDrawerOpened(drawerView: View) {
|
||||||
|
drawerOpenedListener?.invoke()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
accountHeader = AccountHeaderView(context).apply {
|
||||||
|
headerBackground = ImageHolder(R.drawable.header)
|
||||||
|
displayBadgesOnSmallProfileImages = true
|
||||||
|
|
||||||
|
onAccountHeaderListener = { view, profile, current ->
|
||||||
|
if (profile is ProfileSettingDrawerItem) {
|
||||||
|
drawerProfileSettingClickListener?.invoke(profile.identifier.toInt(), view) ?: false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateBadges()
|
||||||
|
if (current) {
|
||||||
|
close()
|
||||||
|
profileSelectionClose()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(drawerProfileSelectedListener?.invoke(profile.identifier.toInt(), profile, current, view) ?: false).also {
|
||||||
|
setToolbarProfileImage(profileList.singleOrNull { it.id == profile.identifier.toInt() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccountHeaderItemLongClickListener = { view, profile, current ->
|
||||||
|
if (profile is ProfileSettingDrawerItem) {
|
||||||
|
drawerProfileSettingLongClickListener?.invoke(profile.identifier.toInt(), view) ?: true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drawerProfileLongClickListener?.invoke(profile.identifier.toInt(), profile, current, view) ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccountHeaderProfileImageListener = { view, profile, current ->
|
||||||
|
drawerProfileImageClickListener?.invoke(profile.identifier.toInt(), profile, current, view) ?: false
|
||||||
|
}
|
||||||
|
//.withTextColor(ContextCompat.getColor(context, R.color.material_drawer_dark_primary_text))
|
||||||
|
}
|
||||||
|
|
||||||
|
drawer = MaterialDrawerSliderView(context).apply {
|
||||||
|
accountHeader = this@NavDrawer.accountHeader
|
||||||
|
itemAnimator = AlphaCrossFadeAnimator()
|
||||||
|
//hasStableIds = true
|
||||||
|
|
||||||
|
onDrawerItemClickListener = { _, drawerItem, position ->
|
||||||
|
if (drawerItem.identifier.toInt() == selection) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val consumed = drawerItemSelectedListener?.invoke(drawerItem.identifier.toInt(), position, drawerItem)
|
||||||
|
if (consumed == false || !drawerItem.isSelectable) {
|
||||||
|
setSelection(selection, false)
|
||||||
|
consumed == false
|
||||||
|
}
|
||||||
|
else if (consumed == true) {
|
||||||
|
when (drawerItem) {
|
||||||
|
is DrawerPrimaryItem -> toolbar.title = drawerItem.appTitle ?: drawerItem.name?.getText(context) ?: ""
|
||||||
|
is BaseDrawerItem<*, *> -> toolbar.title = drawerItem.name?.getText(context) ?: ""
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrawerItemLongClickListener = { _, drawerItem, position ->
|
||||||
|
drawerItemLongClickListener?.invoke(drawerItem.identifier.toInt(), position, drawerItem) ?: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
miniDrawer = MiniDrawerSliderView(context).apply {
|
||||||
|
drawer = this@NavDrawer.drawer
|
||||||
|
includeSecondaryDrawerItems = false
|
||||||
|
try {
|
||||||
|
this::class.java.getDeclaredField("onMiniDrawerItemClickListener").let {
|
||||||
|
it.isAccessible = true
|
||||||
|
it.set(this, { v: View?, position: Int, item: IDrawerItem<*>, type: Int ->
|
||||||
|
if (item is MiniProfileDrawerItem) {
|
||||||
|
profileSelectionOpen()
|
||||||
|
open()
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (_: Exception) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMiniDrawer()
|
||||||
|
|
||||||
|
toolbar.profileImageClickListener = {
|
||||||
|
profileSelectionOpen()
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
|
||||||
|
val configuration = context.resources.configuration
|
||||||
|
decideDrawerMode(
|
||||||
|
configuration.orientation,
|
||||||
|
configuration.screenWidthDp,
|
||||||
|
configuration.screenHeightDp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* _____ _
|
||||||
|
|_ _| |
|
||||||
|
| | | |_ ___ _ __ ___ ___
|
||||||
|
| | | __/ _ \ '_ ` _ \/ __|
|
||||||
|
_| |_| || __/ | | | | \__ \
|
||||||
|
|_____|\__\___|_| |_| |_|__*/
|
||||||
|
operator fun plusAssign(item: IDrawerItem<*>) {
|
||||||
|
appendItem(item)
|
||||||
|
}
|
||||||
|
fun appendItem(item: IDrawerItem<*>) {
|
||||||
|
drawer.addItems(item)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
fun appendItems(vararg items: IDrawerItem<*>) {
|
||||||
|
drawer.addItems(*items)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
fun prependItem(item: IDrawerItem<*>) {
|
||||||
|
drawer.addItemAtPosition(0, item)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
fun prependItems(vararg items: IDrawerItem<*>) {
|
||||||
|
drawer.addItemsAtPosition(0, *items)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
fun addItemAt(index: Int, item: IDrawerItem<*>) {
|
||||||
|
drawer.addItemAtPosition(index, item)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
fun addItemsAt(index: Int, vararg items: IDrawerItem<*>) {
|
||||||
|
drawer.addItemsAtPosition(index, *items)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
fun removeItemById(id: Int) {
|
||||||
|
drawer.removeItems(id.toLong())
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
fun removeItemAt(index: Int) {
|
||||||
|
drawer.removeItemByPosition(index)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
fun removeAllItems() {
|
||||||
|
drawer.removeAllItems()
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getItemById(id: Int, run: (it: IDrawerItem<*>?) -> Unit) {
|
||||||
|
drawer.getDrawerItem(id.toLong()).also {
|
||||||
|
run(it)
|
||||||
|
if (it != null)
|
||||||
|
drawer.updateItem(it)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun getItemByIndex(index: Int, run: (it: IDrawerItem<*>?) -> Unit) {
|
||||||
|
drawer.itemAdapter.itemList.get(index).also {
|
||||||
|
run(it)
|
||||||
|
if (it != null)
|
||||||
|
drawer.updateItem(it)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setItems(vararg items: IDrawerItem<*>) {
|
||||||
|
drawer.removeAllItems()
|
||||||
|
drawer.addItems(*items)
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* _____ _ _ _ _ _
|
||||||
|
| __ \ (_) | | | | | | | |
|
||||||
|
| |__) | __ ___ ____ _| |_ ___ _ __ ___ ___| |_| |__ ___ __| |___
|
||||||
|
| ___/ '__| \ \ / / _` | __/ _ \ | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
|
||||||
|
| | | | | |\ V / (_| | || __/ | | | | | | __/ |_| | | | (_) | (_| \__ \
|
||||||
|
|_| |_| |_| \_/ \__,_|\__\___| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|__*/
|
||||||
|
private fun drawerSetDragMargin(size: Float) {
|
||||||
|
try {
|
||||||
|
val mDrawerLayout = drawerLayout
|
||||||
|
val mDragger = mDrawerLayout::class.java.getDeclaredField(
|
||||||
|
"mLeftDragger"
|
||||||
|
)
|
||||||
|
mDragger.isAccessible = true
|
||||||
|
val draggerObj = mDragger.get(mDrawerLayout) as ViewDragHelper?
|
||||||
|
draggerObj?.edgeSize = size.toInt()
|
||||||
|
|
||||||
|
// update for SDK >= 29 (Android 10)
|
||||||
|
val useSystemInsets = mDrawerLayout::class.java.getDeclaredField(
|
||||||
|
"sEdgeSizeUsingSystemGestureInsets"
|
||||||
|
)
|
||||||
|
useSystemInsets.isAccessible = true
|
||||||
|
useSystemInsets.set(null, false)
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
Toast.makeText(context, "Oops, proguard works", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var miniDrawerVisiblePortrait: Boolean? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
val configuration = context.resources.configuration
|
||||||
|
decideDrawerMode(
|
||||||
|
configuration.orientation,
|
||||||
|
configuration.screenWidthDp,
|
||||||
|
configuration.screenHeightDp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var miniDrawerVisibleLandscape: Boolean? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
val configuration = context.resources.configuration
|
||||||
|
decideDrawerMode(
|
||||||
|
configuration.orientation,
|
||||||
|
configuration.screenWidthDp,
|
||||||
|
configuration.screenHeightDp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun decideDrawerMode(orientation: Int, widthDp: Int, heightDp: Int) {
|
||||||
|
val drawerLayoutParams = DrawerLayout.LayoutParams(WRAP_CONTENT, MATCH_PARENT).apply {
|
||||||
|
gravity = Gravity.START
|
||||||
|
}
|
||||||
|
val fixedLayoutParams = FrameLayout.LayoutParams(UIUtils.convertDpToPixel(300f, context).toInt(), MATCH_PARENT)
|
||||||
|
|
||||||
|
Log.d("NavLib", "Deciding drawer mode:")
|
||||||
|
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||||
|
Log.d("NavLib", "- fixed container disabled")
|
||||||
|
|
||||||
|
if (drawerContainerLandscape.childCount > 0) {
|
||||||
|
drawerContainerLandscape.removeAllViews()
|
||||||
|
}
|
||||||
|
Log.d("NavLib", "- mini drawer land disabled")
|
||||||
|
|
||||||
|
if (drawerLayout.indexOfChild(drawer) == -1) {
|
||||||
|
drawerLayout.addView(drawer, drawerLayoutParams)
|
||||||
|
}
|
||||||
|
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||||
|
Log.d("NavLib", "- slider enabled")
|
||||||
|
|
||||||
|
if ((widthDp >= 480 && miniDrawerVisiblePortrait != false) || miniDrawerVisiblePortrait == true) {
|
||||||
|
if (drawerContainerPortrait.indexOfChild(miniDrawer) == -1)
|
||||||
|
drawerContainerPortrait.addView(miniDrawer)
|
||||||
|
Log.d("NavLib", "- mini drawer port enabled")
|
||||||
|
drawerSetDragMargin(72 * resources.displayMetrics.density)
|
||||||
|
drawerMode = DRAWER_MODE_MINI
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (drawerContainerPortrait.childCount > 0) {
|
||||||
|
drawerContainerPortrait.removeAllViews()
|
||||||
|
}
|
||||||
|
Log.d("NavLib", "- mini drawer port disabled")
|
||||||
|
drawerSetDragMargin(20 * resources.displayMetrics.density)
|
||||||
|
drawerMode = DRAWER_MODE_NORMAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (drawerContainerPortrait.childCount > 0) {
|
||||||
|
drawerContainerPortrait.removeAllViews()
|
||||||
|
}
|
||||||
|
Log.d("NavLib", "- mini drawer port disabled")
|
||||||
|
|
||||||
|
if ((widthDp in 480 until 900 && miniDrawerVisibleLandscape != false) || miniDrawerVisibleLandscape == true) {
|
||||||
|
if (drawerContainerLandscape.indexOfChild(miniDrawer) == -1)
|
||||||
|
drawerContainerLandscape.addView(miniDrawer)
|
||||||
|
Log.d("NavLib", "- mini drawer land enabled")
|
||||||
|
drawerSetDragMargin(72 * resources.displayMetrics.density)
|
||||||
|
drawerMode = DRAWER_MODE_MINI
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (drawerContainerLandscape.childCount > 0) {
|
||||||
|
drawerContainerLandscape.removeAllViews()
|
||||||
|
}
|
||||||
|
Log.d("NavLib", "- mini drawer land disabled")
|
||||||
|
drawerSetDragMargin(20 * resources.displayMetrics.density)
|
||||||
|
drawerMode = DRAWER_MODE_NORMAL
|
||||||
|
}
|
||||||
|
if (widthDp >= 900) {
|
||||||
|
// screen is big enough to show fixed drawer
|
||||||
|
if (drawerLayout.indexOfChild(drawer) != -1) {
|
||||||
|
// remove from slider
|
||||||
|
drawerLayout.removeView(drawer)
|
||||||
|
}
|
||||||
|
// lock the slider
|
||||||
|
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||||
|
Log.d("NavLib", "- slider disabled")
|
||||||
|
// add to fixed container
|
||||||
|
if (drawerContainerLandscape.indexOfChild(drawer) == -1)
|
||||||
|
drawerContainerLandscape.addView(drawer, fixedLayoutParams)
|
||||||
|
drawer.visibility = View.VISIBLE
|
||||||
|
Log.d("NavLib", "- fixed container enabled")
|
||||||
|
drawerMode = DRAWER_MODE_FIXED
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// screen is too small for the fixed drawer
|
||||||
|
if (drawerContainerLandscape.indexOfChild(drawer) != -1) {
|
||||||
|
// remove from fixed container
|
||||||
|
drawerContainerLandscape.removeView(drawer)
|
||||||
|
}
|
||||||
|
Log.d("NavLib", "- fixed container disabled")
|
||||||
|
// unlock the slider
|
||||||
|
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||||
|
if (drawerLayout.indexOfChild(drawer) == -1) {
|
||||||
|
// add to slider
|
||||||
|
drawerLayout.addView(drawer, drawerLayoutParams)
|
||||||
|
}
|
||||||
|
Log.d("NavLib", "- slider enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
miniDrawerElevation.visibility = if (drawerMode == DRAWER_MODE_MINI || drawerMode == DRAWER_MODE_FIXED) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMiniDrawer() {
|
||||||
|
selection = drawer.selectedItemIdentifier.toInt()
|
||||||
|
//if (drawerMode == DRAWER_MODE_MINI)
|
||||||
|
miniDrawer.createItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* _____ _ _ _ _ _ _
|
||||||
|
| __ \ | | | (_) | | | | | |
|
||||||
|
| |__) | _| |__ | |_ ___ _ __ ___ ___| |_| |__ ___ __| |___
|
||||||
|
| ___/ | | | '_ \| | |/ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
|
||||||
|
| | | |_| | |_) | | | (__ | | | | | | __/ |_| | | | (_) | (_| \__ \
|
||||||
|
|_| \__,_|_.__/|_|_|\___| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|__*/
|
||||||
|
var isOpen
|
||||||
|
get() = drawerLayout.isOpen || drawerMode == DRAWER_MODE_FIXED
|
||||||
|
set(value) {
|
||||||
|
if (drawerMode == DRAWER_MODE_FIXED)
|
||||||
|
return
|
||||||
|
if (value && !isOpen) drawerLayout.open() else if (!value && isOpen) drawerLayout.close()
|
||||||
|
}
|
||||||
|
fun open() { isOpen = true }
|
||||||
|
fun close() { isOpen = false }
|
||||||
|
fun toggle() { isOpen = !isOpen }
|
||||||
|
|
||||||
|
var profileSelectionIsOpen
|
||||||
|
get() = accountHeader.selectionListShown
|
||||||
|
set(value) {
|
||||||
|
if (value != profileSelectionIsOpen)
|
||||||
|
profileSelectionToggle()
|
||||||
|
}
|
||||||
|
fun profileSelectionOpen() { profileSelectionIsOpen = true }
|
||||||
|
fun profileSelectionClose() { profileSelectionIsOpen = false }
|
||||||
|
fun profileSelectionToggle() { accountHeader.selectionListShown = !accountHeader.selectionListShown }
|
||||||
|
|
||||||
|
var drawerOpenedListener: (() -> Unit)? = null
|
||||||
|
var drawerClosedListener: (() -> Unit)? = null
|
||||||
|
var drawerItemSelectedListener: ((id: Int, position: Int, drawerItem: IDrawerItem<*>) -> Boolean)? = null
|
||||||
|
var drawerItemLongClickListener: ((id: Int, position: Int, drawerItem: IDrawerItem<*>) -> Boolean)? = null
|
||||||
|
var drawerProfileSelectedListener: ((id: Int, profile: IProfile, current: Boolean, view: View?) -> Boolean)? = null
|
||||||
|
var drawerProfileLongClickListener: ((id: Int, profile: IProfile, current: Boolean, view: View?) -> Boolean)? = null
|
||||||
|
var drawerProfileImageClickListener: ((id: Int, profile: IProfile, current: Boolean, view: View) -> Boolean)? = null
|
||||||
|
var drawerProfileImageLongClickListener: ((id: Int, profile: IProfile, current: Boolean, view: View) -> Boolean)? = null
|
||||||
|
var drawerProfileListEmptyListener: (() -> Unit)? = null
|
||||||
|
var drawerProfileSettingClickListener: ((id: Int, view: View?) -> Boolean)? = null
|
||||||
|
var drawerProfileSettingLongClickListener: ((id: Int, view: View?) -> Boolean)? = null
|
||||||
|
|
||||||
|
fun miniDrawerEnabled(): Boolean = drawerMode == DRAWER_MODE_MINI
|
||||||
|
fun fixedDrawerEnabled(): Boolean = drawerMode == DRAWER_MODE_FIXED
|
||||||
|
|
||||||
|
fun setSelection(id: Int, fireOnClick: Boolean = true) {
|
||||||
|
Log.d("NavDebug", "setSelection(id = $id, fireOnClick = $fireOnClick)")
|
||||||
|
// seems that this cannot be open, because the itemAdapter has Profile items
|
||||||
|
// instead of normal Drawer items...
|
||||||
|
profileSelectionClose()
|
||||||
|
selection = id
|
||||||
|
|
||||||
|
if (drawer.selectedItemIdentifier != id.toLong()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawer.selectedItemIdentifier != id.toLong() || !fireOnClick)
|
||||||
|
drawer.setSelectionAtPosition(drawer.getPosition(id.toLong()), fireOnClick)
|
||||||
|
|
||||||
|
miniDrawer.setSelection(-1L)
|
||||||
|
if (drawerMode == DRAWER_MODE_MINI)
|
||||||
|
miniDrawer.setSelection(id.toLong())
|
||||||
|
}
|
||||||
|
fun getSelection(): Int = selection
|
||||||
|
|
||||||
|
// TODO 2019-08-27 add methods for Drawable, @DrawableRes
|
||||||
|
fun setAccountHeaderBackground(path: String?) {
|
||||||
|
if (path == null) {
|
||||||
|
accountHeader.headerBackground = ImageHolder(R.drawable.header)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
accountHeader.headerBackground = ImageHolder(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* _____ __ _ _
|
||||||
|
| __ \ / _(_) |
|
||||||
|
| |__) | __ ___ | |_ _| | ___ ___
|
||||||
|
| ___/ '__/ _ \| _| | |/ _ \/ __|
|
||||||
|
| | | | | (_) | | | | | __/\__ \
|
||||||
|
|_| |_| \___/|_| |_|_|\___||__*/
|
||||||
|
private var profileList: MutableList<IDrawerProfile> = mutableListOf()
|
||||||
|
|
||||||
|
fun addProfileSettings(vararg items: ProfileSettingDrawerItem) {
|
||||||
|
accountHeader.profiles?.addAll(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateProfileList() {
|
||||||
|
// remove all profile items
|
||||||
|
val profiles = accountHeader.profiles?.filterNot { it is ProfileDrawerItem } as MutableList<IProfile>?
|
||||||
|
|
||||||
|
if (profileList.isEmpty())
|
||||||
|
drawerProfileListEmptyListener?.invoke()
|
||||||
|
|
||||||
|
profileList.forEachIndexed { index, profile ->
|
||||||
|
val image = profile.getImageHolder(context)
|
||||||
|
ProfileDrawerItem()
|
||||||
|
.withIdentifier(profile.id.toLong())
|
||||||
|
.withName(profile.name)
|
||||||
|
.withEmail(profile.subname)
|
||||||
|
.also { it.icon = image }
|
||||||
|
.withBadgeStyle(badgeStyle)
|
||||||
|
.withNameShown(true)
|
||||||
|
.also { profiles?.add(index, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
accountHeader.profiles = profiles
|
||||||
|
|
||||||
|
updateBadges()
|
||||||
|
updateMiniDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setProfileList(profiles: MutableList<out IDrawerProfile>) {
|
||||||
|
profileList = profiles as MutableList<IDrawerProfile>
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
private var currentProfileObj: IDrawerProfile? = null
|
||||||
|
val profileListEmpty: Boolean
|
||||||
|
get() = profileList.isEmpty()
|
||||||
|
var currentProfile: Int
|
||||||
|
get() = accountHeader.activeProfile?.identifier?.toInt() ?: -1
|
||||||
|
set(value) {
|
||||||
|
Log.d("NavDebug", "currentProfile = $value")
|
||||||
|
accountHeader.setActiveProfile(value.toLong(), false)
|
||||||
|
currentProfileObj = profileList.singleOrNull { it.id == value }
|
||||||
|
setToolbarProfileImage(currentProfileObj)
|
||||||
|
updateBadges()
|
||||||
|
}
|
||||||
|
fun appendProfile(profile: IDrawerProfile) {
|
||||||
|
profileList.add(profile)
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun appendProfiles(vararg profiles: IDrawerProfile) {
|
||||||
|
profileList.addAll(profiles)
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun prependProfile(profile: IDrawerProfile) {
|
||||||
|
profileList.add(0, profile)
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun prependProfiles(vararg profiles: IDrawerProfile) {
|
||||||
|
profileList.addAll(0, profiles.asList())
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun addProfileAt(index: Int, profile: IDrawerProfile) {
|
||||||
|
profileList.add(index, profile)
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun addProfilesAt(index: Int, vararg profiles: IDrawerProfile) {
|
||||||
|
profileList.addAll(index, profiles.asList())
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun removeProfileById(id: Int) {
|
||||||
|
profileList = profileList.filterNot { it.id == id }.toMutableList()
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun removeProfileAt(index: Int) {
|
||||||
|
profileList.removeAt(index)
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun removeAllProfile() {
|
||||||
|
profileList.clear()
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
fun removeAllProfileSettings() {
|
||||||
|
accountHeader.profiles = accountHeader.profiles?.filterNot { it is ProfileSettingDrawerItem }?.toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProfileById(id: Int, run: (it: IDrawerProfile?) -> Unit) {
|
||||||
|
profileList.singleOrNull { it.id == id }.also {
|
||||||
|
run(it)
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun getProfileByIndex(index: Int, run: (it: IDrawerProfile?) -> Unit) {
|
||||||
|
profileList.getOrNull(index).also {
|
||||||
|
run(it)
|
||||||
|
updateProfileList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setToolbarProfileImage(profile: IDrawerProfile?) {
|
||||||
|
toolbar.profileImage = profile?.getImageDrawable(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ____ _
|
||||||
|
| _ \ | |
|
||||||
|
| |_) | __ _ __| | __ _ ___ ___
|
||||||
|
| _ < / _` |/ _` |/ _` |/ _ \/ __|
|
||||||
|
| |_) | (_| | (_| | (_| | __/\__ \
|
||||||
|
|____/ \__,_|\__,_|\__, |\___||___/
|
||||||
|
__/ |
|
||||||
|
|__*/
|
||||||
|
private var unreadCounterList: MutableList<IUnreadCounter> = mutableListOf()
|
||||||
|
private val unreadCounterTypeMap = mutableMapOf<Int, Int>()
|
||||||
|
|
||||||
|
fun updateBadges() {
|
||||||
|
|
||||||
|
currentProfileObj = profileList.singleOrNull { it.id == currentProfile }
|
||||||
|
|
||||||
|
drawer.itemAdapter.itemList.items.forEachIndexed { index, item ->
|
||||||
|
if (item is Badgeable) {
|
||||||
|
item.badge = null
|
||||||
|
drawer.updateItem(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var profileCounters = listOf<IUnreadCounter>()
|
||||||
|
|
||||||
|
accountHeader.profiles?.forEach { profile ->
|
||||||
|
if (profile !is ProfileDrawerItem) return@forEach
|
||||||
|
val counters = unreadCounterList.filter { it.profileId == profile.identifier.toInt() }
|
||||||
|
val count = counters.sumBy { it.count }
|
||||||
|
val badge = when {
|
||||||
|
count == 0 -> null
|
||||||
|
count >= 99 -> StringHolder("99+")
|
||||||
|
else -> StringHolder(count.toString())
|
||||||
|
}
|
||||||
|
if (profile.badge != badge) {
|
||||||
|
profile.badge = badge
|
||||||
|
accountHeader.updateProfile(profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentProfile == profile.identifier.toInt())
|
||||||
|
profileCounters = counters
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("NavDebug", "updateBadges()")
|
||||||
|
profileCounters.map {
|
||||||
|
it.drawerItemId = unreadCounterTypeMap[it.type]
|
||||||
|
}
|
||||||
|
var totalCount = 0
|
||||||
|
profileCounters.forEach {
|
||||||
|
if (it.drawerItemId == null)
|
||||||
|
return@forEach
|
||||||
|
if (it.profileId != currentProfile) {
|
||||||
|
//Log.d("NavDebug", "- Remove badge for ${it.drawerItemId}")
|
||||||
|
//drawer?.updateBadge(it.drawerItemId?.toLong() ?: 0, null)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
Log.d("NavDebug", "- Set badge ${it.count} for ${it.drawerItemId}")
|
||||||
|
drawer.updateBadge(
|
||||||
|
it.drawerItemId?.toLong() ?: 0,
|
||||||
|
when {
|
||||||
|
it.count == 0 -> null
|
||||||
|
it.count >= 99 -> StringHolder("99+")
|
||||||
|
else -> StringHolder(it.count.toString())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
totalCount += it.count
|
||||||
|
}
|
||||||
|
updateMiniDrawer()
|
||||||
|
|
||||||
|
if (bottomBar.navigationIcon is LayerDrawable) {
|
||||||
|
(bottomBar.navigationIcon as LayerDrawable?)?.apply {
|
||||||
|
findDrawableByLayerId(R.id.ic_badge)
|
||||||
|
.takeIf { it is BadgeDrawable }
|
||||||
|
?.also { badge ->
|
||||||
|
(badge as BadgeDrawable).setCount(totalCount.toString())
|
||||||
|
mutate()
|
||||||
|
setDrawableByLayerId(R.id.ic_badge, badge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalCount == 0) {
|
||||||
|
toolbar.subtitle = resources.getString(
|
||||||
|
toolbar.subtitleFormat ?: return,
|
||||||
|
currentProfileObj?.name ?: ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toolbar.subtitle = resources.getQuantityString(
|
||||||
|
toolbar.subtitleFormatWithUnread ?: toolbar.subtitleFormat ?: return,
|
||||||
|
totalCount,
|
||||||
|
currentProfileObj?.name ?: "",
|
||||||
|
totalCount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setUnreadCounterList(unreadCounterList: MutableList<out IUnreadCounter>) {
|
||||||
|
this.unreadCounterList = unreadCounterList as MutableList<IUnreadCounter>
|
||||||
|
updateBadges()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addUnreadCounterType(type: Int, drawerItem: Int) {
|
||||||
|
unreadCounterTypeMap[type] = drawerItem
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UnreadCounter(
|
||||||
|
override var profileId: Int,
|
||||||
|
override var type: Int,
|
||||||
|
override var drawerItemId: Int?,
|
||||||
|
override var count: Int
|
||||||
|
) : IUnreadCounter
|
||||||
|
|
||||||
|
fun setUnreadCount(profileId: Int, type: Int, count: Int) {
|
||||||
|
val item = unreadCounterList.singleOrNull {
|
||||||
|
it.type == type && it.profileId == profileId
|
||||||
|
}
|
||||||
|
if (item != null) {
|
||||||
|
item.count = count
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unreadCounterList.add(UnreadCounter(profileId, type, null, count))
|
||||||
|
}
|
||||||
|
updateBadges()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package pl.szczodrzynski.navlib.drawer.items
|
||||||
|
|
||||||
|
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
||||||
|
|
||||||
|
class DrawerPrimaryItem : PrimaryDrawerItem() {
|
||||||
|
var appTitle: String? = null
|
||||||
|
fun withAppTitle(appTitle: String?): PrimaryDrawerItem {
|
||||||
|
this.appTitle = appTitle
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PrimaryDrawerItem.withAppTitle(appTitle: String?): PrimaryDrawerItem {
|
||||||
|
if (this !is DrawerPrimaryItem)
|
||||||
|
return this
|
||||||
|
this.appTitle = appTitle
|
||||||
|
return this
|
||||||
|
}
|
21
navlib/src/main/res/color/mtrl_filled_background_color.xml
Normal file
21
navlib/src/main/res/color/mtrl_filled_background_color.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2018 The Android Open Source Project
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:alpha="0.16" android:color="?attr/colorOnSurface" android:state_hovered="true"/>
|
||||||
|
<item android:alpha="0.04" android:color="?attr/colorOnSurface" android:state_enabled="false"/>
|
||||||
|
<item android:alpha="0.12" android:color="?attr/colorOnSurface"/>
|
||||||
|
</selector>
|
22
navlib/src/main/res/color/text_input_layout_background.xml
Normal file
22
navlib/src/main/res/color/text_input_layout_background.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2018 The Android Open Source Project
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- increased alpha by 1/2 -->
|
||||||
|
<item android:alpha="0.24" android:color="?attr/colorOnSurface" android:state_hovered="true"/>
|
||||||
|
<item android:alpha="0.06" android:color="?attr/colorOnSurface" android:state_enabled="false"/>
|
||||||
|
<item android:alpha="0.18" android:color="?attr/colorOnSurface"/>
|
||||||
|
</selector>
|
10
navlib/src/main/res/drawable-v21/bs_item_background.xml
Normal file
10
navlib/src/main/res/drawable-v21/bs_item_background.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple android:color="#202196f3"
|
||||||
|
android:radius="5dp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@android:id/mask"
|
||||||
|
android:top="3dp"
|
||||||
|
android:bottom="3dp"
|
||||||
|
android:drawable="@color/color" />
|
||||||
|
</ripple>
|
9
navlib/src/main/res/drawable/bottom_sheet_background.xml
Normal file
9
navlib/src/main/res/drawable/bottom_sheet_background.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="16dp"
|
||||||
|
android:topRightRadius="16dp" />
|
||||||
|
<solid android:color="#ffffff" tools:color="?colorBackgroundFloating" />
|
||||||
|
</shape>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="2dp" />
|
||||||
|
<solid android:color="#DEDEDE" />
|
||||||
|
<size
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="4dp" />
|
||||||
|
</shape>
|
10
navlib/src/main/res/drawable/bs_item_background.xml
Normal file
10
navlib/src/main/res/drawable/bs_item_background.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- This will be the drawable used on API < 21 -->
|
||||||
|
<!-- Obviously you can specify here whatever you want -->
|
||||||
|
<!-- like activated states, animated states, shapes, pngs, whatever like any other drawable-->
|
||||||
|
<!-- I've just put 2 states (pressed,focused) using the same color than the v21 ripple -->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/bs_item_background_base" android:state_focused="true"/>
|
||||||
|
<item android:drawable="@drawable/bs_item_background_base" android:state_pressed="true"/>
|
||||||
|
<item android:drawable="@android:color/transparent"/>
|
||||||
|
</selector>
|
11
navlib/src/main/res/drawable/bs_item_background_base.xml
Normal file
11
navlib/src/main/res/drawable/bs_item_background_base.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:top="3dp"
|
||||||
|
android:bottom="3dp">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#202196f3"/>
|
||||||
|
<corners android:radius="5dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
BIN
navlib/src/main/res/drawable/header.png
Normal file
BIN
navlib/src/main/res/drawable/header.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
9
navlib/src/main/res/drawable/ic_android.xml
Normal file
9
navlib/src/main/res/drawable/ic_android.xml
Normal file
@ -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="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71s-0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
|
||||||
|
</vector>
|
9
navlib/src/main/res/drawable/ic_light.xml
Normal file
9
navlib/src/main/res/drawable/ic_light.xml
Normal file
@ -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="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5L1,10.5v2h3v-2zM13,0.55h-2L11,3.5h2L13,0.55zM20.45,4.46l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zM17.24,18.16l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zM12,5.5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM11,22.45h2L13,19.5h-2v2.95zM3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z"/>
|
||||||
|
</vector>
|
10
navlib/src/main/res/drawable/ic_menu_badge.xml
Normal file
10
navlib/src/main/res/drawable/ic_menu_badge.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/ic_menu"
|
||||||
|
android:drawable="@drawable/placeholder"
|
||||||
|
android:gravity="center" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/ic_badge"
|
||||||
|
android:drawable="@drawable/placeholder" />
|
||||||
|
</layer-list>
|
9
navlib/src/main/res/drawable/ic_night.xml
Normal file
9
navlib/src/main/res/drawable/ic_night.xml
Normal file
@ -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,2c-1.05,0 -2.05,0.16 -3,0.46 4.06,1.27 7,5.06 7,9.54 0,4.48 -2.94,8.27 -7,9.54 0.95,0.3 1.95,0.46 3,0.46 5.52,0 10,-4.48 10,-10S14.52,2 9,2z"/>
|
||||||
|
</vector>
|
3
navlib/src/main/res/drawable/placeholder.xml
Normal file
3
navlib/src/main/res/drawable/placeholder.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
</shape>
|
15
navlib/src/main/res/drawable/profile.xml
Normal file
15
navlib/src/main/res/drawable/profile.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="512dp"
|
||||||
|
android:height="512dp"
|
||||||
|
android:viewportWidth="135.5"
|
||||||
|
android:viewportHeight="135.5">
|
||||||
|
<path
|
||||||
|
android:pathData="M0,0h135.5L135.5,135.5L0,135.5z"
|
||||||
|
android:fillColor="#00000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M41.674,55.89a28.412,26.387 85.529,1 0,52.798 0.379a28.412,26.387 85.529,1 0,-52.798 -0.379z"
|
||||||
|
android:fillColor="#b0ffffff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M121,133.2a53.3,39 0,0 1,-50.9 38.9A53.3,39 0,0 1,14.8 136.5a53.3,39 0,0 1,46 -42,53.3 39,0 0,1 59.5,32"
|
||||||
|
android:fillColor="#b0ffffff"/>
|
||||||
|
</vector>
|
8
navlib/src/main/res/drawable/shadow_bottom.xml
Normal file
8
navlib/src/main/res/drawable/shadow_bottom.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:angle="270"
|
||||||
|
android:endColor="#00000000"
|
||||||
|
android:startColor="#40000000"
|
||||||
|
android:type="linear" />
|
||||||
|
</shape>
|
8
navlib/src/main/res/drawable/shadow_right.xml
Normal file
8
navlib/src/main/res/drawable/shadow_right.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:angle="0"
|
||||||
|
android:endColor="#00000000"
|
||||||
|
android:startColor="#40000000"
|
||||||
|
android:type="linear" />
|
||||||
|
</shape>
|
6
navlib/src/main/res/layout/material_drawer.xml
Normal file
6
navlib/src/main/res/layout/material_drawer.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/material_drawer_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="false" />
|
25
navlib/src/main/res/layout/material_drawer_slider.xml
Normal file
25
navlib/src/main/res/layout/material_drawer_slider.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.mikepenz.materialize.view.ScrimInsetsRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/material_drawer_slider_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:layout_marginEnd="@dimen/material_drawer_margin"
|
||||||
|
android:layout_marginRight="@dimen/material_drawer_margin"
|
||||||
|
android:clickable="true"
|
||||||
|
android:elevation="8dp"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/material_drawer_inner_shadow"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentStart="false"
|
||||||
|
android:layout_alignParentLeft="false"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:background="@drawable/material_drawer_shadow_left"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</com.mikepenz.materialize.view.ScrimInsetsRelativeLayout>
|
109
navlib/src/main/res/layout/nav_bottom_sheet.xml
Normal file
109
navlib/src/main/res/layout/nav_bottom_sheet.xml
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:translationZ="10dp"
|
||||||
|
tools:targetApi="lollipop">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/bs_scrim"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#99000000"
|
||||||
|
android:visibility="invisible"
|
||||||
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:id="@+id/bs_view"
|
||||||
|
style="@style/width_max_600dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
app:behavior_hideable="true"
|
||||||
|
app:behavior_peekHeight="auto"
|
||||||
|
app:layout_behavior="@string/bottom_sheet_behavior"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bs_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bottom_sheet_background"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
tools:paddingBottom="48dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/bs_dragBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
app:srcCompat="@drawable/bottom_sheet_control_bar"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/bs_textInputLayout"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:boxBackgroundColor="?boxBackgroundColor"
|
||||||
|
app:endIconMode="clear_text"
|
||||||
|
tools:startIconDrawable="@drawable/ic_android"
|
||||||
|
tools:helperText="0 messages found"
|
||||||
|
tools:hint="Search">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/bs_textInputEditText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
tools:text="John" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bs_toggleGroupContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bs_toggleGroupTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Sorting order" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||||
|
android:id="@+id/bs_toggleGroup"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:singleSelection="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/bs_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
tools:minHeight="50dp"
|
||||||
|
tools:listitem="@layout/nav_bs_item_primary"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
</layout>
|
49
navlib/src/main/res/layout/nav_bs_item_primary.xml
Normal file
49
navlib/src/main/res/layout/nav_bs_item_primary.xml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/item_root"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/item_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
tools:srcCompat="@drawable/ic_android" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:gravity="start|center_vertical"
|
||||||
|
android:textAppearance="@style/NavView.TextView"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="Search activity" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:gravity="center_vertical|start"
|
||||||
|
android:lines="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Small"
|
||||||
|
android:textSize="12sp"
|
||||||
|
tools:text="Some drawer text" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
12
navlib/src/main/res/layout/nav_bs_item_separator.xml
Normal file
12
navlib/src/main/res/layout/nav_bs_item_separator.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="17dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/material_drawer_divider" />
|
||||||
|
</LinearLayout>
|
211
navlib/src/main/res/layout/nav_view.xml
Normal file
211
navlib/src/main/res/layout/nav_view.xml
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge 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"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:parentTag="FrameLayout">
|
||||||
|
|
||||||
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
android:id="@+id/nv_drawerLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/nv_statusBarBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:background="?android:windowBackground"
|
||||||
|
tools:layout_height="25dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/nv_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:paddingTop="24dp"
|
||||||
|
tools:paddingBottom="48dp">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/nv_drawerContainerLandscape"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorSurface_4dp"
|
||||||
|
tools:layout_width="72dp" />
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/nv_coordinator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<pl.szczodrzynski.navlib.NavToolbar
|
||||||
|
android:id="@+id/nv_toolbar"
|
||||||
|
style="@style/Widget.MaterialComponents.Toolbar.Surface"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?actionBarBackground"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:elevation="4dp"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/app_name"
|
||||||
|
app:titleMargin="0dp"
|
||||||
|
tools:targetApi="lollipop">
|
||||||
|
|
||||||
|
<com.mikepenz.materialdrawer.view.BezelImageView
|
||||||
|
android:id="@+id/nv_toolbar_image"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:layout_marginEnd="13dp"
|
||||||
|
android:layout_marginRight="13dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:materialDrawerSelectorOnPress="#80ffffff"
|
||||||
|
tools:src="@tools:sample/backgrounds/scenic" />
|
||||||
|
|
||||||
|
</pl.szczodrzynski.navlib.NavToolbar>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/nv_toolbarElevation"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:layout_marginTop="?actionBarSize"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/shadow_bottom" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/nv_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="?actionBarSize"
|
||||||
|
android:layout_marginBottom="?actionBarSize"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/nv_miniDrawerContainerPortrait"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorSurface_4dp"
|
||||||
|
tools:layout_width="72dp" /><!--tools:layout_width="72dp"-->
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/nv_miniDrawerElevation"
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/shadow_right" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<pl.szczodrzynski.navlib.NavBottomBar
|
||||||
|
android:id="@+id/nv_bottomBar"
|
||||||
|
style="@style/Widget.MaterialComponents.BottomAppBar.Colored"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:gravity="start"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:fabAlignmentMode="center"
|
||||||
|
app:fabAnimationMode="scale" />
|
||||||
|
|
||||||
|
<com.balysv.materialripple.MaterialRippleLayout
|
||||||
|
android:id="@+id/ripple"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="?actionBarSize"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:enabled="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:focusable="false"
|
||||||
|
app:mrl_rippleFadeDuration="200"
|
||||||
|
app:mrl_rippleDuration="350"
|
||||||
|
app:mrl_rippleColor="?colorOnBackground"
|
||||||
|
app:mrl_rippleAlpha="0.3">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:enabled="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:visibility="invisible"/>
|
||||||
|
|
||||||
|
</com.balysv.materialripple.MaterialRippleLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
android:id="@+id/nv_extendedFloatingActionButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:text=""
|
||||||
|
android:visibility="visible"
|
||||||
|
app:backgroundTint="?colorFab"
|
||||||
|
app:layout_anchor="@+id/nv_bottomBar"
|
||||||
|
app:layout_anchorGravity="center|top"
|
||||||
|
tools:icon="@android:drawable/ic_menu_edit" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/nv_floatingActionButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:backgroundTint="?colorFab"
|
||||||
|
app:layout_anchor="@id/nv_bottomBar"
|
||||||
|
tools:srcCompat="@android:drawable/ic_menu_edit" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/nv_navigationBarBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="?navigationBarBackground"
|
||||||
|
tools:layout_height="48dp" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/nv_statusBarDarker"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:background="#44000000"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:layout_height="25dp"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/nv_navigationBarDarker"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="#44000000"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:layout_height="48dp"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet
|
||||||
|
android:id="@+id/nv_bottomSheet"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
|
||||||
|
</merge>
|
6
navlib/src/main/res/values-w600dp/styles.xml
Normal file
6
navlib/src/main/res/values-w600dp/styles.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="width_max_600dp">
|
||||||
|
<item name="android:layout_width">600dp</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
8
navlib/src/main/res/values/attrs.xml
Normal file
8
navlib/src/main/res/values/attrs.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<attr name="actionBarBackground" format="color" />
|
||||||
|
<attr name="navigationBarBackground" format="color" />
|
||||||
|
<attr name="colorFab" format="color" />
|
||||||
|
<attr name="colorFabIcon" format="color" />
|
||||||
|
<attr name="colorOnFab" format="color" />
|
||||||
|
</resources>
|
8
navlib/src/main/res/values/attrs_nav_view.xml
Normal file
8
navlib/src/main/res/values/attrs_nav_view.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<resources>
|
||||||
|
<declare-styleable name="NavView">
|
||||||
|
<attr name="exampleString" format="string" />
|
||||||
|
<attr name="exampleDimension" format="dimension" />
|
||||||
|
<attr name="exampleColor" format="color" />
|
||||||
|
<attr name="exampleDrawable" format="color|reference" />
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
19
navlib/src/main/res/values/colors.xml
Normal file
19
navlib/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="color">#202196f3</color>
|
||||||
|
<color name="blue">#154FBC</color>
|
||||||
|
|
||||||
|
<color name="background_light">#ffffff</color>
|
||||||
|
<color name="background_dark">#242424</color>
|
||||||
|
<color name="background_black">#000000</color>
|
||||||
|
|
||||||
|
<color name="colorSurface_1dp">#0dffffff</color>
|
||||||
|
<color name="colorSurface_2dp">#12ffffff</color>
|
||||||
|
<color name="colorSurface_3dp">#14ffffff</color>
|
||||||
|
<color name="colorSurface_4dp">#17ffffff</color>
|
||||||
|
<color name="colorSurface_6dp">#1cffffff</color>
|
||||||
|
<color name="colorSurface_8dp">#1fffffff</color>
|
||||||
|
<color name="colorSurface_12dp">#24ffffff</color>
|
||||||
|
<color name="colorSurface_16dp">#26ffffff</color>
|
||||||
|
<color name="colorSurface_24dp">#29ffffff</color>
|
||||||
|
</resources>
|
4
navlib/src/main/res/values/dimens.xml
Normal file
4
navlib/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<dimen name="badge_text_size">10sp</dimen>
|
||||||
|
</resources>
|
4
navlib/src/main/res/values/strings.xml
Normal file
4
navlib/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">NavLib</string>
|
||||||
|
<string name="toolbar_subtitle">%1$s</string>
|
||||||
|
</resources>
|
159
navlib/src/main/res/values/styles.xml
Normal file
159
navlib/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<style name="NavView" />
|
||||||
|
<style name="NavView.Widget" />
|
||||||
|
|
||||||
|
<style name="NavView.Widget.FloatingActionButton" parent="Widget.MaterialComponents.FloatingActionButton">
|
||||||
|
<item name="backgroundTint">?colorFab</item>
|
||||||
|
<item name="android:textColor">?colorOnFab</item>
|
||||||
|
</style>
|
||||||
|
<style name="NavView.Widget.ExtendedFloatingActionButton" parent="Widget.MaterialComponents.ExtendedFloatingActionButton">
|
||||||
|
<item name="backgroundTint">?colorFab</item>
|
||||||
|
<item name="android:textColor">?colorOnFab</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="NavView.TextView">
|
||||||
|
<item name="android:textAppearance">@style/NavView.TextView.Normal</item>
|
||||||
|
</style>
|
||||||
|
<!-- title text 20sp, primary, medium -->
|
||||||
|
<!-- subtitle text 16sp, primary, medium -->
|
||||||
|
<!-- large body text 22sp, primary, regular -->
|
||||||
|
<!-- medium body text 18sp, primary, regular -->
|
||||||
|
<!-- normal(default) body text 14sp, primary, regular -->
|
||||||
|
<!-- small body text 14sp, secondary, medium -->
|
||||||
|
<!-- helper body text 14sp, secondary, regular -->
|
||||||
|
<style name="NavView.TextView.Normal" parent="Widget.MaterialComponents.TextView">
|
||||||
|
<item name="android:textAppearance">?attr/textAppearanceBody2</item>
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="android:textColor">?android:textColorPrimary</item>
|
||||||
|
</style>
|
||||||
|
<style name="NavView.TextView.Title" parent="TextAppearance.AppCompat.Title">
|
||||||
|
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||||
|
</style>
|
||||||
|
<style name="NavView.TextView.Subtitle" parent="TextAppearance.AppCompat.Subhead">
|
||||||
|
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||||
|
<item name="android:textSize">16sp</item>
|
||||||
|
<item name="android:fontFamily">sans-serif-medium</item>
|
||||||
|
</style>
|
||||||
|
<style name="NavView.TextView.Large" parent="TextAppearance.AppCompat.Large">
|
||||||
|
</style>
|
||||||
|
<style name="NavView.TextView.Medium" parent="TextAppearance.AppCompat.Medium">
|
||||||
|
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||||
|
</style>
|
||||||
|
<style name="NavView.TextView.Small" parent="TextAppearance.AppCompat.Small">
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="android:fontFamily">sans-serif-medium</item>
|
||||||
|
</style>
|
||||||
|
<style name="NavView.TextView.Helper" parent="TextAppearance.AppCompat.Small">
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<!--<item name="colorAccent">#ffb300</item>-->
|
||||||
|
|
||||||
|
<style name="NavView.Light" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||||
|
<!-- primary styling -->
|
||||||
|
<item name="colorPrimary">#6200EE</item>
|
||||||
|
<item name="colorPrimaryDark">#3700B3</item>
|
||||||
|
<item name="colorPrimaryVariant">#6200EE</item>
|
||||||
|
<item name="colorAccent">#03DAC6</item>
|
||||||
|
<item name="colorSecondary">?colorAccent</item>
|
||||||
|
<item name="colorSecondaryVariant">#018786</item>
|
||||||
|
|
||||||
|
<!-- window colors -->
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
<!-- a descendant theme should specify those two as background colors -->
|
||||||
|
<item name="android:colorBackground">@color/background_light</item>
|
||||||
|
<item name="colorSurface">#ffffff</item>
|
||||||
|
|
||||||
|
<!-- FAB styling -->
|
||||||
|
<item name="colorFab">?colorAccent</item>
|
||||||
|
<item name="colorFabIcon">?colorOnSecondary</item>
|
||||||
|
<item name="colorOnFab">?colorOnSecondary</item>
|
||||||
|
|
||||||
|
<!-- text colors -->
|
||||||
|
<item name="android:textColorPrimary">#db000000</item>
|
||||||
|
<item name="android:textColorSecondary">#99000000</item>
|
||||||
|
<item name="colorOnPrimary">#ffffff</item>
|
||||||
|
<item name="colorOnSecondary">#ffffff</item>
|
||||||
|
<item name="colorOnBackground">?android:textColorPrimary</item>
|
||||||
|
<item name="colorOnSurface">?android:textColorPrimary</item>
|
||||||
|
|
||||||
|
<item name="elevationOverlayColor">#ffffff</item>
|
||||||
|
|
||||||
|
<!-- system bars config -->
|
||||||
|
<item name="actionBarBackground">?colorSurface</item>
|
||||||
|
<item name="navigationBarBackground">?colorPrimaryVariant</item>
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
|
||||||
|
<!-- component styling -->
|
||||||
|
<item name="floatingActionButtonStyle">@style/NavView.Widget.FloatingActionButton</item>
|
||||||
|
<item name="extendedFloatingActionButtonStyle">@style/NavView.Widget.ExtendedFloatingActionButton</item>
|
||||||
|
<item name="boxBackgroundColor" tools:ignore="PrivateResource">@color/mtrl_filled_background_color</item>
|
||||||
|
<item name="android:textViewStyle">@style/NavView.TextView</item>
|
||||||
|
|
||||||
|
<!-- drawer & bottom sheet styling -->
|
||||||
|
<item name="materialDrawerStyle">@style/Widget.MaterialDrawerStyle</item>
|
||||||
|
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
|
||||||
|
<!--<item name="materialDrawerPrimaryIcon">#5F6368</item>-->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="NavView.Dark" parent="Theme.MaterialComponents.NoActionBar">
|
||||||
|
<!-- primary styling -->
|
||||||
|
<item name="colorPrimary">#BB86FC</item>
|
||||||
|
<item name="colorPrimaryDark">#3700B3</item>
|
||||||
|
<item name="colorPrimaryVariant">#6200EE</item>
|
||||||
|
<item name="colorAccent">#03DAC6</item>
|
||||||
|
<item name="colorSecondary">?colorAccent</item>
|
||||||
|
<item name="colorSecondaryVariant">?colorAccent</item>
|
||||||
|
|
||||||
|
<!-- window colors -->
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
<!-- a descendant theme should specify those two as background colors -->
|
||||||
|
<item name="android:colorBackground">@color/background_dark</item>
|
||||||
|
<item name="colorSurface">#333333</item>
|
||||||
|
|
||||||
|
<!-- FAB styling -->
|
||||||
|
<item name="colorFab">?colorAccent</item>
|
||||||
|
<item name="colorFabIcon">?colorOnSecondary</item>
|
||||||
|
<item name="colorOnFab">?colorOnSecondary</item>
|
||||||
|
|
||||||
|
<!-- text colors -->
|
||||||
|
<item name="android:textColorPrimary">#ffffffff</item>
|
||||||
|
<item name="android:textColorSecondary">#99ffffff</item>
|
||||||
|
<item name="colorOnPrimary">#ffffff</item>
|
||||||
|
<item name="colorOnSecondary">#ffffff</item>
|
||||||
|
<item name="colorOnBackground">?android:textColorPrimary</item>
|
||||||
|
<item name="colorOnSurface">?android:textColorPrimary</item>
|
||||||
|
|
||||||
|
<!-- system bars config -->
|
||||||
|
<item name="actionBarBackground">?colorSurface</item>
|
||||||
|
<item name="navigationBarBackground">?colorPrimaryVariant</item>
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
|
||||||
|
<!-- component styling -->
|
||||||
|
<item name="floatingActionButtonStyle">@style/NavView.Widget.FloatingActionButton</item>
|
||||||
|
<item name="extendedFloatingActionButtonStyle">@style/NavView.Widget.ExtendedFloatingActionButton</item>
|
||||||
|
<item name="boxBackgroundColor">@color/text_input_layout_background</item>
|
||||||
|
<item name="android:textViewStyle">@style/NavView.TextView</item>
|
||||||
|
|
||||||
|
<!-- drawer & bottom sheet styling -->
|
||||||
|
<item name="materialDrawerStyle">@style/Widget.MaterialDrawerStyle</item>
|
||||||
|
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
|
||||||
|
<!--<item name="material_drawer_primary_icon">#9AA0A6</item>-->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="NavView.Black" parent="NavView.Dark">
|
||||||
|
<item name="android:colorBackground">@color/background_black</item>
|
||||||
|
<item name="colorSurface">#121212</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<style name="width_max_600dp">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -1,2 +1,2 @@
|
|||||||
rootProject.name='Szkolny.eu'
|
rootProject.name='Szkolny.eu'
|
||||||
include ':app'
|
include ':app', ':navlib', ':navlib-font'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user