mirror of
https://github.com/jrpie/Launcher.git
synced 2025-06-07 17:59:54 +02:00
Compare commits
11 commits
ac1639b77e
...
059480fad1
Author | SHA1 | Date | |
---|---|---|---|
059480fad1 | |||
3b70416b66 | |||
![]() |
4f71011b1a | ||
![]() |
e9585fa4dd | ||
85a7ed24ab | |||
dfaec30fac | |||
1d10d65adb | |||
71193a2e50 | |||
580644f9d4 | |||
ea5a4ad7bf | |||
f94c2b5962 |
20 changed files with 720 additions and 114 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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? {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
+++
|
||||
weight = 10
|
||||
+++
|
||||
|
||||
# Actions and Gestures
|
||||
|
||||
µLauncher's central mechanism for accessing important functionality quickly
|
||||
|
|
|
@ -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
54
docs/app-drawer.md
Normal 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].
|
||||
μ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).
|
|
@ -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 %}}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
+++
|
||||
weight = 40
|
||||
+++
|
||||
|
||||
|
||||
# Contributing
|
||||
|
||||
There are several ways to contribute to this app:
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
+++
|
||||
bookCollapseSection = true
|
||||
weight = 20
|
||||
+++
|
||||
|
||||
# Examples
|
||||
This section contains some examples how μLauncher can be tweaked.
|
||||
|
|
18
docs/examples/apps-on-home-screen.md
Normal file
18
docs/examples/apps-on-home-screen.md
Normal 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 μ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 %}}
|
|
@ -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.
|
||||
|
|
|
@ -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:** `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:** `dropdown`
|
||||
|
||||
**options:** `App Info`,`Add to favorites`,`Hide`,`Rename`,`Uninstall`
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
+++
|
||||
title = 'Widgets'
|
||||
weight = 11
|
||||
+++
|
||||
|
||||
# Widgets
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue