diff --git a/app/build.gradle b/app/build.gradle
index 0ceae1a1..b071c1ef 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -181,6 +181,7 @@ dependencies {
implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
// Szkolny.eu libraries/forks
+ implementation project(":navlib")
implementation "eu.szkolny:android-snowfall:1ca9ea2da3"
implementation "eu.szkolny:agendacalendarview:1.0.4"
implementation "eu.szkolny:cafebar:5bf0c618de"
@@ -191,7 +192,6 @@ dependencies {
implementation "eu.szkolny.selective-dao:annotation:27f8f3f194"
officialImplementation "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:recyclertablayout:700f980584"
implementation "pl.szczodrzynski:tachyon:551943a6b5"
diff --git a/navlib-font/build.gradle b/navlib-font/build.gradle
new file mode 100644
index 00000000..7f254181
--- /dev/null
+++ b/navlib-font/build.gradle
@@ -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"
+}
diff --git a/navlib-font/consumer-proguard-rules.pro b/navlib-font/consumer-proguard-rules.pro
new file mode 100644
index 00000000..ef3e42a0
--- /dev/null
+++ b/navlib-font/consumer-proguard-rules.pro
@@ -0,0 +1 @@
+-keep class com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont { *; }
diff --git a/navlib-font/gradle.properties b/navlib-font/gradle.properties
new file mode 100644
index 00000000..163aa9f2
--- /dev/null
+++ b/navlib-font/gradle.properties
@@ -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
diff --git a/navlib-font/src/main/AndroidManifest.xml b/navlib-font/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..edcc9806
--- /dev/null
+++ b/navlib-font/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+
+
+
+
\ No newline at end of file
diff --git a/navlib-font/src/main/java/com/mikepenz/iconics/typeface/library/navlibfont/NavLibFont.kt b/navlib-font/src/main/java/com/mikepenz/iconics/typeface/library/navlibfont/NavLibFont.kt
new file mode 100644
index 00000000..822fde52
--- /dev/null
+++ b/navlib-font/src/main/java/com/mikepenz/iconics/typeface/library/navlibfont/NavLibFont.kt
@@ -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 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
+ 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 }
+ }
+}
\ No newline at end of file
diff --git a/navlib-font/src/main/res/font/navlibfont_font_v1_0.ttf b/navlib-font/src/main/res/font/navlibfont_font_v1_0.ttf
new file mode 100644
index 00000000..f0da0117
Binary files /dev/null and b/navlib-font/src/main/res/font/navlibfont_font_v1_0.ttf differ
diff --git a/navlib-font/src/main/res/values/font_addon.xml b/navlib-font/src/main/res/values/font_addon.xml
new file mode 100644
index 00000000..1d094be3
--- /dev/null
+++ b/navlib-font/src/main/res/values/font_addon.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont
+
diff --git a/navlib-font/src/main/res/values/font_description.xml b/navlib-font/src/main/res/values/font_description.xml
new file mode 100644
index 00000000..1421308b
--- /dev/null
+++ b/navlib-font/src/main/res/values/font_description.xml
@@ -0,0 +1,31 @@
+
+
+
+
+ year;author;libraryName;libraryWebsite
+ Kuba SzczodrzyĆski
+ https://github.com/kuba2k2/NavLib
+ NavLibFont
+
+ https://github.com/kuba2k2/NavLib
+ @string/NavLibFont_version
+
+ true
+ https://github.com/kuba2k2/NavLib
+
+ 2018
+
diff --git a/navlib/build.gradle b/navlib/build.gradle
new file mode 100644
index 00000000..b935281b
--- /dev/null
+++ b/navlib/build.gradle
@@ -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")
+}
diff --git a/navlib/consumer-rules.pro b/navlib/consumer-rules.pro
new file mode 100644
index 00000000..619be6dd
--- /dev/null
+++ b/navlib/consumer-rules.pro
@@ -0,0 +1 @@
+-keep class androidx.drawerlayout.widget.DrawerLayout { *; }
diff --git a/navlib/proguard-rules.pro b/navlib/proguard-rules.pro
new file mode 100644
index 00000000..06b02c6a
--- /dev/null
+++ b/navlib/proguard-rules.pro
@@ -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 { *; }
diff --git a/navlib/src/main/AndroidManifest.xml b/navlib/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..7b521725
--- /dev/null
+++ b/navlib/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/Anim.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/Anim.kt
new file mode 100644
index 00000000..5b57e48f
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/Anim.kt
@@ -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()
+ }
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/BadgeDrawable.java b/navlib/src/main/java/pl/szczodrzynski/navlib/BadgeDrawable.java
new file mode 100644
index 00000000..c47c3ff2
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/BadgeDrawable.java
@@ -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;
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/BezelGifImageView.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/BezelGifImageView.kt
new file mode 100644
index 00000000..716ee1be
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/BezelGifImageView.kt
@@ -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
+ }
+ }
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/DrawerExtensions.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/DrawerExtensions.kt
new file mode 100644
index 00000000..2ec9084e
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/DrawerExtensions.kt
@@ -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.withIcon(icon: IIcon) = withIcon(pl.szczodrzynski.navlib.ImageHolder(icon))
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/ImageHolder.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/ImageHolder.kt
new file mode 100644
index 00000000..a2075667
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/ImageHolder.kt
@@ -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
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/NavBottomBar.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/NavBottomBar.kt
new file mode 100644
index 00000000..6bc40157
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/NavBottomBar.kt
@@ -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
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/NavToolbar.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/NavToolbar.kt
new file mode 100644
index 00000000..1bc88813
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/NavToolbar.kt
@@ -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)
+ }
+}
\ No newline at end of file
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/NavView.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/NavView.kt
new file mode 100644
index 00000000..496597c3
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/NavView.kt
@@ -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(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(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)
+ }
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/NavigationLoader.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/NavigationLoader.kt
new file mode 100644
index 00000000..09428d74
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/NavigationLoader.kt
@@ -0,0 +1,5 @@
+package pl.szczodrzynski.navlib
+
+interface NavigationLoader {
+ fun load(itemId: Int, callerId: Int, source: Int, args: Map)
+}
\ No newline at end of file
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/SystemBarsUtil.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/SystemBarsUtil.kt
new file mode 100644
index 00000000..07d4501c
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/SystemBarsUtil.kt
@@ -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()
+ }
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/Utils.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/Utils.kt
new file mode 100644
index 00000000..fc324f77
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/Utils.kt
@@ -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(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
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/BottomSheetAdapter.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/BottomSheetAdapter.kt
new file mode 100644
index 00000000..bd2171d8
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/BottomSheetAdapter.kt
@@ -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>) : RecyclerView.Adapter() {
+
+ 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)
+ }
+}
\ No newline at end of file
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/NavBottomSheet.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/NavBottomSheet.kt
new file mode 100644
index 00000000..024985ce
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/NavBottomSheet.kt
@@ -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
+ private var bottomSheetVisible = false
+
+ private val items = ArrayList>()
+ 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(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
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/ViewHolderProvider.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/ViewHolderProvider.kt
new file mode 100644
index 00000000..76ac8cb5
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/ViewHolderProvider.kt
@@ -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 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)
+ }
+}
\ No newline at end of file
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/BottomSheetPrimaryItem.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/BottomSheetPrimaryItem.kt
new file mode 100644
index 00000000..3f7631cb
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/BottomSheetPrimaryItem.kt
@@ -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 {
+
+ /*_ _
+ | | | |
+ | | __ _ _ _ ___ _ _| |_
+ | | / _` | | | |/ _ \| | | | __|
+ | |___| (_| | |_| | (_) | |_| | |_
+ |______\__,_|\__, |\___/ \__,_|\__|
+ __/ |
+ |__*/
+ 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(R.id.item_root)
+ val image = itemView.findViewById(R.id.item_icon)
+ val text = itemView.findViewById(R.id.item_text)
+ val description = itemView.findViewById(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
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/BottomSheetSeparatorItem.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/BottomSheetSeparatorItem.kt
new file mode 100644
index 00000000..cf5403e3
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/BottomSheetSeparatorItem.kt
@@ -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 {
+
+ /*_ _
+ | | | |
+ | | __ _ _ _ ___ _ _| |_
+ | | / _` | | | |/ _ \| | | | __|
+ | |___| (_| | |_| | (_) | |_| | |_
+ |______\__,_|\__, |\___/ \__,_|\__|
+ __/ |
+ |__*/
+ 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) {
+
+ }
+}
\ No newline at end of file
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/IBottomSheetItem.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/IBottomSheetItem.kt
new file mode 100644
index 00000000..e55f337f
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/bottomsheet/items/IBottomSheetItem.kt
@@ -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 {
+
+ 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)
+ }
+}
\ No newline at end of file
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/IDrawerProfile.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/IDrawerProfile.kt
new file mode 100644
index 00000000..30c15166
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/IDrawerProfile.kt
@@ -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)
+}
\ No newline at end of file
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/IUnreadCounter.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/IUnreadCounter.kt
new file mode 100644
index 00000000..ef213c2a
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/IUnreadCounter.kt
@@ -0,0 +1,8 @@
+package pl.szczodrzynski.navlib.drawer
+
+interface IUnreadCounter {
+ var profileId: Int
+ var type: Int
+ var drawerItemId: Int?
+ var count: Int
+}
\ No newline at end of file
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/NavDrawer.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/NavDrawer.kt
new file mode 100644
index 00000000..63c66766
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/NavDrawer.kt
@@ -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 = 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?
+
+ 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) {
+ profileList = profiles as MutableList
+ 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 = mutableListOf()
+ private val unreadCounterTypeMap = mutableMapOf()
+
+ 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()
+
+ 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) {
+ this.unreadCounterList = unreadCounterList as MutableList
+ 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()
+ }
+}
diff --git a/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/items/DrawerPrimaryItem.kt b/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/items/DrawerPrimaryItem.kt
new file mode 100644
index 00000000..7a623747
--- /dev/null
+++ b/navlib/src/main/java/pl/szczodrzynski/navlib/drawer/items/DrawerPrimaryItem.kt
@@ -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
+}
\ No newline at end of file
diff --git a/navlib/src/main/res/color/mtrl_filled_background_color.xml b/navlib/src/main/res/color/mtrl_filled_background_color.xml
new file mode 100644
index 00000000..ed0fc82d
--- /dev/null
+++ b/navlib/src/main/res/color/mtrl_filled_background_color.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/navlib/src/main/res/color/text_input_layout_background.xml b/navlib/src/main/res/color/text_input_layout_background.xml
new file mode 100644
index 00000000..7bfcd005
--- /dev/null
+++ b/navlib/src/main/res/color/text_input_layout_background.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/navlib/src/main/res/drawable-v21/bs_item_background.xml b/navlib/src/main/res/drawable-v21/bs_item_background.xml
new file mode 100644
index 00000000..1cf74bcc
--- /dev/null
+++ b/navlib/src/main/res/drawable-v21/bs_item_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/drawable/bottom_sheet_background.xml b/navlib/src/main/res/drawable/bottom_sheet_background.xml
new file mode 100644
index 00000000..1e6a6852
--- /dev/null
+++ b/navlib/src/main/res/drawable/bottom_sheet_background.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/drawable/bottom_sheet_control_bar.xml b/navlib/src/main/res/drawable/bottom_sheet_control_bar.xml
new file mode 100644
index 00000000..ac5256a5
--- /dev/null
+++ b/navlib/src/main/res/drawable/bottom_sheet_control_bar.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/drawable/bs_item_background.xml b/navlib/src/main/res/drawable/bs_item_background.xml
new file mode 100644
index 00000000..59ad9b32
--- /dev/null
+++ b/navlib/src/main/res/drawable/bs_item_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/drawable/bs_item_background_base.xml b/navlib/src/main/res/drawable/bs_item_background_base.xml
new file mode 100644
index 00000000..454d22b1
--- /dev/null
+++ b/navlib/src/main/res/drawable/bs_item_background_base.xml
@@ -0,0 +1,11 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/drawable/header.png b/navlib/src/main/res/drawable/header.png
new file mode 100644
index 00000000..d0730a1a
Binary files /dev/null and b/navlib/src/main/res/drawable/header.png differ
diff --git a/navlib/src/main/res/drawable/ic_android.xml b/navlib/src/main/res/drawable/ic_android.xml
new file mode 100644
index 00000000..fa2bc215
--- /dev/null
+++ b/navlib/src/main/res/drawable/ic_android.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/navlib/src/main/res/drawable/ic_light.xml b/navlib/src/main/res/drawable/ic_light.xml
new file mode 100644
index 00000000..4f7c019c
--- /dev/null
+++ b/navlib/src/main/res/drawable/ic_light.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/navlib/src/main/res/drawable/ic_menu_badge.xml b/navlib/src/main/res/drawable/ic_menu_badge.xml
new file mode 100644
index 00000000..4355777f
--- /dev/null
+++ b/navlib/src/main/res/drawable/ic_menu_badge.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/drawable/ic_night.xml b/navlib/src/main/res/drawable/ic_night.xml
new file mode 100644
index 00000000..008a4fbf
--- /dev/null
+++ b/navlib/src/main/res/drawable/ic_night.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/navlib/src/main/res/drawable/placeholder.xml b/navlib/src/main/res/drawable/placeholder.xml
new file mode 100644
index 00000000..0a9be837
--- /dev/null
+++ b/navlib/src/main/res/drawable/placeholder.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/drawable/profile.xml b/navlib/src/main/res/drawable/profile.xml
new file mode 100644
index 00000000..eea87f66
--- /dev/null
+++ b/navlib/src/main/res/drawable/profile.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/navlib/src/main/res/drawable/shadow_bottom.xml b/navlib/src/main/res/drawable/shadow_bottom.xml
new file mode 100644
index 00000000..d8dec9b0
--- /dev/null
+++ b/navlib/src/main/res/drawable/shadow_bottom.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/drawable/shadow_right.xml b/navlib/src/main/res/drawable/shadow_right.xml
new file mode 100644
index 00000000..36223703
--- /dev/null
+++ b/navlib/src/main/res/drawable/shadow_right.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/layout/material_drawer.xml b/navlib/src/main/res/layout/material_drawer.xml
new file mode 100644
index 00000000..9369ea24
--- /dev/null
+++ b/navlib/src/main/res/layout/material_drawer.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/layout/material_drawer_slider.xml b/navlib/src/main/res/layout/material_drawer_slider.xml
new file mode 100644
index 00000000..a903073d
--- /dev/null
+++ b/navlib/src/main/res/layout/material_drawer_slider.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/layout/nav_bottom_sheet.xml b/navlib/src/main/res/layout/nav_bottom_sheet.xml
new file mode 100644
index 00000000..5cb25d10
--- /dev/null
+++ b/navlib/src/main/res/layout/nav_bottom_sheet.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/navlib/src/main/res/layout/nav_bs_item_primary.xml b/navlib/src/main/res/layout/nav_bs_item_primary.xml
new file mode 100644
index 00000000..7092d1a6
--- /dev/null
+++ b/navlib/src/main/res/layout/nav_bs_item_primary.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/layout/nav_bs_item_separator.xml b/navlib/src/main/res/layout/nav_bs_item_separator.xml
new file mode 100644
index 00000000..2f948ca4
--- /dev/null
+++ b/navlib/src/main/res/layout/nav_bs_item_separator.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/layout/nav_view.xml b/navlib/src/main/res/layout/nav_view.xml
new file mode 100644
index 00000000..4b46d117
--- /dev/null
+++ b/navlib/src/main/res/layout/nav_view.xml
@@ -0,0 +1,211 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/navlib/src/main/res/values-w600dp/styles.xml b/navlib/src/main/res/values-w600dp/styles.xml
new file mode 100644
index 00000000..6674487f
--- /dev/null
+++ b/navlib/src/main/res/values-w600dp/styles.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/values/attrs.xml b/navlib/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..a239e4e4
--- /dev/null
+++ b/navlib/src/main/res/values/attrs.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/navlib/src/main/res/values/attrs_nav_view.xml b/navlib/src/main/res/values/attrs_nav_view.xml
new file mode 100644
index 00000000..c6169965
--- /dev/null
+++ b/navlib/src/main/res/values/attrs_nav_view.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/navlib/src/main/res/values/colors.xml b/navlib/src/main/res/values/colors.xml
new file mode 100644
index 00000000..e881e7bd
--- /dev/null
+++ b/navlib/src/main/res/values/colors.xml
@@ -0,0 +1,19 @@
+
+
+ #202196f3
+ #154FBC
+
+ #ffffff
+ #242424
+ #000000
+
+ #0dffffff
+ #12ffffff
+ #14ffffff
+ #17ffffff
+ #1cffffff
+ #1fffffff
+ #24ffffff
+ #26ffffff
+ #29ffffff
+
\ No newline at end of file
diff --git a/navlib/src/main/res/values/dimens.xml b/navlib/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..3a8e4fd5
--- /dev/null
+++ b/navlib/src/main/res/values/dimens.xml
@@ -0,0 +1,4 @@
+
+
+ 10sp
+
\ No newline at end of file
diff --git a/navlib/src/main/res/values/strings.xml b/navlib/src/main/res/values/strings.xml
new file mode 100644
index 00000000..6f67dcd6
--- /dev/null
+++ b/navlib/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ NavLib
+ %1$s
+
diff --git a/navlib/src/main/res/values/styles.xml b/navlib/src/main/res/values/styles.xml
new file mode 100644
index 00000000..6ad92c22
--- /dev/null
+++ b/navlib/src/main/res/values/styles.xml
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/settings.gradle b/settings.gradle
index 02529180..d8939d3d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,2 @@
rootProject.name='Szkolny.eu'
-include ':app'
+include ':app', ':navlib', ':navlib-font'