From 06efcfd3fd0a2117dcaac082f4c9d17ae225e5a1 Mon Sep 17 00:00:00 2001 From: en2sv Date: Sun, 20 Aug 2023 19:11:09 +0000 Subject: [PATCH 01/10] Translated using Weblate (Swedish) Currently translated at 100.0% (15 of 15 strings) Translation: Simple Mobile Tools/Simple Launcher Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-launcher/sv/ --- app/src/main/res/values-sv/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 84b538c..1a1d2ad 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -12,7 +12,7 @@ Dolda ikoner Vissa appar kan inte avinstalleras på grund av systembegränsningar, men du kan åtminstone dölja deras ikoner för att slippa se dem. Applåda - Close app drawer on opening an app + Stäng applådan när en app öppnas Startskärm Widgeten är för stor för startskärmens aktuella storlek - + \ No newline at end of file From 95cf8c038475cfe4d2d1bdc5d0d3d9d84469122c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Wed, 23 Aug 2023 11:17:45 +0200 Subject: [PATCH 02/10] Handle home screen pages dragging --- .../launcher/activities/MainActivity.kt | 46 +++++-- .../launcher/views/HomeScreenGrid.kt | 130 +++++++++++++----- 2 files changed, 131 insertions(+), 45 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/launcher/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/launcher/activities/MainActivity.kt index bd5b038..210a72f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/launcher/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/launcher/activities/MainActivity.kt @@ -60,6 +60,8 @@ class MainActivity : SimpleActivity(), FlingListener { private var mMoveGestureThreshold = 0 private var mIgnoreUpEvent = false private var mIgnoreMoveEvents = false + private var mIgnoreXMoveEvents = false + private var mIgnoreYMoveEvents = false private var mLongPressedIcon: HomeScreenGridItem? = null private var mOpenPopupMenu: PopupMenu? = null private var mCachedLaunchers = ArrayList() @@ -362,13 +364,20 @@ class MainActivity : SimpleActivity(), FlingListener { if (hasFingerMoved && !mIgnoreMoveEvents) { val diffY = mTouchDownY - event.y + val diffX = mTouchDownX - event.x - if (isWidgetsFragmentExpanded()) { - val newY = mWidgetsFragmentY - diffY - binding.widgetsFragment.root.y = Math.min(Math.max(0f, newY), mScreenHeight.toFloat()) - } else if (mLongPressedIcon == null) { - val newY = mAllAppsFragmentY - diffY - binding.allAppsFragment.root.y = Math.min(Math.max(0f, newY), mScreenHeight.toFloat()) + if (abs(diffY) > abs(diffX) && !mIgnoreYMoveEvents) { + mIgnoreXMoveEvents = true + if (isWidgetsFragmentExpanded()) { + val newY = mWidgetsFragmentY - diffY + binding.widgetsFragment.root.y = Math.min(Math.max(0f, newY), mScreenHeight.toFloat()) + } else if (mLongPressedIcon == null) { + val newY = mAllAppsFragmentY - diffY + binding.allAppsFragment.root.y = Math.min(Math.max(0f, newY), mScreenHeight.toFloat()) + } + } else if (abs(diffX) > abs(diffY) && !mIgnoreXMoveEvents) { + mIgnoreYMoveEvents = true + binding.homeScreenGrid.root.setSwipeMovement(diffX) } } @@ -386,18 +395,27 @@ class MainActivity : SimpleActivity(), FlingListener { binding.homeScreenGrid.root.itemDraggingStopped() if (!mIgnoreUpEvent) { - if (binding.allAppsFragment.root.y < mScreenHeight * 0.5) { - showFragment(binding.allAppsFragment) - } else if (isAllAppsFragmentExpanded()) { - hideFragment(binding.allAppsFragment) + if (!mIgnoreYMoveEvents) { + if (binding.allAppsFragment.root.y < mScreenHeight * 0.5) { + showFragment(binding.allAppsFragment) + } else if (isAllAppsFragmentExpanded()) { + hideFragment(binding.allAppsFragment) + } + + if (binding.widgetsFragment.root.y < mScreenHeight * 0.5) { + showFragment(binding.widgetsFragment) + } else if (isWidgetsFragmentExpanded()) { + hideFragment(binding.widgetsFragment) + } } - if (binding.widgetsFragment.root.y < mScreenHeight * 0.5) { - showFragment(binding.widgetsFragment) - } else if (isWidgetsFragmentExpanded()) { - hideFragment(binding.widgetsFragment) + if (!mIgnoreXMoveEvents) { + binding.homeScreenGrid.root.finalizeSwipe() } } + + mIgnoreXMoveEvents = false + mIgnoreYMoveEvents = false } } diff --git a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt index 1670ae2..a1af89a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt +++ b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt @@ -37,10 +37,7 @@ import com.simplemobiletools.launcher.extensions.getDrawableForPackageName import com.simplemobiletools.launcher.extensions.homeScreenGridItemsDB import com.simplemobiletools.launcher.helpers.* import com.simplemobiletools.launcher.models.HomeScreenGridItem -import kotlin.math.abs -import kotlin.math.floor -import kotlin.math.max -import kotlin.math.min +import kotlin.math.* class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : RelativeLayout(context, attrs, defStyle) { constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) @@ -77,6 +74,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel private var pageChangeAnimLeftPercentage = 0f private var pageChangeEnabled = true private var pageChangeIndicatorsAlpha = 0f + private var pageChangeSwipedPercentage = 0f // apply fake margins at the home screen. Real ones would cause the icons be cut at dragging at screen sides var sideMargins = Rect() @@ -611,21 +609,12 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel private fun updateWidgetPositionAndSize(widgetView: AppWidgetHostView, item: HomeScreenGridItem): Size { var x = calculateWidgetX(item.left) + width * item.page - width * lastPage - if (pageChangeAnimLeftPercentage > 0f && pageChangeAnimLeftPercentage < 1f && (item.page == currentPage || item.page == lastPage)) { - val xFactor = if (currentPage > lastPage) { - pageChangeAnimLeftPercentage - } else { - -pageChangeAnimLeftPercentage - } - val lastXFactor = if (currentPage > lastPage) { - pageChangeAnimLeftPercentage - 1 - } else { - 1 - pageChangeAnimLeftPercentage - } + if (isBetweenPages() && (item.page == currentPage || item.page == lastPage || (pageChangeSwipedPercentage > 0f && item.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && item.page == currentPage + 1))) { + val xFactor = getXFactorForCurrentPage() + val lastXFactor = getXFactorForLastPage() if (item.page == currentPage) { x += width * xFactor - } - if (item.page == lastPage) { + } else if (item.page == lastPage || (pageChangeSwipedPercentage > 0f && item.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && item.page == currentPage + 1)) { x += width * lastXFactor } } @@ -685,16 +674,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel fillCellSizes() } - val currentXFactor = if (currentPage > lastPage) { - pageChangeAnimLeftPercentage - } else { - -pageChangeAnimLeftPercentage - } - val lastXFactor = if (currentPage > lastPage) { - pageChangeAnimLeftPercentage - 1 - } else { - 1 - pageChangeAnimLeftPercentage - } + val currentXFactor = getXFactorForCurrentPage() + val lastXFactor = getXFactorForLastPage() fun handleItemDrawing(item: HomeScreenGridItem, xFactor: Float) { if (item.id != draggedItem?.id) { @@ -755,6 +736,20 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } } + if (abs(pageChangeSwipedPercentage) > 0f) { + gridItems.filter { + (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) + && ((pageChangeSwipedPercentage > 0f && it.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && it.page == currentPage + 1)) + && !it.docked + }.forEach { item -> + if (item.outOfBounds()) { + return@forEach + } + + handleItemDrawing(item, lastXFactor) + } + } + if (isFirstDraw) { gridItems.filter { it.type == ITEM_TYPE_WIDGET && !it.outOfBounds() }.forEach { item -> bindWidget(item, true) @@ -768,7 +763,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } // Only draw page indicators when there is a need for it - if (pageChangeAnimLeftPercentage > 0f || pageChangeIndicatorsAlpha != 0f) { + if (pageChangeAnimLeftPercentage > 0f || pageChangeIndicatorsAlpha != 0f || abs(pageChangeSwipedPercentage) > 0f) { val pageCount = max(getMaxPage(), currentPage) + 1 val pageIndicatorsRequiredWidth = pageCount * pageIndicatorRadius * 2 + pageCount * (pageIndicatorMargin - 1) val usableWidth = getFakeWidth() @@ -789,8 +784,22 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel // Draw current page indicator on exact position val currentIndicatorRangeStart = pageIndicatorsStart + lastPage * pageIndicatorStep - val currentIndicatorRangeEnd = pageIndicatorsStart + currentPage * pageIndicatorStep - val currentIndicatorPosition = MathUtils.lerp(currentIndicatorRangeStart, currentIndicatorRangeEnd, 1 - pageChangeAnimLeftPercentage) + val indicatorEndPage = if (abs(pageChangeSwipedPercentage) > 0f) { + if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + } else { + currentPage + } + val currentIndicatorRangeEnd = pageIndicatorsStart + indicatorEndPage * pageIndicatorStep + val lerpAmount = if (pageChangeAnimLeftPercentage > 0f) { + 1 - pageChangeAnimLeftPercentage + } else { + abs(pageChangeSwipedPercentage) + } + val currentIndicatorPosition = MathUtils.lerp(currentIndicatorRangeStart, currentIndicatorRangeEnd, lerpAmount) if (pageChangeIndicatorsAlpha != 0f) { currentPageIndicatorPaint.alpha = (pageChangeIndicatorsAlpha * 255.0f).toInt() } else { @@ -1087,11 +1096,13 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel private fun handlePageChange(redraw: Boolean = false) { pageChangeEnabled = false pageChangeIndicatorsAlpha = 0f + val startingAt = 1 - abs(pageChangeSwipedPercentage) + pageChangeSwipedPercentage = 0f removeCallbacks(startFadingIndicators) if (redraw) { redrawGrid() } - ValueAnimator.ofFloat(1f, 0f) + ValueAnimator.ofFloat(startingAt, 0f) .apply { addUpdateListener { pageChangeAnimLeftPercentage = it.animatedValue as Float @@ -1112,6 +1123,63 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } } + private fun isBetweenPages() = + (pageChangeSwipedPercentage > 0f && pageChangeSwipedPercentage < 1f) || (pageChangeAnimLeftPercentage > 0f && pageChangeAnimLeftPercentage < 1f) + + private fun getXFactorForCurrentPage(): Float { + return if (abs(pageChangeSwipedPercentage) > 0f) { + pageChangeSwipedPercentage + } else { + if (currentPage > lastPage) { + pageChangeAnimLeftPercentage + } else { + -pageChangeAnimLeftPercentage + } + } + } + + private fun getXFactorForLastPage(): Float { + return if (abs(pageChangeSwipedPercentage) > 0f) { + (1 - abs(pageChangeSwipedPercentage)) * -sign(pageChangeSwipedPercentage) + } else { + if (currentPage > lastPage) { + pageChangeAnimLeftPercentage - 1 + } else { + 1 - pageChangeAnimLeftPercentage + } + } + } + + fun setSwipeMovement(diffX: Float) { + if (!pageChangeEnabled) { + return + } + + if (currentPage < getMaxPage() && diffX > 0f || currentPage > 0 && diffX < 0f) { + pageChangeSwipedPercentage = (-diffX / width.toFloat()).coerceIn(-1f, 1f) + redrawGrid() + } + } + + fun finalizeSwipe() { + if (abs(pageChangeSwipedPercentage) > 0.5f) { + lastPage = currentPage + currentPage = if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + handlePageChange(true) + } else { + lastPage = if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + handlePageChange(true) + } + } + companion object { private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L private const val PAGE_INDICATORS_FADE_DELAY = PAGE_CHANGE_HOLD_THRESHOLD + 300L From d4f55b8e8b27199bc2d660e371e9f1c5505e8f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Wed, 23 Aug 2023 11:32:15 +0200 Subject: [PATCH 03/10] Fix returning after partial swipe --- .../com/simplemobiletools/launcher/views/HomeScreenGrid.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt index a1af89a..487cb97 100644 --- a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt +++ b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt @@ -1176,6 +1176,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } else { currentPage + 1 } + pageChangeSwipedPercentage = sign(pageChangeSwipedPercentage) * (1 - abs(pageChangeSwipedPercentage)) handlePageChange(true) } } From 8f96073e455fbc6d7adbc129228698d4d28bfa71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Wed, 23 Aug 2023 12:05:56 +0200 Subject: [PATCH 04/10] Fix widget drawing during swipes --- .../launcher/views/HomeScreenGrid.kt | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt index 487cb97..6566844 100644 --- a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt +++ b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt @@ -608,16 +608,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } private fun updateWidgetPositionAndSize(widgetView: AppWidgetHostView, item: HomeScreenGridItem): Size { - var x = calculateWidgetX(item.left) + width * item.page - width * lastPage - if (isBetweenPages() && (item.page == currentPage || item.page == lastPage || (pageChangeSwipedPercentage > 0f && item.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && item.page == currentPage + 1))) { - val xFactor = getXFactorForCurrentPage() - val lastXFactor = getXFactorForLastPage() - if (item.page == currentPage) { - x += width * xFactor - } else if (item.page == lastPage || (pageChangeSwipedPercentage > 0f && item.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && item.page == currentPage + 1)) { - x += width * lastXFactor - } - } + val currentViewPosition = getCurrentViewPositionInFullPageSpace() * width.toFloat() + val x = calculateWidgetX(item.left) + width * item.page - currentViewPosition widgetView.x = x widgetView.y = calculateWidgetY(item.top) val widgetWidth = item.getWidthInCells() * cellWidth @@ -783,23 +775,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } // Draw current page indicator on exact position - val currentIndicatorRangeStart = pageIndicatorsStart + lastPage * pageIndicatorStep - val indicatorEndPage = if (abs(pageChangeSwipedPercentage) > 0f) { - if (pageChangeSwipedPercentage > 0f) { - currentPage - 1 - } else { - currentPage + 1 - } - } else { - currentPage - } - val currentIndicatorRangeEnd = pageIndicatorsStart + indicatorEndPage * pageIndicatorStep - val lerpAmount = if (pageChangeAnimLeftPercentage > 0f) { - 1 - pageChangeAnimLeftPercentage - } else { - abs(pageChangeSwipedPercentage) - } - val currentIndicatorPosition = MathUtils.lerp(currentIndicatorRangeStart, currentIndicatorRangeEnd, lerpAmount) + val currentIndicatorPosition = pageIndicatorsStart + getCurrentViewPositionInFullPageSpace() * pageIndicatorStep if (pageChangeIndicatorsAlpha != 0f) { currentPageIndicatorPaint.alpha = (pageChangeIndicatorsAlpha * 255.0f).toInt() } else { @@ -1114,7 +1090,11 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel pageChangeAnimLeftPercentage = 0f pageChangeEnabled = true lastPage = currentPage - schedulePageChange() + if (draggedItem != null) { + schedulePageChange() + } else { + clearPageChangeFlags() + } scheduleIndicatorsFade() redrawGrid() } @@ -1151,7 +1131,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } fun setSwipeMovement(diffX: Float) { - if (!pageChangeEnabled) { + if (!pageChangeEnabled || draggedItem != null) { return } @@ -1162,6 +1142,10 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } fun finalizeSwipe() { + if (abs(pageChangeSwipedPercentage) == 0f) { + return + } + if (abs(pageChangeSwipedPercentage) > 0.5f) { lastPage = currentPage currentPage = if (pageChangeSwipedPercentage > 0f) { @@ -1181,6 +1165,26 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } } + private fun getCurrentViewPositionInFullPageSpace(): Float { + val rangeStart = lastPage.toFloat() + val rangeEndPage = if (abs(pageChangeSwipedPercentage) > 0f) { + if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + } else { + currentPage + } + val rangeEnd = rangeEndPage.toFloat() + val lerpAmount = if (pageChangeAnimLeftPercentage > 0f) { + 1 - pageChangeAnimLeftPercentage + } else { + abs(pageChangeSwipedPercentage) + } + return MathUtils.lerp(rangeStart, rangeEnd, lerpAmount) + } + companion object { private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L private const val PAGE_INDICATORS_FADE_DELAY = PAGE_CHANGE_HOLD_THRESHOLD + 300L From 53efffde5159d6ad0d9ba2730ea5dd4329dd321c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Wed, 23 Aug 2023 12:41:07 +0200 Subject: [PATCH 05/10] Move page logic into an inner class for better readability --- .../launcher/views/HomeScreenGrid.kt | 528 ++++++++++-------- 1 file changed, 291 insertions(+), 237 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt index 6566844..35635b7 100644 --- a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt +++ b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt @@ -67,14 +67,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel private var redrawWidgets = false private var iconSize = 0 - private var lastPage = 0 - private var currentPage = 0 - private var pageChangeLastArea = PageChangeArea.MIDDLE - private var pageChangeLastAreaEntryTime = 0L - private var pageChangeAnimLeftPercentage = 0f - private var pageChangeEnabled = true - private var pageChangeIndicatorsAlpha = 0f - private var pageChangeSwipedPercentage = 0f + private val pager = Pager() // apply fake margins at the home screen. Real ones would cause the icons be cut at dragging at screen sides var sideMargins = Rect() @@ -90,26 +83,6 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel var itemClickListener: ((HomeScreenGridItem) -> Unit)? = null var itemLongClickListener: ((HomeScreenGridItem) -> Unit)? = null - private val checkAndExecuteDelayedPageChange: Runnable = Runnable { - if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) { - when (pageChangeLastArea) { - PageChangeArea.RIGHT -> nextOrAdditionalPage(true) - PageChangeArea.LEFT -> prevPage(true) - else -> clearPageChangeFlags() - } - } - } - - private val startFadingIndicators: Runnable = Runnable { - ValueAnimator.ofFloat(1f, 0f) - .apply { - addUpdateListener { - pageChangeIndicatorsAlpha = it.animatedValue as Float - redrawGrid() - } - start() - } - } init { ViewCompat.setAccessibilityDelegate(this, HomeScreenGridTouchHelper(this)) @@ -195,7 +168,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } gridItems.removeIf { it.id == item.id } - if (currentPage > getMaxPage()) { + if (pager.isOutsideOfPageRange()) { post { prevPage() } @@ -230,8 +203,6 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel return } - pageChangeIndicatorsAlpha = 1f - removeCallbacks(startFadingIndicators) if (draggedItemCurrentCoords.first == -1 && draggedItemCurrentCoords.second == -1 && draggedItem != null) { if (draggedItem!!.type == ITEM_TYPE_WIDGET) { val draggedWidgetView = widgetViews.firstOrNull { it.tag == draggedItem?.widgetId } @@ -244,47 +215,10 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } draggedItemCurrentCoords = Pair(x, y) - if (x > right - sideMargins.right - cellWidth / 2) { - doWithPageChangeDelay(PageChangeArea.RIGHT) { - nextOrAdditionalPage() - } - } else if (x < left + sideMargins.left + cellWidth / 2) { - doWithPageChangeDelay(PageChangeArea.LEFT) { - prevPage() - } - } else { - clearPageChangeFlags() - } + pager.handleItemMovement(x, y) redrawGrid() } - private fun clearPageChangeFlags() { - pageChangeLastArea = PageChangeArea.MIDDLE - pageChangeLastAreaEntryTime = 0 - removeCallbacks(checkAndExecuteDelayedPageChange) - } - - private fun schedulePageChange() { - pageChangeLastAreaEntryTime = System.currentTimeMillis() - postDelayed(checkAndExecuteDelayedPageChange, PAGE_CHANGE_HOLD_THRESHOLD) - } - - private fun scheduleIndicatorsFade() { - pageChangeIndicatorsAlpha = 1f - postDelayed(startFadingIndicators, PAGE_INDICATORS_FADE_DELAY) - } - - private fun doWithPageChangeDelay(needed: PageChangeArea, pageChangeFunction: () -> Boolean) { - if (pageChangeLastArea != needed) { - pageChangeLastArea = needed - schedulePageChange() - } else if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) { - if (pageChangeFunction()) { - clearPageChangeFlags() - } - } - } - // figure out at which cell was the item dropped, if it is empty fun itemDraggingStopped() { widgetViews.forEach { @@ -295,7 +229,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel return } - scheduleIndicatorsFade() + pager.itemMovementStopped() when (draggedItem!!.type) { ITEM_TYPE_ICON, ITEM_TYPE_SHORTCUT -> addAppIconOrShortcut() ITEM_TYPE_WIDGET -> addWidget() @@ -371,7 +305,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel // check if the destination cell is empty var areAllCellsEmpty = true val wantedCell = Pair(xIndex, yIndex) - gridItems.filter { it.page == currentPage || it.docked }.forEach { item -> + gridItems.filter { pager.isItemOnCurrentPage(it) || it.docked }.forEach { item -> for (xCell in item.left..item.right) { for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) { val cell = Pair(xCell, yCell) @@ -394,7 +328,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel top = yIndex right = xIndex bottom = yIndex - page = currentPage + page = pager.getCurrentPage() docked = yIndex == rowCount - 1 ensureBackgroundThread { @@ -410,7 +344,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel yIndex, xIndex, yIndex, - currentPage, + pager.getCurrentPage(), draggedItem!!.packageName, draggedItem!!.activityName, draggedItem!!.title, @@ -477,7 +411,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } var areAllCellsEmpty = true - gridItems.filter { it.id != draggedItem?.id && (it.page == currentPage || it.docked) }.forEach { item -> + gridItems.filter { it.id != draggedItem?.id && (pager.isItemOnCurrentPage(it) || it.docked) }.forEach { item -> for (xCell in item.left..item.right) { for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) { val cell = Pair(xCell, yCell) @@ -497,7 +431,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel top = widgetRect.top right = widgetRect.right bottom = widgetRect.bottom - page = currentPage + page = pager.getCurrentPage() } ensureBackgroundThread { @@ -514,7 +448,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel widgetItem.top, widgetItem.right, widgetItem.bottom, - currentPage, + pager.getCurrentPage(), false, widgetItem.id!! ) @@ -573,7 +507,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel removeItemFromHomeScreen(item) } - if (currentPage > getMaxPage()) { + if (pager.isOutsideOfPageRange()) { prevPage(redraw = true) } } @@ -608,7 +542,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } private fun updateWidgetPositionAndSize(widgetView: AppWidgetHostView, item: HomeScreenGridItem): Size { - val currentViewPosition = getCurrentViewPositionInFullPageSpace() * width.toFloat() + val currentViewPosition = pager.getCurrentViewPositionInFullPageSpace() * width.toFloat() val x = calculateWidgetX(item.left) + width * item.page - currentViewPosition widgetView.x = x widgetView.y = calculateWidgetY(item.top) @@ -666,8 +600,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel fillCellSizes() } - val currentXFactor = getXFactorForCurrentPage() - val lastXFactor = getXFactorForLastPage() + val currentXFactor = pager.getXFactorForCurrentPage() + val lastXFactor = pager.getXFactorForLastPage() fun handleItemDrawing(item: HomeScreenGridItem, xFactor: Float) { if (item.id != draggedItem?.id) { @@ -702,7 +636,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } } - gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.page == currentPage && !it.docked } + gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && pager.isItemOnCurrentPage(it) && !it.docked } .forEach { item -> if (item.outOfBounds()) { return@forEach @@ -717,8 +651,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel handleItemDrawing(item, 0f) } - if (pageChangeAnimLeftPercentage > 0f) { - gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.page == lastPage && !it.docked } + if (pager.isAnimatingPageChange()) { + gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && pager.isItemOnLastPage(it) && !it.docked } .forEach { item -> if (item.outOfBounds()) { return@forEach @@ -728,10 +662,10 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } } - if (abs(pageChangeSwipedPercentage) > 0f) { + if (pager.isSwiped()) { gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) - && ((pageChangeSwipedPercentage > 0f && it.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && it.page == currentPage + 1)) + && pager.isItemInSwipeRange(it) && !it.docked }.forEach { item -> if (item.outOfBounds()) { @@ -755,19 +689,15 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } // Only draw page indicators when there is a need for it - if (pageChangeAnimLeftPercentage > 0f || pageChangeIndicatorsAlpha != 0f || abs(pageChangeSwipedPercentage) > 0f) { - val pageCount = max(getMaxPage(), currentPage) + 1 + if (pager.shouldDisplayPageChangeIndicator()) { + val pageCount = pager.getPageCount() val pageIndicatorsRequiredWidth = pageCount * pageIndicatorRadius * 2 + pageCount * (pageIndicatorMargin - 1) val usableWidth = getFakeWidth() val pageIndicatorsStart = (usableWidth - pageIndicatorsRequiredWidth) / 2 + sideMargins.left var currentPageIndicatorLeft = pageIndicatorsStart val pageIndicatorY = cellYCoords[rowCount - 1].toFloat() + sideMargins.top + extraYMargin + iconMargin val pageIndicatorStep = pageIndicatorRadius * 2 + pageIndicatorMargin - if (pageChangeIndicatorsAlpha != 0f) { - emptyPageIndicatorPaint.alpha = (pageChangeIndicatorsAlpha * 255.0f).toInt() - } else { - emptyPageIndicatorPaint.alpha = 255 - } + emptyPageIndicatorPaint.alpha = pager.getPageChangeIndicatorsAlpha() // Draw empty page indicators for (page in 0 until pageCount) { canvas.drawCircle(currentPageIndicatorLeft + pageIndicatorRadius, pageIndicatorY, pageIndicatorRadius, emptyPageIndicatorPaint) @@ -775,12 +705,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } // Draw current page indicator on exact position - val currentIndicatorPosition = pageIndicatorsStart + getCurrentViewPositionInFullPageSpace() * pageIndicatorStep - if (pageChangeIndicatorsAlpha != 0f) { - currentPageIndicatorPaint.alpha = (pageChangeIndicatorsAlpha * 255.0f).toInt() - } else { - currentPageIndicatorPaint.alpha = 255 - } + val currentIndicatorPosition = pageIndicatorsStart + pager.getCurrentViewPositionInFullPageSpace() * pageIndicatorStep + currentPageIndicatorPaint.alpha = pager.getPageChangeIndicatorsAlpha() canvas.drawCircle(currentIndicatorPosition + pageIndicatorRadius, pageIndicatorY, pageIndicatorRadius, currentPageIndicatorPaint) } @@ -927,7 +853,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } fun isClickingGridItem(x: Int, y: Int): HomeScreenGridItem? { - for (gridItem in gridItems.filter { it.page == currentPage || it.docked }) { + for (gridItem in gridItems.filter { it.page == pager.getCurrentPage() || it.docked }) { if (gridItem.outOfBounds()) { continue } @@ -1023,166 +949,293 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel private fun getMaxPage() = gridItems.filter { !it.docked && !it.outOfBounds() }.maxOfOrNull { it.page } ?: 0 - private fun nextOrAdditionalPage(redraw: Boolean = false): Boolean { - if (currentPage < getMaxPage() + 1 && pageChangeEnabled) { - lastPage = currentPage - currentPage++ - handlePageChange(redraw) - return true - } - - return false - } - fun nextPage(redraw: Boolean = false): Boolean { - if (currentPage < getMaxPage() && pageChangeEnabled) { - lastPage = currentPage - currentPage++ - handlePageChange(redraw) - return true - } - - return false + return pager.nextPage(redraw) } fun prevPage(redraw: Boolean = false): Boolean { - if (currentPage > 0 && pageChangeEnabled) { - lastPage = currentPage - currentPage-- - handlePageChange(redraw) - return true - } - - return false + return pager.prevPage(redraw) } fun skipToPage(targetPage: Int): Boolean { - if (currentPage != targetPage && targetPage < getMaxPage() + 1) { - lastPage = currentPage - currentPage = targetPage - handlePageChange() - return true - } - - return false + return pager.skipToPage(targetPage) } fun getCurrentIconSize(): Int = iconSize - private fun handlePageChange(redraw: Boolean = false) { - pageChangeEnabled = false - pageChangeIndicatorsAlpha = 0f - val startingAt = 1 - abs(pageChangeSwipedPercentage) - pageChangeSwipedPercentage = 0f - removeCallbacks(startFadingIndicators) - if (redraw) { - redrawGrid() - } - ValueAnimator.ofFloat(startingAt, 0f) - .apply { - addUpdateListener { - pageChangeAnimLeftPercentage = it.animatedValue as Float - redrawGrid() - } - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - super.onAnimationEnd(animation) - pageChangeAnimLeftPercentage = 0f - pageChangeEnabled = true - lastPage = currentPage - if (draggedItem != null) { - schedulePageChange() - } else { - clearPageChangeFlags() - } - scheduleIndicatorsFade() - redrawGrid() - } - }) - start() - } - } - - private fun isBetweenPages() = - (pageChangeSwipedPercentage > 0f && pageChangeSwipedPercentage < 1f) || (pageChangeAnimLeftPercentage > 0f && pageChangeAnimLeftPercentage < 1f) - - private fun getXFactorForCurrentPage(): Float { - return if (abs(pageChangeSwipedPercentage) > 0f) { - pageChangeSwipedPercentage - } else { - if (currentPage > lastPage) { - pageChangeAnimLeftPercentage - } else { - -pageChangeAnimLeftPercentage - } - } - } - - private fun getXFactorForLastPage(): Float { - return if (abs(pageChangeSwipedPercentage) > 0f) { - (1 - abs(pageChangeSwipedPercentage)) * -sign(pageChangeSwipedPercentage) - } else { - if (currentPage > lastPage) { - pageChangeAnimLeftPercentage - 1 - } else { - 1 - pageChangeAnimLeftPercentage - } - } - } fun setSwipeMovement(diffX: Float) { - if (!pageChangeEnabled || draggedItem != null) { - return - } - - if (currentPage < getMaxPage() && diffX > 0f || currentPage > 0 && diffX < 0f) { - pageChangeSwipedPercentage = (-diffX / width.toFloat()).coerceIn(-1f, 1f) - redrawGrid() - } + pager.setSwipeMovement(diffX) } fun finalizeSwipe() { - if (abs(pageChangeSwipedPercentage) == 0f) { - return - } - - if (abs(pageChangeSwipedPercentage) > 0.5f) { - lastPage = currentPage - currentPage = if (pageChangeSwipedPercentage > 0f) { - currentPage - 1 - } else { - currentPage + 1 - } - handlePageChange(true) - } else { - lastPage = if (pageChangeSwipedPercentage > 0f) { - currentPage - 1 - } else { - currentPage + 1 - } - pageChangeSwipedPercentage = sign(pageChangeSwipedPercentage) * (1 - abs(pageChangeSwipedPercentage)) - handlePageChange(true) - } + pager.finalizeSwipe() } - private fun getCurrentViewPositionInFullPageSpace(): Float { - val rangeStart = lastPage.toFloat() - val rangeEndPage = if (abs(pageChangeSwipedPercentage) > 0f) { - if (pageChangeSwipedPercentage > 0f) { - currentPage - 1 + private inner class Pager { + private var lastPage = 0 + private var currentPage = 0 + private var pageChangeLastArea = PageChangeArea.MIDDLE + private var pageChangeLastAreaEntryTime = 0L + private var pageChangeAnimLeftPercentage = 0f + private var pageChangeEnabled = true + private var pageChangeIndicatorsAlpha = 0f + private var pageChangeSwipedPercentage = 0f + + fun getCurrentPage() = currentPage + + fun isItemOnCurrentPage(item: HomeScreenGridItem) = item.page == currentPage + + fun isItemOnLastPage(item: HomeScreenGridItem) = item.page == lastPage + + fun getPageCount() = max(getMaxPage(), pager.currentPage) + 1 + + + fun isOutsideOfPageRange() = currentPage > getMaxPage() + + fun isItemInSwipeRange(item: HomeScreenGridItem) = + ((pageChangeSwipedPercentage > 0f && item.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && item.page == currentPage + 1)) + + fun isSwiped() = abs(pageChangeSwipedPercentage) > 0f + + fun isAnimatingPageChange() = pager.pageChangeAnimLeftPercentage > 0f + + fun shouldDisplayPageChangeIndicator() = isSwiped() || isAnimatingPageChange() || pageChangeIndicatorsAlpha > 0f + + fun getPageChangeIndicatorsAlpha() = if (pageChangeIndicatorsAlpha != 0f) { + (pager.pageChangeIndicatorsAlpha * 255.0f).toInt() + } else { + 255 + } + + fun getXFactorForCurrentPage(): Float { + return if (abs(pageChangeSwipedPercentage) > 0f) { + pageChangeSwipedPercentage } else { - currentPage + 1 + if (currentPage > lastPage) { + pageChangeAnimLeftPercentage + } else { + -pageChangeAnimLeftPercentage + } } - } else { - currentPage } - val rangeEnd = rangeEndPage.toFloat() - val lerpAmount = if (pageChangeAnimLeftPercentage > 0f) { - 1 - pageChangeAnimLeftPercentage - } else { - abs(pageChangeSwipedPercentage) + + fun getXFactorForLastPage(): Float { + return if (abs(pageChangeSwipedPercentage) > 0f) { + (1 - abs(pageChangeSwipedPercentage)) * -sign(pageChangeSwipedPercentage) + } else { + if (currentPage > lastPage) { + pageChangeAnimLeftPercentage - 1 + } else { + 1 - pageChangeAnimLeftPercentage + } + } + } + + fun getCurrentViewPositionInFullPageSpace(): Float { + val rangeStart = lastPage.toFloat() + val rangeEndPage = if (abs(pageChangeSwipedPercentage) > 0f) { + if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + } else { + currentPage + } + val rangeEnd = rangeEndPage.toFloat() + val lerpAmount = if (pageChangeAnimLeftPercentage > 0f) { + 1 - pageChangeAnimLeftPercentage + } else { + abs(pageChangeSwipedPercentage) + } + return MathUtils.lerp(rangeStart, rangeEnd, lerpAmount) + } + + fun setSwipeMovement(diffX: Float) { + if (!pageChangeEnabled || draggedItem != null) { + return + } + + if (currentPage < getMaxPage() && diffX > 0f || currentPage > 0 && diffX < 0f) { + pageChangeSwipedPercentage = (-diffX / width.toFloat()).coerceIn(-1f, 1f) + redrawGrid() + } + } + + fun finalizeSwipe() { + if (abs(pageChangeSwipedPercentage) == 0f) { + return + } + + if (abs(pageChangeSwipedPercentage) > 0.5f) { + lastPage = currentPage + currentPage = if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + handlePageChange(true) + } else { + lastPage = if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + pageChangeSwipedPercentage = sign(pageChangeSwipedPercentage) * (1 - abs(pageChangeSwipedPercentage)) + handlePageChange(true) + } + } + + fun handleItemMovement(x: Int, y: Int) { + showIndicators() + if (x > right - sideMargins.right - cellWidth / 2) { + doWithPageChangeDelay(PageChangeArea.RIGHT) { + nextOrAdditionalPage() + } + } else if (x < left + sideMargins.left + cellWidth / 2) { + doWithPageChangeDelay(PageChangeArea.LEFT) { + prevPage() + } + } else { + clearPageChangeFlags() + } + } + + fun itemMovementStopped() { + scheduleIndicatorsFade() + } + + fun nextPage(redraw: Boolean = false): Boolean { + if (currentPage < getMaxPage() && pageChangeEnabled) { + lastPage = currentPage + currentPage++ + handlePageChange(redraw) + return true + } + + return false + } + + fun prevPage(redraw: Boolean = false): Boolean { + if (currentPage > 0 && pageChangeEnabled) { + lastPage = currentPage + currentPage-- + handlePageChange(redraw) + return true + } + + return false + } + + fun skipToPage(targetPage: Int): Boolean { + if (currentPage != targetPage && targetPage < getMaxPage() + 1) { + lastPage = currentPage + currentPage = targetPage + handlePageChange() + return true + } + + return false + } + + private val checkAndExecuteDelayedPageChange: Runnable = Runnable { + if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) { + when (pageChangeLastArea) { + PageChangeArea.RIGHT -> nextOrAdditionalPage(true) + PageChangeArea.LEFT -> prevPage(true) + else -> clearPageChangeFlags() + } + } + } + + private val startFadingIndicators: Runnable = Runnable { + ValueAnimator.ofFloat(1f, 0f) + .apply { + addUpdateListener { + pageChangeIndicatorsAlpha = it.animatedValue as Float + redrawGrid() + } + start() + } + } + + private fun showIndicators() { + pageChangeIndicatorsAlpha = 1f + removeCallbacks(startFadingIndicators) + } + + private fun clearPageChangeFlags() { + pageChangeLastArea = PageChangeArea.MIDDLE + pageChangeLastAreaEntryTime = 0 + removeCallbacks(checkAndExecuteDelayedPageChange) + } + + private fun schedulePageChange() { + pageChangeLastAreaEntryTime = System.currentTimeMillis() + postDelayed(checkAndExecuteDelayedPageChange, PAGE_CHANGE_HOLD_THRESHOLD) + } + + private fun scheduleIndicatorsFade() { + pageChangeIndicatorsAlpha = 1f + postDelayed(startFadingIndicators, PAGE_INDICATORS_FADE_DELAY) + } + + private fun doWithPageChangeDelay(needed: PageChangeArea, pageChangeFunction: () -> Boolean) { + if (pageChangeLastArea != needed) { + pageChangeLastArea = needed + schedulePageChange() + } else if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) { + if (pageChangeFunction()) { + clearPageChangeFlags() + } + } + } + + private fun nextOrAdditionalPage(redraw: Boolean = false): Boolean { + if (currentPage < getMaxPage() + 1 && pageChangeEnabled) { + lastPage = currentPage + currentPage++ + handlePageChange(redraw) + return true + } + + return false + } + + private fun handlePageChange(redraw: Boolean = false) { + pageChangeEnabled = false + pageChangeIndicatorsAlpha = 0f + val startingAt = 1 - abs(pageChangeSwipedPercentage) + pageChangeSwipedPercentage = 0f + removeCallbacks(startFadingIndicators) + if (redraw) { + redrawGrid() + } + ValueAnimator.ofFloat(startingAt, 0f) + .apply { + addUpdateListener { + pageChangeAnimLeftPercentage = it.animatedValue as Float + redrawGrid() + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + pageChangeAnimLeftPercentage = 0f + pageChangeEnabled = true + lastPage = currentPage + if (draggedItem != null) { + schedulePageChange() + } else { + clearPageChangeFlags() + } + scheduleIndicatorsFade() + redrawGrid() + } + }) + start() + } } - return MathUtils.lerp(rangeStart, rangeEnd, lerpAmount) } companion object { @@ -1195,4 +1248,5 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel RIGHT } } + } From 59975c77f4f7c6fdec51472f6a9cb976d6f9b99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Wed, 23 Aug 2023 12:56:36 +0200 Subject: [PATCH 06/10] Make pager a completely separate class for easier reuse --- .../launcher/views/HomeScreenGrid.kt | 553 +++++++++--------- 1 file changed, 285 insertions(+), 268 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt index 35635b7..f403840 100644 --- a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt +++ b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt @@ -11,6 +11,7 @@ import android.content.Context import android.graphics.* import android.graphics.drawable.BitmapDrawable import android.os.Bundle +import android.os.Handler import android.text.Layout import android.text.StaticLayout import android.text.TextPaint @@ -67,7 +68,14 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel private var redrawWidgets = false private var iconSize = 0 - private val pager = Pager() + private val pager = AnimatedGridPager( + getMaxPage = ::getMaxPage, + redrawGrid = ::redrawGrid, + getWidth = { width }, + getHandler = { handler }, + getNextPageBound = { right - sideMargins.right - cellWidth / 2 }, + getPrevPageBound = { left + sideMargins.left + cellWidth / 2 } + ) // apply fake margins at the home screen. Real ones would cause the icons be cut at dragging at screen sides var sideMargins = Rect() @@ -203,7 +211,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel return } - if (draggedItemCurrentCoords.first == -1 && draggedItemCurrentCoords.second == -1 && draggedItem != null) { + if (draggedItemCurrentCoords.first == -1 && draggedItemCurrentCoords.second == -1) { if (draggedItem!!.type == ITEM_TYPE_WIDGET) { val draggedWidgetView = widgetViews.firstOrNull { it.tag == draggedItem?.widgetId } if (draggedWidgetView != null) { @@ -965,278 +973,28 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel fun setSwipeMovement(diffX: Float) { - pager.setSwipeMovement(diffX) + if (draggedItem != null) { + pager.setSwipeMovement(diffX) + } } fun finalizeSwipe() { pager.finalizeSwipe() } +} - private inner class Pager { - private var lastPage = 0 - private var currentPage = 0 - private var pageChangeLastArea = PageChangeArea.MIDDLE - private var pageChangeLastAreaEntryTime = 0L - private var pageChangeAnimLeftPercentage = 0f - private var pageChangeEnabled = true - private var pageChangeIndicatorsAlpha = 0f - private var pageChangeSwipedPercentage = 0f - - fun getCurrentPage() = currentPage - - fun isItemOnCurrentPage(item: HomeScreenGridItem) = item.page == currentPage - - fun isItemOnLastPage(item: HomeScreenGridItem) = item.page == lastPage - - fun getPageCount() = max(getMaxPage(), pager.currentPage) + 1 - - - fun isOutsideOfPageRange() = currentPage > getMaxPage() - - fun isItemInSwipeRange(item: HomeScreenGridItem) = - ((pageChangeSwipedPercentage > 0f && item.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && item.page == currentPage + 1)) - - fun isSwiped() = abs(pageChangeSwipedPercentage) > 0f - - fun isAnimatingPageChange() = pager.pageChangeAnimLeftPercentage > 0f - - fun shouldDisplayPageChangeIndicator() = isSwiped() || isAnimatingPageChange() || pageChangeIndicatorsAlpha > 0f - - fun getPageChangeIndicatorsAlpha() = if (pageChangeIndicatorsAlpha != 0f) { - (pager.pageChangeIndicatorsAlpha * 255.0f).toInt() - } else { - 255 - } - - fun getXFactorForCurrentPage(): Float { - return if (abs(pageChangeSwipedPercentage) > 0f) { - pageChangeSwipedPercentage - } else { - if (currentPage > lastPage) { - pageChangeAnimLeftPercentage - } else { - -pageChangeAnimLeftPercentage - } - } - } - - fun getXFactorForLastPage(): Float { - return if (abs(pageChangeSwipedPercentage) > 0f) { - (1 - abs(pageChangeSwipedPercentage)) * -sign(pageChangeSwipedPercentage) - } else { - if (currentPage > lastPage) { - pageChangeAnimLeftPercentage - 1 - } else { - 1 - pageChangeAnimLeftPercentage - } - } - } - - fun getCurrentViewPositionInFullPageSpace(): Float { - val rangeStart = lastPage.toFloat() - val rangeEndPage = if (abs(pageChangeSwipedPercentage) > 0f) { - if (pageChangeSwipedPercentage > 0f) { - currentPage - 1 - } else { - currentPage + 1 - } - } else { - currentPage - } - val rangeEnd = rangeEndPage.toFloat() - val lerpAmount = if (pageChangeAnimLeftPercentage > 0f) { - 1 - pageChangeAnimLeftPercentage - } else { - abs(pageChangeSwipedPercentage) - } - return MathUtils.lerp(rangeStart, rangeEnd, lerpAmount) - } - - fun setSwipeMovement(diffX: Float) { - if (!pageChangeEnabled || draggedItem != null) { - return - } - - if (currentPage < getMaxPage() && diffX > 0f || currentPage > 0 && diffX < 0f) { - pageChangeSwipedPercentage = (-diffX / width.toFloat()).coerceIn(-1f, 1f) - redrawGrid() - } - } - - fun finalizeSwipe() { - if (abs(pageChangeSwipedPercentage) == 0f) { - return - } - - if (abs(pageChangeSwipedPercentage) > 0.5f) { - lastPage = currentPage - currentPage = if (pageChangeSwipedPercentage > 0f) { - currentPage - 1 - } else { - currentPage + 1 - } - handlePageChange(true) - } else { - lastPage = if (pageChangeSwipedPercentage > 0f) { - currentPage - 1 - } else { - currentPage + 1 - } - pageChangeSwipedPercentage = sign(pageChangeSwipedPercentage) * (1 - abs(pageChangeSwipedPercentage)) - handlePageChange(true) - } - } - - fun handleItemMovement(x: Int, y: Int) { - showIndicators() - if (x > right - sideMargins.right - cellWidth / 2) { - doWithPageChangeDelay(PageChangeArea.RIGHT) { - nextOrAdditionalPage() - } - } else if (x < left + sideMargins.left + cellWidth / 2) { - doWithPageChangeDelay(PageChangeArea.LEFT) { - prevPage() - } - } else { - clearPageChangeFlags() - } - } - - fun itemMovementStopped() { - scheduleIndicatorsFade() - } - - fun nextPage(redraw: Boolean = false): Boolean { - if (currentPage < getMaxPage() && pageChangeEnabled) { - lastPage = currentPage - currentPage++ - handlePageChange(redraw) - return true - } - - return false - } - - fun prevPage(redraw: Boolean = false): Boolean { - if (currentPage > 0 && pageChangeEnabled) { - lastPage = currentPage - currentPage-- - handlePageChange(redraw) - return true - } - - return false - } - - fun skipToPage(targetPage: Int): Boolean { - if (currentPage != targetPage && targetPage < getMaxPage() + 1) { - lastPage = currentPage - currentPage = targetPage - handlePageChange() - return true - } - - return false - } - - private val checkAndExecuteDelayedPageChange: Runnable = Runnable { - if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) { - when (pageChangeLastArea) { - PageChangeArea.RIGHT -> nextOrAdditionalPage(true) - PageChangeArea.LEFT -> prevPage(true) - else -> clearPageChangeFlags() - } - } - } - - private val startFadingIndicators: Runnable = Runnable { - ValueAnimator.ofFloat(1f, 0f) - .apply { - addUpdateListener { - pageChangeIndicatorsAlpha = it.animatedValue as Float - redrawGrid() - } - start() - } - } - - private fun showIndicators() { - pageChangeIndicatorsAlpha = 1f - removeCallbacks(startFadingIndicators) - } - - private fun clearPageChangeFlags() { - pageChangeLastArea = PageChangeArea.MIDDLE - pageChangeLastAreaEntryTime = 0 - removeCallbacks(checkAndExecuteDelayedPageChange) - } - - private fun schedulePageChange() { - pageChangeLastAreaEntryTime = System.currentTimeMillis() - postDelayed(checkAndExecuteDelayedPageChange, PAGE_CHANGE_HOLD_THRESHOLD) - } - - private fun scheduleIndicatorsFade() { - pageChangeIndicatorsAlpha = 1f - postDelayed(startFadingIndicators, PAGE_INDICATORS_FADE_DELAY) - } - - private fun doWithPageChangeDelay(needed: PageChangeArea, pageChangeFunction: () -> Boolean) { - if (pageChangeLastArea != needed) { - pageChangeLastArea = needed - schedulePageChange() - } else if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) { - if (pageChangeFunction()) { - clearPageChangeFlags() - } - } - } - - private fun nextOrAdditionalPage(redraw: Boolean = false): Boolean { - if (currentPage < getMaxPage() + 1 && pageChangeEnabled) { - lastPage = currentPage - currentPage++ - handlePageChange(redraw) - return true - } - - return false - } - - private fun handlePageChange(redraw: Boolean = false) { - pageChangeEnabled = false - pageChangeIndicatorsAlpha = 0f - val startingAt = 1 - abs(pageChangeSwipedPercentage) - pageChangeSwipedPercentage = 0f - removeCallbacks(startFadingIndicators) - if (redraw) { - redrawGrid() - } - ValueAnimator.ofFloat(startingAt, 0f) - .apply { - addUpdateListener { - pageChangeAnimLeftPercentage = it.animatedValue as Float - redrawGrid() - } - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - super.onAnimationEnd(animation) - pageChangeAnimLeftPercentage = 0f - pageChangeEnabled = true - lastPage = currentPage - if (draggedItem != null) { - schedulePageChange() - } else { - clearPageChangeFlags() - } - scheduleIndicatorsFade() - redrawGrid() - } - }) - start() - } - } - } +/** + * Helper class responsible for managing current page and providing utilities for animating page changes, + * as well as partial dragigng between pages + */ +private class AnimatedGridPager( + private val getMaxPage: () -> Int, + private val redrawGrid: () -> Unit, + private val getWidth: () -> Int, + private val getHandler: () -> Handler, + private val getNextPageBound: () -> Int, + private val getPrevPageBound: () -> Int, +) { companion object { private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L @@ -1249,4 +1007,263 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel } } + private var lastPage = 0 + private var currentPage = 0 + private var pageChangeLastArea = PageChangeArea.MIDDLE + private var pageChangeLastAreaEntryTime = 0L + private var pageChangeAnimLeftPercentage = 0f + private var pageChangeEnabled = true + private var pageChangeIndicatorsAlpha = 0f + private var pageChangeSwipedPercentage = 0f + + fun getCurrentPage() = currentPage + + fun isItemOnCurrentPage(item: HomeScreenGridItem) = item.page == currentPage + + fun isItemOnLastPage(item: HomeScreenGridItem) = item.page == lastPage + + fun getPageCount() = max(getMaxPage(), currentPage) + 1 + + + fun isOutsideOfPageRange() = currentPage > getMaxPage() + + fun isItemInSwipeRange(item: HomeScreenGridItem) = + ((pageChangeSwipedPercentage > 0f && item.page == currentPage - 1) || (pageChangeSwipedPercentage < 0f && item.page == currentPage + 1)) + + fun isSwiped() = abs(pageChangeSwipedPercentage) > 0f + + fun isAnimatingPageChange() = pageChangeAnimLeftPercentage > 0f + + fun shouldDisplayPageChangeIndicator() = isSwiped() || isAnimatingPageChange() || pageChangeIndicatorsAlpha > 0f + + fun getPageChangeIndicatorsAlpha() = if (pageChangeIndicatorsAlpha != 0f) { + (pageChangeIndicatorsAlpha * 255.0f).toInt() + } else { + 255 + } + + fun getXFactorForCurrentPage(): Float { + return if (abs(pageChangeSwipedPercentage) > 0f) { + pageChangeSwipedPercentage + } else { + if (currentPage > lastPage) { + pageChangeAnimLeftPercentage + } else { + -pageChangeAnimLeftPercentage + } + } + } + + fun getXFactorForLastPage(): Float { + return if (abs(pageChangeSwipedPercentage) > 0f) { + (1 - abs(pageChangeSwipedPercentage)) * -sign(pageChangeSwipedPercentage) + } else { + if (currentPage > lastPage) { + pageChangeAnimLeftPercentage - 1 + } else { + 1 - pageChangeAnimLeftPercentage + } + } + } + + fun getCurrentViewPositionInFullPageSpace(): Float { + val rangeStart = lastPage.toFloat() + val rangeEndPage = if (abs(pageChangeSwipedPercentage) > 0f) { + if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + } else { + currentPage + } + val rangeEnd = rangeEndPage.toFloat() + val lerpAmount = if (pageChangeAnimLeftPercentage > 0f) { + 1 - pageChangeAnimLeftPercentage + } else { + abs(pageChangeSwipedPercentage) + } + return MathUtils.lerp(rangeStart, rangeEnd, lerpAmount) + } + + fun setSwipeMovement(diffX: Float) { + if (!pageChangeEnabled) { + return + } + + if (currentPage < getMaxPage() && diffX > 0f || currentPage > 0 && diffX < 0f) { + pageChangeSwipedPercentage = (-diffX / getWidth().toFloat()).coerceIn(-1f, 1f) + redrawGrid() + } + } + + fun finalizeSwipe() { + if (abs(pageChangeSwipedPercentage) == 0f) { + return + } + + if (abs(pageChangeSwipedPercentage) > 0.5f) { + lastPage = currentPage + currentPage = if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + handlePageChange(true) + } else { + lastPage = if (pageChangeSwipedPercentage > 0f) { + currentPage - 1 + } else { + currentPage + 1 + } + pageChangeSwipedPercentage = sign(pageChangeSwipedPercentage) * (1 - abs(pageChangeSwipedPercentage)) + handlePageChange(true) + } + } + + fun handleItemMovement(x: Int, y: Int) { + showIndicators() + if (x > getNextPageBound()) { + doWithPageChangeDelay(PageChangeArea.RIGHT) { + nextOrAdditionalPage() + } + } else if (x < getPrevPageBound()) { + doWithPageChangeDelay(PageChangeArea.LEFT) { + prevPage() + } + } else { + clearPageChangeFlags() + } + } + + fun itemMovementStopped() { + scheduleIndicatorsFade() + } + + fun nextPage(redraw: Boolean = false): Boolean { + if (currentPage < getMaxPage() && pageChangeEnabled) { + lastPage = currentPage + currentPage++ + handlePageChange(redraw) + return true + } + + return false + } + + fun prevPage(redraw: Boolean = false): Boolean { + if (currentPage > 0 && pageChangeEnabled) { + lastPage = currentPage + currentPage-- + handlePageChange(redraw) + return true + } + + return false + } + + fun skipToPage(targetPage: Int): Boolean { + if (currentPage != targetPage && targetPage < getMaxPage() + 1) { + lastPage = currentPage + currentPage = targetPage + handlePageChange() + return true + } + + return false + } + + private val checkAndExecuteDelayedPageChange: Runnable = Runnable { + if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) { + when (pageChangeLastArea) { + PageChangeArea.RIGHT -> nextOrAdditionalPage(true) + PageChangeArea.LEFT -> prevPage(true) + else -> clearPageChangeFlags() + } + } + } + + private val startFadingIndicators: Runnable = Runnable { + ValueAnimator.ofFloat(1f, 0f) + .apply { + addUpdateListener { + pageChangeIndicatorsAlpha = it.animatedValue as Float + redrawGrid() + } + start() + } + } + + private fun showIndicators() { + pageChangeIndicatorsAlpha = 1f + getHandler().removeCallbacks(startFadingIndicators) + } + + private fun clearPageChangeFlags() { + pageChangeLastArea = PageChangeArea.MIDDLE + pageChangeLastAreaEntryTime = 0 + getHandler().removeCallbacks(checkAndExecuteDelayedPageChange) + } + + private fun schedulePageChange() { + pageChangeLastAreaEntryTime = System.currentTimeMillis() + getHandler().postDelayed(checkAndExecuteDelayedPageChange, PAGE_CHANGE_HOLD_THRESHOLD) + } + + private fun scheduleIndicatorsFade() { + pageChangeIndicatorsAlpha = 1f + getHandler().postDelayed(startFadingIndicators, PAGE_INDICATORS_FADE_DELAY) + } + + private fun doWithPageChangeDelay(needed: PageChangeArea, pageChangeFunction: () -> Boolean) { + if (pageChangeLastArea != needed) { + pageChangeLastArea = needed + schedulePageChange() + } else if (System.currentTimeMillis() - pageChangeLastAreaEntryTime > PAGE_CHANGE_HOLD_THRESHOLD) { + if (pageChangeFunction()) { + clearPageChangeFlags() + } + } + } + + private fun nextOrAdditionalPage(redraw: Boolean = false): Boolean { + if (currentPage < getMaxPage() + 1 && pageChangeEnabled) { + lastPage = currentPage + currentPage++ + handlePageChange(redraw) + return true + } + + return false + } + + private fun handlePageChange(redraw: Boolean = false) { + pageChangeEnabled = false + pageChangeIndicatorsAlpha = 0f + val startingAt = 1 - abs(pageChangeSwipedPercentage) + pageChangeSwipedPercentage = 0f + getHandler().removeCallbacks(startFadingIndicators) + if (redraw) { + redrawGrid() + } + ValueAnimator.ofFloat(startingAt, 0f) + .apply { + addUpdateListener { + pageChangeAnimLeftPercentage = it.animatedValue as Float + redrawGrid() + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + pageChangeAnimLeftPercentage = 0f + pageChangeEnabled = true + lastPage = currentPage + clearPageChangeFlags() + scheduleIndicatorsFade() + redrawGrid() + } + }) + start() + } + } } From 6c8800f26694ac6064ebb587a8773dcf192e0f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Wed, 23 Aug 2023 13:08:13 +0200 Subject: [PATCH 07/10] Fix condition for adding swipe movement --- .../com/simplemobiletools/launcher/views/HomeScreenGrid.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt index f403840..c4a96d5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt +++ b/app/src/main/kotlin/com/simplemobiletools/launcher/views/HomeScreenGrid.kt @@ -973,7 +973,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel fun setSwipeMovement(diffX: Float) { - if (draggedItem != null) { + if (draggedItem == null) { pager.setSwipeMovement(diffX) } } From a34bab78afaac317cae40d392ef2b882803b5e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ensar=20Saraj=C4=8Di=C4=87?= Date: Wed, 23 Aug 2023 13:08:25 +0200 Subject: [PATCH 08/10] Prevent flinging while movement on other axis is active --- .../launcher/activities/MainActivity.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/kotlin/com/simplemobiletools/launcher/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/launcher/activities/MainActivity.kt index 210a72f..327ca4e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/launcher/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/launcher/activities/MainActivity.kt @@ -749,6 +749,10 @@ class MainActivity : SimpleActivity(), FlingListener { } override fun onFlingUp() { + if (mIgnoreYMoveEvents) { + return + } + if (!isWidgetsFragmentExpanded()) { mIgnoreUpEvent = true showFragment(binding.allAppsFragment) @@ -757,6 +761,10 @@ class MainActivity : SimpleActivity(), FlingListener { @SuppressLint("WrongConstant") override fun onFlingDown() { + if (mIgnoreYMoveEvents) { + return + } + mIgnoreUpEvent = true if (isAllAppsFragmentExpanded()) { hideFragment(binding.allAppsFragment) @@ -771,11 +779,19 @@ class MainActivity : SimpleActivity(), FlingListener { } override fun onFlingRight() { + if (mIgnoreXMoveEvents) { + return + } + mIgnoreUpEvent = true binding.homeScreenGrid.root.prevPage(redraw = true) } override fun onFlingLeft() { + if (mIgnoreXMoveEvents) { + return + } + mIgnoreUpEvent = true binding.homeScreenGrid.root.nextPage(redraw = true) } From 2dd44a98866df01e0ac85b83c645472897aba43e Mon Sep 17 00:00:00 2001 From: "J. Lavoie" Date: Tue, 22 Aug 2023 13:46:17 +0000 Subject: [PATCH 09/10] Translated using Weblate (French) Currently translated at 100.0% (15 of 15 strings) Translation: Simple Mobile Tools/Simple Launcher Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-launcher/fr/ --- app/src/main/res/values-fr/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0274a14..c99b6cf 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -11,8 +11,8 @@ Gérer les icônes cachées Icônes cachées Certaines applications ne peuvent pas être désinstallées en raison de restrictions du système, mais vous pouvez au moins masquer leurs icônes pour éviter de les voir. - App drawer - Close app drawer on opening an app + Tiroir d\'appli + Fermer le tiroir de l\'appli à l\'ouverture d\'une application Home screen - Widget is too big for current home screen size - + Le widget est trop grand pour la taille actuelle de l\'écran + \ No newline at end of file From 4ff3dd7e1931b38efc9bef19f7eb1a6e1bc518d5 Mon Sep 17 00:00:00 2001 From: "J. Lavoie" Date: Tue, 22 Aug 2023 13:55:27 +0000 Subject: [PATCH 10/10] Translated using Weblate (Italian) Currently translated at 100.0% (15 of 15 strings) Translation: Simple Mobile Tools/Simple Launcher Translate-URL: https://hosted.weblate.org/projects/simple-mobile-tools/simple-launcher/it/ --- app/src/main/res/values-it/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index bde015f..450f88b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -11,8 +11,8 @@ Gestisci le icone nascoste Icone nascoste Alcune applicazioni non possono essere disinstallate a causa di restrizioni di sistema, ma è possibile almeno nascondere le loro icone per evitare di vederle. - App drawer - Close app drawer on opening an app + Cassetto app + Chiudi il cassetto app all\'apertura di un\'app Home screen - Widget is too big for current home screen size - + Il widget è troppo grande per le dimensioni attuali della schermata principale + \ No newline at end of file