mirror of
https://github.com/jrpie/Launcher.git
synced 2025-06-08 02:11:24 +02:00
Merge branch 'master' into migrate-build-files-to-kotlin
This commit is contained in:
commit
8882583f77
54 changed files with 1622 additions and 565 deletions
|
@ -8,6 +8,7 @@
|
||||||
tools:ignore="QueryAllPackagesPermission" />
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
|
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
|
||||||
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".Application"
|
android:name=".Application"
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/launcherBaseTheme"
|
android:theme="@style/launcherBaseTheme"
|
||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.widgets.manage.ManageWidgetPanelsActivity"
|
android:name=".ui.widgets.manage.ManageWidgetPanelsActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
@ -80,6 +82,9 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.LegalInfoActivity"
|
android:name=".ui.LegalInfoActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".ui.ReportCrashActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".actions.lock.LauncherDeviceAdmin"
|
android:name=".actions.lock.LauncherDeviceAdmin"
|
||||||
|
@ -110,5 +115,4 @@
|
||||||
android:resource="@xml/accessibility_service_config" />
|
android:resource="@xml/accessibility_service_config" />
|
||||||
</service>
|
</service>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -25,6 +25,7 @@ import de.jrpie.android.launcher.preferences.resetPreferences
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
|
||||||
const val APP_WIDGET_HOST_ID = 42;
|
const val APP_WIDGET_HOST_ID = 42;
|
||||||
|
@ -106,6 +107,11 @@ class Application : android.app.Application() {
|
||||||
// TODO Error: Invalid resource ID 0x00000000.
|
// TODO Error: Invalid resource ID 0x00000000.
|
||||||
// DynamicColors.applyToActivitiesIfAvailable(this)
|
// DynamicColors.applyToActivitiesIfAvailable(this)
|
||||||
|
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
|
||||||
|
sendCrashNotification(this@Application, throwable)
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||||
torchManager = TorchManager(this)
|
torchManager = TorchManager(this)
|
||||||
|
@ -114,8 +120,6 @@ class Application : android.app.Application() {
|
||||||
appWidgetHost = AppWidgetHost(this.applicationContext, APP_WIDGET_HOST_ID)
|
appWidgetHost = AppWidgetHost(this.applicationContext, APP_WIDGET_HOST_ID)
|
||||||
appWidgetManager = AppWidgetManager.getInstance(this.applicationContext)
|
appWidgetManager = AppWidgetManager.getInstance(this.applicationContext)
|
||||||
|
|
||||||
appWidgetHost.startListening()
|
|
||||||
|
|
||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
LauncherPreferences.init(preferences, this.resources)
|
LauncherPreferences.init(preferences, this.resources)
|
||||||
|
@ -157,6 +161,8 @@ class Application : android.app.Application() {
|
||||||
removeUnusedShortcuts(this)
|
removeUnusedShortcuts(this)
|
||||||
}
|
}
|
||||||
loadApps()
|
loadApps()
|
||||||
|
|
||||||
|
createNotificationChannels(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCustomAppNames(): HashMap<AbstractAppInfo, String> {
|
fun getCustomAppNames(): HashMap<AbstractAppInfo, String> {
|
||||||
|
@ -170,10 +176,4 @@ class Application : android.app.Application() {
|
||||||
apps.postValue(getApps(packageManager, applicationContext))
|
apps.postValue(getApps(packageManager, applicationContext))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTerminate() {
|
|
||||||
appWidgetHost.stopListening()
|
|
||||||
super.onTerminate()
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,6 @@ import android.app.role.RoleManager
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.appwidget.AppWidgetManager
|
|
||||||
import android.appwidget.AppWidgetProvider
|
|
||||||
import android.appwidget.AppWidgetProviderInfo
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.LauncherApps
|
import android.content.pm.LauncherApps
|
||||||
|
@ -227,3 +224,13 @@ fun copyToClipboard(context: Context, text: String) {
|
||||||
val clipData = ClipData.newPlainText("Debug Info", text)
|
val clipData = ClipData.newPlainText("Debug Info", text)
|
||||||
clipboardManager.setPrimaryClip(clipData)
|
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)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
87
app/src/main/java/de/jrpie/android/launcher/Notifications.kt
Normal file
87
app/src/main/java/de/jrpie/android/launcher/Notifications.kt
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package de.jrpie.android.launcher.actions
|
package de.jrpie.android.launcher.actions
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
|
@ -25,11 +26,18 @@ class WidgetPanelAction(val widgetPanelId: Int) : Action {
|
||||||
|
|
||||||
override fun invoke(context: Context, rect: Rect?): Boolean {
|
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()
|
Toast.makeText(context, R.string.alert_widget_panel_not_found, Toast.LENGTH_LONG).show()
|
||||||
} else {
|
} else {
|
||||||
context.startActivity(Intent(context, WidgetPanelActivity::class.java).also {
|
context.startActivity(Intent(context, WidgetPanelActivity::class.java).also {
|
||||||
it.putExtra(EXTRA_PANEL_ID, widgetPanelId)
|
it.putExtra(EXTRA_PANEL_ID, this.widgetPanelId)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -2,7 +2,6 @@ package de.jrpie.android.launcher.preferences
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import de.jrpie.android.launcher.Application
|
|
||||||
import de.jrpie.android.launcher.BuildConfig
|
import de.jrpie.android.launcher.BuildConfig
|
||||||
import de.jrpie.android.launcher.actions.Action
|
import de.jrpie.android.launcher.actions.Action
|
||||||
import de.jrpie.android.launcher.apps.AbstractAppInfo
|
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.AppInfo
|
||||||
import de.jrpie.android.launcher.apps.DetailedAppInfo
|
import de.jrpie.android.launcher.apps.DetailedAppInfo
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1
|
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.migratePreferencesFromVersion2
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
|
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion4
|
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion4
|
||||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
|
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.ui.HomeActivity
|
||||||
import de.jrpie.android.launcher.widgets.ClockWidget
|
import de.jrpie.android.launcher.widgets.ClockWidget
|
||||||
import de.jrpie.android.launcher.widgets.DebugInfoWidget
|
import de.jrpie.android.launcher.widgets.DebugInfoWidget
|
||||||
import de.jrpie.android.launcher.widgets.WidgetPanel
|
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||||
import de.jrpie.android.launcher.widgets.WidgetPosition
|
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.
|
/* Current version of the structure of preferences.
|
||||||
* Increase when breaking changes are introduced and write an appropriate case in
|
* Increase when breaking changes are introduced and write an appropriate case in
|
||||||
* `migratePreferencesToNewVersion`
|
* `migratePreferencesToNewVersion`
|
||||||
*/
|
*/
|
||||||
const val PREFERENCE_VERSION = 100
|
const val PREFERENCE_VERSION = 101
|
||||||
const val UNKNOWN_PREFERENCE_VERSION = -1
|
const val UNKNOWN_PREFERENCE_VERSION = -1
|
||||||
private const val TAG = "Launcher - Preferences"
|
private const val TAG = "Launcher - Preferences"
|
||||||
|
|
||||||
|
@ -64,6 +66,10 @@ fun migratePreferencesToNewVersion(context: Context) {
|
||||||
migratePreferencesFromVersion4(context)
|
migratePreferencesFromVersion4(context)
|
||||||
Log.i(TAG, "migration of preferences complete (4 -> ${PREFERENCE_VERSION}).")
|
Log.i(TAG, "migration of preferences complete (4 -> ${PREFERENCE_VERSION}).")
|
||||||
}
|
}
|
||||||
|
100 -> {
|
||||||
|
migratePreferencesFromVersion100(context)
|
||||||
|
Log.i(TAG, "migration of preferences complete (100 -> ${PREFERENCE_VERSION}).")
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
Log.w(
|
Log.w(
|
||||||
|
@ -76,6 +82,7 @@ fun migratePreferencesToNewVersion(context: Context) {
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Unable to restore preferences:\n${e.stackTrace}")
|
Log.e(TAG, "Unable to restore preferences:\n${e.stackTrace}")
|
||||||
|
sendCrashNotification(context, e)
|
||||||
resetPreferences(context)
|
resetPreferences(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,12 +91,12 @@ fun resetPreferences(context: Context) {
|
||||||
Log.i(TAG, "Resetting preferences")
|
Log.i(TAG, "Resetting preferences")
|
||||||
LauncherPreferences.clear()
|
LauncherPreferences.clear()
|
||||||
LauncherPreferences.internal().versionCode(PREFERENCE_VERSION)
|
LauncherPreferences.internal().versionCode(PREFERENCE_VERSION)
|
||||||
deleteAllWidgets(context)
|
context.getAppWidgetHost().deleteHost()
|
||||||
|
|
||||||
LauncherPreferences.widgets().widgets(
|
LauncherPreferences.widgets().widgets(
|
||||||
setOf(
|
setOf(
|
||||||
ClockWidget(
|
ClockWidget(
|
||||||
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
|
generateInternalId(),
|
||||||
WidgetPosition(1, 3, 10, 4),
|
WidgetPosition(1, 3, 10, 4),
|
||||||
WidgetPanel.HOME.id
|
WidgetPanel.HOME.id
|
||||||
)
|
)
|
||||||
|
@ -101,7 +108,7 @@ fun resetPreferences(context: Context) {
|
||||||
LauncherPreferences.widgets().widgets().also {
|
LauncherPreferences.widgets().widgets().also {
|
||||||
it.add(
|
it.add(
|
||||||
DebugInfoWidget(
|
DebugInfoWidget(
|
||||||
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
|
generateInternalId(),
|
||||||
WidgetPosition(1, 1, 10, 4),
|
WidgetPosition(1, 1, 10, 4),
|
||||||
WidgetPanel.HOME.id
|
WidgetPanel.HOME.id
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -7,19 +7,20 @@ import de.jrpie.android.launcher.preferences.PREFERENCE_VERSION
|
||||||
import de.jrpie.android.launcher.widgets.ClockWidget
|
import de.jrpie.android.launcher.widgets.ClockWidget
|
||||||
import de.jrpie.android.launcher.widgets.WidgetPanel
|
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||||
import de.jrpie.android.launcher.widgets.WidgetPosition
|
import de.jrpie.android.launcher.widgets.WidgetPosition
|
||||||
|
import de.jrpie.android.launcher.widgets.generateInternalId
|
||||||
|
|
||||||
fun migratePreferencesFromVersion4(context: Context) {
|
fun migratePreferencesFromVersion4(context: Context) {
|
||||||
assert(PREFERENCE_VERSION == 100)
|
|
||||||
assert(LauncherPreferences.internal().versionCode() < 100)
|
assert(LauncherPreferences.internal().versionCode() < 100)
|
||||||
|
|
||||||
LauncherPreferences.widgets().widgets(
|
LauncherPreferences.widgets().widgets(
|
||||||
setOf(
|
setOf(
|
||||||
ClockWidget(
|
ClockWidget(
|
||||||
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
|
generateInternalId(),
|
||||||
WidgetPosition(1, 3, 10, 4),
|
WidgetPosition(1, 3, 10, 4),
|
||||||
WidgetPanel.HOME.id
|
WidgetPanel.HOME.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
LauncherPreferences.internal().versionCode(100)
|
LauncherPreferences.internal().versionCode(100)
|
||||||
|
migratePreferencesFromVersion100(context)
|
||||||
}
|
}
|
|
@ -1,16 +1,9 @@
|
||||||
package de.jrpie.android.launcher.ui
|
package de.jrpie.android.launcher.ui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.KeyEvent
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.window.OnBackInvokedDispatcher
|
|
||||||
import de.jrpie.android.launcher.Application
|
import de.jrpie.android.launcher.Application
|
||||||
import de.jrpie.android.launcher.actions.Action
|
import de.jrpie.android.launcher.actions.Action
|
||||||
import de.jrpie.android.launcher.actions.Gesture
|
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.openTutorial
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
import de.jrpie.android.launcher.ui.tutorial.TutorialActivity
|
import de.jrpie.android.launcher.ui.tutorial.TutorialActivity
|
||||||
|
import de.jrpie.android.launcher.ui.util.LauncherGestureActivity
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [HomeActivity] is the actual application Launcher,
|
* [HomeActivity] is the actual application Launcher,
|
||||||
|
@ -32,10 +26,9 @@ import de.jrpie.android.launcher.ui.tutorial.TutorialActivity
|
||||||
* - Setting global variables (preferences etc.)
|
* - Setting global variables (preferences etc.)
|
||||||
* - Opening the [TutorialActivity] on new installations
|
* - Opening the [TutorialActivity] on new installations
|
||||||
*/
|
*/
|
||||||
class HomeActivity : UIObject, Activity() {
|
class HomeActivity : UIObject, LauncherGestureActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivityHomeBinding
|
private lateinit var binding: ActivityHomeBinding
|
||||||
private var touchGestureDetector: TouchGestureDetector? = null
|
|
||||||
|
|
||||||
private var sharedPreferencesListener =
|
private var sharedPreferencesListener =
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
|
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
|
||||||
|
@ -54,35 +47,21 @@ class HomeActivity : UIObject, Activity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super<Activity>.onCreate(savedInstanceState)
|
super<LauncherGestureActivity>.onCreate(savedInstanceState)
|
||||||
super<UIObject>.onCreate()
|
super<UIObject>.onCreate()
|
||||||
|
|
||||||
|
|
||||||
// Initialise layout
|
// Initialise layout
|
||||||
binding = ActivityHomeBinding.inflate(layoutInflater)
|
binding = ActivityHomeBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
setContentView(binding.root)
|
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 {
|
binding.buttonFallbackSettings.setOnClickListener {
|
||||||
LauncherAction.SETTINGS.invoke(this)
|
LauncherAction.SETTINGS.invoke(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
||||||
super.onConfigurationChanged(newConfig)
|
|
||||||
touchGestureDetector?.updateScreenSize(windowManager)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super<Activity>.onStart()
|
super<LauncherGestureActivity>.onStart()
|
||||||
super<UIObject>.onStart()
|
super<UIObject>.onStart()
|
||||||
|
|
||||||
// If the tutorial was not finished, start it
|
// If the tutorial was not finished, start it
|
||||||
|
@ -93,15 +72,6 @@ class HomeActivity : UIObject, Activity() {
|
||||||
LauncherPreferences.getSharedPreferences()
|
LauncherPreferences.getSharedPreferences()
|
||||||
.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
|
.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
|
||||||
|
|
||||||
(application as Application).appWidgetHost.startListening()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun onStop() {
|
|
||||||
(application as Application).appWidgetHost.stopListening()
|
|
||||||
super.onStop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
|
@ -112,7 +82,6 @@ class HomeActivity : UIObject, Activity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateSettingsFallbackButtonVisibility() {
|
private fun updateSettingsFallbackButtonVisibility() {
|
||||||
// If µLauncher settings can not be reached from any action bound to an enabled gesture,
|
// If µLauncher settings can not be reached from any action bound to an enabled gesture,
|
||||||
// show the fallback button.
|
// show the fallback button.
|
||||||
|
@ -131,81 +100,42 @@ class HomeActivity : UIObject, Activity() {
|
||||||
return modifyTheme(super.getTheme())
|
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() {
|
override fun onResume() {
|
||||||
super.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()
|
updateSettingsFallbackButtonVisibility()
|
||||||
|
|
||||||
binding.homeWidgetContainer.updateWidgets(this@HomeActivity,
|
binding.homeWidgetContainer.updateWidgets(this@HomeActivity,
|
||||||
LauncherPreferences.widgets().widgets()
|
LauncherPreferences.widgets().widgets()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(application as Application).appWidgetHost.startListening()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
LauncherPreferences.getSharedPreferences()
|
LauncherPreferences.getSharedPreferences()
|
||||||
.unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
|
.unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("GestureBackNavigation")
|
override fun handleBack() {
|
||||||
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() {
|
|
||||||
Gesture.BACK(this)
|
Gesture.BACK(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getRootView(): View {
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
override fun isHomeScreen(): Boolean {
|
override fun isHomeScreen(): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,39 +1,18 @@
|
||||||
package de.jrpie.android.launcher.ui.settings.meta
|
package de.jrpie.android.launcher.ui.settings.meta
|
||||||
|
|
||||||
import android.content.Context
|
import android.app.AlertDialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.compose.foundation.clickable
|
import android.widget.Button
|
||||||
import androidx.compose.foundation.layout.*
|
import android.widget.TextView
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Warning
|
|
||||||
import androidx.compose.material3.*
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import androidx.compose.ui.platform.ComposeView
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.colorResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import de.jrpie.android.launcher.BuildConfig
|
import de.jrpie.android.launcher.BuildConfig
|
||||||
import de.jrpie.android.launcher.R
|
import de.jrpie.android.launcher.R
|
||||||
import de.jrpie.android.launcher.copyToClipboard
|
import de.jrpie.android.launcher.copyToClipboard
|
||||||
|
import de.jrpie.android.launcher.databinding.SettingsMetaBinding
|
||||||
import de.jrpie.android.launcher.getDeviceInfo
|
import de.jrpie.android.launcher.getDeviceInfo
|
||||||
import de.jrpie.android.launcher.openInBrowser
|
import de.jrpie.android.launcher.openInBrowser
|
||||||
import de.jrpie.android.launcher.openTutorial
|
import de.jrpie.android.launcher.openTutorial
|
||||||
|
@ -51,21 +30,13 @@ import de.jrpie.android.launcher.ui.UIObject
|
||||||
*/
|
*/
|
||||||
class SettingsFragmentMeta : Fragment(), UIObject {
|
class SettingsFragmentMeta : Fragment(), UIObject {
|
||||||
|
|
||||||
|
private lateinit var binding: SettingsMetaBinding
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
return ComposeView(requireContext()).apply {
|
binding = SettingsMetaBinding.inflate(inflater, container, false)
|
||||||
setContent {
|
return binding.root
|
||||||
MaterialTheme {
|
|
||||||
SettingsMetaScreen(
|
|
||||||
context = requireContext(),
|
|
||||||
onResetConfirmed = { requireActivity().finish() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
@ -74,215 +45,102 @@ class SettingsFragmentMeta : Fragment(), UIObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setOnClicks() {
|
override fun setOnClicks() {
|
||||||
// No longer needed as click handlers are defined in Compose
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data class to represent a settings action
|
fun bindURL(view: View, urlRes: Int) {
|
||||||
private data class SettingsAction(
|
view.setOnClickListener {
|
||||||
val textResId: Int,
|
openInBrowser(
|
||||||
val onClick: (Context) -> Unit
|
getString(urlRes),
|
||||||
)
|
requireContext()
|
||||||
|
)
|
||||||
// Composable for the settings meta screen
|
|
||||||
@Composable
|
|
||||||
fun SettingsMetaScreen(
|
|
||||||
context: Context,
|
|
||||||
onResetConfirmed: () -> Unit
|
|
||||||
) {
|
|
||||||
val openAlertDialog = remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(16.dp)
|
|
||||||
) {
|
|
||||||
SettingsButtonList(context, openAlertDialog)
|
|
||||||
if (openAlertDialog.value) {
|
|
||||||
AlertDialogResetSettings(
|
|
||||||
onDismissRequest = { openAlertDialog.value = false },
|
|
||||||
onConfirmation = {
|
|
||||||
openAlertDialog.value = false
|
|
||||||
resetPreferences(context)
|
|
||||||
onResetConfirmed()
|
|
||||||
},
|
|
||||||
dialogTitle = stringResource(R.string.settings_meta_reset),
|
|
||||||
dialogText = stringResource(R.string.settings_meta_reset_confirm),
|
|
||||||
icon = Icons.Default.Warning
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun SettingsMetaScreenPreview() {
|
|
||||||
SettingsMetaScreen(
|
|
||||||
context = LocalContext.current,
|
|
||||||
onResetConfirmed = {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Composable for the scrollable button list and version number
|
|
||||||
@Composable
|
|
||||||
private fun SettingsButtonList(
|
|
||||||
context: Context,
|
|
||||||
openAlertDialog: MutableState<Boolean>
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.verticalScroll(rememberScrollState()),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
val actions = listOf(
|
|
||||||
SettingsAction(R.string.settings_meta_show_tutorial) { openTutorial(it) },
|
|
||||||
SettingsAction(R.string.settings_meta_reset) { openAlertDialog.value = true },
|
|
||||||
SettingsAction(R.string.settings_meta_view_code) {
|
|
||||||
openInBrowser(it.getString(R.string.settings_meta_link_github), it)
|
|
||||||
},
|
|
||||||
SettingsAction(R.string.settings_meta_report_bug) {
|
|
||||||
openInBrowser(it.getString(R.string.settings_meta_report_bug_link), it)
|
|
||||||
},
|
|
||||||
SettingsAction(R.string.settings_meta_join_chat) {
|
|
||||||
openInBrowser(it.getString(R.string.settings_meta_chat_url), it)
|
|
||||||
},
|
|
||||||
SettingsAction(R.string.settings_meta_fork_contact) {
|
|
||||||
openInBrowser(it.getString(R.string.settings_meta_fork_contact_url), it)
|
|
||||||
},
|
|
||||||
SettingsAction(R.string.settings_meta_donate) {
|
|
||||||
openInBrowser(it.getString(R.string.settings_meta_donate_url), it)
|
|
||||||
},
|
|
||||||
SettingsAction(R.string.settings_meta_privacy) {
|
|
||||||
openInBrowser(it.getString(R.string.settings_meta_privacy_url), it)
|
|
||||||
},
|
|
||||||
SettingsAction(R.string.settings_meta_licenses) {
|
|
||||||
it.startActivity(Intent(it, LegalInfoActivity::class.java))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
actions.forEachIndexed { index, action ->
|
|
||||||
SettingsButton(
|
|
||||||
text = stringResource(action.textResId),
|
|
||||||
onClick = { action.onClick(context) }
|
|
||||||
)
|
|
||||||
if (index == 1 || index == 3 || index == 6) {
|
|
||||||
SettingsButtonSpacer()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Version number at the bottom of buttons
|
|
||||||
Text(
|
binding.settingsMetaButtonViewTutorial.setOnClickListener {
|
||||||
text = BuildConfig.VERSION_NAME,
|
openTutorial(requireContext())
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
}
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
textAlign = TextAlign.End,
|
// prompting for settings-reset confirmation
|
||||||
color = colorResource(R.color.finnmglasTheme_text_color),
|
binding.settingsMetaButtonResetSettings.setOnClickListener {
|
||||||
modifier = Modifier
|
AlertDialog.Builder(this.requireContext(), R.style.AlertDialogCustom)
|
||||||
.fillMaxWidth()
|
.setTitle(getString(R.string.settings_meta_reset))
|
||||||
.padding(top = 16.dp, end = 8.dp)
|
.setMessage(getString(R.string.settings_meta_reset_confirm))
|
||||||
.clickable {
|
.setPositiveButton(
|
||||||
val deviceInfo = getDeviceInfo()
|
android.R.string.ok
|
||||||
copyToClipboard(context, deviceInfo)
|
) { _, _ ->
|
||||||
|
resetPreferences(this.requireContext())
|
||||||
|
requireActivity().finish()
|
||||||
}
|
}
|
||||||
)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
Spacer(modifier = Modifier.height(48.dp))
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
AlertDialog.Builder(context, R.style.AlertDialogCustom).apply {
|
||||||
|
setView(R.layout.dialog_report_bug)
|
||||||
|
setTitle(R.string.dialog_report_bug_title)
|
||||||
|
setPositiveButton(R.string.dialog_report_bug_create_report) { _, _ ->
|
||||||
|
openInBrowser(
|
||||||
|
getString(R.string.settings_meta_report_bug_link),
|
||||||
|
requireContext()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
setNegativeButton(R.string.dialog_cancel) { _, _ -> }
|
||||||
|
}.create().also { it.show() }.apply {
|
||||||
|
val info = findViewById<TextView>(R.id.dialog_report_bug_device_info)
|
||||||
|
val buttonClipboard = findViewById<Button>(R.id.dialog_report_bug_button_clipboard)
|
||||||
|
val buttonSecurity = findViewById<Button>(R.id.dialog_report_bug_button_security)
|
||||||
|
info.text = deviceInfo
|
||||||
|
buttonClipboard.setOnClickListener {
|
||||||
|
copyToClipboard(requireContext(), deviceInfo)
|
||||||
|
}
|
||||||
|
info.setOnClickListener {
|
||||||
|
copyToClipboard(requireContext(), deviceInfo)
|
||||||
|
}
|
||||||
|
buttonSecurity.setOnClickListener {
|
||||||
|
openInBrowser(
|
||||||
|
getString(R.string.settings_meta_report_vulnerability_link),
|
||||||
|
requireContext()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// join chat
|
||||||
|
bindURL(binding.settingsMetaButtonJoinChat, R.string.settings_meta_chat_url)
|
||||||
|
|
||||||
|
// contact developer
|
||||||
|
// bindURL(binding.settingsMetaButtonContact, R.string.settings_meta_contact_url)
|
||||||
|
|
||||||
|
// contact fork developer
|
||||||
|
bindURL(binding.settingsMetaButtonForkContact, R.string.settings_meta_fork_contact_url)
|
||||||
|
|
||||||
|
// donate
|
||||||
|
bindURL(binding.settingsMetaButtonDonate, R.string.settings_meta_donate_url)
|
||||||
|
|
||||||
|
// privacy policy
|
||||||
|
bindURL(binding.settingsMetaButtonPrivacy, R.string.settings_meta_privacy_url)
|
||||||
|
|
||||||
|
// legal info
|
||||||
|
binding.settingsMetaButtonLicenses.setOnClickListener {
|
||||||
|
startActivity(Intent(this.context, LegalInfoActivity::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
// version
|
||||||
|
binding.settingsMetaTextVersion.text = BuildConfig.VERSION_NAME
|
||||||
|
binding.settingsMetaTextVersion.setOnClickListener {
|
||||||
|
val deviceInfo = getDeviceInfo()
|
||||||
|
copyToClipboard(requireContext(), deviceInfo)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun SettingsButtonListPreview() {
|
|
||||||
SettingsButtonList(
|
|
||||||
context = LocalContext.current,
|
|
||||||
openAlertDialog = remember { mutableStateOf(false) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Composable for a settings button
|
|
||||||
@Composable
|
|
||||||
fun SettingsButton(
|
|
||||||
text: String,
|
|
||||||
onClick: () -> Unit
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = onClick,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
contentPadding = PaddingValues(8.dp),
|
|
||||||
shape = RoundedCornerShape(4.dp),
|
|
||||||
colors = ButtonDefaults.buttonColors(
|
|
||||||
containerColor = colorResource(R.color.finnmglasTheme_accent_color),
|
|
||||||
contentColor = colorResource(R.color.finnmglasTheme_text_color)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
modifier = Modifier.align(Alignment.CenterVertically),
|
|
||||||
color = colorResource(R.color.finnmglasTheme_text_color)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun SettingsButtonPreview() {
|
|
||||||
SettingsButton(text = "Here's a button preview", onClick = {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Composable for spacing between buttons
|
|
||||||
@Composable
|
|
||||||
fun SettingsButtonSpacer() {
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Composable for the reset settings alert dialog
|
|
||||||
@Composable
|
|
||||||
fun AlertDialogResetSettings(
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
onConfirmation: () -> Unit,
|
|
||||||
dialogTitle: String,
|
|
||||||
dialogText: String,
|
|
||||||
icon: ImageVector
|
|
||||||
) {
|
|
||||||
AlertDialog(
|
|
||||||
icon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = icon,
|
|
||||||
contentDescription = stringResource(android.R.string.dialog_alert_title),
|
|
||||||
tint = MaterialTheme.colorScheme.error
|
|
||||||
)
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Text(text = dialogTitle)
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = dialogText)
|
|
||||||
},
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(onClick = onConfirmation) {
|
|
||||||
Text(stringResource(android.R.string.ok))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = onDismissRequest) {
|
|
||||||
Text(stringResource(android.R.string.cancel))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
properties = DialogProperties(
|
|
||||||
dismissOnBackPress = true,
|
|
||||||
dismissOnClickOutside = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun AlertDialogResetSettingsPreview() {
|
|
||||||
AlertDialogResetSettings(
|
|
||||||
onDismissRequest = {},
|
|
||||||
onConfirmation = {},
|
|
||||||
dialogTitle = "Reset settings",
|
|
||||||
dialogText = "Are you sure you want to reset all settings?",
|
|
||||||
icon = Icons.Default.Warning
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment
|
||||||
import de.jrpie.android.launcher.BuildConfig.VERSION_CODE
|
import de.jrpie.android.launcher.BuildConfig.VERSION_CODE
|
||||||
import de.jrpie.android.launcher.databinding.Tutorial5FinishBinding
|
import de.jrpie.android.launcher.databinding.Tutorial5FinishBinding
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
|
import de.jrpie.android.launcher.requestNotificationPermission
|
||||||
import de.jrpie.android.launcher.setDefaultHomeScreen
|
import de.jrpie.android.launcher.setDefaultHomeScreen
|
||||||
import de.jrpie.android.launcher.ui.UIObject
|
import de.jrpie.android.launcher.ui.UIObject
|
||||||
|
|
||||||
|
@ -31,8 +32,10 @@ class TutorialFragment5Finish : Fragment(), UIObject {
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super<Fragment>.onStart()
|
super<Fragment>.onStart()
|
||||||
super<UIObject>.onStart()
|
super<UIObject>.onStart()
|
||||||
|
requestNotificationPermission(requireActivity())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun setOnClicks() {
|
override fun setOnClicks() {
|
||||||
super.setOnClicks()
|
super.setOnClicks()
|
||||||
binding.tutorialFinishButtonStart.setOnClickListener { finishTutorial() }
|
binding.tutorialFinishButtonStart.setOnClickListener { finishTutorial() }
|
||||||
|
@ -44,6 +47,7 @@ class TutorialFragment5Finish : Fragment(), UIObject {
|
||||||
LauncherPreferences.internal().startedTime(System.currentTimeMillis() / 1000L)
|
LauncherPreferences.internal().startedTime(System.currentTimeMillis() / 1000L)
|
||||||
}
|
}
|
||||||
context?.let { setDefaultHomeScreen(it, checkDefault = true) }
|
context?.let { setDefaultHomeScreen(it, checkDefault = true) }
|
||||||
|
|
||||||
activity?.finish()
|
activity?.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
package de.jrpie.android.launcher.ui.util
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.window.OnBackInvokedDispatcher
|
||||||
|
import de.jrpie.android.launcher.actions.Action
|
||||||
|
import de.jrpie.android.launcher.actions.Gesture
|
||||||
|
import de.jrpie.android.launcher.actions.LauncherAction
|
||||||
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
|
import de.jrpie.android.launcher.ui.TouchGestureDetector
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An activity with a [TouchGestureDetector] as well as handling of volume and back keys set up.
|
||||||
|
*/
|
||||||
|
abstract class LauncherGestureActivity: Activity() {
|
||||||
|
protected var touchGestureDetector: TouchGestureDetector? = null
|
||||||
|
|
||||||
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||||
|
touchGestureDetector?.onTouchEvent(event)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
// Handle back key / gesture on Android 13+, cf. onKeyDown()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
onBackInvokedDispatcher.registerOnBackInvokedCallback(
|
||||||
|
OnBackInvokedDispatcher.PRIORITY_OVERLAY
|
||||||
|
) {
|
||||||
|
handleBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
|
||||||
|
/* This should be initialized in onCreate()
|
||||||
|
However on some devices there seems to be a bug where the touchGestureDetector
|
||||||
|
is not working properly after resuming the app.
|
||||||
|
Reinitializing the touchGestureDetector every time the app is resumed might help to fix that.
|
||||||
|
(see issue #138)
|
||||||
|
*/
|
||||||
|
touchGestureDetector = TouchGestureDetector(
|
||||||
|
this, 0, 0,
|
||||||
|
LauncherPreferences.enabled_gestures().edgeSwipeEdgeWidth() / 100f
|
||||||
|
).also {
|
||||||
|
it.updateScreenSize(windowManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
getRootView()?.setOnApplyWindowInsetsListener { _, windowInsets ->
|
||||||
|
@Suppress("deprecation") // required to support API 29
|
||||||
|
val insets = windowInsets.systemGestureInsets
|
||||||
|
touchGestureDetector?.setSystemGestureInsets(insets)
|
||||||
|
|
||||||
|
windowInsets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("GestureBackNavigation")
|
||||||
|
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
|
when (keyCode) {
|
||||||
|
KeyEvent.KEYCODE_BACK -> {
|
||||||
|
// Only used pre Android 13, cf. onBackInvokedDispatcher
|
||||||
|
handleBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||||
|
if (Action.forGesture(Gesture.VOLUME_UP) == LauncherAction.VOLUME_UP) {
|
||||||
|
// Let the OS handle the key event. This works better with some custom ROMs
|
||||||
|
// and apps like Samsung Sound Assistant.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Gesture.VOLUME_UP(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||||
|
if (Action.forGesture(Gesture.VOLUME_DOWN) == LauncherAction.VOLUME_DOWN) {
|
||||||
|
// see above
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Gesture.VOLUME_DOWN(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
touchGestureDetector?.updateScreenSize(windowManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun getRootView(): View?
|
||||||
|
protected abstract fun handleBack()
|
||||||
|
}
|
|
@ -8,9 +8,11 @@ import androidx.core.view.isVisible
|
||||||
import de.jrpie.android.launcher.actions.Gesture
|
import de.jrpie.android.launcher.actions.Gesture
|
||||||
import de.jrpie.android.launcher.databinding.WidgetClockBinding
|
import de.jrpie.android.launcher.databinding.WidgetClockBinding
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
|
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||||
import java.util.Locale
|
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)
|
val binding: WidgetClockBinding = WidgetClockBinding.inflate(LayoutInflater.from(context), this, true)
|
||||||
init {
|
init {
|
||||||
|
@ -57,7 +59,7 @@ class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId:
|
||||||
binding.clockUpperView.format12Hour = upperFormat
|
binding.clockUpperView.format12Hour = upperFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOnClicks() {
|
private fun setOnClicks() {
|
||||||
binding.clockUpperView.setOnClickListener {
|
binding.clockUpperView.setOnClickListener {
|
||||||
if (LauncherPreferences.clock().flipDateTime()) {
|
if (LauncherPreferences.clock().flipDateTime()) {
|
||||||
Gesture.TIME(context)
|
Gesture.TIME(context)
|
||||||
|
@ -74,7 +76,4 @@ class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,26 +1,31 @@
|
||||||
package de.jrpie.android.launcher.ui.widgets
|
package de.jrpie.android.launcher.ui.widgets
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
import de.jrpie.android.launcher.Application
|
||||||
import de.jrpie.android.launcher.R
|
import de.jrpie.android.launcher.R
|
||||||
import de.jrpie.android.launcher.databinding.ActivityWidgetPanelBinding
|
import de.jrpie.android.launcher.databinding.ActivityWidgetPanelBinding
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
import de.jrpie.android.launcher.ui.UIObject
|
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.ui.widgets.manage.EXTRA_PANEL_ID
|
||||||
import de.jrpie.android.launcher.widgets.WidgetPanel
|
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||||
|
|
||||||
class WidgetPanelActivity : Activity(), UIObject {
|
class WidgetPanelActivity : LauncherGestureActivity(), UIObject {
|
||||||
lateinit var binding: ActivityWidgetPanelBinding
|
var binding: ActivityWidgetPanelBinding? = null
|
||||||
private var widgetPanelId: Int = WidgetPanel.HOME.id
|
|
||||||
|
var widgetPanelId: Int = WidgetPanel.HOME.id
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super<Activity>.onCreate(savedInstanceState)
|
super<LauncherGestureActivity>.onCreate(savedInstanceState)
|
||||||
super<UIObject>.onCreate()
|
super<UIObject>.onCreate()
|
||||||
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
|
|
||||||
val binding = ActivityWidgetPanelBinding.inflate(layoutInflater)
|
val binding = ActivityWidgetPanelBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
|
||||||
|
|
||||||
// The widget container should extend below the status and navigation bars,
|
// The widget container should extend below the status and navigation bars,
|
||||||
// so let's set an empty WindowInsetsListener to prevent it from being moved.
|
// so let's set an empty WindowInsetsListener to prevent it from being moved.
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets ->
|
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets ->
|
||||||
|
@ -55,10 +60,33 @@ class WidgetPanelActivity : Activity(), UIObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super<Activity>.onStart()
|
super<LauncherGestureActivity>.onStart()
|
||||||
super<UIObject>.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 {
|
override fun isHomeScreen(): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.widget.EditText
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import de.jrpie.android.launcher.Application
|
||||||
import de.jrpie.android.launcher.R
|
import de.jrpie.android.launcher.R
|
||||||
import de.jrpie.android.launcher.databinding.ActivityManageWidgetPanelsBinding
|
import de.jrpie.android.launcher.databinding.ActivityManageWidgetPanelsBinding
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||||
|
|
|
@ -92,13 +92,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() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
(application as Application).appWidgetHost.startListening()
|
||||||
|
|
||||||
binding.manageWidgetsContainer.updateWidgets(
|
binding.manageWidgetsContainer.updateWidgets(
|
||||||
this,
|
this,
|
||||||
LauncherPreferences.widgets().widgets()
|
LauncherPreferences.widgets().widgets()
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
|
@ -124,10 +135,6 @@ class ManageWidgetsActivity : UIObject, Activity() {
|
||||||
val appWidgetHost = (application as Application).appWidgetHost
|
val appWidgetHost = (application as Application).appWidgetHost
|
||||||
startActivityForResult(
|
startActivityForResult(
|
||||||
Intent(this, SelectWidgetActivity::class.java).also {
|
Intent(this, SelectWidgetActivity::class.java).also {
|
||||||
it.putExtra(
|
|
||||||
AppWidgetManager.EXTRA_APPWIDGET_ID,
|
|
||||||
appWidgetHost.allocateAppWidgetId()
|
|
||||||
)
|
|
||||||
it.putExtra(
|
it.putExtra(
|
||||||
EXTRA_PANEL_ID,
|
EXTRA_PANEL_ID,
|
||||||
panelId
|
panelId
|
||||||
|
@ -140,13 +147,17 @@ class ManageWidgetsActivity : UIObject, Activity() {
|
||||||
private fun createWidget(data: Intent) {
|
private fun createWidget(data: Intent) {
|
||||||
Log.i("Launcher", "creating widget")
|
Log.i("Launcher", "creating widget")
|
||||||
val appWidgetManager = (application as Application).appWidgetManager
|
val appWidgetManager = (application as Application).appWidgetManager
|
||||||
|
val appWidgetHost = (application as Application).appWidgetHost
|
||||||
val appWidgetId = data.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return
|
val appWidgetId = data.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return
|
||||||
|
|
||||||
val provider = appWidgetManager.getAppWidgetInfo(appWidgetId)
|
|
||||||
|
|
||||||
val display = windowManager.defaultDisplay
|
val display = windowManager.defaultDisplay
|
||||||
|
|
||||||
val widgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId)
|
val widgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId)
|
||||||
|
if (widgetInfo == null) {
|
||||||
|
Log.w("Launcher", "can't access widget")
|
||||||
|
appWidgetHost.deleteAppWidgetId(appWidgetId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val position = WidgetPosition.findFreeSpace(
|
val position = WidgetPosition.findFreeSpace(
|
||||||
WidgetPanel.byId(panelId),
|
WidgetPanel.byId(panelId),
|
||||||
|
@ -154,7 +165,7 @@ class ManageWidgetsActivity : UIObject, Activity() {
|
||||||
max(3, (GRID_SIZE * (widgetInfo.minHeight) / display.height.toFloat()).roundToInt())
|
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(
|
||||||
(LauncherPreferences.widgets().widgets() ?: HashSet()).also {
|
(LauncherPreferences.widgets().widgets() ?: HashSet()).also {
|
||||||
it.add(widget)
|
it.add(widget)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import de.jrpie.android.launcher.Application
|
||||||
import de.jrpie.android.launcher.R
|
import de.jrpie.android.launcher.R
|
||||||
import de.jrpie.android.launcher.databinding.ActivitySelectWidgetBinding
|
import de.jrpie.android.launcher.databinding.ActivitySelectWidgetBinding
|
||||||
import de.jrpie.android.launcher.ui.UIObject
|
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.WidgetPanel
|
||||||
import de.jrpie.android.launcher.widgets.WidgetPosition
|
import de.jrpie.android.launcher.widgets.WidgetPosition
|
||||||
import de.jrpie.android.launcher.widgets.bindAppWidgetOrRequestPermission
|
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.getAppWidgetProviders
|
||||||
import de.jrpie.android.launcher.widgets.updateWidget
|
import de.jrpie.android.launcher.widgets.updateWidget
|
||||||
|
|
||||||
|
@ -38,12 +39,13 @@ private const val REQUEST_WIDGET_PERMISSION = 29
|
||||||
*/
|
*/
|
||||||
class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
||||||
lateinit var binding: ActivitySelectWidgetBinding
|
lateinit var binding: ActivitySelectWidgetBinding
|
||||||
var widgetId: Int = -1
|
|
||||||
var widgetPanelId: Int = WidgetPanel.HOME.id
|
var widgetPanelId: Int = WidgetPanel.HOME.id
|
||||||
|
|
||||||
private fun tryBindWidget(info: LauncherWidgetProvider) {
|
private fun tryBindWidget(info: LauncherWidgetProvider) {
|
||||||
when (info) {
|
when (info) {
|
||||||
is LauncherAppWidgetProvider -> {
|
is LauncherAppWidgetProvider -> {
|
||||||
|
val widgetId =
|
||||||
|
(applicationContext as Application).appWidgetHost.allocateAppWidgetId()
|
||||||
if (bindAppWidgetOrRequestPermission(
|
if (bindAppWidgetOrRequestPermission(
|
||||||
this,
|
this,
|
||||||
info.info,
|
info.info,
|
||||||
|
@ -62,7 +64,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is LauncherClockWidgetProvider -> {
|
is LauncherClockWidgetProvider -> {
|
||||||
updateWidget(ClockWidget(widgetId, WidgetPosition(0, 4, 12, 3), widgetPanelId))
|
updateWidget(ClockWidget(generateInternalId(), WidgetPosition(0, 4, 12, 3), widgetPanelId))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,11 +83,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
|
||||||
widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
|
|
||||||
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
|
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
|
||||||
if (widgetId == -1) {
|
|
||||||
widgetId = getAppWidgetHost().allocateAppWidgetId()
|
|
||||||
}
|
|
||||||
|
|
||||||
val viewManager = LinearLayoutManager(this)
|
val viewManager = LinearLayoutManager(this)
|
||||||
val viewAdapter = SelectWidgetRecyclerAdapter()
|
val viewAdapter = SelectWidgetRecyclerAdapter()
|
||||||
|
|
|
@ -113,7 +113,7 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
|
||||||
height
|
height
|
||||||
)
|
)
|
||||||
selectedWidgetOverlayView = view
|
selectedWidgetOverlayView = view
|
||||||
selectedWidgetView = widgetViewById[view.widgetId] ?: return true
|
selectedWidgetView = widgetViewById[view.widgetId]
|
||||||
startWidgetPosition = position
|
startWidgetPosition = position
|
||||||
|
|
||||||
val positionInView = start.minus(Point(position.left, position.top))
|
val positionInView = start.minus(Point(position.left, position.top))
|
||||||
|
|
|
@ -41,7 +41,7 @@ class AppWidget(
|
||||||
id,
|
id,
|
||||||
position,
|
position,
|
||||||
panelId,
|
panelId,
|
||||||
false,
|
panelId != WidgetPanel.HOME.id,
|
||||||
widgetProviderInfo.provider.packageName,
|
widgetProviderInfo.provider.packageName,
|
||||||
widgetProviderInfo.provider.className,
|
widgetProviderInfo.provider.className,
|
||||||
widgetProviderInfo.profile.hashCode()
|
widgetProviderInfo.profile.hashCode()
|
||||||
|
@ -78,6 +78,10 @@ class AppWidget(
|
||||||
|
|
||||||
override fun createView(activity: Activity): AppWidgetHostView? {
|
override fun createView(activity: Activity): AppWidgetHostView? {
|
||||||
val providerInfo = activity.getAppWidgetManager().getAppWidgetInfo(id) ?: return null
|
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()
|
val view = activity.getAppWidgetHost()
|
||||||
.createView(activity, this.id, providerInfo)
|
.createView(activity, this.id, providerInfo)
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,14 @@ import kotlinx.serialization.Serializable
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("widget:clock")
|
@SerialName("widget:clock")
|
||||||
class ClockWidget(
|
class ClockWidget(
|
||||||
override val id: Int,
|
override var id: Int,
|
||||||
override var position: WidgetPosition,
|
override var position: WidgetPosition,
|
||||||
override val panelId: Int,
|
override val panelId: Int,
|
||||||
override var allowInteraction: Boolean = true
|
override var allowInteraction: Boolean = true
|
||||||
) : Widget() {
|
) : Widget() {
|
||||||
|
|
||||||
override fun createView(activity: Activity): View? {
|
override fun createView(activity: Activity): View {
|
||||||
return ClockView(activity, null, id)
|
return ClockView(activity, null, id, panelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findView(views: Sequence<View>): ClockView? {
|
override fun findView(views: Sequence<View>): ClockView? {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import kotlinx.serialization.Serializable
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("widget:debuginfo")
|
@SerialName("widget:debuginfo")
|
||||||
class DebugInfoWidget(
|
class DebugInfoWidget(
|
||||||
override val id: Int,
|
override var id: Int,
|
||||||
override var position: WidgetPosition,
|
override var position: WidgetPosition,
|
||||||
override val panelId: Int,
|
override val panelId: Int,
|
||||||
override var allowInteraction: Boolean = true
|
override var allowInteraction: Boolean = true
|
||||||
|
|
|
@ -27,7 +27,9 @@ sealed class Widget {
|
||||||
abstract fun configure(activity: Activity, requestCode: Int)
|
abstract fun configure(activity: Activity, requestCode: Int)
|
||||||
|
|
||||||
fun delete(context: Context) {
|
fun delete(context: Context) {
|
||||||
context.getAppWidgetHost().deleteAppWidgetId(id)
|
if (id >= 0) {
|
||||||
|
context.getAppWidgetHost().deleteAppWidgetId(id)
|
||||||
|
}
|
||||||
|
|
||||||
LauncherPreferences.widgets().widgets(
|
LauncherPreferences.widgets().widgets(
|
||||||
LauncherPreferences.widgets().widgets()?.also {
|
LauncherPreferences.widgets().widgets()?.also {
|
||||||
|
|
|
@ -13,12 +13,8 @@ import android.os.UserManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import de.jrpie.android.launcher.Application
|
import de.jrpie.android.launcher.Application
|
||||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
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].
|
* 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.
|
* @return true iff the app widget was bound successfully.
|
||||||
*/
|
*/
|
||||||
fun bindAppWidgetOrRequestPermission(activity: Activity, providerInfo: AppWidgetProviderInfo, id: Int, requestCode: Int? = null): Boolean {
|
fun bindAppWidgetOrRequestPermission(activity: Activity, providerInfo: AppWidgetProviderInfo, appWidgetId: Int, requestCode: Int? = null): Boolean {
|
||||||
val appWidgetId = if(id == -1) {
|
|
||||||
activity.getAppWidgetHost().allocateAppWidgetId()
|
|
||||||
} else { id }
|
|
||||||
|
|
||||||
Log.i("Launcher", "Binding new widget ${appWidgetId}")
|
Log.i("Launcher", "Binding new widget $appWidgetId")
|
||||||
if (!activity.getAppWidgetManager().bindAppWidgetIdIfAllowed(
|
if (!activity.getAppWidgetManager().bindAppWidgetIdIfAllowed(
|
||||||
appWidgetId,
|
appWidgetId,
|
||||||
providerInfo.provider
|
providerInfo.provider
|
||||||
|
@ -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) {
|
fun updateWidgetPanel(widgetPanel: WidgetPanel) {
|
||||||
LauncherPreferences.widgets().customPanels(
|
LauncherPreferences.widgets().customPanels(
|
||||||
(LauncherPreferences.widgets().customPanels() ?: setOf())
|
(LauncherPreferences.widgets().customPanels() ?: setOf())
|
||||||
|
|
11
app/src/main/res/drawable/baseline_bug_report_24.xml
Normal file
11
app/src/main/res/drawable/baseline_bug_report_24.xml
Normal 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>
|
59
app/src/main/res/layout/activity_report_crash.xml
Normal file
59
app/src/main/res/layout/activity_report_crash.xml
Normal 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>
|
|
@ -6,55 +6,96 @@
|
||||||
android:id="@+id/list_apps_row_container"
|
android:id="@+id/list_apps_row_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="15sp">
|
android:layout_marginHorizontal="30sp">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
<ImageView
|
android:layout_width="match_parent"
|
||||||
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"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="10sp"
|
app:cardBackgroundColor="?cardBackgroundColor"
|
||||||
android:layout_marginEnd="10sp"
|
app:cardElevation="8dp"
|
||||||
android:gravity="start"
|
app:cardCornerRadius="12dp"
|
||||||
android:text=""
|
app:cardUseCompatPadding="true"
|
||||||
android:textSize="20sp"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
tools:text="some widget"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/list_widgets_row_icon"
|
|
||||||
app:layout_constraintEnd_toEndOf="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_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
app:layout_constraintTop_toBottomOf="@id/list_widgets_row_description"
|
|
||||||
tools:src="@mipmap/ic_launcher_round"
|
|
||||||
tools:ignore="ContentDescription" />
|
<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>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -41,6 +41,13 @@
|
||||||
android:text="@string/settings_meta_view_code"
|
android:text="@string/settings_meta_view_code"
|
||||||
android:textAllCaps="false" />
|
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
|
<Button
|
||||||
android:id="@+id/settings_meta_button_report_bug"
|
android:id="@+id/settings_meta_button_report_bug"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -33,17 +33,27 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/tutorial_setup_title" />
|
app:layout_constraintTop_toBottomOf="@id/tutorial_setup_title" />
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/tutorial_setup_actions_rview_fragment"
|
android:layout_width="match_parent"
|
||||||
android:name="de.jrpie.android.launcher.ui.settings.actions.SettingsFragmentActionsRecycler"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginTop="32dp"
|
app:cardElevation="8dp"
|
||||||
android:layout_marginBottom="16dp"
|
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_constraintBottom_toTopOf="@id/tutorial_setup_text_bottom"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="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
|
<TextView
|
||||||
android:id="@+id/tutorial_setup_text_bottom"
|
android:id="@+id/tutorial_setup_text_bottom"
|
||||||
|
|
|
@ -97,9 +97,9 @@
|
||||||
<string name="settings_display_screen_timeout_disabled">Bildschirm nicht ausschalten</string>
|
<string name="settings_display_screen_timeout_disabled">Bildschirm nicht ausschalten</string>
|
||||||
<string name="settings_display_rotate_screen">Bildschirm drehen</string>
|
<string name="settings_display_rotate_screen">Bildschirm drehen</string>
|
||||||
<string name="settings_launcher_section_functionality">Funktionalität</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_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_enabled_gestures_edge_swipe_edge_width">Kantenbreite</string>
|
||||||
<string name="settings_functionality_auto_launch">Suchergebnis starten</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>
|
<string name="settings_functionality_search_web_summary">Beim Durchsuchen der Apps Enter drücken, um stattdessen im Internet zu suchen.</string>
|
||||||
|
|
|
@ -93,9 +93,9 @@
|
||||||
<string name="settings_clock_show_seconds">Mostra secondi</string>
|
<string name="settings_clock_show_seconds">Mostra secondi</string>
|
||||||
<string name="settings_launcher_section_date_time"><![CDATA[Data e ora]]></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_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_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_summary">Scorri sui bordi dello schermo</string>
|
||||||
<string name="settings_enabled_gestures_edge_swipe_edge_width">Larghezza bordo</string>
|
<string name="settings_enabled_gestures_edge_swipe_edge_width">Larghezza bordo</string>
|
||||||
<string name="settings_meta_view_code">Codice sorgente</string>
|
<string name="settings_meta_view_code">Codice sorgente</string>
|
||||||
|
@ -287,4 +287,29 @@
|
||||||
<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="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="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_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>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,16 +1,136 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<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="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_title">Nustatymai</string>
|
||||||
|
|
||||||
<string name="settings_tab_actions">Veiksmai</string>
|
<string name="settings_tab_actions">Veiksmai</string>
|
||||||
<string name="settings_tab_launcher">Paleidimo programėlė</string>
|
<string name="settings_tab_launcher">Paleidimo programėlė</string>
|
||||||
<string name="settings_tab_meta">Apie</string>
|
<string name="settings_tab_meta">Apie</string>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
-
|
||||||
|
- Settings : Apps
|
||||||
|
-
|
||||||
|
-->
|
||||||
<string name="settings_gesture_back">Atgal</string>
|
<string name="settings_gesture_back">Atgal</string>
|
||||||
<string name="settings_gesture_description_back">Grįžimo mygtukas / grįžimo gestas</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_description_up">Perbraukimas aukštyn</string>
|
||||||
<string name="settings_gesture_tap_up">Bakstelėkite + 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_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>
|
</resources>
|
||||||
|
|
|
@ -289,9 +289,9 @@
|
||||||
<string name="settings_display_hide_navigation_bar">Navigatiebalk verbergen</string>
|
<string name="settings_display_hide_navigation_bar">Navigatiebalk verbergen</string>
|
||||||
<string name="settings_display_rotate_screen">Scherm draaien</string>
|
<string name="settings_display_rotate_screen">Scherm draaien</string>
|
||||||
<string name="settings_launcher_section_functionality">Functionaliteit</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_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_enabled_gestures_edge_swipe_edge_width">Kantbreedte</string>
|
||||||
<string name="settings_functionality_auto_launch">Start zoekresultaten</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>
|
<string name="settings_functionality_auto_launch_summary">Spatie drukken om deze functie tijdelijk te onderdruken.</string>
|
||||||
|
|
|
@ -285,4 +285,22 @@
|
||||||
<string name="alert_recent_apps_failed">错误:无法展示最近应用屏幕。(如果您刚刚升级了本启动器,请尝试在手机设置中手动禁用再重新启用“无障碍”服务。)</string>
|
<string name="alert_recent_apps_failed">错误:无法展示最近应用屏幕。(如果您刚刚升级了本启动器,请尝试在手机设置中手动禁用再重新启用“无障碍”服务。)</string>
|
||||||
<string name="list_other_launch_other_launcher">启动其他启动器</string>
|
<string name="list_other_launch_other_launcher">启动其他启动器</string>
|
||||||
<string name="settings_functionality_auto_close_keyboard">滚动应用程序列表时自动隐藏键盘</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>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -161,7 +161,9 @@
|
||||||
-
|
-
|
||||||
-->
|
-->
|
||||||
<string name="settings_meta_link_github" translatable="false">https://github.com/jrpie/Launcher</string>
|
<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_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_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_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>
|
<string name="settings_meta_privacy_url" translatable="false">https://s.jrpie.de/android-legal</string>
|
||||||
|
|
|
@ -419,5 +419,29 @@
|
||||||
<string name="list_other_open_widget_panel">Open Widget Panel</string>
|
<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="alert_widget_panel_not_found">This widget panel no longer exists.</string>
|
||||||
<string name="settings_launcher_section_widgets">Widgets</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>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
<item name="android:buttonStyle">@style/Widget.AppCompat.Button.Colored</item>
|
<item name="android:buttonStyle">@style/Widget.AppCompat.Button.Colored</item>
|
||||||
<item name="colorButtonNormal">?colorAccent</item>
|
<item name="colorButtonNormal">?colorAccent</item>
|
||||||
|
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
|
||||||
|
|
||||||
<!--<item name="android:popupMenuStyle">@style/PopupMenuCustom</item>-->
|
<!--<item name="android:popupMenuStyle">@style/PopupMenuCustom</item>-->
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
<item name="colorPrimaryDark">@color/darkTheme_background_color</item>
|
<item name="colorPrimaryDark">@color/darkTheme_background_color</item>
|
||||||
<item name="colorAccent">@color/darkTheme_accent_color</item>
|
<item name="colorAccent">@color/darkTheme_accent_color</item>
|
||||||
<item name="android:colorBackground">@color/darkTheme_background_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>
|
<item name="android:textColor">@color/darkTheme_text_color</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -40,6 +42,7 @@
|
||||||
<item name="colorPrimaryDark">@color/finnmglasTheme_background_color</item>
|
<item name="colorPrimaryDark">@color/finnmglasTheme_background_color</item>
|
||||||
<item name="colorAccent">@color/finnmglasTheme_accent_color</item>
|
<item name="colorAccent">@color/finnmglasTheme_accent_color</item>
|
||||||
<item name="android:colorBackground">@color/finnmglasTheme_background_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>
|
<item name="android:textColor">@color/finnmglasTheme_text_color</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -48,6 +51,7 @@
|
||||||
<item name="colorPrimaryDark">@color/lightTheme_background_color</item>
|
<item name="colorPrimaryDark">@color/lightTheme_background_color</item>
|
||||||
<item name="colorAccent">@color/lightTheme_accent_color</item>
|
<item name="colorAccent">@color/lightTheme_accent_color</item>
|
||||||
<item name="android:colorBackground">@color/lightTheme_background_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>
|
<item name="android:textColor">@color/lightTheme_text_color</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -57,6 +61,7 @@
|
||||||
<item name="colorPrimaryDark">@color/material_dynamic_primary50</item>
|
<item name="colorPrimaryDark">@color/material_dynamic_primary50</item>
|
||||||
<item name="colorAccent">@color/material_dynamic_tertiary50</item>
|
<item name="colorAccent">@color/material_dynamic_tertiary50</item>
|
||||||
<item name="android:colorBackground">@color/material_dynamic_neutral10</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>
|
<item name="android:textColor">@color/material_dynamic_neutral_variant90</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
0
docs/_index.md
Normal file
0
docs/_index.md
Normal file
|
@ -1,4 +1,8 @@
|
||||||
# Gestures and Actions
|
+++
|
||||||
|
weight = 10
|
||||||
|
+++
|
||||||
|
|
||||||
|
# Actions and Gestures
|
||||||
|
|
||||||
µLauncher's central mechanism for accessing important functionality quickly
|
µ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).
|
||||||
|
|
452
docs/alternatives.md
Normal file
452
docs/alternatives.md
Normal 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 contributers, please reference https://www.markdownguide.org/basic-syntax/ -->
|
||||||
|
|
||||||
|
<!-- TEMPLATE
|
||||||
|
|
||||||
|
### <launcher name>
|
||||||
|
|
||||||
|
**License:** `<license type>`
|
||||||
|
[Website](<launcher website url>) | [Repository](<github or other repo url>) | [F-Droid](<fdroid url>)
|
||||||
|
|
||||||
|
<notes can go here>
|
||||||
|
|
||||||
|
**Main mode of interaction:** `<Search | Gestures | App grid>`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
:white_check_mark: | :x: | :grey_question: Search: <list what you can search: e.g., apps, contacts, call history, ...>
|
||||||
|
:white_check_mark: | :x: | :grey_question: Search history
|
||||||
|
:white_check_mark: | :x: | :grey_question: Customizable gestures: <list usable gestures: e.g., up, down, tap+up, ...>
|
||||||
|
:white_check_mark: | :x: | :grey_question: Folders
|
||||||
|
:white_check_mark: | :x: | :grey_question: Tags
|
||||||
|
:white_check_mark: | :x: | :grey_question: Rename apps
|
||||||
|
:white_check_mark: | :x: | :grey_question: Widgets
|
||||||
|
:white_check_mark: | :x: | :grey_question: Private space
|
||||||
|
:white_check_mark: | :x: | :grey_question: Work profile
|
||||||
|
:white_check_mark: | :x: | :grey_question: Pinned shortcuts
|
||||||
|
:white_check_mark: | :x: | :grey_question: Icon packs
|
||||||
|
:white_check_mark: | :x: | :grey_question: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Grid-Based Launchers
|
||||||
|
|
||||||
|
### Discreet Launcher
|
||||||
|
|
||||||
|
**License:** `GPL-3.0`
|
||||||
|
[Website](https://vincent-falzon.com/) | [Repository](https://github.com/falzonv/discreet-launcher) | [F-Droid](https://f-droid.org/en/packages/com.vincent_falzon.discreetlauncher/)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `app grid`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `apps`
|
||||||
|
:x: Search history
|
||||||
|
:x: Customizable gestures
|
||||||
|
:white_check_mark:Folders
|
||||||
|
:x: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:x: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:white_check_mark: Icon packs
|
||||||
|
:x: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fossify
|
||||||
|
|
||||||
|
**License:** `GPL-3.0`
|
||||||
|
[Website](https://www.fossify.org/) | [Repository](https://github.com/FossifyOrg/Launcher) | [F-Droid](https://f-droid.org/en/packages/org.fossify.home/)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `app grid`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `apps`
|
||||||
|
:x: Search history
|
||||||
|
:x: Customizable gestures
|
||||||
|
:white_check_mark: Folders
|
||||||
|
:x: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:white_check_mark: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:x: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:x: Icon packs
|
||||||
|
:white_check_mark: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Lawnchair
|
||||||
|
|
||||||
|
**License:** `Apache License 2.0`
|
||||||
|
[Website](https://lawnchair.app/) | [Repository](https://github.com/LawnchairLauncher/lawnchair)
|
||||||
|
|
||||||
|
Seems to be a regular (grid of apps) launcher.
|
||||||
|
|
||||||
|
**Main mode of interaction:** App grid
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `Apps & Shortcuts` `Web suggestions` `People` `Files` `Android Settings` `Calculator`
|
||||||
|
:white_check_mark: Search history
|
||||||
|
:white_check_mark: Customizable gestures: `double tap` `swipe up` `swipe down` `home button` `back button`
|
||||||
|
:white_check_mark: Folders
|
||||||
|
:x: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:white_check_mark: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:grey_question: Work profile
|
||||||
|
:x: Pinned shortcuts
|
||||||
|
:white_check_mark: Icon packs
|
||||||
|
:white_check_mark: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Rootless Pixel Launcher
|
||||||
|
|
||||||
|
> **Abandoned**
|
||||||
|
|
||||||
|
**License:** `Apache License 2.0`
|
||||||
|
[Repository](https://github.com/amirzaidi/Launcher3)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `App grid`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `Apps`
|
||||||
|
:x: Search history
|
||||||
|
:x: Customizable gestures
|
||||||
|
:x: Folders
|
||||||
|
:x: Tags
|
||||||
|
:x: Rename apps
|
||||||
|
:warning: Widgets `buggy/broken`
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:white_check_mark: Icon packs
|
||||||
|
:x: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Search based
|
||||||
|
|
||||||
|
### Aster Launcher
|
||||||
|
|
||||||
|
> **Abandoned**
|
||||||
|
|
||||||
|
**License:** `GPL-3.0`
|
||||||
|
[Repository](https://github.com/neophtex/AsterLauncher) | [F-Droid](https://f-droid.org/en/packages/com.series.aster.launcher/)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `search`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:warning: Search: `apps` (apps list is buggy/broken) `web`
|
||||||
|
:x: Search history
|
||||||
|
:x: Customizable gestures
|
||||||
|
:x: Folders
|
||||||
|
:x: Tags
|
||||||
|
:x: Rename apps
|
||||||
|
:x: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:x: Icon packs
|
||||||
|
:x: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### KISS Launcher
|
||||||
|
|
||||||
|
**License:** `GPL-3.0`
|
||||||
|
[Website](https://kisslauncher.com/) | [Repository](https://github.com/Neamar/KISS) | [F-Droid](https://f-droid.org/packages/fr.neamar.kiss/)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `Search` `Some gestures available`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `Apps` `Contacts` `Call history`
|
||||||
|
:white_check_mark: Search history
|
||||||
|
:white_check_mark: Customizable gestures: `swipe left` `swipe right` `swipe up` `swipe down` `long press` `double tap`
|
||||||
|
:x: Folders
|
||||||
|
:white_check_mark: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:white_check_mark: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:white_check_mark: Icon packs
|
||||||
|
:grey_question: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Lunar Launcher
|
||||||
|
**License:** `GPL-3.0`
|
||||||
|
[Repository](https://github.com/iamrasel/lunar-launcher) | [F-Droid](https://f-droid.org/en/packages/rasel.lunar.launcher/)
|
||||||
|
|
||||||
|
Even natively supports RSS feeds to homescreen?
|
||||||
|
|
||||||
|
**Main mode of interaction:** `alphabet scroller`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `swipe up` `swipe down` `swipe left` `swipe right` `tap and hold battery indicator/clock` `tap and hold lower part of screen` `double tap` `tap and hold favorite item`
|
||||||
|
:x: Search history
|
||||||
|
:x: Customizable gestures
|
||||||
|
:x: Folders
|
||||||
|
:x: Tags
|
||||||
|
:x: Rename apps
|
||||||
|
:x: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:x: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:x: Icon packs
|
||||||
|
:x: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### OLauncher
|
||||||
|
|
||||||
|
**License:** `GPL-3.0`
|
||||||
|
[Repository](https://github.com/tanujnotes/Olauncher) | [F-Droid](https://f-droid.org/en/packages/app.olauncher/)
|
||||||
|
|
||||||
|
Extremely minimal launcher with lots of forks.
|
||||||
|
|
||||||
|
**Main mode of interaction:** `Search`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `apps`
|
||||||
|
:x: Search history.
|
||||||
|
:white_check_mark: Customizable gestures: `swipe left` `swipe right` `double tap`
|
||||||
|
:x: Folders
|
||||||
|
:x: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:x: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:grey_question: Pinned shortcuts
|
||||||
|
:x: Icon packs
|
||||||
|
:grey_question: Material You
|
||||||
|
|
||||||
|
#### Forks:
|
||||||
|
|
||||||
|
* [Olauncher Clutter Free](https://f-droid.org/en/packages/app.olaunchercf/)
|
||||||
|
* [mLauncher](https://f-droid.org/en/packages/app.mlauncher/)
|
||||||
|
* [CLauncher](https://f-droid.org/en/packages/app.clauncher/) (even more minimalistic, search without feedback)
|
||||||
|
* [CCLauncher](https://f-droid.org/en/packages/app.cclauncher/) (rewrite using compose)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TinyBit Launcher
|
||||||
|
|
||||||
|
**License:** `GPL-3.0`
|
||||||
|
[Repository](https://github.com/TBog/TBLauncher) | [F-Droid](https://f-droid.org/en/packages/rocks.tbog.tblauncher/)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `search` `some gestures`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `apps` `contacts` `web` `maps` `playstore` `youtube`
|
||||||
|
:white_check_mark: Search history
|
||||||
|
:white_check_mark:Customizable gestures: `tap` `double tap` `swipe up` `swipe left` `swipe right` `swipe down on left` `swipe down on right`
|
||||||
|
:x: Folders
|
||||||
|
:white_check_mark: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:white_check_mark: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:white_check_mark: Icon packs
|
||||||
|
:x: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### YAM Launcher
|
||||||
|
|
||||||
|
**License:** `MIT`
|
||||||
|
[Repository](https://codeberg.org/ottoptj/yamlauncher) | [F-Droid](https://f-droid.org/en/packages/eu.ottop.yamlauncher/)
|
||||||
|
|
||||||
|
Similar to OLauncher?
|
||||||
|
|
||||||
|
**Main mode of interaction:** `search` `home screen text buttons`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `apps` `contacts (optional)`
|
||||||
|
:x: Search history
|
||||||
|
:white_check_mark: Customizable gestures: `swipe left` `swipe right`
|
||||||
|
:x: Folders
|
||||||
|
:x: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:x: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:x: Icon packs
|
||||||
|
:white_check_mark: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Directory based
|
||||||
|
|
||||||
|
### folder launcher
|
||||||
|
|
||||||
|
**License:** `MIT`
|
||||||
|
[Repository](https://github.com/Robby-Blue/SimpleFolderLauncher) | [F-Droid](https://f-droid.org/en/packages/me.robbyblue.mylauncher/)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `directory navigation`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `apps`
|
||||||
|
:x: Search history
|
||||||
|
:x: Customizable gestures
|
||||||
|
:white_check_mark: Folders
|
||||||
|
:x: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:white_check_mark: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:x: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:x: Icon packs
|
||||||
|
:x: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ion Launcher
|
||||||
|
|
||||||
|
**License:** `GPL-3.0`
|
||||||
|
[Repository](https://codeberg.org/zagura/ion-launcher) | [F-Droid](https://f-droid.org/en/packages/one.zagura.IonLauncher/)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `App grid` `Search`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:warning: Search: `apps` `contacts: buggy/broken`
|
||||||
|
:white_check_mark: Search history `shows recently launched apps`
|
||||||
|
:x: Customizable gestures
|
||||||
|
:warning: Folders `prebuilt` `not customizable`
|
||||||
|
:x: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:x: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:white_check_mark: Icon packs
|
||||||
|
:x: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gesture based
|
||||||
|
|
||||||
|
### Pie Launcher
|
||||||
|
|
||||||
|
**License:** `MIT`
|
||||||
|
[Repository](https://github.com/markusfisch/PieLauncher)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `Selection wheel`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `apps`
|
||||||
|
:x: Search history
|
||||||
|
:x: Customizable gestures
|
||||||
|
:x: Folders
|
||||||
|
:x: Tags
|
||||||
|
:x: Rename apps
|
||||||
|
:x: Widgets
|
||||||
|
:grey_question: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:x: Icon packs
|
||||||
|
:x: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### µLauncher
|
||||||
|
|
||||||
|
**License:** `MIT`
|
||||||
|
[Repository](https://github.com/jrpie/launcher) | [F-Droid](https://f-droid.org/en/packages/de.jrpie.android.launcher/)
|
||||||
|
|
||||||
|
**Main mode of interaction:** `Gestures` `Search`
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
|
||||||
|
:white_check_mark: Search: `apps`
|
||||||
|
:x: Search history
|
||||||
|
:white_check_mark: Customizable gestures: `35 avilable` [read the docs](https://github.com/jrpie/launcher/blob/master/docs/actions-and-gestures.md)
|
||||||
|
:x: Folders
|
||||||
|
:x: Tags
|
||||||
|
:white_check_mark: Rename apps
|
||||||
|
:white_check_mark: Widgets
|
||||||
|
:white_check_mark: Private space
|
||||||
|
:white_check_mark: Work profile
|
||||||
|
:white_check_mark: Pinned shortcuts
|
||||||
|
:x:Icon packs
|
||||||
|
:white_check_mark: Material You
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Tabular Summary
|
||||||
|
|
||||||
|
#### Legend:
|
||||||
|
|
||||||
|
:white_check_mark: = Supported
|
||||||
|
:x: = Unsupported
|
||||||
|
:warning: = Buggy/Broken; check this launcher's notes above
|
||||||
|
|
||||||
|
| Launcher | Search | Search history | Customizable gestures | Folders | Tags | Rename apps | Widgets | Private space | Work profile | Pinned shortcuts | Icon packs | Material You |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| [µLauncher](#µLauncher) | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
|
||||||
|
| [Fossify](#Fossify) | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :grey_question: | :x: | :white_check_mark: | :x: | :white_check_mark: |
|
||||||
|
| [Lawnchair](#Lawnchair) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :grey_question: | :grey_question: | :x: | :white_check_mark: | :white_check_mark: |
|
||||||
|
| [Rootless Pixel Launcher](#Rootless-Pixel-Launcher) | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :warning: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
|
||||||
|
| [KISS Launcher](#KISS-Launcher) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :grey_question: |
|
||||||
|
| [Lunar Launcher](#Lunar-Launcher) | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :grey_question: | :x: | :white_check_mark: | :x: | :x: |
|
||||||
|
| [OLauncher](#OLauncher) | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :grey_question: | :white_check_mark: | :grey_question: | :x: | :grey_question: |
|
||||||
|
| [TinyBit Launcher](#TinyBit-Launcher) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
|
||||||
|
| [YAM Launcher](#YAM-Launcher) | :white_check_mark: | :x: | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :x: | :white_check_mark: |
|
||||||
|
| [Ion Launcher](#Ion-Launcher) | :warning: | :white_check_mark: | :x: | :warning: | :x: | :white_check_mark: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
|
||||||
|
| [Pie Launcher](#Pie-Launcher) | :white_check_mark: | :x: | :x: | :x: | :x: | :x: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :x: | :x: |
|
||||||
|
| [folder launcher](#folder-launcher) | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :grey_question: | :x: | :white_check_mark: | :x: | :x: |
|
||||||
|
| [Discreet Launcher](#Discreet-Launcher) | :white_check_mark: | :x: | :x: | :white_check_mark: | :x: | :white_check_mark: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
|
||||||
|
| [Aster Launcher](#Aster-Launcher) | :warning: | :x: | :x: | :x: | :x: | :x: | :x: | :grey_question: | :white_check_mark: | :white_check_mark: | :x: | :x: |
|
||||||
|
|
||||||
|
<!-- Automating checkboxes for the table
|
||||||
|
|
||||||
|
You can use this python script, copy the contents of the Features section for the launcher, and paste it in the fixit() function. Note, since you're pasting in multiple lines, you will need to start and end your paste with triple-quotes.
|
||||||
|
|
||||||
|
import re
|
||||||
|
def fix(features):
|
||||||
|
# Use: fixed("""<paste here>""")
|
||||||
|
fixed = ' | '.join(re.findall('(:[\w\_]*:)', features))
|
||||||
|
return f"| {fixed} |"
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Not Tested
|
||||||
|
Feel free to test these and add
|
||||||
|
https://f-droid.org/en/packages/app.easy.launcher/
|
||||||
|
https://f-droid.org/en/packages/de.nodomain.tobihille.seniorlauncher/
|
||||||
|
https://f-droid.org/en/packages/com.mrmannwood.hexlauncher/
|
||||||
|
https://f-droid.org/en/packages/com.simplemobiletools.applauncher/
|
||||||
|
https://f-droid.org/en/packages/peterfajdiga.fastdraw/
|
||||||
|
https://f-droid.org/en/packages/de.mm20.launcher2.release/
|
||||||
|
|
||||||
|
[Even more launchers](https://docs.arcticons.com/faq/supported-launchers) (most of them don't seem to be FOSS)
|
54
docs/app-drawer.md
Normal file
54
docs/app-drawer.md
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
+++
|
||||||
|
weight = 10
|
||||||
|
+++
|
||||||
|
|
||||||
|
# App Drawer
|
||||||
|
|
||||||
|
Apps that are not needed all the time are shown in the app drawer.
|
||||||
|
There are several such drawers, but the basic concept is the same.
|
||||||
|
Besides regular apps, app drawers also show [pinned shortcuts](https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts)[^1].
|
||||||
|
μLauncher treats apps and shortcuts in the same way.
|
||||||
|
|
||||||
|
The idea of the app drawer is to search for apps using the keyboard.
|
||||||
|
By default[^2] an app is launched automatically once it is the only app matching the query.
|
||||||
|
This can be prevented by typing space.
|
||||||
|
Usually only two or three characters are needed which is much faster than scrolling to find an app.
|
||||||
|
|
||||||
|
[^1]: A pinned shortcut is created for example when pinning a website to the home screen.
|
||||||
|
[^2]: There are [several settings](/docs/settings/#functionality) available to modify the behavior.
|
||||||
|
|
||||||
|
When long clicking an app, additional options are shown:
|
||||||
|
* Rename the app
|
||||||
|
* Add to / remove from Favorites: Adds the app to the [Favorite Apps](#favorite-apps) list or removes it from there.
|
||||||
|
* Hide / Show: This hides the app from all drawers (except from [Hidden Apps](#hidden-apps)) or makes it visible again if it was hidden.
|
||||||
|
* App Info: Opens the system settings page for the app.
|
||||||
|
* Uninstall: Tries to uninstall the app or remove the shortcut.
|
||||||
|
|
||||||
|
## All Apps
|
||||||
|
|
||||||
|
This lists all apps except hidden apps.
|
||||||
|
By default it is bound to `Swipe up`.
|
||||||
|
|
||||||
|
## Favorite Apps
|
||||||
|
|
||||||
|
Only shows favorite apps.
|
||||||
|
Pressing the star icon on the bottom right of any app drawer toggles whether
|
||||||
|
only favorite apps should be shown.
|
||||||
|
Additionally the `Favorite Apps` action can be used to launch this drawer directly.
|
||||||
|
By default it is bound to `Swipe up (left edge)`.
|
||||||
|
|
||||||
|
## Private Space
|
||||||
|
|
||||||
|
When [private space](/docs/profiles/#private-space) is available, this drawer
|
||||||
|
shows only apps from the private space.
|
||||||
|
It can be opened using the `Private Space` action.
|
||||||
|
If the private space is locked, instead of showing the list the unlock dialog is shown.
|
||||||
|
|
||||||
|
By default, apps from the private space are shown in All Apps as well,
|
||||||
|
however this is [configurable](/docs/settings/#hide-private-space-from-app-list).
|
||||||
|
|
||||||
|
## Hidden Apps
|
||||||
|
|
||||||
|
This list shows hidden apps.
|
||||||
|
It is only accessible through the settings.
|
||||||
|
The feature is intended to be used only for apps which are not needed at all but [can not be uninstalled](https://en.wikipedia.org/wiki/Software_bloat#Bloatware).
|
|
@ -1,3 +1,8 @@
|
||||||
|
+++
|
||||||
|
weight = 50
|
||||||
|
+++
|
||||||
|
|
||||||
|
|
||||||
# Building from Source
|
# Building from Source
|
||||||
|
|
||||||
## Using the command line
|
## Using the command line
|
||||||
|
@ -37,9 +42,13 @@ for further instructions.
|
||||||
Install [Android Studio](https://developer.android.com/studio), import this project and build it.
|
Install [Android Studio](https://developer.android.com/studio), import this project and build it.
|
||||||
|
|
||||||
See [this guide](https://developer.android.com/studio/run)
|
See [this guide](https://developer.android.com/studio/run)
|
||||||
for further instructions. How to
|
for further instructions.
|
||||||
|
|
||||||
## CI Pipeline
|
## CI Pipeline
|
||||||
|
|
||||||
The [CI pipeline](https://github.com/jrpie/Launcher/actions) automatically creates debug builds.
|
The [CI pipeline](https://github.com/jrpie/Launcher/actions) automatically creates debug builds.
|
||||||
> Note: These builds are *not* signed. They are in built in debug mode and only suitable for testing.
|
|
||||||
|
{{% hint warning %}}
|
||||||
|
Note: These builds are not signed.
|
||||||
|
They are in built in debug mode and only suitable for testing.
|
||||||
|
{{% /hint %}}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
+++
|
||||||
|
title = 'Differences to the original Launcher'
|
||||||
|
+++
|
||||||
|
|
||||||
# Notable changes compared to Finn's Launcher
|
# Notable changes compared to Finn's Launcher
|
||||||
|
|
||||||
µLauncher is a fork of [finnmglas's app Launcher](https://github.com/finnmglas/Launcher).
|
µLauncher is a fork of [finnmglas's app Launcher](https://github.com/finnmglas/Launcher).
|
||||||
|
@ -51,6 +55,5 @@ The complete list of changes can be viewed [here](https://github.com/jrpie/launc
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
\[original-repo\]: [https://github.com/finnmglas/Launcher](https://github.com/finnmglas/Launcher)
|
[original-repo]: https://github.com/finnmglas/Launcher
|
||||||
|
[hack-font]: https://sourcefoundry.org/hack/
|
||||||
\[hack-font\]: [https://sourcefoundry.org/hack/](https://sourcefoundry.org/hack/)
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
+++
|
||||||
|
weight = 40
|
||||||
|
+++
|
||||||
|
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
There are several ways to contribute to this app:
|
There are several ways to contribute to this app:
|
||||||
|
@ -13,7 +18,7 @@ There are several ways to contribute to this app:
|
||||||
- Open a new pull request.
|
- Open a new pull request.
|
||||||
|
|
||||||
|
|
||||||
See [build.md](build.md) for instructions how to build this project.
|
See [here](/docs/build) for instructions how to build this project.
|
||||||
The [CI pipeline](https://github.com/jrpie/Launcher/actions) automatically creates debug builds.
|
The [CI pipeline](https://github.com/jrpie/Launcher/actions) automatically creates debug builds.
|
||||||
|
|
||||||
|
|
||||||
|
|
7
docs/examples/_index.md
Normal file
7
docs/examples/_index.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
+++
|
||||||
|
bookCollapseSection = true
|
||||||
|
weight = 20
|
||||||
|
+++
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
This section contains some examples how μLauncher can be tweaked.
|
18
docs/examples/apps-on-home-screen.md
Normal file
18
docs/examples/apps-on-home-screen.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
+++
|
||||||
|
title = 'Showing Apps on the Home Screen'
|
||||||
|
+++
|
||||||
|
|
||||||
|
# Showing Apps on the Home Screen
|
||||||
|
|
||||||
|
Even though this is somewhat contrary to the general idea of μLauncher,
|
||||||
|
it is possible to show apps on the home screen using widgets.
|
||||||
|
|
||||||
|
Users suggested:
|
||||||
|
* [Launchy](https://launchywidget.com/) (proprietary!)
|
||||||
|
* KWGT Kustom Widget Maker (proprietary!)
|
||||||
|
|
||||||
|
{{% hint danger %}}
|
||||||
|
Both of these apps are not open source and KWGT even has ads.
|
||||||
|
|
||||||
|
Please contact me if you know FOSS alternatives!
|
||||||
|
{{% /hint %}}
|
23
docs/examples/termux/index.md
Normal file
23
docs/examples/termux/index.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
+++
|
||||||
|
title = 'Integration with Termux'
|
||||||
|
+++
|
||||||
|
|
||||||
|
# Termux
|
||||||
|
|
||||||
|
μLauncher has no special support for [Termux](https://termux.dev/).
|
||||||
|
However it is possible to run Termux commands from μ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 μ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 μLauncher, i.e. open μ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.
|
BIN
docs/examples/termux/screenshot1.png
Normal file
BIN
docs/examples/termux/screenshot1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
BIN
docs/examples/termux/screenshot2.png
Normal file
BIN
docs/examples/termux/screenshot2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
43
docs/home.md
43
docs/home.md
|
@ -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?
|
|
||||||
|
|
||||||
[](https://f-droid.org/packages/de.jrpie.android.launcher/)
|
|
||||||
|
|
||||||
[](https://accrescent.app/app/de.jrpie.android.launcher.accrescent)
|
|
||||||
|
|
||||||
[](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})
|
|
||||||
|
|
||||||
[](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
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<!-- missing μLauncher grid view screenshot-->
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
+++
|
||||||
|
title = 'User Profiles'
|
||||||
|
weight = 12
|
||||||
|
+++
|
||||||
|
|
||||||
|
|
||||||
# Work Profile
|
# 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.
|
µ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.
|
||||||
|
|
108
docs/settings.md
108
docs/settings.md
|
@ -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 μ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
|
## 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.
|
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.
|
µ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.
|
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:** `Hack`,`System default`,`Sans serif`,`Serif`,`Monospace`,`Serif monospace`
|
**options:** `Hack`,`System default`,`Sans serif`,`Serif`,`Monospace`,`Serif monospace`
|
||||||
|
|
||||||
> ### Text Shadow
|
### Text Shadow
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Background (app list and setting)
|
### Background (app list and setting)
|
||||||
|
|
||||||
|
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 on widget panels the wallpaper is always shown unmodified.
|
||||||
|
|
||||||
**type:** `dropdown`
|
**type:** `dropdown`
|
||||||
|
|
||||||
**type:** `Transparent`,`Dim`,`Blur`,`Solid`
|
**type:** `Transparent`,`Dim`,`Blur`,`Solid`
|
||||||
|
|
||||||
> ### Monochrome app icons
|
### Monochrome app icons
|
||||||
|
|
||||||
Remove coloring from all app icons. Can help decrease visual stimulus when enabled.
|
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
|
## Date & Time
|
||||||
|
|
||||||
> ### Font (home screen)
|
These settings effect 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:** `dropdown`
|
**type:** `dropdown`
|
||||||
|
|
||||||
**options:** `Hack`,`System default`,`Sans serif`,`Serif`,`Monospace`,`Serif monospace`
|
**options:** `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.
|
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 an 6 digit RGB or 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:** `HEX`,`RGBA`
|
[^2]: More precisely, everything that is vaild 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:** `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:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Show time
|
### Show time
|
||||||
|
|
||||||
Show the current time on the home screen.
|
Show the current time on the home screen.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Show seconds
|
### Show seconds
|
||||||
|
|
||||||
Show the current time down to the second on the home screen.
|
Show the current time down to the second on the home screen.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Show date
|
### Show date
|
||||||
|
|
||||||
Show the current date on the home screen.
|
Show the current date on the home screen.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Flip date and time
|
### Flip date and time
|
||||||
|
|
||||||
Place the current time above the current date on the home screen.
|
Place the current time above the current date on the home screen.
|
||||||
|
|
||||||
|
@ -89,7 +108,7 @@ Place the current time above the current date on the home screen.
|
||||||
|
|
||||||
## Functionality
|
## 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 user keyboard input when no other apps match.
|
||||||
|
|
||||||
|
@ -105,37 +124,37 @@ Press space to temporarily disable this feature and allow text entry without pre
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `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:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Start keyboard for search
|
### Start keyboard for search
|
||||||
|
|
||||||
Automatically open the keyboard when the app drawer is opened.
|
Automatically open the keyboard when the app drawer is opened.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `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.
|
Enable double swipe (two finger) gestures in launcher settings. Does not erase gesture bindings if accidentally turned off.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `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.
|
Enable edge swipe (near edges of screen) gestures in launcher settings. Does not erase gesture bindings if accidentally turned off.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `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.
|
Change how large a margin is used for detecting edge gestures. Shows the edge margin preview when using the slider.
|
||||||
|
|
||||||
**type:** `slider`
|
**type:** `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.
|
||||||
|
|
||||||
|
@ -157,11 +176,11 @@ There are two methods to lock the screen and unfortunately both have downsides.
|
||||||
|
|
||||||
## Apps
|
## Apps
|
||||||
|
|
||||||
> ### Hidden apps
|
### Hidden apps
|
||||||
|
|
||||||
Open an app drawer containing only 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.
|
Remove certain apps from the app drawer if they are already accessible via a gesture.
|
||||||
|
|
||||||
|
@ -169,20 +188,21 @@ Reduces redundancy and tidies up app drawer.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Hide paused apps
|
### Hide paused apps
|
||||||
|
|
||||||
Remove paused apps from the app drawer.
|
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:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Hide private space from app list
|
### Hide private space from app list
|
||||||
|
|
||||||
Remove private space from app drawer.
|
Remove private space from app drawer.
|
||||||
|
Private space apps can be accessed using a separate app drawer which can be opened with the Private Space action.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Layout of app list
|
### Layout of app list
|
||||||
|
|
||||||
Changes how the apps are displayed when accessing the app drawer.
|
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:** `Default`,`Text`,`Grid`
|
**options:** `Default`,`Text`,`Grid`
|
||||||
|
|
||||||
> ### Reverse the app list
|
### Reverse the app list
|
||||||
|
|
||||||
Enable reverse alphabetical sorting of apps in the app drawer.
|
Enable reverse alphabetical sorting of apps in the app drawer.
|
||||||
Useful for keeping apps within easier reach from the keyboard.
|
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
|
## Display
|
||||||
|
|
||||||
> ### Rotate screen
|
### Rotate screen
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Keep screen on
|
### Keep screen on
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
> ### Hide status bar
|
### Hide status bar
|
||||||
|
|
||||||
Remove the top status bar from the home screen.
|
Remove the top status bar from the home screen.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `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 setup properly.
|
||||||
|
|
||||||
**type:** `toggle`
|
**type:** `toggle`
|
||||||
|
|
||||||
## Additional Settings
|
|
||||||
|
|
||||||
> ### App Drawer Long Press on App
|
|
||||||
|
|
||||||
Access additional per-app details and settings. To use, open the app drawer and long press on any app.
|
|
||||||
|
|
||||||
**type:** `dropdown`
|
|
||||||
|
|
||||||
**options:** `App Info`,`Add to favorites`,`Hide`,`Rename`,`Uninstall`
|
|
||||||
|
|
28
docs/widgets.md
Normal file
28
docs/widgets.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
+++
|
||||||
|
title = 'Widgets'
|
||||||
|
weight = 11
|
||||||
|
+++
|
||||||
|
|
||||||
|
# Widgets
|
||||||
|
|
||||||
|
μ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, μ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.
|
||||||
|
|
||||||
|
μLauncher's clock behaves similar as 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.
|
10
fastlane/metadata/android/en-US/changelogs/47.txt
Normal file
10
fastlane/metadata/android/en-US/changelogs/47.txt
Normal 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!)
|
Loading…
Add table
Add a link
Reference in a new issue