[UI] Reformat layout XMLs, cleanup NavLib code (#207)
Some checks failed
Schedule/dispatch / Build nightly release (APK) (push) Has been skipped
Schedule/dispatch / Check new changes (push) Failing after 19s

* Remove Themes and getColorFromAttr() usage

* Reformat all layout XML files

* Remove <layout> from non-data binding XMLs

* Cleanup unused NavLib code

* Migrate NavBottomSheet to view binding, remove unused components

* Add IIcon.toDrawable() extension

* Make BottomSheet scrim opacity linear

* Support touch events on BottomSheet scrim
This commit is contained in:
Kuba Szczodrzyński
2024-07-05 18:46:37 +02:00
committed by GitHub
parent 10043cc62c
commit 09f0c986e0
218 changed files with 6246 additions and 7330 deletions

View File

@ -1,278 +0,0 @@
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

@ -1,85 +0,0 @@
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

@ -2,24 +2,20 @@ 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 androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
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
@SuppressLint("ClickableViewAccessibility")
class NavBottomBar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@ -27,8 +23,6 @@ class NavBottomBar @JvmOverloads constructor(
) : BottomAppBar(context, attrs, defStyle), NavMenuBarBase {
internal lateinit var navView: NavView
override lateinit var bottomSheet: NavBottomSheet
internal lateinit var fabView: FloatingActionButton
internal lateinit var fabExtendedView: ExtendedFloatingActionButton
/**
@ -42,6 +36,7 @@ class NavBottomBar @JvmOverloads constructor(
setFabParams()
navView.setContentMargins()
}
/**
* Whether the FAB should be visible.
*/
@ -50,17 +45,7 @@ class NavBottomBar @JvmOverloads constructor(
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.
@ -70,6 +55,7 @@ class NavBottomBar @JvmOverloads constructor(
field = value
setFabParams()
}
/**
* Whether the FAB should be extended and its text visible.
*/
@ -81,23 +67,20 @@ class NavBottomBar @JvmOverloads constructor(
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.colorOnPrimaryContainer)
sizeDp = 24
})
fabExtendedView.icon = IconicsDrawable(context).apply {
icon = value
colorAttr(context, R.attr.colorOnPrimaryContainer)
sizeDp = 24
}
}
/**
* Set the ExtendedFAB's text.
*/
@ -111,7 +94,6 @@ class NavBottomBar @JvmOverloads constructor(
* Set the FAB's on click listener
*/
fun setFabOnClickListener(onClickListener: OnClickListener?) {
fabView.setOnClickListener(onClickListener)
fabExtendedView.setOnClickListener(onClickListener)
}
@ -119,51 +101,32 @@ class NavBottomBar @JvmOverloads constructor(
override var menuClickListener: (() -> Unit)? = null
init {
setOnTouchListener { _, event ->
if (bottomSheet?.enable != true || bottomSheet?.enableDragToOpen != true)
return@setOnTouchListener false
bottomSheet?.dispatchBottomBarEvent(event)
true
}
elevation = 0f
}
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
fabExtendedView.updateLayoutParams<CoordinatorLayout.LayoutParams> {
if (enable) {
anchorId = this@NavBottomBar.id
anchorGravity = fabGravity or Gravity.TOP
gravity = Gravity.NO_GRAVITY
} else {
anchorId = View.NO_ID
anchorGravity = Gravity.NO_GRAVITY
gravity = fabGravity or Gravity.BOTTOM
}
fabAlignmentMode =
if (fabGravity == Gravity.END)
FAB_ALIGNMENT_MODE_END
else
FAB_ALIGNMENT_MODE_CENTER
}
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()
private fun setFabVisibility() =
if (fabEnable)
fabExtendedView.show()
}
else if (fabEnable) {
fabView.show()
else
fabExtendedView.hide()
}
else {
fabView.hide()
fabExtendedView.hide()
}
}
}

View File

@ -15,7 +15,6 @@ import pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet
interface NavMenuBarBase {
var bottomSheet: NavBottomSheet
var drawerClickListener: (() -> Unit)?
var menuClickListener: (() -> Unit)?
@ -54,11 +53,7 @@ interface NavMenuBarBase {
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
setOnMenuItemClickListener {
if (it.itemId == -1 && bottomSheet.enable) {
bottomSheet.toggle()
} else {
menuClickListener?.invoke()
}
menuClickListener?.invoke()
true
}
}

View File

@ -15,7 +15,6 @@ class NavToolbar @JvmOverloads constructor(
) : MaterialToolbar(context, attrs, defStyle), NavMenuBarBase {
internal lateinit var navView: NavView
override lateinit var bottomSheet: NavBottomSheet
/**
* Shows the toolbar and sets the contentView's margin to be

View File

@ -33,11 +33,11 @@ class NavView @JvmOverloads constructor(
get() = b.nvCoordinator
val toolbar
get() = b.nvToolbar
val bottomBar: NavBottomBar
val bottomBar
get() = b.nvBottomBar
val nightlyText: TextView
val nightlyText
get() = b.nvNightlyText
val bottomSheet: NavBottomSheet
val bottomSheet
get() = b.nvBottomSheet
init {
@ -55,13 +55,18 @@ class NavView @JvmOverloads constructor(
drawer.bottomBar = b.nvBottomBar
b.nvToolbar.navView = this
b.nvToolbar.bottomSheet = b.nvBottomSheet
b.nvToolbar.toolbarImage = b.nvToolbarImage
b.nvBottomBar.navView = this
b.nvBottomBar.bottomSheet = b.nvBottomSheet
b.nvBottomBar.fabView = b.nvFloatingActionButton
b.nvBottomBar.fabExtendedView = b.nvExtendedFloatingActionButton
toolbar.drawerClickListener = drawer::open
toolbar.menuClickListener = bottomSheet::open
bottomBar.drawerClickListener = drawer::open
bottomBar.menuClickListener = bottomSheet::open
b.nvBottomBar.setOnTouchListener(bottomSheet::dispatchBottomSheetEvent)
b.nvBottomSheet.scrimView.setOnTouchListener(bottomSheet::dispatchBottomSheetEvent)
}
fun configSystemBarsUtil(systemBarsUtil: SystemBarsUtil) {

View File

@ -1,11 +1,15 @@
package pl.szczodrzynski.navlib
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.content.res.Resources
import android.graphics.Color
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES
import android.util.DisplayMetrics
import android.util.Log
import android.view.View
import android.view.View.*
@ -351,6 +355,42 @@ class SystemBarsUtil(private val activity: Activity) {
}
}
private fun isTablet(c: Context): Boolean {
return (c.resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
}
private 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
}
private fun applyPadding(left: Int, top: Int, right: Int, bottom: Int) {
marginBySystemBars?.setPadding(left, top, right, bottom)

View File

@ -1,65 +1,20 @@
package pl.szczodrzynski.navlib
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
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 androidx.core.content.ContextCompat
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
}
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.Badgeable
import com.mikepenz.materialdrawer.model.interfaces.withBadge
import com.mikepenz.materialdrawer.util.getDrawerItem
import com.mikepenz.materialdrawer.util.updateItem
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
fun blendColors(background: Int, foreground: Int): Int {
val r1 = (background shr 16 and 0xff)
@ -70,7 +25,6 @@ fun blendColors(background: Int, foreground: Int): Int {
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)
@ -86,42 +40,6 @@ fun elevateSurface(context: Context, dp: Int): Int {
}
}
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)
}
@ -135,25 +53,6 @@ fun getColorFromAttr(context: Context, @AttrRes color: Int): Int {
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 Drawable.setBadgeCount(count: Int) {
if (this is LayerDrawable) {
(this as LayerDrawable?)?.apply {
@ -168,16 +67,10 @@ fun Drawable.setBadgeCount(count: Int) {
}
}
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)
fun MaterialDrawerSliderView.updateBadge(identifier: Long, badge: StringHolder?) {
val drawerItem = getDrawerItem(identifier)
if (drawerItem is Badgeable) {
drawerItem.withBadge(badge)
updateItem(drawerItem)
}
crc = crc and 0xffff
return crc
}

View File

@ -1,135 +1,49 @@
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.content.getSystemService
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.widget.NestedScrollView
import androidx.core.view.isInvisible
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
import pl.szczodrzynski.navlib.databinding.NavBottomSheetBinding
import pl.szczodrzynski.navlib.elevateSurface
class NavBottomSheet @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
) : CoordinatorLayout(context, attrs, defStyle) {
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
private val b = NavBottomSheetBinding.inflate(LayoutInflater.from(context), this, true)
const val SORT_MODE_ASCENDING = 0
const val SORT_MODE_DESCENDING = 1
}
val scrimView
get() = b.bsScrim
private val bottomSheet
get() = b.bsView
private val content
get() = b.bsContent
private val dragBar
get() = b.bsDragBar
private val list
get() = b.bsList
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 var bottomSheetBehavior = BottomSheetBehavior.from<View>(bottomSheet)
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)
init {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
ViewCompat.setOnApplyWindowInsetsListener(list) { view, windowInsets ->
@ -144,32 +58,29 @@ class NavBottomSheet : CoordinatorLayout {
WindowInsetsCompat.CONSUMED
}
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
scrimView.setOnClickListener {
isOpen = false
}
bottomSheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(v: View, p1: Float) {}
override fun onStateChanged(v: View, newState: Int) {
var bottomSheetVisible = false
bottomSheetBehavior.addBottomSheetCallback(object :
BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(view: View, slideOffset: Float) {
val alpha = (1.0f + slideOffset).coerceIn(0.0f, 1.0f)
scrimView.alpha = alpha
scrimView.isInvisible = alpha == 0.0f
}
override fun onStateChanged(view: 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) {
} else if (!bottomSheetVisible) {
bottomSheetVisible = true
if (scrimViewEnabled)
Anim.fadeIn(scrimView, 300, null)
}
}
})
@ -187,11 +98,11 @@ class NavBottomSheet : CoordinatorLayout {
layoutManager = LinearLayoutManager(context)
adapter = this@NavBottomSheet.adapter
}
toggleGroup.addOnButtonCheckedListener(toggleGroupCheckedListener)
textInputEditText.addTextChangedListener(textInputWatcher)
}
fun hideKeyboard() = context.getSystemService<InputMethodManager>()
?.hideSoftInputFromWindow(rootView.windowToken, 0)
var onCloseListener: (() -> Unit)? = null
/* _____ _
@ -203,55 +114,61 @@ class NavBottomSheet : CoordinatorLayout {
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 {
@ -260,6 +177,7 @@ class NavBottomSheet : CoordinatorLayout {
adapter.notifyItemChanged(items.indexOf(it))
}
}
fun getItemByIndex(index: Int, run: (it: IBottomSheetItem<*>?) -> Unit) {
items.getOrNull(index).also {
run(it)
@ -268,185 +186,33 @@ class NavBottomSheet : CoordinatorLayout {
}
}
/* _______ _
|__ __| | |
| | ___ __ _ __ _| | ___ __ _ _ __ ___ _ _ _ __
| |/ _ \ / _` |/ _` | |/ _ \ / _` | '__/ _ \| | | | '_ \
| | (_) | (_| | (_| | | __/ | (_| | | | (_) | |_| | |_) |
|_|\___/ \__, |\__, |_|\___| \__, |_| \___/ \__,_| .__/
__/ | __/ | __/ | | |
|___/ |___/ |___/ |*/
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) {
fun dispatchBottomSheetEvent(view: View, event: MotionEvent): Boolean {
val location = IntArray(2)
bottomSheet.getLocationOnScreen(location)
event.setLocation(event.rawX - location[0], event.rawY - location[1])
bottomSheet.dispatchTouchEvent(event)
return false
}
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
bottomSheetBehavior.state =
if (value)
BottomSheetBehavior.STATE_EXPANDED
else
BottomSheetBehavior.STATE_HIDDEN
}
fun open() { isOpen = true }
fun close() { isOpen = false }
fun open() {
isOpen = true
}
fun close() {
isOpen = false
}
fun toggle() {
if (!enable)
return
isOpen = !isOpen
}
}

View File

@ -212,12 +212,6 @@ class NavDrawer(
profileSelectionOpen()
open()
}
toolbar.drawerClickListener = {
open()
}
bottomBar.drawerClickListener = {
open()
}
val configuration = context.resources.configuration
decideDrawerMode(
@ -227,69 +221,6 @@ class NavDrawer(
)
}
/* _____ _
|_ _| |
| | | |_ ___ _ __ ___ ___
| | | __/ _ \ '_ ` _ \/ __|
_| |_| || __/ | | | | \__ \
|_____|\__\___|_| |_| |_|__*/
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)

View File

@ -4,15 +4,5 @@ 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

@ -1,109 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout
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"
xmlns:app="http://schemas.android.com/apk/res-auto">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:translationZ="10dp"
tools:targetApi="lollipop">
<androidx.coordinatorlayout.widget.CoordinatorLayout
<View
android:id="@+id/bs_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:translationZ="10dp"
tools:targetApi="lollipop">
android:background="#99000000"
android:visibility="invisible"
tools:visibility="gone" />
<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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:maxWidth="600dp"
app:behavior_hideable="true"
app:behavior_peekHeight="auto"
app:layout_behavior="@string/bottom_sheet_behavior"
tools:visibility="visible">
<androidx.core.widget.NestedScrollView
android:id="@+id/bs_view"
<LinearLayout
android:id="@+id/bs_content"
android:layout_width="match_parent"
android:maxWidth="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">
android:background="@drawable/bottom_sheet_background"
android:orientation="vertical"
android:paddingHorizontal="8dp"
tools:paddingBottom="48dp">
<LinearLayout
android:id="@+id/bs_content"
<ImageView
android:id="@+id/bs_dragBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="16dp"
android:focusable="true"
android:focusableInTouchMode="true"
app:srcCompat="@drawable/bottom_sheet_control_bar"
tools:ignore="ContentDescription" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/bs_list"
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="?textInputFilledDenseStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
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>
android:overScrollMode="never"
tools:listitem="@layout/nav_bs_item_primary"
tools:minHeight="50dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,18 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
<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:background="#202020"
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">
android:baselineAligned="false"
android:orientation="horizontal">
<FrameLayout
android:layout_width="0dp"
@ -33,8 +35,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:paddingTop="24dp"
tools:paddingBottom="48dp">
tools:paddingBottom="48dp"
tools:paddingTop="24dp">
<FrameLayout
android:id="@+id/nv_drawerContainerLandscape"
@ -50,22 +52,17 @@
<pl.szczodrzynski.navlib.NavToolbar
android:id="@+id/nv_toolbar"
style="?toolbarSurfaceStyle"
style="?toolbarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?nvActionBarBackground"
android:clipToPadding="false"
android:elevation="4dp"
app:title="@string/app_name"
app:titleMargin="0dp"
tools:targetApi="lollipop">
android:clipToPadding="false">
<TextView
android:id="@+id/nv_nightlyText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_gravity="center_vertical|end"
android:layout_margin="8dp"
android:background="@drawable/nightly_text_rounded"
android:fontFamily="sans-serif-light"
android:padding="4dp"
@ -85,7 +82,6 @@
android:scaleType="centerCrop"
app:materialDrawerSelectorOnPress="#80ffffff"
tools:src="@tools:sample/backgrounds/scenic" />
</pl.szczodrzynski.navlib.NavToolbar>
<View
@ -103,7 +99,7 @@
android:layout_gravity="center"
android:layout_marginTop="?actionBarSize"
android:layout_marginBottom="?actionBarSize"
android:orientation="horizontal" >
android:orientation="horizontal">
<FrameLayout
android:id="@+id/nv_miniDrawerContainerPortrait"
@ -111,13 +107,11 @@
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
@ -126,9 +120,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?nvBottomBarBackground"
android:gravity="start"
android:visibility="visible"
app:fabAlignmentMode="center"
app:fabAnimationMode="scale" />
@ -143,19 +134,7 @@
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:clickable="true"
android:focusable="true"
android:visibility="gone"
app:layout_anchor="@id/nv_bottomBar"
tools:srcCompat="@android:drawable/ic_menu_edit" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>
<View
@ -165,9 +144,7 @@
android:layout_gravity="bottom"
android:background="?nvNavigationBarBackground"
tools:layout_height="48dp" />
</FrameLayout>
</androidx.drawerlayout.widget.DrawerLayout>
<View
@ -194,6 +171,4 @@
android:id="@+id/nv_bottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</merge>