Compare commits

..

47 commits

Author SHA1 Message Date
919108bbd0
fix: stop longPressHandler when activity finishes
Some checks failed
Android CI / build (push) Has been cancelled
2025-06-05 12:02:37 +02:00
ce939111d0
fixed italian translation
Some checks failed
Android CI / build (push) Has been cancelled
2025-05-29 19:29:05 +02:00
Too Late (bot)
33dd3ef3c2
Translations update from Toolate (#175)
* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 99.6% (288 of 289 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/zh_Hans/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (289 of 289 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/ar/

* Translated using Weblate (Italian)

Currently translated at 100.0% (290 of 290 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (290 of 290 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/it/

---------

Co-authored-by: class0068 <monkeyotw@proton.me>
Co-authored-by: abdelbasset jabrane <cehiwa4149@oronny.com>
Co-authored-by: Vladi69 <vladimirogalante@yahoo.it>
Co-authored-by: renar <opensource.tjxzm@aleeas.com>
2025-05-29 19:18:01 +02:00
Luke Wass
1d793c485e
Improve documentation (#190)
* fix grammar
2025-05-29 17:16:05 +02:00
04a2b4d248
merge #193 - codebase improvements
Co-authored-by: Luke Wass <wassupluke@gmail.com>

Squashed commit of the following:

commit 075b4a5353cedea531ec6ebefa60d92de82e8e21
Author: Josia Pietsch <git@jrpie.de>
Date:   Thu May 29 15:34:23 2025 +0200

    some changes

commit ced2e30531
Author: Luke Wass <wassupluke@gmail.com>
Date:   Thu May 29 00:18:21 2025 -0500

    remove unused imports/functions/variables, improve naming convention, remove unused widget context

commit 956ad9795c
Author: Luke Wass <wassupluke@gmail.com>
Date:   Wed May 28 22:40:05 2025 -0500

    add contentDescriptions, ignore unspeakable sections, minor code reorganization

commit cb793860c0
Author: Luke Wass <wassupluke@gmail.com>
Date:   Wed May 28 21:03:03 2025 -0500

    remove empty method

commit 893de14c79
Author: Luke Wass <wassupluke@gmail.com>
Date:   Tue May 27 22:56:03 2025 -0500

    Simplify constructors by removing unused init parameters; clean up handle list type declaration

commit 39164d2e54
Author: Luke Wass <wassupluke@gmail.com>
Date:   Tue May 27 22:54:16 2025 -0500

    Refactor getAppWidgetProviders to use explicit lambda parameter names for clarity

commit 8e53ef0ebe
Author: Luke Wass <wassupluke@gmail.com>
Date:   Tue May 27 22:52:21 2025 -0500

    improve naming convention

commit 8c2a266c22
Author: Luke Wass <wassupluke@gmail.com>
Date:   Tue May 27 22:50:55 2025 -0500

    remove unused resources

commit be03af8ac6
Author: Luke Wass <wassupluke@gmail.com>
Date:   Tue May 27 22:50:33 2025 -0500

    fix table format
2025-05-29 15:48:41 +02:00
15d36eeff4
delete appwidgethost when resetting settings
Some checks failed
Android CI / build (push) Has been cancelled
2025-05-26 14:34:42 +02:00
3c2efe04de
Merge branch 'master' of https://github.com/jrpie/launcher 2025-05-26 14:33:04 +02:00
Luke Wass
d33f250a56
fix cardView shadow clipping (#188)
* fix cardView shadow clipping

* decrease margin above cardView
2025-05-26 13:26:13 +02:00
23c5973501
Merge pull request #182 from wassupluke/improve-tutorial
Some checks are pending
Android CI / build (push) Waiting to run
Add visual cues and interactive styling to the settings fragment
2025-05-25 10:56:04 +00:00
394f66dde9
change card background in light theme 2025-05-25 12:54:54 +02:00
8f3a8539cb
explain background settings (#144) 2025-05-25 03:10:17 +02:00
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
ac1639b77e
add termux example to docs (see #147 and #149)
Some checks are pending
Android CI / build (push) Waiting to run
2025-05-24 19:01:15 +02:00
e4c7ad0994
prepare docs/ for hugo ssg (see #176) 2025-05-24 17:40:32 +02:00
c07ab0e029
prevent crash when unable to access widgetproviderinfo 2025-05-24 13:31:00 +02:00
Luke Wass
a56cc772f7 Add visual cues and interactive styling to the settings fragment 2025-05-20 11:58:12 -05:00
7783d26d4c
Merge pull request #179 from wassupluke/widget-list-spacing
Some checks failed
Android CI / build (push) Has been cancelled
Improve widget list layout
2025-05-20 12:32:41 +00:00
Luke Wass
ba3255d9ec Improve widget list layout 2025-05-19 16:34:56 -05:00
dd3a2e91bd
0.2.2
Some checks failed
Android CI / build (push) Has been cancelled
2025-05-17 13:00:16 +02:00
118efd0b62
Merge pull request #166 from toolatebot/weblate-jrpie-launcher-launcher
Translations update from Toolate
2025-05-17 10:54:51 +00:00
3cfd403b94
Merge pull request #171 from wassupluke/translate-lt
more translations
2025-05-17 10:54:28 +00:00
bd7df4f6a0
(try to) fix #172 2025-05-17 12:39:18 +02:00
31a9049861
try to mitigate #172 2025-05-17 11:42:21 +02:00
Luke Wass
013b835ed8 more translations 2025-05-16 02:53:31 -05:00
renar
271850f75a Translated using Weblate (Italian)
Currently translated at 100.0% (280 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/it/
2025-05-16 00:07:19 +00:00
5f847a8d40
enable widget interaction by default on widget panels
Some checks failed
Android CI / build (push) Has been cancelled
2025-05-15 20:55:15 +02:00
eaece8e334
fix #168 2025-05-15 20:51:45 +02:00
91c9952f7c
Merge pull request #159 from toolatebot/weblate-jrpie-launcher-launcher
Some checks failed
Android CI / build (push) Has been cancelled
Translations update from Toolate
2025-05-13 15:59:52 +02:00
04330ff407
add crash handler 2025-05-13 15:57:18 +02:00
Anonymous
916a272e8f Translated using Weblate (Arabic)
Currently translated at 98.9% (277 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/ar/
2025-05-13 07:21:58 +00:00
Anonymous
520e7d5c0d Translated using Weblate (Portuguese (Brazil))
Currently translated at 98.9% (277 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/pt_BR/
2025-05-13 07:21:58 +00:00
renar
374b688ddf Translated using Weblate (Dutch)
Currently translated at 100.0% (280 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/nl/
2025-05-12 15:07:19 +00:00
renar
12986c15b3 Translated using Weblate (Italian)
Currently translated at 99.6% (279 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/it/
2025-05-12 15:07:19 +00:00
class0068
070d232681 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.9% (277 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/zh_Hans/
2025-05-12 15:07:19 +00:00
renar
ede651525b Translated using Weblate (German)
Currently translated at 88.9% (249 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/de/
2025-05-12 15:07:19 +00:00
Anonymous
435ce32fbd Translated using Weblate (Arabic)
Currently translated at 99.2% (278 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/ar/
2025-05-12 15:07:19 +00:00
Anonymous
6dfdb09cf6 Translated using Weblate (Portuguese (Brazil))
Currently translated at 99.2% (278 of 280 strings)

Translation: jrpie-Launcher/Launcher
Translate-URL: https://toolate.othing.xyz/projects/jrpie-launcher/launcher/pt_BR/
2025-05-12 15:07:19 +00:00
75 changed files with 1729 additions and 454 deletions

View file

@ -23,8 +23,8 @@ android {
minSdkVersion 21
targetSdkVersion 35
compileSdk 35
versionCode 46
versionName "0.2.1"
versionCode 47
versionName "0.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

View file

@ -8,6 +8,7 @@
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".Application"
@ -19,6 +20,7 @@
android:supportsRtl="true"
android:theme="@style/launcherBaseTheme"
tools:ignore="UnusedAttribute">
<activity
android:name=".ui.widgets.manage.ManageWidgetPanelsActivity"
android:exported="false" />
@ -80,6 +82,9 @@
<activity
android:name=".ui.LegalInfoActivity"
android:exported="false" />
<activity
android:name=".ui.ReportCrashActivity"
android:exported="false" />
<receiver
android:name=".actions.lock.LauncherDeviceAdmin"
@ -110,5 +115,4 @@
android:resource="@xml/accessibility_service_config" />
</service>
</application>
</manifest>

View file

@ -25,9 +25,10 @@ import de.jrpie.android.launcher.preferences.resetPreferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.system.exitProcess
const val APP_WIDGET_HOST_ID = 42;
const val APP_WIDGET_HOST_ID = 42
class Application : android.app.Application() {
@ -106,6 +107,11 @@ class Application : android.app.Application() {
// TODO Error: Invalid resource ID 0x00000000.
// DynamicColors.applyToActivitiesIfAvailable(this)
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
sendCrashNotification(this@Application, throwable)
exitProcess(1)
}
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
torchManager = TorchManager(this)
@ -114,8 +120,6 @@ class Application : android.app.Application() {
appWidgetHost = AppWidgetHost(this.applicationContext, APP_WIDGET_HOST_ID)
appWidgetManager = AppWidgetManager.getInstance(this.applicationContext)
appWidgetHost.startListening()
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
LauncherPreferences.init(preferences, this.resources)
@ -157,6 +161,8 @@ class Application : android.app.Application() {
removeUnusedShortcuts(this)
}
loadApps()
createNotificationChannels(this)
}
fun getCustomAppNames(): HashMap<AbstractAppInfo, String> {
@ -170,10 +176,4 @@ class Application : android.app.Application() {
apps.postValue(getApps(packageManager, applicationContext))
}
}
override fun onTerminate() {
appWidgetHost.stopListening()
super.onTerminate()
}
}

View file

@ -6,9 +6,6 @@ import android.app.role.RoleManager
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.ClipboardManager
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.appwidget.AppWidgetProviderInfo
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
@ -227,3 +224,13 @@ fun copyToClipboard(context: Context, text: String) {
val clipData = ClipData.newPlainText("Debug Info", text)
clipboardManager.setPrimaryClip(clipData)
}
fun writeEmail(context: Context, to: String, subject: String, text: String) {
val intent = Intent(Intent.ACTION_SENDTO)
intent.setData("mailto:".toUri())
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(to))
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
intent.putExtra(Intent.EXTRA_TEXT, text)
context.startActivity(Intent.createChooser(intent, context.getString(R.string.send_email)))
}

View file

@ -0,0 +1,87 @@
package de.jrpie.android.launcher
import android.app.Activity
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import de.jrpie.android.launcher.ui.EXTRA_CRASH_LOG
import de.jrpie.android.launcher.ui.ReportCrashActivity
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.random.Random
private val NOTIFICATION_CHANNEL_CRASH = "launcher:crash"
fun createNotificationChannels(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(
NOTIFICATION_CHANNEL_CRASH,
context.getString(R.string.notification_channel_crash),
NotificationManager.IMPORTANCE_HIGH
)
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(notificationChannel)
}
}
fun requestNotificationPermission(activity: Activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return
}
val permission =
(activity.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED)
if (!permission) {
ActivityCompat.requestPermissions(
activity,
arrayOf( android.Manifest.permission.POST_NOTIFICATIONS ),
1
)
}
}
fun sendCrashNotification(context: Context, throwable: Throwable) {
val stringWriter = StringWriter()
val printWriter = PrintWriter(stringWriter)
throwable.printStackTrace(printWriter)
val intent = Intent(context, ReportCrashActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.putExtra(EXTRA_CRASH_LOG, stringWriter.toString())
val pendingIntent = PendingIntent.getActivity(
context,
Random.nextInt(),
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_CRASH)
.setSmallIcon(R.drawable.baseline_bug_report_24)
.setContentTitle(context.getString(R.string.notification_crash_title))
.setContentText(context.getString(R.string.notification_crash_explanation))
.setContentIntent(pendingIntent)
.setAutoCancel(false)
.setPriority(NotificationCompat.PRIORITY_HIGH)
val notificationManager = NotificationManagerCompat.from(context)
try {
notificationManager.notify(
0,
builder.build()
)
} catch (e: SecurityException) {
Log.e("Crash Notification", "Could not send notification")
}
}

View file

@ -1,7 +1,6 @@
package de.jrpie.android.launcher.actions
import android.content.Context
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Build

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

@ -6,7 +6,6 @@ import android.widget.Button
import androidx.appcompat.app.AlertDialog
import de.jrpie.android.launcher.BuildConfig
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.actions.lock.LauncherAccessibilityService
import de.jrpie.android.launcher.preferences.LauncherPreferences

View file

@ -2,7 +2,6 @@ package de.jrpie.android.launcher.preferences
import android.content.Context
import android.util.Log
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.BuildConfig
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.apps.AbstractAppInfo
@ -10,22 +9,25 @@ import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
import de.jrpie.android.launcher.apps.AppInfo
import de.jrpie.android.launcher.apps.DetailedAppInfo
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion100
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion2
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion4
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
import de.jrpie.android.launcher.sendCrashNotification
import de.jrpie.android.launcher.ui.HomeActivity
import de.jrpie.android.launcher.widgets.ClockWidget
import de.jrpie.android.launcher.widgets.DebugInfoWidget
import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import de.jrpie.android.launcher.widgets.deleteAllWidgets
import de.jrpie.android.launcher.widgets.generateInternalId
import de.jrpie.android.launcher.widgets.getAppWidgetHost
/* Current version of the structure of preferences.
* Increase when breaking changes are introduced and write an appropriate case in
* `migratePreferencesToNewVersion`
*/
const val PREFERENCE_VERSION = 100
const val PREFERENCE_VERSION = 101
const val UNKNOWN_PREFERENCE_VERSION = -1
private const val TAG = "Launcher - Preferences"
@ -64,6 +66,10 @@ fun migratePreferencesToNewVersion(context: Context) {
migratePreferencesFromVersion4(context)
Log.i(TAG, "migration of preferences complete (4 -> ${PREFERENCE_VERSION}).")
}
100 -> {
migratePreferencesFromVersion100(context)
Log.i(TAG, "migration of preferences complete (100 -> ${PREFERENCE_VERSION}).")
}
else -> {
Log.w(
@ -76,6 +82,7 @@ fun migratePreferencesToNewVersion(context: Context) {
}
} catch (e: Exception) {
Log.e(TAG, "Unable to restore preferences:\n${e.stackTrace}")
sendCrashNotification(context, e)
resetPreferences(context)
}
}
@ -84,12 +91,12 @@ fun resetPreferences(context: Context) {
Log.i(TAG, "Resetting preferences")
LauncherPreferences.clear()
LauncherPreferences.internal().versionCode(PREFERENCE_VERSION)
deleteAllWidgets(context)
context.getAppWidgetHost().deleteHost()
LauncherPreferences.widgets().widgets(
setOf(
ClockWidget(
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
generateInternalId(),
WidgetPosition(1, 3, 10, 4),
WidgetPanel.HOME.id
)
@ -101,7 +108,7 @@ fun resetPreferences(context: Context) {
LauncherPreferences.widgets().widgets().also {
it.add(
DebugInfoWidget(
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
generateInternalId(),
WidgetPosition(1, 1, 10, 4),
WidgetPanel.HOME.id
)

View file

@ -0,0 +1,39 @@
package de.jrpie.android.launcher.preferences.legacy
import android.content.Context
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
import de.jrpie.android.launcher.widgets.ClockWidget
import de.jrpie.android.launcher.widgets.DebugInfoWidget
import de.jrpie.android.launcher.widgets.generateInternalId
import de.jrpie.android.launcher.widgets.updateWidget
fun migratePreferencesFromVersion100(context: Context) {
assert(PREFERENCE_VERSION == 101)
assert(LauncherPreferences.internal().versionCode() == 100)
val widgets = LauncherPreferences.widgets().widgets() ?: setOf()
widgets.forEach { widget ->
when (widget) {
is ClockWidget -> {
val id = widget.id
val newId = generateInternalId()
(context.applicationContext as Application).appWidgetHost.deleteAppWidgetId(id)
widget.delete(context)
widget.id = newId
updateWidget(widget)
}
is DebugInfoWidget -> {
val id = widget.id
val newId = generateInternalId()
(context.applicationContext as Application).appWidgetHost.deleteAppWidgetId(id)
widget.delete(context)
widget.id = newId
updateWidget(widget)
}
else -> {}
}
}
LauncherPreferences.internal().versionCode(101)
}

View file

@ -1,25 +1,24 @@
package de.jrpie.android.launcher.preferences.legacy
import android.content.Context
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
import de.jrpie.android.launcher.widgets.ClockWidget
import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import de.jrpie.android.launcher.widgets.generateInternalId
fun migratePreferencesFromVersion4(context: Context) {
assert(PREFERENCE_VERSION == 100)
assert(LauncherPreferences.internal().versionCode() < 100)
LauncherPreferences.widgets().widgets(
setOf(
ClockWidget(
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
generateInternalId(),
WidgetPosition(1, 3, 10, 4),
WidgetPanel.HOME.id
)
)
)
LauncherPreferences.internal().versionCode(100)
migratePreferencesFromVersion100(context)
}

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
@ -93,15 +72,6 @@ class HomeActivity : UIObject, Activity() {
LauncherPreferences.getSharedPreferences()
.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
(application as Application).appWidgetHost.startListening()
}
override fun onStop() {
(application as Application).appWidgetHost.stopListening()
super.onStop()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
@ -112,7 +82,6 @@ class HomeActivity : UIObject, Activity() {
}
}
private fun updateSettingsFallbackButtonVisibility() {
// If µLauncher settings can not be reached from any action bound to an enabled gesture,
// show the fallback button.
@ -131,81 +100,42 @@ class HomeActivity : UIObject, Activity() {
return modifyTheme(super.getTheme())
}
override fun onPause() {
try {
(application as Application).appWidgetHost.stopListening()
} catch (e: Exception) {
// Throws a NullPointerException on Android 12 an earlier, see #172
e.printStackTrace()
}
super.onPause()
}
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

@ -1,7 +1,6 @@
package de.jrpie.android.launcher.ui
import android.app.AlertDialog
import android.app.Service
import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.PinItemRequest
@ -45,7 +44,7 @@ class PinShortcutActivity : AppCompatActivity(), UIObject {
binding = ActivityPinShortcutBinding.inflate(layoutInflater)
setContentView(binding.root)
val launcherApps = getSystemService(Service.LAUNCHER_APPS_SERVICE) as LauncherApps
val launcherApps = getSystemService(LAUNCHER_APPS_SERVICE) as LauncherApps
val request = launcherApps.getPinItemRequest(intent)
this.request = request
@ -56,7 +55,7 @@ class PinShortcutActivity : AppCompatActivity(), UIObject {
if (request.requestType == PinItemRequest.REQUEST_TYPE_APPWIDGET) {
// TODO
// TODO handle app widgets
request.getAppWidgetProviderInfo(this)
// startActivity()
finish()

View file

@ -0,0 +1,57 @@
package de.jrpie.android.launcher.ui
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.copyToClipboard
import de.jrpie.android.launcher.databinding.ActivityReportCrashBinding
import de.jrpie.android.launcher.getDeviceInfo
import de.jrpie.android.launcher.openInBrowser
import de.jrpie.android.launcher.writeEmail
const val EXTRA_CRASH_LOG = "crashLog"
class ReportCrashActivity : AppCompatActivity() {
// We don't know what caused the crash, so this Activity should use as little functionality as possible.
// In particular it is not a UIObject (and hence looks quite ugly)
private lateinit var binding: ActivityReportCrashBinding
private var report: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialise layout
binding = ActivityReportCrashBinding.inflate(layoutInflater)
setContentView(binding.root)
setTitle(R.string.report_crash_title)
setSupportActionBar(binding.reportCrashAppbar)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
report = intent.getStringExtra(EXTRA_CRASH_LOG)
binding.reportCrashButtonCopy.setOnClickListener {
copyToClipboard(this,
"Device Info:\n${getDeviceInfo()}\n\nCrash Log:\n${report}")
}
binding.reportCrashButtonMail.setOnClickListener {
writeEmail(
this,
getString(R.string.settings_meta_report_bug_mail),
"Crash in μLauncher",
"Hi!\nUnfortunately, μLauncher crashed:\n" +
"\nDevice Info\n\n${getDeviceInfo()}\n\n" +
"\nCrash Log\n\n${report}\n" +
"\nAdditional Information\n\n" +
"[Please add additional information: What did you do when the crash happened? Do you know how to trigger it? ... ]"
)
}
binding.reportCrashButtonReport.setOnClickListener {
openInBrowser(
getString(R.string.settings_meta_report_bug_link),
this
)
}
}
}

View file

@ -17,6 +17,7 @@ import kotlin.math.max
import kotlin.math.min
import kotlin.math.tan
@Suppress("PrivatePropertyName")
class TouchGestureDetector(
private val context: Context,
var width: Int,
@ -34,13 +35,13 @@ class TouchGestureDetector(
private val MIN_TRIANGLE_HEIGHT = 250
private val longPressHandler = Handler(Looper.getMainLooper())
private var systemGestureInsetTop = 100
private var systemGestureInsetBottom = 0
private var systemGestureInsetLeft = 0
private var systemGestureInsetRight = 0
private val longPressHandler = Handler(Looper.getMainLooper())
data class Vector(val x: Float, val y: Float) {
fun absSquared(): Float {

View file

@ -237,9 +237,4 @@ class AppsRecyclerAdapter(
appFilter.favoritesVisibility = v
updateAppsList()
}
fun setHiddenAppsVisibility(v: AppFilter.Companion.AppSetVisibility) {
appFilter.hiddenVisibility = v
updateAppsList()
}
}

View file

@ -96,7 +96,6 @@ class ListFragmentApps : Fragment(), UIObject {
if (LauncherPreferences.functionality().searchAutoCloseKeyboard()) {
addOnScrollListener(object : RecyclerView.OnScrollListener() {
var totalDy: Int = 0
var threshold = (resources.displayMetrics.density * 100).toInt()
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
totalDy += dy

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

@ -5,9 +5,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import de.jrpie.android.launcher.BuildConfig.VERSION_CODE
import de.jrpie.android.launcher.databinding.Tutorial5FinishBinding
import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.requestNotificationPermission
import de.jrpie.android.launcher.setDefaultHomeScreen
import de.jrpie.android.launcher.ui.UIObject
@ -31,8 +31,10 @@ class TutorialFragment5Finish : Fragment(), UIObject {
override fun onStart() {
super<Fragment>.onStart()
super<UIObject>.onStart()
requestNotificationPermission(requireActivity())
}
override fun setOnClicks() {
super.setOnClicks()
binding.tutorialFinishButtonStart.setOnClickListener { finishTutorial() }
@ -44,6 +46,7 @@ class TutorialFragment5Finish : Fragment(), UIObject {
LauncherPreferences.internal().startedTime(System.currentTimeMillis() / 1000L)
}
context?.let { setDefaultHomeScreen(it, checkDefault = true) }
activity?.finish()
}
}

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

@ -61,7 +61,7 @@ open class WidgetContainerView(
it.value.y + it.value.height
).contains(position) == true
}.any {
Widget.byId(context, it.key)?.allowInteraction == false
Widget.byId(it.key)?.allowInteraction == false
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

View file

@ -1,26 +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 ->
@ -55,10 +60,33 @@ class WidgetPanelActivity : Activity(), UIObject {
}
override fun onStart() {
super<Activity>.onStart()
super<LauncherGestureActivity>.onStart()
super<UIObject>.onStart()
}
override fun onPause() {
try {
(application as Application).appWidgetHost.stopListening()
} catch (e: Exception) {
// Throws a NullPointerException on Android 12 an earlier, see #172
e.printStackTrace()
}
super.onPause()
}
override fun onResume() {
super.onResume()
(application as Application).appWidgetHost.startListening()
}
override fun getRootView(): View? {
return binding?.root
}
override fun handleBack() {
finish()
}
override fun isHomeScreen(): Boolean {
return true
}

View file

@ -5,7 +5,6 @@ import android.appwidget.AppWidgetManager
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Resources
import android.graphics.Rect
import android.os.Bundle
import android.util.Log
import android.view.ViewGroup
@ -21,7 +20,6 @@ import de.jrpie.android.launcher.widgets.GRID_SIZE
import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
@ -92,13 +90,24 @@ class ManageWidgetsActivity : UIObject, Activity() {
}
override fun onPause() {
try {
(application as Application).appWidgetHost.stopListening()
} catch (e: Exception) {
// Throws a NullPointerException on Android 12 an earlier, see #172
e.printStackTrace()
}
super.onPause()
}
override fun onResume() {
super.onResume()
(application as Application).appWidgetHost.startListening()
binding.manageWidgetsContainer.updateWidgets(
this,
LauncherPreferences.widgets().widgets()
)
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
@ -121,13 +130,8 @@ class ManageWidgetsActivity : UIObject, Activity() {
private fun selectWidget() {
val appWidgetHost = (application as Application).appWidgetHost
startActivityForResult(
Intent(this, SelectWidgetActivity::class.java).also {
it.putExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetHost.allocateAppWidgetId()
)
it.putExtra(
EXTRA_PANEL_ID,
panelId
@ -140,13 +144,17 @@ class ManageWidgetsActivity : UIObject, Activity() {
private fun createWidget(data: Intent) {
Log.i("Launcher", "creating widget")
val appWidgetManager = (application as Application).appWidgetManager
val appWidgetHost = (application as Application).appWidgetHost
val appWidgetId = data.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return
val provider = appWidgetManager.getAppWidgetInfo(appWidgetId)
val display = windowManager.defaultDisplay
val widgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId)
if (widgetInfo == null) {
Log.w("Launcher", "can't access widget")
appWidgetHost.deleteAppWidgetId(appWidgetId)
return
}
val position = WidgetPosition.findFreeSpace(
WidgetPanel.byId(panelId),
@ -154,7 +162,7 @@ class ManageWidgetsActivity : UIObject, Activity() {
max(3, (GRID_SIZE * (widgetInfo.minHeight) / display.height.toFloat()).roundToInt())
)
val widget = AppWidget(appWidgetId, position, panelId, provider)
val widget = AppWidget(appWidgetId, position, panelId, widgetInfo)
LauncherPreferences.widgets().widgets(
(LauncherPreferences.widgets().widgets() ?: HashSet()).also {
it.add(widget)

View file

@ -14,6 +14,7 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.databinding.ActivitySelectWidgetBinding
import de.jrpie.android.launcher.ui.UIObject
@ -24,7 +25,7 @@ import de.jrpie.android.launcher.widgets.LauncherWidgetProvider
import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition
import de.jrpie.android.launcher.widgets.bindAppWidgetOrRequestPermission
import de.jrpie.android.launcher.widgets.getAppWidgetHost
import de.jrpie.android.launcher.widgets.generateInternalId
import de.jrpie.android.launcher.widgets.getAppWidgetProviders
import de.jrpie.android.launcher.widgets.updateWidget
@ -38,12 +39,13 @@ private const val REQUEST_WIDGET_PERMISSION = 29
*/
class SelectWidgetActivity : AppCompatActivity(), UIObject {
lateinit var binding: ActivitySelectWidgetBinding
var widgetId: Int = -1
var widgetPanelId: Int = WidgetPanel.HOME.id
private fun tryBindWidget(info: LauncherWidgetProvider) {
when (info) {
is LauncherAppWidgetProvider -> {
val widgetId =
(applicationContext as Application).appWidgetHost.allocateAppWidgetId()
if (bindAppWidgetOrRequestPermission(
this,
info.info,
@ -62,7 +64,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
}
}
is LauncherClockWidgetProvider -> {
updateWidget(ClockWidget(widgetId, WidgetPosition(0, 4, 12, 3), widgetPanelId))
updateWidget(ClockWidget(generateInternalId(), WidgetPosition(0, 4, 12, 3), widgetPanelId))
finish()
}
}
@ -81,11 +83,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
setContentView(binding.root)
widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
if (widgetId == -1) {
widgetId = getAppWidgetHost().allocateAppWidgetId()
}
val viewManager = LinearLayoutManager(this)
val viewAdapter = SelectWidgetRecyclerAdapter()

View file

@ -31,19 +31,19 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
WidgetContainerView(widgetPanelId, context, attrs) {
constructor(context: Context, attrs: AttributeSet?) : this(WidgetPanel.HOME.id, context, attrs)
val TOUCH_SLOP: Int
val TOUCH_SLOP_SQUARE: Int
val LONG_PRESS_TIMEOUT: Long
val touchSlop: Int
val touchSlopSquare: Int
val longPressTimeout: Long
private var overlayViewById = HashMap<Int, WidgetOverlayView>()
init {
val configuration = ViewConfiguration.get(context)
TOUCH_SLOP = configuration.scaledTouchSlop
TOUCH_SLOP_SQUARE = TOUCH_SLOP * TOUCH_SLOP
touchSlop = configuration.scaledTouchSlop
touchSlopSquare = touchSlop * touchSlop
LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout().toLong()
longPressTimeout = ViewConfiguration.getLongPressTimeout().toLong()
}
@ -113,7 +113,7 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
height
)
selectedWidgetOverlayView = view
selectedWidgetView = widgetViewById[view.widgetId] ?: return true
selectedWidgetView = widgetViewById[view.widgetId]
startWidgetPosition = position
val positionInView = start.minus(Point(position.left, position.top))
@ -127,14 +127,14 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
endInteraction()
}
}, LONG_PRESS_TIMEOUT)
}, longPressTimeout)
}
if (event.actionMasked == MotionEvent.ACTION_MOVE ||
event.actionMasked == MotionEvent.ACTION_UP
) {
val distanceX = event.x - (currentGestureStart?.x ?: return true)
val distanceY = event.y - (currentGestureStart?.y ?: return true)
if (distanceX * distanceX + distanceY * distanceY > TOUCH_SLOP_SQUARE) {
if (distanceX * distanceX + distanceY * distanceY > touchSlopSquare) {
longPressHandler.removeCallbacksAndMessages(null)
}
val view = selectedWidgetOverlayView ?: return true
@ -160,9 +160,8 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
}
if (event.actionMasked == MotionEvent.ACTION_UP) {
longPressHandler.removeCallbacksAndMessages(null)
val id = selectedWidgetOverlayView?.widgetId ?: return true
val widget = Widget.byId(context, id) ?: return true
val widget = Widget.byId(id) ?: return true
widget.position = newPosition
endInteraction()
updateWidget(widget)
@ -176,8 +175,16 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
}
private fun endInteraction() {
startWidgetPosition = null
selectedWidgetOverlayView?.mode = null
synchronized(this) {
longPressHandler.removeCallbacksAndMessages(null)
startWidgetPosition = null
selectedWidgetOverlayView?.mode = null
}
}
override fun onDetachedFromWindow() {
endInteraction()
super.onDetachedFromWindow()
}
override fun updateWidgets(activity: Activity, widgets: Collection<Widget>?) {

View file

@ -54,26 +54,18 @@ class WidgetOverlayView : ViewGroup {
var widgetId: Int = -1
set(newId) {
field = newId
preview = Widget.byId(context, widgetId)?.getPreview(context)
preview = Widget.byId(widgetId)?.getPreview(context)
}
constructor(context: Context) : super(context) {
init(null, 0)
}
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(attrs, 0)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
context,
attrs,
defStyle
) {
init(attrs, defStyle)
}
private fun init(attrs: AttributeSet?, defStyle: Int) { }
)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
@ -101,13 +93,13 @@ class WidgetOverlayView : ViewGroup {
}
fun showPopupMenu() {
val widget = Widget.byId(context, widgetId)?: return
val widget = Widget.byId(widgetId)?: return
val menu = PopupMenu(context, popupAnchor)
menu.menu.let {
it.add(
context.getString(R.string.widget_menu_remove)
).setOnMenuItemClickListener { _ ->
Widget.byId(context, widgetId)?.delete(context)
Widget.byId(widgetId)?.delete(context)
return@setOnMenuItemClickListener true
}
it.add(
@ -126,7 +118,7 @@ class WidgetOverlayView : ViewGroup {
}
fun getHandles(): List<Handle> {
return listOf<Handle>(
return listOf(
Handle(WidgetManagerView.EditMode.TOP,
Rect(HANDLE_EDGE_SIZE, 0, width - HANDLE_EDGE_SIZE, HANDLE_SIZE)),
Handle(WidgetManagerView.EditMode.BOTTOM,

View file

@ -41,7 +41,7 @@ class AppWidget(
id,
position,
panelId,
false,
panelId != WidgetPanel.HOME.id,
widgetProviderInfo.provider.packageName,
widgetProviderInfo.provider.className,
widgetProviderInfo.profile.hashCode()
@ -78,6 +78,10 @@ class AppWidget(
override fun createView(activity: Activity): AppWidgetHostView? {
val providerInfo = activity.getAppWidgetManager().getAppWidgetInfo(id) ?: return null
/* TODO: if providerInfo is null, the corresponding app was probably uninstalled.
There does not seem to be a way to recover the widget when the app is installed again,
hence it should be deleted. */
val view = activity.getAppWidgetHost()
.createView(activity, this.id, providerInfo)

View file

@ -12,14 +12,14 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("widget:clock")
class ClockWidget(
override val id: Int,
override var id: Int,
override var position: WidgetPosition,
override val panelId: Int,
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

@ -12,7 +12,7 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("widget:debuginfo")
class DebugInfoWidget(
override val id: Int,
override var id: Int,
override var position: WidgetPosition,
override val panelId: Int,
override var allowInteraction: Boolean = true

View file

@ -27,7 +27,9 @@ sealed class Widget {
abstract fun configure(activity: Activity, requestCode: Int)
fun delete(context: Context) {
context.getAppWidgetHost().deleteAppWidgetId(id)
if (id >= 0) {
context.getAppWidgetHost().deleteAppWidgetId(id)
}
LauncherPreferences.widgets().widgets(
LauncherPreferences.widgets().widgets()?.also {
@ -36,10 +38,6 @@ sealed class Widget {
)
}
fun getPanel(): WidgetPanel? {
return WidgetPanel.byId(panelId)
}
override fun hashCode(): Int {
return id
}
@ -55,9 +53,9 @@ sealed class Widget {
fun deserialize(serialized: String): Widget {
return Json.decodeFromString(serialized)
}
fun byId(context: Context, id: Int): Widget? {
fun byId(id: Int): Widget? {
// TODO: do some caching
return LauncherPreferences.widgets().widgets().firstOrNull() {
return LauncherPreferences.widgets().widgets().firstOrNull {
it.id == id
}
}

View file

@ -13,12 +13,8 @@ import android.os.UserManager
import android.util.Log
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.preferences.LauncherPreferences
import kotlin.math.min
fun deleteAllWidgets(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.getAppWidgetHost().appWidgetIds.forEach { AppWidget(it).delete(context) }
}
}
/**
* Tries to bind [providerInfo] to the id [id].
@ -29,12 +25,9 @@ fun deleteAllWidgets(context: Context) {
*
* @return true iff the app widget was bound successfully.
*/
fun bindAppWidgetOrRequestPermission(activity: Activity, providerInfo: AppWidgetProviderInfo, id: Int, requestCode: Int? = null): Boolean {
val appWidgetId = if(id == -1) {
activity.getAppWidgetHost().allocateAppWidgetId()
} else { id }
fun bindAppWidgetOrRequestPermission(activity: Activity, providerInfo: AppWidgetProviderInfo, appWidgetId: Int, requestCode: Int? = null): Boolean {
Log.i("Launcher", "Binding new widget ${appWidgetId}")
Log.i("Launcher", "Binding new widget $appWidgetId")
if (!activity.getAppWidgetManager().bindAppWidgetIdIfAllowed(
appWidgetId,
providerInfo.provider
@ -62,8 +55,8 @@ fun getAppWidgetProviders( context: Context ): List<LauncherWidgetProvider> {
(context.getSystemService(Service.USER_SERVICE) as UserManager).userProfiles
}
list.addAll(
profiles.map {
appWidgetManager.getInstalledProvidersForProfile(it)
profiles.map { profile ->
appWidgetManager.getInstalledProvidersForProfile(profile)
.map { LauncherAppWidgetProvider(it) }
}.flatten()
)
@ -79,6 +72,13 @@ fun updateWidget(widget: Widget) {
)
}
// TODO: this needs to be improved
fun generateInternalId(): Int {
val minId = min(-5,(LauncherPreferences.widgets().widgets() ?: setOf()).minOfOrNull { it.id } ?: 0)
return minId -1
}
fun updateWidgetPanel(widgetPanel: WidgetPanel) {
LauncherPreferences.widgets().customPanels(
(LauncherPreferences.widgets().customPanels() ?: setOf())
@ -92,4 +92,4 @@ fun Context.getAppWidgetHost(): AppWidgetHost {
}
fun Context.getAppWidgetManager(): AppWidgetManager {
return (this.applicationContext as Application).appWidgetManager
}
}

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:textColor"
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z" />
</vector>

View file

@ -40,6 +40,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:contentDescription="@string/content_description_close"
android:gravity="center"
android:includeFontPadding="true"
android:paddingLeft="16sp"
@ -64,6 +65,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/content_description_add_widget_panel"
android:src="@drawable/baseline_add_24"
app:layout_anchor="@+id/manage_widget_panels_recycler"
app:layout_anchorGravity="end|bottom"

View file

@ -20,6 +20,7 @@
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:contentDescription="@string/content_description_add_widget"
android:focusable="true"
android:src="@drawable/baseline_add_24"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -44,6 +44,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:contentDescription="@string/content_description_close"
android:gravity="center"
android:includeFontPadding="true"
android:paddingLeft="16sp"

View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".ui.ReportCrashActivity">
<com.google.android.material.appbar.AppBarLayout
android:background="@null"
app:elevation="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/report_crash_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<de.jrpie.android.launcher.ui.util.HtmlTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crash_info" />
<Button
android:id="@+id/report_crash_button_copy"
android:text="@string/report_crash_button_copy"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<Button
android:id="@+id/report_crash_button_mail"
android:text="@string/report_crash_button_mail"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/report_crash_button_report"
android:text="@string/report_crash_button_report"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -45,6 +45,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:contentDescription="@string/content_description_close"
android:gravity="center"
android:includeFontPadding="true"
android:paddingLeft="16sp"

View file

@ -62,6 +62,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:contentDescription="@string/content_description_close"
android:gravity="center"
android:includeFontPadding="true"
android:paddingLeft="16sp"
@ -72,6 +73,7 @@
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/list_lock"
android:contentDescription="@string/content_description_lock"
android:visibility="gone"
tools:visibility="visible"
android:layout_width="wrap_content"
@ -91,7 +93,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="?attr/colorAccent"
custom:tabTextColor="?attr/android:textColor" />
custom:tabTextColor="?attr/android:textColor"
tools:ignore="SpeakableTextPresentCheck" />
</com.google.android.material.appbar.AppBarLayout>
@ -110,6 +113,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/list_appbar"
app:layout_constraintVertical_bias="0.0"
custom:layout_behavior="@string/appbar_scrolling_view_behavior" />
custom:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:ignore="SpeakableTextPresentCheck" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -11,6 +11,7 @@
android:id="@+id/list_other_row_icon"
android:layout_width="35sp"
android:layout_height="35sp"
android:contentDescription="@null"
android:gravity="center"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -6,55 +6,96 @@
android:id="@+id/list_apps_row_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15sp">
android:layout_marginHorizontal="30sp">
<ImageView
android:id="@+id/list_widgets_row_icon"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:src="@mipmap/ic_launcher_round"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/list_widgets_row_name"
android:layout_width="0dp"
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10sp"
android:layout_marginEnd="10sp"
android:gravity="start"
android:text=""
android:textSize="20sp"
tools:text="some widget"
app:layout_constraintStart_toEndOf="@id/list_widgets_row_icon"
app:cardBackgroundColor="?cardBackgroundColor"
app:cardElevation="8dp"
app:cardCornerRadius="12dp"
app:cardUseCompatPadding="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/list_widgets_row_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="10sp"
android:gravity="start"
android:text=""
android:textSize="12sp"
app:layout_constraintStart_toStartOf="@+id/list_widgets_row_name"
app:layout_constraintTop_toBottomOf="@+id/list_widgets_row_name"
tools:text="a longer description of the widget" />
<ImageView
android:id="@+id/list_widgets_row_preview"
android:layout_width="0dp"
android:maxHeight="100dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:layout_height="100dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/list_widgets_row_description"
tools:src="@mipmap/ic_launcher_round"
tools:ignore="ContentDescription" />
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/icon_title_description_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/list_widgets_row_icon"
android:layout_width="40dp"
android:layout_height="40dp"
tools:src="@mipmap/ic_launcher_round"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/list_widgets_row_name"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginStart="10sp"
android:layout_marginEnd="10sp"
android:text=""
style="@style/TextAppearance.Material3.HeadlineSmall"
tools:text="Some Widget"
app:layout_constraintStart_toEndOf="@id/list_widgets_row_icon"
app:layout_constraintTop_toTopOf="@id/list_widgets_row_icon"
app:layout_constraintBottom_toBottomOf="@id/list_widgets_row_icon"/>
<TextView
android:id="@+id/list_widgets_row_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text=""
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/list_widgets_row_name"
app:layout_constraintTop_toBottomOf="@+id/list_widgets_row_name"
tools:text="A longer description of the widget that may take up multiple lines." />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/icon_title_description_container">
<ImageView
android:id="@+id/list_widgets_row_preview"
android:layout_width="match_parent"
android:layout_height="100dp"
android:maxHeight="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/list_widgets_row_icon"
tools:src="@mipmap/ic_launcher_round"
tools:ignore="ContentDescription,NotSibling" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -20,6 +20,22 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/settings_system"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@string/settings"
android:gravity="center"
android:includeFontPadding="true"
android:paddingLeft="16sp"
android:paddingRight="16sp"
android:src="@drawable/baseline_settings_applications_24"
custom:layout_constraintBottom_toBottomOf="parent"
custom:layout_constraintStart_toStartOf="parent"
custom:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/settings_heading"
android:layout_width="wrap_content"
@ -40,6 +56,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:contentDescription="@string/content_description_close"
android:gravity="center"
android:includeFontPadding="true"
android:paddingLeft="16sp"
@ -48,28 +65,14 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/settings_system"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:gravity="center"
android:includeFontPadding="true"
android:paddingLeft="16sp"
android:paddingRight="16sp"
android:src="@drawable/baseline_settings_applications_24"
custom:layout_constraintBottom_toBottomOf="parent"
custom:layout_constraintStart_toStartOf="parent"
custom:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/settings_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabTextColor="?attr/android:textColor" />
app:tabTextColor="?attr/android:textColor"
tools:ignore="SpeakableTextPresentCheck" />
</com.google.android.material.appbar.AppBarLayout>

View file

@ -68,6 +68,7 @@
android:id="@+id/settings_actions_row_remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/content_description_settings_actions_row_button_remove"
android:padding="8sp"
android:src="@drawable/baseline_close_24"
app:layout_constraintBottom_toBottomOf="parent"

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

@ -26,6 +26,7 @@
android:id="@+id/tutorial_button_back"
android:layout_width="50dp"
android:layout_height="50dp"
android:contentDescription="@string/content_description_navigate_back"
android:gravity="center"
android:alpha="0.5"
android:src="@drawable/baseline_navigate_before_24"
@ -43,12 +44,14 @@
app:layout_constraintStart_toEndOf="@+id/tutorial_button_back"
app:tabBackground="@drawable/tutorial_tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp" />
app:tabIndicatorHeight="0dp"
tools:ignore="SpeakableTextPresentCheck" />
<ImageView
android:id="@+id/tutorial_button_next"
android:layout_width="50dp"
android:layout_height="50dp"
android:contentDescription="@string/content_description_navigate_next"
android:gravity="center"
android:src="@drawable/baseline_navigate_next_24"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -33,17 +33,27 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tutorial_setup_title" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/tutorial_setup_actions_rview_fragment"
android:name="de.jrpie.android.launcher.ui.settings.actions.SettingsFragmentActionsRecycler"
android:layout_width="0dp"
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="32dp"
android:layout_marginBottom="16dp"
app:cardElevation="8dp"
app:cardBackgroundColor="?cardBackgroundColor"
app:cardCornerRadius="12dp"
app:cardUseCompatPadding="true"
app:layout_constraintTop_toBottomOf="@id/tutorial_setup_subtitle"
app:layout_constraintBottom_toTopOf="@id/tutorial_setup_text_bottom"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tutorial_setup_subtitle" />
app:layout_constraintEnd_toEndOf="parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/tutorial_setup_actions_rview_fragment"
android:name="de.jrpie.android.launcher.ui.settings.actions.SettingsFragmentActionsRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginVertical="4dp"
android:layout_marginStart="10dp"/>
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/tutorial_setup_text_bottom"

View file

@ -95,9 +95,9 @@
<string name="settings_display_hide_status_bar">إخفِ شريط الحالة</string>
<string name="settings_display_rotate_screen">تدوير الشاشة</string>
<string name="settings_launcher_section_functionality">الوظائف</string>
<string name="settings_enabled_gestures_double_swipe">أوامر السحب بأصبعين</string>
<string name="settings_enabled_gestures_double_swipe">إيماءات التمرير المزدوج</string>
<string name="settings_enabled_gestures_double_swipe_summary">اسحب باستخدام أصبعين</string>
<string name="settings_enabled_gestures_edge_swipe">أوامر السحب من الحواف</string>
<string name="settings_enabled_gestures_edge_swipe">إيماءات تمرير الحافة</string>
<string name="settings_enabled_gestures_edge_swipe_edge_width">عرض الحواف</string>
<string name="settings_functionality_auto_launch">إظهار نتائج البحث</string>
<string name="settings_functionality_auto_launch_summary">اضغط مسافة لتعطيل هذه الميزة مؤقتًا</string>
@ -162,11 +162,11 @@
<string name="tutorial_concept_text_2">إنه برنامج مجاني (ترخيص MIT)!\nتأكد من مراجعة المستودع!</string>
<string name="tutorial_concept_label_version">النسخة</string>
<string name="tutorial_usage_title">الاستخدام</string>
<string name="tutorial_usage_text">تحتوي شاشتك الرئيسية على التاريخ والوقت المحليين. لا الهاء.</string>
<string name="tutorial_usage_text">تحتوي شاشة المنزل الخاصة بك على التاريخ والوقت المحليين. بدون أي مشتتات.</string>
<string name="tutorial_app_list_title">كل التطبيقات</string>
<string name="tutorial_app_list_text_2">بمجرد تطابق تطبيق واحد فقط، يتم تشغيله تلقائيًا.\nيمكن تعطيل ذلك عن طريق اضافة مساحة في بداية استعلام.</string>
<string name="tutorial_setup_title">الإعداد</string>
<string name="tutorial_setup_text">اخترنا بعض التطبيقات الافتراضية لك. يمكنك تغييرها الآن إذا كنت تريد:</string>
<string name="tutorial_setup_text">اخترنا لك بعض التطبيقات الافتراضية. يمكنك تغييرها الآن إذا كنت ترغب في ذلك:</string>
<string name="tutorial_setup_text_2">يمكنك أيضًا تغيير اختيارك لاحقًا.</string>
<string name="tutorial_finish_title">لنبدأ!</string>
<string name="tutorial_finish_button">ابدأ</string>
@ -235,7 +235,7 @@
<string name="settings_apps_hide_bound_apps">لا تظهر التطبيقات المرتبطة بإيماءة في قائمة التطبيقات</string>
<string name="list_other_list_favorites">درج التطبيقات المفضلة</string>
<string name="list_other_track_next">الموسيقى: التالي</string>
<string name="tutorial_concept_text">تم تصميم μlauncher ليكون فعال، وخالي من الهاء.\n\nلا يحتوي على أي إعلانات ولا يجمع أي بيانات.</string>
<string name="tutorial_concept_text">تم تصميم μLauncher ليكون فعالًا وخاليًا من مشتتات الانتباه.\n\nلا يحتوي على أي إعلانات ولا يجمع أي بيانات.</string>
<string name="tutorial_finish_text">أنت مستعد للبدء!\n\nآمل أن يكون هذا ذا قيمة كبيرة بالنسبة لك!\n\n- المطورين</string>
<string name="toast_private_space_default_home_screen">يجب أن يكون μlauncher الشاشة الرئيسية الافتراضية للوصول إلى مساحة خاصة.</string>
<string name="settings_gesture_description_left_bottom_edge">اسحب إلى اليسار من أسفل الشاشة</string>
@ -245,7 +245,7 @@
<string name="list_app_info">معلومات التطبيق</string>
<string name="list_app_favorite_add">أضف إلى المفضلة</string>
<string name="list_other_expand_notifications_panel">توسيع لوحة الاشعارات</string>
<string name="tutorial_app_list_text">يمكنك البحث بسرعة من خلال جميع التطبيقات في قائمة التطبيقات.\n\nاسحب لأعلى لفتح القائمة، أو ربطها بإيماءة ما.</string>
<string name="tutorial_app_list_text">يمكنك البحث بسرعة في جميع التطبيقات في قائمة التطبيقات.\n\nاسحب لأعلى لفتحها، أو اربطها بإيماءة مختلفة.</string>
<string name="alert_lock_screen_failed">خطأ: فشل في قفل الشاشة. (إذا قمت للتو بترقية التطبيق ، فحاول تعطيل خدمة الوصول وإعادة تمكينها في إعدادات الهاتف)</string>
<string name="settings_gesture_description_down_right_edge">اسحب إلى الأسفل من حافة الشاشة اليمنى</string>
<string name="settings_theme_font_item_serif_monospace">أحادي المسافة مذيل</string>
@ -257,7 +257,7 @@
<string name="dialog_report_bug_info">شكرا لك على المساعدة في تحسين μLauncher!\nيرجى إضافة المعلومات التالية إلى تقرير الأخطاء الخاص بك:</string>
<string name="settings_meta_contact">اتصل بالمطور الأصلي</string>
<string name="list_other_volume_down">اخفض الصوت</string>
<string name="tutorial_usage_text_2">يمكنك تشغيل أهم تطبيقاتك بوسطة إيماءات اللمس أو الضغط على الأزرار.</string>
<string name="tutorial_usage_text_2">يمكنك تشغيل أهم تطبيقاتك بإيماءات اللمس أو الضغط على الأزرار.</string>
<string name="settings_enabled_gestures_edge_swipe_summary">اسحب من حواف الشاشة</string>
<string name="toast_lock_screen_not_supported">خطأ: قفل الشاشة باستخدام إمكانية الوصول غير مدعوم على هذا الجهاز. الرجاء استخدام طريقة مسؤول الجهاز بدلاً من ذلك.</string>
<string name="accessibility_service_description">يتيح تعيين μlauncher كخدمة إمكانية الوصول قفل الشاشة وفتح قائمة التطبيقات الحديثة. يرجى ملاحظة أنه يتطلب كمية كبيرة من الأذونات. يجب ألا تمنح مثل هذه الأذونات باستخفاف لأي تطبيق. سوف يستخدم μlauncher خدمة إمكانية الوصول فقط لأداء الإجراءات التالية عند طلب المستخدم: * قفل شاشة * فتح التطبيقات الحديثة μlauncher لن يستخدم أبدًا خدمة إمكانية الوصول لجمع البيانات. يمكنك التحقق من شيفرة المصدر للتأكد. يرجى ملاحظة أنه يمكنك قفل الشاشة من خلال منح أذونات مسؤول الجهاز، لكنها لا تعمل مع بصمات الأصابع وفتح الوجه.</string>
@ -288,11 +288,13 @@
يمكنك تغيير اختيارك لاحقًا في الإعدادات.
]]></string>
<string name="widget_clock_description">الساعة الافتراضية لـ μLauncher</string>
<string name="dialog_select_widget_panel_info_no_panels">&lt;![CDATA[لم يتم العثور على لوحات عناصر واجهة المستخدم. يمكنك إنشاء لوحات الأدوات في الإعدادات &gt; المشغّل &gt; إدارة لوحات الأدوات.]].</string>
<string name="dialog_select_widget_panel_info_no_panels"><![CDATA[
لم يتم العثور على أي لوحات أدوات. يمكنك إنشاء لوحات أدوات في الإعدادات > المشغل > إدارة لوحات الأدوات.
]]></string>
<string name="select_widget_title">اختر الأداة</string>
<string name="widget_menu_remove">إزالة</string>
<string name="widget_menu_configure">تهيئة</string>
<string name="widget_menu_enable_interaction">Enable Interaction</string>
<string name="widget_menu_enable_interaction">تمكين التفاعل</string>
<string name="widget_menu_disable_interaction">تعطيل التفاعل</string>
<string name="widget_clock_label">ساعة</string>
<string name="manage_widget_panels_delete">حذف</string>
@ -305,12 +307,35 @@
<string name="alert_widget_panel_not_found">لم تعد لوحة الأدوات هذه موجودة.</string>
<string name="settings_launcher_section_widgets">الأدوات</string>
<plurals name="widget_panel_number_of_widgets">
<item quantity="zero">يحتوي على %d أداة.</item>
<item quantity="one">يحتوي على %d أداة.</item>
<item quantity="two">يحتوي على %d أدوات.</item>
<item quantity="few">يحتوي على %d أدوات.</item>
<item quantity="many">يحتوي على %d أداة.</item>
<item quantity="other"></item>
<item quantity="zero">يحتوي على %1$dأداة.</item>
<item quantity="one">يحتوي على %1$d أداة.</item>
<item quantity="two">يحتوي على %1$d أدوات.</item>
<item quantity="few">يحتوي على%1$d أدوات.</item>
<item quantity="many">يحتوي على %1$d أداة.</item>
<item quantity="other">يحتوي على%1$d أداة.</item>
</plurals>
<string name="settings_functionality_auto_close_keyboard">إغلاق لوحة المفاتيح عند التمرير</string>
<string name="crash_info"><![CDATA[
يبدو أن هناك خطأ ما، نعتذر عن ذلك!<br><br>
لأسباب تتعلق بالخصوصية، لا يتم جمع سجلات الأعطال تلقائيًا.<br>
ومع ذلك، فإن السجلات مفيدة جدًا لتصحيح الأخطاء، لذا سأكون ممتنًا جدًا إذا كان بإمكانك إرسال السجل المرفق عبر البريد
أو إنشاء تقرير عن الخطأ على GitHub.<br><br>
يرجى ملاحظة أن سجلات الأعطال قد تحتوي على <strong>معلومات حساسة</strong>، مثل اسم التطبيق الذي حاولت تشغيله.
يرجى <strong>حذف</strong> مثل هذه المعلومات قبل إرسال التقرير.
<h2>ماذا يمكنني أن أفعل الآن؟</h2>
إذا ظهر هذا الخطأ مرة أخرى، يمكنك تجربة عدة أشياء:
<ul>
<li>إيقاف μLauncher بالقوة</li>
<li>مسح بيانات μLauncher&#39;s (<strong>ستفقد إعداداتك!</strong>)</li>
<li>تثبيت إصدار أقدم (<a href="https://github.com/jrpie/Launcher/releases">GitHub</a>, <a href="https://f-droid.org/en/packages/de.jrpie.android.launcher">F-Droid</a>)</li>
</ul>
]]></string>
<string name="notification_crash_title">تعطل μLauncher</string>
<string name="notification_crash_explanation">آسف! انقر للحصول على مزيد من المعلومات.</string>
<string name="report_crash_button_copy">نسخ تقرير التعطل إلى الحافظة</string>
<string name="report_crash_button_mail">إرسال التقرير بالبريد</string>
<string name="report_crash_button_report">إنشاء تقرير خطأ على GitHub</string>
<string name="report_crash_title">تعطل μLauncher</string>
<string name="send_email">إرسال بريد إلكتروني</string>
<string name="notification_channel_crash">الأعطال ومعلومات التصحيح</string>
</resources>

View file

@ -97,9 +97,9 @@
<string name="settings_display_screen_timeout_disabled">Bildschirm nicht ausschalten</string>
<string name="settings_display_rotate_screen">Bildschirm drehen</string>
<string name="settings_launcher_section_functionality">Funktionalität</string>
<string name="settings_enabled_gestures_double_swipe">Doppelte Wischaktionen</string>
<string name="settings_enabled_gestures_double_swipe">Doppelte Wischgesten</string>
<string name="settings_enabled_gestures_double_swipe_summary">Mit zwei Fingern wischen</string>
<string name="settings_enabled_gestures_edge_swipe">Kantenaktionen</string>
<string name="settings_enabled_gestures_edge_swipe">Kantengesten</string>
<string name="settings_enabled_gestures_edge_swipe_edge_width">Kantenbreite</string>
<string name="settings_functionality_auto_launch">Suchergebnis starten</string>
<string name="settings_functionality_search_web_summary">Beim Durchsuchen der Apps Enter drücken, um stattdessen im Internet zu suchen.</string>

View file

@ -93,9 +93,9 @@
<string name="settings_clock_show_seconds">Mostra secondi</string>
<string name="settings_launcher_section_date_time"><![CDATA[Data e ora]]></string>
<string name="settings_enabled_gestures_double_swipe_summary">Scorri con due dita</string>
<string name="settings_enabled_gestures_double_swipe">Azioni a due dita</string>
<string name="settings_enabled_gestures_double_swipe">Scorrimento a due dita</string>
<string name="settings_functionality_auto_launch">Apri il risultato della ricerca</string>
<string name="settings_enabled_gestures_edge_swipe">Azioni sui bordi dello schermo</string>
<string name="settings_enabled_gestures_edge_swipe">Scorrimento sui bordi dello schermo</string>
<string name="settings_enabled_gestures_edge_swipe_summary">Scorri sui bordi dello schermo</string>
<string name="settings_enabled_gestures_edge_swipe_edge_width">Larghezza bordo</string>
<string name="settings_meta_view_code">Codice sorgente</string>
@ -287,4 +287,53 @@
<string name="tutorial_app_list_text">Puoi cercare rapidamente tra tutte le app nella lista app.\n\nScorri su per la lista o associa ad un gesto diverso.</string>
<string name="alert_recent_apps_failed">Errore: impossibile mostrare le app recenti. (Se hai appena aggiornato l\'app, prova a disabilitare e riabilitare il servizio di accessibilità dalle impostazioni del telefono)</string>
<string name="settings_functionality_auto_close_keyboard">Chiudi la tastiera durante lo scorrimento</string>
<string name="settings_widgets_widgets">Gestione widget</string>
<string name="widget_menu_remove">Rimuovi</string>
<string name="settings_widgets_custom_panels">Gestione pannelli widget</string>
<string name="select_widget_title">Scegli widget</string>
<string name="widget_menu_configure">Configura</string>
<string name="widget_menu_enable_interaction">Abilita interazione</string>
<string name="widget_menu_disable_interaction">Disabilita interazione</string>
<string name="widget_clock_label">Orologio</string>
<string name="widget_clock_description">Orologio predefinito di μLauncher</string>
<string name="manage_widget_panels_delete">Elimina</string>
<string name="manage_widget_panels_rename">Rinomina</string>
<string name="widget_panel_default_name">Pannello widget #%1$d</string>
<string name="dialog_ok">Ok</string>
<string name="widget_panels_title">Pannelli widget</string>
<plurals name="widget_panel_number_of_widgets">
<item quantity="one">Contiene %1$d widget.</item>
<item quantity="many">Contiene %1$d widget.</item>
<item quantity="other">Contiene %1$d widget.</item>
</plurals>
<string name="dialog_create_widget_panel_title">Crea nuovo pannello widget</string>
<string name="list_other_open_widget_panel">Apri pannello widget</string>
<string name="alert_widget_panel_not_found">Questo pannello widget non esiste più.</string>
<string name="settings_launcher_section_widgets">Widget</string>
<string name="dialog_select_widget_panel_title">Seleziona pannello widget</string>
<string name="dialog_select_widget_panel_info_no_panels"><![CDATA[Nessun pannello widget trovato. Puoi creare pannelli widget da Impostazioni > Launcher > Gestione Pannelli Widget.]]></string>
<string name="settings_meta_view_docs">Documentazione</string>
<string name="notification_crash_explanation">Scusa! Clicca per altre info.</string>
<string name="notification_crash_title">µLauncher è crashato</string>
<string name="report_crash_button_copy">Copia il rapporto del crash negli appunti</string>
<string name="report_crash_button_mail">Invia il rapporto per email</string>
<string name="report_crash_button_report">Crea un bug report in GitHub</string>
<string name="report_crash_title">µLauncher è crashato</string>
<string name="send_email">Invia email</string>
<string name="notification_channel_crash">Info su crash e debug</string>
<string name="crash_info"><![CDATA[
Sembra che qualcosa sia andato storto, mi scuso per questo!<br><br>
Per motivi di privacy, i log dei crash non sono raccolti automaticamente.<br>
Comunque i log sono molto utili per il debug, quindi ti sarei molto grato se potessi inviarmi i log per email
oppure aprire un bug report su github.<br><br>
Nota che i crash log potrebbero contenere <strong>informazioni sensibili</strong>, come il nome dell\'app che hai provato ad avviare.
Please <strong>redact</strong> such information before sending the report.
<h2>What can I do now?</h2>
If this bug appears again and again, you can try several things:
<ul>
<li>Force stop μLauncher</li>
<li>Clear μLauncher\'s storage (<strong>Your settings will be lost!</strong>)</li>
<li>Install an older version (<a href=\"https://github.com/jrpie/Launcher/releases\">GitHub</a>, <a href=\"https://f-droid.org/en/packages/de.jrpie.android.launcher\">F-Droid</a>)</li>
</ul>
]]></string>
</resources>

View file

@ -1,16 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
-
- Home - Pradžia
-
-->
<string name="alert_cant_open_title">Nepavyksta paleisti programėlės</string>
<string name="alert_cant_open_message">Norite pakeisti nustatymus?</string>
<string name="toast_cant_open_message">Atidarykite nustatymus norėdami pasirinkti šio gesto veiksmą</string>
<!--
-
- Settings
-
-->
<string name="settings_title">Nustatymai</string>
<string name="settings_tab_actions">Veiksmai</string>
<string name="settings_tab_launcher">Paleidimo programėlė</string>
<string name="settings_tab_meta">Apie</string>
<!--
-
- Settings : Apps
-
-->
<string name="settings_gesture_back">Atgal</string>
<string name="settings_gesture_description_back">Grįžimo mygtukas / grįžimo gestas</string>
<string name="settings_gesture_up">Aukštyn</string>
<string name="settings_gesture_description_up">Perbraukimas aukštyn</string>
<string name="settings_gesture_tap_up">Bakstelėkite + aukštyn</string>
<string name="alert_cant_open_title">Nepavyksta paleisti programėlės</string>
<string name="toast_cant_open_message">Atidarykite nustatymus norėdami pasirinkti šio gesto veiksmą</string>
<string name="settings_gesture_up">Aukštyn</string>
<string name="settings_gesture_description_tap_up">Bakstelėjimas ir perbraukimas aukštyn</string>
<string name="settings_gesture_double_up">Dvigubai aukštyn</string>
<string name="settings_gesture_description_double_up">Perbraukite aukštyn dviem pirštais</string>
<string name="settings_gesture_down">Žemyn</string>
<string name="settings_gesture_description_down">Perbraukite žemyn</string>
<string name="settings_gesture_tap_down">Bakstelėkite + žemyn</string>
<string name="settings_gesture_description_tap_down">Bakstelėkite ir perbraukite žemyn</string>
<string name="settings_gesture_double_down">Dvigubai žemyn</string>
<string name="settings_gesture_description_double_down">Perbraukite dviem pirštais</string>
<string name="settings_gesture_left">Kairėje</string>
<string name="settings_gesture_description_left">Perbraukite į kairę</string>
<string name="settings_gesture_tap_left">Bakstelėkite + kairę</string>
<string name="settings_gesture_description_tap_left">Bakstelėkite ir perbraukite į kairę</string>
<string name="settings_gesture_double_left">Dvigubai kairėje</string>
<string name="settings_gesture_description_double_left">Du pirštais perbraukite kairėn</string>
<string name="settings_gesture_right">Dešinė</string>
<string name="settings_gesture_description_right">Perbraukite į dešinę</string>
<string name="settings_gesture_tap_right">Bakstelėkite + dešinė</string>
<string name="settings_gesture_description_tap_right">Bakstelėkite ir perbraukite į dešinę</string>
<string name="settings_gesture_double_right">Dviguba dešinė</string>
<string name="settings_gesture_description_double_right">Perbraukite į dešinę dviem pirštais</string>
<string name="settings_gesture_right_top_edge">Dešinė (viršuje)</string>
<string name="settings_gesture_description_right_top_edge">Perbraukite tiesiai ekrano viršuje</string>
<string name="settings_gesture_right_bottom_edge">Dešinė (apačia)</string>
<string name="settings_gesture_description_right_bottom_edge">Perbraukite tiesiai ekrano apačioje</string>
<string name="settings_gesture_left_bottom_edge">Kairė (apačia)</string>
<string name="settings_gesture_description_left_bottom_edge">Perbraukite į kairę ekrano apačioje</string>
<string name="settings_gesture_left_top_edge">Kairė (viršuje)</string>
<string name="settings_gesture_description_left_top_edge">Perbraukite kairėn ekrano viršuje</string>
<string name="settings_gesture_up_left_edge">Aukštyn (kairysis kraštas)</string>
<string name="settings_gesture_description_up_left_edge">Perbraukite aukštyn kairiajame ekrano krašte</string>
<string name="settings_gesture_up_right_edge">Aukštyn (dešinysis kraštas)</string>
<string name="settings_gesture_description_up_right_edge">Perbraukite aukštyn dešiniajame ekrano krašte</string>
<string name="settings_gesture_down_left_edge">Žemyn (kairysis kraštas)</string>
<string name="settings_gesture_description_down_left_edge">Perbraukite žemyn kairiajame ekrano krašte</string>
<string name="settings_gesture_down_right_edge">Žemyn (dešinysis kraštas)</string>
<string name="settings_gesture_description_down_right_edge">Žemyn (dešinysis kraštas)</string>
<string name="settings_gesture_vol_up">Garsumo didinimo klavišas</string>
<string name="settings_gesture_description_vol_up">Paspauskite mygtuką „Volume Up“</string>
<string name="settings_gesture_vol_down">Volume žemyn klavišas</string>
<string name="settings_gesture_description_vol_down">Paspauskite mygtuką „Volume Down“</string>
<string name="settings_gesture_double_click">Dukart spustelėkite</string>
<string name="settings_gesture_description_double_click">Dukart spustelėkite tuščią sritį</string>
<string name="settings_gesture_long_click">Ilgas spustelėjimas</string>
<string name="settings_gesture_description_long_click">Ilgai spustelėkite tuščią sritį</string>
<string name="settings_gesture_date">Data</string>
<string name="settings_gesture_description_date">Spustelėkite datą</string>
<string name="settings_gesture_time">Laikas</string>
<string name="settings_gesture_description_time">Spustelėkite laiką</string>
<string name="settings_widgets_widgets">Tvarkykite valdiklius</string>
<string name="settings_widgets_custom_panels">Tvarkykite valdiklio skydelius</string>
<string name="settings_apps_choose">Pasirinkite programą</string>
<string name="settings_apps_install">Įdiekite programas</string>
<string name="settings_apps_toast_store_not_found">Parduotuvėje nerasta</string>
<!--
-
- Settings : Launcher
-
-->
<string name="settings_launcher_section_appearance">Išvaizda</string>
<string name="settings_theme_color_theme">Spalvos tema</string>
<string name="settings_theme_color_theme_item_default">Numatytasis</string>
<string name="settings_theme_color_theme_item_dark">Tamsu</string>
<string name="settings_theme_color_theme_item_light">Šviesa</string>
<string name="settings_theme_color_theme_item_dynamic">Dinaminis</string>
<string name="settings_theme_text_shadow">Teksto šešėlis</string>
<string name="settings_theme_background">Fonas (programų sąrašas ir nustatymas)</string>
<string name="settings_theme_background_item_transparent">Skaidrus</string>
<string name="settings_theme_background_item_dim">Dim</string>
<string name="settings_theme_background_item_blur">Blur</string>
<string name="settings_theme_background_item_solid">Solidus</string>
<string name="settings_theme_font">Šriftas</string>
<!-- names for @array/settings_theme_font_values -->
<string name="settings_theme_font_item_system_default">Sistemos numatytasis</string>
<string name="settings_theme_font_item_sans_serif">Be serifo</string>
<string name="settings_theme_font_item_serif">Serifas</string>
<string name="settings_theme_font_item_monospace">Monoerdvė</string>
<string name="settings_theme_font_item_serif_monospace">Serifo monospace</string>
<string name="settings_theme_monochrome_icons">Vienspalvių programų piktogramos</string>
<string name="settings_launcher_section_date_time"><![CDATA[Date & time]]></string>
<string name="settings_clock_color">Spalva</string>
<string name="settings_clock_time_visible">Rodyti laiką</string>
<string name="settings_clock_date_visible">Rodyti datą</string>
<string name="settings_clock_localized">Naudoti lokalizuotą datos formatą</string>
<string name="settings_clock_show_seconds">Rodyti sekundes</string>
<string name="settings_clock_flip_date_time">Apversti datą ir laiką</string>
<string name="settings_theme_wallpaper">Pasirinkite ekrano foną</string>
<string name="settings_launcher_section_display">Ekranas</string>
</resources>

View file

@ -289,9 +289,9 @@
<string name="settings_display_hide_navigation_bar">Navigatiebalk verbergen</string>
<string name="settings_display_rotate_screen">Scherm draaien</string>
<string name="settings_launcher_section_functionality">Functionaliteit</string>
<string name="settings_enabled_gestures_double_swipe">Dubbele veeg acties</string>
<string name="settings_enabled_gestures_double_swipe">Dubbele veeg gebaaren</string>
<string name="settings_enabled_gestures_double_swipe_summary">Met twee vingers vegen</string>
<string name="settings_enabled_gestures_edge_swipe">Hoek-acties</string>
<string name="settings_enabled_gestures_edge_swipe">Hoek-gebaren</string>
<string name="settings_enabled_gestures_edge_swipe_edge_width">Kantbreedte</string>
<string name="settings_functionality_auto_launch">Start zoekresultaten</string>
<string name="settings_functionality_auto_launch_summary">Spatie drukken om deze functie tijdelijk te onderdruken.</string>

View file

@ -260,7 +260,7 @@
<string name="settings_gesture_swipe_v_reverse">V反向</string>
<string name="settings_gesture_swipe_larger_reverse"><![CDATA[>(反向)]]></string>
<string name="settings_gesture_swipe_smaller_reverse"><![CDATA[<(反向)]]></string>
<string name="settings_functionality_auto_launch_summary">启后将直接启动搜索所匹配到的应用,可以通过在搜索内容前添加空格来临时停用该功能。</string>
<string name="settings_functionality_auto_launch_summary">后将直接启动搜索所匹配到的应用,可以通过在搜索内容前添加空格来临时停用该功能。</string>
<string name="settings_list_layout">应用程序列表样式</string>
<string name="pin_shortcut_button_bind">绑定到手势</string>
<string name="list_other_track_play_pause">音乐:播放 / 暂停</string>
@ -285,4 +285,49 @@
<string name="alert_recent_apps_failed">错误:无法展示最近应用屏幕。(如果您刚刚升级了本启动器,请尝试在手机设置中手动禁用再重新启用“无障碍”服务。)</string>
<string name="list_other_launch_other_launcher">启动其他启动器</string>
<string name="settings_functionality_auto_close_keyboard">滚动应用程序列表时自动隐藏键盘</string>
<string name="settings_widgets_widgets">设置小部件</string>
<string name="widget_menu_remove">删除</string>
<string name="widget_menu_enable_interaction">启用交互功能</string>
<string name="widget_menu_disable_interaction">关闭交互功能</string>
<string name="widget_clock_label">时钟</string>
<string name="manage_widget_panels_delete">删除</string>
<string name="manage_widget_panels_rename">重命名</string>
<string name="widget_panel_default_name">小部件面板 #%1$d</string>
<string name="widget_panels_title">小部件面板</string>
<string name="dialog_select_widget_panel_title">选择小部件面板</string>
<string name="list_other_open_widget_panel">打开小部件面板</string>
<string name="dialog_create_widget_panel_title">创建新面板</string>
<string name="dialog_select_widget_panel_info_no_panels"><![CDATA[未发现小部件面板。您可以在“设置 > 启动器 > 设置小部件面板”中进行创建。]]></string>
<string name="settings_launcher_section_widgets">桌面小部件</string>
<string name="alert_widget_panel_not_found">该小部件面板不存在</string>
<string name="widget_clock_description">μLauncher 默认时钟小部件</string>
<string name="select_widget_title">选择小部件</string>
<string name="settings_widgets_custom_panels">设置小部件面板</string>
<string name="dialog_ok">确认</string>
<plurals name="widget_panel_number_of_widgets">
<item quantity="other">包含 %1$d 个小部件。</item>
</plurals>
<string name="notification_crash_title">μLauncher 崩溃了</string>
<string name="notification_crash_explanation">抱歉!点击查看更多信息。</string>
<string name="report_crash_button_copy">复制崩溃报告到剪贴板</string>
<string name="report_crash_button_mail">通过 Email 发送报告</string>
<string name="report_crash_title">μLauncher 崩溃了</string>
<string name="report_crash_button_report">在 GitHub 上创建错误反馈</string>
<string name="send_email">发送 Email</string>
<string name="notification_channel_crash">崩溃和调试信息</string>
<string name="crash_info"><![CDATA[
发生了一些错误,非常抱歉!<br><br>
出于保护隐私的考量,默认不会记录崩溃日志。<br>
但是,日志对应用调试非常有帮助,若您原意,请将所附日志文件通过 Email 发送给我,
或在 GitHub 上创建错误报告,非常感谢!<br><br>
请注意,崩溃日志可能包含<strong>敏感信息</strong>,如,您尝试启动的应用名称。
因此,请在发送报告前,先将此类信息<strong>去除</strong>
<h2>我现在应该怎么做?</h2>
如果这个错误重复发生,您可以做如下尝试:
<ul>
<li>强行停止 μLauncher</li>
<li>清空 μLauncher 的存储空间(<strong>您的设置将被重置!</strong></li>
<li>安装旧版本(<a href=\"https://github.com/jrpie/Launcher/releases\">GitHub</a>, <a href=\"https://f-droid.org/en/packages/de.jrpie.android.launcher\">F-Droid</a></li>
</ul>
]]></string>
</resources>

View file

@ -11,9 +11,5 @@
<color name="lightTheme_background_color">#fff</color>
<color name="lightTheme_accent_color">#9999ff</color>
<color name="lightTheme_text_color">#000</color>
<color name="light_blue_400">#FF29B6F6</color>
<color name="light_blue_600">#FF039BE5</color>
<color name="gray_400">#FFBDBDBD</color>
<color name="gray_600">#FF757575</color>
</resources>

View file

@ -1,11 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="appbar_padding">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="appbar_padding_top">8dp</dimen>
<dimen name="app_icon_side">40dip</dimen>
<dimen name="app_action_height">48dip</dimen>
</resources>

View file

@ -161,7 +161,9 @@
-
-->
<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>
<string name="settings_meta_fork_contact_url" translatable="false">https://s.jrpie.de/contact</string>
<string name="settings_meta_privacy_url" translatable="false">https://s.jrpie.de/android-legal</string>

View file

@ -419,5 +419,41 @@
<string name="list_other_open_widget_panel">Open Widget Panel</string>
<string name="alert_widget_panel_not_found">This widget panel no longer exists.</string>
<string name="settings_launcher_section_widgets">Widgets</string>
<string name="notification_crash_title">μLauncher crashed</string>
<string name="notification_crash_explanation">Sorry! Click for more information.</string>
<string name="crash_info"><![CDATA[
Looks like something went wrong, sorry about that!<br><br>
For privacy reasons, crash logs are not collected automatically.<br>
However logs are very useful for debugging, so I would be very grateful if you could send me the attached log by mail
or create a bug report on github.<br><br>
Note that crash logs might contain <strong>sensitive information</strong>, e.g. the name of an app you tried to launch.
Please <strong>redact</strong> such information before sending the report.
<h2>What can I do now?</h2>
If this bug appears again and again, you can try several things:
<ul>
<li>Force stop μLauncher</li>
<li>Clear μLauncher\'s storage (<strong>Your settings will be lost!</strong>)</li>
<li>Install an older version (<a href=\"https://github.com/jrpie/Launcher/releases\">GitHub</a>, <a href=\"https://f-droid.org/en/packages/de.jrpie.android.launcher\">F-Droid</a>)</li>
</ul>
]]>
</string>
<string name="report_crash_button_copy">Copy crash report to clipboard</string>
<string name="report_crash_button_mail">Send report by mail</string>
<string name="report_crash_button_report">Create bug report on GitHub</string>
<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>
<!--
-
- Content Descriptions
-
-->
<string name="content_description_add_widget">Add widget</string>
<string name="content_description_add_widget_panel">Add widget panel</string>
<string name="content_description_close">Close</string>
<string name="content_description_navigate_back">Navigate back</string>
<string name="content_description_navigate_next">Navigate next</string>
<string name="content_description_lock">Lock</string>
<string name="content_description_settings_actions_row_button_remove">Remove binding</string>
</resources>

View file

@ -14,6 +14,7 @@
<item name="android:buttonStyle">@style/Widget.AppCompat.Button.Colored</item>
<item name="colorButtonNormal">?colorAccent</item>
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
<!--<item name="android:popupMenuStyle">@style/PopupMenuCustom</item>-->
@ -32,6 +33,7 @@
<item name="colorPrimaryDark">@color/darkTheme_background_color</item>
<item name="colorAccent">@color/darkTheme_accent_color</item>
<item name="android:colorBackground">@color/darkTheme_background_color</item>
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
<item name="android:textColor">@color/darkTheme_text_color</item>
</style>
@ -40,6 +42,7 @@
<item name="colorPrimaryDark">@color/finnmglasTheme_background_color</item>
<item name="colorAccent">@color/finnmglasTheme_accent_color</item>
<item name="android:colorBackground">@color/finnmglasTheme_background_color</item>
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
<item name="android:textColor">@color/finnmglasTheme_text_color</item>
</style>
@ -48,6 +51,7 @@
<item name="colorPrimaryDark">@color/lightTheme_background_color</item>
<item name="colorAccent">@color/lightTheme_accent_color</item>
<item name="android:colorBackground">@color/lightTheme_background_color</item>
<item name="cardBackgroundColor">@color/cardview_light_background</item>
<item name="android:textColor">@color/lightTheme_text_color</item>
</style>
@ -57,6 +61,7 @@
<item name="colorPrimaryDark">@color/material_dynamic_primary50</item>
<item name="colorAccent">@color/material_dynamic_tertiary50</item>
<item name="android:colorBackground">@color/material_dynamic_neutral10</item>
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
<item name="android:textColor">@color/material_dynamic_neutral_variant90</item>
</style>

View file

@ -2,7 +2,7 @@
buildscript {
ext.kotlin_version = '2.0.0'
ext.android_plugin_version = '8.9.2'
ext.android_plugin_version = '8.10.0'
repositories {
google()
mavenCentral()
@ -10,7 +10,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:8.9.2'
classpath 'com.android.tools.build:gradle:8.10.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.android.tools.build:gradle:$android_plugin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"

0
docs/_index.md Normal file
View file

View file

@ -1,7 +1,11 @@
# Gestures and Actions
+++
weight = 10
+++
# Actions and Gestures
µLauncher's central mechanism for accessing important functionality quickly
is to bind actions (e.g. launching an app) to gestures (e.g. swiping up).
is to bind actions (e.g., launching an app) to gestures (e.g., swiping up).
These bindings can be configured in µLauncher Settings > ACTIONS.
@ -28,7 +32,7 @@ These bindings can be configured in µLauncher Settings > ACTIONS.
- Tap then swipe up, down, left, or right
To execute these gesture consistently, it is helpful to think of them as double taps,
To execute these gestures consistently, it is helpful to think of them as double taps,
where the finger stays on the screen after the second tap and then does a swipe.
The swipe must start very shortly after the tap ended.
@ -58,14 +62,14 @@ To any of the available gestures, one of the following actions can be bound:
If private space is set up, an icon to (un)lock it is shown on the top right.
- Open µLauncher's settings
- Toggle private space lock
- Lock the screen: This allows to lock the screen.
There are two mechanisms by which the screen can be locked, accessibility service and device admin.
- Lock the screen: This allows you to lock the screen.
There are two mechanisms by which the screen can be locked: accessibility service and device admin.
- Toggle the flashlight
- Raise, lower or adjust volume
- Raise, lower, or adjust volume
- Play or pause media playback
- Skip to previous or next audio track
- Open notifications panel: Might be useful if the top of your screen is broken.
- Open quick settings panel: Why swipe down twice?
- Open [recent apps](https://developer.android.com/guide/components/activities/recents): Requires accessibility service. Can be used as a workaround for a Android bug.
- Open [recent apps](https://developer.android.com/guide/components/activities/recents): Requires accessibility service. It can be used as a workaround for an Android bug.
- Launch another home screen: Allows using another installed home screen temporarily.
- Do nothing: Just prevents showing the message saying that no action is bound to this gesture.
- Do nothing: Simply prevents showing the message that no action is bound to this gesture.

452
docs/alternatives.md Normal file
View file

@ -0,0 +1,452 @@
+++
weight = 100
+++
# 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 contributors, 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 the 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)

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

@ -0,0 +1,52 @@
+++
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 a 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-pressing 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 that 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
@ -12,7 +17,7 @@ cd Launcher
./gradlew assembleDefaultRelease
```
This will create an apk file at `app/build/outputs/apk/default/release/app-default-release-unsigned.apk`.
This will create an APK file at `app/build/outputs/apk/default/release/app-default-release-unsigned.apk`.
Note that you need to sign it:
@ -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 built in debug mode and are only suitable for testing.
{{% /hint %}}

View file

@ -1,15 +1,19 @@
+++
title = 'Differences to the original Launcher'
+++
# Notable changes compared to Finn's Launcher
µLauncher is a fork of [finnmglas's app Launcher](https://github.com/finnmglas/Launcher).
Here is an incomplete list of changes:
<!--The last commit of the original project is [340ee731](https://github.com/jrpie/launcher/commit/340ee7315293b028c060638e058516435bca296a)
The first commit of µLauncher is [cc2e7710](https://github.com/jrpie/launcher/commit/cc2e7710c824549c367d97a81a1646d27c6c8993),
which at the time was still intended as a patch for launcher.
which at the time was still intended as a patch for the launcher.
The decision to create a hard fork was made two years later.-->
- Additional gestures:
- Back
- V,Λ,<,>
- V, Λ, <, >
- Edge gestures: There is a setting to allow distinguishing swiping at the edges of the screen from swiping in the center.
- Compatible with [work profile](https://www.android.com/enterprise/work-profile/), so apps like [Shelter](https://gitea.angry.im/PeterCxy/Shelter) can be used.
@ -31,26 +35,25 @@ The decision to create a hard fork was made two years later.-->
- This app uses the system wallpaper instead of a custom solution.
- The font has been changed to [Hack][hack-font], other fonts can be selected.
- Font Awesome Icons were replaced by Material icons.
- The gear button on the home screen was removed. A smaller button is show at the top right when necessary.
- The gear button on the home screen was removed. A smaller button is shown at the top right when necessary.
## Search
- The search algorithm was modified to prefer matches at the beginning of the app name, i.e. when searching for `"te"`, `"termux"` is sorted before `"notes"`.
- The search algorithm was modified to prefer matches at the beginning of the app name, i.e., when searching for `"te"`, `"termux"` is sorted before `"notes"`.
- The search bar was moved to the bottom of the screen.
## Technical
- Improved gesture detection.
- Different apps set as default.
- Different apps are set as the defaults.
- Package name was changed to `de.jrpie.android.launcher` to avoid clashing with the original app.
- Dropped support for API < 21 (i.e. pre Lollypop)
- Fixed some bugs
- Some refactoring
- Dropped support for API < 21 (i.e., pre Lollypop).
- Fixed some bugs.
- Some refactoring.
The complete list of changes can be viewed [here](https://github.com/jrpie/launcher/compare/340ee731...master).
---
\[original-repo\]: [https://github.com/finnmglas/Launcher](https://github.com/finnmglas/Launcher)
\[hack-font\]: [https://sourcefoundry.org/hack/](https://sourcefoundry.org/hack/)
[original-repo]: https://github.com/finnmglas/Launcher
[hack-font]: https://sourcefoundry.org/hack/

View file

@ -1,11 +1,16 @@
+++
weight = 40
+++
# Contributing
There are several ways to contribute to this app:
* You can add or improve [translations][toolate].
<br><img src="https://toolate.othing.xyz/widget/jrpie-launcher/launcher/horizontal-auto.svg" alt="translation status">
* If you found a bug or have an idea for a new feature you can [join the chat][chat] or open an [issue][issues].
* If you found a bug or have an idea for a new feature, you can [join the chat][chat] or open an [issue][issues].
> Please note that I work on this project in my free time. Thus I might not respond immediately and not all ideas will be implemented.
> Please note that I work on this project in my free time. Thus, I might not respond immediately, and not all ideas will be implemented.
* You can implement a new feature yourself:
- Create a [fork][fork] of this repository.
@ -13,7 +18,7 @@ There are several ways to contribute to this app:
- Open a new pull request.
See [build.md](build.md) for instructions how to build this project.
See [here](/docs/build) for instructions on how to build this project.
The [CI pipeline](https://github.com/jrpie/Launcher/actions) automatically creates debug builds.

7
docs/examples/_index.md Normal file
View file

@ -0,0 +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

@ -0,0 +1,23 @@
+++
title = 'Integration with Termux'
+++
# Termux
&mu;Launcher has no special support for [Termux](https://termux.dev/).
However it is possible to run Termux commands from &mu;Launcher by using [Termux:Widget](https://wiki.termux.com/wiki/Termux:Widget) to create a pinned shortcut and bind that to a gesture.
* Install Termux:Widget.
* Make sure that &mu;Launcher is set as the default home screen.[^1]
* Put the script you want to run into `~/.shortcuts/`.
* Run `am start com.termux.widget/com.termux.widget.TermuxCreateShortcutActivity`. This will create a pinned shortcut which is treated like an app by &mu;Launcher, i.e. open &mu;Launcher's activity to create a shortcut.
<img src="./screenshot1.png"
alt="screenshot"
width="200" height="400">
<img src="./screenshot2.png"
alt="screenshot"
width="200" height="400">
[^1]: Only the default home screen can access shortcuts.

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -1,43 +0,0 @@
# Welcome to the μLauncher Documentation
## What is μLauncher?
µLauncher is an *minimal* and *distraction-free* Android home screen that lets you launch apps using [swipe gestures and button presses](/actions-and-gestured.md).
This project is a fork of [finnmglas's app Launcher](https://github.com/finnmglas/Launcher). An incomplete list of changes can be found [here](https://github.com/wassupluke/Launcher/blob/master/docs/launcher.md).
## Where can I get μLauncher?
[![Get it on F-Droid](https://fdroid.gitlab.io/artwork/badge/get-it-on.png)](https://f-droid.org/packages/de.jrpie.android.launcher/)
[![Get it on Accrescent](https://accrescent.app/badges/get-it-on.png)](https://accrescent.app/app/de.jrpie.android.launcher.accrescent)
[![Get it on Obtainium](https://raw.githubusercontent.com/ImranR98/Obtainium/b1c8ac6f2ab08497189721a788a5763e28ff64cd/assets/graphics/badge_obtainium.png)](https://apps.obtainium.imranr.dev/redirect?r=obtainium://app/{%22id%22:%22de.jrpie.android.launcher%22,%22url%22:%22https://github.com/jrpie/Launcher%22,%22author%22:%22jrpie%22,%22name%22:%22%c2%b5Launcher%22,%22additionalSettings%22:%22{\%22apkFilterRegEx\%22:\%22release\%22,\%22invertAPKFilter\%22:false,\%22about\%22:\%22%c2%b5Launcher%20is%20a%20minimal%20home%20screen.\%22}%22})
[![Get it on GitHub](https://raw.githubusercontent.com/NeoApplications/Neo-Backup/034b226cea5c1b30eb4f6a6f313e4dadcbb0ece4/badge_github.png)](https://github.com/jrpie/launcher/releases/latest)
> You can also [get it on Google Play](https://play.google.com/store/apps/details?id=de.jrpie.android.launcher), but this is not recommend.
## How can I contribute?
See [docs/contribute](/contribute.md)
## Screenshots
![μLauncher Home Screen screenshot](https://github.com/jrpie/launcher/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg)
![μLauncher Settings screenshot](https://github.com/jrpie/launcher/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg)
![μLauncher All Apps list view with icons screenshot](https://github.com/jrpie/launcher/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg)
![μLauncher Favorite Apps list view with icons screenshot](https://github.com/jrpie/launcher/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/4.jpg)
![μLauncher Choose App to bind to gesture screenshot](https://github.com/jrpie/launcher/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/5.jpg)
![μLauncher App options card from list view with icons screenshot](https://github.com/jrpie/launcher/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/7.jpg
)
![μLauncher All Apps list view without icons screenshot](https://github.com/jrpie/launcher/blob/master/fastlane/metadata/android/en-US/images/phoneScreenshots/8.jpg)
<!-- missing μLauncher grid view screenshot-->

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,19 +1,22 @@
# Launcher Settings
+++
weight = 10
+++
Tweaks and customizations can be made from within the Launcher Settings page.
# Settings
These settings let you change wallpapers, change colors and fonts, enable monochrome app icons, change the app drawer layout, and much more.
Tweaks and customizations can be made from within the settings page.
The settings can be opened by binding the Settings action to a gesture (this is especially useful when configuring &mu;Launcher for the first time) or from the settings icon in the app drawer.[^1]
In the following documentation, 'app drawer' will be used to refer to the 'All Apps', 'Favorite Apps' and 'Private Space' views.
[^1]: i.e., the 'All Apps', 'Favorite Apps', and 'Private Space' views.
## Appearance
> ### Choose a wallpaper
### Choose a wallpaper
This triggers Android's mechanism to change the wallpaper using a photos app, file explorer, or native wallpaper setting app.
µLauncher uses the system-wide wallpaper, i.e. this change also affects other launchers.
This triggers Android's mechanism to change the wallpaper using a photo app, file explorer, or native wallpaper setting app.
µLauncher uses the system-wide wallpaper, i.e., this change also affects other launchers.
> ### Font (in-app font)
### Font (in-app font)
Set the font used within the app settings. This setting does not affect the date/time home screen font.
@ -21,17 +24,26 @@ Set the font used within the app settings. This setting does not affect the date
**options:**&nbsp;`Hack`,`System default`,`Sans serif`,`Serif`,`Monospace`,`Serif monospace`
> ### Text Shadow
### Text Shadow
**type:**&nbsp;`toggle`
> ### Background (app list and setting)
### Background (app list and settings)
Defines which background should be used in app drawers, settings, etc.
to increase legibility.
* `Transparent` does not change the wallpaper.
* `Dim` dims the wallpaper.
* `Blur` tries to blur the wallpaper. This is not possible on all devices. Some older devices don't support the operation. Also blur can be temporarily unavailable when the device is in power saving mode. In these case, `Dim` is used as a fallback.
* `Solid` sets the background to a solid color (depending on the color theme). For the light theme only this option is available.
On the home screen and widget panels, the wallpaper is always shown unmodified.
**type:**&nbsp;`dropdown`
**type:**&nbsp;`Transparent`,`Dim`,`Blur`,`Solid`
> ### Monochrome app icons
### Monochrome app icons
Remove coloring from all app icons. Can help decrease visual stimulus when enabled.
@ -39,49 +51,56 @@ Remove coloring from all app icons. Can help decrease visual stimulus when enabl
## Date & Time
> ### Font (home screen)
These settings affect the clock shown on the home screen (or on widget panels).
If the clock is removed, the settings are not used.
Set the home screen font for date and time. This setting does not affect the in-app font.
### Font (home screen)
Set the home screen font for date and time. This setting does not affect the font of other components.
**type:**&nbsp;`dropdown`
**options:**&nbsp;`Hack`,`System default`,`Sans serif`,`Serif`,`Monospace`,`Serif monospace`
> ### Color [`[bug]`](https://github.com/jrpie/launcher/issues/151)
### Color
Set the color for the home screen date and time.
Accepts a HEX color code (consisting of a '#' followed by three sets of two alphanumeric (letters and numbers) characters. A fourth set of two alphanumeric characters may be added to set the transparency of the color.
Accepts a 6-digit RGB or 8-digit ARGB color code characters.[^2]
Note that on Android, the ARGB color format is used, i.e., the alpha component is specified first.
This differs from the more common RGBA, which is used in web development.
[Color wheel picker](https://rgbacolorpicker.com/color-wheel-picker)
**type:**&nbsp;`HEX`,`RGBA`
[^2]: More precisely, everything that is valid input for [parseColor](https://developer.android.com/reference/android/graphics/Color#parseColor(java.lang.String)) can be used.
> ### Use localized date format
Adapt the display of dates and times to the specific conventions of a particular locale or region. Different locales use different date orders (e.g., MM/DD/YYYY in the US, DD/MM/YYYY in Europe).
**type:**&nbsp;`ARGB`
### Use localized date format
Adapt the display of dates and times to the specific conventions of a particular locale or region as set by the system. Different locales use different date orders (e.g., MM/DD/YYYY in the US, DD/MM/YYYY in Europe).
**type:**&nbsp;`toggle`
> ### Show time
### Show time
Show the current time on the home screen.
**type:**&nbsp;`toggle`
> ### Show seconds
### Show seconds
Show the current time down to the second on the home screen.
**type:**&nbsp;`toggle`
> ### Show date
### Show date
Show the current date on the home screen.
**type:**&nbsp;`toggle`
> ### Flip date and time
### Flip date and time
Place the current time above the current date on the home screen.
@ -89,9 +108,9 @@ Place the current time above the current date on the home screen.
## Functionality
> ### Launch search results
### Launch search results
Launches any app that matches user keyboard input when no other apps match.
Launches any app that matches the user's keyboard input when no other apps match.
As you type inside the app drawer, the app narrows down the list of apps shown based on the app title matching your text input.
With the 'launch search results' setting, once only one matching app remains, it is launched immediately.
@ -105,39 +124,39 @@ Press space to temporarily disable this feature and allow text entry without pre
**type:**&nbsp;`toggle`
> ### Search the web
### Search the web
Press return/enter while searching the app list to launch a web search.
Press return while searching the app list to launch a web search.
**type:**&nbsp;`toggle`
> ### Start keyboard for search
### Start keyboard for search
Automatically open the keyboard when the app drawer is opened.
**type:**&nbsp;`toggle`
> ### Double swipe gestures
### Double swipe gestures
Enable double swipe (two finger) gestures in launcher settings. Does not erase gesture bindings if accidentally turned off.
**type:**&nbsp;`toggle`
> ### Edge swipe gestures
### Edge swipe gestures
Enable edge swipe (near edges of screen) gestures in launcher settings. Does not erase gesture bindings if accidentally turned off.
**type:**&nbsp;`toggle`
> ### Edge width
### Edge width
Change how large a margin is used for detecting edge gestures. Shows the edge margin preview when using the slider.
**type:**&nbsp;`slider`
> ### Choose method for locking the screen
### Choose method for locking the screen
There are two methods to lock the screen and unfortunately both have downsides.
There are two methods to lock the screen, and unfortunately, both have downsides.
1. **`Device Admin`**
@ -147,7 +166,7 @@ There are two methods to lock the screen and unfortunately both have downsides.
- Requires excessive privileges.
- μLauncher will use those privileges *only* for locking the screen.
- As a rule of thumb, it is [not recommended](https://android.stackexchange.com/questions/248171/is-it-safe-to-give-accessibility-permission-to-an-app) to grant access to accessibility services on a random app. Always review the [source code](https://github.com/jrpie/Launcher) before granting accessibility permissions so you familiarize yourself with what the code might do.
- As a rule of thumb, it is [not recommended](https://android.stackexchange.com/questions/248171/is-it-safe-to-give-accessibility-permission-to-an-app) to grant access to accessibility services to a random app. Always review the [source code](https://github.com/jrpie/Launcher) before granting accessibility permissions so you can familiarize yourself with what the code might do.
- On some devices, the start-up PIN will no longer be used for encrypting data after activating an accessibility service.
- This can be [reactivated](https://issuetracker.google.com/issues/37010136#comment36) afterwards.
@ -157,32 +176,33 @@ There are two methods to lock the screen and unfortunately both have downsides.
## Apps
> ### Hidden apps
### Hidden apps
Open an app drawer containing only hidden apps.
> ### Don't show apps that are bound to a gesture in the app list
### Don't show apps that are bound to a gesture in the app list
Remove certain apps from the app drawer if they are already accessible via a gesture.
Reduces redundancy and tidies up app drawer.
Reduces redundancy and tidies up the app drawer.
**type:**&nbsp;`toggle`
> ### Hide paused apps
### Hide paused apps
Remove paused apps from the app drawer.
For example an app belonging to the work profile is paused when the work profile is inactive.
For example, an app belonging to the work profile is paused when the work profile is inactive.
**type:**&nbsp;`toggle`
> ### Hide private space from app list
### Hide private space from app list
Remove private space from app drawer.
Remove private space from the app drawer.
Private space apps can be accessed using a separate app drawer, which can be opened with the Private Space action.
**type:**&nbsp;`toggle`
> ### Layout of app list
### Layout of app list
Changes how the apps are displayed when accessing the app drawer.
@ -195,7 +215,7 @@ Changes how the apps are displayed when accessing the app drawer.
**options:**&nbsp;`Default`,`Text`,`Grid`
> ### Reverse the app list
### Reverse the app list
Enable reverse alphabetical sorting of apps in the app drawer.
Useful for keeping apps within easier reach from the keyboard.
@ -204,32 +224,22 @@ Useful for keeping apps within easier reach from the keyboard.
## Display
> ### Rotate screen
### Rotate screen
**type:**&nbsp;`toggle`
> ### Keep screen on
### Keep screen on
**type:**&nbsp;`toggle`
> ### Hide status bar
### Hide status bar
Remove the top status bar from the home screen.
**type:**&nbsp;`toggle`
> ### Hide navigation bar
### Hide navigation bar
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.
Remove the navigation bar from the home screen. Enabling this setting may make it difficult to use the device if gestures are not set up 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`

28
docs/widgets.md Normal file
View file

@ -0,0 +1,28 @@
+++
title = 'Widgets'
weight = 11
+++
# Widgets
&mu;Launcher allows to add [app widgets](https://developer.android.com/develop/ui/views/appwidgets/overview) to the home screen and to widget panels.
Widgets can be added, moved, removed, and configured in `Settings > Manage Widgets`.
It is configurable whether or not interaction with a widget should be enabled.
* If interaction is enabled, touch events are forwarded to the widget as usual.
However, &mu;Launcher [gestures](/docs/actions-and-gestures/) can not be executed in areas where such a widget is present.
* If interaction is disabled, the widget does not respond to any touch events.
This is recommended when using a widget only to display information.
&mu;Launcher's clock behaves similarly to an app widget and can be managed in the same way.[^1]
[^1]: However, it is technically not an app widget and cannot be used with other launchers.
# Widget Panels
Widget panels can contain widgets that are not needed on the home screen.
They can be managed in `Settings > Manage Widget Panels`.
Widget panels can be opened by using the [Open Widget Panel](/docs/actions-and-gestures/#available-actions) action.

View file

@ -0,0 +1,10 @@
* Fixed a bug related to widget causing crashes on Android 12 and earlier making the app unusable
* Fixed some additional bugs related to widgets
* Improved Lithuanian translation (thank you, wassupluke!)
* Improved Arabic translation (thank you, anonymous contributor!)
* Improved Chinese translation (thank you, class0068!)
* Improved Dutch translation (thank you, renar!)
* Improved German translation (thank you, renar!)
* Improved Italian translation (thank you, renar!)
* Improved Portuguese translation (thank you, anonymous contributor!)