App grouping is not working when I place a app on top of another app in my android launcher

21 Views Asked by At

I am working on a app launcher and I am trying to achieve is when you drag a app ontop of another those 2 app icons are removed from the home screen and a folder icon show up instead when you open the folder the 2 app that were on the home screen are there. now the drag and drop part is working but the grouping of said icons into a folder I listed above is not working

here is my code for my home screen: package org.cpsk12.cpshome

import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
import android.preference.PreferenceManager
import android.provider.Settings
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.LinearLayout
import org.cpsk12.cpshome.HomeActivity.Companion.appsAdapter
import org.cpsk12.cpshome.databinding.ActivityAppsListBinding

class MyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        when (intent!!.action) {
            "android.intent.action.PACKAGE_ADDED", "android.intent.action.PACKAGE_REMOVED" -> {
                Log.d(javaClass.name, "Received")
                appsAdapter.resetFilter()
                appsAdapter.notifyDataSetChanged()
            }
        }
    }
}

class HomeActivity : Activity() {

    private lateinit var binding: ActivityAppsListBinding
    private lateinit var searchBox: EditText
    private lateinit var searchLayout: LinearLayout
    private var isSearchActive = false

    companion object {
        lateinit var appsAdapter: FolderAppsAdapter
    }

    private val isPortrait
        get() = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT

    private val myReceiver = MyReceiver()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityAppsListBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val appDetails = AppRepository(this, getString(R.string.app_name)).findAll()

        appsAdapter = FolderAppsAdapter(this, AppRepository(this, getString(R.string.app_name)))

        binding.appsList.adapter = appsAdapter
        binding.appsList.setOnItemClickListener { _, _, position, _ -> startActivity(appsAdapter.getItem(position)?.intent) }
        binding.appsList.setOnItemLongClickListener { _, _, position, _ ->
            startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(Uri.fromParts("package", appsAdapter.getItem(position)?.packageName, null)))
            return@setOnItemLongClickListener true
        }

        searchBox = findViewById(R.id.searchBox)
        searchLayout = findViewById(R.id.searchLayout)

        searchBox.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

            override fun afterTextChanged(s: Editable?) {
                if (s.isNullOrEmpty()) {
                    isSearchActive = false
                    appsAdapter.resetFilter()
                } else {
                    isSearchActive = true
                    appsAdapter.filter.filter(s.toString())
                }
            }
        })

        searchBox.setOnClickListener {
            val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.showSoftInput(searchBox, InputMethodManager.SHOW_IMPLICIT)
        }

        registerReceiver(myReceiver, IntentFilter().apply {
            addAction("android.intent.action.PACKAGE_ADDED")
            addAction("android.intent.action.PACKAGE_REMOVED")
        })
    }

    override fun onResume() {
        super.onResume()
        if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("needsReload", false)) {
            PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("needsReload", false).apply()
            recreate()
        }
    }

    override fun onBackPressed() {}

    override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        val view = currentFocus
        val ret = super.dispatchTouchEvent(event)

        if (view is EditText) {
            val w = currentFocus
            val scrcoords = IntArray(2)
            w?.getLocationOnScreen(scrcoords)
            val x = event.rawX + w!!.left - scrcoords[0]
            val y = event.rawY + w.top - scrcoords[1]

            if (event.action == MotionEvent.ACTION_UP && (x < 0 || x >= w.width || y < 0 || y >= w.height)) {
                val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                imm.hideSoftInputFromWindow(window.decorView.windowToken, 0)
                searchLayout.requestFocus()
            }
        }
        return ret
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(myReceiver)
    }
}

here is my FolderAppsAdapter code

package org.cpsk12.cpshome

import android.content.ClipData
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.DragEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.Filter
import android.widget.Filterable
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import java.util.*

open class FolderAppsAdapter(private val context: Context, private val appRepository: AppRepository) : BaseAdapter(), Filterable {

    private val appsByFolder: MutableMap<String, MutableList<App>> = mutableMapOf()

    init {
        updateAppsByFolder()
    }

    private fun updateAppsByFolder() {
        appRepository.getFolders().forEach { folder ->
            val appsInFolder = appRepository.findAll().filter { it.folderName == folder }.toMutableList()
            appsByFolder[folder] = appsInFolder
        }
    }

    var filteredApps: List<App> = appRepository.findAll()

    fun resetFilter() {
        filteredApps = appRepository.findAll()
        notifyDataSetChanged()
    }

    override fun getCount(): Int {
        return filteredApps.size
    }

    override fun getItem(position: Int): App? {
        return filteredApps.getOrNull(position)
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        var convertView = convertView
        val app = getItem(position)
        val holder: ViewHolder

        if (convertView != null) {
            holder = convertView.tag as ViewHolder
        } else {
            convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false)
            holder = ViewHolder(
                appIcon = convertView!!.findViewById(R.id.item_app_icon) as ImageView,
                appLabel = convertView.findViewById(R.id.item_app_label) as TextView
            )
            convertView.tag = holder
        }

        app?.let {
            holder.appIcon!!.setImageDrawable(it.icon)
            holder.appLabel!!.text = it.name

            // Handle click for the app
            convertView?.setOnClickListener { view ->
                // Implement your logic for clicking on an app
                // You can launch the app or perform any other action
                val clickedApp = getItem(position)
                // Example: Launch the app
                clickedApp?.intent?.let { intent ->
                    context.startActivity(intent)
                }
            }
        }

        // Handle drag-and-drop for the folder
        convertView?.setOnDragListener { _, event ->
            when (event.action) {
                DragEvent.ACTION_DROP -> {
                    val draggedItem = event.localState as App
                    val folder = appRepository.getFolders().find { folderName ->
                        event.y > convertView.top && event.y < convertView.bottom
                    }
                    folder?.let {
                        // Update the folder for the dragged item
                        draggedItem.folderName = folder

                        // Check if the folder already exists
                        if (!appsByFolder.containsKey(folder)) {
                            appsByFolder[folder] = mutableListOf(draggedItem)
                        } else {
                            appsByFolder[folder]?.add(draggedItem)
                        }

                        // Update the adapter and refresh the view
                        updateAppsByFolder()
                        notifyDataSetChanged()
                    }
                }
            }
            true
        }

        // Enable dragging
        convertView?.setOnLongClickListener {
            val clipData = ClipData.newPlainText("", "")
            val shadowBuilder = View.DragShadowBuilder(it)
            it.startDrag(clipData, shadowBuilder, app, 0)
            true
        }

        return convertView!!
    }

    override fun getFilter(): Filter {
        return object : Filter() {
            override fun performFiltering(constraint: CharSequence?): FilterResults {
                val filterResults = FilterResults()
                val filteredList = mutableListOf<App>()

                if (constraint.isNullOrBlank()) {
                    filteredList.addAll(appRepository.findAll())
                } else {
                    val filterPattern = constraint.toString().toLowerCase(Locale.getDefault()).trim()
                    filteredList.addAll(appRepository.findAll().filter {
                        it.name.toLowerCase(Locale.getDefault()).contains(filterPattern)
                    })
                }

                filterResults.values = filteredList
                filterResults.count = filteredList.size
                return filterResults
            }

            override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
                if (results != null) {
                    filteredApps = results.values as List<App>
                    notifyDataSetChanged()
                }
            }
        }
    }

    fun updateFolders() {
        updateAppsByFolder()
        notifyDataSetChanged()
    }

    private data class ViewHolder(
        internal val appIcon: ImageView? = null,
        internal val appLabel: TextView? = null
    )
}
the app class:

package org.cpsk12.cpshome

import android.content.Intent
import android.graphics.drawable.Drawable

data class App(
    val name: String,
    val packageName: String,
    var folderName: String,
    val icon: Drawable?,
    val intent: Intent? = null,
    var position: Int = -1
)
 

and the AppRepository

package org.cpsk12.cpshome

import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.Context
import android.content.Intent

class AppRepository(private val context: Context, private val appName: String) {

    private val packageManager: PackageManager = context.packageManager
    private val applicationInfo: ApplicationInfo = context.applicationInfo

    // Example settings app
    private val settingsApp = App(
        name = "$appName settings",
        packageName = context.packageName,
        folderName = "Uncategorized",
        icon = packageManager.getApplicationIcon(applicationInfo),
        intent = null
    )

    // Add folder-related methods
    private val folders = mutableSetOf<String>()

    fun addFolder(folderName: String) {
        folders.add(folderName)
    }

    fun getFolders(): Set<String> {
        return folders
    }

    fun findAll(): List<App> {
        val i = Intent(Intent.ACTION_MAIN, null).addCategory(Intent.CATEGORY_LAUNCHER)

        return packageManager.queryIntentActivities(i, 0).map {
            App(
                name = it.loadLabel(packageManager).toString(),
                packageName = it.activityInfo.packageName,
                folderName = "Uncategorized", // Default folder
                icon = it.activityInfo.loadIcon(packageManager),
                intent = loadIntent(it.activityInfo, packageManager)
            )
        }.filterNot {
            it.packageName in setOf(
                "com.android.vending",
                "com.android.settings",
                "org.cpsk12.cpshome",
                "com.android.mms",
                "com.android.contacts",
                "com.android.dialer",
                "com.android.chrome",
                "com.google.android.dialer",
                "com.google.android.apps.messaging"
            )
        }.plus(settingsApp).sortedBy { it.name }
    }

    private fun loadIntent(activityInfo: ActivityInfo, packageManager: PackageManager): Intent? {
        val launchIntent = packageManager.getLaunchIntentForPackage(activityInfo.packageName)
        return launchIntent?.let {
            it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            Intent(it)
        }
    }
}

any help would be absolutely incredible!!!!

0

There are 0 best solutions below