mirror of
https://github.com/He4eT/yamf_launcher.git
synced 2026-11-01 11:28:30 +00:00
Removed app menu closing restriction, made about page image open app info and organized code into smaller packages
This commit is contained in:
parent
fabea8ad76
commit
4cb52fe719
26 changed files with 108 additions and 50 deletions
|
|
@ -25,7 +25,7 @@
|
|||
android:theme="@style/Theme.YamLauncher"
|
||||
tools:targetApi="34">
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:name=".settings.SettingsActivity"
|
||||
android:theme="@style/SettingsTheme"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
<service
|
||||
android:name=".ScreenLockService"
|
||||
android:name=".tasks.ScreenLockService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
|
||||
<intent-filter>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import android.widget.TextView
|
|||
import androidx.appcompat.widget.AppCompatButton
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import eu.ottop.yamlauncher.databinding.ActivityMainBinding
|
||||
import eu.ottop.yamlauncher.settings.SharedPreferenceManager
|
||||
import eu.ottop.yamlauncher.utils.Animations
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AppActionMenu {
|
||||
|
|
@ -33,7 +35,7 @@ class AppActionMenu {
|
|||
userHandle: UserHandle,
|
||||
workProfile: Int,
|
||||
launcherApps: LauncherApps,
|
||||
mainActivity: LauncherActivityInfo?
|
||||
appActivity: LauncherActivityInfo?
|
||||
){
|
||||
val animations = Animations(activity)
|
||||
val sharedPreferenceManager = SharedPreferenceManager(activity)
|
||||
|
|
@ -41,9 +43,9 @@ class AppActionMenu {
|
|||
actionMenu.findViewById<TextView>(R.id.info).setOnClickListener {
|
||||
|
||||
// Launch app info in phone settings
|
||||
if (mainActivity != null) {
|
||||
if (appActivity != null) {
|
||||
launcherApps.startAppDetailsActivity(
|
||||
mainActivity.componentName,
|
||||
appActivity.componentName,
|
||||
userHandle,
|
||||
null,
|
||||
null
|
||||
|
|
@ -72,7 +74,7 @@ class AppActionMenu {
|
|||
val editText = editLayout.findViewById<EditText>(R.id.appNameEdit)
|
||||
val resetButton = editLayout.findViewById<AppCompatButton>(R.id.reset)
|
||||
|
||||
val app = Triple(mainActivity!!, userHandle, workProfile)
|
||||
val app = Triple(appActivity!!, userHandle, workProfile)
|
||||
|
||||
searchView.visibility = View.INVISIBLE
|
||||
editText.requestFocus()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ import android.widget.TextView
|
|||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import eu.ottop.yamlauncher.settings.SharedPreferenceManager
|
||||
import eu.ottop.yamlauncher.utils.AppUtils
|
||||
import eu.ottop.yamlauncher.utils.UIUtils
|
||||
|
||||
|
||||
class AppMenuAdapter(
|
||||
|
|
|
|||
|
|
@ -33,6 +33,18 @@ import androidx.preference.PreferenceManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import eu.ottop.yamlauncher.databinding.ActivityMainBinding
|
||||
import eu.ottop.yamlauncher.settings.SettingsActivity
|
||||
import eu.ottop.yamlauncher.settings.SharedPreferenceManager
|
||||
import eu.ottop.yamlauncher.tasks.BatteryReceiver
|
||||
import eu.ottop.yamlauncher.tasks.ScreenLockService
|
||||
import eu.ottop.yamlauncher.utils.Animations
|
||||
import eu.ottop.yamlauncher.utils.AppMenuEdgeFactory
|
||||
import eu.ottop.yamlauncher.utils.AppMenuLinearLayoutManager
|
||||
import eu.ottop.yamlauncher.utils.AppUtils
|
||||
import eu.ottop.yamlauncher.utils.GestureUtils
|
||||
import eu.ottop.yamlauncher.utils.StringUtils
|
||||
import eu.ottop.yamlauncher.utils.UIUtils
|
||||
import eu.ottop.yamlauncher.utils.WeatherSystem
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -74,8 +86,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
|
|||
private lateinit var preferences: SharedPreferences
|
||||
|
||||
private var isBatteryReceiverRegistered = false
|
||||
private var isJobActive = true
|
||||
var canExit = true
|
||||
var isJobActive = true
|
||||
|
||||
private val swipeThreshold = 100
|
||||
private val swipeVelocityThreshold = 100
|
||||
|
|
@ -525,14 +536,10 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
|
|||
binding.appView.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
|
||||
|
||||
if (bottom - top > oldBottom - oldTop) {
|
||||
// Allow the app menu to be closed after the keyboard is closed
|
||||
canExit = true
|
||||
// If keyboard is closed, remove cursor from the search bar
|
||||
searchView.clearFocus()
|
||||
}
|
||||
else if (bottom - top < oldBottom - oldTop) {
|
||||
// The app menu can't be closed with the keyboard open
|
||||
canExit = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
searchView.addTextChangedListener(object :
|
||||
|
|
@ -666,7 +673,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
|
|||
) {
|
||||
textView.visibility = View.INVISIBLE
|
||||
animations.fadeViewIn(actionMenuLayout)
|
||||
val mainActivity =
|
||||
val appActivity =
|
||||
launcherApps.getActivityList(appInfo.applicationInfo.packageName, userHandle)
|
||||
.firstOrNull()
|
||||
appActionMenu.setActionListeners(
|
||||
|
|
@ -680,7 +687,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
|
|||
userHandle,
|
||||
userProfile,
|
||||
launcherApps,
|
||||
mainActivity
|
||||
appActivity
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.fragment.app.Fragment
|
||||
import eu.ottop.yamlauncher.R
|
||||
import eu.ottop.yamlauncher.utils.StringUtils
|
||||
|
||||
class AboutFragment : Fragment() {
|
||||
|
||||
private val stringUtils = StringUtils()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_about, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Set up about page links
|
||||
stringUtils.setLink(requireActivity().findViewById(R.id.creditText), getString(R.string.my_website_link))
|
||||
stringUtils.setLink(requireActivity().findViewById(R.id.codebergLink), getString(R.string.codeberg_link))
|
||||
stringUtils.setLink(requireActivity().findViewById(R.id.githubLink), getString(R.string.github_link))
|
||||
stringUtils.setLink(requireActivity().findViewById(R.id.stripeLink), getString(R.string.stripe_link))
|
||||
stringUtils.setLink(requireActivity().findViewById(R.id.liberaLink), getString(R.string.libera_link))
|
||||
stringUtils.setLink(requireActivity().findViewById(R.id.weatherLink), getString(R.string.weather_link))
|
||||
|
||||
requireActivity().findViewById<ImageView>(R.id.iconView).setOnClickListener {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = Uri.parse("package:${requireContext().packageName}")
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.LauncherActivityInfo
|
||||
import android.os.UserHandle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.ottop.yamlauncher.R
|
||||
import eu.ottop.yamlauncher.utils.UIUtils
|
||||
|
||||
class GestureAppsAdapter(
|
||||
private val context: Context,
|
||||
var apps: MutableList<Triple<LauncherActivityInfo, UserHandle, Int>>,
|
||||
private val itemClickListener: OnItemClickListener
|
||||
) :
|
||||
RecyclerView.Adapter<GestureAppsAdapter.AppViewHolder>() {
|
||||
|
||||
private val sharedPreferenceManager = SharedPreferenceManager(context)
|
||||
private val uiUtils = UIUtils(context)
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(appInfo: LauncherActivityInfo, profile: Int)
|
||||
}
|
||||
|
||||
inner class AppViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val listItem: FrameLayout = itemView.findViewById(R.id.listItem)
|
||||
val textView: TextView = listItem.findViewById(R.id.appName)
|
||||
|
||||
init {
|
||||
textView.setOnClickListener {
|
||||
val position = bindingAdapterPosition
|
||||
val app = apps[position].first
|
||||
itemClickListener.onItemClick(app, apps[position].third)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.app_item_layout, parent, false)
|
||||
return AppViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
|
||||
val app = apps[position]
|
||||
|
||||
if (app.third != 0) {
|
||||
holder.textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(context.resources,
|
||||
R.drawable.ic_work_app, null),null,null,null)
|
||||
}
|
||||
else {
|
||||
holder.textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(context.resources,
|
||||
R.drawable.ic_empty, null),null,null,null)
|
||||
}
|
||||
|
||||
uiUtils.setAppAlignment(holder.textView)
|
||||
|
||||
uiUtils.setAppSize(holder.textView)
|
||||
|
||||
// Does not need to be specially updated since it's in a separate activity and thus reloads when opened again
|
||||
val appInfo = app.first.activityInfo.applicationInfo
|
||||
|
||||
holder.textView.text = sharedPreferenceManager.getAppName(
|
||||
app.first.applicationInfo.packageName,
|
||||
app.third,
|
||||
context.packageManager.getApplicationLabel(appInfo)
|
||||
)
|
||||
|
||||
holder.textView.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return apps.size
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateApps(newApps: List<Triple<LauncherActivityInfo, UserHandle, Int>>) {
|
||||
apps = newApps.toMutableList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
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.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import eu.ottop.yamlauncher.utils.AppMenuEdgeFactory
|
||||
import eu.ottop.yamlauncher.R
|
||||
import eu.ottop.yamlauncher.utils.AppUtils
|
||||
import eu.ottop.yamlauncher.utils.StringUtils
|
||||
import eu.ottop.yamlauncher.utils.UIUtils
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class GestureAppsFragment(private val direction: String) : Fragment(),
|
||||
GestureAppsAdapter.OnItemClickListener {
|
||||
|
||||
private var adapter: GestureAppsAdapter? = null
|
||||
private lateinit var sharedPreferenceManager: SharedPreferenceManager
|
||||
private var stringUtils = StringUtils()
|
||||
private lateinit var appUtils: AppUtils
|
||||
private lateinit var launcherApps: LauncherApps
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_gesture_apps, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
launcherApps = requireContext().getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
||||
|
||||
appUtils = AppUtils(requireContext(), launcherApps)
|
||||
sharedPreferenceManager = SharedPreferenceManager(requireContext())
|
||||
|
||||
lifecycleScope.launch {
|
||||
adapter = GestureAppsAdapter(
|
||||
requireContext(),
|
||||
appUtils.getInstalledApps().toMutableList(),
|
||||
this@GestureAppsFragment
|
||||
)
|
||||
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.gestureAppRecycler)
|
||||
val appMenuEdgeFactory = AppMenuEdgeFactory(requireActivity())
|
||||
val uiUtils = UIUtils(requireContext())
|
||||
|
||||
recyclerView.edgeEffectFactory = appMenuEdgeFactory
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
recyclerView.scrollToPosition(0)
|
||||
|
||||
val searchView = view.findViewById<TextInputEditText>(R.id.gestureAppSearch)
|
||||
|
||||
uiUtils.setMenuTitleAlignment(view.findViewById(R.id.gestureMenuTitle))
|
||||
uiUtils.setSearchAlignment(searchView)
|
||||
uiUtils.setSearchSize(searchView)
|
||||
|
||||
recyclerView.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
|
||||
|
||||
if (bottom - top > oldBottom - oldTop) {
|
||||
searchView.clearFocus()
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
lifecycleScope.launch {
|
||||
filterItems(s.toString())
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun filterItems(query: String?) {
|
||||
|
||||
val cleanQuery = stringUtils.cleanString(query)
|
||||
val newFilteredApps = mutableListOf<Triple<LauncherActivityInfo, UserHandle, Int>>()
|
||||
val updatedApps = appUtils.getInstalledApps()
|
||||
|
||||
getFilteredApps(cleanQuery, newFilteredApps, updatedApps)
|
||||
|
||||
applySearch(newFilteredApps)
|
||||
|
||||
}
|
||||
|
||||
private fun getFilteredApps(cleanQuery: String?, newFilteredApps: MutableList<Triple<LauncherActivityInfo, UserHandle, Int>>, updatedApps: List<Triple<LauncherActivityInfo, UserHandle, Int>>) {
|
||||
if (cleanQuery.isNullOrEmpty()) {
|
||||
newFilteredApps.addAll(updatedApps)
|
||||
} else {
|
||||
updatedApps.forEach {
|
||||
val cleanItemText = stringUtils.cleanString(sharedPreferenceManager.getAppName(
|
||||
it.first.applicationInfo.packageName,
|
||||
it.third,
|
||||
requireContext().packageManager.getApplicationLabel(it.first.applicationInfo)
|
||||
).toString())
|
||||
if (cleanItemText != null) {
|
||||
if (cleanItemText.contains(cleanQuery, ignoreCase = true)) {
|
||||
newFilteredApps.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applySearch(newFilteredApps: MutableList<Triple<LauncherActivityInfo, UserHandle, Int>>) {
|
||||
adapter?.updateApps(newFilteredApps)
|
||||
}
|
||||
|
||||
private fun showConfirmationDialog(appInfo: LauncherActivityInfo, appName: String, profile: Int) {
|
||||
AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle("Confirmation")
|
||||
setMessage("Are you sure you want to choose $appName?")
|
||||
|
||||
setPositiveButton("Yes") { _, _ ->
|
||||
performConfirmedAction(appInfo, appName, profile)
|
||||
}
|
||||
|
||||
setNegativeButton("Cancel") { _, _ ->
|
||||
}
|
||||
|
||||
}.create().show()
|
||||
}
|
||||
|
||||
private fun performConfirmedAction(appInfo: LauncherActivityInfo, appName: String, profile: Int) {
|
||||
sharedPreferenceManager.setGestures(
|
||||
direction, "$appName§splitter§${appInfo.applicationInfo.packageName}§splitter§$profile"
|
||||
)
|
||||
requireActivity().supportFragmentManager.popBackStack()
|
||||
}
|
||||
|
||||
|
||||
override fun onItemClick(appInfo: LauncherActivityInfo, profile: Int) {
|
||||
showConfirmationDialog(appInfo, sharedPreferenceManager.getAppName(
|
||||
appInfo.applicationInfo.packageName,
|
||||
profile,
|
||||
requireContext().packageManager.getApplicationLabel(appInfo.applicationInfo)
|
||||
).toString(), profile)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.LauncherActivityInfo
|
||||
import android.os.UserHandle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.ottop.yamlauncher.R
|
||||
import eu.ottop.yamlauncher.utils.UIUtils
|
||||
|
||||
class HiddenAppsAdapter(
|
||||
private val context: Context,
|
||||
private var apps: MutableList<Triple<LauncherActivityInfo, UserHandle, Int>>,
|
||||
private val itemClickListener: OnItemClickListener
|
||||
) :
|
||||
RecyclerView.Adapter<HiddenAppsAdapter.AppViewHolder>() {
|
||||
|
||||
private val sharedPreferenceManager = SharedPreferenceManager(context)
|
||||
private val uiUtils = UIUtils(context)
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(appInfo: LauncherActivityInfo, profile: Int)
|
||||
}
|
||||
|
||||
inner class AppViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val listItem: FrameLayout = itemView.findViewById(R.id.listItem)
|
||||
val textView: TextView = listItem.findViewById(R.id.appName)
|
||||
|
||||
init {
|
||||
|
||||
textView.setOnClickListener {
|
||||
val position = bindingAdapterPosition
|
||||
val app = apps[position].first
|
||||
itemClickListener.onItemClick(app, apps[position].third)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.app_item_layout, parent, false)
|
||||
return AppViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
|
||||
val app = apps[position]
|
||||
|
||||
if (app.third != 0) {
|
||||
holder.textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(context.resources,
|
||||
R.drawable.ic_work_app, null),null,null,null)
|
||||
}
|
||||
else {
|
||||
holder.textView.setCompoundDrawablesWithIntrinsicBounds(ResourcesCompat.getDrawable(context.resources,
|
||||
R.drawable.ic_empty, null),null,null,null)
|
||||
}
|
||||
|
||||
uiUtils.setAppAlignment(holder.textView)
|
||||
|
||||
uiUtils.setAppSize(holder.textView)
|
||||
|
||||
// Separate activity from Main so does not need special update
|
||||
val appInfo = app.first.activityInfo.applicationInfo
|
||||
holder.textView.text = sharedPreferenceManager.getAppName(
|
||||
app.first.applicationInfo.packageName,
|
||||
app.third,
|
||||
context.packageManager.getApplicationLabel(appInfo)
|
||||
)
|
||||
|
||||
holder.textView.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return apps.size
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateApps(newApps: List<Triple<LauncherActivityInfo, UserHandle, Int>>) {
|
||||
apps = newApps.toMutableList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
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.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import eu.ottop.yamlauncher.utils.AppMenuEdgeFactory
|
||||
import eu.ottop.yamlauncher.R
|
||||
import eu.ottop.yamlauncher.utils.AppUtils
|
||||
import eu.ottop.yamlauncher.utils.StringUtils
|
||||
import eu.ottop.yamlauncher.utils.UIUtils
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HiddenAppsFragment : Fragment(), HiddenAppsAdapter.OnItemClickListener {
|
||||
|
||||
private lateinit var sharedPreferenceManager: SharedPreferenceManager
|
||||
private var adapter: HiddenAppsAdapter? = null
|
||||
private var stringUtils = StringUtils()
|
||||
private lateinit var uiUtils: UIUtils
|
||||
private lateinit var appUtils: AppUtils
|
||||
private lateinit var launcherApps: LauncherApps
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_hidden_apps, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
launcherApps = requireContext().getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
||||
uiUtils = UIUtils(requireContext())
|
||||
appUtils = AppUtils(requireContext(), launcherApps)
|
||||
sharedPreferenceManager = SharedPreferenceManager(requireContext())
|
||||
|
||||
lifecycleScope.launch {
|
||||
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.hiddenAppRecycler)
|
||||
val appMenuEdgeFactory = AppMenuEdgeFactory(requireActivity())
|
||||
|
||||
adapter = HiddenAppsAdapter(requireContext(), appUtils.getHiddenApps().toMutableList(), this@HiddenAppsFragment)
|
||||
|
||||
|
||||
recyclerView.edgeEffectFactory = appMenuEdgeFactory
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
recyclerView.scrollToPosition(0)
|
||||
|
||||
val searchView = view.findViewById<TextInputEditText>(R.id.hiddenAppSearch)
|
||||
|
||||
uiUtils.setMenuTitleAlignment(view.findViewById(R.id.hiddenMenuTitle))
|
||||
uiUtils.setSearchAlignment(searchView)
|
||||
uiUtils.setSearchSize(searchView)
|
||||
|
||||
recyclerView.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
|
||||
|
||||
if (bottom - top > oldBottom - oldTop) {
|
||||
searchView.clearFocus()
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
lifecycleScope.launch {
|
||||
filterItems(s.toString())
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
if (sharedPreferenceManager.isAutoKeyboardEnabled()) {
|
||||
val imm =
|
||||
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
searchView.requestFocus()
|
||||
imm.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun filterItems(query: String?) {
|
||||
|
||||
val cleanQuery = stringUtils.cleanString(query)
|
||||
val newFilteredApps = mutableListOf<Triple<LauncherActivityInfo, UserHandle, Int>>()
|
||||
val updatedApps = appUtils.getHiddenApps()
|
||||
|
||||
getFilteredApps(cleanQuery, newFilteredApps, updatedApps)
|
||||
|
||||
applySearch(newFilteredApps)
|
||||
|
||||
}
|
||||
|
||||
private fun getFilteredApps(cleanQuery: String?, newFilteredApps: MutableList<Triple<LauncherActivityInfo, UserHandle, Int>>, updatedApps: List<Triple<LauncherActivityInfo, UserHandle, Int>>) {
|
||||
if (cleanQuery.isNullOrEmpty()) {
|
||||
newFilteredApps.addAll(updatedApps)
|
||||
} else {
|
||||
updatedApps.forEach {
|
||||
val cleanItemText = stringUtils.cleanString(sharedPreferenceManager.getAppName(
|
||||
it.first.applicationInfo.packageName,
|
||||
it.third,
|
||||
requireContext().packageManager.getApplicationLabel(it.first.applicationInfo)
|
||||
).toString())
|
||||
if (cleanItemText != null) {
|
||||
if (cleanItemText.contains(cleanQuery, ignoreCase = true)) {
|
||||
newFilteredApps.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applySearch(newFilteredApps: MutableList<Triple<LauncherActivityInfo, UserHandle, Int>>) {
|
||||
adapter?.updateApps(newFilteredApps)
|
||||
}
|
||||
|
||||
private fun showConfirmationDialog(appInfo: LauncherActivityInfo, appName: String, profile: Int) {
|
||||
AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle("Confirmation")
|
||||
setMessage("Are you sure you want to unhide $appName?")
|
||||
setPositiveButton("Yes") { _, _ ->
|
||||
lifecycleScope.launch {
|
||||
performConfirmedAction(appInfo, profile)
|
||||
}
|
||||
}
|
||||
|
||||
setNegativeButton("Cancel") { _, _ ->
|
||||
}
|
||||
}.create().show()
|
||||
}
|
||||
|
||||
private suspend fun performConfirmedAction(appInfo: LauncherActivityInfo, profile: Int) {
|
||||
sharedPreferenceManager.setAppVisible(appInfo.applicationInfo.packageName, profile)
|
||||
adapter?.updateApps(appUtils.getHiddenApps())
|
||||
}
|
||||
|
||||
override fun onItemClick(appInfo: LauncherActivityInfo, profile: Int) {
|
||||
showConfirmationDialog(appInfo, sharedPreferenceManager.getAppName(
|
||||
appInfo.applicationInfo.packageName,
|
||||
profile,
|
||||
requireContext().packageManager.getApplicationLabel(appInfo.applicationInfo)
|
||||
).toString(), profile)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import eu.ottop.yamlauncher.utils.AppMenuEdgeFactory
|
||||
import eu.ottop.yamlauncher.R
|
||||
import eu.ottop.yamlauncher.utils.WeatherSystem
|
||||
import eu.ottop.yamlauncher.utils.StringUtils
|
||||
import eu.ottop.yamlauncher.utils.UIUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class LocationFragment : Fragment(), LocationListAdapter.OnItemClickListener {
|
||||
|
||||
private var adapter: LocationListAdapter? = null
|
||||
private lateinit var weatherSystem: WeatherSystem
|
||||
private lateinit var sharedPreferenceManager: SharedPreferenceManager
|
||||
private val stringUtils = StringUtils()
|
||||
private lateinit var uiUtils: UIUtils
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_location, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
uiUtils = UIUtils(requireContext())
|
||||
weatherSystem = WeatherSystem(requireContext())
|
||||
sharedPreferenceManager = SharedPreferenceManager(requireContext())
|
||||
|
||||
val searchView = view.findViewById<TextInputEditText>(R.id.locationSearch)
|
||||
|
||||
var locationList = mutableListOf<Map<String, String>>()
|
||||
|
||||
stringUtils.setLink(requireActivity().findViewById(R.id.locationLink), getString(R.string.location_link))
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
locationList = weatherSystem.getSearchedLocations(
|
||||
searchView.text.toString()
|
||||
)
|
||||
}
|
||||
|
||||
adapter = LocationListAdapter(requireContext(), locationList, this)
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.locationRecycler)
|
||||
val appMenuEdgeFactory = AppMenuEdgeFactory(requireActivity())
|
||||
uiUtils.setMenuTitleAlignment(view.findViewById(R.id.locationMenuTitle))
|
||||
uiUtils.setSearchAlignment(searchView)
|
||||
uiUtils.setSearchSize(searchView)
|
||||
|
||||
recyclerView.edgeEffectFactory = appMenuEdgeFactory
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
recyclerView.scrollToPosition(0)
|
||||
|
||||
|
||||
|
||||
recyclerView.addOnLayoutChangeListener { _, _, top, _, bottom, _, oldTop, _, oldBottom ->
|
||||
|
||||
if (bottom - top > oldBottom - oldTop) {
|
||||
searchView.clearFocus()
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
// Filtering is not needed since we are creating a list with data pulled from the Open-Meteo api instead of searching an existing list
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
val locations = weatherSystem.getSearchedLocations(
|
||||
searchView.text.toString()
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter?.updateLocations(locations)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (sharedPreferenceManager.isAutoKeyboardEnabled()) {
|
||||
val imm =
|
||||
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
searchView.requestFocus()
|
||||
imm.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showConfirmationDialog(appName: String?, latitude: String?, longitude: String?) {
|
||||
AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle("Confirmation")
|
||||
setMessage("Are you sure you want to select $appName?")
|
||||
setPositiveButton("Yes") { _, _ ->
|
||||
performConfirmedAction(appName, latitude, longitude)
|
||||
}
|
||||
setNegativeButton("Cancel") { _, _ ->
|
||||
|
||||
}
|
||||
|
||||
}.create().show()
|
||||
}
|
||||
|
||||
private fun performConfirmedAction(appName: String?, latitude: String?, longitude: String?) {
|
||||
sharedPreferenceManager.setWeatherLocation("latitude=${latitude}&longitude=${longitude}", appName)
|
||||
requireActivity().supportFragmentManager.popBackStack()
|
||||
}
|
||||
|
||||
|
||||
override fun onItemClick(name: String?, latitude: String?, longitude: String?) {
|
||||
showConfirmationDialog(name, latitude, longitude)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.ottop.yamlauncher.R
|
||||
import eu.ottop.yamlauncher.utils.UIUtils
|
||||
|
||||
class LocationListAdapter(
|
||||
private val context: Context,
|
||||
private var locations: MutableList<Map<String, String>>,
|
||||
private val itemClickListener: OnItemClickListener
|
||||
) :
|
||||
RecyclerView.Adapter<LocationListAdapter.AppViewHolder>() {
|
||||
|
||||
private val uiUtils = UIUtils(context)
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(name: String?, latitude: String?, longitude: String?)
|
||||
}
|
||||
|
||||
inner class AppViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val listItem: ConstraintLayout = itemView.findViewById(R.id.locationPlace)
|
||||
val textView: TextView = listItem.findViewById(R.id.locationName)
|
||||
val regionText: TextView = listItem.findViewById(R.id.regionName)
|
||||
|
||||
init {
|
||||
|
||||
listItem.setOnClickListener {
|
||||
val position = bindingAdapterPosition
|
||||
val name = locations[position]["name"]
|
||||
val latitude = locations[position]["latitude"]
|
||||
val longitude = locations[position]["longitude"]
|
||||
itemClickListener.onItemClick(name, latitude, longitude)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.location_item_layout, parent, false)
|
||||
return AppViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
|
||||
val location = locations[position]
|
||||
|
||||
uiUtils.setAppAlignment(holder.textView, null, holder.regionText)
|
||||
|
||||
uiUtils.setAppSize(holder.textView, null, holder.regionText)
|
||||
|
||||
holder.textView.text = location["name"]
|
||||
holder.regionText.text = context.getString(R.string.region_text, location["region"], location["country"])
|
||||
|
||||
holder.textView.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return locations.size
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateLocations(newApps: MutableList<Map<String, String>>) {
|
||||
locations.clear()
|
||||
locations = newApps
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import eu.ottop.yamlauncher.R
|
||||
import eu.ottop.yamlauncher.databinding.ActivitySettingsBinding
|
||||
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settingsLayout, SettingsFragment())
|
||||
.commit()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreference
|
||||
import eu.ottop.yamlauncher.R
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
private var manualLocationPref: Preference? = null
|
||||
private var leftSwipePref: Preference? = null
|
||||
private var rightSwipePref: Preference? = null
|
||||
private lateinit var sharedPreferenceManager: SharedPreferenceManager
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||
|
||||
sharedPreferenceManager = SharedPreferenceManager(requireContext())
|
||||
|
||||
val gpsLocationPref = findPreference<SwitchPreference?>("gpsLocation")
|
||||
manualLocationPref = findPreference("manualLocation")
|
||||
leftSwipePref = findPreference("leftSwipeApp")
|
||||
rightSwipePref = findPreference("rightSwipeApp")
|
||||
val aboutPref = findPreference<Preference?>("aboutPage")
|
||||
val hiddenPref = findPreference<Preference?>("hiddenApps")
|
||||
val homePref = findPreference<Preference?>("defaultHome")
|
||||
|
||||
// Only enable manual location when gps location is disabled
|
||||
if (gpsLocationPref != null && manualLocationPref != null) {
|
||||
manualLocationPref?.isEnabled = !gpsLocationPref.isChecked
|
||||
|
||||
gpsLocationPref.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
val isGpsEnabled = newValue as Boolean
|
||||
manualLocationPref?.isEnabled = !isGpsEnabled
|
||||
true
|
||||
}
|
||||
|
||||
manualLocationPref?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settingsLayout, LocationFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
hiddenPref?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settingsLayout, HiddenAppsFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
true }
|
||||
|
||||
leftSwipePref?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settingsLayout, GestureAppsFragment("left"))
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
true }
|
||||
|
||||
rightSwipePref?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settingsLayout, GestureAppsFragment("right"))
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
true }
|
||||
|
||||
aboutPref?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
requireActivity().supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settingsLayout, AboutFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
true }
|
||||
|
||||
homePref?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
val intent = Intent(Settings.ACTION_HOME_SETTINGS)
|
||||
if (intent.resolveActivity(requireContext().packageManager) != null) {
|
||||
startActivity(intent)
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Unable to launch settings", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
true }
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
manualLocationPref?.summary = sharedPreferenceManager.getWeatherRegion()
|
||||
|
||||
leftSwipePref?.summary = sharedPreferenceManager.getGestureName("left")
|
||||
|
||||
rightSwipePref?.summary = sharedPreferenceManager.getGestureName("right")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
package eu.ottop.yamlauncher.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.widget.TextView
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
class SharedPreferenceManager (context: Context) {
|
||||
|
||||
private val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
// General UI
|
||||
fun getBgColor(): Int {
|
||||
return Color.parseColor(preferences.getString("bgColor", "#00000000"))
|
||||
}
|
||||
|
||||
fun getTextColor(): Int {
|
||||
return Color.parseColor(preferences.getString("textColor", "#FFF3F3F3"))
|
||||
}
|
||||
|
||||
fun isBarVisible(): Boolean {
|
||||
return preferences.getBoolean("barVisibility", false)
|
||||
}
|
||||
|
||||
fun getAnimationSpeed(): Long {
|
||||
val animSpeed = preferences.getString("animationSpeed", "200")?.toLong()
|
||||
if (animSpeed != null) {
|
||||
return animSpeed
|
||||
}
|
||||
return 200
|
||||
}
|
||||
|
||||
// Home Screen
|
||||
fun getClockAlignment(): String? {
|
||||
return preferences.getString("clockAlignment", "left")
|
||||
}
|
||||
|
||||
fun getClockSize(): String? {
|
||||
return preferences.getString("clockSize","medium")
|
||||
}
|
||||
|
||||
fun getDateSize(): String? {
|
||||
return preferences.getString("dateSize", "medium")
|
||||
}
|
||||
|
||||
fun setShortcut(textView: TextView, packageName: String, profile: Int) {
|
||||
val editor = preferences.edit()
|
||||
editor.putString("shortcut${textView.id}", "$packageName§splitter§$profile§splitter§${textView.text}")
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
fun getShortcut(textView: TextView): List<String>? {
|
||||
val value = preferences.getString("shortcut${textView.id}", "e§splitter§e")
|
||||
return value?.split("§splitter§")
|
||||
}
|
||||
|
||||
fun getShortcutNumber(): Int? {
|
||||
return preferences.getString("shortcutNo", "4")?.toInt()
|
||||
}
|
||||
|
||||
fun getShortcutAlignment(): String? {
|
||||
return preferences.getString("shortcutAlignment", "left")
|
||||
}
|
||||
|
||||
fun getShortcutSize(): String? {
|
||||
return preferences.getString("shortcutSize", "medium")
|
||||
}
|
||||
|
||||
fun isBatteryEnabled(): Boolean {
|
||||
return preferences.getBoolean("batteryEnabled", false)
|
||||
}
|
||||
|
||||
// Weather
|
||||
fun isWeatherEnabled(): Boolean {
|
||||
return preferences.getBoolean("weatherEnabled", false)
|
||||
}
|
||||
|
||||
fun isWeatherGPS(): Boolean {
|
||||
return preferences.getBoolean("gpsLocation", false)
|
||||
}
|
||||
|
||||
fun setWeatherLocation(location: String, region: String?) {
|
||||
val editor = preferences.edit()
|
||||
editor.putString("location", location)
|
||||
editor.putString("locationRegion", region)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
fun getWeatherLocation(): String? {
|
||||
return preferences.getString("location", "")
|
||||
}
|
||||
|
||||
fun getWeatherRegion(): String? {
|
||||
return preferences.getString("locationRegion", "")
|
||||
}
|
||||
|
||||
fun getTempUnits(): String? {
|
||||
return preferences.getString("tempUnits", "celsius")
|
||||
}
|
||||
|
||||
// Gestures
|
||||
fun setGestures(direction: String, appName: String?) {
|
||||
val editor = preferences.edit()
|
||||
editor.putString("${direction}SwipeApp", appName)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
fun getGestureName(direction: String) : String? {
|
||||
val name = preferences.getString("${direction}SwipeApp", "")?.split("§splitter§")
|
||||
return name?.get(0)
|
||||
}
|
||||
|
||||
fun getGestureInfo(direction: String) : List<String>? {
|
||||
return preferences.getString("${direction}SwipeApp", "")?.split("§splitter§")
|
||||
}
|
||||
|
||||
fun isGestureEnabled(direction: String) : Boolean {
|
||||
return preferences.getBoolean("${direction}Swipe", false)
|
||||
}
|
||||
|
||||
fun isDoubleTapEnabled(): Boolean {
|
||||
return preferences.getBoolean("doubleTap", false)
|
||||
}
|
||||
|
||||
// Application Menu
|
||||
fun getAppAlignment(): String? {
|
||||
return preferences.getString("appMenuAlignment", "left")
|
||||
}
|
||||
|
||||
fun getAppSize(): String? {
|
||||
return preferences.getString("appMenuSize", "medium")
|
||||
}
|
||||
|
||||
fun getSearchAlignment(): String? {
|
||||
return preferences.getString("searchAlignment", "left")
|
||||
}
|
||||
|
||||
fun getSearchSize(): String? {
|
||||
return preferences.getString("searchSize", "medium")
|
||||
}
|
||||
|
||||
fun isAutoKeyboardEnabled(): Boolean {
|
||||
return preferences.getBoolean("autoKeyboard", false)
|
||||
}
|
||||
|
||||
// Hidden Apps
|
||||
fun setAppHidden(packageName: String, profile: Int, hidden: Boolean) {
|
||||
val editor = preferences.edit()
|
||||
editor.putBoolean("hidden$packageName-$profile", hidden)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
fun isAppHidden(packageName: String, profile: Int): Boolean {
|
||||
return preferences.getBoolean("hidden$packageName-$profile", false) // Default to false (visible)
|
||||
}
|
||||
|
||||
fun setAppVisible(packageName: String, profile: Int) {
|
||||
val editor = preferences.edit()
|
||||
editor.remove("hidden$packageName-$profile")
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
//Renaming apps
|
||||
fun setAppName(packageName: String, profile: Int, newName: String) {
|
||||
val editor = preferences.edit()
|
||||
editor.putString("name$packageName-$profile", newName)
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
fun getAppName(packageName: String, profile: Int, appName: CharSequence): CharSequence? {
|
||||
return preferences.getString("name$packageName-$profile", appName.toString())
|
||||
}
|
||||
|
||||
fun resetAppName(packageName: String, profile: Int) {
|
||||
val editor = preferences.edit()
|
||||
editor.remove("name$packageName-$profile")
|
||||
editor.apply()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package eu.ottop.yamlauncher.tasks
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.BatteryManager
|
||||
import eu.ottop.yamlauncher.MainActivity
|
||||
|
||||
class BatteryReceiver(private val activity: MainActivity) : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
intent?.let {
|
||||
val level = it.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
|
||||
val scale = it.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
|
||||
val batteryPct = level * 100 / scale.toFloat()
|
||||
activity.modifyDate("${batteryPct.toInt()}%", 3) // Add battery to the date
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun register(context: Context, activity: MainActivity): BatteryReceiver {
|
||||
val receiver = BatteryReceiver(activity)
|
||||
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
|
||||
context.registerReceiver(receiver, filter)
|
||||
return receiver
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package eu.ottop.yamlauncher.tasks
|
||||
|
||||
import android.accessibilityservice.AccessibilityService
|
||||
import android.content.Intent
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
|
||||
class ScreenLockService : AccessibilityService() {
|
||||
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
|
||||
}
|
||||
|
||||
override fun onInterrupt() {
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
if (intent != null && intent.action == "LOCK_SCREEN") {
|
||||
performLockScreen()
|
||||
}
|
||||
stopSelf()
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
private fun performLockScreen() {
|
||||
performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN)
|
||||
}
|
||||
}
|
||||
149
app/src/main/java/eu/ottop/yamlauncher/utils/Animations.kt
Normal file
149
app/src/main/java/eu/ottop/yamlauncher/utils/Animations.kt
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package eu.ottop.yamlauncher.utils
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.View
|
||||
import eu.ottop.yamlauncher.settings.SharedPreferenceManager
|
||||
|
||||
class Animations (context: Context) {
|
||||
|
||||
private val sharedPreferenceManager = SharedPreferenceManager(context)
|
||||
|
||||
// fadeViewIn and fadeViewOut are for smaller item transitions, such as the action menu
|
||||
|
||||
fun fadeViewIn(view: View) {
|
||||
view.fadeIn()
|
||||
}
|
||||
|
||||
fun fadeViewOut(view: View) {
|
||||
view.fadeOut()
|
||||
}
|
||||
|
||||
fun showHome(homeView: View, appView: View) {
|
||||
appView.slideOutToBottom()
|
||||
homeView.fadeIn()
|
||||
}
|
||||
|
||||
fun showApps(homeView: View, appView: View) {
|
||||
appView.slideInFromBottom()
|
||||
homeView.fadeOut()
|
||||
}
|
||||
|
||||
fun backgroundIn(activity: Activity) {
|
||||
val originalColor = sharedPreferenceManager.getBgColor()
|
||||
|
||||
// Only animate darkness onto the transparent background
|
||||
if (originalColor == Color.parseColor("#00000000")) {
|
||||
val newColor = Color.parseColor("#3F000000")
|
||||
val colorDrawable = ColorDrawable(originalColor)
|
||||
activity.window.setBackgroundDrawable(colorDrawable)
|
||||
|
||||
val backgroundColorAnimator = ValueAnimator.ofObject(ArgbEvaluator(), originalColor, newColor)
|
||||
backgroundColorAnimator.addUpdateListener { animator ->
|
||||
colorDrawable.color = animator.animatedValue as Int
|
||||
}
|
||||
|
||||
val duration = sharedPreferenceManager.getAnimationSpeed()
|
||||
backgroundColorAnimator.duration = duration
|
||||
|
||||
backgroundColorAnimator.start()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fun backgroundOut(activity: Activity) {
|
||||
val newColor = sharedPreferenceManager.getBgColor()
|
||||
|
||||
// Only animate darkness onto the transparent background
|
||||
if (newColor == Color.parseColor("#00000000")) {
|
||||
val originalColor = Color.parseColor("#3F000000")
|
||||
val colorDrawable = ColorDrawable(originalColor)
|
||||
activity.window.setBackgroundDrawable(colorDrawable)
|
||||
|
||||
val backgroundColorAnimator = ValueAnimator.ofObject(ArgbEvaluator(), originalColor, newColor)
|
||||
backgroundColorAnimator.addUpdateListener { animator ->
|
||||
colorDrawable.color = animator.animatedValue as Int
|
||||
}
|
||||
|
||||
val duration = sharedPreferenceManager.getAnimationSpeed()
|
||||
backgroundColorAnimator.duration = duration
|
||||
|
||||
backgroundColorAnimator.start()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
private fun View.slideInFromBottom() {
|
||||
if (visibility != View.VISIBLE) {
|
||||
translationY = height.toFloat()/5
|
||||
scaleY = 1.2f
|
||||
alpha = 0f
|
||||
visibility = View.VISIBLE
|
||||
val duration = sharedPreferenceManager.getAnimationSpeed()
|
||||
|
||||
animate()
|
||||
.translationY(0f)
|
||||
.scaleY(1f)
|
||||
.alpha(1f)
|
||||
.setDuration(duration)
|
||||
.setListener(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.slideOutToBottom() {
|
||||
if (visibility == View.VISIBLE) {
|
||||
val duration = sharedPreferenceManager.getAnimationSpeed()
|
||||
|
||||
animate()
|
||||
.translationY(height.toFloat() / 5)
|
||||
.scaleY(1.2f)
|
||||
.alpha(0f)
|
||||
.setDuration(duration/2)
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
visibility = View.INVISIBLE
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.fadeIn() {
|
||||
if (visibility != View.VISIBLE) {
|
||||
alpha = 0f
|
||||
translationY = -height.toFloat()/100
|
||||
visibility = View.VISIBLE
|
||||
val duration = sharedPreferenceManager.getAnimationSpeed()
|
||||
|
||||
animate()
|
||||
.alpha(1f)
|
||||
.translationY(0f)
|
||||
.setDuration(duration)
|
||||
.setListener(null)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.fadeOut() {
|
||||
if (visibility == View.VISIBLE) {
|
||||
val duration = sharedPreferenceManager.getAnimationSpeed()
|
||||
|
||||
animate()
|
||||
.alpha(0f)
|
||||
.translationY(-height.toFloat()/100)
|
||||
.setDuration(duration/2)
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
visibility = View.GONE
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package eu.ottop.yamlauncher.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.widget.EdgeEffect
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class AppMenuEdgeFactory(private val activity: Activity) : RecyclerView.EdgeEffectFactory() {
|
||||
|
||||
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
|
||||
return AppMenuEdgeEffect(activity)
|
||||
}
|
||||
|
||||
inner class AppMenuEdgeEffect(activity: Activity) : EdgeEffect(activity) {
|
||||
|
||||
//This just speeds up the animation when the scrolling hits the edge so that the app menu can be exited sooner
|
||||
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)
|
||||
}
|
||||
|
||||
override fun onPullDistance(deltaDistance: Float, displacement: Float): Float {
|
||||
return super.onPullDistance(deltaDistance * animationSpeedFactor, displacement)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package eu.ottop.yamlauncher.utils
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.ottop.yamlauncher.MainActivity
|
||||
|
||||
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 the user scrolls up when already on top, go back to home. Only if the keyboard isn't open, though
|
||||
if (overscroll < 0 && (firstVisibleItemPosition == 0 || firstVisibleItemPosition < 0) && scrollStarted) {
|
||||
activity.backToHome()
|
||||
}
|
||||
|
||||
if (scrollStarted) {
|
||||
scrollStarted = false
|
||||
}
|
||||
|
||||
return scrollRange
|
||||
}
|
||||
|
||||
}
|
||||
90
app/src/main/java/eu/ottop/yamlauncher/utils/AppUtils.kt
Normal file
90
app/src/main/java/eu/ottop/yamlauncher/utils/AppUtils.kt
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
package eu.ottop.yamlauncher.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.LauncherActivityInfo
|
||||
import android.content.pm.LauncherApps
|
||||
import android.os.UserHandle
|
||||
import android.widget.Toast
|
||||
import eu.ottop.yamlauncher.settings.SharedPreferenceManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class AppUtils(private val context: Context, private val launcherApps: LauncherApps) {
|
||||
|
||||
private val sharedPreferenceManager = SharedPreferenceManager(context)
|
||||
|
||||
suspend fun getInstalledApps(): List<Triple<LauncherActivityInfo, UserHandle, Int>> {
|
||||
val allApps = mutableListOf<Triple<LauncherActivityInfo, UserHandle, Int>>()
|
||||
var sortedApps = listOf<Triple<LauncherActivityInfo, UserHandle, Int>>()
|
||||
withContext(Dispatchers.Default) {
|
||||
for (i in launcherApps.profiles.indices) { // Check apps on both, normal and work profiles
|
||||
launcherApps.getActivityList(null, launcherApps.profiles[i]).forEach { app ->
|
||||
if (!sharedPreferenceManager.isAppHidden( // Only include the app if it isn't set as hidden
|
||||
app.applicationInfo.packageName,
|
||||
i
|
||||
) && app.applicationInfo.packageName != context.applicationInfo.packageName // Hide the launcher itself
|
||||
) {
|
||||
allApps.add(Triple(app, launcherApps.profiles[i], i)) // The i variable gets used to determine whether an app is in the personal profile or work profile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort apps by name
|
||||
sortedApps = allApps.sortedBy {
|
||||
sharedPreferenceManager.getAppName(
|
||||
it.first.applicationInfo.packageName,
|
||||
it.third,
|
||||
context.packageManager.getApplicationLabel(it.first.applicationInfo)
|
||||
).toString().lowercase()
|
||||
}
|
||||
}
|
||||
return sortedApps
|
||||
|
||||
}
|
||||
|
||||
// Get hidden apps for the hidden apps settings
|
||||
suspend fun getHiddenApps(): List<Triple<LauncherActivityInfo, UserHandle, Int>> {
|
||||
val allApps = mutableListOf<Triple<LauncherActivityInfo, UserHandle, Int>>()
|
||||
var sortedApps = listOf<Triple<LauncherActivityInfo, UserHandle, Int>>()
|
||||
withContext(Dispatchers.Default) {
|
||||
for (i in launcherApps.profiles.indices) {
|
||||
launcherApps.getActivityList(null, launcherApps.profiles[i]).forEach { app ->
|
||||
if (sharedPreferenceManager.isAppHidden(app.applicationInfo.packageName, i)) {
|
||||
allApps.add(Triple(app, launcherApps.profiles[i], i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Sort apps by name
|
||||
sortedApps = allApps.sortedBy {
|
||||
sharedPreferenceManager.getAppName(
|
||||
it.first.applicationInfo.packageName,
|
||||
it.third,
|
||||
context.packageManager.getApplicationLabel(it.first.applicationInfo)
|
||||
).toString().lowercase()
|
||||
}
|
||||
}
|
||||
return sortedApps
|
||||
}
|
||||
|
||||
fun getAppInfo(
|
||||
packageName: String,
|
||||
profile: Int
|
||||
): ApplicationInfo? {
|
||||
return try {
|
||||
launcherApps.getApplicationInfo(packageName, 0, launcherApps.profiles[profile])
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun launchApp(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(context, "Cannot launch app", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue