Merge pull request #103 from esensar/feature/folders

Add support for folders on home screen
This commit is contained in:
Tibor Kaputa 2023-08-30 16:06:14 +02:00 committed by GitHub
commit f25738a40f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 1246 additions and 346 deletions

View file

@ -5,6 +5,7 @@ import android.content.pm.PackageManager
import android.os.Bundle
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.normalizeString
import com.simplemobiletools.commons.extensions.viewBinding
import com.simplemobiletools.commons.helpers.NavigationIcon
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
@ -17,12 +18,11 @@ import com.simplemobiletools.launcher.extensions.hiddenIconsDB
import com.simplemobiletools.launcher.models.HiddenIcon
class HiddenIconsActivity : SimpleActivity(), RefreshRecyclerViewListener {
private lateinit var binding: ActivityHiddenIconsBinding
private val binding by viewBinding(ActivityHiddenIconsBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
isMaterialActivity = true
super.onCreate(savedInstanceState)
binding = ActivityHiddenIconsBinding.inflate(layoutInflater)
setContentView(binding.root)
updateIcons()

View file

@ -46,6 +46,7 @@ import com.simplemobiletools.launcher.extensions.*
import com.simplemobiletools.launcher.fragments.MyFragment
import com.simplemobiletools.launcher.helpers.*
import com.simplemobiletools.launcher.interfaces.FlingListener
import com.simplemobiletools.launcher.interfaces.ItemMenuListener
import com.simplemobiletools.launcher.models.AppLauncher
import com.simplemobiletools.launcher.models.HiddenIcon
import com.simplemobiletools.launcher.models.HomeScreenGridItem
@ -72,7 +73,7 @@ class MainActivity : SimpleActivity(), FlingListener {
private var wasJustPaused: Boolean = false
private lateinit var mDetector: GestureDetectorCompat
private lateinit var binding: ActivityMainBinding
private val binding by viewBinding(ActivityMainBinding::inflate)
companion object {
private var mLastUpEvent = 0L
@ -84,7 +85,6 @@ class MainActivity : SimpleActivity(), FlingListener {
useDynamicTheme = false
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
appLaunched(BuildConfig.APPLICATION_ID)
@ -132,79 +132,6 @@ class MainActivity : SimpleActivity(), FlingListener {
}
}
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)
if (item.shortcutInfo == null) {
return
}
ensureBackgroundThread {
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 (page, rect) = findFirstEmptyCell()
val gridItem = HomeScreenGridItem(
null,
rect.left,
rect.top,
rect.right,
rect.bottom,
page,
item.shortcutInfo!!.`package`,
"",
label,
ITEM_TYPE_SHORTCUT,
"",
-1,
shortcutId,
icon.toBitmap(),
false,
icon
)
runOnUiThread {
binding.homeScreenGrid.root.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)
try {
item.accept()
binding.homeScreenGrid.root.storeAndShowGridItem(gridItem)
} catch (ignored: IllegalStateException) {
}
}
}
}
private fun findFirstEmptyCell(): Pair<Int, Rect> {
val gridItems = homeScreenGridItemsDB.getAllItems() as ArrayList<HomeScreenGridItem>
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(Triple(item.page, xCell, yCell))
}
}
}
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 Pair(maxPage + 1, Rect(0, 0, 0, 0))
}
override fun onStart() {
super.onStart()
binding.homeScreenGrid.root.appWidgetHost.startListening()
@ -256,12 +183,13 @@ class MainActivity : SimpleActivity(), FlingListener {
newRowCount = config.homeRowCount,
newColumnCount = config.homeColumnCount
)
binding.homeScreenGrid.root.updateColors()
binding.allAppsFragment.root.onResume()
}
override fun onStop() {
super.onStop()
binding.homeScreenGrid.root.appWidgetHost?.stopListening()
binding.homeScreenGrid.root.appWidgetHost.stopListening()
wasJustPaused = false
}
@ -351,7 +279,7 @@ class MainActivity : SimpleActivity(), FlingListener {
hasFingerMoved(event)
}
if (mLongPressedIcon != null && mOpenPopupMenu != null && hasFingerMoved) {
if (mLongPressedIcon != null && (mOpenPopupMenu != null) && hasFingerMoved) {
mOpenPopupMenu?.dismiss()
mOpenPopupMenu = null
binding.homeScreenGrid.root.itemDraggingStarted(mLongPressedIcon!!)
@ -422,6 +350,80 @@ class MainActivity : SimpleActivity(), FlingListener {
return true
}
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)
if (item.shortcutInfo == null) {
return
}
ensureBackgroundThread {
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 (page, rect) = findFirstEmptyCell()
val gridItem = HomeScreenGridItem(
null,
rect.left,
rect.top,
rect.right,
rect.bottom,
page,
item.shortcutInfo!!.`package`,
"",
label,
ITEM_TYPE_SHORTCUT,
"",
-1,
shortcutId,
icon.toBitmap(),
false,
null,
icon
)
runOnUiThread {
binding.homeScreenGrid.root.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)
try {
item.accept()
binding.homeScreenGrid.root.storeAndShowGridItem(gridItem)
} catch (ignored: IllegalStateException) {
}
}
}
}
private fun findFirstEmptyCell(): Pair<Int, Rect> {
val gridItems = homeScreenGridItemsDB.getAllItems() as ArrayList<HomeScreenGridItem>
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(Triple(item.page, xCell, yCell))
}
}
}
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 Pair(maxPage + 1, Rect(0, 0, 0, 0))
}
// 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))
@ -524,6 +526,9 @@ class MainActivity : SimpleActivity(), FlingListener {
if (clickedGridItem != null) {
performItemClick(clickedGridItem)
}
if (clickedGridItem?.type != ITEM_TYPE_FOLDER) {
binding.homeScreenGrid.root.closeFolder(redraw = true)
}
}
fun closeAppDrawer(delayed: Boolean = false) {
@ -559,20 +564,26 @@ class MainActivity : SimpleActivity(), FlingListener {
}
private fun performItemClick(clickedGridItem: HomeScreenGridItem) {
if (clickedGridItem.type == ITEM_TYPE_ICON) {
launchApp(clickedGridItem.packageName, clickedGridItem.activityName)
} else if (clickedGridItem.type == ITEM_TYPE_SHORTCUT) {
val id = clickedGridItem.shortcutId
val packageName = clickedGridItem.packageName
val userHandle = android.os.Process.myUserHandle()
val shortcutBounds = binding.homeScreenGrid.root.getClickableRect(clickedGridItem)
val launcherApps = applicationContext.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
launcherApps.startShortcut(packageName, id, shortcutBounds, null, userHandle)
when (clickedGridItem.type) {
ITEM_TYPE_ICON -> launchApp(clickedGridItem.packageName, clickedGridItem.activityName)
ITEM_TYPE_FOLDER -> openFolder(clickedGridItem)
ITEM_TYPE_SHORTCUT -> {
val id = clickedGridItem.shortcutId
val packageName = clickedGridItem.packageName
val userHandle = android.os.Process.myUserHandle()
val shortcutBounds = binding.homeScreenGrid.root.getClickableRect(clickedGridItem)
val launcherApps = applicationContext.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
launcherApps.startShortcut(packageName, id, shortcutBounds, null, userHandle)
}
}
}
private fun openFolder(folder: HomeScreenGridItem) {
binding.homeScreenGrid.root.openFolder(folder)
}
private fun performItemLongClick(x: Float, clickedGridItem: HomeScreenGridItem) {
if (clickedGridItem.type == ITEM_TYPE_ICON || clickedGridItem.type == ITEM_TYPE_SHORTCUT) {
if (clickedGridItem.type == ITEM_TYPE_ICON || clickedGridItem.type == ITEM_TYPE_SHORTCUT || clickedGridItem.type == ITEM_TYPE_FOLDER) {
binding.mainHolder.performHapticFeedback()
}
@ -595,7 +606,7 @@ class MainActivity : SimpleActivity(), FlingListener {
binding.homeScreenPopupMenuAnchor.y = anchorY
if (mOpenPopupMenu == null) {
mOpenPopupMenu = handleGridItemPopupMenu(binding.homeScreenPopupMenuAnchor, gridItem, isOnAllAppsFragment)
mOpenPopupMenu = handleGridItemPopupMenu(binding.homeScreenPopupMenuAnchor, gridItem, isOnAllAppsFragment, menuListener)
}
}
@ -623,52 +634,6 @@ class MainActivity : SimpleActivity(), FlingListener {
}
}
private fun handleGridItemPopupMenu(anchorView: View, gridItem: HomeScreenGridItem, isOnAllAppsFragment: Boolean): PopupMenu {
val contextTheme = ContextThemeWrapper(this, getPopupMenuTheme())
return PopupMenu(contextTheme, anchorView, Gravity.TOP or Gravity.END).apply {
if (isQPlus()) {
setForceShowIcon(true)
}
inflate(R.menu.menu_app_icon)
menu.findItem(R.id.rename).isVisible = gridItem.type == ITEM_TYPE_ICON && !isOnAllAppsFragment
menu.findItem(R.id.hide_icon).isVisible = gridItem.type == ITEM_TYPE_ICON && isOnAllAppsFragment
menu.findItem(R.id.resize).isVisible = gridItem.type == ITEM_TYPE_WIDGET
menu.findItem(R.id.app_info).isVisible = gridItem.type == ITEM_TYPE_ICON
menu.findItem(R.id.uninstall).isVisible = gridItem.type == ITEM_TYPE_ICON && canAppBeUninstalled(gridItem.packageName)
menu.findItem(R.id.remove).isVisible = !isOnAllAppsFragment
setOnMenuItemClickListener { item ->
resetFragmentTouches()
when (item.itemId) {
R.id.hide_icon -> hideIcon(gridItem)
R.id.rename -> renameItem(gridItem)
R.id.resize -> binding.homeScreenGrid.root.widgetLongPressed(gridItem)
R.id.app_info -> launchAppInfo(gridItem.packageName)
R.id.remove -> binding.homeScreenGrid.root.removeAppIcon(gridItem)
R.id.uninstall -> uninstallApp(gridItem.packageName)
}
true
}
setOnDismissListener {
mOpenPopupMenu = null
resetFragmentTouches()
}
var visibleMenuItems = 0
for (item in menu.iterator()) {
if (item.isVisible) {
visibleMenuItems++
}
}
val yOffset = resources.getDimension(R.dimen.long_press_anchor_button_offset_y) * (visibleMenuItems - 1)
anchorView.y -= yOffset
show()
}
}
private fun resetFragmentTouches() {
binding.widgetsFragment.root.apply {
touchDownY = -1
@ -714,6 +679,53 @@ class MainActivity : SimpleActivity(), FlingListener {
}
}
val menuListener: ItemMenuListener = object : ItemMenuListener {
override fun onAnyClick() {
resetFragmentTouches()
}
override fun hide(gridItem: HomeScreenGridItem) {
hideIcon(gridItem)
}
override fun rename(gridItem: HomeScreenGridItem) {
renameItem(gridItem)
}
override fun resize(gridItem: HomeScreenGridItem) {
binding.homeScreenGrid.root.widgetLongPressed(gridItem)
}
override fun appInfo(gridItem: HomeScreenGridItem) {
launchAppInfo(gridItem.packageName)
}
override fun remove(gridItem: HomeScreenGridItem) {
binding.homeScreenGrid.root.removeAppIcon(gridItem)
}
override fun uninstall(gridItem: HomeScreenGridItem) {
uninstallApp(gridItem.packageName)
}
override fun onDismiss() {
mOpenPopupMenu = null
resetFragmentTouches()
}
override fun beforeShow(menu: Menu) {
var visibleMenuItems = 0
for (item in menu.iterator()) {
if (item.isVisible) {
visibleMenuItems++
}
}
val yOffset = resources.getDimension(R.dimen.long_press_anchor_button_offset_y) * (visibleMenuItems - 1)
binding.homeScreenPopupMenuAnchor.y -= yOffset
}
}
private class MyGestureListener(private val flingListener: FlingListener) : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(event: MotionEvent): Boolean {
(flingListener as MainActivity).homeScreenClicked(event.x, event.y)
@ -857,7 +869,8 @@ class MainActivity : SimpleActivity(), FlingListener {
-1,
"",
null,
true
true,
null
)
homeScreenGridItems.add(dialerIcon)
}
@ -883,7 +896,8 @@ class MainActivity : SimpleActivity(), FlingListener {
-1,
"",
null,
true
true,
null
)
homeScreenGridItems.add(SMSMessengerIcon)
}
@ -911,7 +925,8 @@ class MainActivity : SimpleActivity(), FlingListener {
-1,
"",
null,
true
true,
null
)
homeScreenGridItems.add(browserIcon)
}
@ -938,7 +953,8 @@ class MainActivity : SimpleActivity(), FlingListener {
-1,
"",
null,
true
true,
null
)
homeScreenGridItems.add(storeIcon)
}
@ -967,7 +983,8 @@ class MainActivity : SimpleActivity(), FlingListener {
-1,
"",
null,
true
true,
null
)
homeScreenGridItems.add(cameraIcon)
}

View file

@ -21,11 +21,10 @@ import kotlin.system.exitProcess
class SettingsActivity : SimpleActivity() {
private lateinit var binding: ActivitySettingsBinding
private val binding by viewBinding(ActivitySettingsBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
isMaterialActivity = true
super.onCreate(savedInstanceState)
binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
updateMaterialActivityViews(binding.settingsCoordinator, binding.settingsHolder, useTransparentNavigation = true, useTopSearchMenu = false)

View file

@ -0,0 +1,109 @@
package com.simplemobiletools.launcher.adapters
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import androidx.core.view.iterator
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.launcher.R
import com.simplemobiletools.launcher.activities.MainActivity
import com.simplemobiletools.launcher.databinding.ItemLauncherLabelBinding
import com.simplemobiletools.launcher.extensions.handleGridItemPopupMenu
import com.simplemobiletools.launcher.interfaces.ItemMenuListenerAdapter
import com.simplemobiletools.launcher.models.HomeScreenGridItem
class FolderIconsAdapter(
activity: BaseSimpleActivity, var items: MutableList<HomeScreenGridItem>, private val iconPadding: Int,
recyclerView: MyRecyclerView, itemClick: (Any) -> Unit
) : MyRecyclerViewAdapter(activity, recyclerView, itemClick) {
override fun getActionMenuId() = 0
override fun actionItemPressed(id: Int) {}
override fun getSelectableItemCount() = itemCount
override fun getIsItemSelectable(position: Int) = false
override fun getItemSelectionKey(position: Int) = items.getOrNull(position)?.id?.toInt()
override fun getItemKeyPosition(key: Int) = items.indexOfFirst { it.id?.toInt() == key }
override fun onActionModeCreated() {}
override fun onActionModeDestroyed() {}
override fun prepareActionMode(menu: Menu) {}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return createViewHolder(ItemLauncherLabelBinding.inflate(layoutInflater, parent, false).root)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
setupView(holder.itemView, item)
bindViewHolder(holder)
}
override fun getItemCount() = items.size
private fun removeItem(item: HomeScreenGridItem) {
val position = items.indexOfFirst { it.id == item.id }
items.removeAt(position)
notifyItemRemoved(position)
}
private fun setupView(view: View, item: HomeScreenGridItem) {
ItemLauncherLabelBinding.bind(view).apply {
launcherLabel.text = item.title
launcherLabel.setTextColor(textColor)
launcherIcon.setPadding(iconPadding, iconPadding, iconPadding, 0)
launcherIcon.setImageDrawable(item.drawable)
val mainListener = (activity as? MainActivity)?.menuListener
root.setOnClickListener { itemClick(item) }
root.setOnLongClickListener {
popupAnchor.y = launcherIcon.y
activity.handleGridItemPopupMenu(popupAnchor, item, false, object : ItemMenuListenerAdapter() {
override fun appInfo(gridItem: HomeScreenGridItem) {
mainListener?.appInfo(gridItem)
}
override fun remove(gridItem: HomeScreenGridItem) {
mainListener?.remove(gridItem)
removeItem(gridItem)
}
override fun uninstall(gridItem: HomeScreenGridItem) {
mainListener?.uninstall(gridItem)
}
override fun rename(gridItem: HomeScreenGridItem) {
mainListener?.rename(gridItem)
}
override fun beforeShow(menu: Menu) {
var visibleMenuItems = 0
for (menuItem in menu.iterator()) {
if (menuItem.isVisible) {
visibleMenuItems++
}
}
val yOffset = resources.getDimension(R.dimen.long_press_anchor_button_offset_y) * (visibleMenuItems - 1)
popupAnchor.y -= yOffset
}
})
true
}
}
}
fun updateItems(items: List<HomeScreenGridItem>) {
this.items.clear()
this.items.addAll(items)
notifyDataSetChanged()
}
}

View file

@ -68,8 +68,8 @@ abstract class AppsDatabase : RoomDatabase() {
private val MIGRATION_4_5 = object : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE `home_screen_grid_items_new` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `left` INTEGER NOT NULL, `top` INTEGER NOT NULL, `right` INTEGER NOT NULL, `bottom` INTEGER NOT NULL, `page` INTEGER NOT NULL, `package_name` TEXT NOT NULL, `activity_name` TEXT NOT NULL, `title` TEXT NOT NULL, `type` INTEGER NOT NULL, `class_name` TEXT NOT NULL, `widget_id` INTEGER NOT NULL, `shortcut_id` TEXT NOT NULL, `icon` BLOB, `docked` INTEGER NOT NULL DEFAULT 0)")
database.execSQL("INSERT INTO `home_screen_grid_items_new` (`id`, `left`, `top`, `right`, `bottom`, `page`, `package_name`, `activity_name`, `title`, `type`, `class_name`, `widget_id`, `shortcut_id`, `icon`, `docked`) SELECT `id`, `left`, `top`, `right`, `bottom`, 0 as `page`, `package_name`, `activity_name`, `title`, `type`, `class_name`, `widget_id`, `shortcut_id`, `icon`, CASE WHEN `type` != 1 AND `top` = 5 THEN 1 ELSE 0 END AS `docked` FROM `home_screen_grid_items` WHERE `intent` IS NULL OR `intent` = ''")
database.execSQL("CREATE TABLE `home_screen_grid_items_new` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `left` INTEGER NOT NULL, `top` INTEGER NOT NULL, `right` INTEGER NOT NULL, `bottom` INTEGER NOT NULL, `page` INTEGER NOT NULL, `package_name` TEXT NOT NULL, `activity_name` TEXT NOT NULL, `title` TEXT NOT NULL, `type` INTEGER NOT NULL, `class_name` TEXT NOT NULL, `widget_id` INTEGER NOT NULL, `shortcut_id` TEXT NOT NULL, `icon` BLOB, `docked` INTEGER NOT NULL DEFAULT 0, `parent_id` INTEGER)")
database.execSQL("INSERT INTO `home_screen_grid_items_new` (`id`, `left`, `top`, `right`, `bottom`, `page`, `package_name`, `activity_name`, `title`, `type`, `class_name`, `widget_id`, `shortcut_id`, `icon`, `docked`, `parent_id`) SELECT `id`, `left`, `top`, `right`, `bottom`, 0 as `page`, `package_name`, `activity_name`, `title`, `type`, `class_name`, `widget_id`, `shortcut_id`, `icon`, CASE WHEN `type` != 1 AND `top` = 5 THEN 1 ELSE 0 END AS `docked`, NULL AS `parent_id` FROM `home_screen_grid_items` WHERE `intent` IS NULL OR `intent` = ''")
database.execSQL("DROP TABLE `home_screen_grid_items`")
database.execSQL("ALTER TABLE `home_screen_grid_items_new` RENAME TO `home_screen_grid_items`")
database.execSQL("CREATE UNIQUE INDEX `index_home_screen_grid_items_id` ON `home_screen_grid_items` (`id`)")

View file

@ -6,9 +6,21 @@ import android.content.Intent
import android.content.pm.ApplicationInfo
import android.net.Uri
import android.provider.Settings
import android.view.ContextThemeWrapper
import android.view.Gravity
import android.view.View
import android.widget.PopupMenu
import com.simplemobiletools.commons.extensions.getPopupMenuTheme
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.helpers.isQPlus
import com.simplemobiletools.launcher.R
import com.simplemobiletools.launcher.activities.SettingsActivity
import com.simplemobiletools.launcher.helpers.ITEM_TYPE_FOLDER
import com.simplemobiletools.launcher.helpers.ITEM_TYPE_ICON
import com.simplemobiletools.launcher.helpers.ITEM_TYPE_WIDGET
import com.simplemobiletools.launcher.helpers.UNINSTALL_APP_REQUEST_CODE
import com.simplemobiletools.launcher.interfaces.ItemMenuListener
import com.simplemobiletools.launcher.models.HomeScreenGridItem
fun Activity.launchApp(packageName: String, activityName: String) {
// if this is true, launch the app settings
@ -56,3 +68,40 @@ fun Activity.uninstallApp(packageName: String) {
startActivityForResult(this, UNINSTALL_APP_REQUEST_CODE)
}
}
fun Activity.handleGridItemPopupMenu(anchorView: View, gridItem: HomeScreenGridItem, isOnAllAppsFragment: Boolean, listener: ItemMenuListener): PopupMenu {
val contextTheme = ContextThemeWrapper(this, getPopupMenuTheme())
return PopupMenu(contextTheme, anchorView, Gravity.TOP or Gravity.END).apply {
if (isQPlus()) {
setForceShowIcon(true)
}
inflate(R.menu.menu_app_icon)
menu.findItem(R.id.rename).isVisible = (gridItem.type == ITEM_TYPE_ICON || gridItem.type == ITEM_TYPE_FOLDER) && !isOnAllAppsFragment
menu.findItem(R.id.hide_icon).isVisible = gridItem.type == ITEM_TYPE_ICON && isOnAllAppsFragment
menu.findItem(R.id.resize).isVisible = gridItem.type == ITEM_TYPE_WIDGET
menu.findItem(R.id.app_info).isVisible = gridItem.type == ITEM_TYPE_ICON
menu.findItem(R.id.uninstall).isVisible = gridItem.type == ITEM_TYPE_ICON && canAppBeUninstalled(gridItem.packageName)
menu.findItem(R.id.remove).isVisible = !isOnAllAppsFragment
setOnMenuItemClickListener { item ->
listener.onAnyClick()
when (item.itemId) {
R.id.hide_icon -> listener.hide(gridItem)
R.id.rename -> listener.rename(gridItem)
R.id.resize -> listener.resize(gridItem)
R.id.app_info -> listener.appInfo(gridItem)
R.id.remove -> listener.remove(gridItem)
R.id.uninstall -> listener.uninstall(gridItem)
}
true
}
setOnDismissListener {
listener.onDismiss()
}
listener.beforeShow(menu)
show()
}
}

View file

@ -207,6 +207,7 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment
"",
null,
false,
null,
appLauncher.drawable
)

View file

@ -267,6 +267,7 @@ class WidgetsFragment(context: Context, attributeSet: AttributeSet) : MyFragment
"",
null,
false,
null,
appWidget.widgetPreviewImage,
appWidget.providerInfo,
appWidget.activityInfo,

View file

@ -27,6 +27,7 @@ const val REQUEST_CREATE_SHORTCUT = 53
const val ITEM_TYPE_ICON = 0
const val ITEM_TYPE_WIDGET = 1
const val ITEM_TYPE_SHORTCUT = 2
const val ITEM_TYPE_FOLDER = 3
const val WIDGET_HOST_ID = 12345
const val MAX_CLICK_DURATION = 150

View file

@ -1,9 +1,6 @@
package com.simplemobiletools.launcher.interfaces
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.*
import com.simplemobiletools.launcher.models.HomeScreenGridItem
@Dao
@ -11,6 +8,9 @@ interface HomeScreenGridItemsDao {
@Query("SELECT * FROM home_screen_grid_items")
fun getAllItems(): List<HomeScreenGridItem>
@Query("SELECT * FROM home_screen_grid_items WHERE parent_id = :folderId")
fun getFolderItems(folderId: Long): List<HomeScreenGridItem>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(item: HomeScreenGridItem): Long
@ -23,12 +23,33 @@ 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, `page` = :page, `docked` = :docked WHERE id = :id")
fun updateItemPosition(left: Int, top: Int, right: Int, bottom: Int, page: Int, docked: Boolean, id: Long)
@Query("UPDATE home_screen_grid_items SET `left` = :left, `top` = :top, `right` = :right, `bottom` = :bottom, `page` = :page, `docked` = :docked , `parent_id` = :parentId WHERE id = :id")
fun updateItemPosition(left: Int, top: Int, right: Int, bottom: Int, page: Int, docked: Boolean, parentId: Long?, id: Long)
@Query("DELETE FROM home_screen_grid_items WHERE id = :id")
fun deleteById(id: Long)
fun deleteItemById(id: Long)
@Query("DELETE FROM home_screen_grid_items WHERE parent_id = :id")
fun deleteItemsWithParentId(id: Long)
@Transaction
fun deleteById(id: Long) {
deleteItemById(id)
deleteItemsWithParentId(id)
}
@Query("DELETE FROM home_screen_grid_items WHERE package_name = :packageName")
fun deleteByPackageName(packageName: String)
fun deleteItemByPackageName(packageName: String)
@Query("DELETE FROM home_screen_grid_items WHERE parent_id IN (SELECT id FROM home_screen_grid_items WHERE package_name = :packageName)")
fun deleteItemsByParentPackageName(packageName: String)
@Query("UPDATE home_screen_grid_items SET `left` = `left` + :shiftBy WHERE parent_id == :folderId AND `left` > :shiftFrom AND id != :excludingId")
fun shiftFolderItems(folderId: Long, shiftFrom: Int, shiftBy: Int, excludingId: Long? = null)
@Transaction
fun deleteByPackageName(packageName: String) {
deleteItemByPackageName(packageName)
deleteItemsByParentPackageName(packageName)
}
}

View file

@ -0,0 +1,28 @@
package com.simplemobiletools.launcher.interfaces
import android.view.Menu
import com.simplemobiletools.launcher.models.HomeScreenGridItem
interface ItemMenuListener {
fun onAnyClick()
fun hide(gridItem: HomeScreenGridItem)
fun rename(gridItem: HomeScreenGridItem)
fun resize(gridItem: HomeScreenGridItem)
fun appInfo(gridItem: HomeScreenGridItem)
fun remove(gridItem: HomeScreenGridItem)
fun uninstall(gridItem: HomeScreenGridItem)
fun onDismiss()
fun beforeShow(menu: Menu)
}
abstract class ItemMenuListenerAdapter : ItemMenuListener {
override fun onAnyClick() = Unit
override fun hide(gridItem: HomeScreenGridItem) = Unit
override fun rename(gridItem: HomeScreenGridItem) = Unit
override fun resize(gridItem: HomeScreenGridItem) = Unit
override fun appInfo(gridItem: HomeScreenGridItem) = Unit
override fun remove(gridItem: HomeScreenGridItem) = Unit
override fun uninstall(gridItem: HomeScreenGridItem) = Unit
override fun onDismiss() = Unit
override fun beforeShow(menu: Menu) = Unit
}

View file

@ -3,6 +3,7 @@ package com.simplemobiletools.launcher.models
import android.appwidget.AppWidgetProviderInfo
import android.content.pm.ActivityInfo
import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.drawable.Drawable
import androidx.room.*
import com.simplemobiletools.launcher.helpers.ITEM_TYPE_ICON
@ -25,6 +26,7 @@ data class HomeScreenGridItem(
@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
@ColumnInfo(name = "parent_id") var parentId: Long? = null, // id of folder this item is in (if it is in any)
@Ignore var drawable: Drawable? = null,
@Ignore var providerInfo: AppWidgetProviderInfo? = null, // used at widgets
@ -32,7 +34,11 @@ data class HomeScreenGridItem(
@Ignore var widthCells: Int = 1,
@Ignore var heightCells: Int = 1
) {
constructor() : this(null, -1, -1, -1, -1, 0, "", "", "", ITEM_TYPE_ICON, "", -1, "", null, false, null, null, null, 1, 1)
companion object {
const val FOLDER_MAX_CAPACITY = 16
}
constructor() : this(null, -1, -1, -1, -1, 0, "", "", "", ITEM_TYPE_ICON, "", -1, "", null, false, null, null, null, null, 1, 1)
fun getWidthInCells() = if (right == -1 || left == -1) {
widthCells
@ -63,4 +69,6 @@ data class HomeScreenGridItem(
}
fun getItemIdentifier() = "$packageName/$activityName"
fun getTopLeft() = Point(left, top)
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/dialog_folder_icons_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/small_margin"
android:layout_marginTop="@dimen/medium_margin"
android:layout_marginEnd="@dimen/small_margin"
android:minHeight="@dimen/min_folder_view_height"
app:layout_constraintHeight_max="@dimen/max_folder_view_height">
<com.simplemobiletools.commons.views.MyRecyclerView
android:id="@+id/dialog_folder_icons_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:itemCount="10"
tools:listitem="@layout/item_launcher_label" />
</androidx.core.widget.NestedScrollView>
</FrameLayout>

View file

@ -27,4 +27,10 @@
android:maxLines="2"
android:textSize="@dimen/smaller_text_size" />
<View
android:id="@+id/popup_anchor"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center_horizontal|top" />
</RelativeLayout>

View file

@ -9,4 +9,6 @@
<dimen name="page_indicator_dot_radius">6dp</dimen>
<dimen name="page_indicator_stroke_width">1dp</dimen>
<dimen name="page_indicator_margin">6dp</dimen>
<dimen name="min_folder_view_height">200dp</dimen>
<dimen name="max_folder_view_height">500dp</dimen>
</resources>