From 5cdd7bca2e8bfd120d2c09ad7c147eae35077f96 Mon Sep 17 00:00:00 2001 From: ottoptj Date: Tue, 28 May 2024 21:02:58 +0300 Subject: [PATCH] Fixed returning from app menu and refactoring --- .../eu/ottop/yamlauncher/AppActionMenu.kt | 37 +- .../eu/ottop/yamlauncher/AppMenuActivity.kt | 349 ------------------ .../ottop/yamlauncher/AppMenuEdgeFactory.kt | 32 +- .../yamlauncher/AppMenuLinearLayoutManager.kt | 31 ++ .../java/eu/ottop/yamlauncher/MainActivity.kt | 213 ++++++----- .../yamlauncher/SharedPreferenceManager.kt | 4 - app/src/main/res/layout/activity_main.xml | 31 +- app/src/main/res/values/strings.xml | 4 + 8 files changed, 184 insertions(+), 517 deletions(-) delete mode 100644 app/src/main/java/eu/ottop/yamlauncher/AppMenuActivity.kt create mode 100644 app/src/main/java/eu/ottop/yamlauncher/AppMenuLinearLayoutManager.kt diff --git a/app/src/main/java/eu/ottop/yamlauncher/AppActionMenu.kt b/app/src/main/java/eu/ottop/yamlauncher/AppActionMenu.kt index 0be33fe..0ed304b 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/AppActionMenu.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/AppActionMenu.kt @@ -16,10 +16,11 @@ import android.widget.EditText import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.widget.AppCompatButton -import eu.ottop.yamlauncher.databinding.ActivityAppMenuBinding import eu.ottop.yamlauncher.databinding.ActivityMainBinding import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class AppActionMenu { @@ -28,7 +29,6 @@ class AppActionMenu { fun setActionListeners( activity: MainActivity, - uiScope: CoroutineScope, binding: ActivityMainBinding, textView: TextView, editLayout: LinearLayout, @@ -106,13 +106,14 @@ class AppActionMenu { workProfile, editText.text.toString() ) - - val newPosition = appUtils.getInstalledApps(activity) - .indexOfFirst { it.first.applicationInfo.packageName == appInfo.packageName && it.second.second == workProfile } - - activity.updateItem(position, app) - activity.moveItem(position, newPosition) - + CoroutineScope(Dispatchers.Default).launch { + val newPosition = appUtils.getInstalledApps(activity) + .indexOfFirst { it.first.applicationInfo.packageName == appInfo.packageName && it.second.second == workProfile } + withContext(Dispatchers.Main) { + activity.updateItem(position, app) + activity.moveItem(position, newPosition) + } + } return@setOnEditorActionListener true @@ -129,11 +130,15 @@ class AppActionMenu { app.first.applicationInfo.packageName, app.second.second ) - val newPosition = appUtils.getInstalledApps(activity) - .indexOfFirst { it.first.applicationInfo.packageName == appInfo.packageName && it.second.second == workProfile } - activity.updateItem(position, app) - activity.moveItem(position, newPosition) + CoroutineScope(Dispatchers.Default).launch { + val newPosition = appUtils.getInstalledApps(activity) + .indexOfFirst { it.first.applicationInfo.packageName == appInfo.packageName && it.second.second == workProfile } + withContext(Dispatchers.Main) { + activity.updateItem(position, app) + activity.moveItem(position, newPosition) + } + } } } @@ -141,8 +146,10 @@ class AppActionMenu { editLayout.visibility = View.GONE textView.visibility = View.GONE actionMenu.visibility = View.GONE - sharedPreferenceManager.setAppHidden(activity, appInfo.packageName, workProfile, true) - activity.manualRefresh() + CoroutineScope(Dispatchers.Default).launch { + sharedPreferenceManager.setAppHidden(activity, appInfo.packageName, workProfile, true) + activity.refreshAppMenu() + } } actionMenu.findViewById(R.id.close).setOnClickListener { diff --git a/app/src/main/java/eu/ottop/yamlauncher/AppMenuActivity.kt b/app/src/main/java/eu/ottop/yamlauncher/AppMenuActivity.kt deleted file mode 100644 index b586e05..0000000 --- a/app/src/main/java/eu/ottop/yamlauncher/AppMenuActivity.kt +++ /dev/null @@ -1,349 +0,0 @@ -package eu.ottop.yamlauncher - -import android.content.Context -import android.content.Intent -import android.content.pm.LauncherActivityInfo -import android.content.pm.LauncherApps -import android.os.Bundle -import android.os.UserHandle -import android.text.Editable -import android.text.TextWatcher -import android.util.Log -import android.view.View -import android.view.inputmethod.InputMethodManager -import android.widget.EditText -import android.widget.LinearLayout -import android.widget.TextView -import android.widget.Toast -import androidx.activity.OnBackPressedCallback -import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.RecyclerView -import eu.ottop.yamlauncher.databinding.ActivityAppMenuBinding -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - - -class AppMenuActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, AppMenuAdapter.OnShortcutListener, AppMenuAdapter.OnItemLongClickListener { - /* - private lateinit var binding: ActivityAppMenuBinding - private lateinit var recyclerView: RecyclerView - private lateinit var searchView: EditText - private lateinit var adapter: AppMenuAdapter - private var job: Job? = null - private var appActionMenu = AppActionMenu() - private lateinit var launcherApps: LauncherApps - private lateinit var installedApps: List>> - - private val sharedPreferenceManager = SharedPreferenceManager() - private val appUtils = AppUtils() - - private lateinit var menuMode: String - - companion object { - private lateinit var callback: (Pair, Pair>) -> Unit - private const val MENU_MODE = "app" - private lateinit var currentApps: List>> - fun start(context: Context, currentApps: List>>, param1: String = "app", callback: (Pair, Pair>) -> Unit) { - val intent = Intent(context, AppMenuActivity::class.java).apply { - putExtra(MENU_MODE, param1) - } - context.startActivity(intent) - - this.callback = callback - this.currentApps = currentApps - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - menuMode = intent.getStringExtra(MENU_MODE) ?: "app" - - binding = ActivityAppMenuBinding.inflate(layoutInflater) - setContentView(binding.root) - setSupportActionBar(null) - if (menuMode == "shortcut") { - binding.menutitle.visibility = View.VISIBLE - } - - launcherApps = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps - searchView = findViewById(R.id.searchView) - - recyclerView = findViewById(R.id.recycler_view) - recyclerView.scrollToPosition(0) - installedApps = currentApps - val newApps = mutableListOf>>() - newApps.addAll(installedApps) - adapter = AppMenuAdapter(this@AppMenuActivity, newApps, this, this,this, menuMode) - recyclerView.adapter = adapter - - setupSearch() - - onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - finish() - - } - }) - } - - override fun onItemClick(appInfo: LauncherActivityInfo, userHandle: UserHandle) { - val mainActivity = launcherApps.getActivityList(appInfo.applicationInfo.packageName, userHandle).firstOrNull() - if (mainActivity != null) { - launcherApps.startMainActivity(mainActivity.componentName, userHandle, null, null) - } else { - Toast.makeText(this, "Cannot launch app", Toast.LENGTH_SHORT).show() - } - } - - override fun onShortcut(appInfo: LauncherActivityInfo, userHandle: UserHandle, textView: TextView, userProfile: Int) { - callback.invoke(Pair(Pair(textView.text.toString(), userProfile), Pair(appInfo, userHandle))) - finish() - } - - override fun onItemLongClick( - appInfo: LauncherActivityInfo, - userHandle: UserHandle, - userProfile: Int, - textView: TextView, - actionMenuLayout: LinearLayout, - editView: LinearLayout, - position: Int - ) { - textView.visibility = View.INVISIBLE - actionMenuLayout.visibility = View.VISIBLE - val mainActivity = - launcherApps.getActivityList(appInfo.applicationInfo.packageName, userHandle) - .firstOrNull() - appActionMenu.setActionListeners( - this@AppMenuActivity, - CoroutineScope(Dispatchers.Main), - binding, - textView, - editView, - actionMenuLayout, - searchView, - appInfo.applicationInfo, - userHandle, - userProfile, - launcherApps, - mainActivity, - position - ) - } - - private fun setupSearch() { - binding.root.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom -> - if (bottom - top > oldBottom - oldTop) { - searchView.clearFocus() - if (searchView.text.isNullOrEmpty()) { - job?.cancel() - startTask() - } - } - else { - job?.cancel() - } - } - - searchView.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) { - filterItems(searchView.text.toString()) - } - - override fun afterTextChanged(s: Editable?) { - } - - }) - } - - private fun filterItems(query: String?) { - CoroutineScope(Dispatchers.Default).launch { - val cleanQuery = query?.clean() - val newFilteredApps = mutableListOf>>() - val updatedApps = appUtils.getInstalledApps(this@AppMenuActivity) - - if (cleanQuery.isNullOrEmpty()) { - manualRefresh() - newFilteredApps.addAll(installedApps) - } else { - updatedApps.forEach { - val cleanItemText = sharedPreferenceManager.getAppName(this@AppMenuActivity, it.first.applicationInfo.packageName, it.second.second, it.first.applicationInfo.loadLabel(packageManager)).toString().clean() - if (cleanItemText.contains(cleanQuery, ignoreCase = true)) { - newFilteredApps.add(it) - } - } - } - - val changes = detectChanges(installedApps, newFilteredApps) - installedApps = newFilteredApps - withContext(Dispatchers.Main) { - applyChanges(changes, installedApps) - } - } - - } - - private fun String.clean(): String { - return this.replace("[^a-zA-Z0-9]".toRegex(), "") - } - - override fun onStop() { - super.onStop() - job?.cancel() - - } - - override fun onDestroy() { - super.onDestroy() - job?.cancel() - } - - override fun onStart() { - super.onStart() - startTask() - - // Keyboard is sometimes open when going back to the app, so close it. - val imm = - getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(binding.root.windowToken, 0) - } - - private fun startTask() { - job = CoroutineScope(Dispatchers.Default).launch { - while (true) { - manualRefresh() - delay(5000) - } - } - } - - fun manualRefresh() { - CoroutineScope(Dispatchers.Default).launch { - val updatedApps = appUtils.getInstalledApps(this@AppMenuActivity) - val changes = detectChanges(installedApps, updatedApps) - installedApps = updatedApps - withContext(Dispatchers.Main) { - applyChanges(changes, installedApps) - } - } - } - - private 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 updates - 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)) - } - - } - } - - // Detect insertions - newList.forEachIndexed { index, newItem -> - if (!oldSet.contains(Pair(newItem.first.applicationInfo.packageName, newItem.second.second))) { - changes.add(Change(ChangeType.INSERT, index)) - } - } - - // Detect removals - oldList.forEachIndexed { index, oldItem -> - if (!newSet.contains(Pair(oldItem.first.applicationInfo.packageName, oldItem.second.second))) { - removalChanges.add(Change(ChangeType.REMOVE, index)) - } - } - - changes.addAll(removalChanges.reversed()) - - return changes - } - - private 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]) - } - } - } - } - - 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) - } - - fun updateItem(position: Int, app: Pair>) { - adapter.updateApp(position, app) - adapter.notifyItemChanged(position) - } - - fun moveItem(position: Int, newPosition: Int) { - Log.d("Movestatus","MOVED") - adapter.moveApp(position, newPosition) - adapter.notifyItemMoved(position, newPosition) - } - -} - -data class Change(val type: ChangeType, val position: Int, val newPosition: Int = 0) - -enum class ChangeType { - INSERT, REMOVE, UPDATE*/ - override fun onItemClick(appInfo: LauncherActivityInfo, userHandle: UserHandle) { - TODO("Not yet implemented") - } - - - - override fun onItemLongClick( - appInfo: LauncherActivityInfo, - userHandle: UserHandle, - userProfile: Int, - textView: TextView, - actionMenuLayout: LinearLayout, - editView: LinearLayout, - position: Int - ) { - TODO("Not yet implemented") - } - - override fun onShortcut( - appInfo: LauncherActivityInfo, - userHandle: UserHandle, - textView: TextView, - userProfile: Int, - shortcutView: TextView - ) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/app/src/main/java/eu/ottop/yamlauncher/AppMenuEdgeFactory.kt b/app/src/main/java/eu/ottop/yamlauncher/AppMenuEdgeFactory.kt index 32e9c7a..1dacd26 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/AppMenuEdgeFactory.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/AppMenuEdgeFactory.kt @@ -1,55 +1,27 @@ package eu.ottop.yamlauncher -import android.os.Handler -import android.os.Looper import android.widget.EdgeEffect import androidx.recyclerview.widget.RecyclerView class AppMenuEdgeFactory(private val activity: MainActivity) : RecyclerView.EdgeEffectFactory() { - private var isScrollingUp = false - private val scrollListener = object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - isScrollingUp = dy < 0 - } - - } override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect { - view.addOnScrollListener(scrollListener) return AppMenuEdgeEffect(activity) } - inner class AppMenuEdgeEffect(private val activity: MainActivity) : EdgeEffect(activity) { - private val animationSpeedFactor = 0.5f - private val pullDistanceThreshold = 0.03f // Set a suitable threshold - private val debounceInterval = 100L // Milliseconds - private var lastActionTime = 0L - + inner class AppMenuEdgeEffect(activity: MainActivity) : EdgeEffect(activity) { + private val animationSpeedFactor = 0.75f override fun onAbsorb(velocity: Int) { super.onAbsorb((velocity * animationSpeedFactor).toInt()) } override fun onPull(deltaDistance: Float, displacement: Float) { super.onPull(deltaDistance * animationSpeedFactor, displacement) - if (shouldTriggerAction(deltaDistance) && isScrollingUp) { - super.onPull(deltaDistance * animationSpeedFactor, displacement) - if (activity.recyclerAtTop()) { - activity.showHome() - } - } else { - super.onPull(deltaDistance * animationSpeedFactor, displacement) - } } override fun onPullDistance(deltaDistance: Float, displacement: Float): Float { return super.onPullDistance(deltaDistance * animationSpeedFactor, displacement) } - private fun shouldTriggerAction(deltaDistance: Float): Boolean { - val currentTime = System.currentTimeMillis() - return deltaDistance > pullDistanceThreshold && (currentTime - lastActionTime > debounceInterval).also { - if (it) lastActionTime = currentTime - } - } } } \ No newline at end of file diff --git a/app/src/main/java/eu/ottop/yamlauncher/AppMenuLinearLayoutManager.kt b/app/src/main/java/eu/ottop/yamlauncher/AppMenuLinearLayoutManager.kt new file mode 100644 index 0000000..abbd8c6 --- /dev/null +++ b/app/src/main/java/eu/ottop/yamlauncher/AppMenuLinearLayoutManager.kt @@ -0,0 +1,31 @@ +package eu.ottop.yamlauncher + +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView + +class AppMenuLinearLayoutManager(private val activity: MainActivity) : LinearLayoutManager(activity) { + + private var firstVisibleItemPosition = 0 + private var scrollStarted = false + + fun setScrollInfo() { + firstVisibleItemPosition = findFirstCompletelyVisibleItemPosition() + scrollStarted = true + } + + override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State?): Int { + val scrollRange = super.scrollVerticallyBy(dy, recycler, state) + val overscroll: Int = dy - scrollRange + + if (overscroll < 0 && firstVisibleItemPosition == 0 && scrollStarted) { + activity.showHome() + } + + if (scrollStarted) { + scrollStarted = false + } + + return scrollRange + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/ottop/yamlauncher/MainActivity.kt b/app/src/main/java/eu/ottop/yamlauncher/MainActivity.kt index 0364d10..80ee83c 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/MainActivity.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/MainActivity.kt @@ -16,7 +16,6 @@ import android.os.UserHandle import android.provider.MediaStore import android.text.Editable import android.text.TextWatcher -import android.util.Log import android.view.GestureDetector import android.view.MotionEvent import android.view.View @@ -58,8 +57,12 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap private var appActionMenu = AppActionMenu() private val sharedPreferenceManager = SharedPreferenceManager() private val appUtils = AppUtils() + private val appMenuLinearLayoutManager = AppMenuLinearLayoutManager(this@MainActivity) private val appMenuEdgeFactory = AppMenuEdgeFactory(this@MainActivity) + private val swipeThreshold = 100 + private val swipeVelocityThreshold = 100 + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) @@ -75,40 +78,91 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { showHome() - } }) + } + + override fun onNewIntent(intent: Intent?) { + showHome() + super.onNewIntent(intent) + + } + override fun onStop() { + super.onStop() + job?.cancel() } - fun recyclerAtTop() : Boolean { - return recyclerView.scrollY == 0 + override fun onDestroy() { + super.onDestroy() + job?.cancel() + } + + override fun onStart() { + super.onStart() + + startTask() + + // Keyboard is sometimes open when going back to the app, so close it. + val imm = + getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(binding.root.windowToken, 0) + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + gestureDetector.onTouchEvent(event) + return super.onTouchEvent(event) + } + + inner class GestureListener : GestureDetector.SimpleOnGestureListener() { + override fun onFling( + e1: MotionEvent?, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { + if (e1 != null) { + val deltaY = e2.y - e1.y + val deltaX = e2.x - e1.x + + // Detect swipe up + if (deltaY < -swipeThreshold && abs(velocityY) > swipeVelocityThreshold) { + openAppMenuActivity() + return true + } + + // Detect swipe down + else if (deltaY > swipeThreshold && abs(velocityY) > swipeVelocityThreshold) { + return true + } + + // Detect swipe left + else if (deltaX < -swipeThreshold && abs(velocityX) > swipeVelocityThreshold){ + startActivity(cameraIntent) + } + + // Detect swipe right + else if (deltaX > -swipeThreshold && abs(velocityX) > swipeVelocityThreshold) { + startActivity(phoneIntent) + } + } + return false + } + } private fun setupApps() { - handleListItems() - + handleListItems() CoroutineScope(Dispatchers.Default).launch { installedApps = appUtils.getInstalledApps(this@MainActivity) - - val newApps = mutableListOf>>() - newApps.addAll(installedApps) + val newApps = installedApps.toMutableList() setupRecyclerView(newApps) - - searchView = findViewById(R.id.searchView) - setupSearch() } - } - private suspend fun setupRecyclerView(newApps: MutableList>>) { - adapter = AppMenuAdapter(this@MainActivity, newApps, this@MainActivity, this@MainActivity, this@MainActivity) - withContext(Dispatchers.Main) { - recyclerView = findViewById(R.id.recycler_view) - recyclerView.edgeEffectFactory = appMenuEdgeFactory - recyclerView.adapter = adapter - recyclerView.scrollToPosition(0) - } + searchView = findViewById(R.id.searchView) + setupSearch() + } private fun handleListItems() { @@ -175,10 +229,38 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap } } + private suspend fun setupRecyclerView(newApps: MutableList>>) { + adapter = AppMenuAdapter(this@MainActivity, newApps, this@MainActivity, this@MainActivity, this@MainActivity) + appMenuLinearLayoutManager.stackFromEnd = true + recyclerView = findViewById(R.id.recycler_view) + withContext(Dispatchers.Main) { + recyclerView.layoutManager = appMenuLinearLayoutManager + recyclerView.edgeEffectFactory = appMenuEdgeFactory + recyclerView.adapter = adapter + recyclerView.scrollToPosition(0) + } + + setupRecyclerListener() + } + + private fun setupRecyclerListener() { + recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { + appMenuLinearLayoutManager.setScrollInfo() + } + } + }) + } + private fun setupSearch() { binding.root.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom -> + if (bottom - top > oldBottom - oldTop) { + searchView.clearFocus() + if (searchView.text.isNullOrEmpty()) { job?.cancel() startTask() @@ -195,7 +277,9 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - filterItems(searchView.text.toString()) + CoroutineScope(Dispatchers.Default).launch { + filterItems(searchView.text.toString()) + } } override fun afterTextChanged(s: Editable?) { @@ -203,8 +287,8 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap }) } - private fun filterItems(query: String?) { - CoroutineScope(Dispatchers.Default).launch { + private suspend fun filterItems(query: String?) { + val cleanQuery = query?.clean() val newFilteredApps = mutableListOf>>() val updatedApps = appUtils.getInstalledApps(this@MainActivity) @@ -212,13 +296,12 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap getFilteredApps(cleanQuery, newFilteredApps, updatedApps) applySearch(newFilteredApps) - } } - private fun getFilteredApps(cleanQuery: String?, newFilteredApps: MutableList>>, updatedApps: List>>) { + private suspend fun getFilteredApps(cleanQuery: String?, newFilteredApps: MutableList>>, updatedApps: List>>) { if (cleanQuery.isNullOrEmpty()) { - manualRefresh() + refreshAppMenu() newFilteredApps.addAll(installedApps) } else { updatedApps.forEach { @@ -242,83 +325,15 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap return this.replace("[^a-zA-Z0-9]".toRegex(), "") } - override fun onNewIntent(intent: Intent?) { - showHome() - super.onNewIntent(intent) - - } - override fun onStop() { - super.onStop() - job?.cancel() - - } - - override fun onDestroy() { - super.onDestroy() - job?.cancel() - } - - override fun onStart() { - super.onStart() - - startTask() - - // Keyboard is sometimes open when going back to the app, so close it. - val imm = - getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(binding.root.windowToken, 0) - } - private fun startTask() { job = CoroutineScope(Dispatchers.Default).launch { while (true) { - manualRefresh() + refreshAppMenu() delay(5000) } } } - override fun onTouchEvent(event: MotionEvent): Boolean { - gestureDetector.onTouchEvent(event) - return super.onTouchEvent(event) - } - - inner class GestureListener : GestureDetector.SimpleOnGestureListener() { - override fun onFling( - e1: MotionEvent?, - e2: MotionEvent, - velocityX: Float, - velocityY: Float - ): Boolean { - if (e1 != null) { - val deltaY = e2.y - e1.y - val deltaX = e2.x - e1.x - - // Detect swipe up - if (deltaY < -SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { - openAppMenuActivity() - return true - } - - // Detect swipe left - else if (deltaX < -SWIPE_THRESHOLD && abs(velocityX) > SWIPE_VELOCITY_THRESHOLD){ - startActivity(phoneIntent) - } - - // Detect swipe right - else if (deltaX > -SWIPE_THRESHOLD && abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { - startActivity(cameraIntent) - } - } - return false - } - - } - companion object { - private const val SWIPE_THRESHOLD = 100 - private const val SWIPE_VELOCITY_THRESHOLD = 100 - } - private fun View.slideInFromBottom(duration: Long = 100) { if (visibility != View.VISIBLE) { translationY = height.toFloat()/5 @@ -500,7 +515,6 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap .firstOrNull() appActionMenu.setActionListeners( this@MainActivity, - CoroutineScope(Dispatchers.Main), binding, textView, editView, @@ -515,8 +529,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap ) } - fun manualRefresh() { - CoroutineScope(Dispatchers.Default).launch { + suspend fun refreshAppMenu() { try { val updatedApps = appUtils.getInstalledApps(this@MainActivity) val changes = detectChanges(installedApps, updatedApps) @@ -527,7 +540,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap } catch (_: UninitializedPropertyAccessException) { } - } + } private fun detectChanges(oldList: List>>, newList: List>>): List { diff --git a/app/src/main/java/eu/ottop/yamlauncher/SharedPreferenceManager.kt b/app/src/main/java/eu/ottop/yamlauncher/SharedPreferenceManager.kt index 549c713..bb8d5a5 100644 --- a/app/src/main/java/eu/ottop/yamlauncher/SharedPreferenceManager.kt +++ b/app/src/main/java/eu/ottop/yamlauncher/SharedPreferenceManager.kt @@ -1,10 +1,6 @@ package eu.ottop.yamlauncher import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager -import android.content.pm.ResolveInfo -import android.provider.MediaStore import android.widget.TextView import androidx.appcompat.app.AppCompatActivity diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e944c26..10fde85 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,5 @@ - - + android:scrollbars="none"> @@ -68,10 +61,10 @@ android:layout_weight="0.1" android:background="@android:color/transparent" android:cursorVisible="true" - android:drawableLeft="@android:drawable/ic_menu_search" + android:drawableStart="@android:drawable/ic_menu_search" android:drawablePadding="8dp" android:editTextColor="#f3f3f3" - android:hint="Search..." + android:hint="@string/search" android:singleLine="true" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textSize="25sp" /> @@ -119,7 +112,7 @@ android:paddingTop="20dp" android:paddingRight="40dp" android:paddingBottom="20dp" - android:text="App" + android:text="@string/shortcut_default" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textColor="#F3F3F3" android:textSize="28sp" @@ -135,7 +128,7 @@ android:paddingTop="20dp" android:paddingRight="40dp" android:paddingBottom="20dp" - android:text="App" + android:text="@string/shortcut_default" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textColor="#F3F3F3" android:textSize="28sp" @@ -151,7 +144,7 @@ android:paddingTop="20dp" android:paddingRight="40dp" android:paddingBottom="20dp" - android:text="App" + android:text="@string/shortcut_default" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textColor="#F3F3F3" android:textSize="28sp" @@ -167,7 +160,7 @@ android:paddingTop="20dp" android:paddingRight="40dp" android:paddingBottom="20dp" - android:text="App" + android:text="@string/shortcut_default" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textColor="#F3F3F3" android:textSize="28sp" @@ -183,7 +176,7 @@ android:paddingTop="20dp" android:paddingRight="40dp" android:paddingBottom="20dp" - android:text="App" + android:text="@string/shortcut_default" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textColor="#F3F3F3" android:textSize="28sp" @@ -199,7 +192,7 @@ android:paddingTop="20dp" android:paddingRight="40dp" android:paddingBottom="20dp" - android:text="App" + android:text="@string/shortcut_default" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textColor="#F3F3F3" android:textSize="28sp" @@ -215,7 +208,7 @@ android:paddingTop="20dp" android:paddingRight="40dp" android:paddingBottom="20dp" - android:text="App" + android:text="@string/shortcut_default" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textColor="#F3F3F3" android:textSize="28sp" @@ -231,7 +224,7 @@ android:paddingTop="20dp" android:paddingRight="40dp" android:paddingBottom="20dp" - android:text="App" + android:text="@string/shortcut_default" android:textAppearance="@android:style/TextAppearance.DeviceDefault" android:textColor="#F3F3F3" android:textSize="28sp" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 443a692..c4100f2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -45,4 +45,8 @@ Hello blank fragment + Select an app + Search… + App + App \ No newline at end of file