Compare commits

...

11 commits

Author SHA1 Message Date
059480fad1
Merge pull request #183 from wassupluke/improve-documentation
Some checks are pending
Android CI / build (push) Waiting to run
pull in hedgedoc content
2025-05-24 22:39:36 +00:00
3b70416b66
implement #181 2025-05-24 23:00:55 +02:00
Luke Wass
4f71011b1a
fix formatting 2025-05-24 15:20:13 -05:00
Luke Wass
e9585fa4dd
pull in hedgedoc content 2025-05-24 15:04:18 -05:00
85a7ed24ab
add documentation button 2025-05-24 21:08:45 +02:00
dfaec30fac
Merge branch 'master' of https://github.com/jrpie/launcher 2025-05-24 20:55:03 +02:00
1d10d65adb
update documentation 2025-05-24 20:41:37 +02:00
71193a2e50
fixed typo 2025-05-24 20:34:15 +02:00
580644f9d4
add documentation of app drawer 2025-05-24 20:29:01 +02:00
ea5a4ad7bf
add example to docs 2025-05-24 19:30:15 +02:00
f94c2b5962
add example to docs 2025-05-24 19:24:48 +02:00
20 changed files with 720 additions and 114 deletions

View file

@ -1,5 +1,6 @@
package de.jrpie.android.launcher.actions
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Rect
@ -25,11 +26,18 @@ class WidgetPanelAction(val widgetPanelId: Int) : Action {
override fun invoke(context: Context, rect: Rect?): Boolean {
if (WidgetPanel.byId(widgetPanelId) == null) {
if (context is WidgetPanelActivity) {
if (context.widgetPanelId == widgetPanelId) {
context.finish()
return true
}
}
if (WidgetPanel.byId(this.widgetPanelId) == null) {
Toast.makeText(context, R.string.alert_widget_panel_not_found, Toast.LENGTH_LONG).show()
} else {
context.startActivity(Intent(context, WidgetPanelActivity::class.java).also {
it.putExtra(EXTRA_PANEL_ID, widgetPanelId)
it.putExtra(EXTRA_PANEL_ID, this.widgetPanelId)
})
}
return true

View file

@ -1,16 +1,9 @@
package de.jrpie.android.launcher.ui
import android.annotation.SuppressLint
import android.app.Activity
import android.content.SharedPreferences
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.window.OnBackInvokedDispatcher
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.Gesture
@ -19,6 +12,7 @@ import de.jrpie.android.launcher.databinding.ActivityHomeBinding
import de.jrpie.android.launcher.openTutorial
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.ui.tutorial.TutorialActivity
import de.jrpie.android.launcher.ui.util.LauncherGestureActivity
/**
* [HomeActivity] is the actual application Launcher,
@ -32,10 +26,9 @@ import de.jrpie.android.launcher.ui.tutorial.TutorialActivity
* - Setting global variables (preferences etc.)
* - Opening the [TutorialActivity] on new installations
*/
class HomeActivity : UIObject, Activity() {
class HomeActivity : UIObject, LauncherGestureActivity() {
private lateinit var binding: ActivityHomeBinding
private var touchGestureDetector: TouchGestureDetector? = null
private var sharedPreferencesListener =
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
@ -54,35 +47,21 @@ class HomeActivity : UIObject, Activity() {
}
override fun onCreate(savedInstanceState: Bundle?) {
super<Activity>.onCreate(savedInstanceState)
super<LauncherGestureActivity>.onCreate(savedInstanceState)
super<UIObject>.onCreate()
// Initialise layout
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
// Handle back key / gesture on Android 13+, cf. onKeyDown()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
onBackInvokedDispatcher.registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_OVERLAY
) {
handleBack()
}
}
binding.buttonFallbackSettings.setOnClickListener {
LauncherAction.SETTINGS.invoke(this)
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
touchGestureDetector?.updateScreenSize(windowManager)
}
override fun onStart() {
super<Activity>.onStart()
super<LauncherGestureActivity>.onStart()
super<UIObject>.onStart()
// If the tutorial was not finished, start it
@ -133,82 +112,30 @@ class HomeActivity : UIObject, Activity() {
override fun onResume() {
super.onResume()
/* This should be initialized in onCreate()
However on some devices there seems to be a bug where the touchGestureDetector
is not working properly after resuming the app.
Reinitializing the touchGestureDetector every time the app is resumed might help to fix that.
(see issue #138)
*/
touchGestureDetector = TouchGestureDetector(
this, 0, 0,
LauncherPreferences.enabled_gestures().edgeSwipeEdgeWidth() / 100f
).also {
it.updateScreenSize(windowManager)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
binding.root.setOnApplyWindowInsetsListener { _, windowInsets ->
@Suppress("deprecation") // required to support API 29
val insets = windowInsets.systemGestureInsets
touchGestureDetector?.setSystemGestureInsets(insets)
windowInsets
}
}
updateSettingsFallbackButtonVisibility()
binding.homeWidgetContainer.updateWidgets(this@HomeActivity,
LauncherPreferences.widgets().widgets()
)
(application as Application).appWidgetHost.startListening()
}
override fun onDestroy() {
LauncherPreferences.getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
super.onDestroy()
}
@SuppressLint("GestureBackNavigation")
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
// Only used pre Android 13, cf. onBackInvokedDispatcher
handleBack()
}
KeyEvent.KEYCODE_VOLUME_UP -> {
if (Action.forGesture(Gesture.VOLUME_UP) == LauncherAction.VOLUME_UP) {
// Let the OS handle the key event. This works better with some custom ROMs
// and apps like Samsung Sound Assistant.
return false
}
Gesture.VOLUME_UP(this)
}
KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (Action.forGesture(Gesture.VOLUME_DOWN) == LauncherAction.VOLUME_DOWN) {
// see above
return false
}
Gesture.VOLUME_DOWN(this)
}
}
return true
}
override fun onTouchEvent(event: MotionEvent): Boolean {
touchGestureDetector?.onTouchEvent(event)
return true
}
private fun handleBack() {
override fun handleBack() {
Gesture.BACK(this)
}
override fun getRootView(): View {
return binding.root
}
override fun isHomeScreen(): Boolean {
return true
}

View file

@ -79,6 +79,9 @@ class SettingsFragmentMeta : Fragment(), UIObject {
// view code
bindURL(binding.settingsMetaButtonViewCode, R.string.settings_meta_link_github)
// view documentation
bindURL(binding.settingsMetaButtonViewDocs, R.string.settings_meta_link_docs)
// report a bug
binding.settingsMetaButtonReportBug.setOnClickListener {
val deviceInfo = getDeviceInfo()

View file

@ -0,0 +1,104 @@
package de.jrpie.android.launcher.ui.util
import android.annotation.SuppressLint
import android.app.Activity
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.window.OnBackInvokedDispatcher
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.actions.LauncherAction
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.ui.TouchGestureDetector
/**
* An activity with a [TouchGestureDetector] as well as handling of volume and back keys set up.
*/
abstract class LauncherGestureActivity: Activity() {
protected var touchGestureDetector: TouchGestureDetector? = null
override fun onTouchEvent(event: MotionEvent): Boolean {
touchGestureDetector?.onTouchEvent(event)
return true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Handle back key / gesture on Android 13+, cf. onKeyDown()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
onBackInvokedDispatcher.registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_OVERLAY
) {
handleBack()
}
}
}
override fun onResume() {
super.onResume()
/* This should be initialized in onCreate()
However on some devices there seems to be a bug where the touchGestureDetector
is not working properly after resuming the app.
Reinitializing the touchGestureDetector every time the app is resumed might help to fix that.
(see issue #138)
*/
touchGestureDetector = TouchGestureDetector(
this, 0, 0,
LauncherPreferences.enabled_gestures().edgeSwipeEdgeWidth() / 100f
).also {
it.updateScreenSize(windowManager)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
getRootView()?.setOnApplyWindowInsetsListener { _, windowInsets ->
@Suppress("deprecation") // required to support API 29
val insets = windowInsets.systemGestureInsets
touchGestureDetector?.setSystemGestureInsets(insets)
windowInsets
}
}
}
@SuppressLint("GestureBackNavigation")
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
// Only used pre Android 13, cf. onBackInvokedDispatcher
handleBack()
}
KeyEvent.KEYCODE_VOLUME_UP -> {
if (Action.forGesture(Gesture.VOLUME_UP) == LauncherAction.VOLUME_UP) {
// Let the OS handle the key event. This works better with some custom ROMs
// and apps like Samsung Sound Assistant.
return false
}
Gesture.VOLUME_UP(this)
}
KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (Action.forGesture(Gesture.VOLUME_DOWN) == LauncherAction.VOLUME_DOWN) {
// see above
return false
}
Gesture.VOLUME_DOWN(this)
}
}
return true
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
touchGestureDetector?.updateScreenSize(windowManager)
}
protected abstract fun getRootView(): View?
protected abstract fun handleBack()
}

View file

@ -8,9 +8,11 @@ import androidx.core.view.isVisible
import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.databinding.WidgetClockBinding
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.widgets.WidgetPanel
import java.util.Locale
class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId: Int): ConstraintLayout(context, attrs) {
class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId: Int, val panelId: Int): ConstraintLayout(context, attrs) {
constructor(context: Context, attrs: AttributeSet?): this(context, attrs, WidgetPanel.HOME.id, -1)
val binding: WidgetClockBinding = WidgetClockBinding.inflate(LayoutInflater.from(context), this, true)
init {
@ -57,7 +59,7 @@ class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId:
binding.clockUpperView.format12Hour = upperFormat
}
fun setOnClicks() {
private fun setOnClicks() {
binding.clockUpperView.setOnClickListener {
if (LauncherPreferences.clock().flipDateTime()) {
Gesture.TIME(context)
@ -74,7 +76,4 @@ class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId:
}
}
}
}

View file

@ -1,27 +1,31 @@
package de.jrpie.android.launcher.ui.widgets
import android.app.Activity
import android.content.res.Resources
import android.os.Bundle
import android.view.View
import androidx.core.view.ViewCompat
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.databinding.ActivityWidgetPanelBinding
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.ui.UIObject
import de.jrpie.android.launcher.ui.util.LauncherGestureActivity
import de.jrpie.android.launcher.ui.widgets.manage.EXTRA_PANEL_ID
import de.jrpie.android.launcher.widgets.WidgetPanel
class WidgetPanelActivity : Activity(), UIObject {
lateinit var binding: ActivityWidgetPanelBinding
private var widgetPanelId: Int = WidgetPanel.HOME.id
class WidgetPanelActivity : LauncherGestureActivity(), UIObject {
var binding: ActivityWidgetPanelBinding? = null
var widgetPanelId: Int = WidgetPanel.HOME.id
override fun onCreate(savedInstanceState: Bundle?) {
super<Activity>.onCreate(savedInstanceState)
super<LauncherGestureActivity>.onCreate(savedInstanceState)
super<UIObject>.onCreate()
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
val binding = ActivityWidgetPanelBinding.inflate(layoutInflater)
setContentView(binding.root)
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
// The widget container should extend below the status and navigation bars,
// so let's set an empty WindowInsetsListener to prevent it from being moved.
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets ->
@ -56,9 +60,8 @@ class WidgetPanelActivity : Activity(), UIObject {
}
override fun onStart() {
super<Activity>.onStart()
super<LauncherGestureActivity>.onStart()
super<UIObject>.onStart()
}
override fun onPause() {
@ -76,6 +79,14 @@ class WidgetPanelActivity : Activity(), UIObject {
(application as Application).appWidgetHost.startListening()
}
override fun getRootView(): View? {
return binding?.root
}
override fun handleBack() {
finish()
}
override fun isHomeScreen(): Boolean {
return true
}

View file

@ -18,8 +18,8 @@ class ClockWidget(
override var allowInteraction: Boolean = true
) : Widget() {
override fun createView(activity: Activity): View? {
return ClockView(activity, null, id)
override fun createView(activity: Activity): View {
return ClockView(activity, null, id, panelId)
}
override fun findView(views: Sequence<View>): ClockView? {

View file

@ -41,6 +41,13 @@
android:text="@string/settings_meta_view_code"
android:textAllCaps="false" />
<Button
android:id="@+id/settings_meta_button_view_docs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_meta_view_docs"
android:textAllCaps="false" />
<Button
android:id="@+id/settings_meta_button_report_bug"
android:layout_width="match_parent"

View file

@ -161,6 +161,7 @@
-
-->
<string name="settings_meta_link_github" translatable="false">https://github.com/jrpie/Launcher</string>
<string name="settings_meta_link_docs" translatable="false">https://launcher.jrpie.de/</string>
<string name="settings_meta_report_bug_link" translatable="false">https://github.com/jrpie/Launcher/issues/new?template=bug_report.yaml</string>
<string name="settings_meta_report_bug_mail" translatable="false">android-launcher-crash@jrpie.de</string>
<string name="settings_meta_report_vulnerability_link" translatable="false">https://github.com/jrpie/Launcher/security/policy</string>

View file

@ -443,4 +443,5 @@
<string name="report_crash_title">μLauncher crashed</string>
<string name="send_email">Send Email</string>
<string name="notification_channel_crash">Crashes and Debug Information</string>
<string name="settings_meta_view_docs">Documentation</string>
</resources>

View file

@ -1,3 +1,7 @@
+++
weight = 10
+++
# Actions and Gestures
µLauncher's central mechanism for accessing important functionality quickly

View file

@ -1,3 +1,452 @@
+++
weight = 100
+++
TODO: move the [hedgedoc](https://pad.abstractnonsen.se/foss-launchers) document here.
<meta http-equiv="refresh" content="0; url=https://pad.abstractnonsen.se/foss-launchers" />
# FOSS Launchers
This is a comparison of open-source home screens for Android.
**Inclusion criteria:** Apps in this list must be [open source](https://opensource.org/licenses) and maintained
<!-- New contributers, please reference https://www.markdownguide.org/basic-syntax/ -->
<!-- TEMPLATE
### <launcher name>
**License:** `<license type>`
[Website](<launcher website url>) | [Repository](<github or other repo url>) | [F-Droid](<fdroid url>)
<notes can go here>
**Main mode of interaction:** `<Search | Gestures | App grid>`
#### Features:
:white_check_mark: | :x: | :grey_question: Search: <list what you can search: e.g., apps, contacts, call history, ...>
:white_check_mark: | :x: | :grey_question: Search history
:white_check_mark: | :x: | :grey_question: Customizable gestures: <list usable gestures: e.g., up, down, tap+up, ...>
:white_check_mark: | :x: | :grey_question: Folders
:white_check_mark: | :x: | :grey_question: Tags
:white_check_mark: | :x: | :grey_question: Rename apps
:white_check_mark: | :x: | :grey_question: Widgets
:white_check_mark: | :x: | :grey_question: Private space
:white_check_mark: | :x: | :grey_question: Work profile
:white_check_mark: | :x: | :grey_question: Pinned shortcuts
:white_check_mark: | :x: | :grey_question: Icon packs
:white_check_mark: | :x: | :grey_question: Material You
---
-->
## Grid-Based Launchers
### Discreet Launcher
**License:** `GPL-3.0`
[Website](https://vincent-falzon.com/) | [Repository](https://github.com/falzonv/discreet-launcher) | [F-Droid](https://f-droid.org/en/packages/com.vincent_falzon.discreetlauncher/)
**Main mode of interaction:** `app grid`
#### Features:
:white_check_mark: Search: `apps`
:x: Search history
:x: Customizable gestures
:white_check_mark:Folders
:x: Tags
:white_check_mark: Rename apps
:x: Widgets
:grey_question: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:white_check_mark: Icon packs
:x: Material You
---
### Fossify
**License:** `GPL-3.0`
[Website](https://www.fossify.org/) | [Repository](https://github.com/FossifyOrg/Launcher) | [F-Droid](https://f-droid.org/en/packages/org.fossify.home/)
**Main mode of interaction:** `app grid`
#### Features:
:white_check_mark: Search: `apps`
:x: Search history
:x: Customizable gestures
:white_check_mark: Folders
:x: Tags
:white_check_mark: Rename apps
:white_check_mark: Widgets
:grey_question: Private space
:x: Work profile
:white_check_mark: Pinned shortcuts
:x: Icon packs
:white_check_mark: Material You
---
### Lawnchair
**License:** `Apache License 2.0`
[Website](https://lawnchair.app/) | [Repository](https://github.com/LawnchairLauncher/lawnchair)
Seems to be a regular (grid of apps) launcher.
**Main mode of interaction:** App grid
#### Features:
:white_check_mark: Search: `Apps & Shortcuts` `Web suggestions` `People` `Files` `Android Settings` `Calculator`
:white_check_mark: Search history
:white_check_mark: Customizable gestures: `double tap` `swipe up` `swipe down` `home button` `back button`
:white_check_mark: Folders
:x: Tags
:white_check_mark: Rename apps
:white_check_mark: Widgets
:grey_question: Private space
:grey_question: Work profile
:x: Pinned shortcuts
:white_check_mark: Icon packs
:white_check_mark: Material You
---
### Rootless Pixel Launcher
> **Abandoned**
**License:** `Apache License 2.0`
[Repository](https://github.com/amirzaidi/Launcher3)
**Main mode of interaction:** `App grid`
#### Features:
:white_check_mark: Search: `Apps`
:x: Search history
:x: Customizable gestures
:x: Folders
:x: Tags
:x: Rename apps
:warning: Widgets `buggy/broken`
:grey_question: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:white_check_mark: Icon packs
:x: Material You
---
## Search based
### Aster Launcher
> **Abandoned**
**License:** `GPL-3.0`
[Repository](https://github.com/neophtex/AsterLauncher) | [F-Droid](https://f-droid.org/en/packages/com.series.aster.launcher/)
**Main mode of interaction:** `search`
#### Features:
:warning: Search: `apps` (apps list is buggy/broken) `web`
:x: Search history
:x: Customizable gestures
:x: Folders
:x: Tags
:x: Rename apps
:x: Widgets
:grey_question: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:x: Icon packs
:x: Material You
---
### KISS Launcher
**License:** `GPL-3.0`
[Website](https://kisslauncher.com/) | [Repository](https://github.com/Neamar/KISS) | [F-Droid](https://f-droid.org/packages/fr.neamar.kiss/)
**Main mode of interaction:** `Search` `Some gestures available`
#### Features:
:white_check_mark: Search: `Apps` `Contacts` `Call history`
:white_check_mark: Search history
:white_check_mark: Customizable gestures: `swipe left` `swipe right` `swipe up` `swipe down` `long press` `double tap`
:x: Folders
:white_check_mark: Tags
:white_check_mark: Rename apps
:white_check_mark: Widgets
:grey_question: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:white_check_mark: Icon packs
:grey_question: Material You
---
### Lunar Launcher
**License:** `GPL-3.0`
[Repository](https://github.com/iamrasel/lunar-launcher) | [F-Droid](https://f-droid.org/en/packages/rasel.lunar.launcher/)
Even natively supports RSS feeds to homescreen?
**Main mode of interaction:** `alphabet scroller`
#### Features:
:white_check_mark: Search: `swipe up` `swipe down` `swipe left` `swipe right` `tap and hold battery indicator/clock` `tap and hold lower part of screen` `double tap` `tap and hold favorite item`
:x: Search history
:x: Customizable gestures
:x: Folders
:x: Tags
:x: Rename apps
:x: Widgets
:grey_question: Private space
:x: Work profile
:white_check_mark: Pinned shortcuts
:x: Icon packs
:x: Material You
---
### OLauncher
**License:** `GPL-3.0`
[Repository](https://github.com/tanujnotes/Olauncher) | [F-Droid](https://f-droid.org/en/packages/app.olauncher/)
Extremely minimal launcher with lots of forks.
**Main mode of interaction:** `Search`
#### Features:
:white_check_mark: Search: `apps`
:x: Search history.
:white_check_mark: Customizable gestures: `swipe left` `swipe right` `double tap`
:x: Folders
:x: Tags
:white_check_mark: Rename apps
:x: Widgets
:grey_question: Private space
:white_check_mark: Work profile
:grey_question: Pinned shortcuts
:x: Icon packs
:grey_question: Material You
#### Forks:
* [Olauncher Clutter Free](https://f-droid.org/en/packages/app.olaunchercf/)
* [mLauncher](https://f-droid.org/en/packages/app.mlauncher/)
* [CLauncher](https://f-droid.org/en/packages/app.clauncher/) (even more minimalistic, search without feedback)
* [CCLauncher](https://f-droid.org/en/packages/app.cclauncher/) (rewrite using compose)
---
### TinyBit Launcher
**License:** `GPL-3.0`
[Repository](https://github.com/TBog/TBLauncher) | [F-Droid](https://f-droid.org/en/packages/rocks.tbog.tblauncher/)
**Main mode of interaction:** `search` `some gestures`
#### Features:
:white_check_mark: Search: `apps` `contacts` `web` `maps` `playstore` `youtube`
:white_check_mark: Search history
:white_check_mark:Customizable gestures: `tap` `double tap` `swipe up` `swipe left` `swipe right` `swipe down on left` `swipe down on right`
:x: Folders
:white_check_mark: Tags
:white_check_mark: Rename apps
:white_check_mark: Widgets
:grey_question: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:white_check_mark: Icon packs
:x: Material You
---
### YAM Launcher
**License:** `MIT`
[Repository](https://codeberg.org/ottoptj/yamlauncher) | [F-Droid](https://f-droid.org/en/packages/eu.ottop.yamlauncher/)
Similar to OLauncher?
**Main mode of interaction:** `search` `home screen text buttons`
#### Features:
:white_check_mark: Search: `apps` `contacts (optional)`
:x: Search history
:white_check_mark: Customizable gestures: `swipe left` `swipe right`
:x: Folders
:x: Tags
:white_check_mark: Rename apps
:x: Widgets
:grey_question: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:x: Icon packs
:white_check_mark: Material You
---
## Directory based
### folder launcher
**License:** `MIT`
[Repository](https://github.com/Robby-Blue/SimpleFolderLauncher) | [F-Droid](https://f-droid.org/en/packages/me.robbyblue.mylauncher/)
**Main mode of interaction:** `directory navigation`
#### Features:
:white_check_mark: Search: `apps`
:x: Search history
:x: Customizable gestures
:white_check_mark: Folders
:x: Tags
:white_check_mark: Rename apps
:white_check_mark: Widgets
:grey_question: Private space
:x: Work profile
:white_check_mark: Pinned shortcuts
:x: Icon packs
:x: Material You
---
### Ion Launcher
**License:** `GPL-3.0`
[Repository](https://codeberg.org/zagura/ion-launcher) | [F-Droid](https://f-droid.org/en/packages/one.zagura.IonLauncher/)
**Main mode of interaction:** `App grid` `Search`
#### Features:
:warning: Search: `apps` `contacts: buggy/broken`
:white_check_mark: Search history `shows recently launched apps`
:x: Customizable gestures
:warning: Folders `prebuilt` `not customizable`
:x: Tags
:white_check_mark: Rename apps
:x: Widgets
:grey_question: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:white_check_mark: Icon packs
:x: Material You
---
## Gesture based
### Pie Launcher
**License:** `MIT`
[Repository](https://github.com/markusfisch/PieLauncher)
**Main mode of interaction:** `Selection wheel`
#### Features:
:white_check_mark: Search: `apps`
:x: Search history
:x: Customizable gestures
:x: Folders
:x: Tags
:x: Rename apps
:x: Widgets
:grey_question: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:x: Icon packs
:x: Material You
---
### µLauncher
**License:** `MIT`
[Repository](https://github.com/jrpie/launcher) | [F-Droid](https://f-droid.org/en/packages/de.jrpie.android.launcher/)
**Main mode of interaction:** `Gestures` `Search`
#### Features:
:white_check_mark: Search: `apps`
:x: Search history
:white_check_mark: Customizable gestures: `35 avilable` [read the docs](https://github.com/jrpie/launcher/blob/master/docs/actions-and-gestures.md)
:x: Folders
:x: Tags
:white_check_mark: Rename apps
:white_check_mark: Widgets
:white_check_mark: Private space
:white_check_mark: Work profile
:white_check_mark: Pinned shortcuts
:x:Icon packs
:white_check_mark: Material You
---
### Tabular Summary
#### Legend:
:white_check_mark: = Supported
:x: = Unsupported
:warning: = Buggy/Broken; check this launcher's notes above
| Launcher | Search | Search history | Customizable gestures | Folders | Tags | Rename apps | Widgets | Private space | Work profile | Pinned shortcuts | Icon packs | Material You |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| [µLauncher](#µLauncher) | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
| [Fossify](#Fossify) | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :grey_question: | :x: | :white_check_mark: | :x: | :white_check_mark: |
| [Lawnchair](#Lawnchair) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :grey_question: | :grey_question: | :x: | :white_check_mark: | :white_check_mark: |
| [Rootless Pixel Launcher](#Rootless-Pixel-Launcher) | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :warning: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| [KISS Launcher](#KISS-Launcher) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :grey_question: |
| [Lunar Launcher](#Lunar-Launcher) | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :grey_question: | :x: | :white_check_mark: | :x: | :x: |
| [OLauncher](#OLauncher) | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :grey_question: | :white_check_mark: | :grey_question: | :x: | :grey_question: |
| [TinyBit Launcher](#TinyBit-Launcher) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| [YAM Launcher](#YAM-Launcher) | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
| [Ion Launcher](#Ion-Launcher) | :warning: | :white_check_mark: | :x: | :warning: | :x: | :white_check_mark: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| [Pie Launcher](#Pie-Launcher) | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :x: | :x: |
| [folder launcher](#folder-launcher) | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :grey_question: | :x: | :white_check_mark: | :x: | :x: |
| [Discreet Launcher](#Discreet-Launcher) | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :white_check_mark: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| [Aster Launcher](#Aster-Launcher) | :warning: | :x: | :x: | :x: | :x: | :x: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :x: | :x: |
<!-- Automating checkboxes for the table
You can use this python script, copy the contents of the Features section for the launcher, and paste it in the fixit() function. Note, since you're pasting in multiple lines, you will need to start and end your paste with triple-quotes.
import re
def fix(features):
# Use: fixed("""<paste here>""")
fixed = ' | '.join(re.findall('(:[\w\_]*:)', features))
return f"| {fixed} |"
-->
---
## Not Tested
Feel free to test these and add
https://f-droid.org/en/packages/app.easy.launcher/
https://f-droid.org/en/packages/de.nodomain.tobihille.seniorlauncher/
https://f-droid.org/en/packages/com.mrmannwood.hexlauncher/
https://f-droid.org/en/packages/com.simplemobiletools.applauncher/
https://f-droid.org/en/packages/peterfajdiga.fastdraw/
https://f-droid.org/en/packages/de.mm20.launcher2.release/
[Even more launchers](https://docs.arcticons.com/faq/supported-launchers) (most of them don't seem to be FOSS)

54
docs/app-drawer.md Normal file
View file

@ -0,0 +1,54 @@
+++
weight = 10
+++
# App Drawer
Apps that are not needed all the time are shown in the app drawer.
There are several such drawers, but the basic concept is the same.
Besides regular apps, app drawers also show [pinned shortcuts](https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts)[^1].
&mu;Launcher treats apps and shortcuts in the same way.
The idea of the app drawer is to search for apps using the keyboard.
By default[^2] an app is launched automatically once it is the only app matching the query.
This can be prevented by typing space.
Usually only two or three characters are needed which is much faster than scrolling to find an app.
[^1]: A pinned shortcut is created for example when pinning a website to the home screen.
[^2]: There are [several settings](/docs/settings/#functionality) available to modify the behavior.
When long clicking an app, additional options are shown:
* Rename the app
* Add to / remove from Favorites: Adds the app to the [Favorite Apps](#favorite-apps) list or removes it from there.
* Hide / Show: This hides the app from all drawers (except from [Hidden Apps](#hidden-apps)) or makes it visible again if it was hidden.
* App Info: Opens the system settings page for the app.
* Uninstall: Tries to uninstall the app or remove the shortcut.
## All Apps
This lists all apps except hidden apps.
By default it is bound to `Swipe up`.
## Favorite Apps
Only shows favorite apps.
Pressing the star icon on the bottom right of any app drawer toggles whether
only favorite apps should be shown.
Additionally the `Favorite Apps` action can be used to launch this drawer directly.
By default it is bound to `Swipe up (left edge)`.
## Private Space
When [private space](/docs/profiles/#private-space) is available, this drawer
shows only apps from the private space.
It can be opened using the `Private Space` action.
If the private space is locked, instead of showing the list the unlock dialog is shown.
By default, apps from the private space are shown in All Apps as well,
however this is [configurable](/docs/settings/#hide-private-space-from-app-list).
## Hidden Apps
This list shows hidden apps.
It is only accessible through the settings.
The feature is intended to be used only for apps which are not needed at all but [can not be uninstalled](https://en.wikipedia.org/wiki/Software_bloat#Bloatware).

View file

@ -1,3 +1,8 @@
+++
weight = 50
+++
# Building from Source
## Using the command line
@ -37,9 +42,13 @@ for further instructions.
Install [Android Studio](https://developer.android.com/studio), import this project and build it.
See [this guide](https://developer.android.com/studio/run)
for further instructions. How to
for further instructions.
## CI Pipeline
The [CI pipeline](https://github.com/jrpie/Launcher/actions) automatically creates debug builds.
> Note: These builds are *not* signed. They are in built in debug mode and only suitable for testing.
{{% hint warning %}}
Note: These builds are not signed.
They are in built in debug mode and only suitable for testing.
{{% /hint %}}

View file

@ -1,3 +1,8 @@
+++
weight = 40
+++
# Contributing
There are several ways to contribute to this app:

View file

@ -1,2 +1,7 @@
+++
bookCollapseSection = true
weight = 20
+++
# Examples
This section contains some examples how &mu;Launcher can be tweaked.

View file

@ -0,0 +1,18 @@
+++
title = 'Showing Apps on the Home Screen'
+++
# Showing Apps on the Home Screen
Even though this is somewhat contrary to the general idea of &mu;Launcher,
it is possible to show apps on the home screen using widgets.
Users suggested:
* [Launchy](https://launchywidget.com/) (proprietary!)
* KWGT Kustom Widget Maker (proprietary!)
{{% hint danger %}}
Both of these apps are not open source and KWGT even has ads.
Please contact me if you know FOSS alternatives!
{{% /hint %}}

View file

@ -1,3 +1,9 @@
+++
title = 'User Profiles'
weight = 12
+++
# Work Profile
µLauncher is compatible with [work profile](https://www.android.com/enterprise/work-profile/), so apps like [Shelter](https://gitea.angry.im/PeterCxy/Shelter) can be used.

View file

@ -1,3 +1,7 @@
+++
weight = 10
+++
# Settings
Tweaks and customizations can be made from within the settings page.
@ -230,13 +234,3 @@ Remove the top status bar from the home screen.
Remove the navigation bar from the home screen. Enabling this setting may make it difficult to use the device if gestures are not setup properly.
**type:**&nbsp;`toggle`
## Additional Settings
### App Drawer Long Press on App
Access additional per-app details and settings. To use, open the app drawer and long press on any app.
**type:**&nbsp;`dropdown`
**options:**&nbsp;`App Info`,`Add to favorites`,`Hide`,`Rename`,`Uninstall`

View file

@ -1,5 +1,6 @@
+++
title = 'Widgets'
weight = 11
+++
# Widgets