add NavLib files/deps into project

This commit is contained in:
Franek 2024-06-18 19:12:08 +02:00
parent d5697b54bd
commit a8ba41a7ad
No known key found for this signature in database
GPG Key ID: 0329F871B2079351
64 changed files with 4298 additions and 3 deletions

View File

@ -210,7 +210,7 @@ dependencies {
implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
// Szkolny.eu libraries/forks
//implementation "com.github.santoni0:NavLib:323288f"
implementation project(":navlib")
implementation "eu.szkolny:android-snowfall:1ca9ea2da3"
implementation "eu.szkolny:agendacalendarview:1.0.4"
implementation "eu.szkolny:cafebar:5bf0c618de"
@ -221,7 +221,6 @@ dependencies {
implementation "eu.szkolny.selective-dao:annotation:6a337f9"
officialImplementation "eu.szkolny:ssl-provider:1.0.0"
unofficialImplementation "eu.szkolny:ssl-provider:1.0.0"
implementation "pl.szczodrzynski:numberslidingpicker:2921225f76"
implementation "pl.szczodrzynski:recyclertablayout:700f980584"
implementation "pl.szczodrzynski:tachyon:551943a6b5"

44
navlib-font/build.gradle Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright 2019 Mike Penz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion setup.compileSdk
defaultConfig {
minSdkVersion setup.minSdk
targetSdkVersion setup.targetSdk
consumerProguardFiles 'consumer-proguard-rules.pro'
versionCode 10
versionName "1.0"
resValue "string", "NavLibFont_version", "${versionName}"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation "com.mikepenz:iconics-typeface-api:5.3.0-b01"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

View File

@ -0,0 +1 @@
-keep class com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont { *; }

View File

@ -0,0 +1,18 @@
#
# Copyright 2019 Mike Penz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
POM_NAME=Android-Iconics NavLibFont Typeface Library
POM_ARTIFACT_ID=navlibfont-typeface
POM_PACKAGING=aar

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2019 Mike Penz
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest package="com.mikepenz.iconics.typeface.library.navlibfont" />

View File

@ -0,0 +1,72 @@
/*
* Copyright 2019 Mike Penz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mikepenz.iconics.typeface.library.navlibfont
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.typeface.ITypeface
import java.util.LinkedList
@Suppress("EnumEntryName")
object NavLibFont : ITypeface {
override val fontRes: Int
get() = R.font.navlibfont_font_v1_0
override val characters: Map<String, Char> by lazy {
Icon.values().associate { it.name to it.character }
}
override val mappingPrefix: String
get() = "nav"
override val fontName: String
get() = "NavLibFont"
override val version: String
get() = "1.0"
override val iconCount: Int
get() = characters.size
override val icons: List<String>
get() = characters.keys.toCollection(LinkedList())
override val author: String
get() = "Kuba Szczodrzyński"
override val url: String
get() = "https://github.com/kuba2k2/NavLib"
override val description: String
get() = ""
override val license: String
get() = ""
override val licenseUrl: String
get() = ""
override fun getIcon(key: String): IIcon = Icon.valueOf(key)
enum class Icon constructor(override val character: Char) : IIcon {
nav_dots_vertical('\ue801'),
nav_menu('\ue800'),
nav_sort_ascending('\ue803'),
nav_sort_descending('\ue802');
override val typeface: ITypeface by lazy { NavLibFont }
}
}

Binary file not shown.

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2019 Mike Penz
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<string name="define_font_NavLibFont">com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont</string>
</resources>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2019 Mike Penz
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<string name="define_FontAwesome">year;author;libraryName;libraryWebsite</string>
<string name="library_FontAwesome_author">Kuba Szczodrzyński</string>
<string name="library_FontAwesome_authorWebsite">https://github.com/kuba2k2/NavLib</string>
<string name="library_FontAwesome_libraryName">NavLibFont</string>
<string name="library_FontAwesome_libraryDescription"></string>
<string name="library_FontAwesome_libraryWebsite">https://github.com/kuba2k2/NavLib</string>
<string name="library_FontAwesome_libraryVersion">@string/NavLibFont_version</string>
<string name="library_FontAwesome_licenseId"></string>
<string name="library_FontAwesome_isOpenSource">true</string>
<string name="library_FontAwesome_repositoryLink">https://github.com/kuba2k2/NavLib</string>
<!-- Custom variables section -->
<string name="library_FontAwesome_year">2018</string>
</resources>

55
navlib/build.gradle Normal file
View File

@ -0,0 +1,55 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion setup.compileSdk
defaultConfig {
minSdkVersion setup.minSdk
targetSdkVersion setup.targetSdk
versionCode release.versionCode
versionName release.versionName
consumerProguardFiles 'consumer-rules.pro'
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
buildFeatures {
dataBinding = true
}
packagingOptions {
exclude 'META-INF/library-core_release.kotlin_module'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "com.google.android.material:material:1.3.0"
api "com.mikepenz:materialize:1.2.1"
api "com.mikepenz:materialdrawer:8.3.3"
api "com.mikepenz:iconics-core:5.3.0-b01"
api "com.mikepenz:itemanimators:1.1.0"
compileOnly "pl.droidsonroids.gif:android-gif-drawable:1.2.15"
implementation "com.balysv:material-ripple:1.0.2"
implementation project(":navlib-font")
}

View File

@ -0,0 +1 @@
-keep class androidx.drawerlayout.widget.DrawerLayout { *; }

23
navlib/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,23 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in init.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class androidx.drawerlayout.widget.DrawerLayout { *; }

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pl.szczodrzynski.navlib" />

View File

@ -0,0 +1,170 @@
package pl.szczodrzynski.navlib
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.DecelerateInterpolator
import android.view.animation.ScaleAnimation
import android.view.animation.Transformation
import android.widget.LinearLayout
object Anim {
fun expand(v: View, duration: Int?, animationListener: Animation.AnimationListener?) {
v.measure(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
val targetHeight = v.measuredHeight
//Log.d("Anim", "targetHeight="+targetHeight);
v.visibility = View.VISIBLE
v.layoutParams.height = 0
val a = object : Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
v.layoutParams.height = if (interpolatedTime == 1.0f)
LinearLayout.LayoutParams.WRAP_CONTENT//(int)(targetHeight * interpolatedTime)
else
(targetHeight * interpolatedTime).toInt()
v.requestLayout()
}
override fun willChangeBounds(): Boolean {
return true
}
}
if (duration == null) {
a.duration = (targetHeight.toFloat() / v.context.resources.displayMetrics.density).toInt().toLong()
} else {
a.duration = duration as Long
}
if (animationListener != null) {
a.setAnimationListener(animationListener)
}
v.startAnimation(a)
}
fun collapse(v: View, duration: Int?, animationListener: Animation.AnimationListener?) {
val initialHeight = v.measuredHeight
val a = object : Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
if (interpolatedTime == 1.0f) {
v.visibility = View.GONE
return
}
v.layoutParams.height = initialHeight - (initialHeight.toFloat() * interpolatedTime).toInt()
v.requestLayout()
}
override fun willChangeBounds(): Boolean {
return true
}
}
if (duration == null) {
a.duration = (initialHeight.toFloat() / v.context.resources.displayMetrics.density).toInt().toLong()
} else {
a.duration = duration as Long
}
if (animationListener != null) {
a.setAnimationListener(animationListener)
}
v.startAnimation(a)
}
fun fadeIn(v: View, duration: Int?, animationListener: Animation.AnimationListener?) {
val fadeIn = AlphaAnimation(0f, 1f)
fadeIn.interpolator = DecelerateInterpolator() //add this
fadeIn.duration = duration!!.toLong()
fadeIn.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
v.visibility = View.VISIBLE
animationListener?.onAnimationStart(animation)
}
override fun onAnimationEnd(animation: Animation) {
animationListener?.onAnimationEnd(animation)
}
override fun onAnimationRepeat(animation: Animation) {
animationListener?.onAnimationRepeat(animation)
}
})
v.startAnimation(fadeIn)
}
fun fadeOut(v: View, duration: Int?, animationListener: Animation.AnimationListener?) {
val fadeOut = AlphaAnimation(1f, 0f)
fadeOut.interpolator = AccelerateInterpolator() //and this
fadeOut.duration = duration!!.toLong()
fadeOut.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
animationListener?.onAnimationStart(animation)
}
override fun onAnimationEnd(animation: Animation) {
v.visibility = View.INVISIBLE
animationListener?.onAnimationEnd(animation)
}
override fun onAnimationRepeat(animation: Animation) {
animationListener?.onAnimationRepeat(animation)
}
})
v.startAnimation(fadeOut)
}
fun scaleView(
v: View,
duration: Int?,
animationListener: Animation.AnimationListener?,
startScale: Float,
endScale: Float
) {
val anim = ScaleAnimation(
1f, 1f, // Start and end values for the X axis scaling
startScale, endScale, // Start and end values for the Y axis scaling
Animation.RELATIVE_TO_SELF, 0f, // Pivot point of X scaling
Animation.RELATIVE_TO_SELF, 0f
) // Pivot point of Y scaling
anim.fillAfter = true // Needed to keep the result of the animation
anim.duration = duration!!.toLong()
anim.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
animationListener?.onAnimationStart(animation)
}
override fun onAnimationEnd(animation: Animation) {
animationListener?.onAnimationEnd(animation)
}
override fun onAnimationRepeat(animation: Animation) {
animationListener?.onAnimationRepeat(animation)
}
})
v.startAnimation(anim)
}
class ResizeAnimation(
private val mView: View,
private val mFromWidth: Float,
private val mFromHeight: Float,
private val mToWidth: Float,
private val mToHeight: Float
) : Animation() {
private val width: Float
private val height: Float
init {
width = mView.width.toFloat()
height = mView.height.toFloat()
duration = 300
}
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
val height = (mToHeight - mFromHeight) * interpolatedTime + mFromHeight
val width = (mToWidth - mFromWidth) * interpolatedTime + mFromWidth
val p = mView.layoutParams
p.width = (width * this.width).toInt()
p.height = (height * this.height).toInt()
mView.requestLayout()
}
}
}

View File

@ -0,0 +1,109 @@
package pl.szczodrzynski.navlib;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import androidx.core.content.ContextCompat;
public class BadgeDrawable extends Drawable {
private Paint mBadgePaint;
private Paint mBadgePaint1;
private Paint mTextPaint;
private Rect mTxtRect = new Rect();
private String mCount = "";
private boolean mWillDraw = false;
public BadgeDrawable(Context context) {
float mTextSize = context.getResources().getDimension(R.dimen.badge_text_size);
mBadgePaint = new Paint();
mBadgePaint.setColor(0xffff3d00);
mBadgePaint.setAntiAlias(true);
mBadgePaint.setStyle(Paint.Style.FILL);
/*mBadgePaint1 = new Paint();
mBadgePaint1.setColor(ContextCompat.getColor(context.getApplicationContext(), R.color.grey_ivory5));
mBadgePaint1.setAntiAlias(true);
mBadgePaint1.setStyle(Paint.Style.FILL);*/
mTextPaint = new Paint();
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTypeface(Typeface.DEFAULT);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
public void draw(Canvas canvas) {
if (!mWillDraw) {
return;
}
Rect bounds = getBounds();
float width = bounds.right - bounds.left;
float height = bounds.bottom - bounds.top;
// Position the badge in the top-right quadrant of the icon.
/*Using Math.max rather than Math.min */
float radius = ((Math.max(width, height) / 2)) / 2;
float centerX = (width - radius - 1) +5;
float centerY = radius -5;
if(mCount.length() <= 2){
// Draw badge circle.
//canvas.drawCircle(centerX, centerY, (int)(radius+7.5), mBadgePaint1);
canvas.drawCircle(centerX, centerY, (int)(radius+5.5), mBadgePaint);
}
else{
//canvas.drawCircle(centerX, centerY, (int)(radius+8.5), mBadgePaint1);
canvas.drawCircle(centerX, centerY, (int)(radius+6.5), mBadgePaint);
// canvas.drawRoundRect(radius, radius, radius, radius, 10, 10, mBadgePaint);
}
// Draw badge count text inside the circle.
mTextPaint.getTextBounds(mCount, 0, mCount.length(), mTxtRect);
float textHeight = mTxtRect.bottom - mTxtRect.top;
float textY = centerY + (textHeight / 2f);
if(mCount.length() > 2)
canvas.drawText("99+", centerX, textY, mTextPaint);
else
canvas.drawText(mCount, centerX, textY, mTextPaint);
}
/*
Sets the count (i.e notifications) to display.
*/
public void setCount(String count) {
mCount = count;
// Only draw a badge if there are notifications.
mWillDraw = !count.equalsIgnoreCase("0");
invalidateSelf();
}
@Override
public void setAlpha(int alpha) {
// do nothing
}
@Override
public void setColorFilter(ColorFilter cf) {
// do nothing
}
@Override
public int getOpacity() {
return PixelFormat.UNKNOWN;
}
}

View File

@ -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
}
}
}
}

View File

@ -0,0 +1,85 @@
package pl.szczodrzynski.navlib
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.materialdrawer.*
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.AbstractDrawerItem
import com.mikepenz.materialdrawer.model.BaseDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.getDrawerItem
import com.mikepenz.materialdrawer.util.updateItem
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
/*inline fun DrawerBuilder.withOnDrawerItemClickListener(crossinline listener: (view: View?, position: Int, drawerItem: IDrawerItem<*>) -> Boolean): DrawerBuilder {
return this.withOnDrawerItemClickListener(object : Drawer.OnDrawerItemClickListener {
override fun onItemClick(view: View?, position: Int, drawerItem: IDrawerItem<*>): Boolean {
return listener(view, position, drawerItem)
}
})
}
inline fun DrawerBuilder.withOnDrawerItemLongClickListener(crossinline listener: (view: View, position: Int, drawerItem: IDrawerItem<*>) -> Boolean): DrawerBuilder {
return this.withOnDrawerItemLongClickListener(object : Drawer.OnDrawerItemLongClickListener {
override fun onItemLongClick(view: View, position: Int, drawerItem: IDrawerItem<*>): Boolean {
return listener(view, position, drawerItem)
}
})
}
inline fun AccountHeaderBuilder.withOnAccountHeaderListener(crossinline listener: (view: View?, profile: IProfile<*>, current: Boolean) -> Boolean): AccountHeaderBuilder {
return this.withOnAccountHeaderListener(object : AccountHeader.OnAccountHeaderListener {
override fun onProfileChanged(view: View?, profile: IProfile<*>, current: Boolean): Boolean {
return listener(view, profile, current)
}
})
}
inline fun AccountHeaderBuilder.withOnAccountHeaderItemLongClickListener(crossinline listener: (view: View, profile: IProfile<*>, current: Boolean) -> Boolean): AccountHeaderBuilder {
return this.withOnAccountHeaderItemLongClickListener(object : AccountHeader.OnAccountHeaderItemLongClickListener {
override fun onProfileLongClick(view: View, profile: IProfile<*>, current: Boolean): Boolean {
return listener(view, profile, current)
}
})
}
inline fun AccountHeaderBuilder.withOnAccountHeaderProfileImageListener(
crossinline onClick: (
view: View,
profile: IProfile<*>,
current: Boolean
) -> Boolean,
crossinline onLongClick: (
view: View,
profile: IProfile<*>,
current: Boolean
) -> Boolean
): AccountHeaderBuilder {
return this.withOnAccountHeaderProfileImageListener(object : AccountHeader.OnAccountHeaderProfileImageListener {
override fun onProfileImageClick(view: View, profile: IProfile<*>, current: Boolean): Boolean {
return onClick(view, profile, current)
}
override fun onProfileImageLongClick(view: View, profile: IProfile<*>, current: Boolean): Boolean {
return onLongClick(view, profile, current)
}
})
}
inline fun MiniDrawer.withOnMiniDrawerItemClickListener(crossinline listener: (view: View?, position: Int, drawerItem: IDrawerItem<*>, type: Int) -> Boolean): MiniDrawer {
return this.withOnMiniDrawerItemClickListener(object : MiniDrawer.OnMiniDrawerItemClickListener {
override fun onItemClick(view: View?, position: Int, drawerItem: IDrawerItem<*>, type: Int): Boolean {
return listener(view, position, drawerItem, type)
}
})
}*/
fun MaterialDrawerSliderView.updateBadge(identifier: Long, badge: StringHolder?) {
val drawerItem = getDrawerItem(identifier)
if (drawerItem is Badgeable) {
drawerItem.withBadge(badge)
updateItem(drawerItem)
}
}
fun <T : Iconable> T.withIcon(icon: IIcon) = withIcon(pl.szczodrzynski.navlib.ImageHolder(icon))

View File

@ -0,0 +1,121 @@
package pl.szczodrzynski.navlib
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable
import android.net.Uri
import android.widget.ImageView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.utils.actionBar
import com.mikepenz.iconics.utils.sizeDp
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import pl.droidsonroids.gif.GifDrawable
import java.io.FileNotFoundException
/**
* Created by mikepenz on 13.07.15.
*/
open class ImageHolder : com.mikepenz.materialdrawer.holder.ImageHolder {
constructor(@DrawableRes iconRes: Int, colorFilter: Int?) : super(iconRes) {
this.colorFilter = colorFilter
}
constructor(iicon: IIcon) : super(null as Drawable?) {
this.iicon = iicon
}
constructor() : super()
constructor(url: String) : super(url)
constructor(uri: Uri) : super(uri)
constructor(icon: Drawable?) : super(icon)
constructor(bitmap: Bitmap) : super(bitmap)
constructor(iconRes: Int) : super(iconRes)
var iicon: IIcon? = null
@ColorInt
var colorFilter: Int? = null
var colorFilterMode: PorterDuff.Mode = PorterDuff.Mode.DST_OVER
/**
* sets an existing image to the imageView
*
* @param imageView
* @param tag used to identify imageViews and define different placeholders
* @return true if an image was set
*/
override fun applyTo(imageView: ImageView, tag: String?): Boolean {
val ii = iicon
if (uri != null) {
if (uri.toString().endsWith(".gif", true)) {
imageView.setImageDrawable(GifDrawable(uri.toString()))
}
else {
val consumed = DrawerImageLoader.instance.setImage(imageView, uri!!, tag)
if (!consumed) {
imageView.setImageURI(uri)
}
}
} else if (icon != null) {
imageView.setImageDrawable(icon)
} else if (bitmap != null) {
imageView.setImageBitmap(bitmap)
} else if (iconRes != -1) {
imageView.setImageResource(iconRes)
} else if (ii != null) {
imageView.setImageDrawable(IconicsDrawable(imageView.context, ii).actionBar())
} else {
imageView.setImageBitmap(null)
return false
}
if (colorFilter != null) {
imageView.colorFilter = PorterDuffColorFilter(colorFilter!!, colorFilterMode)
}
return true
}
/**
* this only handles Drawables
*
* @param ctx
* @param iconColor
* @param tint
* @return
*/
override fun decideIcon(ctx: Context, iconColor: ColorStateList, tint: Boolean, paddingDp: Int): Drawable? {
var icon: Drawable? = icon
val ii = iicon
val uri = uri
when {
ii != null -> icon = IconicsDrawable(ctx).apply {
this.icon = ii
colorList = iconColor
sizeDp = 24
}
iconRes != -1 -> icon = AppCompatResources.getDrawable(ctx, iconRes)
uri != null -> try {
val inputStream = ctx.contentResolver.openInputStream(uri)
icon = Drawable.createFromStream(inputStream, uri.toString())
} catch (e: FileNotFoundException) {
//no need to handle this
}
}
//if we got an icon AND we have auto tinting enabled AND it is no IIcon, tint it ;)
if (icon != null && tint && iicon == null) {
icon = icon.mutate()
icon.setColorFilter(iconColor.defaultColor, PorterDuff.Mode.SRC_IN)
}
return icon
}
}

View File

@ -0,0 +1,211 @@
package pl.szczodrzynski.navlib
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.LayerDrawable
import android.util.AttributeSet
import android.view.Gravity
import android.view.MenuItem
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet
import pl.szczodrzynski.navlib.drawer.NavDrawer
class NavBottomBar : BottomAppBar {
constructor(context: Context) : super(context) {
create(null, 0)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
create(attrs, 0)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
create(attrs, defStyle)
}
var drawer: NavDrawer? = null
var bottomSheet: NavBottomSheet? = null
var fabView: FloatingActionButton? = null
var fabExtendedView: ExtendedFloatingActionButton? = null
/**
* Shows the BottomAppBar and sets the contentView's margin to be
* above the BottomAppBar.
*/
var enable = true
set(value) {
field = value
visibility = if (value) View.VISIBLE else View.GONE
setFabParams()
}
/**
* Whether the FAB should be visible.
*/
var fabEnable = true
set(value) {
field = value
setFabVisibility()
}
/**
* Whether an ExtendableFloatingActionButton should be used
* instead of a normal FloatingActionButton.
* Note that the extendable button does not support end alignment/gravity
* when used together with the bottom app bar.
*/
var fabExtendable = true
set(value) {
field = value
setFabParams()
}
/**
* If BottomAppBar is enabled, sets its fabAlignmentMode.
* Else, sets the actual FAB's gravity.
*/
var fabGravity = Gravity.CENTER
set(value) {
field = value
setFabParams()
}
/**
* Whether the FAB should be extended and its text visible.
*/
var fabExtended = false
set(value) {
field = value
if (fabExtended)
fabExtendedView?.extend()
else
fabExtendedView?.shrink()
}
/**
* Set the FAB's icon.
*/
var fabIcon: IIcon? = null
set(value) {
field = value
fabView?.setImageDrawable(IconicsDrawable(context).apply {
icon = value
colorAttr(context, R.attr.colorFabIcon)
sizeDp = 24
})
fabExtendedView?.icon = IconicsDrawable(context).apply {
icon = value
colorAttr(context, R.attr.colorFabIcon)
sizeDp = 24
}
}
/**
* Set the ExtendedFAB's text.
*/
var fabExtendedText
get() = fabExtendedView?.text
set(value) {
fabExtendedView?.text = value
}
/**
* Set the FAB's on click listener
*/
fun setFabOnClickListener(onClickListener: OnClickListener?) {
fabView?.setOnClickListener(onClickListener)
fabExtendedView?.setOnClickListener(onClickListener)
}
@SuppressLint("ClickableViewAccessibility")
private fun create(attrs: AttributeSet?, defStyle: Int) {
setOnTouchListener { _, event ->
if (bottomSheet?.enable != true || bottomSheet?.enableDragToOpen != true)
return@setOnTouchListener false
bottomSheet?.dispatchBottomBarEvent(event)
true
}
elevation = 0f
val icon = ContextCompat.getDrawable(context, R.drawable.ic_menu_badge) as LayerDrawable?
icon?.apply {
mutate()
setDrawableByLayerId(R.id.ic_menu, IconicsDrawable(context).apply {
this.icon = NavLibFont.Icon.nav_menu
sizeDp = 24
colorAttr(context, R.attr.colorOnPrimary)
})
setDrawableByLayerId(R.id.ic_badge, BadgeDrawable(context))
}
navigationIcon = icon
menu.add(0, -1, 0, "Menu")
.setIcon(IconicsDrawable(context).apply {
this.icon = NavLibFont.Icon.nav_dots_vertical
sizeDp = 24
colorAttr(context, R.attr.colorOnPrimary)
})
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
setNavigationOnClickListener {
drawer?.toggle()
}
super.setOnMenuItemClickListener {
if (it.itemId == -1 && bottomSheet?.enable == true) {
bottomSheet?.toggle()
}
else {
onMenuItemClickListener?.onMenuItemClick(it)
}
true
}
}
private fun setFabParams() {
val layoutParams =
((if (fabExtendable) fabExtendedView?.layoutParams else fabView?.layoutParams) ?: return) as CoordinatorLayout.LayoutParams
if (enable) {
layoutParams.anchorId = this.id
if (fabExtendable)
layoutParams.anchorGravity = if (fabExtendable) fabGravity or Gravity.TOP else Gravity.NO_GRAVITY
layoutParams.gravity = Gravity.NO_GRAVITY
}
else {
layoutParams.anchorId = View.NO_ID
if (fabExtendable)
layoutParams.anchorGravity = Gravity.NO_GRAVITY
layoutParams.gravity = fabGravity or Gravity.BOTTOM
}
fabAlignmentMode = if (fabGravity == Gravity.END) FAB_ALIGNMENT_MODE_END else FAB_ALIGNMENT_MODE_CENTER
if (fabExtendable)
fabExtendedView?.layoutParams = layoutParams
else
fabView?.layoutParams = layoutParams
setFabVisibility()
}
private fun setFabVisibility() {
if (fabEnable && fabExtendable) {
fabView?.hide()
fabExtendedView?.show()
}
else if (fabEnable) {
fabView?.show()
fabExtendedView?.hide()
}
else {
fabView?.hide()
fabExtendedView?.hide()
}
}
private var onMenuItemClickListener: OnMenuItemClickListener? = null
override fun setOnMenuItemClickListener(listener: OnMenuItemClickListener?) {
onMenuItemClickListener = listener
}
}

View File

@ -0,0 +1,55 @@
package pl.szczodrzynski.navlib
import android.content.Context
import android.util.AttributeSet
import android.widget.ImageView
import com.google.android.material.appbar.MaterialToolbar
class NavToolbar : MaterialToolbar {
constructor(context: Context) : super(context) {
create(null, 0)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
create(attrs, 0)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
create(attrs, defStyle)
}
var toolbarImage: ImageView? = null
set(value) {
field = value
toolbarImage?.setOnClickListener {
profileImageClickListener?.invoke()
}
}
override fun setSubtitle(subtitle: CharSequence?) {
if(subtitle.isNullOrEmpty()) {
setPadding(0, 0, 0, 0)
toolbarImage?.translationY = 0f
} else {
setPadding(0, -1, 0, 5)
toolbarImage?.translationY = 6f
}
super.setSubtitle(subtitle)
}
private fun create(attrs: AttributeSet?, defStyle: Int) {
}
var subtitleFormat: Int? = null
var subtitleFormatWithUnread: Int? = null
var profileImageClickListener: (() -> Unit)? = null
var profileImage
get() = toolbarImage?.drawable
set(value) {
toolbarImage?.setImageDrawable(value)
}
}

View File

@ -0,0 +1,213 @@
package pl.szczodrzynski.navlib
import android.content.Context
import android.content.res.Configuration
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.graphics.Point
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.children
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.android.synthetic.main.nav_view.view.*
import pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet
import pl.szczodrzynski.navlib.drawer.NavDrawer
class NavView : FrameLayout {
companion object {
const val SOURCE_OTHER = 0
const val SOURCE_DRAWER = 1
const val SOURCE_BOTTOM_SHEET = 1
}
private var contentView: LinearLayout? = null
private lateinit var statusBarBackground: View
private lateinit var navigationBarBackground: View
private lateinit var mainView: LinearLayout
private lateinit var floatingActionButton: FloatingActionButton
private lateinit var extendedFloatingActionButton: ExtendedFloatingActionButton
lateinit var drawer: NavDrawer
lateinit var toolbar: NavToolbar
lateinit var bottomBar: NavBottomBar
lateinit var bottomSheet: NavBottomSheet
val coordinator by lazy {
findViewById<CoordinatorLayout>(R.id.nv_coordinator)
}
var navigationLoader: NavigationLoader? = null
constructor(context: Context) : super(context) {
create(null, 0)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
create(attrs, 0)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
create(attrs, defStyle)
}
private fun create(attrs: AttributeSet?, defStyle: Int) {
// Load attributes
val a = context.obtainStyledAttributes(attrs, R.styleable.NavView, defStyle, 0)
/*_exampleString = a.getString(
R.styleable.NavView_exampleString
)*/
a.recycle()
val layoutInflater = LayoutInflater.from(context)
layoutInflater.inflate(R.layout.nav_view, this)
contentView = findViewById<LinearLayout>(R.id.nv_content)
statusBarBackground = findViewById(R.id.nv_statusBarBackground)
navigationBarBackground = findViewById(R.id.nv_navigationBarBackground)
mainView = findViewById(R.id.nv_main)
floatingActionButton = findViewById(R.id.nv_floatingActionButton)
extendedFloatingActionButton = findViewById(R.id.nv_extendedFloatingActionButton)
drawer = NavDrawer(
context,
findViewById(R.id.nv_drawerLayout),
findViewById(R.id.nv_drawerContainerLandscape),
findViewById(R.id.nv_miniDrawerContainerPortrait),
findViewById(R.id.nv_miniDrawerElevation)
)
toolbar = findViewById(R.id.nv_toolbar)
bottomBar = findViewById(R.id.nv_bottomBar)
bottomSheet = findViewById(R.id.nv_bottomSheet)
drawer.toolbar = toolbar
drawer.bottomBar = bottomBar
toolbar.toolbarImage = findViewById(R.id.nv_toolbar_image)
bottomBar.drawer = drawer
bottomBar.bottomSheet = bottomSheet
bottomBar.fabView = floatingActionButton
bottomBar.fabExtendedView = extendedFloatingActionButton
ripple.isEnabled = false
ripple.children.forEach { it.isEnabled = false }
//bottomSheetBehavior.peekHeight = displayHeight
}
private fun convertDpToPixel(dp: Float): Float {
val resources = context.resources
val metrics = resources.displayMetrics
return dp * (metrics.densityDpi / 160f)
}
fun gainAttentionOnBottomBar() {
var x = ripple.width.toFloat()
var y = ripple.height.toFloat()
x -= convertDpToPixel(56f) / 2
y -= convertDpToPixel(56f) / 2
ripple.performRipple(Point(x.toInt(), y.toInt()))
}
fun configSystemBarsUtil(systemBarsUtil: SystemBarsUtil) {
this.systemBarsUtil = systemBarsUtil.apply {
this.statusBarBgView = statusBarBackground
this.navigationBarBgView = navigationBarBackground
this.statusBarDarkView = nv_statusBarDarker
//this.navigationBarDarkView = navigationBarBackground
this.insetsListener = nv_drawerLayout
this.marginBySystemBars = mainView
this.paddingByNavigationBar = bottomSheet.getContentView()
}
}
var enableBottomSheet = true
var enableBottomSheetDrag = true
var bottomBarEnable = true
get() = bottomBar.enable
set(value) {
field = value
bottomBar.enable = value
setContentMargins() // TODO combine bottomBarEnable and bottomBar.enable
}
/**
* Shows the toolbar and sets the contentView's margin to be
* below the toolbar.
*/
var showToolbar = true; set(value) {
toolbar.visibility = if (value) View.VISIBLE else View.GONE
field = value
setContentMargins()
}
/**
* Set the FAB's on click listener
*/
fun setFabOnClickListener(onClickListener: OnClickListener?) {
bottomBar.setFabOnClickListener(onClickListener)
}
internal var systemBarsUtil: SystemBarsUtil? = null
private fun setContentMargins() {
val layoutParams = CoordinatorLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
val actionBarSize = 56 * context.resources.displayMetrics.density
layoutParams.topMargin = if (showToolbar) actionBarSize.toInt() else 0
layoutParams.bottomMargin = if (bottomBarEnable) actionBarSize.toInt() else 0
contentView?.layoutParams = layoutParams
}
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
Log.d(
"NavLib",
"CONFIGURATION CHANGED: ${newConfig?.screenWidthDp}x${newConfig?.screenHeightDp} "+if (newConfig?.orientation == ORIENTATION_PORTRAIT) "portrait" else "landscape"
)
systemBarsUtil?.commit()
drawer.decideDrawerMode(
newConfig?.orientation ?: ORIENTATION_PORTRAIT,
newConfig?.screenWidthDp ?: 0,
newConfig?.screenHeightDp ?: 0
)
}
fun onBackPressed(): Boolean {
if (drawer.isOpen && !drawer.fixedDrawerEnabled()) {
if (drawer.profileSelectionIsOpen) {
drawer.profileSelectionClose()
return true
}
drawer.close()
return true
}
if (bottomSheet.isOpen) {
bottomSheet.close()
return true
}
return false
}
override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
if (contentView == null) {
super.addView(child, index, params)
}
else {
contentView!!.addView(child, index, params)
}
}
}

View File

@ -0,0 +1,5 @@
package pl.szczodrzynski.navlib
interface NavigationLoader {
fun load(itemId: Int, callerId: Int, source: Int, args: Map<String, Any?>)
}

View File

@ -0,0 +1,376 @@
package pl.szczodrzynski.navlib
import android.app.Activity
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.content.res.Resources
import android.graphics.Color
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES
import android.util.Log
import android.view.View
import android.view.View.*
import android.view.Window
import android.view.WindowManager
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat
import com.mikepenz.materialize.util.KeyboardUtil
class SystemBarsUtil(private val activity: Activity) {
companion object {
private const val COLOR_TRANSPARENT = Color.TRANSPARENT
/**
* A fallback color.
* Tells to apply a #22000000 overlay over the status/nav bar color.
* This has the same effect as [statusBarDarker].
*/
const val COLOR_HALF_TRANSPARENT = -1
/**
* Use ?colorPrimaryDark as a fallback or status bar color.
*/
const val COLOR_PRIMARY_DARK = -2
/**
* A fallback color.
* Not recommended to use as [statusBarFallbackLight] because it will make status bar
* icons almost invisible.
*/
const val COLOR_DO_NOT_CHANGE = -3
private const val TARGET_MODE_NORMAL = 0
private const val TARGET_MODE_LIGHT = 1
private const val TARGET_MODE_GRADIENT = 2
}
val window: Window by lazy {
activity.window
}
val resources: Resources by lazy {
activity.resources
}
/**
* A view which will have the padding added when the soft input keyboard appears.
*/
var paddingByKeyboard: View? = null
/**
* Whether the app should be fullscreen.
*
* This means it will display under the system bars
* and you should probably provide [statusBarBgView],
* [navigationBarBgView] and [marginBySystemBars].
*/
var appFullscreen = false
/**
* Define the color used to tint the status bar background.
*
* Valid values are [COLOR_PRIMARY_DARK] or a color integer.
*
* You cannot use neither [COLOR_HALF_TRANSPARENT] nor [COLOR_DO_NOT_CHANGE] here.
* See [statusBarDarker].
*/
var statusBarColor = COLOR_PRIMARY_DARK
/**
* Whether the status bar should have a dark overlay (#22000000).
*
* Useful if the [statusBarColor] is set to a bright color and is the same as an action bar.
* Not useful if [statusBarColor] is [COLOR_PRIMARY_DARK].
*/
var statusBarDarker = false
/**
* A fallback status bar color used on Android Lollipop
* when the [statusBarColor] combined with [statusBarDarker] is
* too bright not to blend with status bar icons (they cannot be
* set to dark).
*
* This will (most likely) not be used when [statusBarDarker] is true.
*
* Valid values are [COLOR_HALF_TRANSPARENT], [COLOR_PRIMARY_DARK], [COLOR_DO_NOT_CHANGE].
*/
var statusBarFallbackLight = COLOR_HALF_TRANSPARENT
/**
* A fallback status bar color used on Android KitKat and older.
* On these systems there is a black-to-transparent gradient as
* the status bar background.
*
* Valid values are [COLOR_HALF_TRANSPARENT], [COLOR_PRIMARY_DARK], [COLOR_DO_NOT_CHANGE].
*/
var statusBarFallbackGradient = COLOR_DO_NOT_CHANGE
// TODO remove - test for huawei
var statusBarTranslucent = false
/**
* If false, the nav bar is mostly translucent but not completely transparent.
*/
var navigationBarTransparent = true
/**
* A background view to be resized in order to fit under the status bar.
*/
var statusBarBgView: View? = null
/**
* A background view to be resized in order to fit under the nav bar.
*/
var navigationBarBgView: View? = null
/**
* A dark, half-transparent view to be resized in order to fit under the status bar.
*/
var statusBarDarkView: View? = null
/**
* A dark, half-transparent view to be resized in order to fit under the nav bar.
*/
var navigationBarDarkView: View? = null
/**
* A view which will have the margin added not to overlap with the status/nav bar.
*/
var marginBySystemBars: View? = null
/**
* A view which will listen to the inset applying.
*/
var insetsListener: View? = null
/**
* A view which will have the padding added not to overlap with the nav bar.
* Useful for persistent bottom sheets.
* Requires [marginBySystemBars].
*/
var paddingByNavigationBar: View? = null
private var keyboardUtil: KeyboardUtil? = null
private var insetsApplied = false
fun commit() {
Log.d("NavLib", "SystemBarsUtil applying")
insetsApplied = false
if (paddingByKeyboard != null) {
// thanks mikepenz for this life-saving class
keyboardUtil = KeyboardUtil(activity, paddingByKeyboard)
keyboardUtil?.enable()
}
// get the correct target SB color
var targetStatusBarColor = statusBarColor
if (targetStatusBarColor == COLOR_PRIMARY_DARK)
targetStatusBarColor = getColorFromAttr(activity, R.attr.colorPrimaryDark)
var targetStatusBarDarker = statusBarDarker
// fallback if the SB color is too light for the icons to be visible
// applicable on Lollipop 5.0 and TouchWiz 4.1-4.3
var targetStatusBarFallbackLight = statusBarFallbackLight
if (targetStatusBarFallbackLight == COLOR_PRIMARY_DARK)
targetStatusBarFallbackLight = getColorFromAttr(activity, R.attr.colorPrimaryDark)
// fallback if there is a gradient under the status bar
// applicable on AOSP/similar 4.4 and Huawei EMUI Lollipop
// TODO check huawei 6.0+ for gradient bars, check huawei 4.4
var targetStatusBarFallbackGradient = statusBarFallbackGradient
if (targetStatusBarFallbackGradient == COLOR_PRIMARY_DARK)
targetStatusBarFallbackGradient = getColorFromAttr(activity, R.attr.colorPrimaryDark)
// determines the target mode that will be applied
var targetStatusBarMode = TARGET_MODE_NORMAL
val targetStatusBarLight = ColorUtils.calculateLuminance(targetStatusBarColor) > 0.75 && !targetStatusBarDarker
if (appFullscreen) {
window.decorView.systemUiVisibility = 0
// API 19+ (KitKat 4.4+) - make the app fullscreen.
// On lower APIs this is useless because
// #1 the status/nav bar cannot be transparent (except Samsung TouchWiz)
// #2 tablets do not report status/nav bar height correctly
// #3 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the activity not resize when keyboard is open
// Samsung TouchWiz - app will go fullscreen. There is a problem though, see #3.
var targetAppFullscreen = false
if (SDK_INT >= VERSION_CODES.KITKAT) {
targetAppFullscreen = true
}
if (SDK_INT in VERSION_CODES.KITKAT until VERSION_CODES.LOLLIPOP) {
// API 19-20 (KitKat 4.4) - set gradient status bar
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
// take FallbackGradient color
targetStatusBarMode = TARGET_MODE_GRADIENT
// disable darker even if [statusBarDarker] == true BUT gradient fallback is not COLOR_HALF_TRANSPARENT
//targetStatusBarDarker = targetStatusBarDarker && targetStatusBarFallbackGradient == COLOR_HALF_TRANSPARENT
}
else if (SDK_INT >= VERSION_CODES.LOLLIPOP) {
// API 21+ (Lollipop 5.0+) - set transparent status bar
if (statusBarTranslucent) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
}
else {
window.statusBarColor = Color.TRANSPARENT
}
if (SDK_INT < VERSION_CODES.M && targetStatusBarLight) {
// take FallbackLight color
targetStatusBarMode = TARGET_MODE_LIGHT
}
}
if (SDK_INT >= VERSION_CODES.M && targetStatusBarLight) {
// API 23+ (Marshmallow 6.0+) - set the status bar icons to dark color if [statusBarLight] is true
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
// FOR SAMSUNG/SONY DEVICES (TouchWiz 4.1-4.3)
if (SDK_INT < VERSION_CODES.KITKAT) {
val libs = activity.packageManager.systemSharedLibraryNames
var reflect: String? = null
// TODO galaxy s3 - opening keyboard does not resize activity if fullscreen
if (libs != null) {
for (lib in libs) {
Log.d("SBU", lib)
if (lib == "touchwiz")
// SYSTEM_UI_FLAG_TRANSPARENT_BACKGROUND = 0x00001000
reflect = "SYSTEM_UI_FLAG_TRANSPARENT_BACKGROUND"
else if (lib.startsWith("com.sonyericsson.navigationbar"))
reflect = "SYSTEM_UI_FLAG_TRANSPARENT"
}
if (reflect != null) {
try {
val field = View::class.java.getField(reflect)
var flag = 0
if (field.type === Integer.TYPE)
flag = field.getInt(null)
if (flag != 0) {
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or flag
targetStatusBarMode = TARGET_MODE_LIGHT /* or TARGET_MODE_GRADIENT */
targetAppFullscreen = true
}
} catch (e: Exception) {
}
}
}
}
// TODO huawei detection for 5.0+
targetStatusBarColor = when (targetStatusBarMode) {
TARGET_MODE_LIGHT -> when (targetStatusBarFallbackLight) {
COLOR_DO_NOT_CHANGE -> targetStatusBarColor
COLOR_HALF_TRANSPARENT -> {
targetStatusBarDarker = true
targetStatusBarColor
}
else -> targetStatusBarFallbackLight
}
TARGET_MODE_GRADIENT -> when (targetStatusBarFallbackGradient) {
COLOR_DO_NOT_CHANGE -> {
targetStatusBarDarker = false
targetStatusBarColor
}
COLOR_HALF_TRANSPARENT -> {
targetStatusBarDarker = true
targetStatusBarColor
}
else -> {
targetStatusBarDarker = false
targetStatusBarFallbackGradient
}
}
else -> targetStatusBarColor
}
statusBarBgView?.setBackgroundColor(targetStatusBarColor)
statusBarDarkView?.visibility = if (targetStatusBarDarker) VISIBLE else GONE
if (targetAppFullscreen) {
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
}
// TODO navigation bar options like status bar
// NAVIGATION BAR
if (SDK_INT >= VERSION_CODES.KITKAT && (SDK_INT < VERSION_CODES.LOLLIPOP || !navigationBarTransparent)) {
// API 19-20 (KitKat 4.4) - set gradient navigation bar
// API 21+ (Lollipop 5.0+) - set half-transparent navigation bar if [navigationBarTransparent] is false
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
}
if (SDK_INT >= VERSION_CODES.LOLLIPOP && navigationBarTransparent) {
// API 21+ (Lollipop 5.0+) - set fully transparent navigation bar if [navigationBarTransparent] is true
window.navigationBarColor = Color.TRANSPARENT
}
// PADDING
if (insetsListener != null) {
if (SDK_INT >= VERSION_CODES.LOLLIPOP && false) {
ViewCompat.setOnApplyWindowInsetsListener(insetsListener!!) { _, insets ->
Log.d("NavLib", "Got insets left = ${insets.systemWindowInsetLeft}, top = ${insets.systemWindowInsetTop}, right = ${insets.systemWindowInsetRight}, bottom = ${insets.systemWindowInsetBottom}")
if (insetsApplied)
return@setOnApplyWindowInsetsListener insets.consumeSystemWindowInsets()
Log.d("NavLib", "Applied insets left = ${insets.systemWindowInsetLeft}, top = ${insets.systemWindowInsetTop}, right = ${insets.systemWindowInsetRight}, bottom = ${insets.systemWindowInsetBottom}")
insetsApplied = true
applyPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,
insets.systemWindowInsetBottom
)
insets.consumeSystemWindowInsets()
}
}
else {
var statusBarSize = 0
val statusBarRes = resources.getIdentifier("status_bar_height", "dimen", "android")
if (statusBarRes > 0 && targetAppFullscreen) {
statusBarSize = resources.getDimensionPixelSize(statusBarRes)
}
var navigationBarSize = 0
if (hasNavigationBar(activity) && targetAppFullscreen) {
val orientation = resources.configuration.orientation
val navigationBarRes = when {
orientation == ORIENTATION_PORTRAIT ->
resources.getIdentifier("navigation_bar_height", "dimen", "android")
isTablet(activity) ->
resources.getIdentifier("navigation_bar_height_landscape", "dimen", "android")
else ->
resources.getIdentifier("navigation_bar_width", "dimen", "android")
}
if (navigationBarRes > 0) {
navigationBarSize = resources.getDimensionPixelSize(navigationBarRes)
}
}
applyPadding(
0,
statusBarSize,
0,
navigationBarSize
)
}
}
}
else {
// app not fullscreen
// TODO statusBarColor & navigationBarColor if not fullscreen (it's possible)
}
}
private fun applyPadding(left: Int, top: Int, right: Int, bottom: Int) {
marginBySystemBars?.setPadding(left, top, right, bottom)
statusBarBgView?.layoutParams?.height = top
navigationBarBgView?.layoutParams?.height = bottom
statusBarDarkView?.layoutParams?.height = top
navigationBarDarkView?.layoutParams?.height = bottom
paddingByNavigationBar?.setPadding(
(8 * resources.displayMetrics.density).toInt(),
0,
(8 * resources.displayMetrics.density).toInt(),
bottom
)
}
fun destroy() {
if (paddingByKeyboard != null) {
keyboardUtil?.disable()
}
}
}

View File

@ -0,0 +1,164 @@
package pl.szczodrzynski.navlib
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.View
import android.view.WindowManager
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import com.google.android.material.elevation.ElevationOverlayProvider
import com.mikepenz.iconics.IconicsColor
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.utils.colorInt
/*private val displayMetrics by lazy {
context.resources.displayMetrics
}*/
/*private val configuration by lazy { context.resources.configuration }
private val displayWidth: Int by lazy { configuration.screenWidthDp }
private val displayHeight: Int by lazy { configuration.screenHeightDp }*/
fun getTopInset(context: Context, view: View): Float {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
(view.rootWindowInsets?.systemWindowInsetTop ?: 24)
} else {
24
} * context.resources.displayMetrics.density
}
fun getLeftInset(context: Context, view: View): Float {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
(view.rootWindowInsets?.systemWindowInsetLeft ?: 0)
} else {
0
} * context.resources.displayMetrics.density
}
fun getRightInset(context: Context, view: View): Float {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
(view.rootWindowInsets?.systemWindowInsetRight ?: 0)
} else {
0
} * context.resources.displayMetrics.density
}
fun getBottomInset(context: Context, view: View): Float {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
(view.rootWindowInsets?.systemWindowInsetBottom ?: 48)
} else {
48
} * context.resources.displayMetrics.density
}
fun View.getActivity(): Activity {
return findViewById<View>(android.R.id.content).context as Activity
}
fun blendColors(background: Int, foreground: Int): Int {
val r1 = (background shr 16 and 0xff)
val g1 = (background shr 8 and 0xff)
val b1 = (background and 0xff)
val r2 = (foreground shr 16 and 0xff)
val g2 = (foreground shr 8 and 0xff)
val b2 = (foreground and 0xff)
val a2 = (foreground shr 24 and 0xff)
//ColorUtils.compositeColors()
val factor = a2.toFloat() / 255f
val red = (r1 * (1 - factor) + r2 * factor)
val green = (g1 * (1 - factor) + g2 * factor)
val blue = (b1 * (1 - factor) + b2 * factor)
return (0xff000000 or (red.toLong() shl 16) or (green.toLong() shl 8) or (blue.toLong())).toInt()
}
fun elevateSurface(context: Context, dp: Int): Int {
ElevationOverlayProvider(context).apply {
return compositeOverlay(themeSurfaceColor, dp * context.resources.displayMetrics.density)
}
}
fun isTablet(c: Context): Boolean {
return (c.resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
}
fun hasNavigationBar(context: Context): Boolean {
val id = context.resources.getIdentifier("config_showNavigationBar", "bool", "android")
var hasNavigationBar = id > 0 && context.resources.getBoolean(id)
if (!hasNavigationBar && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val d = (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
val realDisplayMetrics = DisplayMetrics()
d.getRealMetrics(realDisplayMetrics)
val realHeight = realDisplayMetrics.heightPixels
val realWidth = realDisplayMetrics.widthPixels
val displayMetrics = DisplayMetrics()
d.getMetrics(displayMetrics)
val displayHeight = displayMetrics.heightPixels
val displayWidth = displayMetrics.widthPixels
hasNavigationBar = realWidth - displayWidth > 0 || realHeight - displayHeight > 0
}
// Allow a system property to override this. Used by the emulator.
// See also hasNavigationBar().
val navBarOverride = System.getProperty("qemu.hw.mainkeys")
if (navBarOverride == "1")
hasNavigationBar = true
else if (navBarOverride == "0") hasNavigationBar = false
return hasNavigationBar
}
fun IconicsDrawable.colorAttr(context: Context, @AttrRes attrRes: Int) {
colorInt = getColorFromAttr(context, attrRes)
}
fun getColorFromAttr(context: Context, @AttrRes color: Int): Int {
val typedValue = TypedValue()
context.theme.resolveAttribute(color, typedValue, true)
return typedValue.data
}
fun Context.getDrawableFromRes(@DrawableRes id: Int): Drawable {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
resources.getDrawable(id, theme)
}
else {
resources.getDrawable(id)
}
}
@ColorInt
fun Context.getColorFromRes(@ColorRes id: Int): Int {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
resources.getColor(id, theme)
}
else {
resources.getColor(id)
}
}
fun crc16(buffer: String): Int {
/* Note the change here */
var crc = 0x1D0F
for (j in buffer) {
crc = crc.ushr(8) or (crc shl 8) and 0xffff
crc = crc xor (j.toInt() and 0xff)//byte to int, trunc sign
crc = crc xor (crc and 0xff shr 4)
crc = crc xor (crc shl 12 and 0xffff)
crc = crc xor (crc and 0xFF shl 5 and 0xffff)
}
crc = crc and 0xffff
return crc
}

View File

@ -0,0 +1,38 @@
package pl.szczodrzynski.navlib.bottomsheet
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.navlib.R
import pl.szczodrzynski.navlib.bottomsheet.items.IBottomSheetItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
class BottomSheetAdapter(val items: List<IBottomSheetItem<*>>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val viewHolderProvider = ViewHolderProvider()
init {
viewHolderProvider.registerViewHolderFactory(1, R.layout.nav_bs_item_primary) { itemView ->
BottomSheetPrimaryItem.ViewHolder(itemView)
}
viewHolderProvider.registerViewHolderFactory(2, R.layout.nav_bs_item_separator) { itemView ->
BottomSheetSeparatorItem.ViewHolder(itemView)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return viewHolderProvider.provideViewHolder(viewGroup = parent, viewType = viewType)
}
override fun getItemViewType(position: Int): Int {
return items[position].viewType
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
items[position].bindViewHolder(viewHolder = holder)
}
}

View File

@ -0,0 +1,438 @@
package pl.szczodrzynski.navlib.bottomsheet
import android.app.Activity
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.inputmethod.InputMethodManager
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.NestedScrollView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.button.MaterialButton
import com.google.android.material.button.MaterialButtonToggleGroup
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.typeface.library.navlibfont.NavLibFont
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.navlib.*
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import pl.szczodrzynski.navlib.bottomsheet.items.IBottomSheetItem
class NavBottomSheet : CoordinatorLayout {
companion object {
const val TOGGLE_GROUP_SINGLE_SELECTION = 0
const val TOGGLE_GROUP_MULTIPLE_SELECTION = 1
const val TOGGLE_GROUP_SORTING_ORDER = 2
const val SORT_MODE_ASCENDING = 0
const val SORT_MODE_DESCENDING = 1
}
constructor(context: Context) : super(context) {
create(null, 0)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
create(attrs, 0)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
create(attrs, defStyle)
}
private lateinit var scrimView: View
private lateinit var bottomSheet: NestedScrollView
private lateinit var content: LinearLayout
private lateinit var dragBar: View
private lateinit var textInputLayout: TextInputLayout
private lateinit var textInputEditText: TextInputEditText
private lateinit var toggleGroupContainer: LinearLayout
private lateinit var toggleGroup: MaterialButtonToggleGroup
private lateinit var toggleGroupTitleView: TextView
private lateinit var list: RecyclerView
private lateinit var bottomSheetBehavior: BottomSheetBehavior<View>
private var bottomSheetVisible = false
private val items = ArrayList<IBottomSheetItem<*>>()
private val adapter = BottomSheetAdapter(items)
/**
* Enable the bottom sheet.
* This value is mostly relevant to the [pl.szczodrzynski.navlib.NavBottomBar].
*/
var enable = true
set(value) {
field = value
if (!value && bottomSheetVisible)
close()
}
/**
* Whether the [pl.szczodrzynski.navlib.NavBottomBar] should open this BottomSheet
* when the user drags the bottom bar.
*/
var enableDragToOpen = true
/**
* Control the scrim view visibility, shown when BottomSheet
* is expanded.
*/
var scrimViewEnabled = true
set(value) {
scrimView.visibility = if (value) View.INVISIBLE else View.GONE // INVISIBLE
field = value
}
/**
* Whether tapping the Scrim view should hide the BottomSheet.
*/
var scrimViewTapToClose = true
fun hideKeyboard() {
val imm = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(rootView.windowToken, 0)
}
private fun create(attrs: AttributeSet?, defStyle: Int) {
val layoutInflater = LayoutInflater.from(context)
layoutInflater.inflate(R.layout.nav_bottom_sheet, this)
scrimView = findViewById(R.id.bs_scrim)
bottomSheet = findViewById(R.id.bs_view)
content = findViewById(R.id.bs_content)
dragBar = findViewById(R.id.bs_dragBar)
textInputLayout = findViewById(R.id.bs_textInputLayout)
textInputEditText = findViewById(R.id.bs_textInputEditText)
toggleGroupContainer = findViewById(R.id.bs_toggleGroupContainer)
toggleGroup = findViewById(R.id.bs_toggleGroup)
toggleGroupTitleView = findViewById(R.id.bs_toggleGroupTitle)
list = findViewById(R.id.bs_list)
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
scrimView.setOnTouchListener { _, event ->
if (!scrimViewTapToClose)
return@setOnTouchListener true
if (event.action == MotionEvent.ACTION_UP && bottomSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN) {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
}
true
}
bottomSheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(v: View, p1: Float) {}
override fun onStateChanged(v: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN && bottomSheetVisible) {
bottomSheetVisible = false
bottomSheet.scrollTo(0, 0)
if (scrimViewEnabled)
Anim.fadeOut(scrimView, 300, null)
// steal the focus from any EditTexts
dragBar.requestFocus()
hideKeyboard()
onCloseListener?.invoke()
}
else if (!bottomSheetVisible) {
bottomSheetVisible = true
if (scrimViewEnabled)
Anim.fadeIn(scrimView, 300, null)
}
}
})
content.background.colorFilter = PorterDuffColorFilter(
elevateSurface(context, dp = 8),
PorterDuff.Mode.SRC_ATOP
)
// steal the focus from any EditTexts
dragBar.requestFocus()
list.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
adapter = this@NavBottomSheet.adapter
}
toggleGroup.addOnButtonCheckedListener(toggleGroupCheckedListener)
textInputEditText.addTextChangedListener(textInputWatcher)
}
var onCloseListener: (() -> Unit)? = null
/* _____ _
|_ _| |
| | | |_ ___ _ __ ___ ___
| | | __/ _ \ '_ ` _ \/ __|
_| |_| || __/ | | | | \__ \
|_____|\__\___|_| |_| |_|__*/
operator fun plusAssign(item: IBottomSheetItem<*>) {
appendItem(item)
}
fun appendItem(item: IBottomSheetItem<*>) {
items.add(item)
adapter.notifyDataSetChanged()
//adapter.notifyItemInserted(items.size - 1)
}
fun appendItems(vararg items: IBottomSheetItem<*>) {
this.items.addAll(items)
adapter.notifyDataSetChanged()
//adapter.notifyItemRangeInserted(this.items.size - items.size, items.size)
}
fun prependItem(item: IBottomSheetItem<*>) {
items.add(0, item)
adapter.notifyDataSetChanged()
//adapter.notifyItemInserted(0)
}
fun prependItems(vararg items: IBottomSheetItem<*>) {
this.items.addAll(0, items.toList())
adapter.notifyDataSetChanged()
//adapter.notifyItemRangeInserted(0, items.size)
}
fun addItemAt(index: Int, item: IBottomSheetItem<*>) {
items.add(index, item)
adapter.notifyDataSetChanged()
//adapter.notifyItemInserted(index)
}
fun removeItemById(id: Int) {
items.filterNot { it.id == id }
}
fun removeItemAt(index: Int) {
items.removeAt(index)
adapter.notifyDataSetChanged()
//adapter.notifyItemRemoved(index)
}
fun removeAllItems() {
items.clear()
adapter.notifyDataSetChanged()
}
fun removeAllStatic() {
items.removeAll { !it.isContextual }
adapter.notifyDataSetChanged()
}
fun removeAllContextual() {
items.removeAll { it.isContextual }
adapter.notifyDataSetChanged()
}
fun removeSeparators() {
items.removeAll { it is BottomSheetSeparatorItem }
adapter.notifyDataSetChanged()
}
fun getItemById(id: Int, run: (it: IBottomSheetItem<*>?) -> Unit) {
items.singleOrNull { it.id == id }.also {
run(it)
if (it != null)
adapter.notifyItemChanged(items.indexOf(it))
}
}
fun getItemByIndex(index: Int, run: (it: IBottomSheetItem<*>?) -> Unit) {
items.getOrNull(index).also {
run(it)
if (it != null)
adapter.notifyItemChanged(index)
}
}
/* _______ _
|__ __| | |
| | ___ __ _ __ _| | ___ __ _ _ __ ___ _ _ _ __
| |/ _ \ / _` |/ _` | |/ _ \ / _` | '__/ _ \| | | | '_ \
| | (_) | (_| | (_| | | __/ | (_| | | | (_) | |_| | |_) |
|_|\___/ \__, |\__, |_|\___| \__, |_| \___/ \__,_| .__/
__/ | __/ | __/ | | |
|___/ |___/ |___/ |*/
var toggleGroupEnabled
get() = toggleGroupContainer.visibility == View.VISIBLE
set(value) { toggleGroupContainer.visibility = if (value) View.VISIBLE else View.GONE }
var toggleGroupTitle
get() = toggleGroupTitleView.text.toString()
set(value) { toggleGroupTitleView.text = value }
var toggleGroupSelectionMode: Int = TOGGLE_GROUP_SORTING_ORDER
set(value) {
field = value
toggleGroup.isSingleSelection = value != TOGGLE_GROUP_MULTIPLE_SELECTION
}
private fun toggleGroupGetIconicsDrawable(context: Context, icon: IIcon?): Drawable? {
if (icon == null)
return null
return IconicsDrawable(context, icon).apply {
sizeDp = 24
}
}
fun toggleGroupAddItem(id: Int, text: String, @DrawableRes icon: Int, defaultSortOrder: Int = SORT_MODE_ASCENDING) {
toggleGroupAddItem(id, text, context.getDrawableFromRes(icon), defaultSortOrder)
}
fun toggleGroupAddItem(id: Int, text: String, icon: IIcon, defaultSortOrder: Int = SORT_MODE_ASCENDING) {
toggleGroupAddItem(id, text, toggleGroupGetIconicsDrawable(context, icon), defaultSortOrder)
}
fun toggleGroupAddItem(id: Int, text: String, icon: Drawable?, defaultSortOrder: Int = SORT_MODE_ASCENDING) {
if (id < 0)
throw IllegalArgumentException("ID cannot be less than 0")
toggleGroup.addView(MaterialButton(context, null, R.attr.materialButtonOutlinedStyle).apply {
this.id = id + 1
this.tag = defaultSortOrder
this.text = text
this.icon = icon
}, WRAP_CONTENT, WRAP_CONTENT)
}
fun toggleGroupCheck(id: Int) {
toggleGroup.check(id)
}
fun toggleGroupRemoveItems() {
toggleGroup.removeAllViews()
}
private val toggleGroupCheckedListener = MaterialButtonToggleGroup.OnButtonCheckedListener { group, checkedId, isChecked ->
if (group.checkedButtonId == View.NO_ID) {
group.check(checkedId)
return@OnButtonCheckedListener
}
/* TAG bit order
* bit 0 = default sorting mode
* bit 1 = is checked
* bit 2 = current sorting mode
*/
if (toggleGroupSelectionMode == TOGGLE_GROUP_SORTING_ORDER) {
val button = group.findViewById<MaterialButton>(checkedId) ?: return@OnButtonCheckedListener
var tag = button.tag as Int
var sortingMode: Int? = null
if (isChecked) {
sortingMode = if (tag and 0b010 == 1 shl 1) {
/* the view is checked and clicked once again */
if (tag and 0b100 == SORT_MODE_ASCENDING shl 2) SORT_MODE_DESCENDING else SORT_MODE_ASCENDING
} else {
/* the view is first clicked so use the default sorting mode */
if (tag and 0b001 == SORT_MODE_ASCENDING) SORT_MODE_ASCENDING else SORT_MODE_DESCENDING
}
tag = tag and 0b001 /* retain only default sorting mode */
tag = tag or 0b010 /* set as checked */
tag = tag or (sortingMode shl 2) /* set new sorting mode */
}
else {
tag = tag and 0b001 /* retain only default sorting mode */
}
button.tag = tag
button.icon = toggleGroupGetIconicsDrawable(context, when (sortingMode) {
SORT_MODE_ASCENDING -> NavLibFont.Icon.nav_sort_ascending
SORT_MODE_DESCENDING -> NavLibFont.Icon.nav_sort_descending
else -> null
})
if (sortingMode != null) {
toggleGroupSortingOrderListener?.invoke(checkedId, sortingMode)
}
}
else if (toggleGroup.isSingleSelection && isChecked) {
toggleGroupSingleSelectionListener?.invoke(checkedId - 1)
}
else {
toggleGroupMultipleSelectionListener?.invoke(checkedId - 1, isChecked)
}
}
var toggleGroupSingleSelectionListener: ((id: Int) -> Unit)? = null
var toggleGroupMultipleSelectionListener: ((id: Int, checked: Boolean) -> Unit)? = null
var toggleGroupSortingOrderListener: ((id: Int, sortMode: Int) -> Unit)? = null
/* _______ _ _ _
|__ __| | | (_) | |
| | _____ _| |_ _ _ __ _ __ _ _| |_
| |/ _ \ \/ / __| | | '_ \| '_ \| | | | __|
| | __/> <| |_ | | | | | |_) | |_| | |_
|_|\___/_/\_\\__| |_|_| |_| .__/ \__,_|\__|
| |
|*/
var textInputEnabled
get() = textInputLayout.visibility == View.VISIBLE
set(value) { textInputLayout.visibility = if (value) View.VISIBLE else View.GONE }
var textInputText
get() = textInputEditText.text.toString()
set(value) { textInputEditText.setText(value) }
var textInputHint
get() = textInputLayout.hint.toString()
set(value) { textInputLayout.hint = value }
var textInputHelperText
get() = textInputLayout.helperText.toString()
set(value) { textInputLayout.helperText = value }
var textInputError
get() = textInputLayout.error
set(value) { textInputLayout.error = value }
var textInputIcon: Any?
get() = textInputLayout.startIconDrawable
set(value) {
textInputLayout.startIconDrawable = when (value) {
is Drawable -> value
is IIcon -> IconicsDrawable(context).apply {
icon = value
sizeDp = 24
// colorInt = Color.BLACK
}
is Int -> context.getDrawableFromRes(value)
else -> null
}
}
private var textInputWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textInputChangedListener?.onTextChanged(s?.toString() ?: "", start, before, count)
}
}
interface OnTextInputChangedListener {
fun onTextChanged(s: String, start: Int, before: Int, count: Int)
}
var textInputChangedListener: OnTextInputChangedListener? = null
fun dispatchBottomBarEvent(event: MotionEvent) {
val location = IntArray(2)
bottomSheet.getLocationOnScreen(location)
event.setLocation(event.rawX - location[0], event.rawY - location[1])
bottomSheet.dispatchTouchEvent(event)
}
fun setContentPadding(left: Int, top: Int, right: Int, bottom: Int) {
content.setPadding(left, top, right, bottom)
}
fun getContentView() = content
var isOpen
get() = bottomSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN
set(value) {
bottomSheetBehavior.state = if (value) BottomSheetBehavior.STATE_EXPANDED else BottomSheetBehavior.STATE_HIDDEN
}
fun open() { isOpen = true }
fun close() { isOpen = false }
fun toggle() {
if (!enable)
return
isOpen = !isOpen
}
}

View File

@ -0,0 +1,21 @@
package pl.szczodrzynski.navlib.bottomsheet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlin.reflect.KClass
class ViewHolderProvider {
private val viewHolderFactories = hashMapOf<Int, Pair<Int, (View) -> RecyclerView.ViewHolder>>()
fun provideViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val (layoutId: Int, f: (View) -> RecyclerView.ViewHolder) = viewHolderFactories[viewType]!!
val view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, viewGroup, false)
return f(view)
}
fun registerViewHolderFactory(viewType: Int, layoutId: Int, viewHolderFactory: (View) -> RecyclerView.ViewHolder) {
viewHolderFactories[viewType] = Pair(layoutId, viewHolderFactory)
}
}

View File

@ -0,0 +1,124 @@
package pl.szczodrzynski.navlib.bottomsheet.items
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.navlib.ImageHolder
import pl.szczodrzynski.navlib.R
import pl.szczodrzynski.navlib.colorAttr
import pl.szczodrzynski.navlib.getColorFromAttr
data class BottomSheetPrimaryItem(override val isContextual: Boolean = true) : IBottomSheetItem<BottomSheetPrimaryItem.ViewHolder> {
/*_ _
| | | |
| | __ _ _ _ ___ _ _| |_
| | / _` | | | |/ _ \| | | | __|
| |___| (_| | |_| | (_) | |_| | |_
|______\__,_|\__, |\___/ \__,_|\__|
__/ |
|__*/
override var id: Int = -1
override val viewType: Int
get() = 1
override val layoutId: Int
get() = R.layout.nav_bs_item_primary
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val root = itemView.findViewById<View>(R.id.item_root)
val image = itemView.findViewById<ImageView>(R.id.item_icon)
val text = itemView.findViewById<TextView>(R.id.item_text)
val description = itemView.findViewById<TextView>(R.id.item_description)
}
override fun bindViewHolder(viewHolder: ViewHolder) {
viewHolder.root.setOnClickListener(onClickListener)
viewHolder.image.setImageDrawable(IconicsDrawable(viewHolder.text.context).apply {
icon = iconicsIcon
colorAttr(viewHolder.text.context, android.R.attr.textColorSecondary)
sizeDp = 24
})
viewHolder.description.visibility = View.VISIBLE
when {
descriptionRes != null -> viewHolder.description.setText(descriptionRes!!)
description != null -> viewHolder.description.text = description
else -> viewHolder.description.visibility = View.GONE
}
when {
titleRes != null -> viewHolder.text.setText(titleRes!!)
else -> viewHolder.text.text = title
}
viewHolder.text.setTextColor(getColorFromAttr(viewHolder.text.context, android.R.attr.textColorPrimary))
}
/*_____ _
| __ \ | |
| | | | __ _| |_ __ _
| | | |/ _` | __/ _` |
| |__| | (_| | || (_| |
|_____/ \__,_|\__\__,*/
var title: CharSequence? = null
@StringRes
var titleRes: Int? = null
var description: CharSequence? = null
@StringRes
var descriptionRes: Int? = null
var icon: ImageHolder? = null
var iconicsIcon: IIcon? = null
var onClickListener: View.OnClickListener? = null
fun withId(id: Int): BottomSheetPrimaryItem {
this.id = id
return this
}
fun withTitle(title: CharSequence): BottomSheetPrimaryItem {
this.title = title
this.titleRes = null
return this
}
fun withTitle(@StringRes title: Int): BottomSheetPrimaryItem {
this.title = null
this.titleRes = title
return this
}
fun withDescription(description: CharSequence): BottomSheetPrimaryItem {
this.description = description
this.descriptionRes = null
return this
}
fun withDescription(@StringRes description: Int): BottomSheetPrimaryItem {
this.description = null
this.descriptionRes = description
return this
}
fun withIcon(icon: Drawable): BottomSheetPrimaryItem {
this.icon = ImageHolder(icon)
return this
}
fun withIcon(@DrawableRes icon: Int): BottomSheetPrimaryItem {
this.icon = ImageHolder(icon)
return this
}
fun withIcon(icon: IIcon): BottomSheetPrimaryItem {
this.iconicsIcon = icon
return this
}
fun withOnClickListener(onClickListener: View.OnClickListener): BottomSheetPrimaryItem {
this.onClickListener = onClickListener
return this
}
}

View File

@ -0,0 +1,28 @@
package pl.szczodrzynski.navlib.bottomsheet.items
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.navlib.R
data class BottomSheetSeparatorItem(override val isContextual: Boolean = true) : IBottomSheetItem<BottomSheetSeparatorItem.ViewHolder> {
/*_ _
| | | |
| | __ _ _ _ ___ _ _| |_
| | / _` | | | |/ _ \| | | | __|
| |___| (_| | |_| | (_) | |_| | |_
|______\__,_|\__, |\___/ \__,_|\__|
__/ |
|__*/
override var id: Int = -1
override val viewType: Int
get() = 2
override val layoutId: Int
get() = R.layout.nav_bs_item_separator
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun bindViewHolder(viewHolder: ViewHolder) {
}
}

View File

@ -0,0 +1,19 @@
package pl.szczodrzynski.navlib.bottomsheet.items
import android.view.View
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import kotlin.reflect.KClass
interface IBottomSheetItem<T> {
val isContextual: Boolean
var id: Int
val viewType: Int
val layoutId: Int
fun bindViewHolder(viewHolder: T)
fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) {
bindViewHolder(viewHolder as T)
}
}

View File

@ -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)
}

View File

@ -0,0 +1,8 @@
package pl.szczodrzynski.navlib.drawer
interface IUnreadCounter {
var profileId: Int
var type: Int
var drawerItemId: Int?
var count: Int
}

View File

@ -0,0 +1,730 @@
package pl.szczodrzynski.navlib.drawer
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.drawable.LayerDrawable
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.customview.widget.ViewDragHelper
import androidx.drawerlayout.widget.DrawerLayout
import com.mikepenz.fastadapter.IAdapter
import com.mikepenz.itemanimators.AlphaCrossFadeAnimator
import com.mikepenz.materialdrawer.*
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.BaseDrawerItem
import com.mikepenz.materialdrawer.model.MiniProfileDrawerItem
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.*
import com.mikepenz.materialdrawer.widget.AccountHeaderView
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
import com.mikepenz.materialdrawer.widget.MiniDrawerSliderView
import com.mikepenz.materialize.util.UIUtils
import pl.szczodrzynski.navlib.*
import pl.szczodrzynski.navlib.R
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
class NavDrawer(
val context: Context,
val drawerLayout: DrawerLayout,
val drawerContainerLandscape: FrameLayout,
val drawerContainerPortrait: FrameLayout,
val miniDrawerElevation: View
) {
companion object {
private const val DRAWER_MODE_NORMAL = 0
private const val DRAWER_MODE_MINI = 1
private const val DRAWER_MODE_FIXED = 2
}
private lateinit var activity: Activity
private val resources: Resources
get() = context.resources
internal lateinit var toolbar: NavToolbar
internal lateinit var bottomBar: NavBottomBar
private lateinit var drawer: MaterialDrawerSliderView
private lateinit var accountHeader: AccountHeaderView
private lateinit var miniDrawer: MiniDrawerSliderView
private var drawerMode: Int = DRAWER_MODE_NORMAL
private var selection: Int = -1
lateinit var badgeStyle: BadgeStyle
@SuppressLint("ClickableViewAccessibility")
fun init(activity: Activity) {
this.activity = activity
/*badgeStyle = BadgeStyle(
R.drawable.material_drawer_badge,
getColorFromAttr(context, R.attr.colorError),
getColorFromAttr(context, R.attr.colorError),
getColorFromAttr(context, R.attr.colorOnError)
)*/
badgeStyle = BadgeStyle().apply {
textColor = ColorHolder.fromColor(Color.WHITE)
color = ColorHolder.fromColor(0xffd32f2f.toInt())
}
drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
override fun onDrawerStateChanged(newState: Int) {}
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {}
override fun onDrawerClosed(drawerView: View) {
drawerClosedListener?.invoke()
profileSelectionClose()
}
override fun onDrawerOpened(drawerView: View) {
drawerOpenedListener?.invoke()
}
})
accountHeader = AccountHeaderView(context).apply {
headerBackground = ImageHolder(R.drawable.header)
displayBadgesOnSmallProfileImages = true
onAccountHeaderListener = { view, profile, current ->
if (profile is ProfileSettingDrawerItem) {
drawerProfileSettingClickListener?.invoke(profile.identifier.toInt(), view) ?: false
}
else {
updateBadges()
if (current) {
close()
profileSelectionClose()
true
}
else {
(drawerProfileSelectedListener?.invoke(profile.identifier.toInt(), profile, current, view) ?: false).also {
setToolbarProfileImage(profileList.singleOrNull { it.id == profile.identifier.toInt() })
}
}
}
}
onAccountHeaderItemLongClickListener = { view, profile, current ->
if (profile is ProfileSettingDrawerItem) {
drawerProfileSettingLongClickListener?.invoke(profile.identifier.toInt(), view) ?: true
}
else {
drawerProfileLongClickListener?.invoke(profile.identifier.toInt(), profile, current, view) ?: false
}
}
onAccountHeaderProfileImageListener = { view, profile, current ->
drawerProfileImageClickListener?.invoke(profile.identifier.toInt(), profile, current, view) ?: false
}
//.withTextColor(ContextCompat.getColor(context, R.color.material_drawer_dark_primary_text))
}
drawer = MaterialDrawerSliderView(context).apply {
accountHeader = this@NavDrawer.accountHeader
itemAnimator = AlphaCrossFadeAnimator()
//hasStableIds = true
onDrawerItemClickListener = { _, drawerItem, position ->
if (drawerItem.identifier.toInt() == selection) {
false
}
else {
val consumed = drawerItemSelectedListener?.invoke(drawerItem.identifier.toInt(), position, drawerItem)
if (consumed == false || !drawerItem.isSelectable) {
setSelection(selection, false)
consumed == false
}
else if (consumed == true) {
when (drawerItem) {
is DrawerPrimaryItem -> toolbar.title = drawerItem.appTitle ?: drawerItem.name?.getText(context) ?: ""
is BaseDrawerItem<*, *> -> toolbar.title = drawerItem.name?.getText(context) ?: ""
}
false
}
else {
false
}
}
}
onDrawerItemLongClickListener = { _, drawerItem, position ->
drawerItemLongClickListener?.invoke(drawerItem.identifier.toInt(), position, drawerItem) ?: true
}
}
miniDrawer = MiniDrawerSliderView(context).apply {
drawer = this@NavDrawer.drawer
includeSecondaryDrawerItems = false
try {
this::class.java.getDeclaredField("onMiniDrawerItemClickListener").let {
it.isAccessible = true
it.set(this, { v: View?, position: Int, item: IDrawerItem<*>, type: Int ->
if (item is MiniProfileDrawerItem) {
profileSelectionOpen()
open()
true
} else false
})
}
} catch (_: Exception) { }
}
updateMiniDrawer()
toolbar.profileImageClickListener = {
profileSelectionOpen()
open()
}
val configuration = context.resources.configuration
decideDrawerMode(
configuration.orientation,
configuration.screenWidthDp,
configuration.screenHeightDp
)
}
/* _____ _
|_ _| |
| | | |_ ___ _ __ ___ ___
| | | __/ _ \ '_ ` _ \/ __|
_| |_| || __/ | | | | \__ \
|_____|\__\___|_| |_| |_|__*/
operator fun plusAssign(item: IDrawerItem<*>) {
appendItem(item)
}
fun appendItem(item: IDrawerItem<*>) {
drawer.addItems(item)
updateMiniDrawer()
}
fun appendItems(vararg items: IDrawerItem<*>) {
drawer.addItems(*items)
updateMiniDrawer()
}
fun prependItem(item: IDrawerItem<*>) {
drawer.addItemAtPosition(0, item)
updateMiniDrawer()
}
fun prependItems(vararg items: IDrawerItem<*>) {
drawer.addItemsAtPosition(0, *items)
updateMiniDrawer()
}
fun addItemAt(index: Int, item: IDrawerItem<*>) {
drawer.addItemAtPosition(index, item)
updateMiniDrawer()
}
fun addItemsAt(index: Int, vararg items: IDrawerItem<*>) {
drawer.addItemsAtPosition(index, *items)
updateMiniDrawer()
}
fun removeItemById(id: Int) {
drawer.removeItems(id.toLong())
updateMiniDrawer()
}
fun removeItemAt(index: Int) {
drawer.removeItemByPosition(index)
updateMiniDrawer()
}
fun removeAllItems() {
drawer.removeAllItems()
updateMiniDrawer()
}
fun getItemById(id: Int, run: (it: IDrawerItem<*>?) -> Unit) {
drawer.getDrawerItem(id.toLong()).also {
run(it)
if (it != null)
drawer.updateItem(it)
updateMiniDrawer()
}
}
fun getItemByIndex(index: Int, run: (it: IDrawerItem<*>?) -> Unit) {
drawer.itemAdapter.itemList.get(index).also {
run(it)
if (it != null)
drawer.updateItem(it)
updateMiniDrawer()
}
}
fun setItems(vararg items: IDrawerItem<*>) {
drawer.removeAllItems()
drawer.addItems(*items)
updateMiniDrawer()
}
/* _____ _ _ _ _ _
| __ \ (_) | | | | | | | |
| |__) | __ ___ ____ _| |_ ___ _ __ ___ ___| |_| |__ ___ __| |___
| ___/ '__| \ \ / / _` | __/ _ \ | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
| | | | | |\ V / (_| | || __/ | | | | | | __/ |_| | | | (_) | (_| \__ \
|_| |_| |_| \_/ \__,_|\__\___| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|__*/
private fun drawerSetDragMargin(size: Float) {
try {
val mDrawerLayout = drawerLayout
val mDragger = mDrawerLayout::class.java.getDeclaredField(
"mLeftDragger"
)
mDragger.isAccessible = true
val draggerObj = mDragger.get(mDrawerLayout) as ViewDragHelper?
draggerObj?.edgeSize = size.toInt()
// update for SDK >= 29 (Android 10)
val useSystemInsets = mDrawerLayout::class.java.getDeclaredField(
"sEdgeSizeUsingSystemGestureInsets"
)
useSystemInsets.isAccessible = true
useSystemInsets.set(null, false)
}
catch (e: Exception) {
e.printStackTrace()
Toast.makeText(context, "Oops, proguard works", Toast.LENGTH_SHORT).show()
}
}
var miniDrawerVisiblePortrait: Boolean? = null
set(value) {
field = value
val configuration = context.resources.configuration
decideDrawerMode(
configuration.orientation,
configuration.screenWidthDp,
configuration.screenHeightDp
)
}
var miniDrawerVisibleLandscape: Boolean? = null
set(value) {
field = value
val configuration = context.resources.configuration
decideDrawerMode(
configuration.orientation,
configuration.screenWidthDp,
configuration.screenHeightDp
)
}
internal fun decideDrawerMode(orientation: Int, widthDp: Int, heightDp: Int) {
val drawerLayoutParams = DrawerLayout.LayoutParams(WRAP_CONTENT, MATCH_PARENT).apply {
gravity = Gravity.START
}
val fixedLayoutParams = FrameLayout.LayoutParams(UIUtils.convertDpToPixel(300f, context).toInt(), MATCH_PARENT)
Log.d("NavLib", "Deciding drawer mode:")
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
Log.d("NavLib", "- fixed container disabled")
if (drawerContainerLandscape.childCount > 0) {
drawerContainerLandscape.removeAllViews()
}
Log.d("NavLib", "- mini drawer land disabled")
if (drawerLayout.indexOfChild(drawer) == -1) {
drawerLayout.addView(drawer, drawerLayoutParams)
}
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
Log.d("NavLib", "- slider enabled")
if ((widthDp >= 480 && miniDrawerVisiblePortrait != false) || miniDrawerVisiblePortrait == true) {
if (drawerContainerPortrait.indexOfChild(miniDrawer) == -1)
drawerContainerPortrait.addView(miniDrawer)
Log.d("NavLib", "- mini drawer port enabled")
drawerSetDragMargin(72 * resources.displayMetrics.density)
drawerMode = DRAWER_MODE_MINI
updateMiniDrawer()
}
else {
if (drawerContainerPortrait.childCount > 0) {
drawerContainerPortrait.removeAllViews()
}
Log.d("NavLib", "- mini drawer port disabled")
drawerSetDragMargin(20 * resources.displayMetrics.density)
drawerMode = DRAWER_MODE_NORMAL
}
}
else {
if (drawerContainerPortrait.childCount > 0) {
drawerContainerPortrait.removeAllViews()
}
Log.d("NavLib", "- mini drawer port disabled")
if ((widthDp in 480 until 900 && miniDrawerVisibleLandscape != false) || miniDrawerVisibleLandscape == true) {
if (drawerContainerLandscape.indexOfChild(miniDrawer) == -1)
drawerContainerLandscape.addView(miniDrawer)
Log.d("NavLib", "- mini drawer land enabled")
drawerSetDragMargin(72 * resources.displayMetrics.density)
drawerMode = DRAWER_MODE_MINI
updateMiniDrawer()
}
else {
if (drawerContainerLandscape.childCount > 0) {
drawerContainerLandscape.removeAllViews()
}
Log.d("NavLib", "- mini drawer land disabled")
drawerSetDragMargin(20 * resources.displayMetrics.density)
drawerMode = DRAWER_MODE_NORMAL
}
if (widthDp >= 900) {
// screen is big enough to show fixed drawer
if (drawerLayout.indexOfChild(drawer) != -1) {
// remove from slider
drawerLayout.removeView(drawer)
}
// lock the slider
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
Log.d("NavLib", "- slider disabled")
// add to fixed container
if (drawerContainerLandscape.indexOfChild(drawer) == -1)
drawerContainerLandscape.addView(drawer, fixedLayoutParams)
drawer.visibility = View.VISIBLE
Log.d("NavLib", "- fixed container enabled")
drawerMode = DRAWER_MODE_FIXED
}
else {
// screen is too small for the fixed drawer
if (drawerContainerLandscape.indexOfChild(drawer) != -1) {
// remove from fixed container
drawerContainerLandscape.removeView(drawer)
}
Log.d("NavLib", "- fixed container disabled")
// unlock the slider
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
if (drawerLayout.indexOfChild(drawer) == -1) {
// add to slider
drawerLayout.addView(drawer, drawerLayoutParams)
}
Log.d("NavLib", "- slider enabled")
}
}
miniDrawerElevation.visibility = if (drawerMode == DRAWER_MODE_MINI || drawerMode == DRAWER_MODE_FIXED) View.VISIBLE else View.GONE
}
private fun updateMiniDrawer() {
selection = drawer.selectedItemIdentifier.toInt()
//if (drawerMode == DRAWER_MODE_MINI)
miniDrawer.createItems()
}
/* _____ _ _ _ _ _ _
| __ \ | | | (_) | | | | | |
| |__) | _| |__ | |_ ___ _ __ ___ ___| |_| |__ ___ __| |___
| ___/ | | | '_ \| | |/ __| | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
| | | |_| | |_) | | | (__ | | | | | | __/ |_| | | | (_) | (_| \__ \
|_| \__,_|_.__/|_|_|\___| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|__*/
var isOpen
get() = drawerLayout.isOpen || drawerMode == DRAWER_MODE_FIXED
set(value) {
if (drawerMode == DRAWER_MODE_FIXED)
return
if (value && !isOpen) drawerLayout.open() else if (!value && isOpen) drawerLayout.close()
}
fun open() { isOpen = true }
fun close() { isOpen = false }
fun toggle() { isOpen = !isOpen }
var profileSelectionIsOpen
get() = accountHeader.selectionListShown
set(value) {
if (value != profileSelectionIsOpen)
profileSelectionToggle()
}
fun profileSelectionOpen() { profileSelectionIsOpen = true }
fun profileSelectionClose() { profileSelectionIsOpen = false }
fun profileSelectionToggle() { accountHeader.selectionListShown = !accountHeader.selectionListShown }
var drawerOpenedListener: (() -> Unit)? = null
var drawerClosedListener: (() -> Unit)? = null
var drawerItemSelectedListener: ((id: Int, position: Int, drawerItem: IDrawerItem<*>) -> Boolean)? = null
var drawerItemLongClickListener: ((id: Int, position: Int, drawerItem: IDrawerItem<*>) -> Boolean)? = null
var drawerProfileSelectedListener: ((id: Int, profile: IProfile, current: Boolean, view: View?) -> Boolean)? = null
var drawerProfileLongClickListener: ((id: Int, profile: IProfile, current: Boolean, view: View?) -> Boolean)? = null
var drawerProfileImageClickListener: ((id: Int, profile: IProfile, current: Boolean, view: View) -> Boolean)? = null
var drawerProfileImageLongClickListener: ((id: Int, profile: IProfile, current: Boolean, view: View) -> Boolean)? = null
var drawerProfileListEmptyListener: (() -> Unit)? = null
var drawerProfileSettingClickListener: ((id: Int, view: View?) -> Boolean)? = null
var drawerProfileSettingLongClickListener: ((id: Int, view: View?) -> Boolean)? = null
fun miniDrawerEnabled(): Boolean = drawerMode == DRAWER_MODE_MINI
fun fixedDrawerEnabled(): Boolean = drawerMode == DRAWER_MODE_FIXED
fun setSelection(id: Int, fireOnClick: Boolean = true) {
Log.d("NavDebug", "setSelection(id = $id, fireOnClick = $fireOnClick)")
// seems that this cannot be open, because the itemAdapter has Profile items
// instead of normal Drawer items...
profileSelectionClose()
selection = id
if (drawer.selectedItemIdentifier != id.toLong()) {
}
if (drawer.selectedItemIdentifier != id.toLong() || !fireOnClick)
drawer.setSelectionAtPosition(drawer.getPosition(id.toLong()), fireOnClick)
miniDrawer.setSelection(-1L)
if (drawerMode == DRAWER_MODE_MINI)
miniDrawer.setSelection(id.toLong())
}
fun getSelection(): Int = selection
// TODO 2019-08-27 add methods for Drawable, @DrawableRes
fun setAccountHeaderBackground(path: String?) {
if (path == null) {
accountHeader.headerBackground = ImageHolder(R.drawable.header)
return
}
accountHeader.headerBackground = ImageHolder(path)
}
/* _____ __ _ _
| __ \ / _(_) |
| |__) | __ ___ | |_ _| | ___ ___
| ___/ '__/ _ \| _| | |/ _ \/ __|
| | | | | (_) | | | | | __/\__ \
|_| |_| \___/|_| |_|_|\___||__*/
private var profileList: MutableList<IDrawerProfile> = mutableListOf()
fun addProfileSettings(vararg items: ProfileSettingDrawerItem) {
accountHeader.profiles?.addAll(items)
}
private fun updateProfileList() {
// remove all profile items
val profiles = accountHeader.profiles?.filterNot { it is ProfileDrawerItem } as MutableList<IProfile>?
if (profileList.isEmpty())
drawerProfileListEmptyListener?.invoke()
profileList.forEachIndexed { index, profile ->
val image = profile.getImageHolder(context)
ProfileDrawerItem()
.withIdentifier(profile.id.toLong())
.withName(profile.name)
.withEmail(profile.subname)
.also { it.icon = image }
.withBadgeStyle(badgeStyle)
.withNameShown(true)
.also { profiles?.add(index, it) }
}
accountHeader.profiles = profiles
updateBadges()
updateMiniDrawer()
}
fun setProfileList(profiles: MutableList<out IDrawerProfile>) {
profileList = profiles as MutableList<IDrawerProfile>
updateProfileList()
}
private var currentProfileObj: IDrawerProfile? = null
val profileListEmpty: Boolean
get() = profileList.isEmpty()
var currentProfile: Int
get() = accountHeader.activeProfile?.identifier?.toInt() ?: -1
set(value) {
Log.d("NavDebug", "currentProfile = $value")
accountHeader.setActiveProfile(value.toLong(), false)
currentProfileObj = profileList.singleOrNull { it.id == value }
setToolbarProfileImage(currentProfileObj)
updateBadges()
}
fun appendProfile(profile: IDrawerProfile) {
profileList.add(profile)
updateProfileList()
}
fun appendProfiles(vararg profiles: IDrawerProfile) {
profileList.addAll(profiles)
updateProfileList()
}
fun prependProfile(profile: IDrawerProfile) {
profileList.add(0, profile)
updateProfileList()
}
fun prependProfiles(vararg profiles: IDrawerProfile) {
profileList.addAll(0, profiles.asList())
updateProfileList()
}
fun addProfileAt(index: Int, profile: IDrawerProfile) {
profileList.add(index, profile)
updateProfileList()
}
fun addProfilesAt(index: Int, vararg profiles: IDrawerProfile) {
profileList.addAll(index, profiles.asList())
updateProfileList()
}
fun removeProfileById(id: Int) {
profileList = profileList.filterNot { it.id == id }.toMutableList()
updateProfileList()
}
fun removeProfileAt(index: Int) {
profileList.removeAt(index)
updateProfileList()
}
fun removeAllProfile() {
profileList.clear()
updateProfileList()
}
fun removeAllProfileSettings() {
accountHeader.profiles = accountHeader.profiles?.filterNot { it is ProfileSettingDrawerItem }?.toMutableList()
}
fun getProfileById(id: Int, run: (it: IDrawerProfile?) -> Unit) {
profileList.singleOrNull { it.id == id }.also {
run(it)
updateProfileList()
}
}
fun getProfileByIndex(index: Int, run: (it: IDrawerProfile?) -> Unit) {
profileList.getOrNull(index).also {
run(it)
updateProfileList()
}
}
private fun setToolbarProfileImage(profile: IDrawerProfile?) {
toolbar.profileImage = profile?.getImageDrawable(context)
}
/* ____ _
| _ \ | |
| |_) | __ _ __| | __ _ ___ ___
| _ < / _` |/ _` |/ _` |/ _ \/ __|
| |_) | (_| | (_| | (_| | __/\__ \
|____/ \__,_|\__,_|\__, |\___||___/
__/ |
|__*/
private var unreadCounterList: MutableList<IUnreadCounter> = mutableListOf()
private val unreadCounterTypeMap = mutableMapOf<Int, Int>()
fun updateBadges() {
currentProfileObj = profileList.singleOrNull { it.id == currentProfile }
drawer.itemAdapter.itemList.items.forEachIndexed { index, item ->
if (item is Badgeable) {
item.badge = null
drawer.updateItem(item)
}
}
var profileCounters = listOf<IUnreadCounter>()
accountHeader.profiles?.forEach { profile ->
if (profile !is ProfileDrawerItem) return@forEach
val counters = unreadCounterList.filter { it.profileId == profile.identifier.toInt() }
val count = counters.sumBy { it.count }
val badge = when {
count == 0 -> null
count >= 99 -> StringHolder("99+")
else -> StringHolder(count.toString())
}
if (profile.badge != badge) {
profile.badge = badge
accountHeader.updateProfile(profile)
}
if (currentProfile == profile.identifier.toInt())
profileCounters = counters
}
Log.d("NavDebug", "updateBadges()")
profileCounters.map {
it.drawerItemId = unreadCounterTypeMap[it.type]
}
var totalCount = 0
profileCounters.forEach {
if (it.drawerItemId == null)
return@forEach
if (it.profileId != currentProfile) {
//Log.d("NavDebug", "- Remove badge for ${it.drawerItemId}")
//drawer?.updateBadge(it.drawerItemId?.toLong() ?: 0, null)
return@forEach
}
Log.d("NavDebug", "- Set badge ${it.count} for ${it.drawerItemId}")
drawer.updateBadge(
it.drawerItemId?.toLong() ?: 0,
when {
it.count == 0 -> null
it.count >= 99 -> StringHolder("99+")
else -> StringHolder(it.count.toString())
}
)
totalCount += it.count
}
updateMiniDrawer()
if (bottomBar.navigationIcon is LayerDrawable) {
(bottomBar.navigationIcon as LayerDrawable?)?.apply {
findDrawableByLayerId(R.id.ic_badge)
.takeIf { it is BadgeDrawable }
?.also { badge ->
(badge as BadgeDrawable).setCount(totalCount.toString())
mutate()
setDrawableByLayerId(R.id.ic_badge, badge)
}
}
}
if (totalCount == 0) {
toolbar.subtitle = resources.getString(
toolbar.subtitleFormat ?: return,
currentProfileObj?.name ?: ""
)
}
else {
toolbar.subtitle = resources.getQuantityString(
toolbar.subtitleFormatWithUnread ?: toolbar.subtitleFormat ?: return,
totalCount,
currentProfileObj?.name ?: "",
totalCount
)
}
}
fun setUnreadCounterList(unreadCounterList: MutableList<out IUnreadCounter>) {
this.unreadCounterList = unreadCounterList as MutableList<IUnreadCounter>
updateBadges()
}
fun addUnreadCounterType(type: Int, drawerItem: Int) {
unreadCounterTypeMap[type] = drawerItem
}
data class UnreadCounter(
override var profileId: Int,
override var type: Int,
override var drawerItemId: Int?,
override var count: Int
) : IUnreadCounter
fun setUnreadCount(profileId: Int, type: Int, count: Int) {
val item = unreadCounterList.singleOrNull {
it.type == type && it.profileId == profileId
}
if (item != null) {
item.count = count
}
else {
unreadCounterList.add(UnreadCounter(profileId, type, null, count))
}
updateBadges()
}
}

View File

@ -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
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.16" android:color="?attr/colorOnSurface" android:state_hovered="true"/>
<item android:alpha="0.04" android:color="?attr/colorOnSurface" android:state_enabled="false"/>
<item android:alpha="0.12" android:color="?attr/colorOnSurface"/>
</selector>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- increased alpha by 1/2 -->
<item android:alpha="0.24" android:color="?attr/colorOnSurface" android:state_hovered="true"/>
<item android:alpha="0.06" android:color="?attr/colorOnSurface" android:state_enabled="false"/>
<item android:alpha="0.18" android:color="?attr/colorOnSurface"/>
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple android:color="#202196f3"
android:radius="5dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@android:id/mask"
android:top="3dp"
android:bottom="3dp"
android:drawable="@color/color" />
</ripple>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:shape="rectangle">
<corners
android:topLeftRadius="16dp"
android:topRightRadius="16dp" />
<solid android:color="#ffffff" tools:color="?colorBackgroundFloating" />
</shape>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="2dp" />
<solid android:color="#DEDEDE" />
<size
android:width="32dp"
android:height="4dp" />
</shape>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This will be the drawable used on API < 21 -->
<!-- Obviously you can specify here whatever you want -->
<!-- like activated states, animated states, shapes, pngs, whatever like any other drawable-->
<!-- I've just put 2 states (pressed,focused) using the same color than the v21 ripple -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bs_item_background_base" android:state_focused="true"/>
<item android:drawable="@drawable/bs_item_background_base" android:state_pressed="true"/>
<item android:drawable="@android:color/transparent"/>
</selector>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="3dp"
android:bottom="3dp">
<shape>
<solid android:color="#202196f3"/>
<corners android:radius="5dp"/>
</shape>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71s-0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5L1,10.5v2h3v-2zM13,0.55h-2L11,3.5h2L13,0.55zM20.45,4.46l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zM17.24,18.16l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zM12,5.5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM11,22.45h2L13,19.5h-2v2.95zM3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z"/>
</vector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/ic_menu"
android:drawable="@drawable/placeholder"
android:gravity="center" />
<item
android:id="@+id/ic_badge"
android:drawable="@drawable/placeholder" />
</layer-list>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M9,2c-1.05,0 -2.05,0.16 -3,0.46 4.06,1.27 7,5.06 7,9.54 0,4.48 -2.94,8.27 -7,9.54 0.95,0.3 1.95,0.46 3,0.46 5.52,0 10,-4.48 10,-10S14.52,2 9,2z"/>
</vector>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
</shape>

View File

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="135.5"
android:viewportHeight="135.5">
<path
android:pathData="M0,0h135.5L135.5,135.5L0,135.5z"
android:fillColor="#00000000"/>
<path
android:pathData="M41.674,55.89a28.412,26.387 85.529,1 0,52.798 0.379a28.412,26.387 85.529,1 0,-52.798 -0.379z"
android:fillColor="#b0ffffff"/>
<path
android:pathData="M121,133.2a53.3,39 0,0 1,-50.9 38.9A53.3,39 0,0 1,14.8 136.5a53.3,39 0,0 1,46 -42,53.3 39,0 0,1 59.5,32"
android:fillColor="#b0ffffff"/>
</vector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="270"
android:endColor="#00000000"
android:startColor="#40000000"
android:type="linear" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="0"
android:endColor="#00000000"
android:startColor="#40000000"
android:type="linear" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/material_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false" />

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<com.mikepenz.materialize.view.ScrimInsetsRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/material_drawer_slider_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:layout_marginEnd="@dimen/material_drawer_margin"
android:layout_marginRight="@dimen/material_drawer_margin"
android:clickable="true"
android:elevation="8dp"
android:fitsSystemWindows="true"
android:focusable="true"
android:orientation="vertical">
<View
android:id="@+id/material_drawer_inner_shadow"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentStart="false"
android:layout_alignParentLeft="false"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:background="@drawable/material_drawer_shadow_left"
android:visibility="gone" />
</com.mikepenz.materialize.view.ScrimInsetsRelativeLayout>

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:translationZ="10dp"
tools:targetApi="lollipop">
<View
android:id="@+id/bs_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99000000"
android:visibility="invisible"
tools:visibility="gone" />
<androidx.core.widget.NestedScrollView
android:id="@+id/bs_view"
style="@style/width_max_600dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:behavior_hideable="true"
app:behavior_peekHeight="auto"
app:layout_behavior="@string/bottom_sheet_behavior"
tools:visibility="visible">
<LinearLayout
android:id="@+id/bs_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bottom_sheet_background"
android:paddingHorizontal="8dp"
tools:paddingBottom="48dp"
android:orientation="vertical">
<ImageView
android:id="@+id/bs_dragBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:focusable="true"
android:focusableInTouchMode="true"
app:srcCompat="@drawable/bottom_sheet_control_bar"
tools:ignore="ContentDescription" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/bs_textInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:boxBackgroundColor="?boxBackgroundColor"
app:endIconMode="clear_text"
tools:startIconDrawable="@drawable/ic_android"
tools:helperText="0 messages found"
tools:hint="Search">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/bs_textInputEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
tools:text="John" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:id="@+id/bs_toggleGroupContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="16dp"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/bs_toggleGroupTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Sorting order" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/bs_toggleGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleSelection="true" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/bs_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
tools:minHeight="50dp"
tools:listitem="@layout/nav_bs_item_primary"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_root"
android:layout_width="match_parent"
android:layout_height="48dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:gravity="center"
android:background="?selectableItemBackground"
android:orientation="horizontal">
<ImageView
android:id="@+id/item_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
tools:srcCompat="@drawable/ic_android" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:gravity="start|center_vertical"
android:textAppearance="@style/NavView.TextView"
android:textSize="14sp"
tools:text="Search activity" />
<TextView
android:id="@+id/item_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:gravity="center_vertical|start"
android:lines="1"
android:singleLine="true"
android:textAppearance="@style/NavView.TextView.Small"
android:textSize="12sp"
tools:text="Some drawer text" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="17dp"
android:gravity="center"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/material_drawer_divider" />
</LinearLayout>

View File

@ -0,0 +1,211 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:parentTag="FrameLayout">
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/nv_drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:baselineAligned="false">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<View
android:id="@+id/nv_statusBarBackground"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="top"
android:background="?android:windowBackground"
tools:layout_height="25dp" />
<LinearLayout
android:id="@+id/nv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:paddingTop="24dp"
tools:paddingBottom="48dp">
<FrameLayout
android:id="@+id/nv_drawerContainerLandscape"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@color/colorSurface_4dp"
tools:layout_width="72dp" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/nv_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<pl.szczodrzynski.navlib.NavToolbar
android:id="@+id/nv_toolbar"
style="@style/Widget.MaterialComponents.Toolbar.Surface"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?actionBarBackground"
android:clipToPadding="false"
android:elevation="4dp"
android:minHeight="?attr/actionBarSize"
app:title="@string/app_name"
app:titleMargin="0dp"
tools:targetApi="lollipop">
<com.mikepenz.materialdrawer.view.BezelImageView
android:id="@+id/nv_toolbar_image"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="13dp"
android:layout_marginRight="13dp"
android:scaleType="centerCrop"
app:materialDrawerSelectorOnPress="#80ffffff"
tools:src="@tools:sample/backgrounds/scenic" />
</pl.szczodrzynski.navlib.NavToolbar>
<View
android:id="@+id/nv_toolbarElevation"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_marginTop="?actionBarSize"
android:layout_weight="1"
android:background="@drawable/shadow_bottom" />
<LinearLayout
android:id="@+id/nv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginTop="?actionBarSize"
android:layout_marginBottom="?actionBarSize"
android:orientation="horizontal" >
<FrameLayout
android:id="@+id/nv_miniDrawerContainerPortrait"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@color/colorSurface_4dp"
tools:layout_width="72dp" /><!--tools:layout_width="72dp"-->
<View
android:id="@+id/nv_miniDrawerElevation"
android:layout_width="4dp"
android:layout_height="match_parent"
android:background="@drawable/shadow_right" />
</LinearLayout>
<pl.szczodrzynski.navlib.NavBottomBar
android:id="@+id/nv_bottomBar"
style="@style/Widget.MaterialComponents.BottomAppBar.Colored"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="start"
android:visibility="visible"
app:fabAlignmentMode="center"
app:fabAnimationMode="scale" />
<com.balysv.materialripple.MaterialRippleLayout
android:id="@+id/ripple"
android:layout_width="100dp"
android:layout_height="?actionBarSize"
android:layout_gravity="bottom|end"
android:enabled="false"
android:focusableInTouchMode="false"
android:focusable="false"
app:mrl_rippleFadeDuration="200"
app:mrl_rippleDuration="350"
app:mrl_rippleColor="?colorOnBackground"
app:mrl_rippleAlpha="0.3">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:enabled="false"
android:focusableInTouchMode="false"
android:focusable="false"
android:visibility="invisible"/>
</com.balysv.materialripple.MaterialRippleLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/nv_extendedFloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:focusable="true"
android:text=""
android:visibility="visible"
app:backgroundTint="?colorFab"
app:layout_anchor="@+id/nv_bottomBar"
app:layout_anchorGravity="center|top"
tools:icon="@android:drawable/ic_menu_edit" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/nv_floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:focusable="true"
android:visibility="gone"
app:backgroundTint="?colorFab"
app:layout_anchor="@id/nv_bottomBar"
tools:srcCompat="@android:drawable/ic_menu_edit" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>
<View
android:id="@+id/nv_navigationBarBackground"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:background="?navigationBarBackground"
tools:layout_height="48dp" />
</FrameLayout>
</androidx.drawerlayout.widget.DrawerLayout>
<View
android:id="@+id/nv_statusBarDarker"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="top"
android:background="#44000000"
android:visibility="gone"
tools:layout_height="25dp"
tools:visibility="visible" />
<View
android:id="@+id/nv_navigationBarDarker"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:background="#44000000"
android:visibility="gone"
tools:layout_height="48dp"
tools:visibility="visible" />
<pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet
android:id="@+id/nv_bottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</merge>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="width_max_600dp">
<item name="android:layout_width">600dp</item>
</style>
</resources>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="actionBarBackground" format="color" />
<attr name="navigationBarBackground" format="color" />
<attr name="colorFab" format="color" />
<attr name="colorFabIcon" format="color" />
<attr name="colorOnFab" format="color" />
</resources>

View File

@ -0,0 +1,8 @@
<resources>
<declare-styleable name="NavView">
<attr name="exampleString" format="string" />
<attr name="exampleDimension" format="dimension" />
<attr name="exampleColor" format="color" />
<attr name="exampleDrawable" format="color|reference" />
</declare-styleable>
</resources>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="color">#202196f3</color>
<color name="blue">#154FBC</color>
<color name="background_light">#ffffff</color>
<color name="background_dark">#242424</color>
<color name="background_black">#000000</color>
<color name="colorSurface_1dp">#0dffffff</color>
<color name="colorSurface_2dp">#12ffffff</color>
<color name="colorSurface_3dp">#14ffffff</color>
<color name="colorSurface_4dp">#17ffffff</color>
<color name="colorSurface_6dp">#1cffffff</color>
<color name="colorSurface_8dp">#1fffffff</color>
<color name="colorSurface_12dp">#24ffffff</color>
<color name="colorSurface_16dp">#26ffffff</color>
<color name="colorSurface_24dp">#29ffffff</color>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="badge_text_size">10sp</dimen>
</resources>

View File

@ -0,0 +1,4 @@
<resources>
<string name="app_name">NavLib</string>
<string name="toolbar_subtitle">%1$s</string>
</resources>

View File

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="NavView" />
<style name="NavView.Widget" />
<style name="NavView.Widget.FloatingActionButton" parent="Widget.MaterialComponents.FloatingActionButton">
<item name="backgroundTint">?colorFab</item>
<item name="android:textColor">?colorOnFab</item>
</style>
<style name="NavView.Widget.ExtendedFloatingActionButton" parent="Widget.MaterialComponents.ExtendedFloatingActionButton">
<item name="backgroundTint">?colorFab</item>
<item name="android:textColor">?colorOnFab</item>
</style>
<style name="NavView.TextView">
<item name="android:textAppearance">@style/NavView.TextView.Normal</item>
</style>
<!-- title text 20sp, primary, medium -->
<!-- subtitle text 16sp, primary, medium -->
<!-- large body text 22sp, primary, regular -->
<!-- medium body text 18sp, primary, regular -->
<!-- normal(default) body text 14sp, primary, regular -->
<!-- small body text 14sp, secondary, medium -->
<!-- helper body text 14sp, secondary, regular -->
<style name="NavView.TextView.Normal" parent="Widget.MaterialComponents.TextView">
<item name="android:textAppearance">?attr/textAppearanceBody2</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?android:textColorPrimary</item>
</style>
<style name="NavView.TextView.Title" parent="TextAppearance.AppCompat.Title">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NavView.TextView.Subtitle" parent="TextAppearance.AppCompat.Subhead">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">sans-serif-medium</item>
</style>
<style name="NavView.TextView.Large" parent="TextAppearance.AppCompat.Large">
</style>
<style name="NavView.TextView.Medium" parent="TextAppearance.AppCompat.Medium">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NavView.TextView.Small" parent="TextAppearance.AppCompat.Small">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">sans-serif-medium</item>
</style>
<style name="NavView.TextView.Helper" parent="TextAppearance.AppCompat.Small">
<item name="android:textSize">14sp</item>
</style>
<!--<item name="colorAccent">#ffb300</item>-->
<style name="NavView.Light" parent="Theme.MaterialComponents.Light.NoActionBar">
<!-- primary styling -->
<item name="colorPrimary">#6200EE</item>
<item name="colorPrimaryDark">#3700B3</item>
<item name="colorPrimaryVariant">#6200EE</item>
<item name="colorAccent">#03DAC6</item>
<item name="colorSecondary">?colorAccent</item>
<item name="colorSecondaryVariant">#018786</item>
<!-- window colors -->
<item name="android:windowBackground">?android:colorBackground</item>
<!-- a descendant theme should specify those two as background colors -->
<item name="android:colorBackground">@color/background_light</item>
<item name="colorSurface">#ffffff</item>
<!-- FAB styling -->
<item name="colorFab">?colorAccent</item>
<item name="colorFabIcon">?colorOnSecondary</item>
<item name="colorOnFab">?colorOnSecondary</item>
<!-- text colors -->
<item name="android:textColorPrimary">#db000000</item>
<item name="android:textColorSecondary">#99000000</item>
<item name="colorOnPrimary">#ffffff</item>
<item name="colorOnSecondary">#ffffff</item>
<item name="colorOnBackground">?android:textColorPrimary</item>
<item name="colorOnSurface">?android:textColorPrimary</item>
<item name="elevationOverlayColor">#ffffff</item>
<!-- system bars config -->
<item name="actionBarBackground">?colorSurface</item>
<item name="navigationBarBackground">?colorPrimaryVariant</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!-- component styling -->
<item name="floatingActionButtonStyle">@style/NavView.Widget.FloatingActionButton</item>
<item name="extendedFloatingActionButtonStyle">@style/NavView.Widget.ExtendedFloatingActionButton</item>
<item name="boxBackgroundColor" tools:ignore="PrivateResource">@color/mtrl_filled_background_color</item>
<item name="android:textViewStyle">@style/NavView.TextView</item>
<!-- drawer & bottom sheet styling -->
<item name="materialDrawerStyle">@style/Widget.MaterialDrawerStyle</item>
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
<!--<item name="materialDrawerPrimaryIcon">#5F6368</item>-->
</style>
<style name="NavView.Dark" parent="Theme.MaterialComponents.NoActionBar">
<!-- primary styling -->
<item name="colorPrimary">#BB86FC</item>
<item name="colorPrimaryDark">#3700B3</item>
<item name="colorPrimaryVariant">#6200EE</item>
<item name="colorAccent">#03DAC6</item>
<item name="colorSecondary">?colorAccent</item>
<item name="colorSecondaryVariant">?colorAccent</item>
<!-- window colors -->
<item name="android:windowBackground">?android:colorBackground</item>
<!-- a descendant theme should specify those two as background colors -->
<item name="android:colorBackground">@color/background_dark</item>
<item name="colorSurface">#333333</item>
<!-- FAB styling -->
<item name="colorFab">?colorAccent</item>
<item name="colorFabIcon">?colorOnSecondary</item>
<item name="colorOnFab">?colorOnSecondary</item>
<!-- text colors -->
<item name="android:textColorPrimary">#ffffffff</item>
<item name="android:textColorSecondary">#99ffffff</item>
<item name="colorOnPrimary">#ffffff</item>
<item name="colorOnSecondary">#ffffff</item>
<item name="colorOnBackground">?android:textColorPrimary</item>
<item name="colorOnSurface">?android:textColorPrimary</item>
<!-- system bars config -->
<item name="actionBarBackground">?colorSurface</item>
<item name="navigationBarBackground">?colorPrimaryVariant</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!-- component styling -->
<item name="floatingActionButtonStyle">@style/NavView.Widget.FloatingActionButton</item>
<item name="extendedFloatingActionButtonStyle">@style/NavView.Widget.ExtendedFloatingActionButton</item>
<item name="boxBackgroundColor">@color/text_input_layout_background</item>
<item name="android:textViewStyle">@style/NavView.TextView</item>
<!-- drawer & bottom sheet styling -->
<item name="materialDrawerStyle">@style/Widget.MaterialDrawerStyle</item>
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
<!--<item name="material_drawer_primary_icon">#9AA0A6</item>-->
</style>
<style name="NavView.Black" parent="NavView.Dark">
<item name="android:colorBackground">@color/background_black</item>
<item name="colorSurface">#121212</item>
</style>
<style name="width_max_600dp">
<item name="android:layout_width">match_parent</item>
</style>
</resources>

View File

@ -1,2 +1,2 @@
rootProject.name='Szkolny.eu'
include ':app'
include ':app', ':navlib', ':navlib-font'