mirror of
https://github.com/jrpie/Launcher.git
synced 2025-06-08 10:20:15 +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" />
|
||||
<uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" />
|
||||
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<application
|
||||
android:name=".Application"
|
||||
|
@ -19,6 +20,7 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/launcherBaseTheme"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<activity
|
||||
android:name=".ui.widgets.manage.ManageWidgetPanelsActivity"
|
||||
android:exported="false" />
|
||||
|
@ -80,6 +82,9 @@
|
|||
<activity
|
||||
android:name=".ui.LegalInfoActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.ReportCrashActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".actions.lock.LauncherDeviceAdmin"
|
||||
|
@ -110,5 +115,4 @@
|
|||
android:resource="@xml/accessibility_service_config" />
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -25,6 +25,7 @@ import de.jrpie.android.launcher.preferences.resetPreferences
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
||||
const val APP_WIDGET_HOST_ID = 42;
|
||||
|
@ -106,6 +107,11 @@ class Application : android.app.Application() {
|
|||
// TODO Error: Invalid resource ID 0x00000000.
|
||||
// DynamicColors.applyToActivitiesIfAvailable(this)
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
|
||||
sendCrashNotification(this@Application, throwable)
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
|
||||
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||
torchManager = TorchManager(this)
|
||||
|
@ -114,8 +120,6 @@ class Application : android.app.Application() {
|
|||
appWidgetHost = AppWidgetHost(this.applicationContext, APP_WIDGET_HOST_ID)
|
||||
appWidgetManager = AppWidgetManager.getInstance(this.applicationContext)
|
||||
|
||||
appWidgetHost.startListening()
|
||||
|
||||
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
LauncherPreferences.init(preferences, this.resources)
|
||||
|
@ -157,6 +161,8 @@ class Application : android.app.Application() {
|
|||
removeUnusedShortcuts(this)
|
||||
}
|
||||
loadApps()
|
||||
|
||||
createNotificationChannels(this)
|
||||
}
|
||||
|
||||
fun getCustomAppNames(): HashMap<AbstractAppInfo, String> {
|
||||
|
@ -170,10 +176,4 @@ class Application : android.app.Application() {
|
|||
apps.postValue(getApps(packageManager, applicationContext))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
appWidgetHost.stopListening()
|
||||
super.onTerminate()
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@ import android.app.role.RoleManager
|
|||
import android.content.ActivityNotFoundException
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.LauncherApps
|
||||
|
@ -227,3 +224,13 @@ fun copyToClipboard(context: Context, text: String) {
|
|||
val clipData = ClipData.newPlainText("Debug Info", text)
|
||||
clipboardManager.setPrimaryClip(clipData)
|
||||
}
|
||||
|
||||
fun writeEmail(context: Context, to: String, subject: String, text: String) {
|
||||
val intent = Intent(Intent.ACTION_SENDTO)
|
||||
intent.setData("mailto:".toUri())
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(to))
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, subject)
|
||||
intent.putExtra(Intent.EXTRA_TEXT, text)
|
||||
context.startActivity(Intent.createChooser(intent, context.getString(R.string.send_email)))
|
||||
}
|
||||
|
||||
|
|
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
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
|
@ -25,11 +26,18 @@ class WidgetPanelAction(val widgetPanelId: Int) : Action {
|
|||
|
||||
override fun invoke(context: Context, rect: Rect?): Boolean {
|
||||
|
||||
if (WidgetPanel.byId(widgetPanelId) == null) {
|
||||
if (context is WidgetPanelActivity) {
|
||||
if (context.widgetPanelId == widgetPanelId) {
|
||||
context.finish()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (WidgetPanel.byId(this.widgetPanelId) == null) {
|
||||
Toast.makeText(context, R.string.alert_widget_panel_not_found, Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
context.startActivity(Intent(context, WidgetPanelActivity::class.java).also {
|
||||
it.putExtra(EXTRA_PANEL_ID, widgetPanelId)
|
||||
it.putExtra(EXTRA_PANEL_ID, this.widgetPanelId)
|
||||
})
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -2,7 +2,6 @@ package de.jrpie.android.launcher.preferences
|
|||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import de.jrpie.android.launcher.Application
|
||||
import de.jrpie.android.launcher.BuildConfig
|
||||
import de.jrpie.android.launcher.actions.Action
|
||||
import de.jrpie.android.launcher.apps.AbstractAppInfo
|
||||
|
@ -10,22 +9,25 @@ import de.jrpie.android.launcher.apps.AbstractAppInfo.Companion.INVALID_USER
|
|||
import de.jrpie.android.launcher.apps.AppInfo
|
||||
import de.jrpie.android.launcher.apps.DetailedAppInfo
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion1
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion100
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion2
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion3
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersion4
|
||||
import de.jrpie.android.launcher.preferences.legacy.migratePreferencesFromVersionUnknown
|
||||
import de.jrpie.android.launcher.sendCrashNotification
|
||||
import de.jrpie.android.launcher.ui.HomeActivity
|
||||
import de.jrpie.android.launcher.widgets.ClockWidget
|
||||
import de.jrpie.android.launcher.widgets.DebugInfoWidget
|
||||
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||
import de.jrpie.android.launcher.widgets.WidgetPosition
|
||||
import de.jrpie.android.launcher.widgets.deleteAllWidgets
|
||||
import de.jrpie.android.launcher.widgets.generateInternalId
|
||||
import de.jrpie.android.launcher.widgets.getAppWidgetHost
|
||||
|
||||
/* Current version of the structure of preferences.
|
||||
* Increase when breaking changes are introduced and write an appropriate case in
|
||||
* `migratePreferencesToNewVersion`
|
||||
*/
|
||||
const val PREFERENCE_VERSION = 100
|
||||
const val PREFERENCE_VERSION = 101
|
||||
const val UNKNOWN_PREFERENCE_VERSION = -1
|
||||
private const val TAG = "Launcher - Preferences"
|
||||
|
||||
|
@ -64,6 +66,10 @@ fun migratePreferencesToNewVersion(context: Context) {
|
|||
migratePreferencesFromVersion4(context)
|
||||
Log.i(TAG, "migration of preferences complete (4 -> ${PREFERENCE_VERSION}).")
|
||||
}
|
||||
100 -> {
|
||||
migratePreferencesFromVersion100(context)
|
||||
Log.i(TAG, "migration of preferences complete (100 -> ${PREFERENCE_VERSION}).")
|
||||
}
|
||||
|
||||
else -> {
|
||||
Log.w(
|
||||
|
@ -76,6 +82,7 @@ fun migratePreferencesToNewVersion(context: Context) {
|
|||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to restore preferences:\n${e.stackTrace}")
|
||||
sendCrashNotification(context, e)
|
||||
resetPreferences(context)
|
||||
}
|
||||
}
|
||||
|
@ -84,12 +91,12 @@ fun resetPreferences(context: Context) {
|
|||
Log.i(TAG, "Resetting preferences")
|
||||
LauncherPreferences.clear()
|
||||
LauncherPreferences.internal().versionCode(PREFERENCE_VERSION)
|
||||
deleteAllWidgets(context)
|
||||
context.getAppWidgetHost().deleteHost()
|
||||
|
||||
LauncherPreferences.widgets().widgets(
|
||||
setOf(
|
||||
ClockWidget(
|
||||
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
|
||||
generateInternalId(),
|
||||
WidgetPosition(1, 3, 10, 4),
|
||||
WidgetPanel.HOME.id
|
||||
)
|
||||
|
@ -101,7 +108,7 @@ fun resetPreferences(context: Context) {
|
|||
LauncherPreferences.widgets().widgets().also {
|
||||
it.add(
|
||||
DebugInfoWidget(
|
||||
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
|
||||
generateInternalId(),
|
||||
WidgetPosition(1, 1, 10, 4),
|
||||
WidgetPanel.HOME.id
|
||||
)
|
||||
|
|
|
@ -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.WidgetPanel
|
||||
import de.jrpie.android.launcher.widgets.WidgetPosition
|
||||
import de.jrpie.android.launcher.widgets.generateInternalId
|
||||
|
||||
fun migratePreferencesFromVersion4(context: Context) {
|
||||
assert(PREFERENCE_VERSION == 100)
|
||||
assert(LauncherPreferences.internal().versionCode() < 100)
|
||||
|
||||
LauncherPreferences.widgets().widgets(
|
||||
setOf(
|
||||
ClockWidget(
|
||||
(context.applicationContext as Application).appWidgetHost.allocateAppWidgetId(),
|
||||
generateInternalId(),
|
||||
WidgetPosition(1, 3, 10, 4),
|
||||
WidgetPanel.HOME.id
|
||||
)
|
||||
)
|
||||
)
|
||||
LauncherPreferences.internal().versionCode(100)
|
||||
migratePreferencesFromVersion100(context)
|
||||
}
|
|
@ -1,16 +1,9 @@
|
|||
package de.jrpie.android.launcher.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.window.OnBackInvokedDispatcher
|
||||
import de.jrpie.android.launcher.Application
|
||||
import de.jrpie.android.launcher.actions.Action
|
||||
import de.jrpie.android.launcher.actions.Gesture
|
||||
|
@ -19,6 +12,7 @@ import de.jrpie.android.launcher.databinding.ActivityHomeBinding
|
|||
import de.jrpie.android.launcher.openTutorial
|
||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||
import de.jrpie.android.launcher.ui.tutorial.TutorialActivity
|
||||
import de.jrpie.android.launcher.ui.util.LauncherGestureActivity
|
||||
|
||||
/**
|
||||
* [HomeActivity] is the actual application Launcher,
|
||||
|
@ -32,10 +26,9 @@ import de.jrpie.android.launcher.ui.tutorial.TutorialActivity
|
|||
* - Setting global variables (preferences etc.)
|
||||
* - Opening the [TutorialActivity] on new installations
|
||||
*/
|
||||
class HomeActivity : UIObject, Activity() {
|
||||
class HomeActivity : UIObject, LauncherGestureActivity() {
|
||||
|
||||
private lateinit var binding: ActivityHomeBinding
|
||||
private var touchGestureDetector: TouchGestureDetector? = null
|
||||
|
||||
private var sharedPreferencesListener =
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
|
||||
|
@ -54,35 +47,21 @@ class HomeActivity : UIObject, Activity() {
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super<Activity>.onCreate(savedInstanceState)
|
||||
super<LauncherGestureActivity>.onCreate(savedInstanceState)
|
||||
super<UIObject>.onCreate()
|
||||
|
||||
|
||||
// Initialise layout
|
||||
binding = ActivityHomeBinding.inflate(layoutInflater)
|
||||
|
||||
setContentView(binding.root)
|
||||
|
||||
// Handle back key / gesture on Android 13+, cf. onKeyDown()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
onBackInvokedDispatcher.registerOnBackInvokedCallback(
|
||||
OnBackInvokedDispatcher.PRIORITY_OVERLAY
|
||||
) {
|
||||
handleBack()
|
||||
}
|
||||
}
|
||||
binding.buttonFallbackSettings.setOnClickListener {
|
||||
LauncherAction.SETTINGS.invoke(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
touchGestureDetector?.updateScreenSize(windowManager)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super<Activity>.onStart()
|
||||
super<LauncherGestureActivity>.onStart()
|
||||
super<UIObject>.onStart()
|
||||
|
||||
// If the tutorial was not finished, start it
|
||||
|
@ -93,15 +72,6 @@ class HomeActivity : UIObject, Activity() {
|
|||
LauncherPreferences.getSharedPreferences()
|
||||
.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
|
||||
|
||||
(application as Application).appWidgetHost.startListening()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun onStop() {
|
||||
(application as Application).appWidgetHost.stopListening()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
|
@ -112,7 +82,6 @@ class HomeActivity : UIObject, Activity() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun updateSettingsFallbackButtonVisibility() {
|
||||
// If µLauncher settings can not be reached from any action bound to an enabled gesture,
|
||||
// show the fallback button.
|
||||
|
@ -131,81 +100,42 @@ class HomeActivity : UIObject, Activity() {
|
|||
return modifyTheme(super.getTheme())
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
try {
|
||||
(application as Application).appWidgetHost.stopListening()
|
||||
} catch (e: Exception) {
|
||||
// Throws a NullPointerException on Android 12 an earlier, see #172
|
||||
e.printStackTrace()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
/* This should be initialized in onCreate()
|
||||
However on some devices there seems to be a bug where the touchGestureDetector
|
||||
is not working properly after resuming the app.
|
||||
Reinitializing the touchGestureDetector every time the app is resumed might help to fix that.
|
||||
(see issue #138)
|
||||
*/
|
||||
touchGestureDetector = TouchGestureDetector(
|
||||
this, 0, 0,
|
||||
LauncherPreferences.enabled_gestures().edgeSwipeEdgeWidth() / 100f
|
||||
).also {
|
||||
it.updateScreenSize(windowManager)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
binding.root.setOnApplyWindowInsetsListener { _, windowInsets ->
|
||||
@Suppress("deprecation") // required to support API 29
|
||||
val insets = windowInsets.systemGestureInsets
|
||||
touchGestureDetector?.setSystemGestureInsets(insets)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
updateSettingsFallbackButtonVisibility()
|
||||
|
||||
binding.homeWidgetContainer.updateWidgets(this@HomeActivity,
|
||||
LauncherPreferences.widgets().widgets()
|
||||
)
|
||||
|
||||
(application as Application).appWidgetHost.startListening()
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroy() {
|
||||
LauncherPreferences.getSharedPreferences()
|
||||
.unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@SuppressLint("GestureBackNavigation")
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_BACK -> {
|
||||
// Only used pre Android 13, cf. onBackInvokedDispatcher
|
||||
handleBack()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||
if (Action.forGesture(Gesture.VOLUME_UP) == LauncherAction.VOLUME_UP) {
|
||||
// Let the OS handle the key event. This works better with some custom ROMs
|
||||
// and apps like Samsung Sound Assistant.
|
||||
return false
|
||||
}
|
||||
Gesture.VOLUME_UP(this)
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||
if (Action.forGesture(Gesture.VOLUME_DOWN) == LauncherAction.VOLUME_DOWN) {
|
||||
// see above
|
||||
return false
|
||||
}
|
||||
Gesture.VOLUME_DOWN(this)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
touchGestureDetector?.onTouchEvent(event)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun handleBack() {
|
||||
override fun handleBack() {
|
||||
Gesture.BACK(this)
|
||||
}
|
||||
|
||||
override fun getRootView(): View {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun isHomeScreen(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
import android.content.Context
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
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 android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import de.jrpie.android.launcher.BuildConfig
|
||||
import de.jrpie.android.launcher.R
|
||||
import de.jrpie.android.launcher.copyToClipboard
|
||||
import de.jrpie.android.launcher.databinding.SettingsMetaBinding
|
||||
import de.jrpie.android.launcher.getDeviceInfo
|
||||
import de.jrpie.android.launcher.openInBrowser
|
||||
import de.jrpie.android.launcher.openTutorial
|
||||
|
@ -51,21 +30,13 @@ import de.jrpie.android.launcher.ui.UIObject
|
|||
*/
|
||||
class SettingsFragmentMeta : Fragment(), UIObject {
|
||||
|
||||
private lateinit var binding: SettingsMetaBinding
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
SettingsMetaScreen(
|
||||
context = requireContext(),
|
||||
onResetConfirmed = { requireActivity().finish() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
binding = SettingsMetaBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -74,215 +45,102 @@ class SettingsFragmentMeta : Fragment(), UIObject {
|
|||
}
|
||||
|
||||
override fun setOnClicks() {
|
||||
// No longer needed as click handlers are defined in Compose
|
||||
}
|
||||
}
|
||||
|
||||
// Data class to represent a settings action
|
||||
private data class SettingsAction(
|
||||
val textResId: Int,
|
||||
val onClick: (Context) -> Unit
|
||||
)
|
||||
|
||||
// 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()
|
||||
fun bindURL(view: View, urlRes: Int) {
|
||||
view.setOnClickListener {
|
||||
openInBrowser(
|
||||
getString(urlRes),
|
||||
requireContext()
|
||||
)
|
||||
}
|
||||
}
|
||||
// Version number at the bottom of buttons
|
||||
Text(
|
||||
text = BuildConfig.VERSION_NAME,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.End,
|
||||
color = colorResource(R.color.finnmglasTheme_text_color),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, end = 8.dp)
|
||||
.clickable {
|
||||
val deviceInfo = getDeviceInfo()
|
||||
copyToClipboard(context, deviceInfo)
|
||||
|
||||
binding.settingsMetaButtonViewTutorial.setOnClickListener {
|
||||
openTutorial(requireContext())
|
||||
}
|
||||
|
||||
// prompting for settings-reset confirmation
|
||||
binding.settingsMetaButtonResetSettings.setOnClickListener {
|
||||
AlertDialog.Builder(this.requireContext(), R.style.AlertDialogCustom)
|
||||
.setTitle(getString(R.string.settings_meta_reset))
|
||||
.setMessage(getString(R.string.settings_meta_reset_confirm))
|
||||
.setPositiveButton(
|
||||
android.R.string.ok
|
||||
) { _, _ ->
|
||||
resetPreferences(this.requireContext())
|
||||
requireActivity().finish()
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.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.databinding.Tutorial5FinishBinding
|
||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||
import de.jrpie.android.launcher.requestNotificationPermission
|
||||
import de.jrpie.android.launcher.setDefaultHomeScreen
|
||||
import de.jrpie.android.launcher.ui.UIObject
|
||||
|
||||
|
@ -31,8 +32,10 @@ class TutorialFragment5Finish : Fragment(), UIObject {
|
|||
override fun onStart() {
|
||||
super<Fragment>.onStart()
|
||||
super<UIObject>.onStart()
|
||||
requestNotificationPermission(requireActivity())
|
||||
}
|
||||
|
||||
|
||||
override fun setOnClicks() {
|
||||
super.setOnClicks()
|
||||
binding.tutorialFinishButtonStart.setOnClickListener { finishTutorial() }
|
||||
|
@ -44,6 +47,7 @@ class TutorialFragment5Finish : Fragment(), UIObject {
|
|||
LauncherPreferences.internal().startedTime(System.currentTimeMillis() / 1000L)
|
||||
}
|
||||
context?.let { setDefaultHomeScreen(it, checkDefault = true) }
|
||||
|
||||
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.databinding.WidgetClockBinding
|
||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||
import java.util.Locale
|
||||
|
||||
class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId: Int): ConstraintLayout(context, attrs) {
|
||||
class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId: Int, val panelId: Int): ConstraintLayout(context, attrs) {
|
||||
constructor(context: Context, attrs: AttributeSet?): this(context, attrs, WidgetPanel.HOME.id, -1)
|
||||
|
||||
val binding: WidgetClockBinding = WidgetClockBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
init {
|
||||
|
@ -57,7 +59,7 @@ class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId:
|
|||
binding.clockUpperView.format12Hour = upperFormat
|
||||
}
|
||||
|
||||
fun setOnClicks() {
|
||||
private fun setOnClicks() {
|
||||
binding.clockUpperView.setOnClickListener {
|
||||
if (LauncherPreferences.clock().flipDateTime()) {
|
||||
Gesture.TIME(context)
|
||||
|
@ -74,7 +76,4 @@ class ClockView(context: Context, attrs: AttributeSet? = null, val appWidgetId:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,26 +1,31 @@
|
|||
package de.jrpie.android.launcher.ui.widgets
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.view.ViewCompat
|
||||
import de.jrpie.android.launcher.Application
|
||||
import de.jrpie.android.launcher.R
|
||||
import de.jrpie.android.launcher.databinding.ActivityWidgetPanelBinding
|
||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||
import de.jrpie.android.launcher.ui.UIObject
|
||||
import de.jrpie.android.launcher.ui.util.LauncherGestureActivity
|
||||
import de.jrpie.android.launcher.ui.widgets.manage.EXTRA_PANEL_ID
|
||||
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||
|
||||
class WidgetPanelActivity : Activity(), UIObject {
|
||||
lateinit var binding: ActivityWidgetPanelBinding
|
||||
private var widgetPanelId: Int = WidgetPanel.HOME.id
|
||||
class WidgetPanelActivity : LauncherGestureActivity(), UIObject {
|
||||
var binding: ActivityWidgetPanelBinding? = null
|
||||
|
||||
var widgetPanelId: Int = WidgetPanel.HOME.id
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super<Activity>.onCreate(savedInstanceState)
|
||||
super<LauncherGestureActivity>.onCreate(savedInstanceState)
|
||||
super<UIObject>.onCreate()
|
||||
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
|
||||
val binding = ActivityWidgetPanelBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
|
||||
|
||||
// The widget container should extend below the status and navigation bars,
|
||||
// so let's set an empty WindowInsetsListener to prevent it from being moved.
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets ->
|
||||
|
@ -55,10 +60,33 @@ class WidgetPanelActivity : Activity(), UIObject {
|
|||
}
|
||||
|
||||
override fun onStart() {
|
||||
super<Activity>.onStart()
|
||||
super<LauncherGestureActivity>.onStart()
|
||||
super<UIObject>.onStart()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
try {
|
||||
(application as Application).appWidgetHost.stopListening()
|
||||
} catch (e: Exception) {
|
||||
// Throws a NullPointerException on Android 12 an earlier, see #172
|
||||
e.printStackTrace()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
(application as Application).appWidgetHost.startListening()
|
||||
}
|
||||
|
||||
override fun getRootView(): View? {
|
||||
return binding?.root
|
||||
}
|
||||
|
||||
override fun handleBack() {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun isHomeScreen(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.widget.EditText
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import de.jrpie.android.launcher.Application
|
||||
import de.jrpie.android.launcher.R
|
||||
import de.jrpie.android.launcher.databinding.ActivityManageWidgetPanelsBinding
|
||||
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() {
|
||||
super.onResume()
|
||||
(application as Application).appWidgetHost.startListening()
|
||||
|
||||
binding.manageWidgetsContainer.updateWidgets(
|
||||
this,
|
||||
LauncherPreferences.widgets().widgets()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
|
@ -124,10 +135,6 @@ class ManageWidgetsActivity : UIObject, Activity() {
|
|||
val appWidgetHost = (application as Application).appWidgetHost
|
||||
startActivityForResult(
|
||||
Intent(this, SelectWidgetActivity::class.java).also {
|
||||
it.putExtra(
|
||||
AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||
appWidgetHost.allocateAppWidgetId()
|
||||
)
|
||||
it.putExtra(
|
||||
EXTRA_PANEL_ID,
|
||||
panelId
|
||||
|
@ -140,13 +147,17 @@ class ManageWidgetsActivity : UIObject, Activity() {
|
|||
private fun createWidget(data: Intent) {
|
||||
Log.i("Launcher", "creating widget")
|
||||
val appWidgetManager = (application as Application).appWidgetManager
|
||||
val appWidgetHost = (application as Application).appWidgetHost
|
||||
val appWidgetId = data.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return
|
||||
|
||||
val provider = appWidgetManager.getAppWidgetInfo(appWidgetId)
|
||||
|
||||
val display = windowManager.defaultDisplay
|
||||
|
||||
val widgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId)
|
||||
if (widgetInfo == null) {
|
||||
Log.w("Launcher", "can't access widget")
|
||||
appWidgetHost.deleteAppWidgetId(appWidgetId)
|
||||
return
|
||||
}
|
||||
|
||||
val position = WidgetPosition.findFreeSpace(
|
||||
WidgetPanel.byId(panelId),
|
||||
|
@ -154,7 +165,7 @@ class ManageWidgetsActivity : UIObject, Activity() {
|
|||
max(3, (GRID_SIZE * (widgetInfo.minHeight) / display.height.toFloat()).roundToInt())
|
||||
)
|
||||
|
||||
val widget = AppWidget(appWidgetId, position, panelId, provider)
|
||||
val widget = AppWidget(appWidgetId, position, panelId, widgetInfo)
|
||||
LauncherPreferences.widgets().widgets(
|
||||
(LauncherPreferences.widgets().widgets() ?: HashSet()).also {
|
||||
it.add(widget)
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.widget.TextView
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import de.jrpie.android.launcher.Application
|
||||
import de.jrpie.android.launcher.R
|
||||
import de.jrpie.android.launcher.databinding.ActivitySelectWidgetBinding
|
||||
import de.jrpie.android.launcher.ui.UIObject
|
||||
|
@ -24,7 +25,7 @@ import de.jrpie.android.launcher.widgets.LauncherWidgetProvider
|
|||
import de.jrpie.android.launcher.widgets.WidgetPanel
|
||||
import de.jrpie.android.launcher.widgets.WidgetPosition
|
||||
import de.jrpie.android.launcher.widgets.bindAppWidgetOrRequestPermission
|
||||
import de.jrpie.android.launcher.widgets.getAppWidgetHost
|
||||
import de.jrpie.android.launcher.widgets.generateInternalId
|
||||
import de.jrpie.android.launcher.widgets.getAppWidgetProviders
|
||||
import de.jrpie.android.launcher.widgets.updateWidget
|
||||
|
||||
|
@ -38,12 +39,13 @@ private const val REQUEST_WIDGET_PERMISSION = 29
|
|||
*/
|
||||
class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
||||
lateinit var binding: ActivitySelectWidgetBinding
|
||||
var widgetId: Int = -1
|
||||
var widgetPanelId: Int = WidgetPanel.HOME.id
|
||||
|
||||
private fun tryBindWidget(info: LauncherWidgetProvider) {
|
||||
when (info) {
|
||||
is LauncherAppWidgetProvider -> {
|
||||
val widgetId =
|
||||
(applicationContext as Application).appWidgetHost.allocateAppWidgetId()
|
||||
if (bindAppWidgetOrRequestPermission(
|
||||
this,
|
||||
info.info,
|
||||
|
@ -62,7 +64,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
|||
}
|
||||
}
|
||||
is LauncherClockWidgetProvider -> {
|
||||
updateWidget(ClockWidget(widgetId, WidgetPosition(0, 4, 12, 3), widgetPanelId))
|
||||
updateWidget(ClockWidget(generateInternalId(), WidgetPosition(0, 4, 12, 3), widgetPanelId))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
@ -81,11 +83,7 @@ class SelectWidgetActivity : AppCompatActivity(), UIObject {
|
|||
setContentView(binding.root)
|
||||
|
||||
|
||||
widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
|
||||
widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
|
||||
if (widgetId == -1) {
|
||||
widgetId = getAppWidgetHost().allocateAppWidgetId()
|
||||
}
|
||||
|
||||
val viewManager = LinearLayoutManager(this)
|
||||
val viewAdapter = SelectWidgetRecyclerAdapter()
|
||||
|
|
|
@ -113,7 +113,7 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
|
|||
height
|
||||
)
|
||||
selectedWidgetOverlayView = view
|
||||
selectedWidgetView = widgetViewById[view.widgetId] ?: return true
|
||||
selectedWidgetView = widgetViewById[view.widgetId]
|
||||
startWidgetPosition = position
|
||||
|
||||
val positionInView = start.minus(Point(position.left, position.top))
|
||||
|
|
|
@ -41,7 +41,7 @@ class AppWidget(
|
|||
id,
|
||||
position,
|
||||
panelId,
|
||||
false,
|
||||
panelId != WidgetPanel.HOME.id,
|
||||
widgetProviderInfo.provider.packageName,
|
||||
widgetProviderInfo.provider.className,
|
||||
widgetProviderInfo.profile.hashCode()
|
||||
|
@ -78,6 +78,10 @@ class AppWidget(
|
|||
|
||||
override fun createView(activity: Activity): AppWidgetHostView? {
|
||||
val providerInfo = activity.getAppWidgetManager().getAppWidgetInfo(id) ?: return null
|
||||
/* TODO: if providerInfo is null, the corresponding app was probably uninstalled.
|
||||
There does not seem to be a way to recover the widget when the app is installed again,
|
||||
hence it should be deleted. */
|
||||
|
||||
val view = activity.getAppWidgetHost()
|
||||
.createView(activity, this.id, providerInfo)
|
||||
|
||||
|
|
|
@ -12,14 +12,14 @@ import kotlinx.serialization.Serializable
|
|||
@Serializable
|
||||
@SerialName("widget:clock")
|
||||
class ClockWidget(
|
||||
override val id: Int,
|
||||
override var id: Int,
|
||||
override var position: WidgetPosition,
|
||||
override val panelId: Int,
|
||||
override var allowInteraction: Boolean = true
|
||||
) : Widget() {
|
||||
|
||||
override fun createView(activity: Activity): View? {
|
||||
return ClockView(activity, null, id)
|
||||
override fun createView(activity: Activity): View {
|
||||
return ClockView(activity, null, id, panelId)
|
||||
}
|
||||
|
||||
override fun findView(views: Sequence<View>): ClockView? {
|
||||
|
|
|
@ -12,7 +12,7 @@ import kotlinx.serialization.Serializable
|
|||
@Serializable
|
||||
@SerialName("widget:debuginfo")
|
||||
class DebugInfoWidget(
|
||||
override val id: Int,
|
||||
override var id: Int,
|
||||
override var position: WidgetPosition,
|
||||
override val panelId: Int,
|
||||
override var allowInteraction: Boolean = true
|
||||
|
|
|
@ -27,7 +27,9 @@ sealed class Widget {
|
|||
abstract fun configure(activity: Activity, requestCode: Int)
|
||||
|
||||
fun delete(context: Context) {
|
||||
context.getAppWidgetHost().deleteAppWidgetId(id)
|
||||
if (id >= 0) {
|
||||
context.getAppWidgetHost().deleteAppWidgetId(id)
|
||||
}
|
||||
|
||||
LauncherPreferences.widgets().widgets(
|
||||
LauncherPreferences.widgets().widgets()?.also {
|
||||
|
|
|
@ -13,12 +13,8 @@ import android.os.UserManager
|
|||
import android.util.Log
|
||||
import de.jrpie.android.launcher.Application
|
||||
import de.jrpie.android.launcher.preferences.LauncherPreferences
|
||||
import kotlin.math.min
|
||||
|
||||
fun deleteAllWidgets(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.getAppWidgetHost().appWidgetIds.forEach { AppWidget(it).delete(context) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to bind [providerInfo] to the id [id].
|
||||
|
@ -29,12 +25,9 @@ fun deleteAllWidgets(context: Context) {
|
|||
*
|
||||
* @return true iff the app widget was bound successfully.
|
||||
*/
|
||||
fun bindAppWidgetOrRequestPermission(activity: Activity, providerInfo: AppWidgetProviderInfo, id: Int, requestCode: Int? = null): Boolean {
|
||||
val appWidgetId = if(id == -1) {
|
||||
activity.getAppWidgetHost().allocateAppWidgetId()
|
||||
} else { id }
|
||||
fun bindAppWidgetOrRequestPermission(activity: Activity, providerInfo: AppWidgetProviderInfo, appWidgetId: Int, requestCode: Int? = null): Boolean {
|
||||
|
||||
Log.i("Launcher", "Binding new widget ${appWidgetId}")
|
||||
Log.i("Launcher", "Binding new widget $appWidgetId")
|
||||
if (!activity.getAppWidgetManager().bindAppWidgetIdIfAllowed(
|
||||
appWidgetId,
|
||||
providerInfo.provider
|
||||
|
@ -79,6 +72,13 @@ fun updateWidget(widget: Widget) {
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
// TODO: this needs to be improved
|
||||
fun generateInternalId(): Int {
|
||||
val minId = min(-5,(LauncherPreferences.widgets().widgets() ?: setOf()).minOfOrNull { it.id } ?: 0)
|
||||
return minId -1
|
||||
}
|
||||
|
||||
fun updateWidgetPanel(widgetPanel: WidgetPanel) {
|
||||
LauncherPreferences.widgets().customPanels(
|
||||
(LauncherPreferences.widgets().customPanels() ?: setOf())
|
||||
|
@ -92,4 +92,4 @@ fun Context.getAppWidgetHost(): AppWidgetHost {
|
|||
}
|
||||
fun Context.getAppWidgetManager(): AppWidgetManager {
|
||||
return (this.applicationContext as Application).appWidgetManager
|
||||
}
|
||||
}
|
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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="15sp">
|
||||
android:layout_marginHorizontal="30sp">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/list_widgets_row_icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:src="@mipmap/ic_launcher_round"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_widgets_row_name"
|
||||
android:layout_width="0dp"
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10sp"
|
||||
android:layout_marginEnd="10sp"
|
||||
android:gravity="start"
|
||||
android:text=""
|
||||
android:textSize="20sp"
|
||||
tools:text="some widget"
|
||||
app:layout_constraintStart_toEndOf="@id/list_widgets_row_icon"
|
||||
app:cardBackgroundColor="?cardBackgroundColor"
|
||||
app:cardElevation="8dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardUseCompatPadding="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_widgets_row_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10sp"
|
||||
android:gravity="start"
|
||||
android:text=""
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toStartOf="@+id/list_widgets_row_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/list_widgets_row_name"
|
||||
tools:text="a longer description of the widget" />
|
||||
<ImageView
|
||||
android:id="@+id/list_widgets_row_preview"
|
||||
android:layout_width="0dp"
|
||||
android:maxHeight="100dp"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:layout_height="100dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/list_widgets_row_description"
|
||||
tools:src="@mipmap/ic_launcher_round"
|
||||
tools:ignore="ContentDescription" />
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_margin="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/icon_title_description_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/list_widgets_row_icon"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
tools:src="@mipmap/ic_launcher_round"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_widgets_row_name"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginStart="10sp"
|
||||
android:layout_marginEnd="10sp"
|
||||
android:text=""
|
||||
style="@style/TextAppearance.Material3.HeadlineSmall"
|
||||
tools:text="Some Widget"
|
||||
app:layout_constraintStart_toEndOf="@id/list_widgets_row_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/list_widgets_row_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/list_widgets_row_icon"/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_widgets_row_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/list_widgets_row_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/list_widgets_row_name"
|
||||
tools:text="A longer description of the widget that may take up multiple lines." />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/icon_title_description_container">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/list_widgets_row_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:maxHeight="100dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/list_widgets_row_icon"
|
||||
tools:src="@mipmap/ic_launcher_round"
|
||||
tools:ignore="ContentDescription,NotSibling" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -41,6 +41,13 @@
|
|||
android:text="@string/settings_meta_view_code"
|
||||
android:textAllCaps="false" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/settings_meta_button_view_docs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_meta_view_docs"
|
||||
android:textAllCaps="false" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/settings_meta_button_report_bug"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -33,17 +33,27 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tutorial_setup_title" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/tutorial_setup_actions_rview_fragment"
|
||||
android:name="de.jrpie.android.launcher.ui.settings.actions.SettingsFragmentActionsRecycler"
|
||||
android:layout_width="0dp"
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardElevation="8dp"
|
||||
app:cardBackgroundColor="?cardBackgroundColor"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardUseCompatPadding="true"
|
||||
app:layout_constraintTop_toBottomOf="@id/tutorial_setup_subtitle"
|
||||
app:layout_constraintBottom_toTopOf="@id/tutorial_setup_text_bottom"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tutorial_setup_subtitle" />
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/tutorial_setup_actions_rview_fragment"
|
||||
android:name="de.jrpie.android.launcher.ui.settings.actions.SettingsFragmentActionsRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginVertical="4dp"
|
||||
android:layout_marginStart="10dp"/>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tutorial_setup_text_bottom"
|
||||
|
|
|
@ -97,9 +97,9 @@
|
|||
<string name="settings_display_screen_timeout_disabled">Bildschirm nicht ausschalten</string>
|
||||
<string name="settings_display_rotate_screen">Bildschirm drehen</string>
|
||||
<string name="settings_launcher_section_functionality">Funktionalität</string>
|
||||
<string name="settings_enabled_gestures_double_swipe">Doppelte Wischaktionen</string>
|
||||
<string name="settings_enabled_gestures_double_swipe">Doppelte Wischgesten</string>
|
||||
<string name="settings_enabled_gestures_double_swipe_summary">Mit zwei Fingern wischen</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe">Kantenaktionen</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe">Kantengesten</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe_edge_width">Kantenbreite</string>
|
||||
<string name="settings_functionality_auto_launch">Suchergebnis starten</string>
|
||||
<string name="settings_functionality_search_web_summary">Beim Durchsuchen der Apps Enter drücken, um stattdessen im Internet zu suchen.</string>
|
||||
|
|
|
@ -93,9 +93,9 @@
|
|||
<string name="settings_clock_show_seconds">Mostra secondi</string>
|
||||
<string name="settings_launcher_section_date_time"><![CDATA[Data e ora]]></string>
|
||||
<string name="settings_enabled_gestures_double_swipe_summary">Scorri con due dita</string>
|
||||
<string name="settings_enabled_gestures_double_swipe">Azioni a due dita</string>
|
||||
<string name="settings_enabled_gestures_double_swipe">Scorrimento a due dita</string>
|
||||
<string name="settings_functionality_auto_launch">Apri il risultato della ricerca</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe">Azioni sui bordi dello schermo</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe">Scorrimento sui bordi dello schermo</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe_summary">Scorri sui bordi dello schermo</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe_edge_width">Larghezza bordo</string>
|
||||
<string name="settings_meta_view_code">Codice sorgente</string>
|
||||
|
@ -287,4 +287,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="alert_recent_apps_failed">Errore: impossibile mostrare le app recenti. (Se hai appena aggiornato l\'app, prova a disabilitare e riabilitare il servizio di accessibilità dalle impostazioni del telefono)</string>
|
||||
<string name="settings_functionality_auto_close_keyboard">Chiudi la tastiera durante lo scorrimento</string>
|
||||
<string name="settings_widgets_widgets">Gestione widget</string>
|
||||
<string name="widget_menu_remove">Rimuovi</string>
|
||||
<string name="settings_widgets_custom_panels">Gestione pannelli widget</string>
|
||||
<string name="select_widget_title">Scegli widget</string>
|
||||
<string name="widget_menu_configure">Configura</string>
|
||||
<string name="widget_menu_enable_interaction">Abilita interazione</string>
|
||||
<string name="widget_menu_disable_interaction">Disabilita interazione</string>
|
||||
<string name="widget_clock_label">Orologio</string>
|
||||
<string name="widget_clock_description">Orologio predefinito di μLauncher</string>
|
||||
<string name="manage_widget_panels_delete">Elimina</string>
|
||||
<string name="manage_widget_panels_rename">Rinomina</string>
|
||||
<string name="widget_panel_default_name">Pannello widget #%1$d</string>
|
||||
<string name="dialog_ok">Ok</string>
|
||||
<string name="widget_panels_title">Pannelli widget</string>
|
||||
<plurals name="widget_panel_number_of_widgets">
|
||||
<item quantity="one">Contiene %1$d widget.</item>
|
||||
<item quantity="many">Contiene %1$d widget.</item>
|
||||
<item quantity="other">Contiene %1$d widget.</item>
|
||||
</plurals>
|
||||
<string name="dialog_create_widget_panel_title">Crea nuovo pannello widget</string>
|
||||
<string name="list_other_open_widget_panel">Apri pannello widget</string>
|
||||
<string name="alert_widget_panel_not_found">Questo pannello widget non esiste più.</string>
|
||||
<string name="settings_launcher_section_widgets">Widget</string>
|
||||
<string name="dialog_select_widget_panel_title">Seleziona pannello widget</string>
|
||||
<string name="dialog_select_widget_panel_info_no_panels"><![CDATA[Nessun pannello widget trovato. Puoi creare pannelli widget da Impostazioni > Launcher > Gestione Pannelli Widget.]]></string>
|
||||
</resources>
|
||||
|
|
|
@ -1,16 +1,136 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!--
|
||||
-
|
||||
- Home - Pradžia
|
||||
-
|
||||
-->
|
||||
<string name="alert_cant_open_title">Nepavyksta paleisti programėlės</string>
|
||||
<string name="alert_cant_open_message">Norite pakeisti nustatymus?</string>
|
||||
|
||||
<string name="toast_cant_open_message">Atidarykite nustatymus norėdami pasirinkti šio gesto veiksmą</string>
|
||||
|
||||
<!--
|
||||
-
|
||||
- Settings
|
||||
-
|
||||
-->
|
||||
<string name="settings_title">Nustatymai</string>
|
||||
|
||||
<string name="settings_tab_actions">Veiksmai</string>
|
||||
<string name="settings_tab_launcher">Paleidimo programėlė</string>
|
||||
<string name="settings_tab_meta">Apie</string>
|
||||
|
||||
<!--
|
||||
-
|
||||
- Settings : Apps
|
||||
-
|
||||
-->
|
||||
<string name="settings_gesture_back">Atgal</string>
|
||||
<string name="settings_gesture_description_back">Grįžimo mygtukas / grįžimo gestas</string>
|
||||
<string name="settings_gesture_up">Aukštyn</string>
|
||||
<string name="settings_gesture_description_up">Perbraukimas aukštyn</string>
|
||||
<string name="settings_gesture_tap_up">Bakstelėkite + aukštyn</string>
|
||||
<string name="alert_cant_open_title">Nepavyksta paleisti programėlės</string>
|
||||
<string name="toast_cant_open_message">Atidarykite nustatymus norėdami pasirinkti šio gesto veiksmą</string>
|
||||
<string name="settings_gesture_up">Aukštyn</string>
|
||||
<string name="settings_gesture_description_tap_up">Bakstelėjimas ir perbraukimas aukštyn</string>
|
||||
<string name="settings_gesture_double_up">Dvigubai aukštyn</string>
|
||||
<string name="settings_gesture_description_double_up">Perbraukite aukštyn dviem pirštais</string>
|
||||
<string name="settings_gesture_down">Žemyn</string>
|
||||
<string name="settings_gesture_description_down">Perbraukite žemyn</string>
|
||||
<string name="settings_gesture_tap_down">Bakstelėkite + žemyn</string>
|
||||
<string name="settings_gesture_description_tap_down">Bakstelėkite ir perbraukite žemyn</string>
|
||||
<string name="settings_gesture_double_down">Dvigubai žemyn</string>
|
||||
<string name="settings_gesture_description_double_down">Perbraukite dviem pirštais</string>
|
||||
<string name="settings_gesture_left">Kairėje</string>
|
||||
<string name="settings_gesture_description_left">Perbraukite į kairę</string>
|
||||
<string name="settings_gesture_tap_left">Bakstelėkite + kairę</string>
|
||||
<string name="settings_gesture_description_tap_left">Bakstelėkite ir perbraukite į kairę</string>
|
||||
<string name="settings_gesture_double_left">Dvigubai kairėje</string>
|
||||
<string name="settings_gesture_description_double_left">Du pirštais perbraukite kairėn</string>
|
||||
<string name="settings_gesture_right">Dešinė</string>
|
||||
<string name="settings_gesture_description_right">Perbraukite į dešinę</string>
|
||||
<string name="settings_gesture_tap_right">Bakstelėkite + dešinė</string>
|
||||
<string name="settings_gesture_description_tap_right">Bakstelėkite ir perbraukite į dešinę</string>
|
||||
<string name="settings_gesture_double_right">Dviguba dešinė</string>
|
||||
<string name="settings_gesture_description_double_right">Perbraukite į dešinę dviem pirštais</string>
|
||||
<string name="settings_gesture_right_top_edge">Dešinė (viršuje)</string>
|
||||
<string name="settings_gesture_description_right_top_edge">Perbraukite tiesiai ekrano viršuje</string>
|
||||
<string name="settings_gesture_right_bottom_edge">Dešinė (apačia)</string>
|
||||
<string name="settings_gesture_description_right_bottom_edge">Perbraukite tiesiai ekrano apačioje</string>
|
||||
<string name="settings_gesture_left_bottom_edge">Kairė (apačia)</string>
|
||||
<string name="settings_gesture_description_left_bottom_edge">Perbraukite į kairę ekrano apačioje</string>
|
||||
<string name="settings_gesture_left_top_edge">Kairė (viršuje)</string>
|
||||
<string name="settings_gesture_description_left_top_edge">Perbraukite kairėn ekrano viršuje</string>
|
||||
<string name="settings_gesture_up_left_edge">Aukštyn (kairysis kraštas)</string>
|
||||
<string name="settings_gesture_description_up_left_edge">Perbraukite aukštyn kairiajame ekrano krašte</string>
|
||||
<string name="settings_gesture_up_right_edge">Aukštyn (dešinysis kraštas)</string>
|
||||
<string name="settings_gesture_description_up_right_edge">Perbraukite aukštyn dešiniajame ekrano krašte</string>
|
||||
<string name="settings_gesture_down_left_edge">Žemyn (kairysis kraštas)</string>
|
||||
<string name="settings_gesture_description_down_left_edge">Perbraukite žemyn kairiajame ekrano krašte</string>
|
||||
<string name="settings_gesture_down_right_edge">Žemyn (dešinysis kraštas)</string>
|
||||
<string name="settings_gesture_description_down_right_edge">Žemyn (dešinysis kraštas)</string>
|
||||
|
||||
<string name="settings_gesture_vol_up">Garsumo didinimo klavišas</string>
|
||||
<string name="settings_gesture_description_vol_up">Paspauskite mygtuką „Volume Up“</string>
|
||||
<string name="settings_gesture_vol_down">Volume žemyn klavišas</string>
|
||||
<string name="settings_gesture_description_vol_down">Paspauskite mygtuką „Volume Down“</string>
|
||||
<string name="settings_gesture_double_click">Dukart spustelėkite</string>
|
||||
<string name="settings_gesture_description_double_click">Dukart spustelėkite tuščią sritį</string>
|
||||
<string name="settings_gesture_long_click">Ilgas spustelėjimas</string>
|
||||
<string name="settings_gesture_description_long_click">Ilgai spustelėkite tuščią sritį</string>
|
||||
<string name="settings_gesture_date">Data</string>
|
||||
<string name="settings_gesture_description_date">Spustelėkite datą</string>
|
||||
<string name="settings_gesture_time">Laikas</string>
|
||||
<string name="settings_gesture_description_time">Spustelėkite laiką</string>
|
||||
|
||||
<string name="settings_widgets_widgets">Tvarkykite valdiklius</string>
|
||||
<string name="settings_widgets_custom_panels">Tvarkykite valdiklio skydelius</string>
|
||||
|
||||
|
||||
<string name="settings_apps_choose">Pasirinkite programą</string>
|
||||
|
||||
<string name="settings_apps_install">Įdiekite programas</string>
|
||||
<string name="settings_apps_toast_store_not_found">Parduotuvėje nerasta</string>
|
||||
|
||||
<!--
|
||||
-
|
||||
- Settings : Launcher
|
||||
-
|
||||
-->
|
||||
<string name="settings_launcher_section_appearance">Išvaizda</string>
|
||||
|
||||
|
||||
<string name="settings_theme_color_theme">Spalvos tema</string>
|
||||
<string name="settings_theme_color_theme_item_default">Numatytasis</string>
|
||||
<string name="settings_theme_color_theme_item_dark">Tamsu</string>
|
||||
<string name="settings_theme_color_theme_item_light">Šviesa</string>
|
||||
<string name="settings_theme_color_theme_item_dynamic">Dinaminis</string>
|
||||
|
||||
<string name="settings_theme_text_shadow">Teksto šešėlis</string>
|
||||
<string name="settings_theme_background">Fonas (programų sąrašas ir nustatymas)</string>
|
||||
<string name="settings_theme_background_item_transparent">Skaidrus</string>
|
||||
<string name="settings_theme_background_item_dim">Dim</string>
|
||||
<string name="settings_theme_background_item_blur">Blur</string>
|
||||
<string name="settings_theme_background_item_solid">Solidus</string>
|
||||
|
||||
|
||||
<string name="settings_theme_font">Šriftas</string>
|
||||
|
||||
<!-- names for @array/settings_theme_font_values -->
|
||||
<string name="settings_theme_font_item_system_default">Sistemos numatytasis</string>
|
||||
<string name="settings_theme_font_item_sans_serif">Be serifo</string>
|
||||
<string name="settings_theme_font_item_serif">Serifas</string>
|
||||
<string name="settings_theme_font_item_monospace">Monoerdvė</string>
|
||||
<string name="settings_theme_font_item_serif_monospace">Serifo monospace</string>
|
||||
<string name="settings_theme_monochrome_icons">Vienspalvių programų piktogramos</string>
|
||||
|
||||
<string name="settings_launcher_section_date_time"><![CDATA[Date & time]]></string>
|
||||
<string name="settings_clock_color">Spalva</string>
|
||||
<string name="settings_clock_time_visible">Rodyti laiką</string>
|
||||
<string name="settings_clock_date_visible">Rodyti datą</string>
|
||||
<string name="settings_clock_localized">Naudoti lokalizuotą datos formatą</string>
|
||||
<string name="settings_clock_show_seconds">Rodyti sekundes</string>
|
||||
<string name="settings_clock_flip_date_time">Apversti datą ir laiką</string>
|
||||
|
||||
<string name="settings_theme_wallpaper">Pasirinkite ekrano foną</string>
|
||||
<string name="settings_launcher_section_display">Ekranas</string>
|
||||
</resources>
|
||||
|
|
|
@ -289,9 +289,9 @@
|
|||
<string name="settings_display_hide_navigation_bar">Navigatiebalk verbergen</string>
|
||||
<string name="settings_display_rotate_screen">Scherm draaien</string>
|
||||
<string name="settings_launcher_section_functionality">Functionaliteit</string>
|
||||
<string name="settings_enabled_gestures_double_swipe">Dubbele veeg acties</string>
|
||||
<string name="settings_enabled_gestures_double_swipe">Dubbele veeg gebaaren</string>
|
||||
<string name="settings_enabled_gestures_double_swipe_summary">Met twee vingers vegen</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe">Hoek-acties</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe">Hoek-gebaren</string>
|
||||
<string name="settings_enabled_gestures_edge_swipe_edge_width">Kantbreedte</string>
|
||||
<string name="settings_functionality_auto_launch">Start zoekresultaten</string>
|
||||
<string name="settings_functionality_auto_launch_summary">Spatie drukken om deze functie tijdelijk te onderdruken.</string>
|
||||
|
|
|
@ -285,4 +285,22 @@
|
|||
<string name="alert_recent_apps_failed">错误:无法展示最近应用屏幕。(如果您刚刚升级了本启动器,请尝试在手机设置中手动禁用再重新启用“无障碍”服务。)</string>
|
||||
<string name="list_other_launch_other_launcher">启动其他启动器</string>
|
||||
<string name="settings_functionality_auto_close_keyboard">滚动应用程序列表时自动隐藏键盘</string>
|
||||
<string name="settings_widgets_widgets">设置小部件</string>
|
||||
<string name="widget_menu_remove">删除</string>
|
||||
<string name="widget_menu_enable_interaction">开启交互功能</string>
|
||||
<string name="widget_menu_disable_interaction">关闭交互功能</string>
|
||||
<string name="widget_clock_label">时钟</string>
|
||||
<string name="manage_widget_panels_delete">删除</string>
|
||||
<string name="manage_widget_panels_rename">重命名</string>
|
||||
<string name="widget_panel_default_name">小部件面板 #%1$d</string>
|
||||
<string name="widget_panels_title">小部件面板</string>
|
||||
<string name="dialog_select_widget_panel_title">选择小部件面板</string>
|
||||
<string name="list_other_open_widget_panel">打开小部件面板</string>
|
||||
<string name="dialog_create_widget_panel_title">创建新面板</string>
|
||||
<string name="dialog_select_widget_panel_info_no_panels"><![CDATA[未发现小部件面板。您可以在“设置 > 启动器 > 设置小部件面板”中进行创建。]]></string>
|
||||
<string name="settings_launcher_section_widgets">桌面小部件</string>
|
||||
<string name="alert_widget_panel_not_found">该小部件面板不存在</string>
|
||||
<string name="widget_clock_description">μLauncher 默认时钟小部件</string>
|
||||
<string name="select_widget_title">选择小部件</string>
|
||||
<string name="settings_widgets_custom_panels">设置小部件面板</string>
|
||||
</resources>
|
||||
|
|
|
@ -161,7 +161,9 @@
|
|||
-
|
||||
-->
|
||||
<string name="settings_meta_link_github" translatable="false">https://github.com/jrpie/Launcher</string>
|
||||
<string name="settings_meta_link_docs" translatable="false">https://launcher.jrpie.de/</string>
|
||||
<string name="settings_meta_report_bug_link" translatable="false">https://github.com/jrpie/Launcher/issues/new?template=bug_report.yaml</string>
|
||||
<string name="settings_meta_report_bug_mail" translatable="false">android-launcher-crash@jrpie.de</string>
|
||||
<string name="settings_meta_report_vulnerability_link" translatable="false">https://github.com/jrpie/Launcher/security/policy</string>
|
||||
<string name="settings_meta_fork_contact_url" translatable="false">https://s.jrpie.de/contact</string>
|
||||
<string name="settings_meta_privacy_url" translatable="false">https://s.jrpie.de/android-legal</string>
|
||||
|
|
|
@ -419,5 +419,29 @@
|
|||
<string name="list_other_open_widget_panel">Open Widget Panel</string>
|
||||
<string name="alert_widget_panel_not_found">This widget panel no longer exists.</string>
|
||||
<string name="settings_launcher_section_widgets">Widgets</string>
|
||||
|
||||
<string name="notification_crash_title">μLauncher crashed</string>
|
||||
<string name="notification_crash_explanation">Sorry! Click for more information.</string>
|
||||
<string name="crash_info"><![CDATA[
|
||||
Looks like something went wrong, sorry about that!<br><br>
|
||||
For privacy reasons, crash logs are not collected automatically.<br>
|
||||
However logs are very useful for debugging, so I would be very grateful if you could send me the attached log by mail
|
||||
or create a bug report on github.<br><br>
|
||||
Note that crash logs might contain <strong>sensitive information</strong>, e.g. the name of an app you tried to launch.
|
||||
Please <strong>redact</strong> such information before sending the report.
|
||||
<h2>What can I do now?</h2>
|
||||
If this bug appears again and again, you can try several things:
|
||||
<ul>
|
||||
<li>Force stop μLauncher</li>
|
||||
<li>Clear μLauncher\'s storage (<strong>Your settings will be lost!</strong>)</li>
|
||||
<li>Install an older version (<a href=\"https://github.com/jrpie/Launcher/releases\">GitHub</a>, <a href=\"https://f-droid.org/en/packages/de.jrpie.android.launcher\">F-Droid</a>)</li>
|
||||
</ul>
|
||||
]]>
|
||||
</string>
|
||||
<string name="report_crash_button_copy">Copy crash report to clipboard</string>
|
||||
<string name="report_crash_button_mail">Send report by mail</string>
|
||||
<string name="report_crash_button_report">Create bug report on GitHub</string>
|
||||
<string name="report_crash_title">μLauncher crashed</string>
|
||||
<string name="send_email">Send Email</string>
|
||||
<string name="notification_channel_crash">Crashes and Debug Information</string>
|
||||
<string name="settings_meta_view_docs">Documentation</string>
|
||||
</resources>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<item name="android:buttonStyle">@style/Widget.AppCompat.Button.Colored</item>
|
||||
<item name="colorButtonNormal">?colorAccent</item>
|
||||
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
|
||||
|
||||
<!--<item name="android:popupMenuStyle">@style/PopupMenuCustom</item>-->
|
||||
|
||||
|
@ -32,6 +33,7 @@
|
|||
<item name="colorPrimaryDark">@color/darkTheme_background_color</item>
|
||||
<item name="colorAccent">@color/darkTheme_accent_color</item>
|
||||
<item name="android:colorBackground">@color/darkTheme_background_color</item>
|
||||
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
|
||||
<item name="android:textColor">@color/darkTheme_text_color</item>
|
||||
</style>
|
||||
|
||||
|
@ -40,6 +42,7 @@
|
|||
<item name="colorPrimaryDark">@color/finnmglasTheme_background_color</item>
|
||||
<item name="colorAccent">@color/finnmglasTheme_accent_color</item>
|
||||
<item name="android:colorBackground">@color/finnmglasTheme_background_color</item>
|
||||
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
|
||||
<item name="android:textColor">@color/finnmglasTheme_text_color</item>
|
||||
</style>
|
||||
|
||||
|
@ -48,6 +51,7 @@
|
|||
<item name="colorPrimaryDark">@color/lightTheme_background_color</item>
|
||||
<item name="colorAccent">@color/lightTheme_accent_color</item>
|
||||
<item name="android:colorBackground">@color/lightTheme_background_color</item>
|
||||
<item name="cardBackgroundColor">@color/cardview_light_background</item>
|
||||
<item name="android:textColor">@color/lightTheme_text_color</item>
|
||||
</style>
|
||||
|
||||
|
@ -57,6 +61,7 @@
|
|||
<item name="colorPrimaryDark">@color/material_dynamic_primary50</item>
|
||||
<item name="colorAccent">@color/material_dynamic_tertiary50</item>
|
||||
<item name="android:colorBackground">@color/material_dynamic_neutral10</item>
|
||||
<item name="cardBackgroundColor">@color/cardview_dark_background</item>
|
||||
<item name="android:textColor">@color/material_dynamic_neutral_variant90</item>
|
||||
</style>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue