|
|
|
@ -12,6 +12,7 @@ import android.graphics.*
|
|
|
|
|
import android.graphics.drawable.BitmapDrawable
|
|
|
|
|
import android.graphics.drawable.Drawable
|
|
|
|
|
import android.os.Bundle
|
|
|
|
|
import android.os.Handler
|
|
|
|
|
import android.text.Layout
|
|
|
|
|
import android.text.StaticLayout
|
|
|
|
|
import android.text.TextPaint
|
|
|
|
@ -75,13 +76,15 @@ 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 },
|
|
|
|
|
pageChangeStarted = { closeFolder() }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
private var currentlyOpenFolder: HomeScreenFolder? = null
|
|
|
|
|
private var draggingLeftFolderAt: Long? = null
|
|
|
|
@ -101,26 +104,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))
|
|
|
|
@ -242,7 +225,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gridItems.removeIf { it.id == item.id }
|
|
|
|
|
if (currentPage > getMaxPage()) {
|
|
|
|
|
if (pager.isOutsideOfPageRange()) {
|
|
|
|
|
post {
|
|
|
|
|
prevPage()
|
|
|
|
|
}
|
|
|
|
@ -300,8 +283,6 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pageChangeIndicatorsAlpha = 1f
|
|
|
|
|
removeCallbacks(startFadingIndicators)
|
|
|
|
|
if (draggedItemCurrentCoords.first == -1 && draggedItemCurrentCoords.second == -1 && draggedItem != null) {
|
|
|
|
|
if (draggedItem!!.type == ITEM_TYPE_WIDGET) {
|
|
|
|
|
val draggedWidgetView = widgetViews.firstOrNull { it.tag == draggedItem?.widgetId }
|
|
|
|
@ -343,47 +324,10 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
draggingEnteredNewFolderAt = null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
@ -394,7 +338,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scheduleIndicatorsFade()
|
|
|
|
|
pager.itemMovementStopped()
|
|
|
|
|
when (draggedItem!!.type) {
|
|
|
|
|
ITEM_TYPE_FOLDER -> moveItem()
|
|
|
|
|
ITEM_TYPE_ICON, ITEM_TYPE_SHORTCUT -> addAppIconOrShortcut()
|
|
|
|
@ -496,7 +440,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 {
|
|
|
|
@ -659,7 +603,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
top = yIndex
|
|
|
|
|
right = finalXIndex
|
|
|
|
|
bottom = yIndex
|
|
|
|
|
page = currentPage
|
|
|
|
|
page = pager.getCurrentPage()
|
|
|
|
|
docked = yIndex == rowCount - 1
|
|
|
|
|
parentId = newParentId
|
|
|
|
|
|
|
|
|
@ -699,7 +643,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
yIndex,
|
|
|
|
|
finalXIndex,
|
|
|
|
|
yIndex,
|
|
|
|
|
currentPage,
|
|
|
|
|
pager.getCurrentPage(),
|
|
|
|
|
draggedItem!!.packageName,
|
|
|
|
|
draggedItem!!.activityName,
|
|
|
|
|
draggedItem!!.title,
|
|
|
|
@ -790,7 +734,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 {
|
|
|
|
@ -807,7 +751,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
widgetItem.top,
|
|
|
|
|
widgetItem.right,
|
|
|
|
|
widgetItem.bottom,
|
|
|
|
|
currentPage,
|
|
|
|
|
pager.getCurrentPage(),
|
|
|
|
|
false,
|
|
|
|
|
null,
|
|
|
|
|
widgetItem.id!!
|
|
|
|
@ -867,7 +811,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
removeItemFromHomeScreen(item)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentPage > getMaxPage()) {
|
|
|
|
|
if (pager.isOutsideOfPageRange()) {
|
|
|
|
|
prevPage(redraw = true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -902,25 +846,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
|
|
|
|
@ -977,16 +904,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 handleGridItemDrawing(item: HomeScreenGridItem, baseItemX: Int, baseItemY: Int) {
|
|
|
|
|
if (item.id != draggedItem?.id) {
|
|
|
|
@ -1040,7 +959,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.type == ITEM_TYPE_FOLDER) && it.page == currentPage && !it.docked && it.parentId == null }
|
|
|
|
|
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT || it.type == ITEM_TYPE_FOLDER) && pager.isItemOnCurrentPage(it) && !it.docked && it.parentId == null }
|
|
|
|
|
.forEach { item ->
|
|
|
|
|
if (item.outOfBounds()) {
|
|
|
|
|
return@forEach
|
|
|
|
@ -1056,8 +975,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
|
|
|
|
|
handleMainGridItemDrawing(item, 0f)
|
|
|
|
|
}
|
|
|
|
|
if (pageChangeAnimLeftPercentage > 0f) {
|
|
|
|
|
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT || it.type == ITEM_TYPE_FOLDER) && it.page == lastPage && !it.docked && it.parentId == null }
|
|
|
|
|
if (pager.isAnimatingPageChange()) {
|
|
|
|
|
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT || it.type == ITEM_TYPE_FOLDER) && pager.isItemOnLastPage(it) && !it.docked && it.parentId == null }
|
|
|
|
|
.forEach { item ->
|
|
|
|
|
if (item.outOfBounds()) {
|
|
|
|
|
return@forEach
|
|
|
|
@ -1067,6 +986,21 @@ 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 || it.type == ITEM_TYPE_FOLDER)
|
|
|
|
|
&& pager.isItemInSwipeRange(it)
|
|
|
|
|
&& !it.docked
|
|
|
|
|
&& it.parentId == null
|
|
|
|
|
}.forEach { item ->
|
|
|
|
|
if (item.outOfBounds()) {
|
|
|
|
|
return@forEach
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleMainGridItemDrawing(item, lastXFactor)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isFirstDraw) {
|
|
|
|
|
gridItems.filter { it.type == ITEM_TYPE_WIDGET && !it.outOfBounds() }.forEach { item ->
|
|
|
|
|
bindWidget(item, true)
|
|
|
|
@ -1080,19 +1014,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)
|
|
|
|
@ -1100,14 +1030,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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1117,6 +1041,10 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
val folderRect = folder.getDrawingRect()
|
|
|
|
|
val folderItemsRect = folder.getItemsDrawingRect()
|
|
|
|
|
|
|
|
|
|
val currentViewPosition = pager.getCurrentViewPositionInFullPageSpace() * width.toFloat()
|
|
|
|
|
val rectOffset = width * folder.item.page - currentViewPosition
|
|
|
|
|
folderRect.offset(rectOffset, 0f)
|
|
|
|
|
|
|
|
|
|
canvas.drawRoundRect(folderRect, roundedCornerRadius, roundedCornerRadius, folderBackgroundPaint)
|
|
|
|
|
|
|
|
|
|
canvas.withScale(folder.scale, folder.scale, folderRect.centerX(), folderRect.centerY()) {
|
|
|
|
@ -1427,48 +1355,29 @@ 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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun nextPage(redraw: Boolean = false): Boolean {
|
|
|
|
|
if (currentPage < getMaxPage() && pageChangeEnabled) {
|
|
|
|
|
lastPage = currentPage
|
|
|
|
|
currentPage++
|
|
|
|
|
handlePageChange(redraw)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
return pager.nextPage(redraw)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun prevPage(redraw: Boolean = false): Boolean {
|
|
|
|
|
if (currentPage > 0 && pageChangeEnabled) {
|
|
|
|
|
lastPage = currentPage
|
|
|
|
|
currentPage--
|
|
|
|
|
handlePageChange(redraw)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
return pager.prevPage(redraw)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun skipToPage(targetPage: Int): Boolean {
|
|
|
|
|
if (currentPage != targetPage && targetPage < getMaxPage() + 1) {
|
|
|
|
|
lastPage = currentPage
|
|
|
|
|
currentPage = targetPage
|
|
|
|
|
handlePageChange()
|
|
|
|
|
return true
|
|
|
|
|
return pager.skipToPage(targetPage)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
fun getCurrentIconSize(): Int = iconSize
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setSwipeMovement(diffX: Float) {
|
|
|
|
|
if (draggedItem == null) {
|
|
|
|
|
pager.setSwipeMovement(diffX)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun finalizeSwipe() {
|
|
|
|
|
pager.finalizeSwipe()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun openFolder(folder: HomeScreenGridItem) {
|
|
|
|
@ -1489,42 +1398,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun getCurrentIconSize(): Int = iconSize
|
|
|
|
|
|
|
|
|
|
fun getCurrentCellSize(): Int = cellWidth
|
|
|
|
|
|
|
|
|
|
fun getCurrentCellMargin(): Int = iconMargin
|
|
|
|
|
|
|
|
|
|
private fun handlePageChange(redraw: Boolean = false) {
|
|
|
|
|
pageChangeEnabled = false
|
|
|
|
|
pageChangeIndicatorsAlpha = 0f
|
|
|
|
|
closeFolder()
|
|
|
|
|
removeCallbacks(startFadingIndicators)
|
|
|
|
|
if (redraw) {
|
|
|
|
|
redrawGrid()
|
|
|
|
|
}
|
|
|
|
|
ValueAnimator.ofFloat(1f, 0f)
|
|
|
|
|
.apply {
|
|
|
|
|
addUpdateListener {
|
|
|
|
|
pageChangeAnimLeftPercentage = it.animatedValue as Float
|
|
|
|
|
redrawGrid()
|
|
|
|
|
}
|
|
|
|
|
addListener(object : AnimatorListenerAdapter() {
|
|
|
|
|
override fun onAnimationEnd(animation: Animator) {
|
|
|
|
|
super.onAnimationEnd(animation)
|
|
|
|
|
pageChangeAnimLeftPercentage = 0f
|
|
|
|
|
pageChangeEnabled = true
|
|
|
|
|
lastPage = currentPage
|
|
|
|
|
schedulePageChange()
|
|
|
|
|
scheduleIndicatorsFade()
|
|
|
|
|
redrawGrid()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
start()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun ArrayList<HomeScreenGridItem>.filterVisibleOnly() = filter { (it.page == currentPage || it.docked) && it.parentId == null }
|
|
|
|
|
private fun ArrayList<HomeScreenGridItem>.filterVisibleOnly() = filter { (pager.isItemOnCurrentPage(it) || it.docked) && it.parentId == null }
|
|
|
|
|
|
|
|
|
|
private fun HomeScreenGridItem.toFolder(animateOpening: Boolean = false) = HomeScreenFolder(this, animateOpening)
|
|
|
|
|
|
|
|
|
@ -1533,6 +1407,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
animateOpening: Boolean
|
|
|
|
|
) {
|
|
|
|
|
var scale: Float = 1f
|
|
|
|
|
private var closing = false
|
|
|
|
|
|
|
|
|
|
init {
|
|
|
|
|
if (animateOpening) {
|
|
|
|
@ -1659,6 +1534,10 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
|
|
|
|
|
fun animateClosing(callback: () -> Unit) {
|
|
|
|
|
post {
|
|
|
|
|
if (closing) {
|
|
|
|
|
return@post
|
|
|
|
|
}
|
|
|
|
|
closing = true
|
|
|
|
|
ValueAnimator.ofFloat(scale, 0.2f)
|
|
|
|
|
.apply {
|
|
|
|
|
interpolator = DecelerateInterpolator()
|
|
|
|
@ -1695,3 +1574,291 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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,
|
|
|
|
|
private val pageChangeStarted: () -> Unit
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentPage < getMaxPage() && diffX > 0f || currentPage > 0 && diffX < 0f) {
|
|
|
|
|
pageChangeSwipedPercentage = (-diffX / getWidth().toFloat()).coerceIn(-1f, 1f)
|
|
|
|
|
pageChangeStarted()
|
|
|
|
|
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 {
|
|
|
|
|
if (currentPage < getMaxPage() && pageChangeEnabled) {
|
|
|
|
|
lastPage = currentPage
|
|
|
|
|
currentPage++
|
|
|
|
|
handlePageChange(redraw)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun prevPage(redraw: Boolean = false): Boolean {
|
|
|
|
|
if (currentPage > 0 && pageChangeEnabled) {
|
|
|
|
|
lastPage = currentPage
|
|
|
|
|
currentPage--
|
|
|
|
|
handlePageChange(redraw)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun skipToPage(targetPage: Int): Boolean {
|
|
|
|
|
if (currentPage != targetPage && targetPage < getMaxPage() + 1) {
|
|
|
|
|
lastPage = currentPage
|
|
|
|
|
currentPage = targetPage
|
|
|
|
|
handlePageChange()
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
pageChangeStarted()
|
|
|
|
|
val startingAt = 1 - abs(pageChangeSwipedPercentage)
|
|
|
|
|
pageChangeSwipedPercentage = 0f
|
|
|
|
|
getHandler().removeCallbacks(startFadingIndicators)
|
|
|
|
|
if (redraw) {
|
|
|
|
|
redrawGrid()
|
|
|
|
|
}
|
|
|
|
|
ValueAnimator.ofFloat(startingAt, 0f)
|
|
|
|
|
.apply {
|
|
|
|
|
addUpdateListener {
|
|
|
|
|
pageChangeAnimLeftPercentage = it.animatedValue as Float
|
|
|
|
|
redrawGrid()
|
|
|
|
|
}
|
|
|
|
|
addListener(object : AnimatorListenerAdapter() {
|
|
|
|
|
override fun onAnimationEnd(animation: Animator) {
|
|
|
|
|
super.onAnimationEnd(animation)
|
|
|
|
|
pageChangeAnimLeftPercentage = 0f
|
|
|
|
|
pageChangeEnabled = true
|
|
|
|
|
lastPage = currentPage
|
|
|
|
|
clearPageChangeFlags()
|
|
|
|
|
scheduleIndicatorsFade()
|
|
|
|
|
redrawGrid()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
start()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|