mirror of
				https://github.com/SimpleMobileTools/Simple-Launcher.git
				synced 2025-06-05 21:59:15 +02:00 
			
		
		
		
	Merge pull request #103 from esensar/feature/folders
Add support for folders on home screen
This commit is contained in:
		| @@ -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() | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|             } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
| } | ||||
| @@ -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`)") | ||||
|   | ||||
| @@ -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() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -207,6 +207,7 @@ class AllAppsFragment(context: Context, attributeSet: AttributeSet) : MyFragment | ||||
|             "", | ||||
|             null, | ||||
|             false, | ||||
|             null, | ||||
|             appLauncher.drawable | ||||
|         ) | ||||
|  | ||||
|   | ||||
| @@ -267,6 +267,7 @@ class WidgetsFragment(context: Context, attributeSet: AttributeSet) : MyFragment | ||||
|             "", | ||||
|             null, | ||||
|             false, | ||||
|             null, | ||||
|             appWidget.widgetPreviewImage, | ||||
|             appWidget.providerInfo, | ||||
|             appWidget.activityInfo, | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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) | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										26
									
								
								app/src/main/res/layout/dialog_folder_icons.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/src/main/res/layout/dialog_folder_icons.xml
									
									
									
									
									
										Normal 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> | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user