mirror of
https://github.com/He4eT/yamf_launcher.git
synced 2026-05-04 17:37:25 +00:00
Fixed returning from app menu and refactoring
This commit is contained in:
parent
1a748ce709
commit
5cdd7bca2e
8 changed files with 183 additions and 516 deletions
|
|
@ -16,10 +16,11 @@ import android.widget.EditText
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.widget.AppCompatButton
|
import androidx.appcompat.widget.AppCompatButton
|
||||||
import eu.ottop.yamlauncher.databinding.ActivityAppMenuBinding
|
|
||||||
import eu.ottop.yamlauncher.databinding.ActivityMainBinding
|
import eu.ottop.yamlauncher.databinding.ActivityMainBinding
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class AppActionMenu {
|
class AppActionMenu {
|
||||||
|
|
||||||
|
|
@ -28,7 +29,6 @@ class AppActionMenu {
|
||||||
|
|
||||||
fun setActionListeners(
|
fun setActionListeners(
|
||||||
activity: MainActivity,
|
activity: MainActivity,
|
||||||
uiScope: CoroutineScope,
|
|
||||||
binding: ActivityMainBinding,
|
binding: ActivityMainBinding,
|
||||||
textView: TextView,
|
textView: TextView,
|
||||||
editLayout: LinearLayout,
|
editLayout: LinearLayout,
|
||||||
|
|
@ -106,13 +106,14 @@ class AppActionMenu {
|
||||||
workProfile,
|
workProfile,
|
||||||
editText.text.toString()
|
editText.text.toString()
|
||||||
)
|
)
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
val newPosition = appUtils.getInstalledApps(activity)
|
val newPosition = appUtils.getInstalledApps(activity)
|
||||||
.indexOfFirst { it.first.applicationInfo.packageName == appInfo.packageName && it.second.second == workProfile }
|
.indexOfFirst { it.first.applicationInfo.packageName == appInfo.packageName && it.second.second == workProfile }
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
activity.updateItem(position, app)
|
activity.updateItem(position, app)
|
||||||
activity.moveItem(position, newPosition)
|
activity.moveItem(position, newPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return@setOnEditorActionListener true
|
return@setOnEditorActionListener true
|
||||||
|
|
@ -129,11 +130,15 @@ class AppActionMenu {
|
||||||
app.first.applicationInfo.packageName,
|
app.first.applicationInfo.packageName,
|
||||||
app.second.second
|
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
|
editLayout.visibility = View.GONE
|
||||||
textView.visibility = View.GONE
|
textView.visibility = View.GONE
|
||||||
actionMenu.visibility = View.GONE
|
actionMenu.visibility = View.GONE
|
||||||
sharedPreferenceManager.setAppHidden(activity, appInfo.packageName, workProfile, true)
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
activity.manualRefresh()
|
sharedPreferenceManager.setAppHidden(activity, appInfo.packageName, workProfile, true)
|
||||||
|
activity.refreshAppMenu()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actionMenu.findViewById<TextView>(R.id.close).setOnClickListener {
|
actionMenu.findViewById<TextView>(R.id.close).setOnClickListener {
|
||||||
|
|
|
||||||
|
|
@ -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<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>
|
|
||||||
|
|
||||||
private val sharedPreferenceManager = SharedPreferenceManager()
|
|
||||||
private val appUtils = AppUtils()
|
|
||||||
|
|
||||||
private lateinit var menuMode: String
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private lateinit var callback: (Pair<Pair<String, Int>, Pair<LauncherActivityInfo, UserHandle>>) -> Unit
|
|
||||||
private const val MENU_MODE = "app"
|
|
||||||
private lateinit var currentApps: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>
|
|
||||||
fun start(context: Context, currentApps: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>, param1: String = "app", callback: (Pair<Pair<String, Int>, Pair<LauncherActivityInfo, UserHandle>>) -> 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<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>()
|
|
||||||
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<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>()
|
|
||||||
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<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>, newList: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>): List<Change> {
|
|
||||||
val changes = mutableListOf<Change>()
|
|
||||||
val removalChanges = mutableListOf<Change>()
|
|
||||||
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<Change>, updatedApps: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>) {
|
|
||||||
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<LauncherActivityInfo, Pair<UserHandle, Int>>) {
|
|
||||||
adapter.addApp(position, app)
|
|
||||||
adapter.notifyItemInserted(position)
|
|
||||||
}
|
|
||||||
private fun removeItem(position: Int) {
|
|
||||||
adapter.removeApp(position)
|
|
||||||
adapter.notifyItemRemoved(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateItem(position: Int, app: Pair<LauncherActivityInfo, Pair<UserHandle, Int>>) {
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +1,27 @@
|
||||||
package eu.ottop.yamlauncher
|
package eu.ottop.yamlauncher
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.widget.EdgeEffect
|
import android.widget.EdgeEffect
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
class AppMenuEdgeFactory(private val activity: MainActivity) : RecyclerView.EdgeEffectFactory() {
|
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 {
|
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
|
||||||
view.addOnScrollListener(scrollListener)
|
|
||||||
return AppMenuEdgeEffect(activity)
|
return AppMenuEdgeEffect(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class AppMenuEdgeEffect(private val activity: MainActivity) : EdgeEffect(activity) {
|
inner class AppMenuEdgeEffect(activity: MainActivity) : EdgeEffect(activity) {
|
||||||
private val animationSpeedFactor = 0.5f
|
private val animationSpeedFactor = 0.75f
|
||||||
private val pullDistanceThreshold = 0.03f // Set a suitable threshold
|
|
||||||
private val debounceInterval = 100L // Milliseconds
|
|
||||||
private var lastActionTime = 0L
|
|
||||||
|
|
||||||
override fun onAbsorb(velocity: Int) {
|
override fun onAbsorb(velocity: Int) {
|
||||||
super.onAbsorb((velocity * animationSpeedFactor).toInt())
|
super.onAbsorb((velocity * animationSpeedFactor).toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPull(deltaDistance: Float, displacement: Float) {
|
override fun onPull(deltaDistance: Float, displacement: Float) {
|
||||||
super.onPull(deltaDistance * animationSpeedFactor, displacement)
|
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 {
|
override fun onPullDistance(deltaDistance: Float, displacement: Float): Float {
|
||||||
return super.onPullDistance(deltaDistance * animationSpeedFactor, displacement)
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,6 @@ import android.os.UserHandle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.util.Log
|
|
||||||
import android.view.GestureDetector
|
import android.view.GestureDetector
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
|
@ -58,8 +57,12 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
private var appActionMenu = AppActionMenu()
|
private var appActionMenu = AppActionMenu()
|
||||||
private val sharedPreferenceManager = SharedPreferenceManager()
|
private val sharedPreferenceManager = SharedPreferenceManager()
|
||||||
private val appUtils = AppUtils()
|
private val appUtils = AppUtils()
|
||||||
|
private val appMenuLinearLayoutManager = AppMenuLinearLayoutManager(this@MainActivity)
|
||||||
private val appMenuEdgeFactory = AppMenuEdgeFactory(this@MainActivity)
|
private val appMenuEdgeFactory = AppMenuEdgeFactory(this@MainActivity)
|
||||||
|
|
||||||
|
private val swipeThreshold = 100
|
||||||
|
private val swipeVelocityThreshold = 100
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
|
@ -75,40 +78,91 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||||
override fun handleOnBackPressed() {
|
override fun handleOnBackPressed() {
|
||||||
showHome()
|
showHome()
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent?) {
|
||||||
|
showHome()
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
|
||||||
|
}
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
job?.cancel()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun recyclerAtTop() : Boolean {
|
override fun onDestroy() {
|
||||||
return recyclerView.scrollY == 0
|
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() {
|
private fun setupApps() {
|
||||||
handleListItems()
|
handleListItems()
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Default).launch {
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
installedApps = appUtils.getInstalledApps(this@MainActivity)
|
installedApps = appUtils.getInstalledApps(this@MainActivity)
|
||||||
|
val newApps = installedApps.toMutableList()
|
||||||
val newApps = mutableListOf<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>()
|
|
||||||
newApps.addAll(installedApps)
|
|
||||||
|
|
||||||
setupRecyclerView(newApps)
|
setupRecyclerView(newApps)
|
||||||
|
|
||||||
searchView = findViewById(R.id.searchView)
|
|
||||||
setupSearch()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun setupRecyclerView(newApps: MutableList<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>) {
|
searchView = findViewById(R.id.searchView)
|
||||||
adapter = AppMenuAdapter(this@MainActivity, newApps, this@MainActivity, this@MainActivity, this@MainActivity)
|
setupSearch()
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
recyclerView = findViewById(R.id.recycler_view)
|
|
||||||
recyclerView.edgeEffectFactory = appMenuEdgeFactory
|
|
||||||
recyclerView.adapter = adapter
|
|
||||||
recyclerView.scrollToPosition(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleListItems() {
|
private fun handleListItems() {
|
||||||
|
|
@ -175,10 +229,38 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun setupRecyclerView(newApps: MutableList<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>) {
|
||||||
|
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() {
|
private fun setupSearch() {
|
||||||
binding.root.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
|
binding.root.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
|
||||||
|
|
||||||
if (bottom - top > oldBottom - oldTop) {
|
if (bottom - top > oldBottom - oldTop) {
|
||||||
|
|
||||||
searchView.clearFocus()
|
searchView.clearFocus()
|
||||||
|
|
||||||
if (searchView.text.isNullOrEmpty()) {
|
if (searchView.text.isNullOrEmpty()) {
|
||||||
job?.cancel()
|
job?.cancel()
|
||||||
startTask()
|
startTask()
|
||||||
|
|
@ -195,7 +277,9 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
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?) {
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
|
@ -203,8 +287,8 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun filterItems(query: String?) {
|
private suspend fun filterItems(query: String?) {
|
||||||
CoroutineScope(Dispatchers.Default).launch {
|
|
||||||
val cleanQuery = query?.clean()
|
val cleanQuery = query?.clean()
|
||||||
val newFilteredApps = mutableListOf<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>()
|
val newFilteredApps = mutableListOf<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>()
|
||||||
val updatedApps = appUtils.getInstalledApps(this@MainActivity)
|
val updatedApps = appUtils.getInstalledApps(this@MainActivity)
|
||||||
|
|
@ -212,13 +296,12 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
getFilteredApps(cleanQuery, newFilteredApps, updatedApps)
|
getFilteredApps(cleanQuery, newFilteredApps, updatedApps)
|
||||||
|
|
||||||
applySearch(newFilteredApps)
|
applySearch(newFilteredApps)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFilteredApps(cleanQuery: String?, newFilteredApps: MutableList<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>, updatedApps: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>) {
|
private suspend fun getFilteredApps(cleanQuery: String?, newFilteredApps: MutableList<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>, updatedApps: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>) {
|
||||||
if (cleanQuery.isNullOrEmpty()) {
|
if (cleanQuery.isNullOrEmpty()) {
|
||||||
manualRefresh()
|
refreshAppMenu()
|
||||||
newFilteredApps.addAll(installedApps)
|
newFilteredApps.addAll(installedApps)
|
||||||
} else {
|
} else {
|
||||||
updatedApps.forEach {
|
updatedApps.forEach {
|
||||||
|
|
@ -242,83 +325,15 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
return this.replace("[^a-zA-Z0-9]".toRegex(), "")
|
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() {
|
private fun startTask() {
|
||||||
job = CoroutineScope(Dispatchers.Default).launch {
|
job = CoroutineScope(Dispatchers.Default).launch {
|
||||||
while (true) {
|
while (true) {
|
||||||
manualRefresh()
|
refreshAppMenu()
|
||||||
delay(5000)
|
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) {
|
private fun View.slideInFromBottom(duration: Long = 100) {
|
||||||
if (visibility != View.VISIBLE) {
|
if (visibility != View.VISIBLE) {
|
||||||
translationY = height.toFloat()/5
|
translationY = height.toFloat()/5
|
||||||
|
|
@ -500,7 +515,6 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
appActionMenu.setActionListeners(
|
appActionMenu.setActionListeners(
|
||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
CoroutineScope(Dispatchers.Main),
|
|
||||||
binding,
|
binding,
|
||||||
textView,
|
textView,
|
||||||
editView,
|
editView,
|
||||||
|
|
@ -515,8 +529,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun manualRefresh() {
|
suspend fun refreshAppMenu() {
|
||||||
CoroutineScope(Dispatchers.Default).launch {
|
|
||||||
try {
|
try {
|
||||||
val updatedApps = appUtils.getInstalledApps(this@MainActivity)
|
val updatedApps = appUtils.getInstalledApps(this@MainActivity)
|
||||||
val changes = detectChanges(installedApps, updatedApps)
|
val changes = detectChanges(installedApps, updatedApps)
|
||||||
|
|
@ -527,7 +540,7 @@ class MainActivity : AppCompatActivity(), AppMenuAdapter.OnItemClickListener, Ap
|
||||||
}
|
}
|
||||||
catch (_: UninitializedPropertyAccessException) {
|
catch (_: UninitializedPropertyAccessException) {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun detectChanges(oldList: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>, newList: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>): List<Change> {
|
private fun detectChanges(oldList: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>, newList: List<Pair<LauncherActivityInfo, Pair<UserHandle, Int>>>): List<Change> {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
package eu.ottop.yamlauncher
|
package eu.ottop.yamlauncher
|
||||||
|
|
||||||
import android.content.Context
|
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 android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/main_view"
|
android:id="@+id/main_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
@ -10,10 +9,6 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context=".MainActivity">
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
<ViewSwitcher
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/app_view"
|
android:id="@+id/app_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
@ -36,7 +31,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="Select an app"
|
android:text="@string/select_an_app"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#C1F3F3F3"
|
android:textColor="#C1F3F3F3"
|
||||||
android:textSize="36sp"
|
android:textSize="36sp"
|
||||||
|
|
@ -51,9 +46,7 @@
|
||||||
android:fadingEdgeLength="20dp"
|
android:fadingEdgeLength="20dp"
|
||||||
android:padding="0dp"
|
android:padding="0dp"
|
||||||
android:requiresFadingEdge="vertical"
|
android:requiresFadingEdge="vertical"
|
||||||
android:scrollbars="none"
|
android:scrollbars="none">
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:stackFromEnd="true">
|
|
||||||
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
|
|
@ -68,10 +61,10 @@
|
||||||
android:layout_weight="0.1"
|
android:layout_weight="0.1"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:cursorVisible="true"
|
android:cursorVisible="true"
|
||||||
android:drawableLeft="@android:drawable/ic_menu_search"
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:editTextColor="#f3f3f3"
|
android:editTextColor="#f3f3f3"
|
||||||
android:hint="Search..."
|
android:hint="@string/search"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textSize="25sp" />
|
android:textSize="25sp" />
|
||||||
|
|
@ -119,7 +112,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="App"
|
android:text="@string/shortcut_default"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#F3F3F3"
|
android:textColor="#F3F3F3"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
|
|
@ -135,7 +128,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="App"
|
android:text="@string/shortcut_default"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#F3F3F3"
|
android:textColor="#F3F3F3"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
|
|
@ -151,7 +144,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="App"
|
android:text="@string/shortcut_default"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#F3F3F3"
|
android:textColor="#F3F3F3"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
|
|
@ -167,7 +160,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="App"
|
android:text="@string/shortcut_default"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#F3F3F3"
|
android:textColor="#F3F3F3"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
|
|
@ -183,7 +176,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="App"
|
android:text="@string/shortcut_default"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#F3F3F3"
|
android:textColor="#F3F3F3"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
|
|
@ -199,7 +192,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="App"
|
android:text="@string/shortcut_default"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#F3F3F3"
|
android:textColor="#F3F3F3"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
|
|
@ -215,7 +208,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="App"
|
android:text="@string/shortcut_default"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#F3F3F3"
|
android:textColor="#F3F3F3"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
|
|
@ -231,7 +224,7 @@
|
||||||
android:paddingTop="20dp"
|
android:paddingTop="20dp"
|
||||||
android:paddingRight="40dp"
|
android:paddingRight="40dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
android:text="App"
|
android:text="@string/shortcut_default"
|
||||||
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
android:textAppearance="@android:style/TextAppearance.DeviceDefault"
|
||||||
android:textColor="#F3F3F3"
|
android:textColor="#F3F3F3"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
|
|
|
||||||
|
|
@ -45,4 +45,8 @@
|
||||||
</string>
|
</string>
|
||||||
<!-- TODO: Remove or change this placeholder text -->
|
<!-- TODO: Remove or change this placeholder text -->
|
||||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
|
<string name="select_an_app">Select an app</string>
|
||||||
|
<string name="search">Search…</string>
|
||||||
|
<string name="app">App</string>
|
||||||
|
<string name="shortcut_default">App</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue