diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 3cc336b..feb0c69 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -8,15 +8,6 @@
-
diff --git a/app/build.gradle b/app/build.gradle
index 8db8817..b6a1b7c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -41,8 +41,6 @@ android {
}
-
-
buildTypes {
release {
// minifyEnabled true
diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt
index 231ae46..a3acbb7 100644
--- a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt
@@ -32,6 +32,12 @@ enum class LauncherAction(
R.drawable.baseline_menu_24,
::openAppsList
),
+ CHOOSE_FROM_FAVORITES(
+ "launcher:chooseFromFavorites",
+ R.string.list_other_list_favorites,
+ R.drawable.baseline_favorite_24,
+ { context -> openAppsList(context, true)}
+ ),
VOLUME_UP(
"launcher:volumeUp",
R.string.list_other_volume_up,
@@ -174,12 +180,15 @@ private fun openSettings(context: Context) {
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)
intent.putExtra("intention", ListActivity.ListActivityIntention.VIEW.toString())
+ intent.putExtra("favorite", favorite)
+ intent.putExtra("hidden", hidden)
context.startActivity(intent)
}
+
diff --git a/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt b/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt
new file mode 100644
index 0000000..a0c0490
--- /dev/null
+++ b/app/src/main/java/de/jrpie/android/launcher/apps/AppFilter.kt
@@ -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): List {
+ 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 = ArrayList()
+ val appsSecondary: MutableList = 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/jrpie/android/launcher/apps/AppInfo.kt b/app/src/main/java/de/jrpie/android/launcher/apps/AppInfo.kt
index 2586cfe..9a73339 100644
--- a/app/src/main/java/de/jrpie/android/launcher/apps/AppInfo.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/apps/AppInfo.kt
@@ -17,6 +17,17 @@ class AppInfo(val packageName: CharSequence, val user: Int = INVALID_USER) {
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(
context: Context
): LauncherActivityInfo? {
diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java
index 678f8d0..4c25b6e 100644
--- a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java
+++ b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java
@@ -1,12 +1,18 @@
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.apps.AppInfo;
import de.jrpie.android.launcher.preferences.theme.Background;
import de.jrpie.android.launcher.preferences.theme.ColorTheme;
import de.jrpie.android.launcher.preferences.theme.Font;
import eu.jonahbauer.android.preference.annotations.Preference;
import eu.jonahbauer.android.preference.annotations.PreferenceGroup;
import eu.jonahbauer.android.preference.annotations.Preferences;
+import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializationException;
+import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSerializer;
@Preferences(
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 = "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 = "general", prefix = "settings_general_", suffix = "_key", value = {
@@ -53,4 +63,32 @@ import eu.jonahbauer.android.preference.annotations.Preferences;
}),
})
public final class LauncherPreferences$Config {
+ public static class AppInfoSetSerializer implements PreferenceSerializer, Set> {
+
+ @Override
+ public Set serialize(Set value) throws PreferenceSerializationException {
+ if (value == null) return null;
+
+ var serialized = new HashSet(value.size());
+ for (var app : value) {
+ serialized.add(app.serialize());
+ }
+
+ return serialized;
+ }
+
+ @Override
+ public Set deserialize(Set value) throws PreferenceSerializationException {
+ if (value == null) return null;
+
+ var deserialized = new HashSet(value.size());
+
+ for (var s : value) {
+ deserialized.add(AppInfo.Companion.deserialize(s));
+ }
+
+ return deserialized;
+ }
+
+ }
}
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/list/ListActivity.kt b/app/src/main/java/de/jrpie/android/launcher/ui/list/ListActivity.kt
index fbe9e28..1074fb4 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/list/ListActivity.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/list/ListActivity.kt
@@ -27,6 +27,8 @@ import de.jrpie.android.launcher.ui.list.other.ListFragmentOther
// TODO: Better solution for this intercommunication functionality (used in list-fragments)
var intention = ListActivity.ListActivityIntention.VIEW
+var showFavorites = false
+var showHidden = false
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 */
}
+
override fun onCreate(savedInstanceState: Bundle?) {
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
binding = ListBinding.inflate(layoutInflater)
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 {
return modifyTheme(super.getTheme())
}
@@ -114,22 +148,13 @@ class ListActivity : AppCompatActivity(), UIObject {
}
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
if (intention == ListActivityIntention.VIEW) {
binding.listTabs.visibility = View.GONE
}
- binding.listHeading.text = getString(intention.titleResource)
+ updateTitle()
val sectionsPagerAdapter = ListSectionsPagerAdapter(this, supportFragmentManager)
val viewPager: ViewPager = findViewById(R.id.list_viewpager)
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/list/apps/AppsRecyclerAdapter.kt b/app/src/main/java/de/jrpie/android/launcher/ui/list/apps/AppsRecyclerAdapter.kt
index 02abfe8..618236a 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/list/apps/AppsRecyclerAdapter.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/list/apps/AppsRecyclerAdapter.kt
@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Intent
import android.graphics.Rect
import android.os.AsyncTask
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -12,9 +13,11 @@ import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.snackbar.Snackbar
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.REQUEST_CHOOSE_APP
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.DetailedAppInfo
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.ui.list.ListActivity
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.
@@ -37,13 +38,16 @@ import kotlin.text.Regex.Companion.escapeReplacement
*/
class AppsRecyclerAdapter(
val activity: Activity,
+ val root: View,
private val intention: ListActivity.ListActivityIntention
= ListActivity.ListActivityIntention.VIEW,
- private val forGesture: String? = ""
+ private val forGesture: String? = "",
+ private var appFilter: AppFilter = AppFilter("")
) :
RecyclerView.Adapter() {
- private val appsListDisplayed: MutableList
+ private val appsListDisplayed: MutableList = mutableListOf()
+
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
View.OnClickListener {
@@ -61,6 +65,7 @@ class AppsRecyclerAdapter(
}
}
+
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
val appLabel = appsListDisplayed[i].label.toString()
val appIcon = appsListDisplayed[i].icon
@@ -106,6 +111,15 @@ class AppsRecyclerAdapter(
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 {
when (it.itemId) {
R.id.app_menu_delete -> {
@@ -118,6 +132,53 @@ class AppsRecyclerAdapter(
true
}
+ R.id.app_menu_favorite -> {
+ var favorites: MutableSet =
+ 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 =
+ 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
}
}
@@ -144,9 +205,8 @@ class AppsRecyclerAdapter(
AsyncTask.execute { loadApps(activity.packageManager, activity) }
notifyDataSetChanged()
}
+ updateAppsList()
- appsListDisplayed = ArrayList()
- appsListDisplayed.addAll(appsList)
}
fun selectItem(pos: Int, rect: Rect = Rect()) {
@@ -169,42 +229,13 @@ class AppsRecyclerAdapter(
}
}
- /**
- * 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, "")
- }
+ fun updateAppsList(triggerAutoLaunch: Boolean = false) {
appsListDisplayed.clear()
- if (text.isEmpty()) {
- appsListDisplayed.addAll(appsList)
- } else {
- val appsSecondary: MutableList = ArrayList()
- val normalizedText: String = normalize(text)
- for (item in appsList) {
- val itemLabel: String = normalize(item.label.toString())
+ appsListDisplayed.addAll(appFilter(appsList))
- if (itemLabel.startsWith(normalizedText)) {
- appsListDisplayed.add(item)
- } else if (itemLabel.contains(normalizedText)) {
- appsSecondary.add(item)
- }
- }
- appsListDisplayed.addAll(appsSecondary)
- }
-
- if (appsListDisplayed.size == 1 && intention == ListActivity.ListActivityIntention.VIEW
+ if (triggerAutoLaunch &&
+ appsListDisplayed.size == 1
+ && intention == ListActivity.ListActivityIntention.VIEW
&& LauncherPreferences.functionality().searchAutoLaunch()
) {
val info = appsListDisplayed[0]
@@ -217,4 +248,23 @@ class AppsRecyclerAdapter(
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()
+ }
}
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/list/apps/ListFragmentApps.kt b/app/src/main/java/de/jrpie/android/launcher/ui/list/apps/ListFragmentApps.kt
index 6712e2c..441e88a 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/list/apps/ListFragmentApps.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/list/apps/ListFragmentApps.kt
@@ -1,11 +1,13 @@
package de.jrpie.android.launcher.ui.list.apps
+import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
+import de.jrpie.android.launcher.apps.AppFilter
import de.jrpie.android.launcher.databinding.ListAppsBinding
import de.jrpie.android.launcher.openSoftKeyboard
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.forGesture
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 {
private lateinit var binding: ListAppsBinding
+ private lateinit var appsRViewAdapter: AppsRecyclerAdapter
+
+ private var sharedPreferencesListener =
+ SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
+ appsRViewAdapter.updateAppsList()
+ }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -34,13 +44,28 @@ class ListFragmentApps : Fragment(), UIObject {
override fun onStart() {
super.onStart()
super.onStart()
+ LauncherPreferences.getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
+
+ binding.listAppsCheckBoxFavorites.isChecked = showFavorites
}
+ override fun onStop() {
+ super.onStop()
+ LauncherPreferences.getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
+ }
+
+
override fun setOnClicks() {}
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
binding.listAppsRview.apply {
@@ -54,17 +79,23 @@ class ListFragmentApps : Fragment(), UIObject {
androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
- appsRViewAdapter.filter(query)
+ appsRViewAdapter.setSearchString(query)
appsRViewAdapter.selectItem(0)
return true
}
override fun onQueryTextChange(newText: String): Boolean {
- appsRViewAdapter.filter(newText)
+ appsRViewAdapter.setSearchString(newText)
return false
}
})
+ binding.listAppsCheckBoxFavorites.setOnClickListener {
+ showFavorites = binding.listAppsCheckBoxFavorites.isChecked
+ appsRViewAdapter.setShowOnlyFavorites(showFavorites)
+ (activity as? ListActivity)?.updateTitle()
+ }
+
if (intention == ListActivity.ListActivityIntention.VIEW
&& LauncherPreferences.functionality().searchAutoOpenKeyboard()
) {
diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt
index d436ca3..1c6e0df 100644
--- a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt
+++ b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt
@@ -5,6 +5,7 @@ import android.content.SharedPreferences
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
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.setDefaultHomeScreen
@@ -65,6 +66,14 @@ class SettingsFragmentLauncher : PreferenceFragmentCompat() {
setDefaultHomeScreen(requireContext(), checkDefault = false)
true
}
+
+ val hiddenApps = findPreference(
+ LauncherPreferences.apps().keys().hidden()
+ )
+ hiddenApps?.setOnPreferenceClickListener {
+ openAppsList(requireContext(), favorite = false, hidden = true)
+ true
+ }
updateVisibility()
}
}
diff --git a/app/src/main/res/drawable/baseline_favorite_24.xml b/app/src/main/res/drawable/baseline_favorite_24.xml
index 90fac96..87df0dc 100644
--- a/app/src/main/res/drawable/baseline_favorite_24.xml
+++ b/app/src/main/res/drawable/baseline_favorite_24.xml
@@ -1,5 +1,12 @@
-
-
-
-
+
+
+
+
diff --git a/app/src/main/res/drawable/baseline_favorite_border_24.xml b/app/src/main/res/drawable/baseline_favorite_border_24.xml
index c6f3cc3..f514a45 100644
--- a/app/src/main/res/drawable/baseline_favorite_border_24.xml
+++ b/app/src/main/res/drawable/baseline_favorite_border_24.xml
@@ -1,5 +1,12 @@
-
-
-
-
+
+
+
+
diff --git a/app/src/main/res/drawable/checkbox_favorite.xml b/app/src/main/res/drawable/checkbox_favorite.xml
new file mode 100644
index 0000000..fe3254d
--- /dev/null
+++ b/app/src/main/res/drawable/checkbox_favorite.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_apps.xml b/app/src/main/res/layout/list_apps.xml
index a06f335..958025d 100644
--- a/app/src/main/res/layout/list_apps.xml
+++ b/app/src/main/res/layout/list_apps.xml
@@ -1,7 +1,6 @@
+
-
+ android:orientation="horizontal"
+ android:layout_margin="8dp"
+ >
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/tutorial_finish.xml b/app/src/main/res/layout/tutorial_finish.xml
index 1a55e96..eea44af 100644
--- a/app/src/main/res/layout/tutorial_finish.xml
+++ b/app/src/main/res/layout/tutorial_finish.xml
@@ -1,8 +1,6 @@
-
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 10a1d86..093a28c 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -9,7 +9,10 @@
internal.started_before
internal.first_startup
internal.version_code
- general.select_launcher
+ apps.favorites
+ apps.hidden
+
+ general.select_launcher
All Apps
+ Favorite Apps
+ Hidden Apps
Choose App
Apps
@@ -151,6 +156,10 @@
Uninstall
App Info
+ Add to favorites
+ Remove from favorites
+ Hide
+ Show
Removed the selected application
Unable to remove application
@@ -159,6 +168,7 @@
Launcher Settings
All Applications
+ Favorite Applications
Music: Louder
Music: Quieter
Music: Next
@@ -195,4 +205,6 @@
Settings
More options
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.
+ App hidden. You can make it visible again in settings.
+ Undo
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index a5e682b..d75781a 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -97,6 +97,17 @@
android:title="@string/settings_enabled_gestures_edge_swipe"/>
+
+
+
+
+
+