Merge pull request #113 from esensar/improvement/page-swiping
Improve page change behavior
This commit is contained in:
commit
a1b459f396
|
@ -60,6 +60,8 @@ class MainActivity : SimpleActivity(), FlingListener {
|
|||
private var mMoveGestureThreshold = 0
|
||||
private var mIgnoreUpEvent = false
|
||||
private var mIgnoreMoveEvents = false
|
||||
private var mIgnoreXMoveEvents = false
|
||||
private var mIgnoreYMoveEvents = false
|
||||
private var mLongPressedIcon: HomeScreenGridItem? = null
|
||||
private var mOpenPopupMenu: PopupMenu? = null
|
||||
private var mCachedLaunchers = ArrayList<AppLauncher>()
|
||||
|
@ -362,13 +364,20 @@ class MainActivity : SimpleActivity(), FlingListener {
|
|||
|
||||
if (hasFingerMoved && !mIgnoreMoveEvents) {
|
||||
val diffY = mTouchDownY - event.y
|
||||
val diffX = mTouchDownX - event.x
|
||||
|
||||
if (isWidgetsFragmentExpanded()) {
|
||||
val newY = mWidgetsFragmentY - diffY
|
||||
binding.widgetsFragment.root.y = Math.min(Math.max(0f, newY), mScreenHeight.toFloat())
|
||||
} else if (mLongPressedIcon == null) {
|
||||
val newY = mAllAppsFragmentY - diffY
|
||||
binding.allAppsFragment.root.y = Math.min(Math.max(0f, newY), mScreenHeight.toFloat())
|
||||
if (abs(diffY) > abs(diffX) && !mIgnoreYMoveEvents) {
|
||||
mIgnoreXMoveEvents = true
|
||||
if (isWidgetsFragmentExpanded()) {
|
||||
val newY = mWidgetsFragmentY - diffY
|
||||
binding.widgetsFragment.root.y = Math.min(Math.max(0f, newY), mScreenHeight.toFloat())
|
||||
} else if (mLongPressedIcon == null) {
|
||||
val newY = mAllAppsFragmentY - diffY
|
||||
binding.allAppsFragment.root.y = Math.min(Math.max(0f, newY), mScreenHeight.toFloat())
|
||||
}
|
||||
} else if (abs(diffX) > abs(diffY) && !mIgnoreXMoveEvents) {
|
||||
mIgnoreYMoveEvents = true
|
||||
binding.homeScreenGrid.root.setSwipeMovement(diffX)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,18 +395,27 @@ class MainActivity : SimpleActivity(), FlingListener {
|
|||
binding.homeScreenGrid.root.itemDraggingStopped()
|
||||
|
||||
if (!mIgnoreUpEvent) {
|
||||
if (binding.allAppsFragment.root.y < mScreenHeight * 0.5) {
|
||||
showFragment(binding.allAppsFragment)
|
||||
} else if (isAllAppsFragmentExpanded()) {
|
||||
hideFragment(binding.allAppsFragment)
|
||||
if (!mIgnoreYMoveEvents) {
|
||||
if (binding.allAppsFragment.root.y < mScreenHeight * 0.5) {
|
||||
showFragment(binding.allAppsFragment)
|
||||
} else if (isAllAppsFragmentExpanded()) {
|
||||
hideFragment(binding.allAppsFragment)
|
||||
}
|
||||
|
||||
if (binding.widgetsFragment.root.y < mScreenHeight * 0.5) {
|
||||
showFragment(binding.widgetsFragment)
|
||||
} else if (isWidgetsFragmentExpanded()) {
|
||||
hideFragment(binding.widgetsFragment)
|
||||
}
|
||||
}
|
||||
|
||||
if (binding.widgetsFragment.root.y < mScreenHeight * 0.5) {
|
||||
showFragment(binding.widgetsFragment)
|
||||
} else if (isWidgetsFragmentExpanded()) {
|
||||
hideFragment(binding.widgetsFragment)
|
||||
if (!mIgnoreXMoveEvents) {
|
||||
binding.homeScreenGrid.root.finalizeSwipe()
|
||||
}
|
||||
}
|
||||
|
||||
mIgnoreXMoveEvents = false
|
||||
mIgnoreYMoveEvents = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -731,6 +749,10 @@ class MainActivity : SimpleActivity(), FlingListener {
|
|||
}
|
||||
|
||||
override fun onFlingUp() {
|
||||
if (mIgnoreYMoveEvents) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isWidgetsFragmentExpanded()) {
|
||||
mIgnoreUpEvent = true
|
||||
showFragment(binding.allAppsFragment)
|
||||
|
@ -739,6 +761,10 @@ class MainActivity : SimpleActivity(), FlingListener {
|
|||
|
||||
@SuppressLint("WrongConstant")
|
||||
override fun onFlingDown() {
|
||||
if (mIgnoreYMoveEvents) {
|
||||
return
|
||||
}
|
||||
|
||||
mIgnoreUpEvent = true
|
||||
if (isAllAppsFragmentExpanded()) {
|
||||
hideFragment(binding.allAppsFragment)
|
||||
|
@ -753,11 +779,19 @@ class MainActivity : SimpleActivity(), FlingListener {
|
|||
}
|
||||
|
||||
override fun onFlingRight() {
|
||||
if (mIgnoreXMoveEvents) {
|
||||
return
|
||||
}
|
||||
|
||||
mIgnoreUpEvent = true
|
||||
binding.homeScreenGrid.root.prevPage(redraw = true)
|
||||
}
|
||||
|
||||
override fun onFlingLeft() {
|
||||
if (mIgnoreXMoveEvents) {
|
||||
return
|
||||
}
|
||||
|
||||
mIgnoreUpEvent = true
|
||||
binding.homeScreenGrid.root.nextPage(redraw = true)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.content.Context
|
|||
import android.graphics.*
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
|
@ -37,10 +38,7 @@ import com.simplemobiletools.launcher.extensions.getDrawableForPackageName
|
|||
import com.simplemobiletools.launcher.extensions.homeScreenGridItemsDB
|
||||
import com.simplemobiletools.launcher.helpers.*
|
||||
import com.simplemobiletools.launcher.models.HomeScreenGridItem
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.*
|
||||
|
||||
class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : RelativeLayout(context, attrs, defStyle) {
|
||||
constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0)
|
||||
|
@ -70,13 +68,14 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
private var redrawWidgets = false
|
||||
private var iconSize = 0
|
||||
|
||||
private var lastPage = 0
|
||||
private var currentPage = 0
|
||||
private var pageChangeLastArea = PageChangeArea.MIDDLE
|
||||
private var pageChangeLastAreaEntryTime = 0L
|
||||
private var pageChangeAnimLeftPercentage = 0f
|
||||
private var pageChangeEnabled = true
|
||||
private var pageChangeIndicatorsAlpha = 0f
|
||||
private val pager = AnimatedGridPager(
|
||||
getMaxPage = ::getMaxPage,
|
||||
redrawGrid = ::redrawGrid,
|
||||
getWidth = { width },
|
||||
getHandler = { handler },
|
||||
getNextPageBound = { right - sideMargins.right - cellWidth / 2 },
|
||||
getPrevPageBound = { left + sideMargins.left + cellWidth / 2 }
|
||||
)
|
||||
|
||||
// apply fake margins at the home screen. Real ones would cause the icons be cut at dragging at screen sides
|
||||
var sideMargins = Rect()
|
||||
|
@ -92,26 +91,6 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
var itemClickListener: ((HomeScreenGridItem) -> Unit)? = null
|
||||
var itemLongClickListener: ((HomeScreenGridItem) -> Unit)? = null
|
||||
|
||||
private val checkAndExecuteDelayedPageChange: Runnable = Runnable {
|
||||
if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) {
|
||||
when (pageChangeLastArea) {
|
||||
PageChangeArea.RIGHT -> nextOrAdditionalPage(true)
|
||||
PageChangeArea.LEFT -> prevPage(true)
|
||||
else -> clearPageChangeFlags()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val startFadingIndicators: Runnable = Runnable {
|
||||
ValueAnimator.ofFloat(1f, 0f)
|
||||
.apply {
|
||||
addUpdateListener {
|
||||
pageChangeIndicatorsAlpha = it.animatedValue as Float
|
||||
redrawGrid()
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
ViewCompat.setAccessibilityDelegate(this, HomeScreenGridTouchHelper(this))
|
||||
|
@ -197,7 +176,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
|
||||
gridItems.removeIf { it.id == item.id }
|
||||
if (currentPage > getMaxPage()) {
|
||||
if (pager.isOutsideOfPageRange()) {
|
||||
post {
|
||||
prevPage()
|
||||
}
|
||||
|
@ -232,9 +211,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
return
|
||||
}
|
||||
|
||||
pageChangeIndicatorsAlpha = 1f
|
||||
removeCallbacks(startFadingIndicators)
|
||||
if (draggedItemCurrentCoords.first == -1 && draggedItemCurrentCoords.second == -1 && draggedItem != null) {
|
||||
if (draggedItemCurrentCoords.first == -1 && draggedItemCurrentCoords.second == -1) {
|
||||
if (draggedItem!!.type == ITEM_TYPE_WIDGET) {
|
||||
val draggedWidgetView = widgetViews.firstOrNull { it.tag == draggedItem?.widgetId }
|
||||
if (draggedWidgetView != null) {
|
||||
|
@ -246,47 +223,10 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
|
||||
draggedItemCurrentCoords = Pair(x, y)
|
||||
if (x > right - sideMargins.right - cellWidth / 2) {
|
||||
doWithPageChangeDelay(PageChangeArea.RIGHT) {
|
||||
nextOrAdditionalPage()
|
||||
}
|
||||
} else if (x < left + sideMargins.left + cellWidth / 2) {
|
||||
doWithPageChangeDelay(PageChangeArea.LEFT) {
|
||||
prevPage()
|
||||
}
|
||||
} else {
|
||||
clearPageChangeFlags()
|
||||
}
|
||||
pager.handleItemMovement(x, y)
|
||||
redrawGrid()
|
||||
}
|
||||
|
||||
private fun clearPageChangeFlags() {
|
||||
pageChangeLastArea = PageChangeArea.MIDDLE
|
||||
pageChangeLastAreaEntryTime = 0
|
||||
removeCallbacks(checkAndExecuteDelayedPageChange)
|
||||
}
|
||||
|
||||
private fun schedulePageChange() {
|
||||
pageChangeLastAreaEntryTime = System.currentTimeMillis()
|
||||
postDelayed(checkAndExecuteDelayedPageChange, PAGE_CHANGE_HOLD_THRESHOLD)
|
||||
}
|
||||
|
||||
private fun scheduleIndicatorsFade() {
|
||||
pageChangeIndicatorsAlpha = 1f
|
||||
postDelayed(startFadingIndicators, PAGE_INDICATORS_FADE_DELAY)
|
||||
}
|
||||
|
||||
private fun doWithPageChangeDelay(needed: PageChangeArea, pageChangeFunction: () -> Boolean) {
|
||||
if (pageChangeLastArea != needed) {
|
||||
pageChangeLastArea = needed
|
||||
schedulePageChange()
|
||||
} else if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) {
|
||||
if (pageChangeFunction()) {
|
||||
clearPageChangeFlags()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// figure out at which cell was the item dropped, if it is empty
|
||||
fun itemDraggingStopped() {
|
||||
widgetViews.forEach {
|
||||
|
@ -297,7 +237,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
return
|
||||
}
|
||||
|
||||
scheduleIndicatorsFade()
|
||||
pager.itemMovementStopped()
|
||||
when (draggedItem!!.type) {
|
||||
ITEM_TYPE_ICON, ITEM_TYPE_SHORTCUT -> addAppIconOrShortcut()
|
||||
ITEM_TYPE_WIDGET -> addWidget()
|
||||
|
@ -373,7 +313,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
// check if the destination cell is empty
|
||||
var areAllCellsEmpty = true
|
||||
val wantedCell = Pair(xIndex, yIndex)
|
||||
gridItems.filter { it.page == currentPage || it.docked }.forEach { item ->
|
||||
gridItems.filter { pager.isItemOnCurrentPage(it) || it.docked }.forEach { item ->
|
||||
for (xCell in item.left..item.right) {
|
||||
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
|
||||
val cell = Pair(xCell, yCell)
|
||||
|
@ -396,7 +336,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
top = yIndex
|
||||
right = xIndex
|
||||
bottom = yIndex
|
||||
page = currentPage
|
||||
page = pager.getCurrentPage()
|
||||
docked = yIndex == rowCount - 1
|
||||
|
||||
ensureBackgroundThread {
|
||||
|
@ -412,7 +352,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
yIndex,
|
||||
xIndex,
|
||||
yIndex,
|
||||
currentPage,
|
||||
pager.getCurrentPage(),
|
||||
draggedItem!!.packageName,
|
||||
draggedItem!!.activityName,
|
||||
draggedItem!!.title,
|
||||
|
@ -479,7 +419,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
|
||||
var areAllCellsEmpty = true
|
||||
gridItems.filter { it.id != draggedItem?.id && (it.page == currentPage || it.docked) }.forEach { item ->
|
||||
gridItems.filter { it.id != draggedItem?.id && (pager.isItemOnCurrentPage(it) || it.docked) }.forEach { item ->
|
||||
for (xCell in item.left..item.right) {
|
||||
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
|
||||
val cell = Pair(xCell, yCell)
|
||||
|
@ -499,7 +439,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
top = widgetRect.top
|
||||
right = widgetRect.right
|
||||
bottom = widgetRect.bottom
|
||||
page = currentPage
|
||||
page = pager.getCurrentPage()
|
||||
}
|
||||
|
||||
ensureBackgroundThread {
|
||||
|
@ -516,7 +456,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
widgetItem.top,
|
||||
widgetItem.right,
|
||||
widgetItem.bottom,
|
||||
currentPage,
|
||||
pager.getCurrentPage(),
|
||||
false,
|
||||
widgetItem.id!!
|
||||
)
|
||||
|
@ -575,7 +515,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
removeItemFromHomeScreen(item)
|
||||
}
|
||||
|
||||
if (currentPage > getMaxPage()) {
|
||||
if (pager.isOutsideOfPageRange()) {
|
||||
prevPage(redraw = true)
|
||||
}
|
||||
}
|
||||
|
@ -610,25 +550,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
|
||||
private fun updateWidgetPositionAndSize(widgetView: AppWidgetHostView, item: HomeScreenGridItem): Size {
|
||||
var x = calculateWidgetX(item.left) + width * item.page - width * lastPage
|
||||
if (pageChangeAnimLeftPercentage > 0f && pageChangeAnimLeftPercentage < 1f && (item.page == currentPage || item.page == lastPage)) {
|
||||
val xFactor = if (currentPage > lastPage) {
|
||||
pageChangeAnimLeftPercentage
|
||||
} else {
|
||||
-pageChangeAnimLeftPercentage
|
||||
}
|
||||
val lastXFactor = if (currentPage > lastPage) {
|
||||
pageChangeAnimLeftPercentage - 1
|
||||
} else {
|
||||
1 - pageChangeAnimLeftPercentage
|
||||
}
|
||||
if (item.page == currentPage) {
|
||||
x += width * xFactor
|
||||
}
|
||||
if (item.page == lastPage) {
|
||||
x += width * lastXFactor
|
||||
}
|
||||
}
|
||||
val currentViewPosition = pager.getCurrentViewPositionInFullPageSpace() * width.toFloat()
|
||||
val x = calculateWidgetX(item.left) + width * item.page - currentViewPosition
|
||||
widgetView.x = x
|
||||
widgetView.y = calculateWidgetY(item.top)
|
||||
val widgetWidth = item.getWidthInCells() * cellWidth
|
||||
|
@ -685,16 +608,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
fillCellSizes()
|
||||
}
|
||||
|
||||
val currentXFactor = if (currentPage > lastPage) {
|
||||
pageChangeAnimLeftPercentage
|
||||
} else {
|
||||
-pageChangeAnimLeftPercentage
|
||||
}
|
||||
val lastXFactor = if (currentPage > lastPage) {
|
||||
pageChangeAnimLeftPercentage - 1
|
||||
} else {
|
||||
1 - pageChangeAnimLeftPercentage
|
||||
}
|
||||
val currentXFactor = pager.getXFactorForCurrentPage()
|
||||
val lastXFactor = pager.getXFactorForLastPage()
|
||||
|
||||
fun handleItemDrawing(item: HomeScreenGridItem, xFactor: Float) {
|
||||
if (item.id != draggedItem?.id) {
|
||||
|
@ -729,7 +644,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
}
|
||||
|
||||
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.page == currentPage && !it.docked }
|
||||
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && pager.isItemOnCurrentPage(it) && !it.docked }
|
||||
.forEach { item ->
|
||||
if (item.outOfBounds()) {
|
||||
return@forEach
|
||||
|
@ -744,8 +659,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
|
||||
handleItemDrawing(item, 0f)
|
||||
}
|
||||
if (pageChangeAnimLeftPercentage > 0f) {
|
||||
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.page == lastPage && !it.docked }
|
||||
if (pager.isAnimatingPageChange()) {
|
||||
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && pager.isItemOnLastPage(it) && !it.docked }
|
||||
.forEach { item ->
|
||||
if (item.outOfBounds()) {
|
||||
return@forEach
|
||||
|
@ -755,6 +670,20 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
}
|
||||
|
||||
if (pager.isSwiped()) {
|
||||
gridItems.filter {
|
||||
(it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT)
|
||||
&& pager.isItemInSwipeRange(it)
|
||||
&& !it.docked
|
||||
}.forEach { item ->
|
||||
if (item.outOfBounds()) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
handleItemDrawing(item, lastXFactor)
|
||||
}
|
||||
}
|
||||
|
||||
if (isFirstDraw) {
|
||||
gridItems.filter { it.type == ITEM_TYPE_WIDGET && !it.outOfBounds() }.forEach { item ->
|
||||
bindWidget(item, true)
|
||||
|
@ -768,19 +697,15 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
|
||||
// Only draw page indicators when there is a need for it
|
||||
if (pageChangeAnimLeftPercentage > 0f || pageChangeIndicatorsAlpha != 0f) {
|
||||
val pageCount = max(getMaxPage(), currentPage) + 1
|
||||
if (pager.shouldDisplayPageChangeIndicator()) {
|
||||
val pageCount = pager.getPageCount()
|
||||
val pageIndicatorsRequiredWidth = pageCount * pageIndicatorRadius * 2 + pageCount * (pageIndicatorMargin - 1)
|
||||
val usableWidth = getFakeWidth()
|
||||
val pageIndicatorsStart = (usableWidth - pageIndicatorsRequiredWidth) / 2 + sideMargins.left
|
||||
var currentPageIndicatorLeft = pageIndicatorsStart
|
||||
val pageIndicatorY = cellYCoords[rowCount - 1].toFloat() + sideMargins.top + extraYMargin + iconMargin
|
||||
val pageIndicatorStep = pageIndicatorRadius * 2 + pageIndicatorMargin
|
||||
if (pageChangeIndicatorsAlpha != 0f) {
|
||||
emptyPageIndicatorPaint.alpha = (pageChangeIndicatorsAlpha * 255.0f).toInt()
|
||||
} else {
|
||||
emptyPageIndicatorPaint.alpha = 255
|
||||
}
|
||||
emptyPageIndicatorPaint.alpha = pager.getPageChangeIndicatorsAlpha()
|
||||
// Draw empty page indicators
|
||||
for (page in 0 until pageCount) {
|
||||
canvas.drawCircle(currentPageIndicatorLeft + pageIndicatorRadius, pageIndicatorY, pageIndicatorRadius, emptyPageIndicatorPaint)
|
||||
|
@ -788,14 +713,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
|
||||
// Draw current page indicator on exact position
|
||||
val currentIndicatorRangeStart = pageIndicatorsStart + lastPage * pageIndicatorStep
|
||||
val currentIndicatorRangeEnd = pageIndicatorsStart + currentPage * pageIndicatorStep
|
||||
val currentIndicatorPosition = MathUtils.lerp(currentIndicatorRangeStart, currentIndicatorRangeEnd, 1 - pageChangeAnimLeftPercentage)
|
||||
if (pageChangeIndicatorsAlpha != 0f) {
|
||||
currentPageIndicatorPaint.alpha = (pageChangeIndicatorsAlpha * 255.0f).toInt()
|
||||
} else {
|
||||
currentPageIndicatorPaint.alpha = 255
|
||||
}
|
||||
val currentIndicatorPosition = pageIndicatorsStart + pager.getCurrentViewPositionInFullPageSpace() * pageIndicatorStep
|
||||
currentPageIndicatorPaint.alpha = pager.getPageChangeIndicatorsAlpha()
|
||||
canvas.drawCircle(currentIndicatorPosition + pageIndicatorRadius, pageIndicatorY, pageIndicatorRadius, currentPageIndicatorPaint)
|
||||
}
|
||||
|
||||
|
@ -942,7 +861,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
}
|
||||
|
||||
fun isClickingGridItem(x: Int, y: Int): HomeScreenGridItem? {
|
||||
for (gridItem in gridItems.filter { it.page == currentPage || it.docked }) {
|
||||
for (gridItem in gridItems.filter { it.page == pager.getCurrentPage() || it.docked }) {
|
||||
if (gridItem.outOfBounds()) {
|
||||
continue
|
||||
}
|
||||
|
@ -1038,15 +957,187 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
|
||||
private fun getMaxPage() = gridItems.filter { !it.docked && !it.outOfBounds() }.maxOfOrNull { it.page } ?: 0
|
||||
|
||||
private fun nextOrAdditionalPage(redraw: Boolean = false): Boolean {
|
||||
if (currentPage < getMaxPage() + 1 && pageChangeEnabled) {
|
||||
lastPage = currentPage
|
||||
currentPage++
|
||||
handlePageChange(redraw)
|
||||
return true
|
||||
fun nextPage(redraw: Boolean = false): Boolean {
|
||||
return pager.nextPage(redraw)
|
||||
}
|
||||
|
||||
fun prevPage(redraw: Boolean = false): Boolean {
|
||||
return pager.prevPage(redraw)
|
||||
}
|
||||
|
||||
fun skipToPage(targetPage: Int): Boolean {
|
||||
return pager.skipToPage(targetPage)
|
||||
}
|
||||
|
||||
fun getCurrentIconSize(): Int = iconSize
|
||||
|
||||
|
||||
fun setSwipeMovement(diffX: Float) {
|
||||
if (draggedItem == null) {
|
||||
pager.setSwipeMovement(diffX)
|
||||
}
|
||||
}
|
||||
|
||||
fun finalizeSwipe() {
|
||||
pager.finalizeSwipe()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class responsible for managing current page and providing utilities for animating page changes,
|
||||
* as well as partial dragigng between pages
|
||||
*/
|
||||
private class AnimatedGridPager(
|
||||
private val getMaxPage: () -> Int,
|
||||
private val redrawGrid: () -> Unit,
|
||||
private val getWidth: () -> Int,
|
||||
private val getHandler: () -> Handler,
|
||||
private val getNextPageBound: () -> Int,
|
||||
private val getPrevPageBound: () -> Int,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L
|
||||
private const val PAGE_INDICATORS_FADE_DELAY = PAGE_CHANGE_HOLD_THRESHOLD + 300L
|
||||
|
||||
private enum class PageChangeArea {
|
||||
LEFT,
|
||||
MIDDLE,
|
||||
RIGHT
|
||||
}
|
||||
}
|
||||
|
||||
private var lastPage = 0
|
||||
private var currentPage = 0
|
||||
private var pageChangeLastArea = PageChangeArea.MIDDLE
|
||||
private var pageChangeLastAreaEntryTime = 0L
|
||||
private var pageChangeAnimLeftPercentage = 0f
|
||||
private var pageChangeEnabled = true
|
||||
private var pageChangeIndicatorsAlpha = 0f
|
||||
private var pageChangeSwipedPercentage = 0f
|
||||
|
||||
fun getCurrentPage() = currentPage
|
||||
|
||||
fun isItemOnCurrentPage(item: HomeScreenGridItem) = item.page == currentPage
|
||||
|
||||
fun isItemOnLastPage(item: HomeScreenGridItem) = item.page == lastPage
|
||||
|
||||
fun getPageCount() = max(getMaxPage(), currentPage) + 1
|
||||
|
||||
|
||||
fun isOutsideOfPageRange() = currentPage > getMaxPage()
|
||||
|
||||
fun isItemInSwipeRange(item: HomeScreenGridItem) =
|
||||
((pageChangeSwipedPercentage > 0f && item.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && item.page == currentPage + 1))
|
||||
|
||||
fun isSwiped() = abs(pageChangeSwipedPercentage) > 0f
|
||||
|
||||
fun isAnimatingPageChange() = pageChangeAnimLeftPercentage > 0f
|
||||
|
||||
fun shouldDisplayPageChangeIndicator() = isSwiped() || isAnimatingPageChange() || pageChangeIndicatorsAlpha > 0f
|
||||
|
||||
fun getPageChangeIndicatorsAlpha() = if (pageChangeIndicatorsAlpha != 0f) {
|
||||
(pageChangeIndicatorsAlpha * 255.0f).toInt()
|
||||
} else {
|
||||
255
|
||||
}
|
||||
|
||||
fun getXFactorForCurrentPage(): Float {
|
||||
return if (abs(pageChangeSwipedPercentage) > 0f) {
|
||||
pageChangeSwipedPercentage
|
||||
} else {
|
||||
if (currentPage > lastPage) {
|
||||
pageChangeAnimLeftPercentage
|
||||
} else {
|
||||
-pageChangeAnimLeftPercentage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getXFactorForLastPage(): Float {
|
||||
return if (abs(pageChangeSwipedPercentage) > 0f) {
|
||||
(1 - abs(pageChangeSwipedPercentage)) * -sign(pageChangeSwipedPercentage)
|
||||
} else {
|
||||
if (currentPage > lastPage) {
|
||||
pageChangeAnimLeftPercentage - 1
|
||||
} else {
|
||||
1 - pageChangeAnimLeftPercentage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCurrentViewPositionInFullPageSpace(): Float {
|
||||
val rangeStart = lastPage.toFloat()
|
||||
val rangeEndPage = if (abs(pageChangeSwipedPercentage) > 0f) {
|
||||
if (pageChangeSwipedPercentage > 0f) {
|
||||
currentPage - 1
|
||||
} else {
|
||||
currentPage + 1
|
||||
}
|
||||
} else {
|
||||
currentPage
|
||||
}
|
||||
val rangeEnd = rangeEndPage.toFloat()
|
||||
val lerpAmount = if (pageChangeAnimLeftPercentage > 0f) {
|
||||
1 - pageChangeAnimLeftPercentage
|
||||
} else {
|
||||
abs(pageChangeSwipedPercentage)
|
||||
}
|
||||
return MathUtils.lerp(rangeStart, rangeEnd, lerpAmount)
|
||||
}
|
||||
|
||||
fun setSwipeMovement(diffX: Float) {
|
||||
if (!pageChangeEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
return false
|
||||
if (currentPage < getMaxPage() && diffX > 0f || currentPage > 0 && diffX < 0f) {
|
||||
pageChangeSwipedPercentage = (-diffX / getWidth().toFloat()).coerceIn(-1f, 1f)
|
||||
redrawGrid()
|
||||
}
|
||||
}
|
||||
|
||||
fun finalizeSwipe() {
|
||||
if (abs(pageChangeSwipedPercentage) == 0f) {
|
||||
return
|
||||
}
|
||||
|
||||
if (abs(pageChangeSwipedPercentage) > 0.5f) {
|
||||
lastPage = currentPage
|
||||
currentPage = if (pageChangeSwipedPercentage > 0f) {
|
||||
currentPage - 1
|
||||
} else {
|
||||
currentPage + 1
|
||||
}
|
||||
handlePageChange(true)
|
||||
} else {
|
||||
lastPage = if (pageChangeSwipedPercentage > 0f) {
|
||||
currentPage - 1
|
||||
} else {
|
||||
currentPage + 1
|
||||
}
|
||||
pageChangeSwipedPercentage = sign(pageChangeSwipedPercentage) * (1 - abs(pageChangeSwipedPercentage))
|
||||
handlePageChange(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleItemMovement(x: Int, y: Int) {
|
||||
showIndicators()
|
||||
if (x > getNextPageBound()) {
|
||||
doWithPageChangeDelay(PageChangeArea.RIGHT) {
|
||||
nextOrAdditionalPage()
|
||||
}
|
||||
} else if (x < getPrevPageBound()) {
|
||||
doWithPageChangeDelay(PageChangeArea.LEFT) {
|
||||
prevPage()
|
||||
}
|
||||
} else {
|
||||
clearPageChangeFlags()
|
||||
}
|
||||
}
|
||||
|
||||
fun itemMovementStopped() {
|
||||
scheduleIndicatorsFade()
|
||||
}
|
||||
|
||||
fun nextPage(redraw: Boolean = false): Boolean {
|
||||
|
@ -1082,16 +1173,80 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
return false
|
||||
}
|
||||
|
||||
fun getCurrentIconSize(): Int = iconSize
|
||||
private val checkAndExecuteDelayedPageChange: Runnable = Runnable {
|
||||
if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) {
|
||||
when (pageChangeLastArea) {
|
||||
PageChangeArea.RIGHT -> nextOrAdditionalPage(true)
|
||||
PageChangeArea.LEFT -> prevPage(true)
|
||||
else -> clearPageChangeFlags()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val startFadingIndicators: Runnable = Runnable {
|
||||
ValueAnimator.ofFloat(1f, 0f)
|
||||
.apply {
|
||||
addUpdateListener {
|
||||
pageChangeIndicatorsAlpha = it.animatedValue as Float
|
||||
redrawGrid()
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showIndicators() {
|
||||
pageChangeIndicatorsAlpha = 1f
|
||||
getHandler().removeCallbacks(startFadingIndicators)
|
||||
}
|
||||
|
||||
private fun clearPageChangeFlags() {
|
||||
pageChangeLastArea = PageChangeArea.MIDDLE
|
||||
pageChangeLastAreaEntryTime = 0
|
||||
getHandler().removeCallbacks(checkAndExecuteDelayedPageChange)
|
||||
}
|
||||
|
||||
private fun schedulePageChange() {
|
||||
pageChangeLastAreaEntryTime = System.currentTimeMillis()
|
||||
getHandler().postDelayed(checkAndExecuteDelayedPageChange, PAGE_CHANGE_HOLD_THRESHOLD)
|
||||
}
|
||||
|
||||
private fun scheduleIndicatorsFade() {
|
||||
pageChangeIndicatorsAlpha = 1f
|
||||
getHandler().postDelayed(startFadingIndicators, PAGE_INDICATORS_FADE_DELAY)
|
||||
}
|
||||
|
||||
private fun doWithPageChangeDelay(needed: PageChangeArea, pageChangeFunction: () -> Boolean) {
|
||||
if (pageChangeLastArea != needed) {
|
||||
pageChangeLastArea = needed
|
||||
schedulePageChange()
|
||||
} else if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) {
|
||||
if (pageChangeFunction()) {
|
||||
clearPageChangeFlags()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun nextOrAdditionalPage(redraw: Boolean = false): Boolean {
|
||||
if (currentPage < getMaxPage() + 1 && pageChangeEnabled) {
|
||||
lastPage = currentPage
|
||||
currentPage++
|
||||
handlePageChange(redraw)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun handlePageChange(redraw: Boolean = false) {
|
||||
pageChangeEnabled = false
|
||||
pageChangeIndicatorsAlpha = 0f
|
||||
removeCallbacks(startFadingIndicators)
|
||||
val startingAt = 1 - abs(pageChangeSwipedPercentage)
|
||||
pageChangeSwipedPercentage = 0f
|
||||
getHandler().removeCallbacks(startFadingIndicators)
|
||||
if (redraw) {
|
||||
redrawGrid()
|
||||
}
|
||||
ValueAnimator.ofFloat(1f, 0f)
|
||||
ValueAnimator.ofFloat(startingAt, 0f)
|
||||
.apply {
|
||||
addUpdateListener {
|
||||
pageChangeAnimLeftPercentage = it.animatedValue as Float
|
||||
|
@ -1103,7 +1258,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
pageChangeAnimLeftPercentage = 0f
|
||||
pageChangeEnabled = true
|
||||
lastPage = currentPage
|
||||
schedulePageChange()
|
||||
clearPageChangeFlags()
|
||||
scheduleIndicatorsFade()
|
||||
redrawGrid()
|
||||
}
|
||||
|
@ -1111,15 +1266,4 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|||
start()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L
|
||||
private const val PAGE_INDICATORS_FADE_DELAY = PAGE_CHANGE_HOLD_THRESHOLD + 300L
|
||||
|
||||
private enum class PageChangeArea {
|
||||
LEFT,
|
||||
MIDDLE,
|
||||
RIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue