Implemented #14 and #15: favorite apps and hidden apps

This commit is contained in:
Josia Pietsch 2024-09-23 18:45:20 +02:00
parent e4b1bccf85
commit 662efd4ecb
Signed by: jrpie
GPG key ID: E70B571D66986A2D
19 changed files with 372 additions and 89 deletions

View file

@ -8,15 +8,6 @@
<package name="io.ktor" alias="false" withSubpackages="true" /> <package name="io.ktor" alias="false" withSubpackages="true" />
</value> </value>
</option> </option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">

View file

@ -41,8 +41,6 @@ android {
} }
buildTypes { buildTypes {
release { release {
// minifyEnabled true // minifyEnabled true

View file

@ -32,6 +32,12 @@ enum class LauncherAction(
R.drawable.baseline_menu_24, R.drawable.baseline_menu_24,
::openAppsList ::openAppsList
), ),
CHOOSE_FROM_FAVORITES(
"launcher:chooseFromFavorites",
R.string.list_other_list_favorites,
R.drawable.baseline_favorite_24,
{ context -> openAppsList(context, true)}
),
VOLUME_UP( VOLUME_UP(
"launcher:volumeUp", "launcher:volumeUp",
R.string.list_other_volume_up, R.string.list_other_volume_up,
@ -174,12 +180,15 @@ private fun openSettings(context: Context) {
context.startActivity(Intent(context, SettingsActivity::class.java)) context.startActivity(Intent(context, SettingsActivity::class.java))
} }
private fun openAppsList(context: Context) { fun openAppsList(context: Context, favorite: Boolean = false, hidden: Boolean = false) {
val intent = Intent(context, ListActivity::class.java) val intent = Intent(context, ListActivity::class.java)
intent.putExtra("intention", ListActivity.ListActivityIntention.VIEW.toString()) intent.putExtra("intention", ListActivity.ListActivityIntention.VIEW.toString())
intent.putExtra("favorite", favorite)
intent.putExtra("hidden", hidden)
context.startActivity(intent) context.startActivity(intent)
} }

View file

@ -0,0 +1,55 @@
package de.jrpie.android.launcher.apps
import de.jrpie.android.launcher.preferences.LauncherPreferences
import java.util.*
import kotlin.text.Regex.Companion.escapeReplacement
class AppFilter(
var search: String,
var showOnlyFavorites: Boolean = false,
var showOnlyHidden: Boolean = false
) {
operator fun invoke(apps: List<DetailedAppInfo>): List<DetailedAppInfo> {
var apps = apps
val hidden = LauncherPreferences.apps().hidden() ?: setOf()
apps = apps.filter { info -> !showOnlyHidden.xor(hidden.contains(info.app)) }
if (showOnlyFavorites) {
val favorites = LauncherPreferences.apps().favorites() ?: setOf()
apps = apps.filter { info -> favorites.contains(info.app) }
}
// normalize text for search
var allowedSpecialCharacters = search
.lowercase(Locale.ROOT)
.toCharArray()
.distinct()
.filter { c -> !c.isLetter() }
.map { c -> escapeReplacement(c.toString()) }
.fold("") { x, y -> x + y }
var disallowedCharsRegex = "[^\\p{L}$allowedSpecialCharacters]".toRegex()
fun normalize(text: String): String {
return text.lowercase(Locale.ROOT).replace(disallowedCharsRegex, "")
}
if (search.isEmpty()) {
return apps;
} else {
val r: MutableList<DetailedAppInfo> = ArrayList()
val appsSecondary: MutableList<DetailedAppInfo> = ArrayList()
val normalizedText: String = normalize(search)
for (item in apps) {
val itemLabel: String = normalize(item.label.toString())
if (itemLabel.startsWith(normalizedText)) {
r.add(item)
} else if (itemLabel.contains(normalizedText)) {
appsSecondary.add(item)
}
}
r.addAll(appsSecondary)
return r;
}
}
}

View file

@ -17,6 +17,17 @@ class AppInfo(val packageName: CharSequence, val user: Int = INVALID_USER) {
return "$packageName;$u" return "$packageName;$u"
} }
override fun equals(other: Any?): Boolean {
if(other is AppInfo) {
return other.user == user && other.packageName == packageName
}
return super.equals(other)
}
override fun hashCode(): Int {
return packageName.hashCode()
}
fun getLauncherActivityInfo( fun getLauncherActivityInfo(
context: Context context: Context
): LauncherActivityInfo? { ): LauncherActivityInfo? {

View file

@ -1,12 +1,18 @@
package de.jrpie.android.launcher.preferences; package de.jrpie.android.launcher.preferences;
import java.util.HashSet;
import java.util.Set;
import de.jrpie.android.launcher.R; import de.jrpie.android.launcher.R;
import de.jrpie.android.launcher.apps.AppInfo;
import de.jrpie.android.launcher.preferences.theme.Background; import de.jrpie.android.launcher.preferences.theme.Background;
import de.jrpie.android.launcher.preferences.theme.ColorTheme; import de.jrpie.android.launcher.preferences.theme.ColorTheme;
import de.jrpie.android.launcher.preferences.theme.Font; import de.jrpie.android.launcher.preferences.theme.Font;
import eu.jonahbauer.android.preference.annotations.Preference; import eu.jonahbauer.android.preference.annotations.Preference;
import eu.jonahbauer.android.preference.annotations.PreferenceGroup; import eu.jonahbauer.android.preference.annotations.PreferenceGroup;
import eu.jonahbauer.android.preference.annotations.Preferences; import eu.jonahbauer.android.preference.annotations.Preferences;
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializationException;
import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer;
@Preferences( @Preferences(
name = "de.jrpie.android.launcher.preferences.LauncherPreferences", name = "de.jrpie.android.launcher.preferences.LauncherPreferences",
@ -18,6 +24,10 @@ import eu.jonahbauer.android.preference.annotations.Preferences;
@Preference(name = "started_time", type = long.class), @Preference(name = "started_time", type = long.class),
@Preference(name = "version_code", type = int.class, defaultValue = "-1"), @Preference(name = "version_code", type = int.class, defaultValue = "-1"),
}), }),
@PreferenceGroup(name = "apps", prefix = "settings_apps_", suffix = "_key", value = {
@Preference(name = "favorites", type = Set.class, serializer = LauncherPreferences$Config.AppInfoSetSerializer.class),
@Preference(name = "hidden", type = Set.class, serializer = LauncherPreferences$Config.AppInfoSetSerializer.class),
}),
@PreferenceGroup(name = "gestures", prefix = "settings_gesture_", suffix = "_key", value = { @PreferenceGroup(name = "gestures", prefix = "settings_gesture_", suffix = "_key", value = {
}), }),
@PreferenceGroup(name = "general", prefix = "settings_general_", suffix = "_key", value = { @PreferenceGroup(name = "general", prefix = "settings_general_", suffix = "_key", value = {
@ -53,4 +63,32 @@ import eu.jonahbauer.android.preference.annotations.Preferences;
}), }),
}) })
public final class LauncherPreferences$Config { public final class LauncherPreferences$Config {
public static class AppInfoSetSerializer implements PreferenceSerializer<Set<AppInfo>, Set<String>> {
@Override
public Set<String> serialize(Set<AppInfo> value) throws PreferenceSerializationException {
if (value == null) return null;
var serialized = new HashSet<String>(value.size());
for (var app : value) {
serialized.add(app.serialize());
}
return serialized;
}
@Override
public Set<AppInfo> deserialize(Set<String> value) throws PreferenceSerializationException {
if (value == null) return null;
var deserialized = new HashSet<AppInfo>(value.size());
for (var s : value) {
deserialized.add(AppInfo.Companion.deserialize(s));
}
return deserialized;
}
}
} }

View file

@ -27,6 +27,8 @@ import de.jrpie.android.launcher.ui.list.other.ListFragmentOther
// TODO: Better solution for this intercommunication functionality (used in list-fragments) // TODO: Better solution for this intercommunication functionality (used in list-fragments)
var intention = ListActivity.ListActivityIntention.VIEW var intention = ListActivity.ListActivityIntention.VIEW
var showFavorites = false
var showHidden = false
var forGesture: String? = null var forGesture: String? = null
/** /**
@ -45,9 +47,24 @@ class ListActivity : AppCompatActivity(), UIObject {
PICK(R.string.list_title_pick) /* choose app or action to associate to a gesture */ PICK(R.string.list_title_pick) /* choose app or action to associate to a gesture */
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// get info about which action this activity is open for
intent.extras?.let { bundle ->
intention = bundle.getString("intention")
?.let { ListActivityIntention.valueOf(it) }
?: ListActivityIntention.VIEW
showFavorites = bundle.getBoolean("favorite") ?: false
showHidden = bundle.getBoolean("hidden") ?: false
if (intention != ListActivityIntention.VIEW)
forGesture = bundle.getString("forGesture")
}
// Initialise layout // Initialise layout
binding = ListBinding.inflate(layoutInflater) binding = ListBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@ -105,6 +122,23 @@ class ListActivity : AppCompatActivity(), UIObject {
} }
} }
fun updateTitle() {
var titleResource = intention.titleResource
if (intention == ListActivityIntention.VIEW) {
titleResource = if (showHidden) {
R.string.list_title_hidden
} else if (showFavorites) {
R.string.list_title_favorite
} else {
R.string.list_title_view
}
}
binding.listHeading.text = getString(titleResource)
}
override fun getTheme(): Resources.Theme { override fun getTheme(): Resources.Theme {
return modifyTheme(super.getTheme()) return modifyTheme(super.getTheme())
} }
@ -114,22 +148,13 @@ class ListActivity : AppCompatActivity(), UIObject {
} }
override fun adjustLayout() { override fun adjustLayout() {
// get info about which action this activity is open for
intent.extras?.let { bundle ->
intention = bundle.getString("intention")
?.let { ListActivityIntention.valueOf(it) }
?: ListActivityIntention.VIEW
if (intention != ListActivityIntention.VIEW)
forGesture = bundle.getString("forGesture")
}
// Hide tabs for the "view" action // Hide tabs for the "view" action
if (intention == ListActivityIntention.VIEW) { if (intention == ListActivityIntention.VIEW) {
binding.listTabs.visibility = View.GONE binding.listTabs.visibility = View.GONE
} }
binding.listHeading.text = getString(intention.titleResource) updateTitle()
val sectionsPagerAdapter = ListSectionsPagerAdapter(this, supportFragmentManager) val sectionsPagerAdapter = ListSectionsPagerAdapter(this, supportFragmentManager)
val viewPager: ViewPager = findViewById(R.id.list_viewpager) val viewPager: ViewPager = findViewById(R.id.list_viewpager)

View file

@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
import android.os.AsyncTask import android.os.AsyncTask
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -12,9 +13,11 @@ import android.widget.ImageView
import android.widget.PopupMenu import android.widget.PopupMenu
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import de.jrpie.android.launcher.R import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.REQUEST_CHOOSE_APP import de.jrpie.android.launcher.REQUEST_CHOOSE_APP
import de.jrpie.android.launcher.actions.AppAction import de.jrpie.android.launcher.actions.AppAction
import de.jrpie.android.launcher.apps.AppFilter
import de.jrpie.android.launcher.apps.AppInfo import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.DetailedAppInfo import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.appsList import de.jrpie.android.launcher.appsList
@ -24,8 +27,6 @@ import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.transformGrayscale import de.jrpie.android.launcher.transformGrayscale
import de.jrpie.android.launcher.ui.list.ListActivity import de.jrpie.android.launcher.ui.list.ListActivity
import de.jrpie.android.launcher.uninstallApp import de.jrpie.android.launcher.uninstallApp
import java.util.*
import kotlin.text.Regex.Companion.escapeReplacement
/** /**
* A [RecyclerView] (efficient scrollable list) containing all apps on the users device. * A [RecyclerView] (efficient scrollable list) containing all apps on the users device.
@ -37,13 +38,16 @@ import kotlin.text.Regex.Companion.escapeReplacement
*/ */
class AppsRecyclerAdapter( class AppsRecyclerAdapter(
val activity: Activity, val activity: Activity,
val root: View,
private val intention: ListActivity.ListActivityIntention private val intention: ListActivity.ListActivityIntention
= ListActivity.ListActivityIntention.VIEW, = ListActivity.ListActivityIntention.VIEW,
private val forGesture: String? = "" private val forGesture: String? = "",
private var appFilter: AppFilter = AppFilter("")
) : ) :
RecyclerView.Adapter<AppsRecyclerAdapter.ViewHolder>() { RecyclerView.Adapter<AppsRecyclerAdapter.ViewHolder>() {
private val appsListDisplayed: MutableList<DetailedAppInfo> private val appsListDisplayed: MutableList<DetailedAppInfo> = mutableListOf()
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
View.OnClickListener { View.OnClickListener {
@ -61,6 +65,7 @@ class AppsRecyclerAdapter(
} }
} }
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) { override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
val appLabel = appsListDisplayed[i].label.toString() val appLabel = appsListDisplayed[i].label.toString()
val appIcon = appsListDisplayed[i].icon val appIcon = appsListDisplayed[i].icon
@ -106,6 +111,15 @@ class AppsRecyclerAdapter(
popup.menu.findItem(R.id.app_menu_delete).setVisible(false) popup.menu.findItem(R.id.app_menu_delete).setVisible(false)
} }
if (LauncherPreferences.apps().hidden()?.contains(appInfo.app) == true) {
popup.menu.findItem(R.id.app_menu_hidden).setTitle(R.string.list_app_hidden_remove)
}
if (LauncherPreferences.apps().favorites()?.contains(appInfo.app) == true) {
popup.menu.findItem(R.id.app_menu_favorite).setTitle(R.string.list_app_favorite_remove)
}
popup.setOnMenuItemClickListener { popup.setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.app_menu_delete -> { R.id.app_menu_delete -> {
@ -118,6 +132,53 @@ class AppsRecyclerAdapter(
true true
} }
R.id.app_menu_favorite -> {
var favorites: MutableSet<AppInfo> =
LauncherPreferences.apps().favorites() ?: mutableSetOf()
Log.i("LAUNCHER", favorites.size.toString())
for (app in favorites) {
Log.i("LAUNCHER", app.serialize())
}
if (favorites.contains(appInfo.app)) {
favorites.remove(appInfo.app)
Log.i("LAUNCHER", "Removing " + appInfo.app.serialize() + " from favorites.")
} else {
Log.i("LAUNCHER", "Adding " + appInfo.app.serialize() + " to favorites.")
favorites.add(appInfo.app)
}
Log.i("LAUNCHER", favorites.size.toString())
for (app in favorites) {
Log.i("LAUNCHER", app.serialize())
}
LauncherPreferences.apps().favorites(favorites)
true
}
R.id.app_menu_hidden -> {
var hidden: MutableSet<AppInfo> =
LauncherPreferences.apps().hidden() ?: mutableSetOf()
if (hidden.contains(appInfo.app)) {
hidden.remove(appInfo.app)
} else {
hidden.add(appInfo.app)
Snackbar.make(root, R.string.snackbar_app_hidden, Snackbar.LENGTH_LONG)
.setAction(R.string.undo) {
LauncherPreferences.apps().hidden(
LauncherPreferences.apps().hidden().minus(appInfo.app)
)
}.show()
}
LauncherPreferences.apps().hidden(hidden)
true
}
else -> false else -> false
} }
} }
@ -144,9 +205,8 @@ class AppsRecyclerAdapter(
AsyncTask.execute { loadApps(activity.packageManager, activity) } AsyncTask.execute { loadApps(activity.packageManager, activity) }
notifyDataSetChanged() notifyDataSetChanged()
} }
updateAppsList()
appsListDisplayed = ArrayList()
appsListDisplayed.addAll(appsList)
} }
fun selectItem(pos: Int, rect: Rect = Rect()) { fun selectItem(pos: Int, rect: Rect = Rect()) {
@ -169,42 +229,13 @@ class AppsRecyclerAdapter(
} }
} }
/** fun updateAppsList(triggerAutoLaunch: Boolean = false) {
* The function [filter] is used to search elements within this [RecyclerView].
*/
fun filter(text: String) {
// normalize text for search
var allowedSpecialCharacters = text
.lowercase(Locale.ROOT)
.toCharArray()
.distinct()
.filter { c -> !c.isLetter() }
.map { c -> escapeReplacement(c.toString()) }
.fold("") { x, y -> x + y }
var disallowedCharsRegex = "[^\\p{L}$allowedSpecialCharacters]".toRegex()
fun normalize(text: String): String {
return text.lowercase(Locale.ROOT).replace(disallowedCharsRegex, "")
}
appsListDisplayed.clear() appsListDisplayed.clear()
if (text.isEmpty()) { appsListDisplayed.addAll(appFilter(appsList))
appsListDisplayed.addAll(appsList)
} else {
val appsSecondary: MutableList<DetailedAppInfo> = ArrayList()
val normalizedText: String = normalize(text)
for (item in appsList) {
val itemLabel: String = normalize(item.label.toString())
if (itemLabel.startsWith(normalizedText)) { if (triggerAutoLaunch &&
appsListDisplayed.add(item) appsListDisplayed.size == 1
} else if (itemLabel.contains(normalizedText)) { && intention == ListActivity.ListActivityIntention.VIEW
appsSecondary.add(item)
}
}
appsListDisplayed.addAll(appsSecondary)
}
if (appsListDisplayed.size == 1 && intention == ListActivity.ListActivityIntention.VIEW
&& LauncherPreferences.functionality().searchAutoLaunch() && LauncherPreferences.functionality().searchAutoLaunch()
) { ) {
val info = appsListDisplayed[0] val info = appsListDisplayed[0]
@ -217,4 +248,23 @@ class AppsRecyclerAdapter(
notifyDataSetChanged() notifyDataSetChanged()
} }
/**
* The function [setSearchString] is used to search elements within this [RecyclerView].
*/
fun setSearchString(search: String) {
appFilter.search = search
updateAppsList(true)
}
fun setShowOnlyFavorites(show: Boolean) {
appFilter.showOnlyFavorites = show
updateAppsList()
}
fun setShowHiddenApps(show: Boolean) {
appFilter.showOnlyHidden = show
updateAppsList()
}
} }

View file

@ -1,11 +1,13 @@
package de.jrpie.android.launcher.ui.list.apps package de.jrpie.android.launcher.ui.list.apps
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import de.jrpie.android.launcher.apps.AppFilter
import de.jrpie.android.launcher.databinding.ListAppsBinding import de.jrpie.android.launcher.databinding.ListAppsBinding
import de.jrpie.android.launcher.openSoftKeyboard import de.jrpie.android.launcher.openSoftKeyboard
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
@ -13,6 +15,8 @@ import de.jrpie.android.launcher.ui.UIObject
import de.jrpie.android.launcher.ui.list.ListActivity import de.jrpie.android.launcher.ui.list.ListActivity
import de.jrpie.android.launcher.ui.list.forGesture import de.jrpie.android.launcher.ui.list.forGesture
import de.jrpie.android.launcher.ui.list.intention import de.jrpie.android.launcher.ui.list.intention
import de.jrpie.android.launcher.ui.list.showFavorites
import de.jrpie.android.launcher.ui.list.showHidden
/** /**
@ -22,6 +26,12 @@ import de.jrpie.android.launcher.ui.list.intention
*/ */
class ListFragmentApps : Fragment(), UIObject { class ListFragmentApps : Fragment(), UIObject {
private lateinit var binding: ListAppsBinding private lateinit var binding: ListAppsBinding
private lateinit var appsRViewAdapter: AppsRecyclerAdapter
private var sharedPreferencesListener =
SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
appsRViewAdapter.updateAppsList()
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@ -34,13 +44,28 @@ class ListFragmentApps : Fragment(), UIObject {
override fun onStart() { override fun onStart() {
super<Fragment>.onStart() super<Fragment>.onStart()
super<UIObject>.onStart() super<UIObject>.onStart()
LauncherPreferences.getSharedPreferences()
.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
binding.listAppsCheckBoxFavorites.isChecked = showFavorites
} }
override fun onStop() {
super.onStop()
LauncherPreferences.getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
}
override fun setOnClicks() {} override fun setOnClicks() {}
override fun adjustLayout() { override fun adjustLayout() {
val appsRViewAdapter = AppsRecyclerAdapter(requireActivity(), intention, forGesture) appsRViewAdapter =
AppsRecyclerAdapter(
requireActivity(), binding.root, intention, forGesture,
appFilter = AppFilter("", showOnlyFavorites = showFavorites, showOnlyHidden = showHidden)
)
// set up the list / recycler // set up the list / recycler
binding.listAppsRview.apply { binding.listAppsRview.apply {
@ -54,17 +79,23 @@ class ListFragmentApps : Fragment(), UIObject {
androidx.appcompat.widget.SearchView.OnQueryTextListener { androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
appsRViewAdapter.filter(query) appsRViewAdapter.setSearchString(query)
appsRViewAdapter.selectItem(0) appsRViewAdapter.selectItem(0)
return true return true
} }
override fun onQueryTextChange(newText: String): Boolean { override fun onQueryTextChange(newText: String): Boolean {
appsRViewAdapter.filter(newText) appsRViewAdapter.setSearchString(newText)
return false return false
} }
}) })
binding.listAppsCheckBoxFavorites.setOnClickListener {
showFavorites = binding.listAppsCheckBoxFavorites.isChecked
appsRViewAdapter.setShowOnlyFavorites(showFavorites)
(activity as? ListActivity)?.updateTitle()
}
if (intention == ListActivity.ListActivityIntention.VIEW if (intention == ListActivity.ListActivityIntention.VIEW
&& LauncherPreferences.functionality().searchAutoOpenKeyboard() && LauncherPreferences.functionality().searchAutoOpenKeyboard()
) { ) {

View file

@ -5,6 +5,7 @@ import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import de.jrpie.android.launcher.R import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.actions.openAppsList
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.setDefaultHomeScreen import de.jrpie.android.launcher.setDefaultHomeScreen
@ -65,6 +66,14 @@ class SettingsFragmentLauncher : PreferenceFragmentCompat() {
setDefaultHomeScreen(requireContext(), checkDefault = false) setDefaultHomeScreen(requireContext(), checkDefault = false)
true true
} }
val hiddenApps = findPreference<androidx.preference.Preference>(
LauncherPreferences.apps().keys().hidden()
)
hiddenApps?.setOnPreferenceClickListener {
openAppsList(requireContext(), favorite = false, hidden = true)
true
}
updateVisibility() updateVisibility()
} }
} }

View file

@ -1,5 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
<path android:fillColor="@android:color/white" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/> android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:textColor"
android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z" />
</vector> </vector>

View file

@ -1,5 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
<path android:fillColor="@android:color/white" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/> android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:textColor"
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z" />
</vector> </vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/baseline_favorite_24" android:state_checked="true" />
<item android:drawable="@drawable/baseline_favorite_border_24" android:state_checked="false" />
</selector>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/list_apps_container" android:id="@+id/list_apps_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -17,21 +16,41 @@
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:layout_weight="1" android:layout_weight="1"
android:fadeScrollbars="false"
android:fastScrollAlwaysVisible="true"
android:fastScrollEnabled="true"
android:scrollbars="vertical"> android:scrollbars="vertical">
<!--android:scrollbars="vertical"-->
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
<androidx.appcompat.widget.SearchView <LinearLayout
android:id="@+id/list_apps_searchview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8sp" android:orientation="horizontal"
android:layout_weight="0" android:layout_margin="8dp"
android:iconifiedByDefault="false" >
app:iconifiedByDefault="false"
app:queryHint="@string/list_apps_search_hint" <androidx.appcompat.widget.SearchView
app:searchHintIcon="@drawable/baseline_search_24" android:id="@+id/list_apps_searchview"
app:searchIcon="@drawable/baseline_search_24" /> android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:iconifiedByDefault="false"
app:iconifiedByDefault="false"
app:queryHint="@string/list_apps_search_hint"
app:searchHintIcon="@drawable/baseline_search_24"
app:searchIcon="@drawable/baseline_search_24" />
<CheckBox
android:id="@+id/list_apps_check_box_favorites"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0"
android:layout_marginEnd="16dp"
android:button="@drawable/checkbox_favorite" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tutorial_finish_container" android:id="@+id/tutorial_finish_container"
android:paddingLeft="32sp" android:paddingLeft="32sp"

View file

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/app_menu_delete"
android:title="@string/list_app_delete" />
<item android:id="@+id/app_menu_info" <item android:id="@+id/app_menu_info"
android:title="@string/list_app_info" /> android:title="@string/list_app_info" />
<item android:id="@+id/app_menu_favorite"
android:title="@string/list_app_favorite_add" />
<item android:id="@+id/app_menu_hidden"
android:title="@string/list_app_hidden_add" />
<item android:id="@+id/app_menu_delete"
android:title="@string/list_app_delete" />
</menu> </menu>

View file

@ -9,7 +9,10 @@
<string name="settings_internal_started_key" translatable="false">internal.started_before</string> <string name="settings_internal_started_key" translatable="false">internal.started_before</string>
<string name="settings_internal_started_time_key" translatable="false">internal.first_startup</string> <string name="settings_internal_started_time_key" translatable="false">internal.first_startup</string>
<string name="settings_internal_version_code_key" translatable="false">internal.version_code</string> <string name="settings_internal_version_code_key" translatable="false">internal.version_code</string>
<string name="settings_general_choose_home_screen_key" translatable="false">general.select_launcher</string> <string name="settings_apps_favorites_key" translatable="false">apps.favorites</string>
<string name="settings_apps_hidden_key" translatable="false">apps.hidden</string>
<string name="settings_general_choose_home_screen_key" translatable="false">general.select_launcher</string>
<!-- <!--
- -
- Settings : Gestures - Settings : Gestures

View file

@ -114,6 +114,9 @@
<string name="settings_launcher_sensitivity">Sensitivity</string> <string name="settings_launcher_sensitivity">Sensitivity</string>
<string name="settings_launcher_section_apps">Apps</string>
<string name="settings_apps_hidden">Hidden apps</string>
<!-- <!--
- -
- Settings : Meta - Settings : Meta
@ -144,6 +147,8 @@
- -
--> -->
<string name="list_title_view">All Apps</string> <string name="list_title_view">All Apps</string>
<string name="list_title_favorite">Favorite Apps</string>
<string name="list_title_hidden">Hidden Apps</string>
<string name="list_title_pick">Choose App</string> <string name="list_title_pick">Choose App</string>
<string name="list_tab_app">Apps</string> <string name="list_tab_app">Apps</string>
@ -151,6 +156,10 @@
<string name="list_app_delete">Uninstall</string> <string name="list_app_delete">Uninstall</string>
<string name="list_app_info">App Info</string> <string name="list_app_info">App Info</string>
<string name="list_app_favorite_add">Add to favorites</string>
<string name="list_app_favorite_remove">Remove from favorites</string>
<string name="list_app_hidden_add">Hide</string>
<string name="list_app_hidden_remove">Show</string>
<string name="list_removed">Removed the selected application</string> <string name="list_removed">Removed the selected application</string>
<string name="list_not_removed">Unable to remove application</string> <string name="list_not_removed">Unable to remove application</string>
@ -159,6 +168,7 @@
<string name="list_other_settings">Launcher Settings</string> <string name="list_other_settings">Launcher Settings</string>
<string name="list_other_list">All Applications</string> <string name="list_other_list">All Applications</string>
<string name="list_other_list_favorites">Favorite Applications</string>
<string name="list_other_volume_up">Music: Louder</string> <string name="list_other_volume_up">Music: Louder</string>
<string name="list_other_volume_down">Music: Quieter</string> <string name="list_other_volume_down">Music: Quieter</string>
<string name="list_other_track_next">Music: Next</string> <string name="list_other_track_next">Music: Next</string>
@ -195,4 +205,6 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="ic_menu_alt">More options</string> <string name="ic_menu_alt">More options</string>
<string name="alert_cant_expand_notifications_panel">Error: Can\'t expand status bar.\nThis action is using functionality that is not part of the published Android API. Unfortunately, it does not seem to work on your device.</string> <string name="alert_cant_expand_notifications_panel">Error: Can\'t expand status bar.\nThis action is using functionality that is not part of the published Android API. Unfortunately, it does not seem to work on your device.</string>
<string name="snackbar_app_hidden">App hidden. You can make it visible again in settings.</string>
<string name="undo">Undo</string>
</resources> </resources>

View file

@ -97,6 +97,17 @@
android:title="@string/settings_enabled_gestures_edge_swipe"/> android:title="@string/settings_enabled_gestures_edge_swipe"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/settings_launcher_section_apps"
app:allowDividerAbove="false">
<Preference
android:key="@string/settings_apps_hidden_key"
android:title="@string/settings_apps_hidden"
/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/settings_launcher_section_display" android:title="@string/settings_launcher_section_display"