Add folder opening and closing animation

This commit is contained in:
Ensar Sarajčić 2023-08-23 16:48:56 +02:00
parent 7a5c698322
commit 3cc00157cc

View File

@ -20,9 +20,11 @@ import android.util.AttributeSet
import android.util.Size import android.util.Size
import android.util.SizeF import android.util.SizeF
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.core.graphics.withScale
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.customview.widget.ExploreByTouchHelper import androidx.customview.widget.ExploreByTouchHelper
@ -81,7 +83,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
private var pageChangeEnabled = true private var pageChangeEnabled = true
private var pageChangeIndicatorsAlpha = 0f private var pageChangeIndicatorsAlpha = 0f
private var currentlyOpenFolder: HomeScreenGridItem? = null private var currentlyOpenFolder: HomeScreenFolder? = null
private var draggingLeftFolderAt: Long? = null private var draggingLeftFolderAt: Long? = null
private var draggingEnteredNewFolderAt: Long? = null private var draggingEnteredNewFolderAt: Long? = null
@ -194,7 +196,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
if (item.type == ITEM_TYPE_ICON) { if (item.type == ITEM_TYPE_ICON) {
item.drawable = context.getDrawableForPackageName(item.packageName) item.drawable = context.getDrawableForPackageName(item.packageName)
} else if (item.type == ITEM_TYPE_FOLDER) { } else if (item.type == ITEM_TYPE_FOLDER) {
item.drawable = item.generateFolderDrawable() item.drawable = item.toFolder().generateDrawable()
} else if (item.type == ITEM_TYPE_SHORTCUT) { } else if (item.type == ITEM_TYPE_SHORTCUT) {
if (item.icon != null) { if (item.icon != null) {
item.drawable = BitmapDrawable(item.icon) item.drawable = BitmapDrawable(item.icon)
@ -270,7 +272,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
if (draggedItem!!.drawable == null) { if (draggedItem!!.drawable == null) {
if (draggedItem?.type == ITEM_TYPE_FOLDER) { if (draggedItem?.type == ITEM_TYPE_FOLDER) {
draggedItem!!.drawable = draggedGridItem!!.generateFolderDrawable() draggedItem!!.drawable = draggedGridItem.toFolder().generateDrawable()
} else { } else {
draggedItem!!.drawable = context.getDrawableForPackageName(draggedGridItem.packageName) draggedItem!!.drawable = context.getDrawableForPackageName(draggedGridItem.packageName)
} }
@ -285,7 +287,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
currentlyOpenFolder?.also { folder -> currentlyOpenFolder?.also { folder ->
if (folder.getFolderRect().contains(x.toFloat(), y.toFloat())) { if (folder.getDrawingRect().contains(x.toFloat(), y.toFloat())) {
draggingLeftFolderAt = null draggingLeftFolderAt = null
} else { } else {
draggingLeftFolderAt.also { draggingLeftFolderAt.also {
@ -324,7 +326,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
if (it == null) { if (it == null) {
draggingEnteredNewFolderAt = System.currentTimeMillis() draggingEnteredNewFolderAt = System.currentTimeMillis()
} else if (System.currentTimeMillis() - it > FOLDER_OPEN_HOLD_THRESHOLD) { } else if (System.currentTimeMillis() - it > FOLDER_OPEN_HOLD_THRESHOLD) {
if (coveredFolder.getFolderItems().count() >= HomeScreenGridItem.FOLDER_MAX_CAPACITY && draggedItem?.parentId != coveredFolder.id) { if (coveredFolder.toFolder().getItems()
.count() >= HomeScreenGridItem.FOLDER_MAX_CAPACITY && draggedItem?.parentId != coveredFolder.id
) {
performHapticFeedback() performHapticFeedback()
draggingEnteredNewFolderAt = null draggingEnteredNewFolderAt = null
} else { } else {
@ -518,16 +522,16 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
var redrawIcons = false var redrawIcons = false
val folder = currentlyOpenFolder val folder = currentlyOpenFolder
if (folder != null && folder.getFolderItemsRect().contains( if (folder != null && folder.getItemsDrawingRect().contains(
(sideMargins.left + draggedItemCurrentCoords.first).toFloat(), (sideMargins.left + draggedItemCurrentCoords.first).toFloat(),
(sideMargins.top + draggedItemCurrentCoords.second).toFloat() (sideMargins.top + draggedItemCurrentCoords.second).toFloat()
) )
) { ) {
val center = folder.getFolderGridCenters().minBy { val center = folder.getItemsGridCenters().minBy {
abs(it.second - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.third - draggedItemCurrentCoords.second + sideMargins.top) abs(it.second - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.third - draggedItemCurrentCoords.second + sideMargins.top)
} }
isDroppingPositionValid = true isDroppingPositionValid = true
potentialParent = folder potentialParent = folder.item
xIndex = center.first xIndex = center.first
yIndex = 0 yIndex = 0
redrawIcons = true redrawIcons = true
@ -568,7 +572,13 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
if (potentialParent != null) { if (potentialParent != null) {
if (potentialParent?.type == ITEM_TYPE_FOLDER) { if (potentialParent?.type == ITEM_TYPE_FOLDER) {
addAppIconOrShortcut(draggedHomeGridItem, xIndex!!, yIndex!!, potentialParent?.id, toFolderEnd = potentialParent != currentlyOpenFolder) addAppIconOrShortcut(
draggedHomeGridItem,
xIndex!!,
yIndex!!,
potentialParent?.id,
toFolderEnd = potentialParent != currentlyOpenFolder?.item
)
} else { } else {
val parentItem = potentialParent!!.copy( val parentItem = potentialParent!!.copy(
type = ITEM_TYPE_FOLDER, type = ITEM_TYPE_FOLDER,
@ -615,7 +625,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
) { ) {
if (newParentId != null && newParentId != draggedHomeGridItem?.parentId) { if (newParentId != null && newParentId != draggedHomeGridItem?.parentId) {
gridItems.firstOrNull { it.id == newParentId }?.also { gridItems.firstOrNull { it.id == newParentId }?.also {
if (it.getFolderItems().count() >= HomeScreenGridItem.FOLDER_MAX_CAPACITY) { if (it.toFolder().getItems().count() >= HomeScreenGridItem.FOLDER_MAX_CAPACITY) {
performHapticFeedback() performHapticFeedback()
draggedItem = null draggedItem = null
draggedItemCurrentCoords = Pair(-1, -1) draggedItemCurrentCoords = Pair(-1, -1)
@ -627,9 +637,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
val finalXIndex = if (newParentId != null) { val finalXIndex = if (newParentId != null) {
if (toFolderEnd) { if (toFolderEnd) {
gridItems.firstOrNull { it.id == newParentId }?.getFolderItems()?.maxOf { it.left + 1 } ?: 0 gridItems.firstOrNull { it.id == newParentId }?.toFolder()?.getItems()?.maxOf { it.left + 1 } ?: 0
} else { } else {
min(xIndex, gridItems.firstOrNull { it.id == newParentId }?.getFolderItems()?.maxOf { min(xIndex, gridItems.firstOrNull { it.id == newParentId }?.toFolder()?.getItems()?.maxOf {
if (draggedHomeGridItem?.parentId == newParentId) { if (draggedHomeGridItem?.parentId == newParentId) {
it.left it.left
} else { } else {
@ -654,7 +664,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
parentId = newParentId parentId = newParentId
val oldParent = gridItems.firstOrNull { it.id == oldParentId } val oldParent = gridItems.firstOrNull { it.id == oldParentId }
val deleteOldParent = if (oldParent?.getFolderItems()?.isEmpty() == true) { val deleteOldParent = if (oldParent?.toFolder()?.getItems()?.isEmpty() == true) {
gridItems.remove(oldParent) gridItems.remove(oldParent)
true true
} else { } else {
@ -983,7 +993,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
val drawableX = baseItemX + iconMargin + extraXMargin val drawableX = baseItemX + iconMargin + extraXMargin
val drawable = if (item.type == ITEM_TYPE_FOLDER) { val drawable = if (item.type == ITEM_TYPE_FOLDER) {
item.generateFolderDrawable() item.toFolder().generateDrawable()
} else { } else {
item.drawable!! item.drawable!!
} }
@ -1103,16 +1113,23 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
val folder = currentlyOpenFolder val folder = currentlyOpenFolder
if (folder != null) { if (folder != null) {
val items = folder.getFolderItems() val items = folder.getItems()
val folderRect = folder.getFolderRect() val folderRect = folder.getDrawingRect()
val folderItemsRect = folder.getFolderItemsRect() val folderItemsRect = folder.getItemsDrawingRect()
canvas.drawRoundRect(folderRect, roundedCornerRadius, roundedCornerRadius, folderBackgroundPaint) canvas.drawRoundRect(folderRect, roundedCornerRadius, roundedCornerRadius, folderBackgroundPaint)
canvas.withScale(folder.scale, folder.scale, folderRect.centerX(), folderRect.centerY()) {
val textX = folderRect.left + folderPadding val textX = folderRect.left + folderPadding
val textY = folderRect.top + folderPadding + folderTitleTextPaint.textSize val textY = folderRect.top + folderPadding + folderTitleTextPaint.textSize
val staticLayout = StaticLayout.Builder val staticLayout = StaticLayout.Builder
.obtain(folder.title, 0, folder.title.length, folderTitleTextPaint, (folderRect.width() - 2 * folderPadding).toInt()) .obtain(
folder.item.title,
0,
folder.item.title.length,
folderTitleTextPaint,
(folderRect.width() - 2 * folderPadding * folder.scale).toInt()
)
.setMaxLines(1) .setMaxLines(1)
.setEllipsize(TextUtils.TruncateAt.END) .setEllipsize(TextUtils.TruncateAt.END)
.setAlignment(Layout.Alignment.ALIGN_CENTER) .setAlignment(Layout.Alignment.ALIGN_CENTER)
@ -1124,7 +1141,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
canvas.restore() canvas.restore()
items.forEach { item -> items.forEach { item ->
val (row, column) = item.getPositionInFolder(folder) val (row, column) = folder.getItemPosition(item)
handleGridItemDrawing( handleGridItemDrawing(
item, item,
(folderItemsRect.left + column * cellWidth).roundToInt(), (folderItemsRect.left + column * cellWidth).roundToInt(),
@ -1132,16 +1149,17 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
) )
} }
} }
}
if (draggedItem != null && draggedItemCurrentCoords.first != -1 && draggedItemCurrentCoords.second != -1) { if (draggedItem != null && draggedItemCurrentCoords.first != -1 && draggedItemCurrentCoords.second != -1) {
if (draggedItem!!.type == ITEM_TYPE_ICON || draggedItem!!.type == ITEM_TYPE_SHORTCUT || draggedItem!!.type == ITEM_TYPE_FOLDER) { if (draggedItem!!.type == ITEM_TYPE_ICON || draggedItem!!.type == ITEM_TYPE_SHORTCUT || draggedItem!!.type == ITEM_TYPE_FOLDER) {
if (folder != null && folder.getFolderItemsRect().contains( if (folder != null && folder.getItemsDrawingRect().contains(
(sideMargins.left + draggedItemCurrentCoords.first).toFloat(), (sideMargins.left + draggedItemCurrentCoords.first).toFloat(),
(sideMargins.top + draggedItemCurrentCoords.second).toFloat() (sideMargins.top + draggedItemCurrentCoords.second).toFloat()
) )
) { ) {
val center = folder.getFolderGridCenters().minBy { val center = folder.getItemsGridCenters().minBy {
abs(it.second - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.third - draggedItemCurrentCoords.second + sideMargins.top) abs(it.second - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.third - draggedItemCurrentCoords.second + sideMargins.top)
} }
@ -1261,9 +1279,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
val folder = currentlyOpenFolder val folder = currentlyOpenFolder
val clickableLeft: Int val clickableLeft: Int
val clickableTop: Int val clickableTop: Int
if (folder != null && item.parentId == folder.id) { if (folder != null && item.parentId == folder.item.id) {
val folderRect = folder.getFolderItemsRect() val folderRect = folder.getItemsDrawingRect()
val (row, column) = item.getPositionInFolder(folder) val (row, column) = folder.getItemPosition(item)
clickableLeft = (folderRect.left + column * cellWidth + extraXMargin).toInt() clickableLeft = (folderRect.left + column * cellWidth + extraXMargin).toInt()
clickableTop = (folderRect.top + row * cellHeight - iconMargin + extraYMargin).toInt() clickableTop = (folderRect.top + row * cellHeight - iconMargin + extraYMargin).toInt()
} else { } else {
@ -1305,7 +1323,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
fun isClickingGridItem(x: Int, y: Int): HomeScreenGridItem? { fun isClickingGridItem(x: Int, y: Int): HomeScreenGridItem? {
currentlyOpenFolder?.also { folder -> currentlyOpenFolder?.also { folder ->
folder.getFolderItems().forEach { gridItem -> folder.getItems().forEach { gridItem ->
val rect = getClickableRect(gridItem) val rect = getClickableRect(gridItem)
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) { if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
return gridItem return gridItem
@ -1454,16 +1472,22 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
fun openFolder(folder: HomeScreenGridItem) { fun openFolder(folder: HomeScreenGridItem) {
currentlyOpenFolder = folder if (currentlyOpenFolder == null) {
currentlyOpenFolder = folder.toFolder(animateOpening = true)
redrawGrid() redrawGrid()
} else {
closeFolder()
}
} }
fun closeFolder(redraw: Boolean = false) { fun closeFolder(redraw: Boolean = false) {
currentlyOpenFolder?.animateClosing {
currentlyOpenFolder = null currentlyOpenFolder = null
if (redraw) { if (redraw) {
redrawGrid() redrawGrid()
} }
} }
}
fun getCurrentIconSize(): Int = iconSize fun getCurrentIconSize(): Int = iconSize
@ -1502,10 +1526,43 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
private fun ArrayList<HomeScreenGridItem>.filterVisibleOnly() = filter { (it.page == currentPage || it.docked) && it.parentId == null } private fun ArrayList<HomeScreenGridItem>.filterVisibleOnly() = filter { (it.page == currentPage || it.docked) && it.parentId == null }
private fun HomeScreenGridItem.getFolderItems() = private fun HomeScreenGridItem.toFolder(animateOpening: Boolean = false) = HomeScreenFolder(this, animateOpening)
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.parentId == id }
private fun HomeScreenGridItem.generateFolderDrawable(): Drawable? { private inner class HomeScreenFolder(
val item: HomeScreenGridItem,
animateOpening: Boolean
) {
var scale: Float = 1f
init {
if (animateOpening) {
scale = 0f
post {
ValueAnimator.ofFloat(0f, 1f)
.apply {
interpolator = DecelerateInterpolator()
addUpdateListener {
scale = it.animatedValue as Float
redrawGrid()
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
scale = 1f
redrawGrid()
}
})
duration = FOLDER_ANIMATION_DURATION
start()
}
}
}
}
fun getItems() =
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.parentId == item.id }
fun generateDrawable(): Drawable? {
if (iconSize == 0) { if (iconSize == 0) {
return null return null
} }
@ -1515,8 +1572,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
val circlePath = Path().apply { addCircle((iconSize / 2).toFloat(), (iconSize / 2).toFloat(), (iconSize / 2).toFloat(), Path.Direction.CCW) } val circlePath = Path().apply { addCircle((iconSize / 2).toFloat(), (iconSize / 2).toFloat(), (iconSize / 2).toFloat(), Path.Direction.CCW) }
canvas.clipPath(circlePath) canvas.clipPath(circlePath)
canvas.drawPaint(folderIconBackgroundPaint) canvas.drawPaint(folderIconBackgroundPaint)
val items = getFolderItems() val items = getItems()
val itemsCount = getFolderItems().count() val itemsCount = getItems().count()
val folderColumnCount = ceil(sqrt(itemsCount.toDouble())).roundToInt() val folderColumnCount = ceil(sqrt(itemsCount.toDouble())).roundToInt()
val folderRowCount = ceil(itemsCount.toFloat() / folderColumnCount).roundToInt() val folderRowCount = ceil(itemsCount.toFloat() / folderColumnCount).roundToInt()
val scaledCellSize = (iconSize.toFloat() / folderColumnCount) val scaledCellSize = (iconSize.toFloat() / folderColumnCount)
@ -1524,7 +1581,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
val scaledIconSize = (iconSize - (folderColumnCount + 1) * scaledGap) / folderColumnCount val scaledIconSize = (iconSize - (folderColumnCount + 1) * scaledGap) / folderColumnCount
val extraYMargin = if (folderRowCount < folderColumnCount) (scaledIconSize + scaledGap) / 2 else 0f val extraYMargin = if (folderRowCount < folderColumnCount) (scaledIconSize + scaledGap) / 2 else 0f
items.forEach { items.forEach {
val (row, column) = it.getPositionInFolder(this@generateFolderDrawable) val (row, column) = getItemPosition(it)
val drawableX = (scaledGap + column * scaledIconSize + column * scaledGap).toInt() val drawableX = (scaledGap + column * scaledIconSize + column * scaledGap).toInt()
val drawableY = (extraYMargin + scaledGap + row * scaledIconSize + row * scaledGap).toInt() val drawableY = (extraYMargin + scaledGap + row * scaledIconSize + row * scaledGap).toInt()
it.drawable?.setBounds(drawableX, drawableY, drawableX + scaledIconSize.toInt(), drawableY + scaledIconSize.toInt()) it.drawable?.setBounds(drawableX, drawableY, drawableX + scaledIconSize.toInt(), drawableY + scaledIconSize.toInt())
@ -1534,38 +1591,39 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
return BitmapDrawable(resources, bitmap) return BitmapDrawable(resources, bitmap)
} }
private fun HomeScreenGridItem.getFolderRect(): RectF { fun getDrawingRect(overrideScale: Float? = null): RectF {
val count = getFolderItems().count() val finalScale = overrideScale ?: scale
val count = getItems().count()
if (count == 0) { if (count == 0) {
return RectF(0f, 0f, 0f, 0f) return RectF(0f, 0f, 0f, 0f)
} }
val columnsCount = ceil(sqrt(count.toDouble())).toInt() val columnsCount = ceil(sqrt(count.toDouble())).toInt()
val rowsCount = ceil(count.toFloat() / columnsCount).toInt() val rowsCount = ceil(count.toFloat() / columnsCount).toInt()
val centerX = cellXCoords[left] + cellWidth / 2 + sideMargins.left val centerX = cellXCoords[item.left] + cellWidth / 2 + sideMargins.left
val centerY = cellYCoords[top] + cellHeight / 2 + sideMargins.top val centerY = cellYCoords[item.top] + cellHeight / 2 + sideMargins.top
val folderDialogWidth = (columnsCount * cellWidth + 2 * folderPadding) val folderDialogWidth = (columnsCount * cellWidth + 2 * folderPadding) * finalScale
val folderDialogHeight = (rowsCount * cellHeight + 3 * folderPadding + folderTitleTextPaint.textSize) val folderDialogHeight = (rowsCount * cellHeight + 3 * folderPadding + folderTitleTextPaint.textSize) * finalScale
var folderDialogTop = centerY - folderDialogHeight / 2 var folderDialogTop = centerY - folderDialogHeight / 2
var folderDialogLeft = centerX - folderDialogWidth / 2 var folderDialogLeft = centerX - folderDialogWidth / 2
if (folderDialogLeft < this@HomeScreenGrid.left + sideMargins.left) { if (folderDialogLeft < left + sideMargins.left) {
folderDialogLeft += this@HomeScreenGrid.left + sideMargins.left - folderDialogLeft folderDialogLeft += left + sideMargins.left - folderDialogLeft
} }
if (folderDialogLeft + folderDialogWidth > this@HomeScreenGrid.right - sideMargins.right) { if (folderDialogLeft + folderDialogWidth > right - sideMargins.right) {
folderDialogLeft -= folderDialogLeft + folderDialogWidth - (this@HomeScreenGrid.right - sideMargins.right) folderDialogLeft -= folderDialogLeft + folderDialogWidth - (right - sideMargins.right)
} }
if (folderDialogTop < this@HomeScreenGrid.top + sideMargins.top) { if (folderDialogTop < top + sideMargins.top) {
folderDialogTop += this@HomeScreenGrid.top + sideMargins.top - folderDialogTop folderDialogTop += top + sideMargins.top - folderDialogTop
} }
if (folderDialogTop + folderDialogHeight > this@HomeScreenGrid.bottom - sideMargins.bottom) { if (folderDialogTop + folderDialogHeight > bottom - sideMargins.bottom) {
folderDialogTop -= folderDialogTop + folderDialogHeight - (this@HomeScreenGrid.bottom - sideMargins.bottom) folderDialogTop -= folderDialogTop + folderDialogHeight - (bottom - sideMargins.bottom)
} }
return RectF(folderDialogLeft, folderDialogTop, folderDialogLeft + folderDialogWidth, folderDialogTop + folderDialogHeight) return RectF(folderDialogLeft, folderDialogTop, folderDialogLeft + folderDialogWidth, folderDialogTop + folderDialogHeight)
} }
private fun HomeScreenGridItem.getFolderItemsRect(): RectF { fun getItemsDrawingRect(): RectF {
val folderRect = getFolderRect() val folderRect = getDrawingRect(overrideScale = 1f)
return RectF( return RectF(
folderRect.left + folderPadding, folderRect.left + folderPadding,
folderRect.top + folderPadding * 2 + folderTitleTextPaint.textSize, folderRect.top + folderPadding * 2 + folderTitleTextPaint.textSize,
@ -1574,11 +1632,11 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
) )
} }
private fun HomeScreenGridItem.getFolderGridCenters(): List<Triple<Int, Int, Int>> { fun getItemsGridCenters(): List<Triple<Int, Int, Int>> {
val count = getFolderItems().count() val count = getItems().count()
val columnsCount = ceil(sqrt(count.toDouble())).roundToInt() val columnsCount = ceil(sqrt(count.toDouble())).roundToInt()
val rowsCount = ceil(count.toFloat() / columnsCount).roundToInt() val rowsCount = ceil(count.toFloat() / columnsCount).roundToInt()
val folderItemsRect = getFolderItemsRect() val folderItemsRect = getItemsDrawingRect()
return (0 until columnsCount * rowsCount) return (0 until columnsCount * rowsCount)
.toList() .toList()
.map { Pair(it % columnsCount, it / columnsCount) } .map { Pair(it % columnsCount, it / columnsCount) }
@ -1591,19 +1649,43 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
} }
private fun HomeScreenGridItem.getPositionInFolder(folder: HomeScreenGridItem): Pair<Int, Int> { fun getItemPosition(item: HomeScreenGridItem): Pair<Int, Int> {
val count = folder.getFolderItems().count() val count = getItems().count()
val columnsCount = ceil(sqrt(count.toDouble())).roundToInt() val columnsCount = ceil(sqrt(count.toDouble())).roundToInt()
val column = left % columnsCount val column = item.left % columnsCount
val row = left / columnsCount val row = item.left / columnsCount
return Pair(row, column) return Pair(row, column)
} }
fun animateClosing(callback: () -> Unit) {
post {
ValueAnimator.ofFloat(scale, 0.2f)
.apply {
interpolator = DecelerateInterpolator()
addUpdateListener {
scale = it.animatedValue as Float
redrawGrid()
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
scale = 0.2f
callback()
}
})
duration = (FOLDER_ANIMATION_DURATION * (max(0f, scale - 0.2f))).toLong()
start()
}
}
}
}
companion object { companion object {
private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L
private const val PAGE_INDICATORS_FADE_DELAY = PAGE_CHANGE_HOLD_THRESHOLD + 300L private const val PAGE_INDICATORS_FADE_DELAY = PAGE_CHANGE_HOLD_THRESHOLD + 300L
private const val FOLDER_OPEN_HOLD_THRESHOLD = 500L private const val FOLDER_OPEN_HOLD_THRESHOLD = 500L
private const val FOLDER_CLOSE_HOLD_THRESHOLD = 300L private const val FOLDER_CLOSE_HOLD_THRESHOLD = 300L
private const val FOLDER_ANIMATION_DURATION = 200L
private enum class PageChangeArea { private enum class PageChangeArea {
LEFT, LEFT,
@ -1612,3 +1694,4 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
} }
} }