Przeglądaj źródła

Update Unlauncher app data flow (#82)

Updates the data flow for the Unlauncher apps (that populate the data in the drawer) so that the installed apps are loaded into the Unlauncher data straight from the system data (and not piggybacking on the `AddAppViewModel`).
Joshua Kuestersteffen 4 lat temu
rodzic
commit
343d88f671

+ 37 - 2
app/src/main/java/com/sduduzog/slimlauncher/adapters/AppDrawerAdapter.kt

@@ -1,5 +1,8 @@
 package com.sduduzog.slimlauncher.adapters
 package com.sduduzog.slimlauncher.adapters
 
 
+import android.annotation.SuppressLint
+import android.text.Editable
+import android.text.TextWatcher
 import android.view.LayoutInflater
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup
@@ -16,18 +19,23 @@ class AppDrawerAdapter(
     lifecycleOwner: LifecycleOwner,
     lifecycleOwner: LifecycleOwner,
     appsRepo: UnlauncherAppsRepository
     appsRepo: UnlauncherAppsRepository
 ) : RecyclerView.Adapter<AppDrawerAdapter.ViewHolder>() {
 ) : RecyclerView.Adapter<AppDrawerAdapter.ViewHolder>() {
+    private val regex = Regex("[!@#\$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>/? ]")
+
     private var apps: List<UnlauncherApp> = listOf()
     private var apps: List<UnlauncherApp> = listOf()
+    private var filteredApps: List<UnlauncherApp> = listOf()
+    private var filterQuery = ""
 
 
     init {
     init {
         appsRepo.liveData().observe(lifecycleOwner, { unlauncherApps ->
         appsRepo.liveData().observe(lifecycleOwner, { unlauncherApps ->
             apps = unlauncherApps.appsList.filter { app -> app.displayInDrawer }.toList()
             apps = unlauncherApps.appsList.filter { app -> app.displayInDrawer }.toList()
+            updateDisplayedApps()
         })
         })
     }
     }
 
 
-    override fun getItemCount(): Int = apps.size
+    override fun getItemCount(): Int = filteredApps.size
 
 
     override fun onBindViewHolder(holder: ViewHolder, position: Int) {
     override fun onBindViewHolder(holder: ViewHolder, position: Int) {
-        val item = apps[position]
+        val item = filteredApps[position]
         holder.appName.text = item.displayName
         holder.appName.text = item.displayName
         holder.itemView.setOnClickListener {
         holder.itemView.setOnClickListener {
             listener.onAppClicked(item)
             listener.onAppClicked(item)
@@ -40,6 +48,33 @@ class AppDrawerAdapter(
         return ViewHolder(view)
         return ViewHolder(view)
     }
     }
 
 
+    fun setAppFilter(query: String = "") {
+        filterQuery = regex.replace(query, "")
+        this.updateDisplayedApps()
+    }
+
+    @SuppressLint("NotifyDataSetChanged")
+    private fun updateDisplayedApps() {
+        filteredApps = apps.filter { app ->
+            regex.replace(app.displayName, "").contains(filterQuery, ignoreCase = true)
+        }.toList()
+        notifyDataSetChanged()
+    }
+
+    val searchBoxListener: TextWatcher = object : TextWatcher {
+        override fun afterTextChanged(s: Editable?) {
+            // Do nothing
+        }
+
+        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
+            // Do nothing
+        }
+
+        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+            setAppFilter(s.toString())
+        }
+    }
+
     inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
     inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
 
 
         val appName: TextView = itemView.findViewById(R.id.aa_list_item_app_name)
         val appName: TextView = itemView.findViewById(R.id.aa_list_item_app_name)

+ 84 - 39
app/src/main/java/com/sduduzog/slimlauncher/datasource/apps/UnlauncherAppsRepository.kt

@@ -8,11 +8,10 @@ import androidx.lifecycle.asLiveData
 import com.jkuester.unlauncher.datastore.UnlauncherApp
 import com.jkuester.unlauncher.datastore.UnlauncherApp
 import com.jkuester.unlauncher.datastore.UnlauncherApps
 import com.jkuester.unlauncher.datastore.UnlauncherApps
 import com.sduduzog.slimlauncher.data.model.App
 import com.sduduzog.slimlauncher.data.model.App
+import com.sduduzog.slimlauncher.models.HomeApp
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
 import java.io.IOException
 import java.io.IOException
 
 
 class UnlauncherAppsRepository(
 class UnlauncherAppsRepository(
@@ -38,62 +37,108 @@ class UnlauncherAppsRepository(
         return unlauncherAppsFlow.asLiveData()
         return unlauncherAppsFlow.asLiveData()
     }
     }
 
 
-    fun setApps(apps: List<App>) {
-        lifecycleScope.launch {
-            unlauncherAppsStore.updateData { unlauncherApps ->
-                val unlauncherAppsBuilder = unlauncherApps.toBuilder()
-                // Add any new apps
-                apps.filter { app ->
-                    findApp(
-                        unlauncherApps,
-                        app.packageName,
-                        app.activityName
-                    ) == null
-                }.forEach { app ->
-                    val index =
-                        unlauncherAppsBuilder.appsList.indexOfFirst { unlauncherApp -> unlauncherApp.displayName > app.appName }
-                    unlauncherAppsBuilder.addApps(
-                        if (index >= 0) index else unlauncherAppsBuilder.appsList.size,
-                        UnlauncherApp.newBuilder().setPackageName(app.packageName)
-                            .setClassName(app.activityName).setUserSerial(app.userSerial)
-                            .setDisplayName(app.appName).setDisplayInDrawer(true)
+    suspend fun setApps(apps: List<App>) {
+        unlauncherAppsStore.updateData { unlauncherApps ->
+            val unlauncherAppsBuilder = unlauncherApps.toBuilder()
+            // Add any new apps
+            apps.filter { app ->
+                findApp(
+                    unlauncherAppsBuilder.appsList,
+                    app.packageName,
+                    app.activityName
+                ) == null
+            }.forEach { app ->
+                val index =
+                    unlauncherAppsBuilder.appsList.indexOfFirst { unlauncherApp -> unlauncherApp.displayName > app.appName }
+                unlauncherAppsBuilder.addApps(
+                    if (index >= 0) index else unlauncherAppsBuilder.appsList.size,
+                    UnlauncherApp.newBuilder().setPackageName(app.packageName)
+                        .setClassName(app.activityName).setUserSerial(app.userSerial)
+                        .setDisplayName(app.appName).setDisplayInDrawer(true)
+                )
+            }
+            // Remove any apps that no longer exist
+            unlauncherApps.appsList.filter { unlauncherApp ->
+                apps.find { app ->
+                    unlauncherApp.packageName == app.packageName && unlauncherApp.className == app.activityName
+                } == null
+            }.forEach { unlauncherApp ->
+                unlauncherAppsBuilder.removeApps(
+                    unlauncherAppsBuilder.appsList.indexOf(
+                        unlauncherApp
                     )
                     )
+                )
+            }
+
+            unlauncherAppsBuilder.build()
+        }
+    }
+
+    suspend fun setHomeApps(apps: List<HomeApp>) {
+        unlauncherAppsStore.updateData { unlauncherApps ->
+            val unlauncherAppsBuilder = unlauncherApps.toBuilder()
+            val unlauncherHomeApps = mutableListOf<UnlauncherApp>()
+
+            // Set home apps
+            apps.forEach { homeApp ->
+                findApp(
+                    unlauncherAppsBuilder.appsList,
+                    homeApp.packageName,
+                    homeApp.activityName
+                )?.let { unlauncherApp ->
+                    if (!unlauncherApp.homeApp) {
+                        val index = unlauncherAppsBuilder.appsList.indexOf(unlauncherApp)
+                        if (index >= 0) {
+                            unlauncherAppsBuilder.setApps(
+                                index,
+                                unlauncherApp.toBuilder().setHomeApp(true)
+                                    .setDisplayInDrawer(false)
+                                    .build()
+                            )
+                        }
+                    }
+                    unlauncherHomeApps.add(unlauncherApp)
                 }
                 }
-                // Remove any apps that no longer exist
-                unlauncherApps.appsList.filter { unlauncherApp ->
-                    apps.find { app ->
-                        unlauncherApp.packageName == app.packageName && unlauncherApp.className == app.activityName
-                    } == null
-                }.forEach { unlauncherApp ->
-                    unlauncherAppsBuilder.removeApps(
-                        unlauncherAppsBuilder.appsList.indexOf(
-                            unlauncherApp
+            }
+
+            // Clear out old home apps
+            unlauncherAppsBuilder.appsList
+                .filter { findApp(unlauncherHomeApps, it.packageName, it.className) == null }
+                .filter { it.homeApp }
+                .forEach { unlauncherApp ->
+                    val index = unlauncherAppsBuilder.appsList.indexOf(unlauncherApp)
+                    if (index >= 0) {
+                        unlauncherAppsBuilder.setApps(
+                            index,
+                            unlauncherApp.toBuilder().setHomeApp(false).setDisplayInDrawer(true)
+                                .build()
                         )
                         )
-                    )
+                    }
                 }
                 }
 
 
-                unlauncherAppsBuilder.build()
-            }
+            unlauncherAppsBuilder.build()
         }
         }
     }
     }
 
 
     fun updateDisplayInDrawer(appToUpdate: UnlauncherApp, displayInDrawer: Boolean) {
     fun updateDisplayInDrawer(appToUpdate: UnlauncherApp, displayInDrawer: Boolean) {
         lifecycleScope.launch {
         lifecycleScope.launch {
             unlauncherAppsStore.updateData { currentApps ->
             unlauncherAppsStore.updateData { currentApps ->
-                currentApps.toBuilder().setApps(
-                    currentApps.appsList.indexOf(appToUpdate),
-                    appToUpdate.toBuilder().setDisplayInDrawer(displayInDrawer)
-                ).build()
+                val builder = currentApps.toBuilder()
+                val index = builder.appsList.indexOf(appToUpdate)
+                if (index >= 0) {
+                    builder.setApps(index, appToUpdate.toBuilder().setDisplayInDrawer(displayInDrawer))
+                }
+                builder.build()
             }
             }
         }
         }
     }
     }
 
 
     private fun findApp(
     private fun findApp(
-        unlauncherApps: UnlauncherApps,
+        unlauncherApps: List<UnlauncherApp>,
         packageName: String,
         packageName: String,
         className: String
         className: String
     ): UnlauncherApp? {
     ): UnlauncherApp? {
-        return unlauncherApps.appsList.firstOrNull { app ->
+        return unlauncherApps.firstOrNull { app ->
             packageName == app.packageName && className == app.className
             packageName == app.packageName && className == app.className
         }
         }
     }
     }

+ 43 - 48
app/src/main/java/com/sduduzog/slimlauncher/ui/main/HomeFragment.kt

@@ -8,8 +8,6 @@ import android.os.UserManager
 import android.provider.AlarmClock
 import android.provider.AlarmClock
 import android.provider.CalendarContract
 import android.provider.CalendarContract
 import android.provider.MediaStore
 import android.provider.MediaStore
-import android.text.Editable
-import android.text.TextWatcher
 import android.view.LayoutInflater
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup
@@ -17,6 +15,7 @@ import android.view.inputmethod.InputMethodManager
 import androidx.constraintlayout.motion.widget.MotionLayout
 import androidx.constraintlayout.motion.widget.MotionLayout
 import androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener
 import androidx.constraintlayout.motion.widget.MotionLayout.TransitionListener
 import androidx.lifecycle.Observer
 import androidx.lifecycle.Observer
+import androidx.lifecycle.lifecycleScope
 import androidx.navigation.Navigation
 import androidx.navigation.Navigation
 import com.jkuester.unlauncher.datastore.UnlauncherApp
 import com.jkuester.unlauncher.datastore.UnlauncherApp
 import com.sduduzog.slimlauncher.R
 import com.sduduzog.slimlauncher.R
@@ -28,6 +27,7 @@ import com.sduduzog.slimlauncher.utils.BaseFragment
 import com.sduduzog.slimlauncher.utils.OnLaunchAppListener
 import com.sduduzog.slimlauncher.utils.OnLaunchAppListener
 import dagger.hilt.android.AndroidEntryPoint
 import dagger.hilt.android.AndroidEntryPoint
 import kotlinx.android.synthetic.main.home_fragment.*
 import kotlinx.android.synthetic.main.home_fragment.*
+import kotlinx.coroutines.launch
 import java.text.DateFormat
 import java.text.DateFormat
 import java.text.SimpleDateFormat
 import java.text.SimpleDateFormat
 import java.util.*
 import java.util.*
@@ -36,6 +36,7 @@ import java.util.*
 class HomeFragment(private val viewModel: MainViewModel) : BaseFragment(), OnLaunchAppListener {
 class HomeFragment(private val viewModel: MainViewModel) : BaseFragment(), OnLaunchAppListener {
 
 
     private lateinit var receiver: BroadcastReceiver
     private lateinit var receiver: BroadcastReceiver
+    private lateinit var appDrawerAdapter: AppDrawerAdapter
 
 
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                               savedInstanceState: Bundle?): View {
                               savedInstanceState: Bundle?): View {
@@ -49,6 +50,8 @@ class HomeFragment(private val viewModel: MainViewModel) : BaseFragment(), OnLau
         home_fragment_list.adapter = adapter1
         home_fragment_list.adapter = adapter1
         home_fragment_list_exp.adapter = adapter2
         home_fragment_list_exp.adapter = adapter2
 
 
+        val unlauncherAppsRepo = getUnlauncherDataSource().unlauncherAppsRepo
+
         viewModel.apps.observe(viewLifecycleOwner, Observer { list ->
         viewModel.apps.observe(viewLifecycleOwner, Observer { list ->
             list?.let { apps ->
             list?.let { apps ->
                 adapter1.setItems(apps.filter {
                 adapter1.setItems(apps.filter {
@@ -57,40 +60,20 @@ class HomeFragment(private val viewModel: MainViewModel) : BaseFragment(), OnLau
                 adapter2.setItems(apps.filter {
                 adapter2.setItems(apps.filter {
                     it.sortingIndex >= 3
                     it.sortingIndex >= 3
                 })
                 })
-            }
-        })
-
-        setEventListeners()
 
 
-        val unlauncherAppRepo = getUnlauncherDataSource().unlauncherAppsRepo;
-        app_drawer_fragment_list.adapter =
-            AppDrawerAdapter(AppDrawerListener(), viewLifecycleOwner, unlauncherAppRepo)
-        // Send the apps to Unlauncher data source
-        viewModel.addAppViewModel.apps.observe(viewLifecycleOwner, Observer {
-            it?.let { apps -> unlauncherAppRepo.setApps(apps) }
-        })
-        home_fragment.setTransitionListener(object : TransitionListener {
-            override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
-                // hide the keyboard and remove focus from the EditText when swiping back up
-                if (currentId == motionLayout?.startState) {
-                    resetAppDrawerEditText()
-                    val inputMethodManager = requireContext().getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
-                    inputMethodManager.hideSoftInputFromWindow(requireView().windowToken, 0)
+                // Set the home apps in the Unlauncher data
+                lifecycleScope.launch {
+                    unlauncherAppsRepo.setHomeApps(apps)
                 }
                 }
             }
             }
+        })
 
 
-            override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) {
-                // do nothing
-            }
+        appDrawerAdapter =
+            AppDrawerAdapter(AppDrawerListener(), viewLifecycleOwner, unlauncherAppsRepo)
 
 
-            override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) {
-                // do nothing
-            }
+        setEventListeners()
 
 
-            override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) {
-                // do nothing
-            }
-        })
+        app_drawer_fragment_list.adapter = appDrawerAdapter
     }
     }
 
 
     override fun onStart() {
     override fun onStart() {
@@ -105,8 +88,12 @@ class HomeFragment(private val viewModel: MainViewModel) : BaseFragment(), OnLau
         super.onResume()
         super.onResume()
         updateClock()
         updateClock()
 
 
-        viewModel.addAppViewModel.setInstalledApps(getInstalledApps())
-        viewModel.addAppViewModel.filterApps("")
+        lifecycleScope.launch {
+            getUnlauncherDataSource().unlauncherAppsRepo.setApps(getInstalledApps())
+        }
+        if (!::appDrawerAdapter.isInitialized) {
+            appDrawerAdapter.setAppFilter()
+        }
     }
     }
 
 
     override fun onStop() {
     override fun onStop() {
@@ -183,7 +170,30 @@ class HomeFragment(private val viewModel: MainViewModel) : BaseFragment(), OnLau
                 }
                 }
             })
             })
 
 
-        app_drawer_edit_text.addTextChangedListener(onTextChangeListener)
+        app_drawer_edit_text.addTextChangedListener(appDrawerAdapter.searchBoxListener)
+
+        home_fragment.setTransitionListener(object : TransitionListener {
+            override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
+                // hide the keyboard and remove focus from the EditText when swiping back up
+                if (currentId == motionLayout?.startState) {
+                    resetAppDrawerEditText()
+                    val inputMethodManager = requireContext().getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
+                    inputMethodManager.hideSoftInputFromWindow(requireView().windowToken, 0)
+                }
+            }
+
+            override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) {
+                // do nothing
+            }
+
+            override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) {
+                // do nothing
+            }
+
+            override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) {
+                // do nothing
+            }
+        })
     }
     }
 
 
     fun updateClock() {
     fun updateClock() {
@@ -243,21 +253,6 @@ class HomeFragment(private val viewModel: MainViewModel) : BaseFragment(), OnLau
         app_drawer_edit_text.clearFocus()
         app_drawer_edit_text.clearFocus()
     }
     }
 
 
-    private val onTextChangeListener: TextWatcher = object : TextWatcher {
-
-        override fun afterTextChanged(s: Editable?) {
-            // Do nothing
-        }
-
-        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
-            // Do nothing
-        }
-
-        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
-            viewModel.addAppViewModel.filterApps(s.toString())
-        }
-    }
-
     inner class AppDrawerListener {
     inner class AppDrawerListener {
         fun onAppClicked(app: UnlauncherApp) {
         fun onAppClicked(app: UnlauncherApp) {
             launchApp(app.packageName, app.className, app.userSerial)
             launchApp(app.packageName, app.className, app.userSerial)

+ 1 - 0
app/src/main/proto/unlauncher_apps.proto

@@ -9,6 +9,7 @@ message UnlauncherApp {
   int64 user_serial = 3;
   int64 user_serial = 3;
   string display_name = 4;
   string display_name = 4;
   bool display_in_drawer = 5;
   bool display_in_drawer = 5;
+  bool home_app = 6;
 }
 }
 
 
 message UnlauncherApps {
 message UnlauncherApps {