Added a long-press menu (info, delete, hide, rename). Rename not done. Others work.

This commit is contained in:
ottoptj 2024-05-07 07:38:34 +03:00
commit 71338fb9f1
10 changed files with 249 additions and 615 deletions

View file

@ -46,6 +46,7 @@ dependencies {
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0") implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
implementation("androidx.navigation:navigation-ui-ktx:2.6.0") implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
implementation("com.google.code.gson:gson:2.10")
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

View file

@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
<application <application
android:allowBackup="true" android:allowBackup="true"

View file

@ -1,34 +1,29 @@
package eu.ottop.yamlauncher package eu.ottop.yamlauncher
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.LauncherActivityInfo import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps import android.content.pm.LauncherApps
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.os.UserHandle import android.os.UserHandle
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.SearchView import android.widget.SearchView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import eu.ottop.yamlauncher.databinding.ActivityAppMenuBinding import eu.ottop.yamlauncher.databinding.ActivityAppMenuBinding
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.SortedMap
class AppMenuActivity : AppCompatActivity() { class AppMenuActivity : AppCompatActivity() {
@ -40,10 +35,11 @@ class AppMenuActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityAppMenuBinding.inflate(layoutInflater) binding = ActivityAppMenuBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setSupportActionBar(null) setSupportActionBar(null)
shownApps = listOf<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>() shownApps = listOf()
searchView = findViewById(R.id.searchView) searchView = findViewById(R.id.searchView)
container = findViewById(R.id.container) container = findViewById(R.id.container)
@ -62,7 +58,7 @@ class AppMenuActivity : AppCompatActivity() {
}) })
binding.root.addOnLayoutChangeListener { view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> binding.root.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
if (bottom - top > oldBottom - oldTop) { if (bottom - top > oldBottom - oldTop) {
searchView.clearFocus() searchView.clearFocus()
} }
@ -70,45 +66,6 @@ class AppMenuActivity : AppCompatActivity() {
} }
override fun onStop() {
super.onStop()
checkApps?.cancel()
}
override fun onStart() {
super.onStart()
startTask()
}
private fun startTask() {
checkApps = GlobalScope.launch {
while (true) {
if (!listsEqual(shownApps, getInstalledPersonalApps())) {
Log.d("AHAHHAHAA","AHJAHAHA")
shownApps = getInstalledPersonalApps()
runOnUiThread {
deleteAppMenuContents()
createPersonalAppMenu()
}
}
delay(1000)
}
}
}
private fun listsEqual(list1: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>, list2: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>): Boolean {
if (list1.size != list2.size) return false
for (i in list1.indices) {
if (list1[i].first.componentName != list2[i].first.componentName || list1[i].second.first != list2[i].second.first) {
return false
}
}
return true
}
private fun filterItems(query: String?) { private fun filterItems(query: String?) {
val cleanQuery = query?.replace("[^a-zA-Z0-9]".toRegex(), "") val cleanQuery = query?.replace("[^a-zA-Z0-9]".toRegex(), "")
@ -128,30 +85,101 @@ class AppMenuActivity : AppCompatActivity() {
} }
} }
override fun onStop() {
super.onStop()
checkApps?.cancel()
}
override fun onStart() {
super.onStart()
startTask()
}
private fun startTask() {
checkApps = GlobalScope.launch {
while (true) {
if (!listsEqual(shownApps, getInstalledApps())) {
shownApps = getInstalledApps()
runOnUiThread {
refreshAppMenu()
}
}
delay(1000)
}
}
}
private fun listsEqual(
list1: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>,
list2: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>
): Boolean {
if (list1.size != list2.size) return false
for (i in list1.indices) {
if (list1[i].first.componentName != list2[i].first.componentName || list1[i].second.first != list2[i].second.first) {
return false
}
}
return true
}
private fun refreshAppMenu() {
deleteAppMenuContents()
createAppMenu()
}
private fun deleteAppMenuContents(): Boolean {
binding.container.removeAllViewsInLayout()
return true
}
private fun createAppMenu(): Boolean {
val apps = getInstalledApps()
apps.forEach { appInfo ->
createAppText(appInfo.first, appInfo.second.first, appInfo.second.second)
}
return true
}
private fun getInstalledApps(): List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>> { private fun getInstalledApps(): List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>> {
val allApps = mutableListOf<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>() val allApps = mutableListOf<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>()
val launcherApps = this.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps val launcherApps = this.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
for (i in launcherApps.profiles.indices) { for (i in launcherApps.profiles.indices) {
launcherApps.getActivityList(null, launcherApps.profiles[i]).forEach { app -> launcherApps.getActivityList(null, launcherApps.profiles[i]).forEach { app ->
allApps.add(Pair(app, Pair(launcherApps.profiles[i], i))) if (!isAppHidden(app.activityInfo.applicationInfo.packageName, i)) {
allApps.add(Pair(app, Pair(launcherApps.profiles[i], i)))
}
} }
} }
return allApps.sortedBy { it.first.label.toString().lowercase() } return allApps.sortedBy {
it.first.applicationInfo.loadLabel(packageManager).toString().lowercase()
}
} }
private fun getInstalledPersonalApps(): List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>> { private fun createAppText(
return getInstalledApps() appInfo: LauncherActivityInfo,
} userHandle: UserHandle,
workProfile: Int
private fun getInstalledWorkApps(): List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>> { ): Boolean {
return getInstalledApps()
}
private fun createAppMenu(appInfo: LauncherActivityInfo, userHandle: UserHandle, workProfile: Int): Boolean {
val appInfo = appInfo.activityInfo.applicationInfo val appInfo = appInfo.activityInfo.applicationInfo
val textView = TextView(this)
val textView = TextView(this)
val launcherApps = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val mainActivity =
launcherApps.getActivityList(appInfo.packageName, userHandle).firstOrNull()
setupTextView(textView, appInfo, workProfile)
setupTextListeners(textView, appInfo, userHandle, workProfile, launcherApps, mainActivity)
binding.container.addView(textView)
return true
}
private fun setupTextView(textView: TextView, appInfo: ApplicationInfo, workProfile: Int) {
val states = arrayOf( val states = arrayOf(
intArrayOf(-android.R.attr.state_pressed), intArrayOf(-android.R.attr.state_pressed),
intArrayOf(android.R.attr.state_pressed) intArrayOf(android.R.attr.state_pressed)
@ -164,57 +192,136 @@ class AppMenuActivity : AppCompatActivity() {
with(textView) { with(textView) {
textSize = 28f textSize = 28f
setPadding(0,0,0,50) setPadding(0, 10, 0, 80)
isClickable = true isClickable = true
focusable = View.FOCUSABLE focusable = View.FOCUSABLE
gravity = Gravity.START gravity = Gravity.START
if (workProfile != 0) { text = if (workProfile != 0) {
text = "*"+appInfo.loadLabel(packageManager) "*" + appInfo.loadLabel(packageManager)
} } else {
else { appInfo.loadLabel(packageManager)
text = appInfo.loadLabel(packageManager)
} }
setTextColor(ColorStateList(states, colors)) setTextColor(ColorStateList(states, colors))
} }
}
private fun setupTextListeners(
textView: TextView,
appInfo: ApplicationInfo,
userHandle: UserHandle,
workProfile: Int,
launcherApps: LauncherApps,
mainActivity: LauncherActivityInfo?
) {
textView.setOnLongClickListener { textView.setOnLongClickListener {
Toast.makeText(this, "Long press detected on ${appInfo.loadLabel(packageManager)}", Toast.LENGTH_SHORT).show() appActionMenu(textView, appInfo, userHandle, workProfile, launcherApps, mainActivity)
true
} }
textView.setOnClickListener { textView.setOnClickListener {
val launcherApps = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val packageName = appInfo.packageName
val mainActivity = launcherApps.getActivityList(packageName, userHandle).firstOrNull()
if (mainActivity != null) { if (mainActivity != null) {
launcherApps.startMainActivity(mainActivity.componentName, userHandle, null, null) launcherApps.startMainActivity(mainActivity.componentName, userHandle, null, null)
} else { } else {
Toast.makeText(this, "Unable to launch ${appInfo.loadLabel(packageManager)}", Toast.LENGTH_SHORT).show() Toast.makeText(
this,
"Unable to launch ${appInfo.loadLabel(packageManager)}",
Toast.LENGTH_SHORT
).show()
}
}
}
private fun appActionMenu(
textView: TextView,
appInfo: ApplicationInfo,
userHandle: UserHandle,
workProfile: Int,
launcherApps: LauncherApps,
mainActivity: LauncherActivityInfo?
): Boolean {
val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val popupView = inflater.inflate(R.layout.app_action_menu, null)
val popupWindow = PopupWindow(
popupView,
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
popupWindow.isOutsideTouchable = true
popupWindow.isFocusable = true
popupWindow.animationStyle = android.R.style.Animation_Translucent
if (appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) {
popupView.findViewById<TextView>(R.id.uninstall).visibility = View.GONE
}
textView.visibility = View.INVISIBLE
popupWindow.showAsDropDown(textView, 0, -textView.height)
popupWindow.setOnDismissListener {
textView.visibility = View.VISIBLE
}
popupView.findViewById<TextView>(R.id.info).setOnClickListener {
if (mainActivity != null) {
launcherApps.startAppDetailsActivity(
mainActivity.componentName,
userHandle,
null,
null
)
} }
popupWindow.dismiss()
} }
// Add the button to the LinearLayout popupView.findViewById<TextView>(R.id.uninstall).setOnClickListener {
binding.container.addView(textView) val intent = Intent(Intent.ACTION_DELETE)
intent.data = Uri.parse("package:${appInfo.packageName}")
intent.putExtra(Intent.EXTRA_USER, userHandle)
startActivity(intent)
return true popupWindow.dismiss()
}
private fun createPersonalAppMenu(): Boolean {
val pApps = getInstalledPersonalApps()
pApps.forEach { appInfo ->
createAppMenu(appInfo.first, appInfo.second.first, appInfo.second.second)
} }
return true
popupView.findViewById<TextView>(R.id.rename).setOnClickListener {
// Handle rename action
popupWindow.dismiss()
}
popupView.findViewById<TextView>(R.id.hide).setOnClickListener {
setAppHidden(appInfo.packageName, workProfile, true)
refreshAppMenu()
popupWindow.dismiss()
}
return true // Indicate that the long click event is consumed}
} }
private fun deleteAppMenuContents(): Boolean { private fun setAppHidden(packageName: String, profile: Int, hidden: Boolean) {
binding.container.removeAllViewsInLayout() // Get the shared preferences editor
return true val editor = getSharedPreferences("app_data", MODE_PRIVATE).edit()
val key = "$packageName-${profile}"
editor.putBoolean(key, hidden)
editor.apply()
}
private fun isAppHidden(packageName: String, profile: Int): Boolean {
// Get the shared preferences object
val sharedPref = getSharedPreferences("app_data", MODE_PRIVATE)
val key = "$packageName-${profile}"
return sharedPref.getBoolean(key, false) // Default to false (visible)
}
private fun setAppVisible(packageName: String, profile: Int) {
// Get the shared preferences editor
val editor = getSharedPreferences("app_data", MODE_PRIVATE).edit()
val key = "$packageName-${profile}"
editor.remove(key)
editor.apply()
} }
} }

View file

@ -1,24 +1,9 @@
package eu.ottop.yamlauncher package eu.ottop.yamlauncher
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.View.FOCUSABLE
import android.view.View.OnFocusChangeListener
import android.widget.LinearLayout
import android.widget.SearchView
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import eu.ottop.yamlauncher.databinding.ActivityMainBinding import eu.ottop.yamlauncher.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {

View file

@ -1,241 +0,0 @@
package eu.ottop.yamlauncher
import android.content.Context
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.os.UserHandle
import android.util.Log
import android.view.Gravity
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.SearchView
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.getSystemService
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import eu.ottop.yamlauncher.databinding.ActivityAppMenuBinding
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [PersonalAppsFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class PersonalAppsFragment : Fragment() {
private lateinit var binding: ActivityAppMenuBinding
private lateinit var searchView: SearchView
private lateinit var container: LinearLayout
private lateinit var tabLayout: TabLayout
private var checkApps: Job? = null
private lateinit var shownApps: List<LauncherActivityInfo>
override fun onCreateView(
inflater: LayoutInflater, viewContainer: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_personal_apps, viewContainer, false)
// Initialize views from the fragment layout
container = view.findViewById(R.id.container)
searchView = requireActivity().findViewById(R.id.searchView)
shownApps = listOf<LauncherActivityInfo>()
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
searchView.clearFocus()
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
// Filter items based on the search query
filterItems(newText)
return true
}
})
return view
}
override fun onPause() {
super.onPause()
checkApps?.cancel()
searchView.setOnQueryTextListener(null)
}
override fun onStart() {
super.onStart()
startTask()
}
private fun filterItems(query: String?) {
val cleanQuery = query?.replace("[^a-zA-Z0-9]".toRegex(), "")
for (i in 0 until container.childCount) {
val view = container.getChildAt(i)
if (view is TextView) {
val itemText = view.text.toString()
val cleanItemText = itemText.replace("[^a-zA-Z0-9]".toRegex(), "")
if (cleanItemText.contains(cleanQuery ?: "", ignoreCase = true)) {
view.visibility = View.VISIBLE
} else {
view.visibility = View.GONE
}
}
}
}
private fun startTask() {
checkApps = GlobalScope.launch {
while (true) {
Log.d("APPSITUATION", "Ahahahaha")
if (!listsEqual(shownApps, getInstalledPersonalApps())) {
shownApps = getInstalledPersonalApps()
requireActivity().runOnUiThread {
deleteAppMenuContents()
createPersonalAppMenu()
}
}
delay(1000)
}
}
}
private fun listsEqual(
list1: List<LauncherActivityInfo>,
list2: List<LauncherActivityInfo>
): Boolean {
if (list1.size != list2.size) return false
for (i in list1.indices) {
if (list1[i].label != list2[i].label) {
return false
}
}
return true
}
private fun getInstalledApps(profile: Int): List<LauncherActivityInfo> {
val allApps = mutableListOf<LauncherActivityInfo>()
val launcherApps =
requireActivity().getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
allApps.addAll(launcherApps.getActivityList(null, launcherApps.profiles[profile]))
return allApps.sortedWith(compareBy {
it.applicationInfo.loadLabel(requireActivity().packageManager).toString().lowercase()
})
}
private fun getInstalledPersonalApps(): List<LauncherActivityInfo> {
return getInstalledApps(0)
}
private fun createAppMenu(
appInfo: LauncherActivityInfo,
userHandle: UserHandle,
workProfile: Boolean
): Boolean {
val appInfo = appInfo.activityInfo.applicationInfo
val textView = TextView(requireContext())
textView.textSize = 28f
textView.setPadding(0, 0, 0, 50)
val states = arrayOf(
intArrayOf(-android.R.attr.state_pressed),
intArrayOf(android.R.attr.state_pressed)
)
val colors = intArrayOf(
Color.parseColor("#f3f3f3"), // Default text color
Color.parseColor("#c3c3c3") // Text color when pressed
)
textView.setTextColor(ColorStateList(states, colors))
textView.isClickable = true
textView.focusable = View.FOCUSABLE
textView.gravity = Gravity.START
textView.text = appInfo.loadLabel(requireActivity().packageManager)
textView.setOnLongClickListener {
Toast.makeText(
requireContext(),
"Long press detected on ${appInfo.loadLabel(requireActivity().packageManager)}",
Toast.LENGTH_SHORT
).show()
true
}
textView.setOnClickListener {
val launcherApps =
requireActivity().getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val packageName = appInfo.packageName
val mainActivity = launcherApps.getActivityList(packageName, userHandle).firstOrNull()
if (mainActivity != null) {
launcherApps.startMainActivity(mainActivity.componentName, userHandle, null, null)
} else {
Toast.makeText(
requireContext(),
"Unable to launch ${appInfo.loadLabel(requireActivity().packageManager)}",
Toast.LENGTH_SHORT
).show()
}
}
// Add the button to the LinearLayout
container.addView(textView)
return true
}
private fun createPersonalAppMenu(): Boolean {
val pApps = getInstalledPersonalApps()
pApps.forEach { appInfo ->
createAppMenu(appInfo, pApps.first().user, false)
}
return true
}
private fun deleteAppMenuContents(): Boolean {
binding.container.removeAllViewsInLayout()
return true
}
fun setActivityReferences(binding: ActivityAppMenuBinding) {
this.binding = binding
}
companion object {
fun newInstance(binding: ActivityAppMenuBinding) = PersonalAppsFragment().apply {
this.binding = binding
}
}
}

View file

@ -1,237 +0,0 @@
package eu.ottop.yamlauncher
import android.content.Context
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.os.UserHandle
import android.util.Log
import android.view.Gravity
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.SearchView
import android.widget.TextView
import android.widget.Toast
import com.google.android.material.tabs.TabLayout
import eu.ottop.yamlauncher.databinding.ActivityAppMenuBinding
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [WorkAppsFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class WorkAppsFragment : Fragment() {
private lateinit var binding: ActivityAppMenuBinding
private lateinit var searchView: SearchView
private lateinit var container: LinearLayout
private lateinit var tabLayout: TabLayout
private var checkApps: Job? = null
private lateinit var shownApps: List<LauncherActivityInfo>
override fun onCreateView(
inflater: LayoutInflater, viewContainer: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_personal_apps, viewContainer, false)
// Initialize views from the fragment layout
container = view.findViewById(R.id.container)
searchView = requireActivity().findViewById(R.id.searchView)
shownApps = listOf<LauncherActivityInfo>()
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
searchView.clearFocus()
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
filterItems(newText)
return true
}
})
return view
}
override fun onPause() {
super.onPause()
checkApps?.cancel()
}
override fun onStart() {
super.onStart()
startTask()
}
private fun filterItems(query: String?) {
val cleanQuery = query?.replace("[^a-zA-Z0-9]".toRegex(), "")
for (i in 0 until container.childCount) {
val view = container.getChildAt(i)
if (view is TextView) {
val itemText = view.text.toString()
val cleanItemText = itemText.replace("[^a-zA-Z0-9]".toRegex(), "")
if (cleanItemText.contains(cleanQuery ?: "", ignoreCase = true)) {
view.visibility = View.VISIBLE
} else {
view.visibility = View.GONE
}
}
}
}
private fun startTask() {
checkApps = GlobalScope.launch {
while (true) {
Log.d("APPSITUATION1234", "Ahahahaha")
if (!listsEqual(shownApps, getInstalledWorkApps())) {
shownApps = getInstalledWorkApps()
requireActivity().runOnUiThread {
deleteAppMenuContents()
createWorkAppMenu()
}
}
delay(1000)
}
}
}
private fun listsEqual(
list1: List<LauncherActivityInfo>,
list2: List<LauncherActivityInfo>
): Boolean {
if (list1.size != list2.size) return false
for (i in list1.indices) {
if (list1[i].label != list2[i].label) {
return false
}
}
return true
}
private fun getInstalledApps(profile: Int): List<LauncherActivityInfo> {
val allApps = mutableListOf<LauncherActivityInfo>()
val launcherApps =
requireActivity().getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
allApps.addAll(launcherApps.getActivityList(null, launcherApps.profiles[profile]))
return allApps.sortedWith(compareBy {
it.applicationInfo.loadLabel(requireActivity().packageManager).toString().lowercase()
})
}
private fun getInstalledWorkApps(): List<LauncherActivityInfo> {
return getInstalledApps(1)
}
private fun createAppMenu(
appInfo: LauncherActivityInfo,
userHandle: UserHandle,
workProfile: Boolean
): Boolean {
val appInfo = appInfo.activityInfo.applicationInfo
val textView = TextView(requireContext())
textView.textSize = 28f
textView.setPadding(0, 0, 0, 50)
val states = arrayOf(
intArrayOf(-android.R.attr.state_pressed),
intArrayOf(android.R.attr.state_pressed)
)
val colors = intArrayOf(
Color.parseColor("#f3f3f3"), // Default text color
Color.parseColor("#c3c3c3") // Text color when pressed
)
textView.setTextColor(ColorStateList(states, colors))
textView.isClickable = true
textView.focusable = View.FOCUSABLE
textView.gravity = Gravity.START
textView.text = appInfo.loadLabel(requireActivity().packageManager)
textView.setOnLongClickListener {
Toast.makeText(
requireContext(),
"Long press detected on ${appInfo.loadLabel(requireActivity().packageManager)}",
Toast.LENGTH_SHORT
).show()
true
}
textView.setOnClickListener {
val launcherApps =
requireActivity().getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
val packageName = appInfo.packageName
val mainActivity = launcherApps.getActivityList(packageName, userHandle).firstOrNull()
if (mainActivity != null) {
launcherApps.startMainActivity(mainActivity.componentName, userHandle, null, null)
} else {
Toast.makeText(
requireContext(),
"Unable to launch ${appInfo.loadLabel(requireActivity().packageManager)}",
Toast.LENGTH_SHORT
).show()
}
}
// Add the button to the LinearLayout
container.addView(textView)
return true
}
private fun createWorkAppMenu(): Boolean {
val pApps = getInstalledWorkApps()
pApps.forEach { appInfo ->
createAppMenu(appInfo, pApps.first().user, false)
}
return true
}
private fun deleteAppMenuContents(): Boolean {
binding.container.removeAllViewsInLayout()
return true
}
fun setActivityReferences(binding: ActivityAppMenuBinding) {
this.binding = binding
}
companion object {
fun newInstance(binding: ActivityAppMenuBinding) = WorkAppsFragment().apply {
this.binding = binding
}
}
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Pressed state -->
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#0FFFFFFF"/> <!-- Change color to indicate pressed state -->
</shape>
</item>
<!-- Normal state -->
<item>
<shape android:shape="rectangle">
<solid android:color="#00FFFFFF"/> <!-- Change color to indicate normal state -->
</shape>
</item>
</selector>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#6F000000">
<TextView
android:id="@+id/info"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:foreground="@drawable/app_action_foreground"
android:drawableTop="@android:drawable/ic_menu_info_details"
android:text="Info"
android:textAlignment="center"
android:textColor="#F3F3F3FF" />
<TextView
android:id="@+id/uninstall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:foreground="@drawable/app_action_foreground"
android:drawableTop="@android:drawable/ic_menu_delete"
android:text="Uninstall"
android:textAlignment="center"
android:textColor="#F3F3F3FF" />
<TextView
android:id="@+id/rename"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:foreground="@drawable/app_action_foreground"
android:drawableTop="@android:drawable/ic_menu_edit"
android:text="Rename"
android:textAlignment="center"
android:textColor="#F3F3F3FF" />
<TextView
android:id="@+id/hide"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:foreground="@drawable/app_action_foreground"
android:drawableTop="@android:drawable/ic_menu_view"
android:text="Hide"
android:textAlignment="center"
android:textColor="#F3F3F3FF" />
</LinearLayout>

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:layout_marginHorizontal="10dp"
android:layout_marginTop="25dp"
android:layout_weight="0.9"
android:fadingEdge="none"
android:fillViewport="true"
android:requiresFadingEdge="vertical"
android:scrollbars="none">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fadingEdge="none"
android:orientation="vertical"
android:padding="16dp"/>
</ScrollView>

View file

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:layout_marginHorizontal="10dp"
android:layout_marginTop="25dp"
android:layout_weight="0.9"
android:fadingEdge="none"
android:fillViewport="true"
android:requiresFadingEdge="vertical"
android:scrollbars="none">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fadingEdge="none"
android:orientation="vertical"
android:padding="16dp">
<!-- Buttons dynamically added here -->
</LinearLayout>
</ScrollView>