fix(flashbar): use material components in FlashbarView

This commit is contained in:
Akash Yadav
2023-12-27 15:54:10 +05:30
parent 01a681254c
commit b7b9d695e9
11 changed files with 148 additions and 805 deletions
+3
View File
@@ -73,6 +73,9 @@
<TypeScriptCodeStyleSettings>
<option name="INDENT_CHAINED_CALLS" value="false" />
</TypeScriptCodeStyleSettings>
<XML>
<option name="XML_ALIGN_ATTRIBUTES" value="true" />
</XML>
<files>
<extensions>
<pair source="cc" header="h" fileNamingConvention="NONE" />
@@ -881,8 +881,8 @@ open class IDEEditor @JvmOverloads constructor(context: Context, attrs: Attribut
private fun configureFlashbar(builder: Flashbar.Builder, @StringRes message: Int,
cancelChecker: ICancelChecker) {
builder.message(message).negativeActionText(android.R.string.cancel)
.negativeActionTapListener { bar: Flashbar ->
builder.message(message).primaryActionText(android.R.string.cancel)
.primaryActionTapListener { bar: Flashbar ->
cancelChecker.cancel()
bar.dismiss()
}
+2
View File
@@ -25,6 +25,8 @@ android {
}
dependencies {
implementation(libs.common.utilcode)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.annotation)
implementation(libs.androidx.core.ktx)
@@ -75,9 +75,10 @@ class Flashbar private constructor(private var builder: Builder) {
flashbarContainerView = FlashbarContainerView(builder.activity)
flashbarContainerView.adjustOrientation(builder.activity)
flashbarContainerView.addParent(this)
flashbarContainerView.fitsSystemWindows = true
flashbarView = FlashbarView(builder.activity)
flashbarView.init(builder.gravity, builder.castShadow, builder.shadowStrength)
flashbarView.init(builder.gravity)
flashbarView.adjustWitPositionAndOrientation(builder.activity, builder.gravity)
flashbarView.addParent(flashbarContainerView)
@@ -181,7 +182,6 @@ class Flashbar private constructor(private var builder: Builder) {
internal var overlay: Boolean = false
internal var overlayColor: Int = ContextCompat.getColor(activity, R.color.modal)
internal var overlayBlockable: Boolean = false
internal var castShadow: Boolean = true
internal var shadowStrength: Int = DEFAULT_SHADOW_STRENGTH
internal var enableSwipeToDismiss: Boolean = false
internal var vibrationTargets: List<Vibration> = emptyList()
@@ -308,15 +308,6 @@ class Flashbar private constructor(private var builder: Builder) {
/** Specifies if modal overlay is blockable and should comsume touch events */
fun overlayBlockable() = apply { this.overlayBlockable = true }
/** Specifies if the flashbar should cast shadow and the strength of it */
@JvmOverloads
fun castShadow(shadow: Boolean = true, strength: Int = DEFAULT_SHADOW_STRENGTH) = apply {
require(strength > 0) { "Shadow strength can not be negative or zero" }
this.castShadow = shadow
this.shadowStrength = strength
}
/** Specifies the enter animation of the flashbar */
fun enterAnimation(builder: FlashAnimBarBuilder) = apply { this.enterAnimBuilder = builder }
@@ -20,6 +20,7 @@ package com.itsaky.androidide.flashbar
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.PorterDuff
import android.graphics.Typeface
@@ -37,6 +38,13 @@ import android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM
import android.widget.RelativeLayout.ALIGN_PARENT_TOP
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.view.marginBottom
import androidx.core.view.marginLeft
import androidx.core.view.marginRight
import androidx.core.view.marginTop
import androidx.core.view.updateLayoutParams
import com.google.android.material.button.MaterialButton
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.itsaky.androidide.flashbar.Flashbar.Gravity
import com.itsaky.androidide.flashbar.Flashbar.Gravity.BOTTOM
import com.itsaky.androidide.flashbar.Flashbar.Gravity.TOP
@@ -48,11 +56,7 @@ import com.itsaky.androidide.flashbar.Flashbar.ProgressPosition.RIGHT
import com.itsaky.androidide.flashbar.SwipeDismissTouchListener.DismissCallbacks
import com.itsaky.androidide.flashbar.anim.FlashAnimIconBuilder
import com.itsaky.androidide.flashbar.databinding.FlashBarViewBinding
import com.itsaky.androidide.flashbar.util.convertDpToPx
import com.itsaky.androidide.flashbar.util.getStatusBarHeightInPx
import com.itsaky.androidide.flashbar.view.FbButton
import com.itsaky.androidide.flashbar.view.FbProgress
import com.itsaky.androidide.flashbar.view.ShadowView
/**
* The actual Flashbar withView representation that can consist of the title, message, button, icon,
@@ -84,22 +88,22 @@ class FlashbarView(context: Context) : LinearLayout(context) {
private val fbIcon: ImageView
get() = this.binding.fbIcon
private val fbLeftProgress: FbProgress
private val fbLeftProgress: CircularProgressIndicator
get() = this.binding.fbLeftProgress
private val fbRightProgress: FbProgress
private val fbRightProgress: CircularProgressIndicator
get() = this.binding.fbRightProgress
private val fbMessage: TextView
get() = this.binding.fbMessage
private val fbPrimaryAction: FbButton
private val fbPrimaryAction: MaterialButton
get() = this.binding.fbPrimaryAction
private val fbPositiveAction: FbButton
private val fbPositiveAction: MaterialButton
get() = this.binding.fbPositiveAction
private val fbNegativeAction: FbButton
private val fbNegativeAction: MaterialButton
get() = this.binding.fbNegativeAction
private val fbSecondaryActionContainer: LinearLayout
@@ -123,22 +127,27 @@ class FlashbarView(context: Context) : LinearLayout(context) {
}
}
internal fun init(gravity: Gravity, castShadow: Boolean, shadowStrength: Int) {
internal fun init(gravity: Gravity) {
this.gravity = gravity
this.orientation = VERTICAL
// If the bar appears with the bottom, then the shadow needs to added to the top of it,
// Thus, before the inflation of the bar
if (castShadow && gravity == BOTTOM) {
castShadow(ShadowView.ShadowType.TOP, shadowStrength)
}
this.binding = FlashBarViewBinding.inflate(LayoutInflater.from(context), this, true)
// If the bar appears with the top, then the shadow needs to added to the bottom of it,
// Thus, after the inflation of the bar
if (castShadow && gravity == TOP) {
castShadow(ShadowView.ShadowType.BOTTOM, shadowStrength)
this.binding.root.apply {
// add margin to the card view so that the card elevation is visible
val dp16 = context.resources.getDimensionPixelSize(R.dimen.fb_card_elevation)
val (topIncr, bottomIncr) = when (gravity) {
TOP -> 0 to dp16
BOTTOM -> dp16 to 0
}
updateLayoutParams<MarginLayoutParams> {
setMargins(
/* left = */ marginLeft,
/* top = */ marginTop + topIncr,
/* right = */ marginRight,
/* bottom = */ marginBottom + bottomIncr
)
}
}
}
@@ -153,6 +162,7 @@ class FlashbarView(context: Context) : LinearLayout(context) {
flashbarViewContentLp.topMargin = statusBarHeight.plus(compensationMarginTop / 2)
flashbarViewLp.addRule(ALIGN_PARENT_TOP)
}
BOTTOM -> {
flashbarViewContentLp.bottomMargin = compensationMarginBottom
flashbarViewLp.addRule(ALIGN_PARENT_BOTTOM)
@@ -456,6 +466,7 @@ class FlashbarView(context: Context) : LinearLayout(context) {
fbLeftProgress.visibility = VISIBLE
fbRightProgress.visibility = GONE
}
RIGHT -> {
fbLeftProgress.visibility = GONE
fbRightProgress.visibility = VISIBLE
@@ -472,13 +483,6 @@ class FlashbarView(context: Context) : LinearLayout(context) {
RIGHT -> fbRightProgress
}
progressBar.setBarColor(progressTint)
}
private fun castShadow(shadowType: ShadowView.ShadowType, strength: Int) {
val params = RelativeLayout.LayoutParams(MATCH_PARENT, context.convertDpToPx(strength))
val shadow = ShadowView(context)
shadow.applyShadow(shadowType)
addView(shadow, params)
progressBar.progressTintList = ColorStateList.valueOf(progressTint)
}
}
@@ -1,17 +0,0 @@
package com.itsaky.androidide.flashbar.view
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import com.itsaky.androidide.flashbar.R
internal class FbButton : AppCompatTextView {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, R.style.FbButtonStyle)
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int
) : super(context, attrs, defStyleAttr)
}
@@ -1,581 +0,0 @@
package com.itsaky.androidide.flashbar.view
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.os.Build
import android.os.Build.VERSION
import android.os.Parcel
import android.os.Parcelable
import android.os.SystemClock
import android.provider.Settings
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import com.itsaky.androidide.flashbar.R
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.roundToInt
/** Forked from Todd Davies' Progress Wheel https://github.com/Todd-Davies/ProgressWheel */
class FbProgress : View {
private val barLength = 16
private val barMaxLength = 270
private val pauseGrowingTime: Long = 200
private var circleRadius = 28
private var barWidth = 4
private var rimWidth = 4
private var fillRadius = false
private var timeStartGrowing = 0.0
private var barSpinCycleTime = 460.0
private var barExtraLength = 0f
private var barGrowingFromFront = true
private var pausedTimeWithoutGrowing: Long = 0
private var barColor = -0x56000000
private var rimColor = 0x00FFFFFF
private val barPaint = Paint()
private val rimPaint = Paint()
private var circleBounds = RectF()
private var spinSpeed = 230.0f
private var lastTimeAnimated: Long = 0
private var linearProgress: Boolean = false
private var progress = 0.0f
private var targetProgress = 0.0f
private var isSpinning = false
private var shouldAnimate: Boolean = false
private var callback: ProgressCallback? = null
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
parseAttributes(context.obtainStyledAttributes(attrs, R.styleable.FbProgress))
setAnimationEnabled()
}
constructor(context: Context) : super(context) {
setAnimationEnabled()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val viewWidth = circleRadius + this.paddingLeft + this.paddingRight
val viewHeight = circleRadius + this.paddingTop + this.paddingBottom
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val width: Int =
when (widthMode) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> viewWidth.coerceAtMost(widthSize)
else -> viewWidth
}
val height: Int =
when {
heightMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.EXACTLY -> heightSize
heightMode == MeasureSpec.AT_MOST -> viewHeight.coerceAtMost(heightSize)
else -> viewHeight
}
setMeasuredDimension(width, height)
}
override fun onSizeChanged(w: Int, h: Int, oldW: Int, oldH: Int) {
super.onSizeChanged(w, h, oldW, oldH)
setupBounds(w, h)
setupPaints()
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawArc(circleBounds, 360f, 360f, false, rimPaint)
var mustInvalidate = false
if (!shouldAnimate) {
return
}
if (isSpinning) {
mustInvalidate = true
val deltaTime = SystemClock.uptimeMillis() - lastTimeAnimated
val deltaNormalized = deltaTime * spinSpeed / 1000.0f
updateBarLength(deltaTime)
progress += deltaNormalized
if (progress > 360) {
progress -= 360f
runCallback(-1.0f)
}
lastTimeAnimated = SystemClock.uptimeMillis()
var from = progress - 90
var length = barLength + barExtraLength
if (isInEditMode) {
from = 0f
length = 135f
}
canvas.drawArc(circleBounds, from, length, false, barPaint)
} else {
val oldProgress = progress
if (progress != targetProgress) {
mustInvalidate = true
val deltaTime = (SystemClock.uptimeMillis() - lastTimeAnimated).toFloat() / 1000
val deltaNormalized = deltaTime * spinSpeed
progress = (progress + deltaNormalized).coerceAtMost(targetProgress)
lastTimeAnimated = SystemClock.uptimeMillis()
}
if (oldProgress != progress) {
runCallback()
}
var offset = 0.0f
var progress = this.progress
if (!linearProgress) {
val factor = 2.0f
offset =
(1.0f - (1.0f - this.progress / 360.0f).toDouble().pow((2.0f * factor).toDouble()))
.toFloat() * 360.0f
progress =
(1.0f - (1.0f - this.progress / 360.0f).toDouble().pow(factor.toDouble())).toFloat() *
360.0f
}
if (isInEditMode) {
progress = 360f
}
canvas.drawArc(circleBounds, offset - 90, progress, false, barPaint)
}
if (mustInvalidate) {
invalidate()
}
}
override fun onVisibilityChanged(changedView: View, visibility: Int) {
super.onVisibilityChanged(changedView, visibility)
if (visibility == VISIBLE) {
lastTimeAnimated = SystemClock.uptimeMillis()
}
}
public override fun onSaveInstanceState(): Parcelable {
val superState = super.onSaveInstanceState()
val ss = WheelSavedState(superState)
ss.mProgress = this.progress
ss.mTargetProgress = this.targetProgress
ss.isSpinning = this.isSpinning
ss.spinSpeed = this.spinSpeed
ss.barWidth = this.barWidth
ss.barColor = this.barColor
ss.rimWidth = this.rimWidth
ss.rimColor = this.rimColor
ss.circleRadius = this.circleRadius
ss.linearProgress = this.linearProgress
ss.fillRadius = this.fillRadius
return ss
}
public override fun onRestoreInstanceState(state: Parcelable) {
if (state !is WheelSavedState) {
super.onRestoreInstanceState(state)
return
}
super.onRestoreInstanceState(state.superState)
this.progress = state.mProgress
this.targetProgress = state.mTargetProgress
this.isSpinning = state.isSpinning
this.spinSpeed = state.spinSpeed
this.barWidth = state.barWidth
this.barColor = state.barColor
this.rimWidth = state.rimWidth
this.rimColor = state.rimColor
this.circleRadius = state.circleRadius
this.linearProgress = state.linearProgress
this.fillRadius = state.fillRadius
this.lastTimeAnimated = SystemClock.uptimeMillis()
}
fun resetCount() {
progress = 0.0f
targetProgress = 0.0f
invalidate()
}
fun stopSpinning() {
isSpinning = false
progress = 0.0f
targetProgress = 0.0f
invalidate()
}
fun spin() {
lastTimeAnimated = SystemClock.uptimeMillis()
isSpinning = true
invalidate()
}
fun setInstantProgress(update: Float) {
var progressUpdate = update
if (isSpinning) {
progress = 0.0f
isSpinning = false
}
if (progressUpdate > 1.0f) {
progressUpdate -= 1.0f
} else if (progressUpdate < 0) {
progressUpdate = 0f
}
if (progressUpdate == targetProgress) {
return
}
targetProgress = (progressUpdate * 360.0f).coerceAtMost(360.0f)
progress = targetProgress
lastTimeAnimated = SystemClock.uptimeMillis()
invalidate()
}
fun getProgress(): Float {
return if (isSpinning) -1f else progress / 360.0f
}
fun setProgress(update: Float) {
var progress = update
if (isSpinning) {
this.progress = 0.0f
isSpinning = false
runCallback()
}
if (progress > 1.0f) {
progress -= 1.0f
} else if (progress < 0) {
progress = 0f
}
if (progress == targetProgress) {
return
}
if (this.progress == targetProgress) {
lastTimeAnimated = SystemClock.uptimeMillis()
}
targetProgress = (progress * 360.0f).coerceAtMost(360.0f)
invalidate()
}
fun setLinearProgress(isLinear: Boolean) {
linearProgress = isLinear
if (!isSpinning) {
invalidate()
}
}
fun getCircleRadius() = circleRadius
fun setCircleRadius(circleRadius: Int) {
this.circleRadius = circleRadius
if (!isSpinning) {
invalidate()
}
}
fun getBarWidth() = barWidth
fun setBarWidth(barWidth: Int) {
this.barWidth = barWidth
if (!isSpinning) {
invalidate()
}
}
fun getBarColor() = barColor
fun setBarColor(barColor: Int) {
this.barColor = barColor
setupPaints()
if (!isSpinning) {
invalidate()
}
}
fun getRimColor(): Int {
return rimColor
}
fun setRimColor(rimColor: Int) {
this.rimColor = rimColor
setupPaints()
if (!isSpinning) {
invalidate()
}
}
fun getSpinSpeed() = spinSpeed / 360.0f
fun setSpinSpeed(spinSpeed: Float) {
this.spinSpeed = spinSpeed * 360.0f
}
fun getRimWidth() = rimWidth
fun setRimWidth(rimWidth: Int) {
this.rimWidth = rimWidth
if (!isSpinning) {
invalidate()
}
}
fun setCallback(progressCallback: ProgressCallback) {
callback = progressCallback
if (!isSpinning) {
runCallback()
}
}
private fun setAnimationEnabled() {
val currentApiVersion = VERSION.SDK_INT
val animationValue =
if (currentApiVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Settings.Global.getFloat(
context.contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE,
1f
)
} else {
Settings.System.getFloat(
context.contentResolver,
Settings.System.ANIMATOR_DURATION_SCALE,
1f
)
}
shouldAnimate = animationValue != 0f
}
private fun setupBounds(layoutWidth: Int, layoutHeight: Int) {
val paddingTop = paddingTop
val paddingBottom = paddingBottom
val paddingLeft = paddingLeft
val paddingRight = paddingRight
if (!fillRadius) {
val minValue =
(layoutWidth - paddingLeft - paddingRight).coerceAtMost(
layoutHeight - paddingBottom - paddingTop
)
val circleDiameter = minValue.coerceAtMost(circleRadius * 2 - barWidth * 2)
val xOffset = (layoutWidth - paddingLeft - paddingRight - circleDiameter) / 2 + paddingLeft
val yOffset = (layoutHeight - paddingTop - paddingBottom - circleDiameter) / 2 + paddingTop
circleBounds =
RectF(
(xOffset + barWidth).toFloat(),
(yOffset + barWidth).toFloat(),
(xOffset + circleDiameter - barWidth).toFloat(),
(yOffset + circleDiameter - barWidth).toFloat()
)
} else {
circleBounds =
RectF(
(paddingLeft + barWidth).toFloat(),
(paddingTop + barWidth).toFloat(),
(layoutWidth - paddingRight - barWidth).toFloat(),
(layoutHeight - paddingBottom - barWidth).toFloat()
)
}
}
private fun parseAttributes(a: TypedArray) {
val metrics = context.resources.displayMetrics
barWidth =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, barWidth.toFloat(), metrics).toInt()
rimWidth =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rimWidth.toFloat(), metrics).toInt()
circleRadius =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, circleRadius.toFloat(), metrics)
.toInt()
circleRadius =
a.getDimension(R.styleable.FbProgress_fbp_circleRadius, circleRadius.toFloat()).toInt()
fillRadius = a.getBoolean(R.styleable.FbProgress_fbp_fillRadius, false)
barWidth = a.getDimension(R.styleable.FbProgress_fbp_barWidth, barWidth.toFloat()).toInt()
rimWidth = a.getDimension(R.styleable.FbProgress_fbp_rimWidth, rimWidth.toFloat()).toInt()
val baseSpinSpeed = a.getFloat(R.styleable.FbProgress_fbp_spinSpeed, spinSpeed / 360.0f)
spinSpeed = baseSpinSpeed * 360
barSpinCycleTime =
a.getInt(R.styleable.FbProgress_fbp_barSpinCycleTime, barSpinCycleTime.toInt()).toDouble()
barColor = a.getColor(R.styleable.FbProgress_fbp_barColor, barColor)
rimColor = a.getColor(R.styleable.FbProgress_fbp_rimColor, rimColor)
linearProgress = a.getBoolean(R.styleable.FbProgress_fbp_linearProgress, false)
if (a.getBoolean(R.styleable.FbProgress_fbp_progressIndeterminate, false)) {
spin()
}
a.recycle()
}
private fun setupPaints() {
barPaint.color = barColor
barPaint.isAntiAlias = true
barPaint.style = Paint.Style.STROKE
barPaint.strokeWidth = barWidth.toFloat()
rimPaint.color = rimColor
rimPaint.isAntiAlias = true
rimPaint.style = Paint.Style.STROKE
rimPaint.strokeWidth = rimWidth.toFloat()
}
private fun updateBarLength(deltaTimeInMilliSeconds: Long) {
if (pausedTimeWithoutGrowing >= pauseGrowingTime) {
timeStartGrowing += deltaTimeInMilliSeconds.toDouble()
if (timeStartGrowing > barSpinCycleTime) {
timeStartGrowing -= barSpinCycleTime
pausedTimeWithoutGrowing = 0
barGrowingFromFront = !barGrowingFromFront
}
val distance = cos((timeStartGrowing / barSpinCycleTime + 1) * Math.PI).toFloat() / 2 + 0.5f
val destLength = (barMaxLength - barLength).toFloat()
if (barGrowingFromFront) {
barExtraLength = distance * destLength
} else {
val newLength = destLength * (1 - distance)
progress += barExtraLength - newLength
barExtraLength = newLength
}
} else {
pausedTimeWithoutGrowing += deltaTimeInMilliSeconds
}
}
private fun runCallback(value: Float) {
if (callback != null) {
callback!!.onProgressUpdate(value)
}
}
private fun runCallback() {
if (callback != null) {
val normalizedProgress = (progress * 100 / 360.0f).roundToInt().toFloat() / 100
callback!!.onProgressUpdate(normalizedProgress)
}
}
interface ProgressCallback {
fun onProgressUpdate(progress: Float)
}
internal class WheelSavedState : View.BaseSavedState {
var mProgress: Float = 0.toFloat()
var mTargetProgress: Float = 0.toFloat()
var isSpinning: Boolean = false
var spinSpeed: Float = 0.toFloat()
var barWidth: Int = 0
var barColor: Int = 0
var rimWidth: Int = 0
var rimColor: Int = 0
var circleRadius: Int = 0
var linearProgress: Boolean = false
var fillRadius: Boolean = false
constructor(superState: Parcelable?) : super(superState)
private constructor(`in`: Parcel) : super(`in`) {
this.mProgress = `in`.readFloat()
this.mTargetProgress = `in`.readFloat()
this.isSpinning = `in`.readByte().toInt() != 0
this.spinSpeed = `in`.readFloat()
this.barWidth = `in`.readInt()
this.barColor = `in`.readInt()
this.rimWidth = `in`.readInt()
this.rimColor = `in`.readInt()
this.circleRadius = `in`.readInt()
this.linearProgress = `in`.readByte().toInt() != 0
this.fillRadius = `in`.readByte().toInt() != 0
}
override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags)
out.writeFloat(this.mProgress)
out.writeFloat(this.mTargetProgress)
out.writeByte((if (isSpinning) 1 else 0).toByte())
out.writeFloat(this.spinSpeed)
out.writeInt(this.barWidth)
out.writeInt(this.barColor)
out.writeInt(this.rimWidth)
out.writeInt(this.rimColor)
out.writeInt(this.circleRadius)
out.writeByte((if (linearProgress) 1 else 0).toByte())
out.writeByte((if (fillRadius) 1 else 0).toByte())
}
companion object {
@Suppress("unused")
@JvmField
val CREATOR: Parcelable.Creator<WheelSavedState> =
object : Parcelable.Creator<WheelSavedState> {
override fun createFromParcel(`in`: Parcel): WheelSavedState {
return WheelSavedState(`in`)
}
override fun newArray(size: Int): Array<WheelSavedState> {
return newArray(size)
}
}
}
}
}
@@ -1,33 +0,0 @@
package com.itsaky.androidide.flashbar.view
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import com.itsaky.androidide.flashbar.R
import com.itsaky.androidide.flashbar.view.ShadowView.ShadowType.BOTTOM
import com.itsaky.androidide.flashbar.view.ShadowView.ShadowType.TOP
internal class ShadowView
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
View(context, attrs, defStyleAttr) {
internal fun applyShadow(type: ShadowType) {
when (type) {
TOP -> setShadow(R.drawable.shadow_top)
BOTTOM -> setShadow(R.drawable.shadow_bottom)
}
}
private fun setShadow(@DrawableRes id: Int) {
val shadow = ContextCompat.getDrawable(context, id)
this.background = shadow
}
enum class ShadowType {
TOP,
BOTTOM
}
}
@@ -1,133 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.google.android.material.card.MaterialCardView 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:id="@+id/fbRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/warm_grey"
android:clickable="true"
android:fitsSystemWindows="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical"
android:padding="@dimen/fb_content_padding_all">
app:cardBackgroundColor="?colorPrimaryContainer"
app:cardElevation="@dimen/fb_card_elevation"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Material3.Corner.Bottom">
<LinearLayout
android:id="@+id/fbContent"
<LinearLayout
android:id="@+id/fbRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/fb_content_padding_all"
android:orientation="vertical">
<LinearLayout
android:id="@+id/fbContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<com.itsaky.androidide.flashbar.view.FbProgress
android:id="@+id/fbLeftProgress"
style="@style/FbCircularIndeterminateProgressStyle"
android:layout_width="@dimen/fb_progress_size"
android:layout_height="@dimen/fb_progress_size"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/fb_margin_progress"
android:layout_marginRight="@dimen/fb_margin_progress"
android:visibility="gone" />
<ImageView
android:id="@+id/fbIcon"
android:layout_width="@dimen/fb_icon_size"
android:layout_height="@dimen/fb_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/fb_margin_right_icon"
android:layout_marginRight="@dimen/fb_margin_right_icon"
android:src="@drawable/ic_info"
android:visibility="gone"
app:tint="@color/white"
tools:visibility="visible" />
android:orientation="vertical"
tools:ignore="UselessParent">
<LinearLayout
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="8"
android:orientation="vertical">
android:gravity="center_vertical">
<TextView
android:id="@+id/fbTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/fb_title_message_margin"
android:textAppearance="@style/FbTitleTextStyle"
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/fbLeftProgress"
android:layout_width="@dimen/fb_progress_size"
android:layout_height="@dimen/fb_progress_size"
android:layout_marginEnd="@dimen/fb_margin_progress"
android:layout_marginRight="@dimen/fb_margin_progress"
android:layout_gravity="center_vertical"
android:indeterminate="true"
android:visibility="gone" />
<ImageView
android:id="@+id/fbIcon"
android:layout_width="@dimen/fb_icon_size"
android:layout_height="@dimen/fb_icon_size"
android:layout_marginEnd="@dimen/fb_margin_right_icon"
android:layout_marginRight="@dimen/fb_margin_right_icon"
android:layout_gravity="center_vertical"
android:importantForAccessibility="no"
android:src="@drawable/ic_info"
android:visibility="gone"
tools:text="Title"
app:tint="?colorOnPrimaryContainer"
tools:visibility="visible" />
<TextView
android:id="@+id/fbMessage"
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="8"
android:orientation="vertical">
<TextView
android:id="@+id/fbTitle"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/fb_title_message_margin"
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
android:textColor="?colorOnPrimaryContainer"
android:visibility="gone"
tools:text="Title"
tools:visibility="visible" />
<TextView
android:id="@+id/fbMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/fb_title_message_margin"
android:justificationMode="inter_word"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:visibility="gone"
tools:text="A quick brown fox jumps of the lazy dog."
tools:visibility="visible" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/fbPrimaryAction"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/fb_title_message_margin"
android:justificationMode="inter_word"
android:textAppearance="@style/FbMessageStyle"
android:textAllCaps="false"
android:visibility="gone"
tools:text="A quick brown fox jumps of the lazy dog."
tools:text="CLICK"
tools:visibility="visible" />
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/fbRightProgress"
android:layout_width="@dimen/fb_progress_size"
android:layout_height="@dimen/fb_progress_size"
android:layout_marginStart="@dimen/fb_margin_progress"
android:layout_marginLeft="@dimen/fb_margin_progress"
android:layout_gravity="center_vertical"
android:indeterminate="true"
android:visibility="gone" />
</LinearLayout>
<com.itsaky.androidide.flashbar.view.FbButton
android:id="@+id/fbPrimaryAction"
style="@style/FbButtonStyle"
android:layout_width="wrap_content"
<LinearLayout
android:id="@+id/fbSecondaryActionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/FbButtonTextStyle"
android:visibility="gone"
tools:text="CLICK"
tools:visibility="visible" />
android:layout_marginTop="@dimen/fb_margin_secondary_action_container"
android:gravity="end"
android:visibility="gone">
<com.itsaky.androidide.flashbar.view.FbProgress
android:id="@+id/fbRightProgress"
style="@style/FbCircularIndeterminateProgressStyle"
android:layout_width="@dimen/fb_progress_size"
android:layout_height="@dimen/fb_progress_size"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/fb_margin_progress"
android:layout_marginLeft="@dimen/fb_margin_progress"
android:visibility="gone" />
<com.google.android.material.button.MaterialButton
android:id="@+id/fbNegativeAction"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
tools:text="CANCEL"
tools:visibility="gone" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/fbPositiveAction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/fb_margin_secondary_action"
android:layout_marginLeft="@dimen/fb_margin_secondary_action"
android:visibility="gone"
tools:text="OK"
tools:visibility="gone" />
<LinearLayout
android:id="@+id/fbSecondaryActionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/fb_margin_secondary_action_container"
android:gravity="end"
android:visibility="gone">
<com.itsaky.androidide.flashbar.view.FbButton
android:id="@+id/fbPositiveAction"
style="@style/FbButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/FbButtonTextStyle"
android:visibility="gone"
tools:text="OK"
tools:visibility="gone" />
<com.itsaky.androidide.flashbar.view.FbButton
android:id="@+id/fbNegativeAction"
style="@style/FbButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/fb_margin_secondary_action"
android:layout_marginLeft="12dp"
android:textAppearance="@style/FbButtonTextStyle"
android:visibility="gone"
tools:text="CANCEL"
tools:visibility="gone" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="fb_card_elevation">8dp</dimen>
<dimen name="fb_content_padding_all">16dp</dimen>
<dimen name="fb_title_text_size">16sp</dimen>
<dimen name="fb_message_text_size">14sp</dimen>
@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="FbTitleTextStyle" parent="TextAppearance.AppCompat">
<item name="android:textColor">@color/white</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">@dimen/fb_title_text_size</item>
</style>
<style name="FbMessageStyle" parent="TextAppearance.AppCompat">
<item name="android:textColor">@color/white</item>
<item name="android:textSize">@dimen/fb_message_text_size</item>
</style>
<style name="FbButtonTextStyle" parent="TextAppearance.AppCompat">
<item name="android:textColor">@color/white</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">@dimen/fb_button_text_size</item>
</style>
<style name="FbButtonStyle">
<item name="android:paddingLeft">8dp</item>
<item name="android:paddingRight">8dp</item>
<item name="android:paddingTop">4dp</item>
<item name="android:paddingBottom">4dp</item>
<item name="android:background">@drawable/fb_button_background_selector</item>
<item name="android:clickable">true</item>
<item name="android:focusable">true</item>
</style>
<style name="FbCircularIndeterminateProgressStyle">
<item name="fbp_barColor">@color/white</item>
<item name="fbp_barWidth">2dp</item>
<item name="fbp_progressIndeterminate">true</item>
</style>
</resources>