mirror of
https://github.com/SimpleMobileTools/Simple-Launcher.git
synced 2025-06-05 21:59:15 +02:00
Merge pull request #86 from esensar/feature/47-multi-panel-home
Implement multiple panes for home screen grid
This commit is contained in:
@@ -50,6 +50,7 @@ import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.activity_main.view.*
|
||||
import kotlinx.android.synthetic.main.all_apps_fragment.view.*
|
||||
import kotlinx.android.synthetic.main.widgets_fragment.view.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class MainActivity : SimpleActivity(), FlingListener {
|
||||
private val ANIMATION_DURATION = 150L
|
||||
@@ -92,7 +93,7 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
mScreenHeight = realScreenSize.y
|
||||
mAllAppsFragmentY = mScreenHeight
|
||||
mWidgetsFragmentY = mScreenHeight
|
||||
mMoveGestureThreshold = resources.getDimension(R.dimen.move_gesture_threshold).toInt()
|
||||
mMoveGestureThreshold = resources.getDimensionPixelSize(R.dimen.move_gesture_threshold)
|
||||
|
||||
arrayOf(all_apps_fragment as MyFragment, widgets_fragment as MyFragment).forEach { fragment ->
|
||||
fragment.setupFragment(this)
|
||||
@@ -100,6 +101,25 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
fragment.beVisible()
|
||||
}
|
||||
|
||||
handleIntentAction(intent)
|
||||
|
||||
home_screen_grid.itemClickListener = {
|
||||
performItemClick(it)
|
||||
}
|
||||
|
||||
home_screen_grid.itemLongClickListener = {
|
||||
performItemLongClick(home_screen_grid.getClickableRect(it).left.toFloat(), it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
if (intent != null) {
|
||||
handleIntentAction(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleIntentAction(intent: Intent) {
|
||||
if (intent.action == LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT) {
|
||||
val launcherApps = applicationContext.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
||||
val item = launcherApps.getPinItemRequest(intent)
|
||||
@@ -111,13 +131,14 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
val shortcutId = item.shortcutInfo?.id!!
|
||||
val label = item.shortcutInfo?.shortLabel?.toString() ?: item.shortcutInfo?.longLabel?.toString() ?: ""
|
||||
val icon = launcherApps.getShortcutIconDrawable(item.shortcutInfo!!, resources.displayMetrics.densityDpi)
|
||||
val rect = findFirstEmptyCell() ?: return@ensureBackgroundThread
|
||||
val (page, rect) = findFirstEmptyCell()
|
||||
val gridItem = HomeScreenGridItem(
|
||||
null,
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right,
|
||||
rect.bottom,
|
||||
page,
|
||||
item.shortcutInfo!!.`package`,
|
||||
"",
|
||||
label,
|
||||
@@ -127,9 +148,13 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
"",
|
||||
shortcutId,
|
||||
icon.toBitmap(),
|
||||
false,
|
||||
icon
|
||||
)
|
||||
|
||||
runOnUiThread {
|
||||
home_screen_grid.skipToPage(page)
|
||||
}
|
||||
// delay showing the shortcut both to let the user see adding it in realtime and hackily avoid concurrent modification exception at HomeScreenGrid
|
||||
Thread.sleep(2000)
|
||||
|
||||
@@ -140,37 +165,32 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
home_screen_grid.itemClickListener = {
|
||||
performItemClick(it)
|
||||
}
|
||||
|
||||
home_screen_grid.itemLongClickListener = {
|
||||
performItemLongClick(home_screen_grid.getClickableRect(it).left.toFloat(), it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findFirstEmptyCell(): Rect? {
|
||||
private fun findFirstEmptyCell(): Pair<Int, Rect> {
|
||||
val gridItems = homeScreenGridItemsDB.getAllItems() as ArrayList<HomeScreenGridItem>
|
||||
val occupiedCells = ArrayList<Pair<Int, Int>>()
|
||||
val maxPage = gridItems.map { it.page }.max()
|
||||
val occupiedCells = ArrayList<Triple<Int, Int, Int>>()
|
||||
gridItems.forEach { item ->
|
||||
for (xCell in item.left..item.right) {
|
||||
for (yCell in item.top..item.bottom) {
|
||||
occupiedCells.add(Pair(xCell, yCell))
|
||||
occupiedCells.add(Triple(item.page, xCell, yCell))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (checkedYCell in 0 until config.homeColumnCount) {
|
||||
for (checkedXCell in 0 until config.homeRowCount - 1) {
|
||||
val wantedCell = Pair(checkedXCell, checkedYCell)
|
||||
if (!occupiedCells.contains(wantedCell)) {
|
||||
return Rect(wantedCell.first, wantedCell.second, wantedCell.first, wantedCell.second)
|
||||
for (page in 0 until maxPage) {
|
||||
for (checkedYCell in 0 until config.homeColumnCount) {
|
||||
for (checkedXCell in 0 until config.homeRowCount - 1) {
|
||||
val wantedCell = Triple(page, checkedXCell, checkedYCell)
|
||||
if (!occupiedCells.contains(wantedCell)) {
|
||||
return Pair(page, Rect(wantedCell.second, wantedCell.third, wantedCell.second, wantedCell.third))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
return Pair(maxPage + 1, Rect(0, 0, 0, 0))
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
@@ -318,7 +338,7 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
home_screen_grid.draggedItemMoved(event.x.toInt(), event.y.toInt())
|
||||
}
|
||||
|
||||
if (mTouchDownY != -1 && !mIgnoreMoveEvents) {
|
||||
if (hasFingerMoved && !mIgnoreMoveEvents) {
|
||||
val diffY = mTouchDownY - event.y
|
||||
|
||||
if (isWidgetsFragmentExpanded()) {
|
||||
@@ -364,7 +384,7 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
|
||||
// some devices ACTION_MOVE keeps triggering for the whole long press duration, but we are interested in real moves only, when coords change
|
||||
private fun hasFingerMoved(event: MotionEvent) = mTouchDownX != -1 && mTouchDownY != -1 &&
|
||||
(Math.abs(mTouchDownX - event.x) > mMoveGestureThreshold) || (Math.abs(mTouchDownY - event.y) > mMoveGestureThreshold)
|
||||
((Math.abs(mTouchDownX - event.x) > mMoveGestureThreshold) || (Math.abs(mTouchDownY - event.y) > mMoveGestureThreshold))
|
||||
|
||||
private fun refetchLaunchers() {
|
||||
val launchers = getAllAppLaunchers()
|
||||
@@ -647,11 +667,20 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
return true
|
||||
}
|
||||
|
||||
if (velocityY > 0) {
|
||||
flingListener.onFlingDown()
|
||||
} else {
|
||||
flingListener.onFlingUp()
|
||||
if (abs(velocityY) > abs(velocityX)) {
|
||||
if (velocityY > 0) {
|
||||
flingListener.onFlingDown()
|
||||
} else {
|
||||
flingListener.onFlingUp()
|
||||
}
|
||||
} else if (abs(velocityX) > abs(velocityY)) {
|
||||
if (velocityX > 0) {
|
||||
flingListener.onFlingRight()
|
||||
} else {
|
||||
flingListener.onFlingLeft()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -682,6 +711,14 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFlingRight() {
|
||||
home_screen_grid.prevPage(redraw = true)
|
||||
}
|
||||
|
||||
override fun onFlingLeft() {
|
||||
home_screen_grid.nextPage(redraw = true)
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
fun getAllAppLaunchers(): ArrayList<AppLauncher> {
|
||||
val hiddenIcons = hiddenIconsDB.getHiddenIcons().map {
|
||||
@@ -728,7 +765,24 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
val defaultDialerPackage = (getSystemService(Context.TELECOM_SERVICE) as TelecomManager).defaultDialerPackage
|
||||
appLaunchers.firstOrNull { it.packageName == defaultDialerPackage }?.apply {
|
||||
val dialerIcon =
|
||||
HomeScreenGridItem(null, 0, config.homeRowCount - 1, 0, config.homeRowCount - 1, defaultDialerPackage, "", title, ITEM_TYPE_ICON, "", -1, "", "", null)
|
||||
HomeScreenGridItem(
|
||||
null,
|
||||
0,
|
||||
config.homeRowCount - 1,
|
||||
0,
|
||||
config.homeRowCount - 1,
|
||||
0,
|
||||
defaultDialerPackage,
|
||||
"",
|
||||
title,
|
||||
ITEM_TYPE_ICON,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
true
|
||||
)
|
||||
homeScreenGridItems.add(dialerIcon)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -738,7 +792,24 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
val defaultSMSMessengerPackage = Telephony.Sms.getDefaultSmsPackage(this)
|
||||
appLaunchers.firstOrNull { it.packageName == defaultSMSMessengerPackage }?.apply {
|
||||
val SMSMessengerIcon =
|
||||
HomeScreenGridItem(null, 1, config.homeRowCount - 1, 1, config.homeRowCount - 1, defaultSMSMessengerPackage, "", title, ITEM_TYPE_ICON, "", -1, "", "", null)
|
||||
HomeScreenGridItem(
|
||||
null,
|
||||
1,
|
||||
config.homeRowCount - 1,
|
||||
1,
|
||||
config.homeRowCount - 1,
|
||||
0,
|
||||
defaultSMSMessengerPackage,
|
||||
"",
|
||||
title,
|
||||
ITEM_TYPE_ICON,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
true
|
||||
)
|
||||
homeScreenGridItems.add(SMSMessengerIcon)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -750,7 +821,24 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
val defaultBrowserPackage = resolveInfo!!.activityInfo.packageName
|
||||
appLaunchers.firstOrNull { it.packageName == defaultBrowserPackage }?.apply {
|
||||
val browserIcon =
|
||||
HomeScreenGridItem(null, 2, config.homeRowCount - 1, 2, config.homeRowCount - 1, defaultBrowserPackage, "", title, ITEM_TYPE_ICON, "", -1, "", "", null)
|
||||
HomeScreenGridItem(
|
||||
null,
|
||||
2,
|
||||
config.homeRowCount - 1,
|
||||
2,
|
||||
config.homeRowCount - 1,
|
||||
0,
|
||||
defaultBrowserPackage,
|
||||
"",
|
||||
title,
|
||||
ITEM_TYPE_ICON,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
true
|
||||
)
|
||||
homeScreenGridItems.add(browserIcon)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -761,7 +849,24 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
val storePackage = potentialStores.firstOrNull { isPackageInstalled(it) && appLaunchers.map { it.packageName }.contains(it) }
|
||||
if (storePackage != null) {
|
||||
appLaunchers.firstOrNull { it.packageName == storePackage }?.apply {
|
||||
val storeIcon = HomeScreenGridItem(null, 3, config.homeRowCount - 1, 3, config.homeRowCount - 1, storePackage, "", title, ITEM_TYPE_ICON, "", -1, "", "", null)
|
||||
val storeIcon = HomeScreenGridItem(
|
||||
null,
|
||||
3,
|
||||
config.homeRowCount - 1,
|
||||
3,
|
||||
config.homeRowCount - 1,
|
||||
0,
|
||||
storePackage,
|
||||
"",
|
||||
title,
|
||||
ITEM_TYPE_ICON,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
true
|
||||
)
|
||||
homeScreenGridItems.add(storeIcon)
|
||||
}
|
||||
}
|
||||
@@ -774,7 +879,24 @@ class MainActivity : SimpleActivity(), FlingListener {
|
||||
val defaultCameraPackage = resolveInfo!!.activityInfo.packageName
|
||||
appLaunchers.firstOrNull { it.packageName == defaultCameraPackage }?.apply {
|
||||
val cameraIcon =
|
||||
HomeScreenGridItem(null, 4, config.homeRowCount - 1, 4, config.homeRowCount - 1, defaultCameraPackage, "", title, ITEM_TYPE_ICON, "", -1, "", "", null)
|
||||
HomeScreenGridItem(
|
||||
null,
|
||||
4,
|
||||
config.homeRowCount - 1,
|
||||
4,
|
||||
config.homeRowCount - 1,
|
||||
0,
|
||||
defaultCameraPackage,
|
||||
"",
|
||||
title,
|
||||
ITEM_TYPE_ICON,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
true
|
||||
)
|
||||
homeScreenGridItems.add(cameraIcon)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
@@ -15,7 +15,7 @@ import com.simplemobiletools.launcher.models.AppLauncher
|
||||
import com.simplemobiletools.launcher.models.HiddenIcon
|
||||
import com.simplemobiletools.launcher.models.HomeScreenGridItem
|
||||
|
||||
@Database(entities = [AppLauncher::class, HomeScreenGridItem::class, HiddenIcon::class], version = 4)
|
||||
@Database(entities = [AppLauncher::class, HomeScreenGridItem::class, HiddenIcon::class], version = 5)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppsDatabase : RoomDatabase() {
|
||||
|
||||
@@ -36,6 +36,7 @@ abstract class AppsDatabase : RoomDatabase() {
|
||||
.addMigrations(MIGRATION_1_2)
|
||||
.addMigrations(MIGRATION_2_3)
|
||||
.addMigrations(MIGRATION_3_4)
|
||||
.addMigrations(MIGRATION_4_5)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -64,5 +65,13 @@ abstract class AppsDatabase : RoomDatabase() {
|
||||
database.execSQL("CREATE UNIQUE INDEX `index_hidden_icons_id` ON `hidden_icons` (`id`)")
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_4_5 = object : Migration(4, 5) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE home_screen_grid_items ADD COLUMN page INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("ALTER TABLE home_screen_grid_items ADD COLUMN docked INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("UPDATE home_screen_grid_items SET docked = 1 WHERE page = 0 AND type != 1 AND top = 5")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -198,6 +198,7 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
appLauncher.packageName,
|
||||
appLauncher.activityName,
|
||||
appLauncher.title,
|
||||
@@ -207,6 +208,7 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
false,
|
||||
appLauncher.drawable
|
||||
)
|
||||
|
||||
|
@@ -256,6 +256,7 @@ class WidgetsFragment(context: Context, attributeSet: AttributeSet) : MyFragment
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
appWidget.appPackageName,
|
||||
"",
|
||||
"",
|
||||
@@ -265,6 +266,7 @@ class WidgetsFragment(context: Context, attributeSet: AttributeSet) : MyFragment
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
false,
|
||||
appWidget.widgetPreviewImage,
|
||||
appWidget.providerInfo,
|
||||
appWidget.activityInfo,
|
||||
|
@@ -4,4 +4,8 @@ interface FlingListener {
|
||||
fun onFlingUp()
|
||||
|
||||
fun onFlingDown()
|
||||
|
||||
fun onFlingRight()
|
||||
|
||||
fun onFlingLeft()
|
||||
}
|
||||
|
@@ -23,8 +23,8 @@ interface HomeScreenGridItemsDao {
|
||||
@Query("UPDATE home_screen_grid_items SET title = :title WHERE id = :id")
|
||||
fun updateItemTitle(title: String, id: Long): Int
|
||||
|
||||
@Query("UPDATE home_screen_grid_items SET `left` = :left, `top` = :top, `right` = :right, `bottom` = :bottom WHERE id = :id")
|
||||
fun updateItemPosition(left: Int, top: Int, right: Int, bottom: Int, id: Long)
|
||||
@Query("UPDATE home_screen_grid_items SET `left` = :left, `top` = :top, `right` = :right, `bottom` = :bottom, `page` = :page, `docked` = :docked WHERE id = :id")
|
||||
fun updateItemPosition(left: Int, top: Int, right: Int, bottom: Int, page: Int, docked: Boolean, id: Long)
|
||||
|
||||
@Query("DELETE FROM home_screen_grid_items WHERE id = :id")
|
||||
fun deleteById(id: Long)
|
||||
|
@@ -15,6 +15,7 @@ data class HomeScreenGridItem(
|
||||
@ColumnInfo(name = "top") var top: Int,
|
||||
@ColumnInfo(name = "right") var right: Int,
|
||||
@ColumnInfo(name = "bottom") var bottom: Int,
|
||||
@ColumnInfo(name = "page") var page: Int,
|
||||
@ColumnInfo(name = "package_name") var packageName: String,
|
||||
@ColumnInfo(name = "activity_name") var activityName: String, // needed at apps that create multiple icons at install, not just the launcher
|
||||
@ColumnInfo(name = "title") var title: String,
|
||||
@@ -24,6 +25,7 @@ data class HomeScreenGridItem(
|
||||
@ColumnInfo(name = "intent") var intent: String, // used at static and dynamic shortcuts on click
|
||||
@ColumnInfo(name = "shortcut_id") var shortcutId: String, // used at pinned shortcuts at startLauncher call
|
||||
@ColumnInfo(name = "icon") var icon: Bitmap? = null, // store images of pinned shortcuts, those cannot be retrieved after creating
|
||||
@ColumnInfo(name = "docked") var docked: Boolean = false, // special flag, meaning that page, top and bottom don't matter for this item, it is always at the bottom of the screen
|
||||
|
||||
@Ignore var drawable: Drawable? = null,
|
||||
@Ignore var providerInfo: AppWidgetProviderInfo? = null, // used at widgets
|
||||
@@ -31,7 +33,7 @@ data class HomeScreenGridItem(
|
||||
@Ignore var widthCells: Int = 1,
|
||||
@Ignore var heightCells: Int = 1
|
||||
) {
|
||||
constructor() : this(null, -1, -1, -1, -1, "", "", "", ITEM_TYPE_ICON, "", -1, "", "", null, null, null, null, 1, 1)
|
||||
constructor() : this(null, -1, -1, -1, -1, 0, "", "", "", ITEM_TYPE_ICON, "", -1, "", "", null, false, null, null, null, 1, 1)
|
||||
|
||||
fun getWidthInCells() = if (right == -1 || left == -1) {
|
||||
widthCells
|
||||
@@ -45,5 +47,21 @@ data class HomeScreenGridItem(
|
||||
bottom - top + 1
|
||||
}
|
||||
|
||||
fun getDockAdjustedTop(rowCount: Int): Int {
|
||||
return if (!docked) {
|
||||
top
|
||||
} else {
|
||||
rowCount - 1
|
||||
}
|
||||
}
|
||||
|
||||
fun getDockAdjustedBottom(rowCount: Int): Int {
|
||||
return if (!docked) {
|
||||
bottom
|
||||
} else {
|
||||
rowCount - 1
|
||||
}
|
||||
}
|
||||
|
||||
fun getItemIdentifier() = "$packageName/$activityName"
|
||||
}
|
||||
|
@@ -1,5 +1,8 @@
|
||||
package com.simplemobiletools.launcher.views
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.appwidget.AppWidgetHostView
|
||||
import android.appwidget.AppWidgetManager
|
||||
@@ -21,6 +24,7 @@ import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.customview.widget.ExploreByTouchHelper
|
||||
import com.google.android.material.math.MathUtils
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.helpers.isSPlus
|
||||
@@ -34,6 +38,7 @@ import com.simplemobiletools.launcher.models.HomeScreenGridItem
|
||||
import kotlinx.android.synthetic.main.activity_main.view.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : RelativeLayout(context, attrs, defStyle) {
|
||||
@@ -51,14 +56,25 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
private var iconMargin = (context.resources.getDimension(R.dimen.icon_side_margin) * 5 / columnCount).toInt()
|
||||
private var labelSideMargin = context.resources.getDimension(R.dimen.small_margin).toInt()
|
||||
private var roundedCornerRadius = context.resources.getDimension(R.dimen.activity_margin)
|
||||
private var pageIndicatorRadius = context.resources.getDimension(R.dimen.page_indicator_dot_radius)
|
||||
private var pageIndicatorMargin = context.resources.getDimension(R.dimen.page_indicator_margin)
|
||||
private var textPaint: TextPaint
|
||||
private var dragShadowCirclePaint: Paint
|
||||
private var emptyPageIndicatorPaint: Paint
|
||||
private var currentPageIndicatorPaint: Paint
|
||||
private var draggedItem: HomeScreenGridItem? = null
|
||||
private var resizedWidget: HomeScreenGridItem? = null
|
||||
private var isFirstDraw = true
|
||||
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
|
||||
|
||||
// apply fake margins at the home screen. Real ones would cause the icons be cut at dragging at screen sides
|
||||
var sideMargins = Rect()
|
||||
|
||||
@@ -73,6 +89,16 @@ 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
ViewCompat.setAccessibilityDelegate(this, HomeScreenGridTouchHelper(this))
|
||||
|
||||
@@ -88,6 +114,14 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
style = Paint.Style.STROKE
|
||||
}
|
||||
|
||||
emptyPageIndicatorPaint = Paint(dragShadowCirclePaint).apply {
|
||||
strokeWidth = context.resources.getDimension(R.dimen.page_indicator_stroke_width)
|
||||
}
|
||||
currentPageIndicatorPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = context.resources.getColor(R.color.hint_white)
|
||||
style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
val sideMargin = context.resources.getDimension(R.dimen.normal_margin).toInt()
|
||||
sideMargins.apply {
|
||||
top = context.statusBarHeight
|
||||
@@ -144,6 +178,11 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
|
||||
gridItems.removeIf { it.id == item.id }
|
||||
if (currentPage > getMaxPage()) {
|
||||
post {
|
||||
prevPage()
|
||||
}
|
||||
}
|
||||
redrawGrid()
|
||||
}
|
||||
}
|
||||
@@ -186,9 +225,42 @@ 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()
|
||||
}
|
||||
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 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 {
|
||||
@@ -235,7 +307,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
updateWidgetPositionAndSize(widgetView, item)
|
||||
ensureBackgroundThread {
|
||||
context.homeScreenGridItemsDB.updateItemPosition(item.left, item.top, item.right, item.bottom, item.id!!)
|
||||
context.homeScreenGridItemsDB.updateItemPosition(item.left, item.top, item.right, item.bottom, item.page, false, item.id!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,9 +346,9 @@ 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.forEach { item ->
|
||||
gridItems.filter { it.page == currentPage || it.docked }.forEach { item ->
|
||||
for (xCell in item.left..item.right) {
|
||||
for (yCell in item.top..item.bottom) {
|
||||
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
|
||||
val cell = Pair(xCell, yCell)
|
||||
val isAnyCellOccupied = wantedCell == cell
|
||||
if (isAnyCellOccupied) {
|
||||
@@ -297,9 +369,11 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
top = yIndex
|
||||
right = xIndex
|
||||
bottom = yIndex
|
||||
page = currentPage
|
||||
docked = yIndex == rowCount - 1
|
||||
|
||||
ensureBackgroundThread {
|
||||
context.homeScreenGridItemsDB.updateItemPosition(left, top, right, bottom, id!!)
|
||||
context.homeScreenGridItemsDB.updateItemPosition(left, top, right, bottom, page, docked, id!!)
|
||||
}
|
||||
}
|
||||
redrawIcons = true
|
||||
@@ -311,6 +385,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
yIndex,
|
||||
xIndex,
|
||||
yIndex,
|
||||
currentPage,
|
||||
draggedItem!!.packageName,
|
||||
draggedItem!!.activityName,
|
||||
draggedItem!!.title,
|
||||
@@ -320,6 +395,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
"",
|
||||
"",
|
||||
draggedItem!!.icon,
|
||||
yIndex == rowCount - 1,
|
||||
draggedItem!!.drawable,
|
||||
draggedItem!!.providerInfo,
|
||||
draggedItem!!.activityInfo
|
||||
@@ -377,9 +453,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
|
||||
var areAllCellsEmpty = true
|
||||
gridItems.filter { it.id != draggedItem?.id }.forEach { item ->
|
||||
gridItems.filter { it.id != draggedItem?.id && (it.page == currentPage || it.docked) }.forEach { item ->
|
||||
for (xCell in item.left..item.right) {
|
||||
for (yCell in item.top..item.bottom) {
|
||||
for (yCell in item.getDockAdjustedTop(rowCount)..item.getDockAdjustedBottom(rowCount)) {
|
||||
val cell = Pair(xCell, yCell)
|
||||
val isAnyCellOccupied = widgetTargetCells.contains(cell)
|
||||
if (isAnyCellOccupied) {
|
||||
@@ -397,6 +473,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
top = widgetRect.top
|
||||
right = widgetRect.right
|
||||
bottom = widgetRect.bottom
|
||||
page = currentPage
|
||||
}
|
||||
|
||||
ensureBackgroundThread {
|
||||
@@ -408,7 +485,15 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
bindWidget(widgetItem, false)
|
||||
}
|
||||
} else {
|
||||
context.homeScreenGridItemsDB.updateItemPosition(widgetItem.left, widgetItem.top, widgetItem.right, widgetItem.bottom, widgetItem.id!!)
|
||||
context.homeScreenGridItemsDB.updateItemPosition(
|
||||
widgetItem.left,
|
||||
widgetItem.top,
|
||||
widgetItem.right,
|
||||
widgetItem.bottom,
|
||||
currentPage,
|
||||
false,
|
||||
widgetItem.id!!
|
||||
)
|
||||
val widgetView = widgetViews.firstOrNull { it.tag == widgetItem.widgetId }
|
||||
if (widgetView != null && !widgetItem.outOfBounds()) {
|
||||
post {
|
||||
@@ -423,6 +508,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
right = widgetItem.right
|
||||
top = widgetItem.top
|
||||
bottom = widgetItem.bottom
|
||||
page = widgetItem.page
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -462,6 +548,10 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
} else {
|
||||
removeItemFromHomeScreen(item)
|
||||
}
|
||||
|
||||
if (currentPage > getMaxPage()) {
|
||||
prevPage(redraw = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -494,7 +584,26 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
|
||||
private fun updateWidgetPositionAndSize(widgetView: AppWidgetHostView, item: HomeScreenGridItem): Size {
|
||||
widgetView.x = calculateWidgetX(item.left)
|
||||
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 (item.page == currentPage) {
|
||||
x += width * xFactor
|
||||
}
|
||||
if (item.page == lastPage) {
|
||||
x += width * lastXFactor
|
||||
}
|
||||
}
|
||||
widgetView.x = x
|
||||
widgetView.y = calculateWidgetY(item.top)
|
||||
val widgetWidth = item.getWidthInCells() * cellWidth
|
||||
val widgetHeight = item.getHeightInCells() * cellHeight
|
||||
@@ -554,16 +663,23 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
fillCellSizes()
|
||||
}
|
||||
|
||||
gridItems.filter { it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT }.forEach { item ->
|
||||
if (item.outOfBounds()) {
|
||||
return@forEach
|
||||
}
|
||||
val currentXFactor = if (currentPage > lastPage) {
|
||||
pageChangeAnimLeftPercentage
|
||||
} else {
|
||||
-pageChangeAnimLeftPercentage
|
||||
}
|
||||
val lastXFactor = if (currentPage > lastPage) {
|
||||
pageChangeAnimLeftPercentage - 1
|
||||
} else {
|
||||
1 - pageChangeAnimLeftPercentage
|
||||
}
|
||||
|
||||
fun handleItemDrawing(item: HomeScreenGridItem, xFactor: Float) {
|
||||
if (item.id != draggedItem?.id) {
|
||||
val drawableX = cellXCoords[item.left] + iconMargin + extraXMargin + sideMargins.left
|
||||
val drawableX = cellXCoords[item.left] + iconMargin + extraXMargin + sideMargins.left + (width * xFactor).toInt()
|
||||
|
||||
if (item.top == rowCount - 1) {
|
||||
val drawableY = cellYCoords[item.top] + cellHeight - iconMargin - iconSize + sideMargins.top
|
||||
if (item.docked) {
|
||||
val drawableY = cellYCoords[rowCount - 1] + cellHeight - iconMargin - iconSize + sideMargins.top
|
||||
|
||||
item.drawable!!.setBounds(drawableX, drawableY, drawableX + iconSize, drawableY + iconSize)
|
||||
} else {
|
||||
@@ -571,7 +687,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
item.drawable!!.setBounds(drawableX, drawableY, drawableX + iconSize, drawableY + iconSize)
|
||||
|
||||
if (item.id != draggedItem?.id && item.title.isNotEmpty()) {
|
||||
val textX = cellXCoords[item.left].toFloat() + labelSideMargin + sideMargins.left
|
||||
val textX = cellXCoords[item.left].toFloat() + labelSideMargin + sideMargins.left + width * xFactor
|
||||
val textY = cellYCoords[item.top].toFloat() + iconSize + iconMargin + extraYMargin + labelSideMargin + sideMargins.top
|
||||
val staticLayout = StaticLayout.Builder
|
||||
.obtain(item.title, 0, item.title.length, textPaint, cellWidth - 2 * labelSideMargin)
|
||||
@@ -591,11 +707,64 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
}
|
||||
|
||||
if (isFirstDraw || redrawWidgets) {
|
||||
gridItems.filter { it.type == ITEM_TYPE_WIDGET && !it.outOfBounds() }.forEach { item ->
|
||||
bindWidget(item, isFirstDraw)
|
||||
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.page == currentPage && !it.docked }
|
||||
.forEach { item ->
|
||||
if (item.outOfBounds()) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
handleItemDrawing(item, currentXFactor)
|
||||
}
|
||||
redrawWidgets = false
|
||||
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.docked }.forEach { item ->
|
||||
if (item.outOfBounds()) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
handleItemDrawing(item, 0f)
|
||||
}
|
||||
if (pageChangeAnimLeftPercentage > 0f && pageChangeAnimLeftPercentage < 1f) {
|
||||
gridItems.filter { (it.drawable != null && it.type == ITEM_TYPE_ICON || it.type == ITEM_TYPE_SHORTCUT) && it.page == lastPage && !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)
|
||||
}
|
||||
} else {
|
||||
gridItems.filter { it.type == ITEM_TYPE_WIDGET && !it.outOfBounds() }.forEach { item ->
|
||||
widgetViews.firstOrNull { it.tag == item.widgetId }?.also {
|
||||
updateWidgetPositionAndSize(it, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only draw page indicators when there is a need for it
|
||||
if (getMaxPage() > 0 || currentPage > 0) {
|
||||
val pageCount = max(getMaxPage(), currentPage) + 1
|
||||
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
|
||||
// Draw empty page indicators
|
||||
for (page in 0 until pageCount) {
|
||||
canvas.drawCircle(currentPageIndicatorLeft + pageIndicatorRadius, pageIndicatorY, pageIndicatorRadius, emptyPageIndicatorPaint)
|
||||
currentPageIndicatorLeft += pageIndicatorStep
|
||||
}
|
||||
|
||||
// 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)
|
||||
canvas.drawCircle(currentIndicatorPosition + pageIndicatorRadius, pageIndicatorY, pageIndicatorRadius, currentPageIndicatorPaint)
|
||||
}
|
||||
|
||||
if (draggedItem != null && draggedItemCurrentCoords.first != -1 && draggedItemCurrentCoords.second != -1) {
|
||||
@@ -707,8 +876,8 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
|
||||
val clickableLeft = cellXCoords[item.left] + sideMargins.left + extraXMargin
|
||||
val clickableTop = if (item.top == rowCount - 1) {
|
||||
cellYCoords[item.top] + cellHeight - iconSize - iconMargin
|
||||
val clickableTop = if (item.docked) {
|
||||
cellYCoords[item.getDockAdjustedTop(rowCount)] + cellHeight - iconSize - iconMargin
|
||||
} else {
|
||||
cellYCoords[item.top] - iconMargin + extraYMargin
|
||||
} + sideMargins.top
|
||||
@@ -741,7 +910,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
|
||||
fun isClickingGridItem(x: Int, y: Int): HomeScreenGridItem? {
|
||||
for (gridItem in gridItems) {
|
||||
for (gridItem in gridItems.filter { it.page == currentPage || it.docked }) {
|
||||
if (gridItem.outOfBounds()) {
|
||||
continue
|
||||
}
|
||||
@@ -775,7 +944,7 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
|
||||
private fun HomeScreenGridItem.outOfBounds(): Boolean {
|
||||
return (left >= cellXCoords.size || right >= cellXCoords.size || top >= cellYCoords.size || bottom >= cellYCoords.size)
|
||||
return (left >= cellXCoords.size || right >= cellXCoords.size || (!docked && (top >= cellYCoords.size - 1 || bottom >= cellYCoords.size - 1)))
|
||||
}
|
||||
|
||||
private inner class HomeScreenGridTouchHelper(host: View) : ExploreByTouchHelper(host) {
|
||||
@@ -790,7 +959,9 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
}
|
||||
|
||||
override fun getVisibleVirtualViews(virtualViewIds: MutableList<Int>?) {
|
||||
val sorted = gridItems.sortedBy { it.top * 100 + it.left }
|
||||
val sorted = gridItems.sortedBy {
|
||||
it.getDockAdjustedTop(rowCount) * 100 + it.left
|
||||
}
|
||||
sorted.forEachIndexed { index, homeScreenGridItem ->
|
||||
virtualViewIds?.add(index, homeScreenGridItem.id?.toInt() ?: index)
|
||||
}
|
||||
@@ -832,4 +1003,85 @@ class HomeScreenGrid(context: Context, attrs: AttributeSet, defStyle: Int) : Rel
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 fun handlePageChange(redraw: Boolean = false) {
|
||||
pageChangeEnabled = false
|
||||
if (redraw) {
|
||||
redrawGrid()
|
||||
}
|
||||
ValueAnimator.ofFloat(1f, 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
|
||||
schedulePageChange()
|
||||
redrawGrid()
|
||||
}
|
||||
})
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PAGE_CHANGE_HOLD_THRESHOLD = 500L
|
||||
|
||||
private enum class PageChangeArea {
|
||||
LEFT,
|
||||
MIDDLE,
|
||||
RIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,5 +5,8 @@
|
||||
<dimen name="widget_preview_size">140dp</dimen>
|
||||
<dimen name="icon_side_margin">10dp</dimen>
|
||||
<dimen name="resize_frame_dot_radius">8dp</dimen>
|
||||
<dimen name="move_gesture_threshold">5dp</dimen>
|
||||
<dimen name="move_gesture_threshold">20dp</dimen>
|
||||
<dimen name="page_indicator_dot_radius">4dp</dimen>
|
||||
<dimen name="page_indicator_stroke_width">1dp</dimen>
|
||||
<dimen name="page_indicator_margin">4dp</dimen>
|
||||
</resources>
|
||||
|
Reference in New Issue
Block a user