Simplify drawing and cells logic

This commit is contained in:
Ensar Sarajčić 2023-08-24 12:49:13 +02:00
parent ece3890133
commit 1863a9e6a9
2 changed files with 153 additions and 141 deletions

View File

@ -3,6 +3,7 @@ package com.simplemobiletools.launcher.models
import android.appwidget.AppWidgetProviderInfo import android.appwidget.AppWidgetProviderInfo
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.room.* import androidx.room.*
import com.simplemobiletools.launcher.helpers.ITEM_TYPE_ICON import com.simplemobiletools.launcher.helpers.ITEM_TYPE_ICON
@ -68,4 +69,6 @@ data class HomeScreenGridItem(
} }
fun getItemIdentifier() = "$packageName/$activityName" fun getItemIdentifier() = "$packageName/$activityName"
fun getTopLeft() = Point(left, top)
} }

View File

@ -26,6 +26,7 @@ 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.graphics.withScale
import androidx.core.graphics.withTranslation
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
@ -48,8 +49,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
private lateinit var binding: HomeScreenGridBinding private lateinit var binding: HomeScreenGridBinding
private var columnCount = context.config.homeColumnCount private var columnCount = context.config.homeColumnCount
private var rowCount = context.config.homeRowCount private var rowCount = context.config.homeRowCount
private var cellXCoords = ArrayList<Int>(columnCount) private var dockTop = 0
private var cellYCoords = ArrayList<Int>(rowCount) private val cells = mutableMapOf<Point, Rect>()
var cellWidth = 0 var cellWidth = 0
var cellHeight = 0 var cellHeight = 0
private var extraXMargin = 0 private var extraXMargin = 0
@ -94,7 +95,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
var sideMargins = Rect() var sideMargins = Rect()
private var gridItems = ArrayList<HomeScreenGridItem>() private var gridItems = ArrayList<HomeScreenGridItem>()
private var gridCenters = ArrayList<Pair<Int, Int>>() private var gridCenters = ArrayList<Point>()
private var draggedItemCurrentCoords = Pair(-1, -1) private var draggedItemCurrentCoords = Pair(-1, -1)
private var widgetViews = ArrayList<MyAppWidgetHostView>() private var widgetViews = ArrayList<MyAppWidgetHostView>()
@ -201,8 +202,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
if (columnCount != newColumnCount || rowCount != newRowCount) { if (columnCount != newColumnCount || rowCount != newRowCount) {
rowCount = newRowCount rowCount = newRowCount
columnCount = newColumnCount columnCount = newColumnCount
cellXCoords = ArrayList(columnCount) cells.clear()
cellYCoords = ArrayList(rowCount)
gridCenters.clear() gridCenters.clear()
iconMargin = (context.resources.getDimension(R.dimen.icon_side_margin) * 5 / columnCount).toInt() iconMargin = (context.resources.getDimension(R.dimen.icon_side_margin) * 5 / columnCount).toInt()
redrawWidgets = true redrawWidgets = true
@ -297,11 +297,11 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
draggedItemCurrentCoords = Pair(x, y) draggedItemCurrentCoords = Pair(x, y)
val center = gridCenters.minBy { val center = gridCenters.minBy {
abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.second - draggedItemCurrentCoords.second + sideMargins.top) abs(it.x - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.y - draggedItemCurrentCoords.second + sideMargins.top)
} }
val coveredCell = getClosestGridCells(center) val coveredCell = getClosestGridCells(center)
if (coveredCell != null) { if (coveredCell != null) {
val coveredFolder = gridItems.firstOrNull { it.type == ITEM_TYPE_FOLDER && it.left == coveredCell.first && it.top == coveredCell.second } val coveredFolder = gridItems.firstOrNull { it.type == ITEM_TYPE_FOLDER && it.left == coveredCell.x && it.top == coveredCell.y }
if (coveredFolder != null && coveredFolder.id != draggedItem?.id) { if (coveredFolder != null && coveredFolder.id != draggedItem?.id) {
draggingEnteredNewFolderAt.also { draggingEnteredNewFolderAt.also {
if (it == null) { if (it == null) {
@ -404,14 +404,14 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
private fun moveItem() { private fun moveItem() {
val draggedHomeGridItem = gridItems.firstOrNull { it.id == draggedItem?.id } val draggedHomeGridItem = gridItems.firstOrNull { it.id == draggedItem?.id }
val center = gridCenters.minBy { val center = gridCenters.minBy {
abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.second - draggedItemCurrentCoords.second + sideMargins.top) abs(it.x - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.y - draggedItemCurrentCoords.second + sideMargins.top)
} }
var redrawIcons = false var redrawIcons = false
val gridCells = getClosestGridCells(center) val gridCells = getClosestGridCells(center)
if (gridCells != null) { if (gridCells != null) {
val xIndex = gridCells.first val xIndex = gridCells.x
val yIndex = gridCells.second val yIndex = gridCells.y
// check if the destination cell is empty // check if the destination cell is empty
var isDroppingPositionValid = true var isDroppingPositionValid = true
@ -420,7 +420,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
if (draggedHomeGridItem?.type == ITEM_TYPE_FOLDER && yIndex == rowCount - 1) { if (draggedHomeGridItem?.type == ITEM_TYPE_FOLDER && yIndex == rowCount - 1) {
isDroppingPositionValid = false isDroppingPositionValid = false
} else { } else {
gridItems.filterVisibleOnly().forEach { item -> gridItems.filterVisibleOnCurrentPageOnly().forEach { item ->
for (xCell in item.left..item.right) { for (xCell in item.left..item.right) {
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) { for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
val cell = Pair(xCell, yCell) val cell = Pair(xCell, yCell)
@ -481,18 +481,18 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
redrawIcons = true redrawIcons = true
} else { } else {
val center = gridCenters.minBy { val center = gridCenters.minBy {
Math.abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + Math.abs(it.second - draggedItemCurrentCoords.second + sideMargins.top) Math.abs(it.x - draggedItemCurrentCoords.first + sideMargins.left) + Math.abs(it.y - draggedItemCurrentCoords.second + sideMargins.top)
} }
val gridCells = getClosestGridCells(center) val gridCells = getClosestGridCells(center)
if (gridCells != null) { if (gridCells != null) {
xIndex = gridCells.first xIndex = gridCells.x
yIndex = gridCells.second yIndex = gridCells.y
// check if the destination cell is empty or a folder // check if the destination cell is empty or a folder
isDroppingPositionValid = true isDroppingPositionValid = true
val wantedCell = Pair(xIndex, yIndex) val wantedCell = Pair(xIndex, yIndex)
gridItems.filterVisibleOnly().forEach { item -> gridItems.filterVisibleOnCurrentPageOnly().forEach { item ->
for (xCell in item.left..item.right) { for (xCell in item.left..item.right) {
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) { for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
val cell = Pair(xCell, yCell) val cell = Pair(xCell, yCell)
@ -700,7 +700,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
private fun addWidget() { private fun addWidget() {
val center = gridCenters.minBy { val center = gridCenters.minBy {
Math.abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + Math.abs(it.second - draggedItemCurrentCoords.second + sideMargins.top) Math.abs(it.x - draggedItemCurrentCoords.first + sideMargins.left) + Math.abs(it.y - draggedItemCurrentCoords.second + sideMargins.top)
} }
val gridCells = getClosestGridCells(center) val gridCells = getClosestGridCells(center)
@ -714,7 +714,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
var areAllCellsEmpty = true var areAllCellsEmpty = true
gridItems.filterVisibleOnly().filter { it.id != draggedItem?.id }.forEach { item -> gridItems.filterVisibleOnCurrentPageOnly().filter { it.id != draggedItem?.id }.forEach { item ->
for (xCell in item.left..item.right) { for (xCell in item.left..item.right) {
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) { for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
val cell = Pair(xCell, yCell) val cell = Pair(xCell, yCell)
@ -759,8 +759,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
val widgetView = widgetViews.firstOrNull { it.tag == widgetItem.widgetId } val widgetView = widgetViews.firstOrNull { it.tag == widgetItem.widgetId }
if (widgetView != null && !widgetItem.outOfBounds()) { if (widgetView != null && !widgetItem.outOfBounds()) {
post { post {
widgetView.x = calculateWidgetX(widgetItem.left) val widgetPos = calculateWidgetPos(widgetItem.getTopLeft())
widgetView.y = calculateWidgetY(widgetItem.top) widgetView.x = widgetPos.x.toFloat()
widgetView.y = widgetPos.y.toFloat()
widgetView.beVisible() widgetView.beVisible()
} }
} }
@ -847,9 +848,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
private fun updateWidgetPositionAndSize(widgetView: AppWidgetHostView, item: HomeScreenGridItem): Size { private fun updateWidgetPositionAndSize(widgetView: AppWidgetHostView, item: HomeScreenGridItem): Size {
val currentViewPosition = pager.getCurrentViewPositionInFullPageSpace() * width.toFloat() val currentViewPosition = pager.getCurrentViewPositionInFullPageSpace() * width.toFloat()
val x = calculateWidgetX(item.left) + width * item.page - currentViewPosition val widgetPos = calculateWidgetPos(item.getTopLeft())
widgetView.x = x widgetView.x = widgetPos.x + width * item.page - currentViewPosition
widgetView.y = calculateWidgetY(item.top) widgetView.y = widgetPos.y.toFloat()
val widgetWidth = item.getWidthInCells() * cellWidth val widgetWidth = item.getWidthInCells() * cellWidth
val widgetHeight = item.getHeightInCells() * cellHeight val widgetHeight = item.getHeightInCells() * cellHeight
@ -869,21 +870,17 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
return Size(widgetWidth, widgetHeight) return Size(widgetWidth, widgetHeight)
} }
private fun calculateWidgetX(leftCell: Int) = cellXCoords[leftCell] + sideMargins.left.toFloat() + extraXMargin private fun calculateWidgetPos(topLeft: Point): Point {
val cell = cells[topLeft]!!
private fun calculateWidgetY(topCell: Int) = cellYCoords[topCell] + sideMargins.top.toFloat() + extraYMargin return Point(
cell.left + sideMargins.left + extraXMargin,
cell.top + sideMargins.top + extraYMargin
)
}
// convert stuff like 102x192 to grid cells like 0x1 // convert stuff like 102x192 to grid cells like 0x1
private fun getClosestGridCells(center: Pair<Int, Int>): Pair<Int, Int>? { private fun getClosestGridCells(center: Point): Point? {
cellXCoords.forEachIndexed { xIndex, xCell -> return cells.entries.firstOrNull { (_, cell) -> center.x == cell.centerX() && center.y == cell.centerY() }?.key
cellYCoords.forEachIndexed { yIndex, yCell ->
if (xCell + cellWidth / 2 == center.first && yCell + cellHeight / 2 == center.second) {
return Pair(xIndex, yIndex)
}
}
}
return null
} }
private fun redrawGrid() { private fun redrawGrid() {
@ -900,66 +897,22 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
@SuppressLint("DrawAllocation") @SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
super.onDraw(canvas) super.onDraw(canvas)
if (cellXCoords.isEmpty()) { if (cells.isEmpty()) {
fillCellSizes() fillCellSizes()
} }
val currentXFactor = pager.getXFactorForCurrentPage() val currentXFactor = pager.getXFactorForCurrentPage()
val lastXFactor = pager.getXFactorForLastPage() val lastXFactor = pager.getXFactorForLastPage()
fun handleGridItemDrawing(item: HomeScreenGridItem, baseItemX: Int, baseItemY: Int) {
if (item.id != draggedItem?.id) {
val drawableX = baseItemX + iconMargin + extraXMargin
val drawable = if (item.type == ITEM_TYPE_FOLDER) {
item.toFolder().generateDrawable()
} else {
item.drawable!!
}
if (item.docked) {
val drawableY = cellYCoords[rowCount - 1] + cellHeight - iconMargin - iconSize + sideMargins.top
drawable?.setBounds(drawableX, drawableY, drawableX + iconSize, drawableY + iconSize)
} else {
val drawableY = baseItemY + iconMargin + extraYMargin
drawable?.setBounds(drawableX, drawableY, drawableX + iconSize, drawableY + iconSize)
if (item.id != draggedItem?.id && item.title.isNotEmpty()) {
val textX = baseItemX.toFloat() + labelSideMargin
val textY = baseItemY.toFloat() + iconSize + iconMargin + extraYMargin + labelSideMargin
val textPaintToUse = if (item.parentId == null) {
textPaint
} else {
contrastTextPaint
}
val staticLayout = StaticLayout.Builder
.obtain(item.title, 0, item.title.length, textPaintToUse, cellWidth - 2 * labelSideMargin)
.setMaxLines(2)
.setEllipsize(TextUtils.TruncateAt.END)
.setAlignment(Layout.Alignment.ALIGN_CENTER)
.build()
canvas.save()
canvas.translate(textX, textY)
staticLayout.draw(canvas)
canvas.restore()
}
}
drawable?.draw(canvas)
}
}
fun handleMainGridItemDrawing(item: HomeScreenGridItem, xFactor: Float) { fun handleMainGridItemDrawing(item: HomeScreenGridItem, xFactor: Float) {
handleGridItemDrawing( val offsetX = sideMargins.left + (this@HomeScreenGrid.width * xFactor).toInt()
item, val offsetY = sideMargins.top
cellXCoords[item.left] + sideMargins.left + (width * xFactor).toInt(), cells[item.getTopLeft()]!!.withOffset(offsetX, offsetY) {
cellYCoords[item.top] + sideMargins.top canvas.drawItemInCell(item, this)
) }
} }
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 } gridItems.filter { it.isSingleCellType() && pager.isItemOnCurrentPage(it) && !it.docked && it.parentId == null }
.forEach { item -> .forEach { item ->
if (item.outOfBounds()) { if (item.outOfBounds()) {
return@forEach return@forEach
@ -967,7 +920,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
handleMainGridItemDrawing(item, currentXFactor) handleMainGridItemDrawing(item, currentXFactor)
} }
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT || it.type == ITEM_TYPE_FOLDER) && it.docked && it.parentId == null } gridItems.filter { it.isSingleCellType() && it.docked && it.parentId == null }
.forEach { item -> .forEach { item ->
if (item.outOfBounds()) { if (item.outOfBounds()) {
return@forEach return@forEach
@ -976,7 +929,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
handleMainGridItemDrawing(item, 0f) handleMainGridItemDrawing(item, 0f)
} }
if (pager.isAnimatingPageChange()) { 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 } gridItems.filter { it.isSingleCellType() && pager.isItemOnLastPage(it) && !it.docked && it.parentId == null }
.forEach { item -> .forEach { item ->
if (item.outOfBounds()) { if (item.outOfBounds()) {
return@forEach return@forEach
@ -988,7 +941,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
if (pager.isSwiped()) { if (pager.isSwiped()) {
gridItems.filter { gridItems.filter {
(it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT || it.type == ITEM_TYPE_FOLDER) it.isSingleCellType()
&& pager.isItemInSwipeRange(it) && pager.isItemInSwipeRange(it)
&& !it.docked && !it.docked
&& it.parentId == null && it.parentId == null
@ -1020,7 +973,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
val usableWidth = getFakeWidth() val usableWidth = getFakeWidth()
val pageIndicatorsStart = (usableWidth - pageIndicatorsRequiredWidth) / 2 + sideMargins.left val pageIndicatorsStart = (usableWidth - pageIndicatorsRequiredWidth) / 2 + sideMargins.left
var currentPageIndicatorLeft = pageIndicatorsStart var currentPageIndicatorLeft = pageIndicatorsStart
val pageIndicatorY = cellYCoords[rowCount - 1].toFloat() + sideMargins.top + extraYMargin + iconMargin val pageIndicatorY = dockTop.toFloat() + sideMargins.top + extraYMargin + iconMargin
val pageIndicatorStep = pageIndicatorRadius * 2 + pageIndicatorMargin val pageIndicatorStep = pageIndicatorRadius * 2 + pageIndicatorMargin
emptyPageIndicatorPaint.alpha = pager.getPageChangeIndicatorsAlpha() emptyPageIndicatorPaint.alpha = pager.getPageChangeIndicatorsAlpha()
// Draw empty page indicators // Draw empty page indicators
@ -1063,24 +1016,27 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
.setAlignment(Layout.Alignment.ALIGN_CENTER) .setAlignment(Layout.Alignment.ALIGN_CENTER)
.build() .build()
canvas.save() withTranslation(textX, textY) {
canvas.translate(textX, textY)
staticLayout.draw(canvas) staticLayout.draw(canvas)
canvas.restore() }
items.forEach { item -> items.forEach { item ->
val (row, column) = folder.getItemPosition(item) val (row, column) = folder.getItemPosition(item)
handleGridItemDrawing( val left = (folderItemsRect.left + column * cellWidth).roundToInt()
item, val top = (folderItemsRect.top + row * cellHeight).roundToInt()
(folderItemsRect.left + column * cellWidth).roundToInt(), val rect = Rect(
(folderItemsRect.top + row * cellHeight).roundToInt() left,
top,
left + cellWidth,
top + cellHeight
) )
canvas.drawItemInCell(item, rect)
} }
} }
} }
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!!.isSingleCellType()) {
if (folder != null && folder.getItemsDrawingRect().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()
@ -1098,16 +1054,17 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} else { } else {
// draw a circle under the current cell // draw a circle under the current cell
val center = gridCenters.minBy { val center = gridCenters.minBy {
abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.second - draggedItemCurrentCoords.second + sideMargins.top) abs(it.x - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.y - draggedItemCurrentCoords.second + sideMargins.top)
} }
val gridCells = getClosestGridCells(center) val gridCells = getClosestGridCells(center)
if (gridCells != null) { if (gridCells != null) {
val shadowX = cellXCoords[gridCells.first] + iconMargin + iconSize / 2f + extraXMargin + sideMargins.left val cell = cells[gridCells]!!
val shadowY = if (gridCells.second == rowCount - 1) { val shadowX = cell.left + iconMargin + iconSize / 2f + extraXMargin + sideMargins.left
cellYCoords[gridCells.second] + cellHeight - iconMargin - iconSize / 2f val shadowY = if (gridCells.y == rowCount - 1) {
cell.top + cellHeight - iconMargin - iconSize / 2f
} else { } else {
cellYCoords[gridCells.second] + iconMargin + iconSize / 2f + extraYMargin cell.top + iconMargin + iconSize / 2f + extraYMargin
} + sideMargins.top } + sideMargins.top
canvas.drawCircle(shadowX, shadowY, iconSize / 2f, dragShadowCirclePaint) canvas.drawCircle(shadowX, shadowY, iconSize / 2f, dragShadowCirclePaint)
@ -1123,14 +1080,15 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
// at first draw we are loading the widget from the database at some exact spot, not dragging it // at first draw we are loading the widget from the database at some exact spot, not dragging it
if (!isFirstDraw) { if (!isFirstDraw) {
val center = gridCenters.minBy { val center = gridCenters.minBy {
Math.abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + Math.abs(it.second - draggedItemCurrentCoords.second + sideMargins.top) Math.abs(it.x - draggedItemCurrentCoords.first + sideMargins.left) + Math.abs(it.y - draggedItemCurrentCoords.second + sideMargins.top)
} }
val gridCells = getClosestGridCells(center) val gridCells = getClosestGridCells(center)
if (gridCells != null) { if (gridCells != null) {
val widgetRect = getWidgetOccupiedRect(gridCells) val widgetRect = getWidgetOccupiedRect(gridCells)
val leftSide = calculateWidgetX(widgetRect.left) val widgetPos = calculateWidgetPos(Point(widgetRect.left, widgetRect.top))
val topSide = calculateWidgetY(widgetRect.top) val leftSide = widgetPos.x.toFloat()
val topSide = widgetPos.y.toFloat()
val rightSide = leftSide + draggedItem!!.getWidthInCells() * cellWidth val rightSide = leftSide + draggedItem!!.getWidthInCells() * cellWidth
val bottomSide = topSide + draggedItem!!.getHeightInCells() * cellHeight val bottomSide = topSide + draggedItem!!.getHeightInCells() * cellHeight
canvas.drawRoundRect(leftSide, topSide, rightSide, bottomSide, roundedCornerRadius, roundedCornerRadius, dragShadowCirclePaint) canvas.drawRoundRect(leftSide, topSide, rightSide, bottomSide, roundedCornerRadius, roundedCornerRadius, dragShadowCirclePaint)
@ -1170,17 +1128,17 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
0 0
} }
iconSize = min(cellWidth, cellHeight) - 2 * iconMargin iconSize = min(cellWidth, cellHeight) - 2 * iconMargin
dockTop = (context.config.homeRowCount - 1) * cellHeight
for (i in 0 until context.config.homeColumnCount) { for (i in 0 until context.config.homeColumnCount) {
cellXCoords.add(i, i * cellWidth) for (j in 0 until context.config.homeRowCount) {
} val rect = Rect(
i * cellWidth,
for (i in 0 until context.config.homeRowCount) { j * cellHeight,
cellYCoords.add(i, i * cellHeight) (i + 1) * cellWidth,
} (j + 1) * cellHeight,
)
cellXCoords.forEach { x -> cells[Point(i, j)] = rect
cellYCoords.forEach { y -> gridCenters.add(Point(rect.centerX(), rect.centerY()))
gridCenters.add(Pair(x + cellWidth / 2, y + cellHeight / 2))
} }
} }
} }
@ -1200,7 +1158,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
// get the clickable area around the icon, it includes text too // get the clickable area around the icon, it includes text too
fun getClickableRect(item: HomeScreenGridItem): Rect { fun getClickableRect(item: HomeScreenGridItem): Rect {
if (cellXCoords.isEmpty()) { if (cells.isEmpty()) {
fillCellSizes() fillCellSizes()
} }
@ -1213,11 +1171,12 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
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 {
clickableLeft = cellXCoords[item.left] + sideMargins.left + extraXMargin val cell = cells[item.getTopLeft()]!!
clickableLeft = cell.left + sideMargins.left + extraXMargin
clickableTop = if (item.docked) { clickableTop = if (item.docked) {
cellYCoords[item.getDockAdjustedTop(rowCount)] + cellHeight - iconSize - iconMargin dockTop + cellHeight - iconSize - iconMargin
} else { } else {
cellYCoords[item.top] - iconMargin + extraYMargin cell.top - iconMargin + extraYMargin
} + sideMargins.top } + sideMargins.top
} }
@ -1225,9 +1184,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
// drag the center of the widget, not the top left corner // drag the center of the widget, not the top left corner
private fun getWidgetOccupiedRect(item: Pair<Int, Int>): Rect { private fun getWidgetOccupiedRect(item: Point): Rect {
val left = item.first - floor((draggedItem!!.getWidthInCells() - 1) / 2.0).toInt() val left = item.x - floor((draggedItem!!.getWidthInCells() - 1) / 2.0).toInt()
val rect = Rect(left, item.second, left + draggedItem!!.getWidthInCells() - 1, item.second + draggedItem!!.getHeightInCells() - 1) val rect = Rect(left, item.y, left + draggedItem!!.getWidthInCells() - 1, item.y + draggedItem!!.getHeightInCells() - 1)
if (rect.left < 0) { if (rect.left < 0) {
rect.right -= rect.left rect.right -= rect.left
rect.left = 0 rect.left = 0
@ -1259,19 +1218,20 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
} }
for (gridItem in gridItems.filterVisibleOnly()) { for (gridItem in gridItems.filterVisibleOnCurrentPageOnly()) {
if (gridItem.outOfBounds()) { if (gridItem.outOfBounds()) {
continue continue
} }
if (gridItem.type == ITEM_TYPE_ICON || gridItem.type == ITEM_TYPE_SHORTCUT || gridItem.type == ITEM_TYPE_FOLDER) { if (gridItem.isSingleCellType()) {
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
} }
} else if (gridItem.type == ITEM_TYPE_WIDGET) { } else if (gridItem.type == ITEM_TYPE_WIDGET) {
val left = calculateWidgetX(gridItem.left) val widgetPos = calculateWidgetPos(gridItem.getTopLeft())
val top = calculateWidgetY(gridItem.top) val left = widgetPos.x.toFloat()
val top = widgetPos.y.toFloat()
val right = left + gridItem.getWidthInCells() * cellWidth val right = left + gridItem.getWidthInCells() * cellWidth
val bottom = top + gridItem.getHeightInCells() * cellHeight val bottom = top + gridItem.getHeightInCells() * cellHeight
@ -1293,7 +1253,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
private fun HomeScreenGridItem.outOfBounds(): Boolean { private fun HomeScreenGridItem.outOfBounds(): Boolean {
return (left >= cellXCoords.size || right >= cellXCoords.size || (!docked && (top >= cellYCoords.size - 1 || bottom >= cellYCoords.size - 1))) return (left >= columnCount || right >= columnCount || (!docked && (top >= rowCount - 1 || bottom >= rowCount - 1)))
} }
private inner class HomeScreenGridTouchHelper(host: View) : ExploreByTouchHelper(host) { private inner class HomeScreenGridTouchHelper(host: View) : ExploreByTouchHelper(host) {
@ -1398,7 +1358,63 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
} }
private fun ArrayList<HomeScreenGridItem>.filterVisibleOnly() = filter { (pager.isItemOnCurrentPage(it) || it.docked) && it.parentId == null } private fun Canvas.drawItemInCell(item: HomeScreenGridItem, cell: Rect) {
if (item.id != draggedItem?.id) {
val drawableX = cell.left + iconMargin + extraXMargin
val drawable = if (item.type == ITEM_TYPE_FOLDER) {
item.toFolder().generateDrawable()
} else {
item.drawable!!
}
if (item.docked) {
val drawableY = dockTop + cellHeight - iconMargin - iconSize + sideMargins.top
drawable?.setBounds(drawableX, drawableY, drawableX + iconSize, drawableY + iconSize)
} else {
val drawableY = cell.top + iconMargin + extraYMargin
drawable?.setBounds(drawableX, drawableY, drawableX + iconSize, drawableY + iconSize)
if (item.id != draggedItem?.id && item.title.isNotEmpty()) {
val textX = cell.left.toFloat() + labelSideMargin
val textY = cell.top.toFloat() + iconSize + iconMargin + extraYMargin + labelSideMargin
val textPaintToUse = if (item.parentId == null) {
textPaint
} else {
contrastTextPaint
}
val staticLayout = StaticLayout.Builder
.obtain(item.title, 0, item.title.length, textPaintToUse, cellWidth - 2 * labelSideMargin)
.setMaxLines(2)
.setEllipsize(TextUtils.TruncateAt.END)
.setAlignment(Layout.Alignment.ALIGN_CENTER)
.build()
withTranslation(textX, textY) {
staticLayout.draw(this)
}
}
}
drawable?.draw(this)
}
}
private fun Rect.withOffset(offsetX: Int, offsetY: Int, block: Rect.() -> Unit) {
offset(offsetX, offsetY)
try {
block()
} finally {
offset(-offsetX, -offsetY)
}
}
private fun ArrayList<HomeScreenGridItem>.filterVisibleOnCurrentPageOnly() = filter { it.visibleOnCurrentPage() }
private fun HomeScreenGridItem.visibleOnCurrentPage() = (pager.isItemOnCurrentPage(this) || docked) && parentId == null
private fun HomeScreenGridItem.isSingleCellType() = (drawable != null && type == ITEM_TYPE_ICON || type == ITEM_TYPE_SHORTCUT || type == ITEM_TYPE_FOLDER)
private fun HomeScreenGridItem.toFolder(animateOpening: Boolean = false) = HomeScreenFolder(this, animateOpening) private fun HomeScreenGridItem.toFolder(animateOpening: Boolean = false) = HomeScreenFolder(this, animateOpening)
@ -1435,7 +1451,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
fun getItems() = fun getItems() =
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.parentId == item.id } gridItems.filter { it.isSingleCellType() && it.parentId == item.id }
fun generateDrawable(): Drawable? { fun generateDrawable(): Drawable? {
if (iconSize == 0) { if (iconSize == 0) {
@ -1474,8 +1490,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
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[item.left] + cellWidth / 2 + sideMargins.left val cell = cells[item.getTopLeft()]!!
val centerY = cellYCoords[item.top] + cellHeight / 2 + sideMargins.top val centerX = sideMargins.left + cell.centerX()
val centerY = sideMargins.top + cell.centerY()
val folderDialogWidth = (columnsCount * cellWidth + 2 * folderPadding) * finalScale val folderDialogWidth = (columnsCount * cellWidth + 2 * folderPadding) * finalScale
val folderDialogHeight = (rowsCount * cellHeight + 3 * folderPadding + folderTitleTextPaint.textSize) * finalScale val folderDialogHeight = (rowsCount * cellHeight + 3 * folderPadding + folderTitleTextPaint.textSize) * finalScale
var folderDialogTop = centerY - folderDialogHeight / 2 var folderDialogTop = centerY - folderDialogHeight / 2
@ -1560,17 +1577,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
} }
companion object { companion object {
private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L
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 const val FOLDER_ANIMATION_DURATION = 200L
private enum class PageChangeArea {
LEFT,
MIDDLE,
RIGHT
}
} }
} }