From 84e9ee2280010f0dd0edc5b72eb80fbb0ee02677 Mon Sep 17 00:00:00 2001 From: ottoptj Date: Mon, 5 Aug 2024 15:49:07 +0300 Subject: [PATCH] Fixed an issue with updating apps (and brought back old recycler updating logic tot ry out) --- .../eu/ottop/yamlauncher/AppActionMenu.kt | 1 + .../eu/ottop/yamlauncher/AppMenuAdapter.kt | 63 +++++++-- .../java/eu/ottop/yamlauncher/AppUtils.kt | 18 ++- .../ottop/yamlauncher/GestureAppsFragment.kt | 10 +- .../java/eu/ottop/yamlauncher/MainActivity.kt | 126 +++++++++++++++--- app/src/main/res/xml/root_preferences.xml | 4 +- 6 files changed, 186 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/eu/ottop/yamlauncher/AppActionMenu.kt b/app/src/main/java/eu/ottop/yamlauncher/AppActionMenu.kt index 297a657..4a4e7ad 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/AppActionMenu.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/AppActionMenu.kt @@ -61,6 +61,7 @@ class AppActionMenu { activity.startActivity(intent) animations.fadeViewOut(actionMenu, 100) + textView.visibility = View.VISIBLE } diff --git a/app/src/main/java/eu/ottop/yamlauncher/AppMenuAdapter.kt b/app/src/main/java/eu/ottop/yamlauncher/AppMenuAdapter.kt index 3bc6be3..0641c31 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/AppMenuAdapter.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/AppMenuAdapter.kt @@ -1,10 +1,10 @@ package eu.ottop.yamlauncher -import android.R.color import android.annotation.SuppressLint import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.LauncherActivityInfo +import android.content.pm.LauncherApps import android.graphics.BlendMode import android.graphics.BlendModeColorFilter import android.graphics.Color @@ -27,7 +27,8 @@ class AppMenuAdapter( var apps: MutableList>>, private val itemClickListener: OnItemClickListener, private val shortcutListener: OnShortcutListener, - private val itemLongClickListener: OnItemLongClickListener + private val itemLongClickListener: OnItemLongClickListener, + private val launcherApps: LauncherApps ) : RecyclerView.Adapter() { @@ -151,17 +152,38 @@ class AppMenuAdapter( } } - val appInfo = app.first.activityInfo.applicationInfo - holder.textView.setTextColor(Color.parseColor(preferences?.getString("textColor", "#FFF3F3F3"))) - holder.textView.text = sharedPreferenceManager.getAppName(activity, app.first.applicationInfo.packageName,app.second.second, holder.itemView.context.packageManager.getApplicationLabel(appInfo)) - holder.editText.setText(holder.textView.text) + val appUtils = AppUtils() + var appInfo = appUtils.getAppInfo( + launcherApps, + app.first.applicationInfo.packageName, + app.second.second + ) - if (appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) { - holder.actionMenuLayout.findViewById(R.id.uninstall).visibility = View.GONE - } - else { - holder.actionMenuLayout.findViewById(R.id.uninstall).visibility = View.VISIBLE + holder.textView.setTextColor(Color.parseColor(preferences?.getString("textColor", "#FFF3F3F3"))) + var appLabel: CharSequence = "" + appLabel = appInfo?.loadLabel(activity.packageManager) ?: "Removing..." + + println(appLabel) + + if (appInfo != null) { + holder.textView.text = sharedPreferenceManager.getAppName( + activity, + appInfo.packageName, + app.second.second, + appLabel + ) + + holder.editText.setText(holder.textView.text) + + if (appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) { + holder.actionMenuLayout.findViewById(R.id.uninstall).visibility = + View.GONE + } else { + holder.actionMenuLayout.findViewById(R.id.uninstall).visibility = + View.VISIBLE + } } + else {holder.textView.text = appLabel} holder.textView.visibility = View.VISIBLE } @@ -170,9 +192,28 @@ class AppMenuAdapter( return apps.size } + fun addApp(position: Int, app: Pair>) { + apps.add(position, app) + } + + fun removeApp(position: Int) { + apps.removeAt(position) + } + + fun updateApp(position: Int, app: Pair>) { + apps[position] = app + } + + fun moveApp(position: Int, newPosition: Int) { + val app = apps.removeAt(position) + apps.add(newPosition, app) + + } + @SuppressLint("NotifyDataSetChanged") fun updateApps(newApps: List>>) { apps = newApps.toMutableList() notifyDataSetChanged() } + } \ No newline at end of file diff --git a/app/src/main/java/eu/ottop/yamlauncher/AppUtils.kt b/app/src/main/java/eu/ottop/yamlauncher/AppUtils.kt index 5824af4..a1852e3 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/AppUtils.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/AppUtils.kt @@ -1,6 +1,7 @@ package eu.ottop.yamlauncher import android.app.Activity +import android.content.pm.ApplicationInfo import android.content.pm.LauncherActivityInfo import android.content.pm.LauncherApps import android.os.UserHandle @@ -10,9 +11,9 @@ class AppUtils { private val sharedPreferenceManager = SharedPreferenceManager() - fun getInstalledApps(activity: Activity): List>> { + fun getInstalledApps(activity: Activity, launcherApps: LauncherApps): List>> { val allApps = mutableListOf>>() - val launcherApps = activity.getSystemService(AppCompatActivity.LAUNCHER_APPS_SERVICE) as LauncherApps + for (i in launcherApps.profiles.indices) { launcherApps.getActivityList(null, launcherApps.profiles[i]).forEach { app -> if (!sharedPreferenceManager.isAppHidden(activity, app.applicationInfo.packageName, i) && app.applicationInfo.packageName != activity.applicationInfo.packageName) { @@ -21,7 +22,7 @@ class AppUtils { } } return allApps.sortedBy { - sharedPreferenceManager.getAppName(activity, it.first.applicationInfo.packageName,it.second.second, activity.packageManager.getApplicationLabel(it.first.applicationInfo)).toString().lowercase() + sharedPreferenceManager.getAppName(activity, it.first.applicationInfo.packageName,it.second.second, it.first.applicationInfo.loadLabel(activity.packageManager)).toString().lowercase() } } @@ -40,4 +41,15 @@ class AppUtils { sharedPreferenceManager.getAppName(activity, it.first.applicationInfo.packageName,it.second.second, activity.packageManager.getApplicationLabel(it.first.applicationInfo)).toString().lowercase() } } + + fun getAppInfo( + launcherApps: LauncherApps, + packageName: String, + profile: Int + ): ApplicationInfo? { + + return launcherApps.getActivityList( + packageName, launcherApps.profiles[profile] + ).firstOrNull()?.applicationInfo + } } \ No newline at end of file diff --git a/app/src/main/java/eu/ottop/yamlauncher/GestureAppsFragment.kt b/app/src/main/java/eu/ottop/yamlauncher/GestureAppsFragment.kt index 439992e..42e1d78 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/GestureAppsFragment.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/GestureAppsFragment.kt @@ -2,7 +2,9 @@ package eu.ottop.yamlauncher import android.app.Activity import android.app.AlertDialog +import android.content.Context import android.content.pm.LauncherActivityInfo +import android.content.pm.LauncherApps import android.os.Bundle import android.os.UserHandle import android.text.Editable @@ -13,6 +15,7 @@ import android.view.View import android.view.ViewGroup import android.widget.EditText import android.widget.Toast +import androidx.core.content.ContextCompat.getSystemService import androidx.fragment.app.setFragmentResult import androidx.preference.Preference import androidx.recyclerview.widget.RecyclerView @@ -27,6 +30,7 @@ class GestureAppsFragment : Fragment(), GestureAppsAdapter.OnItemClickListener { private val sharedPreferenceManager = SharedPreferenceManager() private var stringUtils = StringUtils() private val appUtils = AppUtils() + private lateinit var launcherApps: LauncherApps override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -43,9 +47,11 @@ class GestureAppsFragment : Fragment(), GestureAppsAdapter.OnItemClickListener { withContext(Dispatchers.Default) { + launcherApps = requireContext().getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps + adapter = GestureAppsAdapter( requireContext(), - appUtils.getInstalledApps(activity as Activity).toMutableList(), + appUtils.getInstalledApps(activity as Activity, launcherApps).toMutableList(), this@GestureAppsFragment ) } @@ -93,7 +99,7 @@ class GestureAppsFragment : Fragment(), GestureAppsAdapter.OnItemClickListener { val cleanQuery = stringUtils.cleanString(query) val newFilteredApps = mutableListOf>>() - val updatedApps = appUtils.getInstalledApps(requireActivity()) + val updatedApps = appUtils.getInstalledApps(requireActivity(), launcherApps) getFilteredApps(cleanQuery, newFilteredApps, updatedApps) diff --git a/app/src/main/java/eu/ottop/yamlauncher/MainActivity.kt b/app/src/main/java/eu/ottop/yamlauncher/MainActivity.kt index 8b4ab52..b116389 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/MainActivity.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/MainActivity.kt @@ -15,7 +15,6 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import android.os.UserHandle -import android.provider.MediaStore import android.text.Editable import android.text.TextWatcher import android.view.GestureDetector @@ -25,11 +24,8 @@ import android.view.View import android.view.View.TEXT_ALIGNMENT_CENTER import android.view.View.TEXT_ALIGNMENT_TEXT_END import android.view.View.TEXT_ALIGNMENT_TEXT_START -import android.view.ViewGroup import android.view.ViewTreeObserver -import android.view.WindowManager import android.view.inputmethod.InputMethodManager -import android.widget.EditText import android.widget.LinearLayout import android.widget.TextClock import android.widget.TextView @@ -37,8 +33,6 @@ import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.core.view.children import androidx.core.view.marginLeft @@ -425,7 +419,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh private fun setupApps() { handleListItems() CoroutineScope(Dispatchers.Default).launch { - installedApps = appUtils.getInstalledApps(this@MainActivity) + installedApps = appUtils.getInstalledApps(this@MainActivity, launcherApps) val newApps = installedApps.toMutableList() setupRecyclerView(newApps) @@ -499,7 +493,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh } private suspend fun setupRecyclerView(newApps: MutableList>>) { - adapter = AppMenuAdapter(this@MainActivity, newApps, this@MainActivity, this@MainActivity, this@MainActivity) + adapter = AppMenuAdapter(this@MainActivity, newApps, this@MainActivity, this@MainActivity, this@MainActivity, launcherApps) appMenuLinearLayoutManager.stackFromEnd = true recyclerView = findViewById(R.id.recycler_view) withContext(Dispatchers.Main) { @@ -560,7 +554,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh val cleanQuery = stringUtils.cleanString(query) val newFilteredApps = mutableListOf>>() - val updatedApps = appUtils.getInstalledApps(this@MainActivity) + val updatedApps = appUtils.getInstalledApps(this@MainActivity, launcherApps) getFilteredApps(cleanQuery, newFilteredApps, updatedApps) @@ -625,7 +619,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh }, 100) handler.postDelayed({ CoroutineScope(Dispatchers.Default).launch { - refreshAppMenu() + try { withContext(Dispatchers.Main) { @@ -711,18 +705,27 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh } suspend fun refreshAppMenu() { + try { + val updatedApps = appUtils.getInstalledApps(this@MainActivity, launcherApps) + val changes = detectChanges(installedApps, updatedApps) + installedApps = updatedApps + withContext(Dispatchers.Main) { + applyChanges(changes, installedApps) + } + } + catch (_: UninitializedPropertyAccessException) { + } + /* try { - val updatedApps = appUtils.getInstalledApps(this@MainActivity) + val updatedApps = appUtils.getInstalledApps(this@MainActivity, launcherApps) println("update running") - if (!listsEqual(installedApps, updatedApps)) { - withContext(Dispatchers.Main) { - updateMenu(updatedApps) - } - installedApps = updatedApps + withContext(Dispatchers.Main) { + updateMenu(updatedApps) } + installedApps = updatedApps } catch (_: UninitializedPropertyAccessException) { - } + }*/ } @@ -799,7 +802,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh searchView.textAlignment = View.TEXT_ALIGNMENT_VIEW_START } "center" -> { - searchView.textAlignment = View.TEXT_ALIGNMENT_CENTER + searchView.textAlignment = TEXT_ALIGNMENT_CENTER } "right" -> { searchView.textAlignment = View.TEXT_ALIGNMENT_VIEW_END @@ -902,4 +905,91 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh false } } + + fun detectChanges(oldList: List>>, newList: List>>): List { + val changes = mutableListOf() + val removalChanges = mutableListOf() + val oldSet = oldList.map { Pair(it.first.applicationInfo.packageName, it.second.second) }.toSet() + val newSet = newList.map { Pair(it.first.applicationInfo.packageName, it.second.second) }.toSet() + + // Detect removals + oldList.forEachIndexed { index, oldItem -> + if (!newSet.contains(Pair(oldItem.first.applicationInfo.packageName, oldItem.second.second))) { + removalChanges.add(Change(ChangeType.REMOVE, index)) + } + } + + // Detect insertions + newList.forEachIndexed { index, newItem -> + if (!oldSet.contains(Pair(newItem.first.applicationInfo.packageName, newItem.second.second))) { + changes.add(Change(ChangeType.INSERT, index)) + } + } + + oldList.forEachIndexed { index, oldItem -> + if (newSet.contains(Pair(oldItem.first.applicationInfo.packageName, oldItem.second.second))) { + val newIndex = newList.indexOfFirst { it.first.applicationInfo.packageName == oldItem.first.applicationInfo.packageName && it.second.second == oldItem.second.second } + if (oldItem.first.componentName != newList[newIndex].first.componentName) { + changes.add(Change(ChangeType.UPDATE, index)) + } + if (index != newIndex) { + changes.add(Change(ChangeType.MOVE, index)) + } + + } + } + + changes.addAll(removalChanges.reversed()) + + return changes + } + + @SuppressLint("NotifyDataSetChanged") + fun applyChanges(changes: List, updatedApps: List>>) { + changes.forEach { change -> + when (change.type) { + ChangeType.INSERT -> { + insertItem(change.position, updatedApps[change.position]) + } + ChangeType.REMOVE -> { + try { + removeItem(change.position) + } + catch (_: IndexOutOfBoundsException) { + } + } + ChangeType.UPDATE -> { + updateItem(change.position, updatedApps[change.position]) + } + + ChangeType.MOVE -> { + adapter?.updateApps(updatedApps) + adapter?.notifyDataSetChanged() + println("moved") + } + + } + } + } + + private fun insertItem(position: Int, app: Pair>) { + adapter?.addApp(position, app) + adapter?.notifyItemInserted(position) + } + private fun removeItem(position: Int) { + adapter?.removeApp(position) + adapter?.notifyItemRemoved(position) + } + + private fun updateItem(position: Int, app: Pair>) { + adapter?.updateApp(position, app) + adapter?.notifyItemChanged(position) + } + +} + +data class Change(val type: ChangeType, val position: Int, val newPosition: Int = 0) + +enum class ChangeType { + INSERT, REMOVE, UPDATE, MOVE } \ No newline at end of file diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index fe28ef3..e83b888 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -123,7 +123,7 @@