Handle moving items inside a folder

This commit is contained in:
Ensar Sarajčić 2023-08-18 17:09:15 +02:00
parent 10603be76b
commit 62686f48c3
2 changed files with 171 additions and 88 deletions

View File

@ -44,8 +44,8 @@ interface HomeScreenGridItemsDao {
@Query("DELETE FROM home_screen_grid_items WHERE parent_id IN (SELECT id FROM home_screen_grid_items WHERE package_name = :packageName)")
fun deleteItemsByParentPackageName(packageName: String)
@Query("UPDATE home_screen_grid_items SET `left` = `left` + :shiftBy WHERE parent_id == :folderId AND `left` >= :shiftFrom")
fun shiftFolderItems(folderId: Long, shiftFrom: Int, shiftBy: Int)
@Query("UPDATE home_screen_grid_items SET `left` = `left` + :shiftBy WHERE parent_id == :folderId AND `left` > :shiftFrom AND id != :excludingId")
fun shiftFolderItems(folderId: Long, shiftFrom: Int, shiftBy: Int, excludingId: Long)
@Transaction
fun deleteByPackageName(packageName: String) {

View File

@ -250,6 +250,11 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
fun itemDraggingStarted(draggedGridItem: HomeScreenGridItem) {
draggedItem = draggedGridItem
if (draggedGridItem.type == ITEM_TYPE_WIDGET) {
closeFolder()
}
if (draggedItem!!.drawable == null) {
if (draggedItem?.type == ITEM_TYPE_FOLDER) {
draggedItem!!.drawable =
@ -424,14 +429,19 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
// check if the destination cell is empty
var isDroppingPositionValid = true
val wantedCell = Pair(xIndex, yIndex)
gridItems.filterVisibleOnly().forEach { item ->
for (xCell in item.left..item.right) {
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
val cell = Pair(xCell, yCell)
val isAnyCellOccupied = wantedCell == cell
if (isAnyCellOccupied) {
isDroppingPositionValid = false
return@forEach
// No moving folder into the dock
if (draggedHomeGridItem?.type == ITEM_TYPE_FOLDER && yIndex == rowCount - 1) {
isDroppingPositionValid = false
} else {
gridItems.filterVisibleOnly().forEach { item ->
for (xCell in item.left..item.right) {
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
val cell = Pair(xCell, yCell)
val isAnyCellOccupied = wantedCell == cell
if (isAnyCellOccupied) {
isDroppingPositionValid = false
return@forEach
}
}
}
}
@ -462,72 +472,92 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
}
private fun addAppIconOrShortcut() {
val center = gridCenters.minBy {
Math.abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + Math.abs(it.second - draggedItemCurrentCoords.second + sideMargins.top)
var isDroppingPositionValid: Boolean = false
var potentialParent: HomeScreenGridItem? = null
var xIndex: Int? = null
var yIndex: Int? = null
var redrawIcons = false
val folder = currentlyOpenFolder
if (folder != null && folder.getFolderItemsRect().contains(
(sideMargins.left + draggedItemCurrentCoords.first).toFloat(),
(sideMargins.top + draggedItemCurrentCoords.second).toFloat()
)
) {
val center = folder.getFolderGridCenters().minBy {
abs(it.second - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.third - draggedItemCurrentCoords.second + sideMargins.top)
}
isDroppingPositionValid = true
potentialParent = folder
xIndex = center.first
yIndex = 0
redrawIcons = true
} else {
val center = gridCenters.minBy {
Math.abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + Math.abs(it.second - draggedItemCurrentCoords.second + sideMargins.top)
}
val gridCells = getClosestGridCells(center)
if (gridCells != null) {
xIndex = gridCells.first
yIndex = gridCells.second
// check if the destination cell is empty or a folder
isDroppingPositionValid = true
val wantedCell = Pair(xIndex, yIndex)
gridItems.filterVisibleOnly().forEach { item ->
for (xCell in item.left..item.right) {
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
val cell = Pair(xCell, yCell)
val isAnyCellOccupied = wantedCell == cell
if (isAnyCellOccupied) {
if (item.type != ITEM_TYPE_WIDGET && !item.docked) {
potentialParent = item
} else {
isDroppingPositionValid = false
}
return@forEach
}
}
}
}
}
}
var redrawIcons = false
val gridCells = getClosestGridCells(center)
if (gridCells != null) {
val xIndex = gridCells.first
val yIndex = gridCells.second
if (isDroppingPositionValid) {
val draggedHomeGridItem = gridItems.firstOrNull { it.id == draggedItem?.id }
// check if the destination cell is empty or a folder
var isDroppingPositionValid = true
var potentialParent: HomeScreenGridItem? = null
val wantedCell = Pair(xIndex, yIndex)
gridItems.filterVisibleOnly().forEach { item ->
for (xCell in item.left..item.right) {
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
val cell = Pair(xCell, yCell)
val isAnyCellOccupied = wantedCell == cell
if (isAnyCellOccupied) {
if (item.type != ITEM_TYPE_WIDGET) {
potentialParent = item
} else {
isDroppingPositionValid = false
}
return@forEach
}
}
}
}
if (isDroppingPositionValid) {
val draggedHomeGridItem = gridItems.firstOrNull { it.id == draggedItem?.id }
if (potentialParent != null) {
if (potentialParent?.type == ITEM_TYPE_FOLDER) {
addAppIconOrShortcut(draggedHomeGridItem, xIndex, yIndex, potentialParent?.id)
} else {
val parentItem = potentialParent!!.copy(
type = ITEM_TYPE_FOLDER,
id = null,
title = resources.getString(com.simplemobiletools.commons.R.string.folder)
)
ensureBackgroundThread {
val newId = context.homeScreenGridItemsDB.insert(parentItem)
parentItem.id = newId
potentialParent?.apply {
parentId = newId
left = 0
context.homeScreenGridItemsDB.updateItemPosition(left, top, right, bottom, page, docked, newId, id!!)
}
(context as? MainActivity)?.runOnUiThread {
gridItems.add(parentItem)
addAppIconOrShortcut(draggedHomeGridItem, xIndex, yIndex, newId)
}
}
}
return
if (potentialParent != null) {
if (potentialParent?.type == ITEM_TYPE_FOLDER) {
addAppIconOrShortcut(draggedHomeGridItem, xIndex!!, yIndex!!, potentialParent?.id, toFolderEnd = potentialParent != currentlyOpenFolder)
} else {
addAppIconOrShortcut(draggedHomeGridItem, xIndex, yIndex)
return
val parentItem = potentialParent!!.copy(
type = ITEM_TYPE_FOLDER,
id = null,
title = resources.getString(com.simplemobiletools.commons.R.string.folder)
)
ensureBackgroundThread {
val newId = context.homeScreenGridItemsDB.insert(parentItem)
parentItem.id = newId
potentialParent?.apply {
parentId = newId
left = 0
context.homeScreenGridItemsDB.updateItemPosition(left, top, right, bottom, page, docked, newId, id!!)
}
(context as? MainActivity)?.runOnUiThread {
gridItems.add(parentItem)
addAppIconOrShortcut(draggedHomeGridItem, xIndex!!, yIndex!!, newId)
}
}
}
return
} else {
performHapticFeedback()
redrawIcons = true
addAppIconOrShortcut(draggedHomeGridItem, xIndex!!, yIndex!!)
return
}
} else {
performHapticFeedback()
redrawIcons = true
}
draggedItem = null
@ -537,12 +567,24 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
}
}
private fun addAppIconOrShortcut(draggedHomeGridItem: HomeScreenGridItem?, xIndex: Int, yIndex: Int, newParentId: Long? = null) {
private fun addAppIconOrShortcut(
draggedHomeGridItem: HomeScreenGridItem?,
xIndex: Int,
yIndex: Int,
newParentId: Long? = null,
toFolderEnd: Boolean = true
) {
val finalXIndex = if (newParentId != null) {
if (newParentId == draggedHomeGridItem?.parentId) {
draggedHomeGridItem.left
} else {
if (toFolderEnd) {
gridItems.firstOrNull { it.id == newParentId }?.getFolderItems()?.maxOf { it.left + 1 } ?: 0
} else {
min(xIndex, gridItems.firstOrNull { it.id == newParentId }?.getFolderItems()?.maxOf {
if (draggedHomeGridItem?.parentId == newParentId) {
it.left
} else {
it.left + 1
}
} ?: 0)
}
} else {
xIndex
@ -572,11 +614,19 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
context.homeScreenGridItemsDB.updateItemPosition(left, top, right, bottom, page, docked, newParentId, id!!)
if (deleteOldParent && oldParentId != null) {
context.homeScreenGridItemsDB.deleteById(oldParentId)
} else if (oldParentId != null) {
gridItems.filter { it.parentId == oldParentId && it.left >= oldLeft }.forEach {
} else if (oldParentId != null && gridItems.none { it.parentId == oldParentId && it.left == oldLeft }) {
gridItems.filter { it.parentId == oldParentId && it.left > oldLeft && it.id != id }.forEach {
it.left -= 1
}
context.homeScreenGridItemsDB.shiftFolderItems(oldParentId, oldLeft, -1)
context.homeScreenGridItemsDB.shiftFolderItems(oldParentId, oldLeft, -1, id!!)
}
if (newParentId != null && gridItems.any { it.parentId == newParentId && it.left == left }) {
gridItems.filter { it.parentId == newParentId && it.left >= left && it.id != id }.forEach {
it.left += 1
}
context.homeScreenGridItemsDB.shiftFolderItems(newParentId, left - 1, +1, id!!)
}
}
}
@ -1024,21 +1074,37 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
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) {
// draw a circle under the current cell
val center = gridCenters.minBy {
abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.second - draggedItemCurrentCoords.second + sideMargins.top)
}
if (folder != null && folder.getFolderItemsRect().contains(
(sideMargins.left + draggedItemCurrentCoords.first).toFloat(),
(sideMargins.top + draggedItemCurrentCoords.second).toFloat()
)
) {
val gridCells = getClosestGridCells(center)
if (gridCells != null) {
val shadowX = cellXCoords[gridCells.first] + iconMargin + iconSize / 2f + extraXMargin + sideMargins.left
val shadowY = if (gridCells.second == rowCount - 1) {
cellYCoords[gridCells.second] + cellHeight - iconMargin - iconSize / 2f
} else {
cellYCoords[gridCells.second] + iconMargin + iconSize / 2f + extraYMargin
} + sideMargins.top
val center = folder.getFolderGridCenters().minBy {
abs(it.second - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.third - draggedItemCurrentCoords.second + sideMargins.top)
}
val shadowX = center.second - cellWidth / 2 + iconMargin + iconSize / 2f + extraXMargin
val shadowY = center.third - cellHeight / 2 + iconMargin + iconSize / 2f + extraYMargin
canvas.drawCircle(shadowX, shadowY, iconSize / 2f, dragShadowCirclePaint)
} else {
// draw a circle under the current cell
val center = gridCenters.minBy {
abs(it.first - draggedItemCurrentCoords.first + sideMargins.left) + abs(it.second - draggedItemCurrentCoords.second + sideMargins.top)
}
val gridCells = getClosestGridCells(center)
if (gridCells != null) {
val shadowX = cellXCoords[gridCells.first] + iconMargin + iconSize / 2f + extraXMargin + sideMargins.left
val shadowY = if (gridCells.second == rowCount - 1) {
cellYCoords[gridCells.second] + cellHeight - iconMargin - iconSize / 2f
} else {
cellYCoords[gridCells.second] + iconMargin + iconSize / 2f + extraYMargin
} + sideMargins.top
canvas.drawCircle(shadowX, shadowY, iconSize / 2f, dragShadowCirclePaint)
}
}
// show the app icon itself at dragging, move it above the finger a bit to make it visible
@ -1415,6 +1481,23 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
)
}
private fun HomeScreenGridItem.getFolderGridCenters(): List<Triple<Int, Int, Int>> {
val count = getFolderItems().count()
val columnsCount = ceil(sqrt(count.toDouble())).roundToInt()
val rowsCount = ceil(count.toFloat() / columnsCount).roundToInt()
val folderItemsRect = getFolderItemsRect()
return (0 until columnsCount * rowsCount)
.toList()
.map { Pair(it % columnsCount, it / columnsCount) }
.mapIndexed { index, (x, y) ->
Triple(
index,
(folderItemsRect.left + x * cellWidth + cellWidth / 2).toInt(),
(folderItemsRect.top + y * cellHeight + cellHeight / 2).toInt()
)
}
}
private fun HomeScreenGridItem.getPositionInFolder(folder: HomeScreenGridItem): Pair<Int, Int> {
val count = folder.getFolderItems().count()
val columnsCount = ceil(sqrt(count.toDouble())).roundToInt()