Simplify drawing and cells logic
This commit is contained in:
parent
ece3890133
commit
1863a9e6a9
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue