From 9f5c58eb6474eee1c7a98a89ef7371d85cb7ddbc Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 13 May 2022 19:08:45 +0530 Subject: [PATCH 1/4] merging issue resolved --- app/build.gradle.kts | 3 +- .../kiwixmobile/intro/CustomPageIndicator.kt | 808 ++++++++++++++++++ app/src/main/res/layout/fragment_intro.xml | 2 +- .../main/res/values/attrs_page_indicator.xml | 27 + buildSrc/src/main/kotlin/Libs.kt | 9 +- buildSrc/src/main/kotlin/Versions.kt | 4 +- .../kotlin/plugin/AllProjectConfigurer.kt | 1 + 7 files changed, 847 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt create mode 100644 app/src/main/res/values/attrs_page_indicator.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1c7bfff8b..ccf06054a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -87,5 +87,6 @@ play { dependencies { implementation(Libs.squidb) - implementation(Libs.ink_page_indicator) + implementation(Libs.squidb_annotations) + add("kapt", Libs.squidb_processor) } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt b/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt new file mode 100644 index 000000000..8b4d205ef --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt @@ -0,0 +1,808 @@ +/* + * Kiwix Android + * Copyright (c) 2022 Kiwix + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package org.kiwix.kiwixmobile.intro + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.AnimatorSet +import android.animation.ValueAnimator +import android.content.Context +import android.database.DataSetObserver +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Path +import android.graphics.RectF +import android.os.Parcel +import android.os.Parcelable +import android.util.AttributeSet +import android.view.View +import android.view.animation.Interpolator +import androidx.core.view.ViewCompat +import androidx.interpolator.view.animation.FastOutSlowInInterpolator +import androidx.viewpager.widget.ViewPager +import androidx.viewpager.widget.ViewPager.OnPageChangeListener +import org.kiwix.kiwixmobile.R +import java.util.Arrays +import kotlin.math.abs +import kotlin.math.max +import kotlin.math.min + +class CustomPageIndicator @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0 +) : View(context, attrs, defStyle), OnPageChangeListener, View.OnAttachStateChangeListener { + // configurable attributes + private val dotDiameter: Int + private val gap: Int + private val animDuration: Long + private val unselectedColour: Int + private val selectedColour: Int + + // derived from attributes + private val dotRadius: Float + private val halfDotRadius: Float + private val animHalfDuration: Long + private var dotTopY = 0f + private var dotCenterY = 0f + private var dotBottomY = 0f + + // ViewPager + private var viewPager: ViewPager? = null + + // state + private var pageCount = 0 + private var currentPage = 0 + private var previousPage = 0 + private var selectedDotX = 0f + private var selectedDotInPosition = false + private var dotCenterX: FloatArray? = null + private lateinit var joiningFractions: FloatArray + private var retreatingJoinX1 = 0f + private var retreatingJoinX2 = 0f + private lateinit var dotRevealFractions: FloatArray + private var attachedToWindow = false + private var pageChanging = false + + // drawing + private val unselectedPaint: Paint + private val selectedPaint: Paint + private val combinedUnselectedPath: Path + private val unselectedDotPath: Path + private val unselectedDotLeftPath: Path + private val unselectedDotRightPath: Path + private val rectF: RectF + + // animation + private var moveAnimation: ValueAnimator? = null + private val joiningAnimationSet: AnimatorSet? = null + private var retreatAnimation: PendingRetreatAnimator? = null + private lateinit var revealAnimations: Array + private val interpolator: Interpolator + + // working values for beziers + private var endX1 = 0f + private var endY1 = 0f + private var endX2 = 0f + private var endY2 = 0f + private var controlX1 = 0f + private var controlY1 = 0f + private var controlX2 = 0f + private var controlY2 = 0f + fun setViewPager(viewPager: ViewPager) { + this.viewPager = viewPager + viewPager.addOnPageChangeListener(this) + setPageCount(viewPager.adapter!!.count) + viewPager.adapter!!.registerDataSetObserver(object : DataSetObserver() { + override fun onChanged() { + setPageCount(this@CustomPageIndicator.viewPager!!.adapter!!.count) + } + }) + setCurrentPageImmediate() + } + + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + if (attachedToWindow) { + var fraction = positionOffset + val currentPosition = if (pageChanging) previousPage else currentPage + var leftDotPosition = position + // when swiping from #2 to #1 ViewPager reports position as 1 and a descending offset + // need to convert this into our left-dot-based 'coordinate space' + if (currentPosition != position) { + fraction = 1f - positionOffset + + // if user scrolls completely to next page then the position param updates to that + // new page but we're not ready to switch our 'current' page yet so adjust for that + if (fraction == 1f) { + leftDotPosition = min(currentPosition, position) + } + } + setJoiningFraction(leftDotPosition, fraction) + } + } + + override fun onPageSelected(position: Int) { + if (attachedToWindow) { + // this is the main event we're interested in! + setSelectedPage(position) + } else { + // when not attached, don't animate the move, just store immediately + setCurrentPageImmediate() + } + } + + override fun onPageScrollStateChanged(state: Int) { + // nothing to do + } + + private fun setPageCount(pages: Int) { + pageCount = pages + resetState() + requestLayout() + } + + private fun calculateDotPositions(width: Int, height: Int) { + val left = paddingLeft + val top = paddingTop + val right = width - paddingRight + val bottom = height - paddingBottom + val requiredWidth = requiredWidth + val startLeft = left + (right - left - requiredWidth) / 2 + dotRadius + dotCenterX = FloatArray(pageCount) + for (i in 0 until pageCount) { + dotCenterX!![i] = startLeft + i * (dotDiameter + gap) + } + // todo just top aligning for now… should make this smarter + dotTopY = top.toFloat() + dotCenterY = top + dotRadius + dotBottomY = (top + dotDiameter).toFloat() + setCurrentPageImmediate() + } + + private fun setCurrentPageImmediate() { + currentPage = if (viewPager != null) { + viewPager!!.currentItem + } else { + 0 + } + if (dotCenterX != null && dotCenterX!!.isNotEmpty() && + (moveAnimation == null || !moveAnimation!!.isStarted) + ) { + selectedDotX = dotCenterX!![currentPage] + } + } + + private fun resetState() { + joiningFractions = FloatArray(if (pageCount == 0) 0 else pageCount - 1) + Arrays.fill(joiningFractions, 0f) + dotRevealFractions = FloatArray(pageCount) + Arrays.fill(dotRevealFractions, 0f) + retreatingJoinX1 = INVALID_FRACTION + retreatingJoinX2 = INVALID_FRACTION + selectedDotInPosition = true + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val desiredHeight = desiredHeight + val height: Int = when (MeasureSpec.getMode(heightMeasureSpec)) { + MeasureSpec.EXACTLY -> MeasureSpec.getSize(heightMeasureSpec) + MeasureSpec.AT_MOST -> min(desiredHeight, MeasureSpec.getSize(heightMeasureSpec)) + else -> desiredHeight + } + val desiredWidth = desiredWidth + val width: Int = when (MeasureSpec.getMode(widthMeasureSpec)) { + MeasureSpec.EXACTLY -> MeasureSpec.getSize(widthMeasureSpec) + MeasureSpec.AT_MOST -> min(desiredWidth, MeasureSpec.getSize(widthMeasureSpec)) + else -> desiredWidth + } + setMeasuredDimension(width, height) + calculateDotPositions(width, height) + } + + private val desiredHeight: Int + get() = paddingTop + dotDiameter + paddingBottom + private val requiredWidth: Int + get() = pageCount * dotDiameter + (pageCount - 1) * gap + private val desiredWidth: Int + get() = paddingLeft + requiredWidth + paddingRight + + override fun onViewAttachedToWindow(view: View) { + attachedToWindow = true + } + + override fun onViewDetachedFromWindow(view: View) { + attachedToWindow = false + } + + override fun onDraw(canvas: Canvas) { + if (viewPager == null || pageCount == 0) return + drawUnselected(canvas) + drawSelected(canvas) + } + + private fun drawUnselected(canvas: Canvas) { + combinedUnselectedPath.rewind() + + // draw any settled, revealing or joining dots + for (page in 0 until pageCount) { + val nextXIndex = if (page == pageCount - 1) page else page + 1 + val unselectedPath = getUnselectedPath( + page, + dotCenterX!![page], + dotCenterX!![nextXIndex], + if (page == pageCount - 1) INVALID_FRACTION else joiningFractions[page], + dotRevealFractions[page] + ) + unselectedPath.addPath(combinedUnselectedPath) + combinedUnselectedPath.addPath(unselectedPath) + } + // draw any retreating joins + if (retreatingJoinX1 != INVALID_FRACTION) { + val retreatingJoinPath = retreatingJoinPath + combinedUnselectedPath.addPath(retreatingJoinPath) + } + canvas.drawPath(combinedUnselectedPath, unselectedPaint) + } + + private fun getUnselectedPath( + page: Int, + centerX: Float, + nextCenterX: Float, + joiningFraction: Float, + dotRevealFraction: Float + ): Path { + unselectedDotPath.rewind() + if ((joiningFraction == 0f || joiningFraction == INVALID_FRACTION) && + dotRevealFraction == 0f && !(page == currentPage && selectedDotInPosition) + ) { + + // case #1 – At rest + unselectedDotPath.addCircle(dotCenterX!![page], dotCenterY, dotRadius, Path.Direction.CW) + } + if (joiningFraction > 0f && joiningFraction <= 0.5f && retreatingJoinX1 == INVALID_FRACTION) { + calculateDotRightPath(centerX, nextCenterX, joiningFraction) + } + if (joiningFraction > 0.5f && joiningFraction < 1f && retreatingJoinX1 == INVALID_FRACTION) { + + // case #3 – Joining neighbour, combined curved + + // adjust the fraction so that it goes from 0.3 -> 1 to produce a more realistic 'join' + val adjustedFraction = (joiningFraction - 0.2f) * 1.25f + + // start in the bottom left + unselectedDotPath.moveTo(centerX, dotBottomY) + + // semi-circle to the top left + rectF[centerX - dotRadius, dotTopY, centerX + dotRadius] = dotBottomY + unselectedDotPath.arcTo(rectF, 90f, 180f, true) + + // bezier to the middle top of the join + endX1 = centerX + dotRadius + gap / 2 + endY1 = dotCenterY - adjustedFraction * dotRadius + controlX1 = endX1 - adjustedFraction * dotRadius + controlY1 = dotTopY + controlX2 = endX1 - (1 - adjustedFraction) * dotRadius + controlY2 = endY1 + unselectedDotPath.cubicTo( + controlX1, controlY1, + controlX2, controlY2, + endX1, endY1 + ) + + // bezier to the top right of the join + endX2 = nextCenterX + endY2 = dotTopY + controlX1 = endX1 + (1 - adjustedFraction) * dotRadius + controlY1 = endY1 + controlX2 = endX1 + adjustedFraction * dotRadius + controlY2 = dotTopY + unselectedDotPath.cubicTo( + controlX1, controlY1, + controlX2, controlY2, + endX2, endY2 + ) + + // semi-circle to the bottom right + rectF[nextCenterX - dotRadius, dotTopY, nextCenterX + dotRadius] = dotBottomY + unselectedDotPath.arcTo(rectF, 270f, 180f, true) + + // bezier to the middle bottom of the join + // endX1 stays the same + endY1 = dotCenterY + adjustedFraction * dotRadius + controlX1 = endX1 + adjustedFraction * dotRadius + controlY1 = dotBottomY + controlX2 = endX1 + (1 - adjustedFraction) * dotRadius + controlY2 = endY1 + unselectedDotPath.cubicTo( + controlX1, controlY1, + controlX2, controlY2, + endX1, endY1 + ) + + // bezier back to the start point in the bottom left + endX2 = centerX + endY2 = dotBottomY + controlX1 = endX1 - (1 - adjustedFraction) * dotRadius + controlY1 = endY1 + controlX2 = endX1 - adjustedFraction * dotRadius + controlY2 = endY2 + unselectedDotPath.cubicTo( + controlX1, controlY1, + controlX2, controlY2, + endX2, endY2 + ) + } + if (joiningFraction == 1f && retreatingJoinX1 == INVALID_FRACTION) { + + // case #4 Joining neighbour, combined straight technically we could use case 3 for this + // situation as well but assume that this is an optimization rather than faffing around + // with beziers just to draw a rounded rect + rectF[centerX - dotRadius, dotTopY, nextCenterX + dotRadius] = dotBottomY + unselectedDotPath.addRoundRect(rectF, dotRadius, dotRadius, Path.Direction.CW) + } + + // case #5 is handled by #getRetreatingJoinPath() + // this is done separately so that we can have a single retreating path spanning + // multiple dots and therefore animate it's movement smoothly + if (dotRevealFraction > MINIMAL_REVEAL) { + + // case #6 – previously hidden dot revealing + unselectedDotPath.addCircle( + centerX, dotCenterY, dotRevealFraction * dotRadius, + Path.Direction.CW + ) + } + return unselectedDotPath + } + + private fun calculateDotRightPath( + centerX: Float, + nextCenterX: Float, + joiningFraction: Float + ) { + // case #2 – Joining neighbour, still separate + + // start with the left dot + unselectedDotLeftPath.rewind() + + // start at the bottom center + unselectedDotLeftPath.moveTo(centerX, dotBottomY) + + // semi circle to the top center + rectF[centerX - dotRadius, dotTopY, centerX + dotRadius] = dotBottomY + unselectedDotLeftPath.arcTo(rectF, 90f, 180f, true) + + // cubic to the right middle + endX1 = centerX + dotRadius + joiningFraction * gap + endY1 = dotCenterY + controlX1 = centerX + halfDotRadius + controlY1 = dotTopY + controlX2 = endX1 + controlY2 = endY1 - halfDotRadius + unselectedDotLeftPath.cubicTo( + controlX1, controlY1, + controlX2, controlY2, + endX1, endY1 + ) + + // cubic back to the bottom center + endX2 = centerX + endY2 = dotBottomY + controlX1 = endX1 + controlY1 = endY1 + halfDotRadius + controlX2 = centerX + halfDotRadius + controlY2 = dotBottomY + unselectedDotLeftPath.cubicTo( + controlX1, controlY1, + controlX2, controlY2, + endX2, endY2 + ) + unselectedDotPath.addPath(unselectedDotLeftPath) + + // now do the next dot to the right + unselectedDotRightPath.rewind() + + // start at the bottom center + unselectedDotRightPath.moveTo(nextCenterX, dotBottomY) + + // semi circle to the top center + rectF[nextCenterX - dotRadius, dotTopY, nextCenterX + dotRadius] = dotBottomY + unselectedDotRightPath.arcTo(rectF, 90f, -180f, true) + + // cubic to the left middle + endX1 = nextCenterX - dotRadius - joiningFraction * gap + endY1 = dotCenterY + controlX1 = nextCenterX - halfDotRadius + controlY1 = dotTopY + controlX2 = endX1 + controlY2 = endY1 - halfDotRadius + unselectedDotRightPath.cubicTo( + controlX1, controlY1, + controlX2, controlY2, + endX1, endY1 + ) + + // cubic back to the bottom center + endX2 = nextCenterX + endY2 = dotBottomY + controlX1 = endX1 + controlY1 = endY1 + halfDotRadius + controlX2 = endX2 - halfDotRadius + controlY2 = dotBottomY + unselectedDotRightPath.cubicTo( + controlX1, controlY1, + controlX2, controlY2, + endX2, endY2 + ) + unselectedDotPath.addPath(unselectedDotRightPath) + } + + private val retreatingJoinPath: Path + get() { + unselectedDotPath.rewind() + rectF[retreatingJoinX1, dotTopY, retreatingJoinX2] = dotBottomY + unselectedDotPath.addRoundRect(rectF, dotRadius, dotRadius, Path.Direction.CW) + return unselectedDotPath + } + + private fun drawSelected(canvas: Canvas) { + canvas.drawCircle(selectedDotX, dotCenterY, dotRadius, selectedPaint) + } + + private fun setSelectedPage(now: Int) { + if (now == currentPage || dotCenterX == null || dotCenterX!!.size <= now) return + pageChanging = true + previousPage = currentPage + currentPage = now + val steps = abs(now - previousPage) + if (steps > 1) { + if (now > previousPage) { + for (i in 0 until steps) { + setJoiningFraction(previousPage + i, 1f) + } + } else { + for (i in -1 downTo -steps + 1) { + setJoiningFraction(previousPage + i, 1f) + } + } + } + + // create the anim to move the selected dot – this animator will kick off + // retreat animations when it has moved 75% of the way. + // The retreat animation in turn will kick of reveal anims when the + // retreat has passed any dots to be revealed + moveAnimation = createMoveSelectedAnimator(dotCenterX!![now], previousPage, now, steps) + moveAnimation!!.start() + } + + private fun createMoveSelectedAnimator( + moveTo: Float, + was: Int, + now: Int, + steps: Int + ): ValueAnimator { + + // create the actual move animator + val moveSelected = ValueAnimator.ofFloat(selectedDotX, moveTo) + + // also set up a pending retreat anim – this starts when the move is 75% complete + retreatAnimation = PendingRetreatAnimator( + was, now, steps, + if (now > was) + RightwardStartPredicate(moveTo - (moveTo - selectedDotX) * 0.25f) + else LeftwardStartPredicate( + moveTo + (selectedDotX - moveTo) * 0.25f + ) + ) + retreatAnimation!!.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + resetState() + pageChanging = false + } + }) + moveSelected.addUpdateListener { valueAnimator -> // todo avoid autoboxing + selectedDotX = valueAnimator.animatedValue as Float + retreatAnimation!!.startIfNecessary(selectedDotX) + ViewCompat.postInvalidateOnAnimation(this@CustomPageIndicator) + } + moveSelected.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator) { + // set a flag so that we continue to draw the unselected dot in the target position + // until the selected dot has finished moving into place + selectedDotInPosition = false + } + + override fun onAnimationEnd(animation: Animator) { + // set a flag when anim finishes so that we don't draw both selected & unselected + // page dots + selectedDotInPosition = true + } + }) + // slightly delay the start to give the joins a chance to run + // unless dot isn't in position yet – then don't delay! + moveSelected.startDelay = if (selectedDotInPosition) animDuration / 4L else 0L + moveSelected.duration = animDuration * 3L / 4L + moveSelected.interpolator = interpolator + return moveSelected + } + + private fun setJoiningFraction(leftDot: Int, fraction: Float) { + if (leftDot < joiningFractions.size) { + joiningFractions[leftDot] = fraction + ViewCompat.postInvalidateOnAnimation(this) + } + } + + private fun clearJoiningFractions() { + Arrays.fill(joiningFractions, 0f) + ViewCompat.postInvalidateOnAnimation(this) + } + + private fun setDotRevealFraction(dot: Int, fraction: Float) { + if (dot < dotRevealFractions.size) { + dotRevealFractions[dot] = fraction + } + ViewCompat.postInvalidateOnAnimation(this) + } + + private fun cancelJoiningAnimations() { + if (joiningAnimationSet != null && joiningAnimationSet.isRunning) { + joiningAnimationSet.cancel() + } + } + + /** + * A [ValueAnimator] that starts once a given predicate returns true. + */ + abstract inner class PendingStartAnimator(private var predicate: StartPredicate) : + ValueAnimator() { + private var hasStarted = false + fun startIfNecessary(currentValue: Float) { + if (!hasStarted && predicate.shouldStart(currentValue)) { + start() + hasStarted = true + } + } + } + + inner class PendingRetreatAnimator(was: Int, now: Int, steps: Int, predicate: StartPredicate) : + PendingStartAnimator(predicate) { + init { + duration = animHalfDuration + interpolator = interpolator + + // work out the start/end values of the retreating join from the direction we're + // travelling in. Also look at the current selected dot position, i.e. we're moving on + // before a prior anim has finished. + val initialX1 = if (now > was) min( + dotCenterX!![was], + selectedDotX + ) - dotRadius else dotCenterX!![now] - dotRadius + val finalX1 = if (now > was) dotCenterX!![now] - dotRadius else dotCenterX!![now] - dotRadius + val initialX2 = if (now > was) dotCenterX!![now] + dotRadius else max( + dotCenterX!![was], selectedDotX + ) + dotRadius + val finalX2 = if (now > was) dotCenterX!![now] + dotRadius else dotCenterX!![now] + dotRadius + revealAnimations = arrayOfNulls(steps) + // hold on to the indexes of the dots that will be hidden by the retreat so that + // we can initialize their revealFraction's i.e. make sure they're hidden while the + // reveal animation runs + val dotsToHide = IntArray(steps) + if (initialX1 != finalX1) { // rightward retreat + setFloatValues(initialX1, finalX1) + // create the reveal animations that will run when the retreat passes them + for (i in 0 until steps) { + revealAnimations[i] = PendingRevealAnimator( + was + i, + RightwardStartPredicate(dotCenterX!![was + i]) + ) + dotsToHide[i] = was + i + } + addUpdateListener { valueAnimator -> // todo avoid autoboxing + retreatingJoinX1 = valueAnimator.animatedValue as Float + ViewCompat.postInvalidateOnAnimation(this@CustomPageIndicator) + // start any reveal animations if we've passed them + for (pendingReveal in revealAnimations) { + pendingReveal!!.startIfNecessary(retreatingJoinX1) + } + } + } else { // (initialX2 != finalX2) leftward retreat + setFloatValues(initialX2, finalX2) + // create the reveal animations that will run when the retreat passes them + for (i in 0 until steps) { + revealAnimations[i] = PendingRevealAnimator( + was - i, + LeftwardStartPredicate(dotCenterX!![was - i]) + ) + dotsToHide[i] = was - i + } + addUpdateListener { valueAnimator -> // todo avoid autoboxing + retreatingJoinX2 = valueAnimator.animatedValue as Float + ViewCompat.postInvalidateOnAnimation(this@CustomPageIndicator) + // start any reveal animations if we've passed them + for (pendingReveal in revealAnimations) { + pendingReveal!!.startIfNecessary(retreatingJoinX2) + } + } + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator) { + cancelJoiningAnimations() + clearJoiningFractions() + // we need to set this so that the dots are hidden until the reveal anim runs + for (dot in dotsToHide) { + setDotRevealFraction(dot, MINIMAL_REVEAL) + } + retreatingJoinX1 = initialX1 + retreatingJoinX2 = initialX2 + ViewCompat.postInvalidateOnAnimation(this@CustomPageIndicator) + } + + override fun onAnimationEnd(animation: Animator) { + retreatingJoinX1 = INVALID_FRACTION + retreatingJoinX2 = INVALID_FRACTION + ViewCompat.postInvalidateOnAnimation(this@CustomPageIndicator) + } + }) + } + } + + /** + * An Animator that animates a given dot's revealFraction i.e. scales it up + */ + inner class PendingRevealAnimator(dot: Int, predicate: StartPredicate) : + PendingStartAnimator(predicate) { + private val dot: Int + + init { + setFloatValues(MINIMAL_REVEAL, 1f) + this.dot = dot + duration = animHalfDuration + interpolator = interpolator + addUpdateListener { valueAnimator -> // todo avoid autoboxing + setDotRevealFraction( + this@PendingRevealAnimator.dot, + valueAnimator.animatedValue as Float + ) + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + setDotRevealFraction(this@PendingRevealAnimator.dot, 0f) + ViewCompat.postInvalidateOnAnimation(this@CustomPageIndicator) + } + }) + } + } + + /** + * A predicate used to start an animation when a test passes + */ + abstract inner class StartPredicate(protected var thresholdValue: Float) { + abstract fun shouldStart(currentValue: Float): Boolean + } + + /** + * A predicate used to start an animation when a given value is greater than a threshold + */ + inner class RightwardStartPredicate(thresholdValue: Float) : StartPredicate(thresholdValue) { + override fun shouldStart(currentValue: Float): Boolean = currentValue > thresholdValue + } + + /** + * A predicate used to start an animation then a given value is less than a threshold + */ + inner class LeftwardStartPredicate(thresholdValue: Float) : StartPredicate(thresholdValue) { + override fun shouldStart(currentValue: Float): Boolean = currentValue < thresholdValue + } + + public override fun onRestoreInstanceState(state: Parcelable) { + val savedState = state as SavedState + super.onRestoreInstanceState(savedState.superState) + currentPage = savedState.currentPage + requestLayout() + } + + public override fun onSaveInstanceState(): Parcelable? { + val superState = super.onSaveInstanceState() + val savedState = SavedState(superState) + savedState.currentPage = currentPage + return savedState + } + + internal class SavedState : BaseSavedState { + var currentPage = 0 + + constructor(superState: Parcelable?) : super(superState) + private constructor(`in`: Parcel) : super(`in`) { + currentPage = `in`.readInt() + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeInt(currentPage) + } + + companion object { + @JvmField val CREATOR: Parcelable.Creator = + object : Parcelable.Creator { + override fun createFromParcel(`in`: Parcel): SavedState? = SavedState(`in`) + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + } + } + + companion object { + // defaults + private const val DEFAULT_DOT_SIZE = 8 // dp + private const val DEFAULT_GAP = 12 // dp + private const val DEFAULT_ANIM_DURATION = 400 // ms + private const val DEFAULT_UNSELECTED_COLOUR = -0x7f000001 // 50% white + private const val DEFAULT_SELECTED_COLOUR = -0x1 // 100% white + + // constants + private const val INVALID_FRACTION = -1f + private const val MINIMAL_REVEAL = 0.00001f + } + + init { + val density = context.resources.displayMetrics.density.toInt() + + // Load attributes + val a = getContext().obtainStyledAttributes( + attrs, R.styleable.CustomPageIndicator, defStyle, 0 + ) + dotDiameter = a.getDimensionPixelSize( + R.styleable.CustomPageIndicator_ipi_dotDiameter, + DEFAULT_DOT_SIZE * density + ) + dotRadius = (dotDiameter / 2).toFloat() + halfDotRadius = dotRadius / 2 + gap = a.getDimensionPixelSize( + R.styleable.CustomPageIndicator_ipi_dotGap, + DEFAULT_GAP * density + ) + animDuration = a.getInteger( + R.styleable.CustomPageIndicator_ipi_animationDuration, + DEFAULT_ANIM_DURATION + ).toLong() + animHalfDuration = animDuration / 2 + unselectedColour = a.getColor( + R.styleable.CustomPageIndicator_ipi_pageIndicatorColor, + DEFAULT_UNSELECTED_COLOUR + ) + selectedColour = a.getColor( + R.styleable.CustomPageIndicator_ipi_currentPageIndicatorColor, + DEFAULT_SELECTED_COLOUR + ) + a.recycle() + unselectedPaint = Paint(Paint.ANTI_ALIAS_FLAG) + unselectedPaint.color = unselectedColour + selectedPaint = Paint(Paint.ANTI_ALIAS_FLAG) + selectedPaint.color = selectedColour + interpolator = FastOutSlowInInterpolator() + + // create paths & rect now – reuse & rewind later + combinedUnselectedPath = Path() + unselectedDotPath = Path() + unselectedDotLeftPath = Path() + unselectedDotRightPath = Path() + rectF = RectF() + addOnAttachStateChangeListener(this) + } +} diff --git a/app/src/main/res/layout/fragment_intro.xml b/app/src/main/res/layout/fragment_intro.xml index ba9b9b357..7341c3ebd 100644 --- a/app/src/main/res/layout/fragment_intro.xml +++ b/app/src/main/res/layout/fragment_intro.xml @@ -25,7 +25,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> - + + + + + + + + + + diff --git a/buildSrc/src/main/kotlin/Libs.kt b/buildSrc/src/main/kotlin/Libs.kt index cd1d72a66..5ff4de5b7 100644 --- a/buildSrc/src/main/kotlin/Libs.kt +++ b/buildSrc/src/main/kotlin/Libs.kt @@ -15,6 +15,12 @@ object Libs { const val select_folder_document_file = "androidx.documentfile:documentfile:" + Versions.document_file_version + /** + * https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout#kts + */ + const val swipe_refresh_layout = + "androidx.swiperefreshlayout:swiperefreshlayout:" + Versions.swipe_refresh_layout_version + /** * https://github.com/Kotlin/kotlinx.coroutines */ @@ -288,9 +294,6 @@ object Libs { const val javax_annotation_api: String = "javax.annotation:javax.annotation-api:" + Versions.javax_annotation_api - const val ink_page_indicator: String = "com.pacioianu.david:ink-page-indicator:" + - Versions.ink_page_indicator - /** * http://github.com/square/leakcanary/ */ diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index c31fa64ad..af60bc56f 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -12,6 +12,8 @@ import org.gradle.plugin.use.PluginDependencySpec */ object Versions { + const val swipe_refresh_layout_version: String = "1.1.0" + const val document_file_version: String = "1.0.1" const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.4.1" @@ -50,8 +52,6 @@ object Versions { const val javax_annotation_api: String = "1.3.2" - const val ink_page_indicator: String = "1.3.0" - const val leakcanary_android: String = "2.5" const val constraintlayout: String = "2.0.4" diff --git a/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt b/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt index 2cdedc4f3..a551f97d5 100644 --- a/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt +++ b/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt @@ -187,6 +187,7 @@ class AllProjectConfigurer { implementation(Libs.fetch) implementation(Libs.rxandroid) implementation(Libs.rxjava) + implementation(Libs.swipe_refresh_layout) implementation(Libs.preference_ktx) } } From 24a036c3d3164db3b7c74a2d2d4bb68ce8d91df1 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Sat, 14 May 2022 12:24:13 +0530 Subject: [PATCH 2/4] library code migrated to our project --- .../kiwixmobile/intro/CustomPageIndicator.kt | 340 +++++++++--------- 1 file changed, 172 insertions(+), 168 deletions(-) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt b/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt index 8b4d205ef..f178e70bc 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt @@ -42,6 +42,13 @@ import kotlin.math.abs import kotlin.math.max import kotlin.math.min +/** + * Author DavidPacioianu + * Reference From + * https://github.com/DavidPacioianu/InkPageIndicator + * We refactor this java file to kotlin file + */ + class CustomPageIndicator @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -161,7 +168,7 @@ class CustomPageIndicator @JvmOverloads constructor( val top = paddingTop val right = width - paddingRight val bottom = height - paddingBottom - val requiredWidth = requiredWidth + val requiredWidth = getRequiredWidth() val startLeft = left + (right - left - requiredWidth) / 2 + dotRadius dotCenterX = FloatArray(pageCount) for (i in 0 until pageCount) { @@ -180,10 +187,10 @@ class CustomPageIndicator @JvmOverloads constructor( } else { 0 } - if (dotCenterX != null && dotCenterX!!.isNotEmpty() && - (moveAnimation == null || !moveAnimation!!.isStarted) + if (dotCenterX != null && dotCenterX!!.isNotEmpty() ) { - selectedDotX = dotCenterX!![currentPage] + if (moveAnimation == null || !moveAnimation!!.isStarted) + selectedDotX = dotCenterX!![currentPage] } } @@ -198,13 +205,13 @@ class CustomPageIndicator @JvmOverloads constructor( } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - val desiredHeight = desiredHeight + val desiredHeight = getDesiredHeight() val height: Int = when (MeasureSpec.getMode(heightMeasureSpec)) { MeasureSpec.EXACTLY -> MeasureSpec.getSize(heightMeasureSpec) MeasureSpec.AT_MOST -> min(desiredHeight, MeasureSpec.getSize(heightMeasureSpec)) else -> desiredHeight } - val desiredWidth = desiredWidth + val desiredWidth = getDesiredWidth() val width: Int = when (MeasureSpec.getMode(widthMeasureSpec)) { MeasureSpec.EXACTLY -> MeasureSpec.getSize(widthMeasureSpec) MeasureSpec.AT_MOST -> min(desiredWidth, MeasureSpec.getSize(widthMeasureSpec)) @@ -214,12 +221,14 @@ class CustomPageIndicator @JvmOverloads constructor( calculateDotPositions(width, height) } - private val desiredHeight: Int - get() = paddingTop + dotDiameter + paddingBottom - private val requiredWidth: Int - get() = pageCount * dotDiameter + (pageCount - 1) * gap - private val desiredWidth: Int - get() = paddingLeft + requiredWidth + paddingRight + private fun getDesiredHeight(): Int = + paddingTop + dotDiameter + paddingBottom + + private fun getRequiredWidth(): Int = + pageCount * dotDiameter + (pageCount - 1) * gap + + private fun getDesiredWidth(): Int = + paddingLeft + getRequiredWidth() + paddingRight override fun onViewAttachedToWindow(view: View) { attachedToWindow = true @@ -267,95 +276,27 @@ class CustomPageIndicator @JvmOverloads constructor( dotRevealFraction: Float ): Path { unselectedDotPath.rewind() - if ((joiningFraction == 0f || joiningFraction == INVALID_FRACTION) && - dotRevealFraction == 0f && !(page == currentPage && selectedDotInPosition) + if ((joiningFraction == selectedFactor || joiningFraction == INVALID_FRACTION) ) { - - // case #1 – At rest - unselectedDotPath.addCircle(dotCenterX!![page], dotCenterY, dotRadius, Path.Direction.CW) + if (dotRevealFraction == selectedFactor && !(page == currentPage && selectedDotInPosition)) { + // case #1 – At rest + unselectedDotPath.addCircle(dotCenterX!![page], dotCenterY, dotRadius, Path.Direction.CW) + } } - if (joiningFraction > 0f && joiningFraction <= 0.5f && retreatingJoinX1 == INVALID_FRACTION) { - calculateDotRightPath(centerX, nextCenterX, joiningFraction) - } - if (joiningFraction > 0.5f && joiningFraction < 1f && retreatingJoinX1 == INVALID_FRACTION) { - - // case #3 – Joining neighbour, combined curved - - // adjust the fraction so that it goes from 0.3 -> 1 to produce a more realistic 'join' - val adjustedFraction = (joiningFraction - 0.2f) * 1.25f - - // start in the bottom left - unselectedDotPath.moveTo(centerX, dotBottomY) - - // semi-circle to the top left - rectF[centerX - dotRadius, dotTopY, centerX + dotRadius] = dotBottomY - unselectedDotPath.arcTo(rectF, 90f, 180f, true) - - // bezier to the middle top of the join - endX1 = centerX + dotRadius + gap / 2 - endY1 = dotCenterY - adjustedFraction * dotRadius - controlX1 = endX1 - adjustedFraction * dotRadius - controlY1 = dotTopY - controlX2 = endX1 - (1 - adjustedFraction) * dotRadius - controlY2 = endY1 - unselectedDotPath.cubicTo( - controlX1, controlY1, - controlX2, controlY2, - endX1, endY1 - ) - - // bezier to the top right of the join - endX2 = nextCenterX - endY2 = dotTopY - controlX1 = endX1 + (1 - adjustedFraction) * dotRadius - controlY1 = endY1 - controlX2 = endX1 + adjustedFraction * dotRadius - controlY2 = dotTopY - unselectedDotPath.cubicTo( - controlX1, controlY1, - controlX2, controlY2, - endX2, endY2 - ) - - // semi-circle to the bottom right - rectF[nextCenterX - dotRadius, dotTopY, nextCenterX + dotRadius] = dotBottomY - unselectedDotPath.arcTo(rectF, 270f, 180f, true) - - // bezier to the middle bottom of the join - // endX1 stays the same - endY1 = dotCenterY + adjustedFraction * dotRadius - controlX1 = endX1 + adjustedFraction * dotRadius - controlY1 = dotBottomY - controlX2 = endX1 + (1 - adjustedFraction) * dotRadius - controlY2 = endY1 - unselectedDotPath.cubicTo( - controlX1, controlY1, - controlX2, controlY2, - endX1, endY1 - ) - - // bezier back to the start point in the bottom left - endX2 = centerX - endY2 = dotBottomY - controlX1 = endX1 - (1 - adjustedFraction) * dotRadius - controlY1 = endY1 - controlX2 = endX1 - adjustedFraction * dotRadius - controlY2 = endY2 - unselectedDotPath.cubicTo( - controlX1, controlY1, - controlX2, controlY2, - endX2, endY2 - ) + unselectedDotRightPath(centerX, joiningFraction, nextCenterX) + if (joiningFraction > smallUnSelectedFactor && + joiningFraction < unselectedFactor && + retreatingJoinX1 == INVALID_FRACTION + ) { + joinNeighbour(joiningFraction, centerX, nextCenterX) } if (joiningFraction == 1f && retreatingJoinX1 == INVALID_FRACTION) { - // case #4 Joining neighbour, combined straight technically we could use case 3 for this // situation as well but assume that this is an optimization rather than faffing around // with beziers just to draw a rounded rect rectF[centerX - dotRadius, dotTopY, nextCenterX + dotRadius] = dotBottomY unselectedDotPath.addRoundRect(rectF, dotRadius, dotRadius, Path.Direction.CW) } - // case #5 is handled by #getRetreatingJoinPath() // this is done separately so that we can have a single retreating path spanning // multiple dots and therefore animate it's movement smoothly @@ -370,86 +311,137 @@ class CustomPageIndicator @JvmOverloads constructor( return unselectedDotPath } - private fun calculateDotRightPath( + private fun joinNeighbour( + joiningFraction: Float, centerX: Float, - nextCenterX: Float, - joiningFraction: Float + nextCenterX: Float ) { - // case #2 – Joining neighbour, still separate - - // start with the left dot - unselectedDotLeftPath.rewind() - - // start at the bottom center - unselectedDotLeftPath.moveTo(centerX, dotBottomY) - - // semi circle to the top center + // case #3 – Joining neighbour, combined curved + // adjust the fraction so that it goes from 0.3 -> 1 to produce a more realistic 'join' + val adjustedFraction = + (joiningFraction - zeroPointTwoFractionConst) * onePointTwoFiveFractionConst + // start in the bottom left + unselectedDotPath.moveTo(centerX, dotBottomY) + // semi-circle to the top left rectF[centerX - dotRadius, dotTopY, centerX + dotRadius] = dotBottomY - unselectedDotLeftPath.arcTo(rectF, 90f, 180f, true) - - // cubic to the right middle - endX1 = centerX + dotRadius + joiningFraction * gap - endY1 = dotCenterY - controlX1 = centerX + halfDotRadius + unselectedDotPath.arcTo(rectF, startAngle, sweepAngle, true) + // bezier to the middle top of the join + endX1 = centerX + dotRadius + gap / 2 + endY1 = dotCenterY - adjustedFraction * dotRadius + controlX1 = endX1 - adjustedFraction * dotRadius controlY1 = dotTopY - controlX2 = endX1 - controlY2 = endY1 - halfDotRadius - unselectedDotLeftPath.cubicTo( - controlX1, controlY1, - controlX2, controlY2, - endX1, endY1 + controlX2 = endX1 - (1 - adjustedFraction) * dotRadius + controlY2 = endY1 + unselectedDotPath.cubicTo( + controlX1, controlY1, controlX2, controlY2, endX1, endY1 ) - - // cubic back to the bottom center + // bezier to the top right of the join + endX2 = nextCenterX + endY2 = dotTopY + controlX1 = endX1 + (1 - adjustedFraction) * dotRadius + controlY1 = endY1 + controlX2 = endX1 + adjustedFraction * dotRadius + controlY2 = dotTopY + unselectedDotPath.cubicTo( + controlX1, controlY1, controlX2, controlY2, endX2, endY2 + ) + // semi-circle to the bottom right + rectF[nextCenterX - dotRadius, dotTopY, nextCenterX + dotRadius] = dotBottomY + unselectedDotPath.arcTo( + rectF, + startAngle + sweepAngle, + sweepAngle, + true + ) + // bezier to the middle bottom of the join + // endX1 stays the same + endY1 = dotCenterY + adjustedFraction * dotRadius + controlX1 = endX1 + adjustedFraction * dotRadius + controlY1 = dotBottomY + controlX2 = endX1 + (1 - adjustedFraction) * dotRadius + controlY2 = endY1 + unselectedDotPath.cubicTo( + controlX1, controlY1, controlX2, controlY2, endX1, endY1 + ) + // bezier back to the start point in the bottom left endX2 = centerX endY2 = dotBottomY - controlX1 = endX1 - controlY1 = endY1 + halfDotRadius - controlX2 = centerX + halfDotRadius - controlY2 = dotBottomY - unselectedDotLeftPath.cubicTo( - controlX1, controlY1, - controlX2, controlY2, - endX2, endY2 + controlX1 = endX1 - (1 - adjustedFraction) * dotRadius + controlY1 = endY1 + controlX2 = endX1 - adjustedFraction * dotRadius + controlY2 = endY2 + unselectedDotPath.cubicTo( + controlX1, controlY1, controlX2, controlY2, endX2, endY2 ) - unselectedDotPath.addPath(unselectedDotLeftPath) + } - // now do the next dot to the right - unselectedDotRightPath.rewind() - - // start at the bottom center - unselectedDotRightPath.moveTo(nextCenterX, dotBottomY) - - // semi circle to the top center - rectF[nextCenterX - dotRadius, dotTopY, nextCenterX + dotRadius] = dotBottomY - unselectedDotRightPath.arcTo(rectF, 90f, -180f, true) - - // cubic to the left middle - endX1 = nextCenterX - dotRadius - joiningFraction * gap - endY1 = dotCenterY - controlX1 = nextCenterX - halfDotRadius - controlY1 = dotTopY - controlX2 = endX1 - controlY2 = endY1 - halfDotRadius - unselectedDotRightPath.cubicTo( - controlX1, controlY1, - controlX2, controlY2, - endX1, endY1 - ) - - // cubic back to the bottom center - endX2 = nextCenterX - endY2 = dotBottomY - controlX1 = endX1 - controlY1 = endY1 + halfDotRadius - controlX2 = endX2 - halfDotRadius - controlY2 = dotBottomY - unselectedDotRightPath.cubicTo( - controlX1, controlY1, - controlX2, controlY2, - endX2, endY2 - ) - unselectedDotPath.addPath(unselectedDotRightPath) + private fun unselectedDotRightPath( + centerX: Float, + joiningFraction: Float, + nextCenterX: Float + ) { + if (joiningFraction > selectedFactor && + joiningFraction <= smallUnSelectedFactor && + retreatingJoinX1 == INVALID_FRACTION + ) { + // case #2 – Joining neighbour, still separate + // start with the left dot + unselectedDotLeftPath.rewind() + // start at the bottom center + unselectedDotLeftPath.moveTo(centerX, dotBottomY) + // semi circle to the top center + rectF[centerX - dotRadius, dotTopY, centerX + dotRadius] = dotBottomY + unselectedDotLeftPath.arcTo(rectF, startAngle, sweepAngle, true) + // cubic to the right middle + endX1 = centerX + dotRadius + joiningFraction * gap + endY1 = dotCenterY + controlX1 = centerX + halfDotRadius + controlY1 = dotTopY + controlX2 = endX1 + controlY2 = endY1 - halfDotRadius + unselectedDotLeftPath.cubicTo( + controlX1, controlY1, controlX2, controlY2, endX1, endY1 + ) + // cubic back to the bottom center + endX2 = centerX + endY2 = dotBottomY + controlX1 = endX1 + controlY1 = endY1 + halfDotRadius + controlX2 = centerX + halfDotRadius + controlY2 = dotBottomY + unselectedDotLeftPath.cubicTo( + controlX1, controlY1, controlX2, controlY2, endX2, endY2 + ) + unselectedDotPath.addPath(unselectedDotLeftPath) + // now do the next dot to the right + unselectedDotRightPath.rewind() + // start at the bottom center + unselectedDotRightPath.moveTo(nextCenterX, dotBottomY) + // semi circle to the top center + rectF[nextCenterX - dotRadius, dotTopY, nextCenterX + dotRadius] = dotBottomY + unselectedDotRightPath.arcTo(rectF, startAngle, negativeSweepAngle, true) + // cubic to the left middle + endX1 = nextCenterX - dotRadius - joiningFraction * gap + endY1 = dotCenterY + controlX1 = nextCenterX - halfDotRadius + controlY1 = dotTopY + controlX2 = endX1 + controlY2 = endY1 - halfDotRadius + unselectedDotRightPath.cubicTo( + controlX1, controlY1, controlX2, controlY2, endX1, endY1 + ) + // cubic back to the bottom center + endX2 = nextCenterX + endY2 = dotBottomY + controlX1 = endX1 + controlY1 = endY1 + halfDotRadius + controlX2 = endX2 - halfDotRadius + controlY2 = dotBottomY + unselectedDotRightPath.cubicTo( + controlX1, controlY1, controlX2, controlY2, endX2, endY2 + ) + unselectedDotPath.addPath(unselectedDotRightPath) + } } private val retreatingJoinPath: Path @@ -504,9 +496,9 @@ class CustomPageIndicator @JvmOverloads constructor( retreatAnimation = PendingRetreatAnimator( was, now, steps, if (now > was) - RightwardStartPredicate(moveTo - (moveTo - selectedDotX) * 0.25f) + RightwardStartPredicate(moveTo - (moveTo - selectedDotX) * thresholdMultiplier) else LeftwardStartPredicate( - moveTo + (selectedDotX - moveTo) * 0.25f + moveTo + (selectedDotX - moveTo) * thresholdMultiplier ) ) retreatAnimation!!.addListener(object : AnimatorListenerAdapter() { @@ -535,8 +527,8 @@ class CustomPageIndicator @JvmOverloads constructor( }) // slightly delay the start to give the joins a chance to run // unless dot isn't in position yet – then don't delay! - moveSelected.startDelay = if (selectedDotInPosition) animDuration / 4L else 0L - moveSelected.duration = animDuration * 3L / 4L + moveSelected.startDelay = if (selectedDotInPosition) animDuration / fourLong else zeroLong + moveSelected.duration = animDuration * threeLong / fourLong moveSelected.interpolator = interpolator return moveSelected } @@ -728,8 +720,8 @@ class CustomPageIndicator @JvmOverloads constructor( var currentPage = 0 constructor(superState: Parcelable?) : super(superState) - private constructor(`in`: Parcel) : super(`in`) { - currentPage = `in`.readInt() + private constructor(data: Parcel) : super(data) { + currentPage = data.readInt() } override fun writeToParcel(dest: Parcel, flags: Int) { @@ -758,6 +750,18 @@ class CustomPageIndicator @JvmOverloads constructor( // constants private const val INVALID_FRACTION = -1f private const val MINIMAL_REVEAL = 0.00001f + private const val zeroPointTwoFractionConst: Float = 0.2f + private const val onePointTwoFiveFractionConst: Float = 1.25f + private const val unselectedFactor: Float = 1f + private const val smallUnSelectedFactor: Float = 0.5f + private const val selectedFactor: Float = 0f + private const val threeLong: Long = 3L + private const val zeroLong: Long = 0L + private const val fourLong: Long = 4L + private const val thresholdMultiplier: Float = 0.25f + private const val negativeSweepAngle: Float = -180f + private const val sweepAngle: Float = 180f + private const val startAngle: Float = 90f } init { From c0a102a99c081935e3472a8e8818b61bf3052206 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 17 May 2022 16:18:33 +0530 Subject: [PATCH 3/4] remove migrated lib --- app/build.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ccf06054a..db44ea235 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -87,6 +87,4 @@ play { dependencies { implementation(Libs.squidb) - implementation(Libs.squidb_annotations) - add("kapt", Libs.squidb_processor) } From cadf1265a8883dcd80441bfa93bd6edbe03127e3 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Wed, 18 May 2022 18:39:04 +0530 Subject: [PATCH 4/4] Removing swipe refresh layout implementation --- buildSrc/src/main/kotlin/Libs.kt | 6 ------ buildSrc/src/main/kotlin/Versions.kt | 2 -- buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt | 1 - 3 files changed, 9 deletions(-) diff --git a/buildSrc/src/main/kotlin/Libs.kt b/buildSrc/src/main/kotlin/Libs.kt index 5ff4de5b7..6d5176e10 100644 --- a/buildSrc/src/main/kotlin/Libs.kt +++ b/buildSrc/src/main/kotlin/Libs.kt @@ -15,12 +15,6 @@ object Libs { const val select_folder_document_file = "androidx.documentfile:documentfile:" + Versions.document_file_version - /** - * https://developer.android.com/jetpack/androidx/releases/swiperefreshlayout#kts - */ - const val swipe_refresh_layout = - "androidx.swiperefreshlayout:swiperefreshlayout:" + Versions.swipe_refresh_layout_version - /** * https://github.com/Kotlin/kotlinx.coroutines */ diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index af60bc56f..93041465c 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -12,8 +12,6 @@ import org.gradle.plugin.use.PluginDependencySpec */ object Versions { - const val swipe_refresh_layout_version: String = "1.1.0" - const val document_file_version: String = "1.0.1" const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.4.1" diff --git a/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt b/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt index a551f97d5..2cdedc4f3 100644 --- a/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt +++ b/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt @@ -187,7 +187,6 @@ class AllProjectConfigurer { implementation(Libs.fetch) implementation(Libs.rxandroid) implementation(Libs.rxjava) - implementation(Libs.swipe_refresh_layout) implementation(Libs.preference_ktx) } }