Merge branch 'main' into Hide-zombie-secure-folder-tab

This commit is contained in:
MM20 2025-04-02 20:42:23 +02:00
commit bffd440408
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
78 changed files with 5707 additions and 2094 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@
*.aar
*.ap_
*.aab
*.dm
# Files for the ART/Dalvik VM
*.dex

View File

@ -2,6 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.SET_ALARM" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
@ -23,6 +27,7 @@
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.VIBRATE" />

View File

@ -13,7 +13,6 @@ import de.mm20.launcher2.widgets.FavoritesWidget
import de.mm20.launcher2.widgets.WidgetRepository
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import org.koin.androidx.compose.inject
@Composable

View File

@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetState
import androidx.compose.material3.rememberModalBottomSheetState
@ -21,6 +22,7 @@ fun BottomSheetDialog(
content: @Composable (paddingValues: PaddingValues) -> Unit,
) {
ModalBottomSheet(
modifier = Modifier.statusBarsPadding().padding(top = 8.dp),
sheetState = bottomSheetState,
onDismissRequest = onDismissRequest,
) {

View File

@ -1,6 +1,7 @@
package de.mm20.launcher2.ui.ktx
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import hct.Hct
import kotlin.math.atan2
import kotlin.math.roundToInt
@ -20,6 +21,14 @@ fun Color.Companion.hct(hue: Float, chroma: Float, tone: Float): Color {
return Color(hct.toInt())
}
fun Color.atTone(tone: Int): Color {
return Color(
Hct.fromInt(this.toArgb()).apply {
this.tone = tone.toDouble()
}.toInt()
)
}
val Color.hue: Float
get() {
val r = this.red / 255f
@ -28,3 +37,5 @@ val Color.hue: Float
// sqrt(3)
return atan2(1.7320508f * (g - b), 2f * r - g - b)
}
fun android.graphics.Color.toComposeColor() = Color(this.toArgb())

View File

@ -64,6 +64,7 @@ fun SearchColumn(
) {
val columns = LocalGridSettings.current.columnCount
val showList = LocalGridSettings.current.showList
val context = LocalContext.current
val viewModel: SearchVM = viewModel()
@ -93,6 +94,7 @@ fun SearchColumn(
val bestMatch by viewModel.bestMatch
val query by viewModel.searchQuery
val isSearchEmpty by viewModel.isSearchEmpty
val missingCalendarPermission by viewModel.missingCalendarPermission.collectAsState(false)
@ -111,13 +113,14 @@ fun SearchColumn(
val expandedCategory: SearchCategory? by viewModel.expandedCategory
var selectedAppProfileIndex: Int by remember(isSearchEmpty) { mutableIntStateOf(0) }
var selectedContactIndex: Int by remember(contacts) { mutableIntStateOf(-1) }
var selectedFileIndex: Int by remember(files) { mutableIntStateOf(-1) }
var selectedCalendarIndex: Int by remember(events) { mutableIntStateOf(-1) }
var selectedLocationIndex: Int by remember(locations) { mutableIntStateOf(-1) }
var selectedShortcutIndex: Int by remember(appShortcuts) { mutableIntStateOf(-1) }
var selectedArticleIndex: Int by remember(wikipedia) { mutableIntStateOf(-1) }
var selectedWebsiteIndex: Int by remember(website) { mutableIntStateOf(-1) }
var selectedAppIndex: Int by remember(query) { mutableIntStateOf(-1) }
var selectedContactIndex: Int by remember(query) { mutableIntStateOf(-1) }
var selectedFileIndex: Int by remember(query) { mutableIntStateOf(-1) }
var selectedCalendarIndex: Int by remember(query) { mutableIntStateOf(-1) }
var selectedLocationIndex: Int by remember(query) { mutableIntStateOf(-1) }
var selectedShortcutIndex: Int by remember(query) { mutableIntStateOf(-1) }
var selectedArticleIndex: Int by remember(query) { mutableIntStateOf(-1) }
var selectedWebsiteIndex: Int by remember(query) { mutableIntStateOf(-1) }
val showFilters by viewModel.showFilters
@ -193,6 +196,9 @@ fun SearchColumn(
columns = columns,
reverse = reverse,
showProfileLockControls = hasProfilesPermission,
showList = showList,
selectedIndex = selectedAppIndex,
onSelect = { selectedAppIndex = it },
)
} else {
AppResults(
@ -202,7 +208,10 @@ fun SearchColumn(
selectedAppProfileIndex = it
},
columns = columns,
reverse = reverse
reverse = reverse,
showList = showList,
selectedIndex = selectedAppIndex,
onSelect = { selectedAppIndex = it },
)
}

View File

@ -4,6 +4,7 @@ import android.app.PendingIntent
import android.content.Intent
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
@ -61,7 +62,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import androidx.compose.ui.unit.roundToIntRect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.compose.AsyncImage
import de.mm20.launcher2.crashreporter.CrashReporter
@ -85,11 +85,15 @@ import kotlinx.coroutines.launch
fun AppItem(
modifier: Modifier = Modifier,
app: Application,
showDetails: Boolean,
onBack: () -> Unit
) {
val viewModel: SearchableItemVM = listItemViewModel(key = "search-${app.key}")
val iconSize = LocalGridSettings.current.iconSize.dp.toPixels()
val badge by viewModel.badge.collectAsStateWithLifecycle(null)
val icon by viewModel.icon.collectAsStateWithLifecycle()
LaunchedEffect(app) {
viewModel.init(app, iconSize.toInt())
}
@ -97,386 +101,440 @@ fun AppItem(
val context = LocalContext.current
val scope = rememberCoroutineScope()
Column(
modifier = modifier.verticalScroll(rememberScrollState())
) {
Row {
Column(
modifier = Modifier
.weight(1f)
.padding(16.dp)
) {
Text(
text = app.labelOverride ?: app.label,
style = MaterialTheme.typography.titleMedium
)
if (!app.isPrivate) {
val tags by viewModel.tags.collectAsState(emptyList())
if (tags.isNotEmpty()) {
Text(
modifier = Modifier.padding(top = 1.dp, bottom = 4.dp),
text = tags.joinToString(separator = " #", prefix = "#"),
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.labelSmall
)
}
app.versionName?.let {
Text(
text = stringResource(R.string.app_info_version, it),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 4.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
Text(
text = app.componentName.packageName,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 1.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
} else {
Text(
stringResource(R.string.profile_private_profile_state_locked),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 8.dp),
color = MaterialTheme.colorScheme.secondary,
)
}
}
val badge by viewModel.badge.collectAsStateWithLifecycle(null)
val icon by viewModel.icon.collectAsStateWithLifecycle()
ShapedLauncherIcon(
size = 48.dp,
modifier = Modifier
.padding(16.dp),
badge = { badge },
icon = { icon },
)
}
val notifications by viewModel.notifications.collectAsState(emptyList())
AnimatedVisibility(notifications.isNotEmpty()) {
var showAllNotifications by remember { mutableStateOf(false) }
AnimatedContent(
showAllNotifications || notifications.size == 1,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 12.dp)
.border(
1.dp,
MaterialTheme.colorScheme.outlineVariant,
MaterialTheme.shapes.small
)
.clip(MaterialTheme.shapes.small)
) { showAll ->
if (showAll) {
Column(
modifier = Modifier.animateContentSize()
) {
for ((i, not) in notifications.withIndex()) {
val icon =
remember(not.smallIcon) { not.smallIcon?.loadDrawable(context) }
if (not.title == null && not.text == null) continue
if (i > 0) {
HorizontalDivider()
}
Row(
verticalAlignment = Alignment.CenterVertically,
SharedTransitionLayout(modifier = modifier) {
AnimatedContent(showDetails) { showDetails ->
if (showDetails) {
Column(
modifier = Modifier.verticalScroll(rememberScrollState())
) {
Row {
Column(
modifier = Modifier
.weight(1f)
.padding(16.dp)
) {
Text(
text = app.labelOverride ?: app.label,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.clickable {
try {
not.contentIntent?.sendWithBackgroundPermission(context)
} catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e)
}
}
.padding(vertical = 4.dp)
) {
Box(
modifier = Modifier
.padding(horizontal = 12.dp)
.clip(CircleShape)
.background(Color(not.color))
.size(32.dp)
.padding(8.dp),
contentAlignment = Alignment.Center,
) {
AsyncImage(
modifier = Modifier.fillMaxSize(),
model = icon,
contentDescription = null
.sharedBounds(
rememberSharedContentState("label"),
this@AnimatedContent,
),
)
if (!app.isPrivate) {
val tags by viewModel.tags.collectAsState(emptyList())
if (tags.isNotEmpty()) {
Text(
modifier = Modifier.padding(top = 1.dp, bottom = 4.dp),
text = tags.joinToString(separator = " #", prefix = "#"),
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.labelSmall
)
}
Column(
modifier = Modifier.weight(1f)
) {
if (not.title != null) {
Text(
not.title!!,
style = MaterialTheme.typography.titleSmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
if (not.text != null) {
Text(
not.text!!,
modifier = Modifier.padding(top = 2.dp),
style = MaterialTheme.typography.bodySmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
app.versionName?.let {
Text(
text = stringResource(R.string.app_info_version, it),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 4.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
Text(
text = app.componentName.packageName,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 1.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
} else {
Text(
stringResource(R.string.profile_private_profile_state_locked),
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 8.dp),
color = MaterialTheme.colorScheme.secondary,
)
}
}
ShapedLauncherIcon(
size = 48.dp,
modifier = Modifier
.padding(16.dp),
badge = { badge },
icon = { icon },
)
}
val notifications by viewModel.notifications.collectAsState(emptyList())
AnimatedVisibility(notifications.isNotEmpty()) {
var showAllNotifications by remember { mutableStateOf(false) }
AnimatedContent(
showAllNotifications || notifications.size == 1,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 12.dp)
.border(
1.dp,
MaterialTheme.colorScheme.outlineVariant,
MaterialTheme.shapes.small
)
.clip(MaterialTheme.shapes.small)
) { showAll ->
if (showAll) {
Column(
modifier = Modifier.animateContentSize()
) {
for ((i, not) in notifications.withIndex()) {
val icon =
remember(not.smallIcon) {
not.smallIcon?.loadDrawable(
context
)
}
if (not.title == null && not.text == null) continue
if (i > 0) {
HorizontalDivider()
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable {
try {
not.contentIntent?.sendWithBackgroundPermission(
context
)
} catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e)
}
}
.padding(vertical = 4.dp)
) {
Box(
modifier = Modifier
.padding(horizontal = 12.dp)
.clip(CircleShape)
.background(Color(not.color))
.size(32.dp)
.padding(8.dp),
contentAlignment = Alignment.Center,
) {
AsyncImage(
modifier = Modifier.fillMaxSize(),
model = icon,
contentDescription = null
)
}
Column(
modifier = Modifier.weight(1f)
) {
if (not.title != null) {
Text(
not.title!!,
style = MaterialTheme.typography.titleSmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
if (not.text != null) {
Text(
not.text!!,
modifier = Modifier.padding(top = 2.dp),
style = MaterialTheme.typography.bodySmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
if (not.isClearable) {
IconButton(
onClick = {
viewModel.clearNotification(not)
}
) {
Icon(Icons.Rounded.Clear, null)
}
}
}
}
}
if (not.isClearable) {
} else {
Row(
modifier = Modifier
.clickable {
showAllNotifications = true
}
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Rounded.Notifications,
null,
modifier = Modifier.padding(horizontal = 16.dp)
)
Text(
pluralStringResource(
R.plurals.app_info_notifications,
notifications.size,
notifications.size
),
style = MaterialTheme.typography.titleSmall,
modifier = Modifier.weight(1f),
)
Icon(
Icons.AutoMirrored.Rounded.NavigateNext,
null,
modifier = Modifier.padding(horizontal = 12.dp)
)
}
}
}
}
val shortcuts by viewModel.children.collectAsState(emptyList())
if (shortcuts.isNotEmpty()) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 12.dp)
.border(
1.dp,
MaterialTheme.colorScheme.outlineVariant,
MaterialTheme.shapes.small
)
.clip(MaterialTheme.shapes.small)
) {
for ((i, shortcut) in shortcuts.withIndex()) {
val isPinned by remember(shortcut) {
viewModel.isChildPinned(
shortcut
)
}.collectAsState(
false
)
val iconSizePx = 32.dp.toPixels()
val icon by
remember {
viewModel.getChildIcon(
shortcut,
iconSizePx.toInt()
)
}.collectAsState(null)
if (i > 0) {
HorizontalDivider()
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable {
viewModel.launchChild(context, shortcut)
}
.padding(vertical = 4.dp)
) {
ShapedLauncherIcon(
size = 32.dp,
icon = { icon },
shape = CircleShape,
modifier = Modifier
.padding(horizontal = 12.dp)
.size(32.dp),
)
Text(
shortcut.labelOverride ?: shortcut.label,
modifier = Modifier.weight(1f),
style = MaterialTheme.typography.titleSmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
IconButton(
onClick = {
viewModel.clearNotification(not)
if (isPinned) {
viewModel.unpinChild(shortcut)
} else {
viewModel.pinChild(shortcut)
}
}
) {
Icon(Icons.Rounded.Clear, null)
Icon(
if (isPinned) Icons.Rounded.Star else Icons.Rounded.StarOutline,
stringResource(if (isPinned) R.string.menu_favorites_unpin else R.string.menu_favorites_pin),
)
}
}
}
}
}
} else {
Row(
modifier = Modifier
.clickable {
showAllNotifications = true
}
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Rounded.Notifications,
null,
modifier = Modifier.padding(horizontal = 16.dp)
)
Text(
pluralStringResource(
R.plurals.app_info_notifications,
notifications.size,
notifications.size
),
style = MaterialTheme.typography.titleSmall,
modifier = Modifier.weight(1f),
)
Icon(
Icons.AutoMirrored.Rounded.NavigateNext,
null,
modifier = Modifier.padding(horizontal = 12.dp)
)
}
}
}
}
val shortcuts by viewModel.children.collectAsState(emptyList())
if (shortcuts.isNotEmpty()) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 12.dp)
.border(
1.dp,
MaterialTheme.colorScheme.outlineVariant,
MaterialTheme.shapes.small
)
.clip(MaterialTheme.shapes.small)
) {
for ((i, shortcut) in shortcuts.withIndex()) {
val isPinned by remember(shortcut) { viewModel.isChildPinned(shortcut) }.collectAsState(
false
val toolbarActions = mutableListOf<ToolbarAction>()
if (LocalFavoritesEnabled.current) {
val isPinned by viewModel.isPinned.collectAsState(false)
val favAction = if (isPinned) {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_unpin),
icon = Icons.Rounded.Star,
action = {
viewModel.unpin()
}
)
} else {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_pin),
icon = Icons.Rounded.StarOutline,
action = {
viewModel.pin()
})
}
toolbarActions.add(favAction)
}
if (!app.isPrivate) {
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_app_info),
icon = Icons.Rounded.Info
) {
app.openAppDetails(context)
})
}
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_launch),
icon = Icons.AutoMirrored.Rounded.OpenInNew,
action = {
viewModel.launch(context)
}
)
)
val iconSizePx = 32.dp.toPixels()
val icon by
remember {
viewModel.getChildIcon(
shortcut,
iconSizePx.toInt()
)
}.collectAsState(null)
if (i > 0) {
HorizontalDivider()
val sheetManager = LocalBottomSheetManager.current
if (!app.isPrivate) {
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_customize),
icon = Icons.Rounded.Tune,
action = { sheetManager.showCustomizeSearchableModal(app) }
))
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable {
viewModel.launchChild(context, shortcut)
}
.padding(vertical = 4.dp)
) {
ShapedLauncherIcon(
size = 32.dp,
icon = { icon },
shape = CircleShape,
modifier = Modifier
.padding(horizontal = 12.dp)
.size(32.dp),
)
Text(
shortcut.labelOverride ?: shortcut.label,
modifier = Modifier.weight(1f),
style = MaterialTheme.typography.titleSmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
IconButton(
onClick = {
if (isPinned) {
viewModel.unpinChild(shortcut)
} else {
viewModel.pinChild(shortcut)
if (!app.isPrivate) {
val storeDetails = remember(app) { app.getStoreDetails(context) }
val shareAction = if (storeDetails == null) {
DefaultToolbarAction(
label = stringResource(R.string.menu_share),
icon = Icons.Rounded.Share
) {
scope.launch {
app.shareApkFile(context)
}
}
) {
Icon(
if (isPinned) Icons.Rounded.Star else Icons.Rounded.StarOutline,
stringResource(if (isPinned) R.string.menu_favorites_unpin else R.string.menu_favorites_pin),
} else {
SubmenuToolbarAction(
label = stringResource(R.string.menu_share),
icon = Icons.Rounded.Share,
children = listOf(
DefaultToolbarAction(
label = stringResource(
R.string.menu_share_store_link,
storeDetails.label
),
icon = Icons.Rounded.Link,
action = {
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.putExtra(
Intent.EXTRA_TEXT,
storeDetails.url
)
shareIntent.type = "text/plain"
context.startActivity(
Intent.createChooser(
shareIntent,
null
)
)
}
),
DefaultToolbarAction(
label = stringResource(R.string.menu_share_apk_file),
icon = Icons.Rounded.Android
) {
scope.launch {
app.shareApkFile(context)
}
}
)
)
}
toolbarActions.add(shareAction)
}
}
}
}
val toolbarActions = mutableListOf<ToolbarAction>()
if (LocalFavoritesEnabled.current) {
val isPinned by viewModel.isPinned.collectAsState(false)
val favAction = if (isPinned) {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_unpin),
icon = Icons.Rounded.Star,
action = {
viewModel.unpin()
if (app.canUninstall) {
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_uninstall),
icon = Icons.Rounded.Delete,
) {
app.uninstall(context)
onBack()
}
)
}
)
} else {
DefaultToolbarAction(
label = stringResource(R.string.menu_favorites_pin),
icon = Icons.Rounded.StarOutline,
action = {
viewModel.pin()
})
}
toolbarActions.add(favAction)
}
if (!app.isPrivate) {
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_app_info),
icon = Icons.Rounded.Info
) {
app.openAppDetails(context)
})
}
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_launch),
icon = Icons.AutoMirrored.Rounded.OpenInNew,
action = {
viewModel.launch(context)
}
)
)
val sheetManager = LocalBottomSheetManager.current
if (!app.isPrivate) {
toolbarActions.add(DefaultToolbarAction(
label = stringResource(R.string.menu_customize),
icon = Icons.Rounded.Tune,
action = { sheetManager.showCustomizeSearchableModal(app) }
))
}
if (!app.isPrivate) {
val storeDetails = remember(app) { app.getStoreDetails(context) }
val shareAction = if (storeDetails == null) {
DefaultToolbarAction(
label = stringResource(R.string.menu_share),
icon = Icons.Rounded.Share
) {
scope.launch {
app.shareApkFile(context)
}
}
} else {
SubmenuToolbarAction(
label = stringResource(R.string.menu_share),
icon = Icons.Rounded.Share,
children = listOf(
DefaultToolbarAction(
label = stringResource(
R.string.menu_share_store_link,
storeDetails.label
),
icon = Icons.Rounded.Link,
action = {
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.putExtra(Intent.EXTRA_TEXT, storeDetails.url)
shareIntent.type = "text/plain"
context.startActivity(Intent.createChooser(shareIntent, null))
Toolbar(
leftActions = listOf(
DefaultToolbarAction(
label = stringResource(id = R.string.menu_back),
icon = Icons.AutoMirrored.Rounded.ArrowBack
) {
onBack()
}
),
DefaultToolbarAction(
label = stringResource(R.string.menu_share_apk_file),
icon = Icons.Rounded.Android
) {
scope.launch {
app.shareApkFile(context)
}
}
rightActions = toolbarActions
)
)
}
} else {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
if (LocalGridSettings.current.showListIcons) {
ShapedLauncherIcon(
size = LocalGridSettings.current.iconSize.dp,
modifier = Modifier
.padding(end = 16.dp),
badge = { badge },
icon = { icon },
)
}
Text(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = app.labelOverride ?: app.label,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.sharedBounds(
rememberSharedContentState("label"),
this@AnimatedContent,
),
)
}
}
toolbarActions.add(shareAction)
}
if (app.canUninstall) {
toolbarActions.add(
DefaultToolbarAction(
label = stringResource(R.string.menu_uninstall),
icon = Icons.Rounded.Delete,
) {
app.uninstall(context)
onBack()
}
)
}
Toolbar(
leftActions = listOf(
DefaultToolbarAction(
label = stringResource(id = R.string.menu_back),
icon = Icons.AutoMirrored.Rounded.ArrowBack
) {
onBack()
}
),
rightActions = toolbarActions
)
}
}
@ -507,6 +565,7 @@ fun AppItemGridPopup(
y = lerp(-16.dp, 0.dp, animationProgress)
),
app = app,
showDetails = true,
onBack = onDismiss
)
}

View File

@ -4,7 +4,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@ -22,9 +22,9 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.LeadingIconTab
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.PrimaryScrollableTabRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@ -36,6 +36,8 @@ import de.mm20.launcher2.search.Application
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.launcher.search.common.grid.GridItem
import de.mm20.launcher2.ui.launcher.search.common.grid.GridResults
import de.mm20.launcher2.ui.launcher.search.common.list.ListItem
import de.mm20.launcher2.ui.launcher.search.common.list.ListResults
import de.mm20.launcher2.ui.layout.BottomReversed
import de.mm20.launcher2.ui.locals.LocalGridSettings
@ -47,158 +49,192 @@ fun LazyListScope.AppResults(
isProfileLocked: Boolean = false,
onProfileLockChange: ((Profile, Boolean) -> Unit)? = null,
apps: List<Application>,
selectedIndex: Int,
onSelect: (Int) -> Unit,
highlightedItem: Application? = null,
columns: Int,
reverse: Boolean,
showList: Boolean,
) {
GridResults(
key = "apps",
items = apps.filter { it.user == profiles[selectedProfileIndex].userHandle },
before = if (profiles.size > 1) {
{
Column(
verticalArrangement = if (reverse) Arrangement.BottomReversed else Arrangement.Top,
val before = if (profiles.size > 1) {
@Composable {
Column(
verticalArrangement = if (reverse) Arrangement.BottomReversed else Arrangement.Top,
) {
PrimaryScrollableTabRow(
selectedTabIndex = selectedProfileIndex,
containerColor = Color.Transparent,
edgePadding = 16.dp,
divider = {}
) {
PrimaryScrollableTabRow(
selectedTabIndex = selectedProfileIndex,
containerColor = Color.Transparent,
edgePadding = 16.dp,
divider = {}
) {
for ((i, profile) in profiles.withIndex()) {
LeadingIconTab(
selected = selectedProfileIndex == profiles.indexOf(profile),
text = {
Text(
when (profile.type) {
Profile.Type.Personal -> stringResource(R.string.apps_profile_main)
Profile.Type.Work -> stringResource(R.string.apps_profile_work)
Profile.Type.Private -> stringResource(R.string.apps_profile_private)
}
)
},
icon = {
for ((i, profile) in profiles.withIndex()) {
LeadingIconTab(
selected = selectedProfileIndex == profiles.indexOf(profile),
text = {
Text(
when (profile.type) {
Profile.Type.Personal -> Icon(
Icons.Rounded.Person,
contentDescription = null
)
Profile.Type.Work -> Icon(
Icons.Rounded.Work,
contentDescription = null
)
Profile.Type.Private -> Icon(
Icons.Rounded.PrivateSpace,
contentDescription = null
)
Profile.Type.Personal -> stringResource(R.string.apps_profile_main)
Profile.Type.Work -> stringResource(R.string.apps_profile_work)
Profile.Type.Private -> stringResource(R.string.apps_profile_private)
}
},
onClick = {
onProfileSelected(i)
)
},
icon = {
when (profile.type) {
Profile.Type.Personal -> Icon(
Icons.Rounded.Person,
contentDescription = null
)
Profile.Type.Work -> Icon(
Icons.Rounded.Work,
contentDescription = null
)
Profile.Type.Private -> Icon(
Icons.Rounded.PrivateSpace,
contentDescription = null
)
}
)
}
},
onClick = {
onProfileSelected(i)
}
)
}
HorizontalDivider()
}
val profileType = profiles[selectedProfileIndex].type
if (profileType != Profile.Type.Personal) {
if (isProfileLocked) {
Column(
modifier = Modifier
.padding(12.dp)
.fillMaxWidth()
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, MaterialTheme.shapes.small)
.background(MaterialTheme.colorScheme.surfaceContainer, MaterialTheme.shapes.small)
.padding(vertical = 64.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
if (profileType == Profile.Type.Work) Icons.Rounded.WorkOff else Icons.Rounded.Lock,
contentDescription = null,
modifier = Modifier.size(48.dp),
tint = MaterialTheme.colorScheme.secondary,
if (!showList || isProfileLocked) {
HorizontalDivider()
}
val profileType = profiles[selectedProfileIndex].type
if (profileType != Profile.Type.Personal) {
if (isProfileLocked) {
Column(
modifier = Modifier
.padding(12.dp)
.fillMaxWidth()
.border(
1.dp,
MaterialTheme.colorScheme.outlineVariant,
MaterialTheme.shapes.small
)
Text(
stringResource(
if (profileType == Profile.Type.Work) R.string.profile_work_profile_state_locked
else R.string.profile_private_profile_state_locked
),
modifier = Modifier.padding(top = 8.dp),
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.titleSmall,
.background(
MaterialTheme.colorScheme.surfaceContainer,
MaterialTheme.shapes.small
)
if (showProfileLockControls) {
Button(
modifier = Modifier.padding(top = 32.dp),
onClick = {
onProfileLockChange?.invoke(
profiles[selectedProfileIndex],
false
)
},
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
) {
Icon(
if (profileType == Profile.Type.Work) Icons.Rounded.Work else Icons.Rounded.LockOpen,
contentDescription = null,
modifier = Modifier
.padding(end = ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
.padding(vertical = 64.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
if (profileType == Profile.Type.Work) Icons.Rounded.WorkOff else Icons.Rounded.Lock,
contentDescription = null,
modifier = Modifier.size(48.dp),
tint = MaterialTheme.colorScheme.secondary,
)
Text(
stringResource(
if (profileType == Profile.Type.Work) R.string.profile_work_profile_state_locked
else R.string.profile_private_profile_state_locked
),
modifier = Modifier.padding(top = 8.dp),
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.titleSmall,
)
if (showProfileLockControls) {
Button(
modifier = Modifier.padding(top = 32.dp),
onClick = {
onProfileLockChange?.invoke(
profiles[selectedProfileIndex],
false
)
Text(
stringResource(
if (profileType == Profile.Type.Work) R.string.profile_work_profile_action_unlock
else R.string.profile_private_profile_action_unlock
)
},
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
) {
Icon(
if (profileType == Profile.Type.Work) Icons.Rounded.Work else Icons.Rounded.LockOpen,
contentDescription = null,
modifier = Modifier
.padding(end = ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
)
Text(
stringResource(
if (profileType == Profile.Type.Work) R.string.profile_work_profile_action_unlock
else R.string.profile_private_profile_action_unlock
)
}
)
}
}
} else if (showProfileLockControls) {
FilledTonalButton(
}
} else if (showProfileLockControls) {
FilledTonalButton(
modifier = Modifier
.padding(12.dp)
.fillMaxWidth(),
onClick = {
onProfileLockChange?.invoke(
profiles[selectedProfileIndex],
true
)
},
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
) {
Icon(
if (profileType == Profile.Type.Work) Icons.Rounded.WorkOff else Icons.Rounded.Lock,
contentDescription = null,
modifier = Modifier
.padding(12.dp)
.fillMaxWidth(),
onClick = {
onProfileLockChange?.invoke(
profiles[selectedProfileIndex],
true
)
},
contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
) {
Icon(
if (profileType == Profile.Type.Work) Icons.Rounded.WorkOff else Icons.Rounded.Lock,
contentDescription = null,
modifier = Modifier
.padding(end = ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
.padding(end = ButtonDefaults.IconSpacing)
.size(ButtonDefaults.IconSize)
)
Text(
stringResource(
if (profileType == Profile.Type.Work) R.string.profile_work_profile_action_lock
else R.string.profile_private_profile_action_lock
)
Text(
stringResource(
if (profileType == Profile.Type.Work) R.string.profile_work_profile_action_lock
else R.string.profile_private_profile_action_lock
)
)
}
)
}
}
}
}
} else null,
itemContent = {
GridItem(
item = it,
showLabels = LocalGridSettings.current.showLabels,
highlight = it.key == highlightedItem?.key
)
},
reverse = reverse,
columns = columns,
)
}
} else null
if (showList) {
ListResults(
key = "apps",
items = apps.filter { it.user == profiles[selectedProfileIndex].userHandle },
before = before?.let { { it() } },
selectedIndex = selectedIndex,
itemContent = { app, showDetails, index ->
ListItem(
modifier = Modifier
.fillMaxWidth(),
item = app,
showDetails = showDetails,
onShowDetails = { onSelect(if(it) index else -1) },
highlight = highlightedItem?.key == app.key
)
},
reverse = reverse,
)
} else {
GridResults(
key = "apps",
items = apps.filter { it.user == profiles[selectedProfileIndex].userHandle },
before = before,
itemContent = {
GridItem(
item = it,
showLabels = LocalGridSettings.current.showLabels,
highlight = it.key == highlightedItem?.key
)
},
reverse = reverse,
columns = columns,
)
}
}

View File

@ -16,6 +16,7 @@ import de.mm20.launcher2.notifications.Notification
import de.mm20.launcher2.notifications.NotificationRepository
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.search.ContactSearchSettings
import de.mm20.launcher2.preferences.search.LocationSearchSettings
import de.mm20.launcher2.search.AppShortcut
import de.mm20.launcher2.search.Application
@ -53,6 +54,7 @@ class SearchableItemVM : ListItemViewModel(), KoinComponent {
private val appShortcutRepository: AppShortcutRepository by inject()
private val permissionsManager: PermissionsManager by inject()
private val locationSearchSettings: LocationSearchSettings by inject()
private val contactSearchSettings: ContactSearchSettings by inject()
val isUpToDate = MutableStateFlow(true)
@ -138,7 +140,7 @@ class SearchableItemVM : ListItemViewModel(), KoinComponent {
}
val bundle = options.toBundle()
if (searchable.launch(context, bundle)) {
favoritesService.reportLaunch(searchable)
reportUsage(searchable)
return true
} else if (searchable is Application || searchable is AppShortcut) {
favoritesService.reset(searchable)
@ -168,7 +170,7 @@ class SearchableItemVM : ListItemViewModel(), KoinComponent {
fun launchChild(context: Context, child: SavableSearchable) {
if (child.launch(context, null)) {
favoritesService.reportLaunch(child)
reportUsage(child)
}
}
@ -246,4 +248,11 @@ class SearchableItemVM : ListItemViewModel(), KoinComponent {
val mapTileServerUrl = locationSearchSettings.tileServer
.map { it ?: LocationSearchSettings.DefaultTileServerUrl }
.stateIn(viewModelScope, SharingStarted.Lazily, "")
val callOnTap = contactSearchSettings.callOnTap
.stateIn(viewModelScope, SharingStarted.Lazily, false)
fun reportUsage(searchable: SavableSearchable) {
favoritesService.reportLaunch(searchable)
}
}

View File

@ -1,6 +1,5 @@
package de.mm20.launcher2.ui.launcher.search.common.grid
import android.util.Log
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.MutableTransitionState
@ -13,6 +12,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
@ -110,6 +110,7 @@ fun GridItem(
Column(
modifier = modifier
.padding(4.dp)
.combinedClickable(
onClick = {
if (!launchOnPress || !viewModel.launch(context, bounds)) {
@ -170,7 +171,9 @@ fun GridItem(
modifier = Modifier
.padding(4.dp)
.onGloballyPositioned {
bounds = it.boundsInWindow().roundToIntRect()
bounds = it
.boundsInWindow()
.roundToIntRect()
} then
if (highlight) Modifier.background(
MaterialTheme.colorScheme.surface,
@ -195,10 +198,10 @@ fun GridItem(
color = MaterialTheme.colorScheme.onBackground,
)
}
}
if (showPopup) {
ItemPopup(origin = bounds, searchable = item, onDismissRequest = { showPopup = false })
}
if (showPopup) {
ItemPopup(origin = bounds, searchable = item, onDismissRequest = { showPopup = false })
}
}
@ -398,7 +401,7 @@ private fun Modifier.placeOverlay(
constraints.maxHeight - placeable.height,
),
animationProgress.pow(2)
).toInt()
)
)
}
}
@ -410,4 +413,4 @@ private fun lerp(start: Float, stop: Float, fraction: Float): Float {
private fun lerp(start: Int, stop: Int, fraction: Float): Int {
return start + (fraction * (stop - start)).toInt()
}
}

View File

@ -83,8 +83,8 @@ fun <T : SavableSearchable> LazyListScope.GridResults(
.padding(
top = if (it == 0) 8.dp else 0.dp,
bottom = if (it == rows - 1) 8.dp else 0.dp,
start = 4.dp,
end = 4.dp,
start = if (columns == 1) 0.dp else 4.dp,
end = if (columns == 1) 0.dp else 4.dp,
)
) {
Row {
@ -94,7 +94,6 @@ fun <T : SavableSearchable> LazyListScope.GridResults(
Box(
modifier = Modifier
.weight(1f)
.padding(4.dp)
) {
itemContent(item)
}

View File

@ -5,6 +5,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@ -22,6 +23,7 @@ import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.roundToIntRect
import de.mm20.launcher2.search.AppShortcut
import de.mm20.launcher2.search.Application
import de.mm20.launcher2.search.Article
import de.mm20.launcher2.search.CalendarEvent
import de.mm20.launcher2.search.Contact
@ -30,6 +32,7 @@ import de.mm20.launcher2.search.Location
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.Website
import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.launcher.search.apps.AppItem
import de.mm20.launcher2.ui.launcher.search.calendar.CalendarItem
import de.mm20.launcher2.ui.launcher.search.common.SearchableItemVM
import de.mm20.launcher2.ui.launcher.search.contacts.ContactItem
@ -80,6 +83,25 @@ fun ListItem(
LocalContentColor provides MaterialTheme.colorScheme.onSurface
) {
when (item) {
is Application -> {
AppItem(
modifier = Modifier
.fillMaxWidth()
.heightIn(max = 9999.dp) // we have infinite space, but there is an inner scroll that needs a constraint
.combinedClickable(
enabled = !showDetails,
onClick = {
if (!viewModel.launch(context, bounds)) {
onShowDetails(true)
}
},
onLongClick = { onShowDetails(true) }
),
app = item,
showDetails = showDetails,
onBack = { onShowDetails(false) }
)
}
is Contact -> {
ContactItem(
modifier = Modifier

View File

@ -83,6 +83,8 @@ import de.mm20.launcher2.ui.modifier.scale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import androidx.core.net.toUri
import de.mm20.launcher2.ktx.checkPermission
@Composable
fun ContactItem(
@ -101,6 +103,7 @@ fun ContactItem(
}
val icon by viewModel.icon.collectAsStateWithLifecycle()
val callOnTap by viewModel.callOnTap.collectAsStateWithLifecycle(false)
val badge by viewModel.badge.collectAsState(null)
SharedTransitionLayout {
@ -163,6 +166,7 @@ fun ContactItem(
.fillMaxWidth(),
secondaryAction = {
IconButton(onClick = {
viewModel.reportUsage(contact)
context.tryStartActivity(
Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("smsto:${it.number}")
@ -180,10 +184,14 @@ fun ContactItem(
expandedSection = if (it) 0 else -1
},
onContact = {
viewModel.reportUsage(contact)
context.tryStartActivity(
Intent(Intent.ACTION_DIAL).apply {
data = Uri.parse("tel:${it.number}")
}
Intent(
if (callOnTap)
Intent.ACTION_CALL
else
Intent.ACTION_DIAL
).setData("tel:${it.number}".toUri())
)
},
copyText = { it.number },
@ -208,6 +216,7 @@ fun ContactItem(
expandedSection = if (it) 1 else -1
},
onContact = {
viewModel.reportUsage(contact)
context.tryStartActivity(
Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("mailto:${it.address}")
@ -231,6 +240,7 @@ fun ContactItem(
secondaryAction = if (canNavigate) {
{
IconButton(onClick = {
viewModel.reportUsage(contact)
context.tryStartActivity(
Intent(Intent.ACTION_VIEW).apply {
data =
@ -254,6 +264,7 @@ fun ContactItem(
expandedSection = if (it) 2 else -1
},
onContact = {
viewModel.reportUsage(contact)
context.tryStartActivity(
Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("geo:0,0?q=${it.address}")
@ -295,11 +306,22 @@ fun ContactItem(
app.key
}
}
val itemsWithPermission = remember(app) {
app.value.filter {
// exclude activities we have no permission for
val resolvedActivityInfo = context.packageManager.resolveActivity(
Intent(Intent.ACTION_VIEW).setDataAndType(it.uri, it.mimeType),
0
)?.activityInfo ?: return@filter false
resolvedActivityInfo.permission == null || context.checkPermission(resolvedActivityInfo.permission)
}
}
ContactInfo(
icon = Icons.AutoMirrored.Rounded.OpenInNew,
customIcon = appIcon,
label = label,
items = app.value,
items = itemsWithPermission,
itemLabel = { it.label },
expanded = expandedSection == 3 + i,
modifier = Modifier
@ -309,6 +331,7 @@ fun ContactItem(
expandedSection = if (it) 3 + i else -1
},
onContact = {
viewModel.reportUsage(contact)
context.tryStartActivity(
Intent(Intent.ACTION_VIEW).apply {
setDataAndType(

View File

@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@ -38,7 +37,9 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -48,6 +49,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
@ -75,6 +77,7 @@ import de.mm20.launcher2.ui.ktx.contrast
import de.mm20.launcher2.ui.ktx.hue
import de.mm20.launcher2.ui.ktx.hueRotate
import de.mm20.launcher2.ui.ktx.invert
import de.mm20.launcher2.ui.ktx.toDp
import de.mm20.launcher2.ui.locals.LocalDarkTheme
import org.koin.android.ext.koin.androidContext
import org.koin.core.component.KoinComponent
@ -166,18 +169,19 @@ fun MapTiles(
fadeOut() + scaleOut(targetScale = scale)
}
) { (start, stop, zoom) ->
val sideLength = stop.x - start.x + 1
Column(modifier = Modifier.fillMaxWidth()) {
var tileWidth by remember { mutableIntStateOf(0) }
Column(modifier = Modifier
.fillMaxWidth()
// Needed to force all tiles to be the _exact_ same size. With weight(1f) we get rounding errors and gaps.
.onSizeChanged { tileWidth = it.width / (stop.x - start.x + 1) }
) {
for (y in start.y..stop.y) {
Row(
modifier = Modifier
.fillMaxWidth()
) {
Row(modifier = Modifier.fillMaxWidth()) {
for (x in start.x..stop.x) {
AsyncImage(
modifier = Modifier
.weight(1f / sideLength)
.aspectRatio(1f)
.width(tileWidth.toDp())
.height(tileWidth.toDp())
.background(MaterialTheme.colorScheme.secondaryContainer),
imageLoader = MapTileLoader.loader,
model = MapTileLoader.getTileRequest(tileServerUrl, x, y, zoom),

View File

@ -31,6 +31,7 @@ import androidx.compose.material.icons.rounded.Height
import androidx.compose.material.icons.rounded.HorizontalSplit
import androidx.compose.material.icons.rounded.LightMode
import androidx.compose.material.icons.rounded.MusicNote
import androidx.compose.material.icons.rounded.Timer
import androidx.compose.material.icons.rounded.Today
import androidx.compose.material.icons.rounded.Tune
import androidx.compose.material.icons.rounded.VerticalSplit
@ -67,6 +68,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.preferences.ClockWidgetAlignment
import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.base.LocalTime
@ -83,6 +85,7 @@ import de.mm20.launcher2.ui.launcher.widgets.clock.clocks.SegmentClock
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.PartProvider
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreenVM
import de.mm20.launcher2.ui.utils.isTwentyFourHours
import org.koin.androidx.compose.inject
@Composable
@ -164,7 +167,8 @@ fun ClockWidget(
Box(
modifier = Modifier
.then(if (fillScreenHeight) Modifier.weight(1f) else Modifier)
.fillMaxWidth().padding(horizontal = if (compact == true) 0.dp else 24.dp),
.fillMaxWidth()
.padding(horizontal = if (compact == true) 0.dp else 24.dp),
contentAlignment = when (alignment) {
ClockWidgetAlignment.Center -> Alignment.Center
ClockWidgetAlignment.Top -> Alignment.TopCenter
@ -265,34 +269,42 @@ fun Clock(
darkColors: Boolean = false
) {
val time = LocalTime.current
val context = LocalContext.current
val clockSettings: ClockWidgetSettings by inject()
val showSeconds by clockSettings.showSeconds.collectAsState(initial = false)
val useThemeColor by clockSettings.useThemeColor.collectAsState(initial = false)
val timeFormat by clockSettings.timeFormat.collectAsState(null)
if (timeFormat == null) return
val isTwentyFourHours = timeFormat!!.isTwentyFourHours(context)
when (style) {
is ClockWidgetStyle.Digital1 -> DigitalClock1(
time,
style,
compact,
showSeconds,
useThemeColor,
darkColors
time = time,
compact = compact,
showSeconds = showSeconds,
twentyFourHours = isTwentyFourHours,
useThemeColor = useThemeColor,
darkColors = darkColors,
)
is ClockWidgetStyle.Digital2 -> DigitalClock2(
time,
compact,
showSeconds,
useThemeColor,
darkColors
time = time,
compact = compact,
showSeconds = showSeconds,
twentyFourHours = isTwentyFourHours,
useThemeColor = useThemeColor,
darkColors = darkColors,
)
is ClockWidgetStyle.Binary -> BinaryClock(
time,
compact,
showSeconds,
useThemeColor,
darkColors
time = time,
compact = compact,
showSeconds = showSeconds,
twentyFourHours = isTwentyFourHours,
useThemeColor = useThemeColor,
darkColors = darkColors,
)
is ClockWidgetStyle.Analog -> AnalogClock(
@ -304,19 +316,21 @@ fun Clock(
)
is ClockWidgetStyle.Orbit -> OrbitClock(
time,
compact,
showSeconds,
useThemeColor,
darkColors
time = time,
compact = compact,
showSeconds = showSeconds,
twentyFourHours = isTwentyFourHours,
useThemeColor = useThemeColor,
darkColors = darkColors,
)
is ClockWidgetStyle.Segment -> SegmentClock(
time,
compact,
showSeconds,
useThemeColor,
darkColors
time = time,
compact = compact,
showSeconds = showSeconds,
twentyFourHours = isTwentyFourHours,
useThemeColor = useThemeColor,
darkColors = darkColors,
)
is ClockWidgetStyle.Custom -> CustomClock(style, compact, useThemeColor, darkColors)
@ -349,6 +363,7 @@ fun ConfigureClockWidgetSheet(
val fillHeight by viewModel.fillHeight.collectAsState()
val alignment by viewModel.alignment.collectAsState()
val showSeconds by viewModel.showSeconds.collectAsState()
val timeFormat by viewModel.timeFormat.collectAsState()
val useAccentColor by viewModel.useThemeColor.collectAsState()
val parts by viewModel.parts.collectAsState()
@ -488,13 +503,63 @@ fun ConfigureClockWidgetSheet(
AnimatedVisibility(compact == false && style !is ClockWidgetStyle.Custom) {
SwitchPreference(
title = stringResource(R.string.preference_clock_widget_show_seconds),
icon = Icons.Rounded.AccessTime,
icon = Icons.Rounded.Timer,
value = showSeconds,
onValueChanged = {
viewModel.setShowSeconds(it)
}
)
}
AnimatedVisibility(
style !is ClockWidgetStyle.Analog &&
style !is ClockWidgetStyle.Custom &&
style !is ClockWidgetStyle.Empty
) {
var showDropdown by remember { mutableStateOf(false) }
Preference(
title = stringResource(R.string.preference_clock_widget_time_format),
summary = when (timeFormat) {
TimeFormat.TwelveHour -> stringResource(R.string.preference_clock_widget_time_format_12h)
TimeFormat.TwentyFourHour -> stringResource(R.string.preference_clock_widget_time_format_24h)
TimeFormat.System -> stringResource(R.string.preference_clock_widget_time_format_system)
},
icon = Icons.Rounded.AccessTime,
onClick = {
showDropdown = true
}
)
DropdownMenu(
expanded = showDropdown,
onDismissRequest = { showDropdown = false }) {
DropdownMenuItem(
text = {
Text(stringResource(R.string.preference_clock_widget_time_format_system))
},
onClick = {
viewModel.setTimeFormat(TimeFormat.System)
showDropdown = false
}
)
DropdownMenuItem(
text = {
Text(stringResource(R.string.preference_clock_widget_time_format_24h))
},
onClick = {
viewModel.setTimeFormat(TimeFormat.TwentyFourHour)
showDropdown = false
}
)
DropdownMenuItem(
text = {
Text(stringResource(R.string.preference_clock_widget_time_format_12h))
},
onClick = {
viewModel.setTimeFormat(TimeFormat.TwelveHour)
showDropdown = false
}
)
}
}
}
}
OutlinedCard(

View File

@ -13,14 +13,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.ui.locals.LocalDarkTheme
import de.mm20.launcher2.ui.utils.isTwentyFourHours
import java.util.Calendar
@Composable
fun BinaryClock(
time: Long,
compact: Boolean,
twentyFourHours: Boolean,
showSeconds: Boolean,
useThemeColor: Boolean,
darkColors: Boolean,
@ -30,8 +35,8 @@ fun BinaryClock(
date.timeInMillis = time
val second = date[Calendar.SECOND]
val minute = date[Calendar.MINUTE]
var hour = date[Calendar.HOUR]
if (hour == 0) hour = 12
var hour = date[if(!twentyFourHours) Calendar.HOUR else Calendar.HOUR_OF_DAY]
if (!twentyFourHours && hour == 0) hour = 12
val color = if (useThemeColor) {
if (!darkColors) {
@ -56,11 +61,11 @@ fun BinaryClock(
Row(
modifier = Modifier.padding(start = 0.dp, top = 24.dp, end = 0.dp, bottom = 6.dp)
) {
for (i in 0 until 10) {
val active = if (i < 4) {
hour and (1 shl (3 - i)) != 0
for (i in 0 until if (twentyFourHours) 11 else 10) {
val active = if (i < if (twentyFourHours) 5 else 4) {
hour and (1 shl ((if (twentyFourHours) 4 else 3) - i)) != 0
} else {
minute and (1 shl (9 - i)) != 0
minute and (1 shl ((if (twentyFourHours) 10 else 9) - i)) != 0
}
Box(
modifier = Modifier
@ -70,7 +75,7 @@ fun BinaryClock(
if (active) color else disabledColor
)
)
if (i == 3) {
if (i == if (twentyFourHours) 4 else 3) {
Box(Modifier.size(8.dp))
}
}
@ -98,8 +103,8 @@ fun BinaryClock(
horizontalAlignment = Alignment.End
) {
Row {
for (i in 0 until 4) {
val active = hour and (1 shl (3 - i)) != 0
for (i in 0 until if (twentyFourHours) 5 else 4) {
val active = hour and (1 shl ((if (twentyFourHours) 4 else 3) - i)) != 0
Box(
modifier = Modifier
.padding( 4.dp)

View File

@ -33,6 +33,7 @@ fun DigitalClock1(
time: Long,
style: ClockWidgetStyle.Digital1 = ClockWidgetStyle.Digital1(),
compact: Boolean,
twentyFourHours: Boolean,
showSeconds: Boolean,
useThemeColor: Boolean,
darkColors: Boolean,
@ -40,10 +41,10 @@ fun DigitalClock1(
val verticalLayout = !compact
val format = SimpleDateFormat(
when {
DateFormat.is24HourFormat(LocalContext.current) && verticalLayout -> {
twentyFourHours && verticalLayout -> {
"HH\nmm"
}
DateFormat.is24HourFormat(LocalContext.current) -> {
twentyFourHours -> {
"HH mm"
}
verticalLayout -> {

View File

@ -21,6 +21,7 @@ fun DigitalClock2(
time: Long,
compact: Boolean,
showSeconds: Boolean,
twentyFourHours: Boolean,
useThemeColor: Boolean,
darkColors: Boolean,
) {
@ -40,14 +41,14 @@ fun DigitalClock2(
}
val formatString = if (verticalLayout && showSeconds) {
if (DateFormat.is24HourFormat(LocalContext.current)) {
if (twentyFourHours) {
"HH:mm:ss"
}
else {
"hh:mm:ss"
}
} else {
if (DateFormat.is24HourFormat(LocalContext.current)) {
if (twentyFourHours) {
"HH:mm"
}
else {

View File

@ -48,6 +48,7 @@ fun OrbitClock(
time: Long,
compact: Boolean,
showSeconds: Boolean,
twentyFourHours: Boolean,
useThemeColor: Boolean,
darkColors: Boolean,
) {
@ -59,7 +60,7 @@ fun OrbitClock(
val minute = parsed.minute
val hour = parsed.hour
val formattedHour = (
if (DateFormat.is24HourFormat(LocalContext.current))
if (twentyFourHours)
hour
else {
((hour + 11) % 12) + 1

View File

@ -52,11 +52,12 @@ fun SegmentClock(
time: Long,
compact: Boolean,
showSeconds: Boolean,
twentyFourHours: Boolean,
useThemeColor: Boolean,
darkColors: Boolean,
) {
val parsed = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault())
val hour = if (DateFormat.is24HourFormat(LocalContext.current)) parsed.hour else (((parsed.hour + 11) % 12) + 1)
val hour = if (twentyFourHours) parsed.hour else (((parsed.hour + 11) % 12) + 1)
val minute = parsed.minute
val second = parsed.second

View File

@ -38,10 +38,12 @@ import de.mm20.launcher2.ui.settings.about.AboutSettingsScreen
import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen
import de.mm20.launcher2.ui.settings.backup.BackupSettingsScreen
import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
import de.mm20.launcher2.ui.settings.calendarsearch.CalendarProviderSettingsScreen
import de.mm20.launcher2.ui.settings.calendarsearch.CalendarSearchSettingsScreen
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen
import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen
import de.mm20.launcher2.ui.settings.contacts.ContactsSettingsScreen
import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen
import de.mm20.launcher2.ui.settings.crashreporter.CrashReporterScreen
import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen
@ -91,16 +93,18 @@ class SettingsActivity : BaseActivity() {
val navController = rememberNavController()
LaunchedEffect(route) {
try {
navController.navigate(route ?: "settings") {
popUpTo("settings") {
inclusive = true
if (route != null) {
try {
navController.navigate(route ?: "settings") {
popUpTo("settings") {
inclusive = true
}
}
}
} catch (e: IllegalArgumentException) {
navController.navigate("settings") {
popUpTo("settings") {
inclusive = true
} catch (e: IllegalArgumentException) {
navController.navigate("settings") {
popUpTo("settings") {
inclusive = true
}
}
}
}
@ -198,6 +202,11 @@ class SettingsActivity : BaseActivity() {
composable("settings/search/calendar") {
CalendarSearchSettingsScreen()
}
composable("settings/search/calendar/{providerId}") {
CalendarProviderSettingsScreen(
it.arguments?.getString("providerId") ?: return@composable
)
}
composable("settings/search/searchactions") {
SearchActionsSettingsScreen()
}
@ -219,6 +228,9 @@ class SettingsActivity : BaseActivity() {
composable("settings/favorites") {
FavoritesSettingsScreen()
}
composable("settings/search/contacts") {
ContactsSettingsScreen()
}
composable("settings/integrations") {
IntegrationsSettingsScreen()
}

View File

@ -0,0 +1,97 @@
package de.mm20.launcher2.ui.settings.calendarsearch
import android.app.PendingIntent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ErrorOutline
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.calendar.providers.CalendarList
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.ktx.sendWithBackgroundPermission
import de.mm20.launcher2.plugin.PluginState
import de.mm20.launcher2.themes.atTone
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.Banner
import de.mm20.launcher2.ui.component.preferences.CheckboxPreference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
import de.mm20.launcher2.ui.locals.LocalDarkTheme
@Composable
fun CalendarProviderSettingsScreen(providerId: String) {
val viewModel = viewModel<CalendarProviderSettingsScreenVM>()
LaunchedEffect(providerId) {
viewModel.init(providerId)
}
val enabled by viewModel.isProviderEnabled.collectAsStateWithLifecycle(false)
val calendarLists by viewModel.calendarLists.collectAsStateWithLifecycle(sortedMapOf<String, List<CalendarList>>())
val excludedCalendars by viewModel.excludedCalendars.collectAsStateWithLifecycle(emptySet())
val pluginState by viewModel.pluginState.collectAsStateWithLifecycle(null)
val providerAvailable = providerId == "local" || pluginState != null
PreferenceScreen(
title = pluginState?.plugin?.label ?: stringResource(R.string.preference_search_calendar)
) {
if (!providerAvailable) {
return@PreferenceScreen
}
item {
PreferenceCategory {
SwitchPreference(
title =
if (providerId == "local") stringResource(R.string.preference_search_calendar)
else pluginState?.plugin?.label ?: "",
summary =
if (providerId == "local") stringResource(R.string.preference_search_local_calendar_summary)
else (pluginState?.state as? PluginState.Ready)?.text
?: pluginState?.plugin?.description,
value = enabled && (pluginState == null || pluginState?.state is PluginState.Ready),
onValueChanged = { viewModel.setProviderEnabled(providerId, it) }
)
}
}
items(calendarLists.toList()) { (k, v) ->
PreferenceCategory(
title = k,
) {
for (list in v) {
CheckboxPreference(
title = list.name,
value = !excludedCalendars.contains(list.id),
onValueChanged = { viewModel.setCalendarExcluded(list.id, !it) },
checkboxColors = CheckboxDefaults.colors(
checkedColor = if (list.color == 0) MaterialTheme.colorScheme.primary
else Color(
list.color.atTone(if (LocalDarkTheme.current) 80 else 40)
),
checkmarkColor = if (list.color == 0) MaterialTheme.colorScheme.onPrimary
else Color(
list.color.atTone(if (LocalDarkTheme.current) 20 else 100)
)
),
enabled = enabled,
)
}
}
}
}
}

View File

@ -0,0 +1,42 @@
package de.mm20.launcher2.ui.settings.calendarsearch
import androidx.lifecycle.ViewModel
import de.mm20.launcher2.calendar.CalendarRepository
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class CalendarProviderSettingsScreenVM: ViewModel(), KoinComponent {
private val providerId = MutableStateFlow<String>("")
fun init(providerId: String) {
this.providerId.value = providerId
}
private val calendarSearchSettings: CalendarSearchSettings by inject()
private val calendarRepository: CalendarRepository by inject()
private val pluginService: PluginService by inject()
val pluginState = providerId.flatMapLatest { pluginService.getPluginWithState(it) }
val isProviderEnabled = providerId.flatMapLatest { calendarSearchSettings.isProviderEnabled(it) }
fun setProviderEnabled(providerId: String, enabled: Boolean) {
calendarSearchSettings.setProviderEnabled(providerId, enabled)
}
val calendarLists = providerId
.flatMapLatest { calendarRepository.getCalendars(it) }
.map { it.groupBy { it.owner }.toSortedMap(compareBy { it }) }
val excludedCalendars = calendarSearchSettings.excludedCalendars
fun setCalendarExcluded(calendarId: String, excluded: Boolean) {
calendarSearchSettings.setCalendarExcluded(calendarId, excluded)
}
}

View File

@ -1,32 +1,21 @@
package de.mm20.launcher2.ui.settings.calendarsearch
import android.app.PendingIntent
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.CalendarToday
import androidx.compose.material.icons.rounded.Checklist
import androidx.compose.material.icons.rounded.ErrorOutline
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
@ -35,163 +24,83 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.ktx.sendWithBackgroundPermission
import de.mm20.launcher2.plugin.PluginState
import de.mm20.launcher2.search.calendar.CalendarListType
import de.mm20.launcher2.themes.atTone
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.Banner
import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.preferences.CheckboxPreference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
import de.mm20.launcher2.ui.locals.LocalDarkTheme
import de.mm20.launcher2.ui.locals.LocalNavController
@Composable
fun CalendarSearchSettingsScreen() {
val viewModel: CalendarSearchSettingsScreenVM = viewModel()
val context = LocalContext.current
val navController = LocalNavController.current
val hasCalendarPermission by viewModel.hasCalendarPermission.collectAsState(null)
val plugins by viewModel.availablePlugins.collectAsState(emptyList())
val plugins by viewModel.availablePlugins.collectAsStateWithLifecycle(emptyList(), minActiveState = Lifecycle.State.RESUMED)
val enabledProviders by viewModel.enabledProviders.collectAsState(emptySet())
val calendarLists by viewModel.calendarLists.collectAsStateWithLifecycle(
null,
minActiveState = Lifecycle.State.RESUMED
)
val excludedCalendars by viewModel.excludedCalendars.collectAsState(emptyList())
var showDialogForProvider by remember { mutableStateOf<String?>(null) }
PreferenceScreen(title = stringResource(R.string.preference_search_calendar)) {
item {
AnimatedVisibility(hasCalendarPermission == false) {
MissingPermissionBanner(
text = stringResource(R.string.missing_permission_calendar_search_settings),
onClick = {
viewModel.requestCalendarPermission(context as AppCompatActivity)
},
modifier = Modifier.padding(16.dp)
)
}
val selectedCalendars = remember(excludedCalendars, calendarLists) {
calendarLists?.count { it.providerId == "local" }
?.minus(excludedCalendars.count {
it.startsWith("local:")
})
}
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_calendar),
summary = if (selectedCalendars != null && calendarLists != null) "$selectedCalendars lists selected"
else stringResource(R.string.preference_search_calendar_summary),
switchValue = enabledProviders.contains("local") && hasCalendarPermission == true,
onSwitchChanged = {
viewModel.setProviderEnabled("local", it)
},
enabled = hasCalendarPermission == true,
onClick = {
showDialogForProvider = "local"
}
)
for (plugin in plugins) {
val state = plugin.state
if (state is PluginState.SetupRequired) {
Banner(
modifier = Modifier.padding(16.dp),
text = state.message
?: stringResource(id = R.string.plugin_state_setup_required),
icon = Icons.Rounded.ErrorOutline,
primaryAction = {
TextButton(onClick = {
try {
state.setupActivity.sendWithBackgroundPermission(context)
} catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e)
}
}) {
Text(stringResource(id = R.string.plugin_action_setup))
}
}
PreferenceCategory {
AnimatedVisibility(hasCalendarPermission == false) {
MissingPermissionBanner(
text = stringResource(R.string.missing_permission_calendar_search_settings),
onClick = {
viewModel.requestCalendarPermission(context as AppCompatActivity)
},
modifier = Modifier.padding(16.dp)
)
}
val selectedCalendars = remember(excludedCalendars, calendarLists) {
calendarLists?.count { it.providerId == plugin.plugin.authority }
?.minus(excludedCalendars.count {
it.startsWith(
"${plugin.plugin.authority}:"
)
})
}
PreferenceWithSwitch(
title = plugin.plugin.label,
enabled = state is PluginState.Ready,
summary = (state as? PluginState.SetupRequired)?.message
?: if (selectedCalendars != null && calendarLists != null) {
pluralStringResource(
R.plurals.calendar_search_enabled_lists,
selectedCalendars,
selectedCalendars
)
}
else (state as? PluginState.Ready)?.text ?: plugin.plugin.description,
switchValue = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
title = stringResource(R.string.preference_search_calendar),
summary = stringResource(R.string.preference_search_local_calendar_summary),
switchValue = enabledProviders.contains("local") && hasCalendarPermission == true,
onSwitchChanged = {
viewModel.setProviderEnabled(plugin.plugin.authority, it)
viewModel.setProviderEnabled("local", it)
},
enabled = hasCalendarPermission == true,
onClick = {
showDialogForProvider = plugin.plugin.authority
navController?.navigate("settings/search/calendar/local")
}
)
}
}
}
Log.d("MM20", "${calendarLists.toString()}")
val dialogCalendarLists by remember {
derivedStateOf {
if (showDialogForProvider == null) null
else calendarLists?.filter { it.providerId == showDialogForProvider }
}
}
if (showDialogForProvider != null && dialogCalendarLists != null) {
ModalBottomSheet(
onDismissRequest = {
showDialogForProvider = null
},
) {
val groups = remember(dialogCalendarLists) {
dialogCalendarLists!!.groupBy { it.owner }.entries.sortedBy { it.key }
}
LazyColumn {
items(groups) {
PreferenceCategory(
title = it.key,
iconPadding = false,
) {
for (list in it.value) {
CheckboxPreference(
title = list.name,
iconPadding = false,
value = list.id !in excludedCalendars,
onValueChanged = { value ->
viewModel.setCalendarExcluded(list.id, !value)
},
checkboxColors = CheckboxDefaults.colors(
checkedColor = if (list.color == 0) MaterialTheme.colorScheme.primary
else Color(
list.color.atTone(if (LocalDarkTheme.current) 80 else 40)
),
checkmarkColor = if (list.color == 0) MaterialTheme.colorScheme.onPrimary
else Color(
list.color.atTone(if (LocalDarkTheme.current) 20 else 100)
)
)
)
}
for (plugin in plugins) {
val state = plugin.state
if (state is PluginState.SetupRequired) {
Banner(
modifier = Modifier.padding(16.dp),
text = state.message
?: stringResource(id = R.string.plugin_state_setup_required),
icon = Icons.Rounded.ErrorOutline,
primaryAction = {
TextButton(onClick = {
try {
state.setupActivity.sendWithBackgroundPermission(context)
} catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e)
}
}) {
Text(stringResource(id = R.string.plugin_action_setup))
}
}
)
}
PreferenceWithSwitch(
title = plugin.plugin.label,
enabled = state is PluginState.Ready,
summary = (state as? PluginState.SetupRequired)?.message
?: (state as? PluginState.Ready)?.text
?: plugin.plugin.description,
switchValue = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
onSwitchChanged = {
viewModel.setProviderEnabled(plugin.plugin.authority, it)
},
onClick = {
navController?.navigate("settings/search/calendar/${plugin.plugin.authority}")
}
)
}
}
}

View File

@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.ClockWidgetAlignment
import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
@ -21,7 +22,7 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
settings.setCompact(compact)
}
val availableClockStyles = combine(settings.digital1, settings.custom) {digital1, custom ->
val availableClockStyles = combine(settings.digital1, settings.custom) { digital1, custom ->
listOf(
digital1,
ClockWidgetStyle.Digital2,
@ -54,6 +55,13 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
settings.setShowSeconds(showSeconds)
}
val timeFormat = settings.timeFormat
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), TimeFormat.System)
fun setTimeFormat(timeFormat: TimeFormat) {
settings.setTimeFormat(timeFormat)
}
val useThemeColor = settings.useThemeColor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)

View File

@ -0,0 +1,58 @@
package de.mm20.launcher2.ui.settings.contacts
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Call
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
@Composable
fun ContactsSettingsScreen() {
val viewModel: ContactsSettingsScreenVM = viewModel()
val context = LocalContext.current
val hasCallPermission by viewModel.hasCallPermission.collectAsStateWithLifecycle(null)
val callOnTap by viewModel.callOnTap.collectAsStateWithLifecycle(null)
PreferenceScreen(
title = stringResource(R.string.preference_search_contacts)
) {
item {
PreferenceCategory {
AnimatedVisibility(hasCallPermission == false) {
MissingPermissionBanner(
text = stringResource(R.string.missing_permission_call_contacts_settings),
onClick = {
viewModel.requestCallPermission(context as AppCompatActivity)
},
modifier = Modifier.padding(16.dp)
)
}
SwitchPreference(
title = stringResource(R.string.preference_contacts_call_on_tap),
summary = stringResource(R.string.preference_contacts_call_on_tap_summary),
icon = Icons.Rounded.Call,
value = callOnTap == true && hasCallPermission == true,
onValueChanged = {
viewModel.setCallOnTap(it)
},
enabled = hasCallPermission == true
)
}
}
}
}

View File

@ -0,0 +1,30 @@
package de.mm20.launcher2.ui.settings.contacts
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.search.ContactSearchSettings
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class ContactsSettingsScreenVM : ViewModel(), KoinComponent {
private val settings: ContactSearchSettings by inject()
private val permissionsManager: PermissionsManager by inject()
val hasCallPermission = permissionsManager.hasPermission(PermissionGroup.Call)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun requestCallPermission(activity: AppCompatActivity) =
permissionsManager.requestPermission(activity, PermissionGroup.Call)
val callOnTap = settings.callOnTap
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCallOnTap(callOnTap: Boolean) =
settings.setCallOnTap(callOnTap)
}

View File

@ -1,6 +1,5 @@
package de.mm20.launcher2.ui.settings.icons
import android.graphics.drawable.ColorDrawable
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.BorderStroke
@ -17,9 +16,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.FormatPaint
import androidx.compose.material.icons.rounded.Palette
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
@ -42,13 +39,10 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.icons.IconPack
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.icons.StaticIconLayer
import de.mm20.launcher2.icons.StaticLauncherIcon
import de.mm20.launcher2.preferences.IconShape
import de.mm20.launcher2.preferences.ui.GridSettings
import de.mm20.launcher2.ui.R
@ -116,6 +110,26 @@ fun IconsSettingsScreen() {
viewModel.setShowLabels(it)
}
)
SwitchPreference(
title = stringResource(R.string.preference_grid_list_style),
summary = stringResource(R.string.preference_grid_list_style_summary),
value = grid.showList,
onValueChanged = {
viewModel.setShowList(it)
}
)
AnimatedVisibility(
grid.showList
) {
SwitchPreference(
title = stringResource(R.string.preference_grid_list_icons),
summary = stringResource(R.string.preference_grid_list_icons_summary),
value = grid.showListIcons,
onValueChanged = {
viewModel.setShowListIcons(it)
}
)
}
SliderPreference(
title = stringResource(R.string.preference_grid_column_count),
value = grid.columnCount,
@ -430,30 +444,14 @@ fun IconShapePreference(
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val context = LocalContext.current
ShapedLauncherIcon(
size = 48.dp,
icon = {
StaticLauncherIcon(
foregroundLayer = StaticIconLayer(
icon = ContextCompat.getDrawable(
context,
R.mipmap.ic_launcher_foreground
)!!,
scale = 1.5f,
),
backgroundLayer = StaticIconLayer(
icon = ColorDrawable(
context.getColor(R.color.ic_launcher_background)
)
)
)
},
modifier = Modifier.clickable {
onValueChanged(it)
showDialog = false
},
shape = getShape(it)
Box(
modifier = Modifier.clip(getShape(it))
.size(48.dp)
.background(MaterialTheme.colorScheme.primary)
.clickable {
onValueChanged(it)
showDialog = false
}
)
Text(
getShapeName(it) ?: "",

View File

@ -53,6 +53,14 @@ class IconsSettingsScreenVM(
uiSettings.setGridShowLabels(showLabels)
}
fun setShowList(showList: Boolean) {
uiSettings.setGridShowList(showList)
}
fun setShowListIcons(showIcons: Boolean) {
uiSettings.setGridShowListIcons(showIcons)
}
val iconShape = uiSettings.iconShape
fun setIconShape(iconShape: IconShape) {
uiSettings.setIconShape(iconShape)

View File

@ -6,7 +6,6 @@ import de.mm20.launcher2.plugin.PluginType
import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.preferences.search.LocationSearchSettings
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

View File

@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.settings.plugins
import android.app.Activity
import android.app.PendingIntent
import android.content.Intent
import androidx.activity.compose.LocalActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
@ -19,6 +20,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material.icons.automirrored.rounded.InsertDriveFile
@ -77,7 +79,7 @@ import de.mm20.launcher2.ui.locals.LocalNavController
@Composable
fun PluginSettingsScreen(pluginId: String) {
val navController = LocalNavController.current
val activity = LocalContext.current as AppCompatActivity
val activity = LocalActivity.current
val context = LocalContext.current
val viewModel: PluginSettingsScreenVM = viewModel()
LaunchedEffect(pluginId) {
@ -142,7 +144,7 @@ fun PluginSettingsScreen(pluginId: String) {
navigationIcon = {
IconButton(onClick = {
if (navController?.navigateUp() != true) {
activity.onBackPressed()
activity?.onBackPressed()
}
}) {
Icon(
@ -155,7 +157,7 @@ fun PluginSettingsScreen(pluginId: String) {
if (pluginPackage?.settings != null) {
IconButton(onClick = {
pluginPackage?.settings?.let {
activity.startActivity(it)
activity?.startActivity(it)
}
}) {
Icon(
@ -322,7 +324,9 @@ fun PluginSettingsScreen(pluginId: String) {
)
}
AnimatedVisibility(pluginPackage?.enabled == true && hasPermission == true) {
Column {
Column(
modifier = Modifier.verticalScroll(rememberScrollState())
) {
if (filePlugins.isNotEmpty()) {
PreferenceCategory(
stringResource(R.string.plugin_type_filesearch),

View File

@ -16,15 +16,13 @@ import androidx.compose.material.icons.rounded.Loop
import androidx.compose.material.icons.rounded.Person
import androidx.compose.material.icons.rounded.Place
import androidx.compose.material.icons.rounded.Public
import androidx.compose.material.icons.rounded.Sort
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material.icons.rounded.Tag
import androidx.compose.material.icons.rounded.Today
import androidx.compose.material.icons.rounded.VisibilityOff
import androidx.compose.material.icons.rounded.Warning
import androidx.compose.material.icons.rounded.Work
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -33,11 +31,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.icons.Wikipedia
import de.mm20.launcher2.plugin.PluginType
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.component.MissingPermissionBanner
@ -48,7 +46,6 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
import de.mm20.launcher2.icons.Wikipedia
import de.mm20.launcher2.ui.launcher.search.filters.SearchFilters
import de.mm20.launcher2.ui.locals.LocalNavController
@ -57,19 +54,40 @@ fun SearchSettingsScreen() {
val viewModel: SearchSettingsScreenVM = viewModel()
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val navController = LocalNavController.current
var showFilterEditor by remember {
mutableStateOf(false)
}
var showFilterEditor by remember { mutableStateOf(false) }
val plugins by viewModel.plugins.collectAsStateWithLifecycle(emptyList())
val hasCalendarPlugins by remember { derivedStateOf { plugins.any { it.plugin.type == PluginType.Calendar } } }
val hasLocationPlugins by remember { derivedStateOf { plugins.any { it.plugin.type == PluginType.LocationSearch } } }
val hasAppShortcutsPermission by viewModel.hasAppShortcutPermission.collectAsStateWithLifecycle(null)
val hasContactsPermission by viewModel.hasContactsPermission.collectAsStateWithLifecycle(null)
val hasCalendarPermission by viewModel.hasCalendarPermission.collectAsStateWithLifecycle(null)
val hasLocationPermission by viewModel.hasLocationPermission.collectAsStateWithLifecycle(null)
val favorites by viewModel.favorites.collectAsStateWithLifecycle(null)
val appShortcuts by viewModel.appShortcuts.collectAsStateWithLifecycle(null)
val calendar by viewModel.calendarSearch.collectAsStateWithLifecycle(null)
val places by viewModel.placesSearch.collectAsStateWithLifecycle(null)
val contacts by viewModel.contacts.collectAsStateWithLifecycle(null)
val calculator by viewModel.calculator.collectAsStateWithLifecycle(null)
val unitConverter by viewModel.unitConverter.collectAsStateWithLifecycle(null)
val wikipedia by viewModel.wikipedia.collectAsStateWithLifecycle(null)
val websites by viewModel.websites.collectAsStateWithLifecycle(null)
val autoFocus by viewModel.autoFocus.collectAsStateWithLifecycle(null)
val launchOnEnter by viewModel.launchOnEnter.collectAsStateWithLifecycle(null)
val reverseSearchResults by viewModel.reverseSearchResults.collectAsStateWithLifecycle(null)
val filterBar by viewModel.filterBar.collectAsStateWithLifecycle(null)
PreferenceScreen(title = stringResource(R.string.preference_screen_search)) {
item {
PreferenceCategory {
val favorites by viewModel.favorites.collectAsStateWithLifecycle(null)
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_favorites),
summary = stringResource(R.string.preference_search_favorites_summary),
@ -92,9 +110,6 @@ fun SearchSettingsScreen() {
}
)
val hasContactsPermission by viewModel.hasContactsPermission.collectAsStateWithLifecycle(
null
)
AnimatedVisibility(hasContactsPermission == false) {
MissingPermissionBanner(
text = stringResource(R.string.missing_permission_contact_search_settings),
@ -104,30 +119,53 @@ fun SearchSettingsScreen() {
modifier = Modifier.padding(16.dp)
)
}
val contacts by viewModel.contacts.collectAsStateWithLifecycle(null)
SwitchPreference(
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_contacts),
summary = stringResource(R.string.preference_search_contacts_summary),
icon = Icons.Rounded.Person,
value = contacts == true && hasContactsPermission == true,
onValueChanged = {
switchValue = contacts == true && hasContactsPermission == true,
onSwitchChanged = {
viewModel.setContacts(it)
},
onClick = {
navController?.navigate("settings/search/contacts")
},
enabled = hasContactsPermission == true
)
Preference(
title = stringResource(R.string.preference_search_calendar),
summary = stringResource(R.string.preference_search_calendar_summary),
icon = Icons.Rounded.Today,
onClick = {
navController?.navigate("settings/search/calendar")
},
)
val hasAppShortcutsPermission by viewModel.hasAppShortcutPermission.collectAsStateWithLifecycle(
null
)
if (hasCalendarPlugins) {
Preference(
title = stringResource(R.string.preference_search_calendar),
summary = stringResource(R.string.preference_search_calendar_summary),
icon = Icons.Rounded.Today,
onClick = {
navController?.navigate("settings/search/calendar")
},
)
} else {
AnimatedVisibility(hasCalendarPermission == false) {
MissingPermissionBanner(
text = stringResource(R.string.missing_permission_calendar_search_settings),
onClick = {
viewModel.requestCalendarPermission(context as AppCompatActivity)
},
modifier = Modifier.padding(16.dp)
)
}
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_calendar),
summary = stringResource(R.string.preference_search_calendar_summary),
switchValue = calendar == true,
onSwitchChanged = {
viewModel.setCalendarSearch(it)
},
icon = Icons.Rounded.Today,
enabled = hasCalendarPermission == true,
onClick = {
navController?.navigate("settings/search/calendar/local")
}
)
}
AnimatedVisibility(hasAppShortcutsPermission == false) {
MissingPermissionBanner(
text = stringResource(
@ -140,7 +178,6 @@ fun SearchSettingsScreen() {
modifier = Modifier.padding(16.dp)
)
}
val appShortcuts by viewModel.appShortcuts.collectAsStateWithLifecycle(null)
SwitchPreference(
title = stringResource(R.string.preference_search_appshortcuts),
summary = stringResource(R.string.preference_search_appshortcuts_summary),
@ -152,7 +189,6 @@ fun SearchSettingsScreen() {
enabled = hasAppShortcutsPermission == true
)
val calculator by viewModel.calculator.collectAsStateWithLifecycle(null)
SwitchPreference(
title = stringResource(R.string.preference_search_calculator),
summary = stringResource(R.string.preference_search_calculator_summary),
@ -163,7 +199,6 @@ fun SearchSettingsScreen() {
}
)
val unitConverter by viewModel.unitConverter.collectAsStateWithLifecycle(null)
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_unitconverter),
summary = stringResource(R.string.preference_search_unitconverter_summary),
@ -177,7 +212,6 @@ fun SearchSettingsScreen() {
}
)
val wikipedia by viewModel.wikipedia.collectAsStateWithLifecycle(null)
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_wikipedia),
summary = stringResource(R.string.preference_search_wikipedia_summary),
@ -191,7 +225,6 @@ fun SearchSettingsScreen() {
}
)
val websites by viewModel.websites.collectAsStateWithLifecycle(null)
SwitchPreference(
title = stringResource(R.string.preference_search_websites),
summary = stringResource(R.string.preference_search_websites_summary),
@ -202,14 +235,43 @@ fun SearchSettingsScreen() {
}
)
Preference(
title = stringResource(R.string.preference_search_locations),
summary = stringResource(R.string.preference_search_locations_summary),
icon = Icons.Rounded.Place,
onClick = {
navController?.navigate("settings/search/locations")
}
)
AnimatedVisibility(hasLocationPermission == false) {
MissingPermissionBanner(
text = stringResource(
R.string.missing_permission_location_search,
),
onClick = {
viewModel.requestLocationPermission(context as AppCompatActivity)
},
modifier = Modifier.padding(16.dp)
)
}
if (hasLocationPlugins) {
Preference(
title = stringResource(R.string.preference_search_locations),
summary = stringResource(R.string.preference_search_locations_summary),
icon = Icons.Rounded.Place,
enabled = hasLocationPermission == true,
onClick = {
navController?.navigate("settings/search/locations")
}
)
} else {
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_locations),
summary = stringResource(R.string.preference_search_locations_summary),
icon = Icons.Rounded.Place,
onClick = {
navController?.navigate("settings/search/locations")
},
switchValue = places == true,
onSwitchChanged = {
viewModel.setPlacesSearch(it)
},
enabled = hasLocationPermission == true,
)
}
Preference(
title = stringResource(R.string.preference_screen_search_actions),
@ -242,7 +304,6 @@ fun SearchSettingsScreen() {
}
}
item {
val filterBar by viewModel.filterBar.collectAsStateWithLifecycle(null)
PreferenceCategory {
Preference(
title = stringResource(R.string.preference_default_filter),
@ -264,7 +325,7 @@ fun SearchSettingsScreen() {
Preference(
title = stringResource(R.string.preference_customize_filter_bar),
summary = stringResource(R.string.preference_customize_filter_bar_summary),
onClick = {
onClick = {
navController?.navigate("settings/search/filterbar")
}
)
@ -273,7 +334,6 @@ fun SearchSettingsScreen() {
}
item {
PreferenceCategory {
val autoFocus by viewModel.autoFocus.collectAsStateWithLifecycle(null)
SwitchPreference(
title = stringResource(R.string.preference_search_bar_auto_focus),
summary = stringResource(R.string.preference_search_bar_auto_focus_summary),
@ -283,7 +343,6 @@ fun SearchSettingsScreen() {
viewModel.setAutoFocus(it)
}
)
val launchOnEnter by viewModel.launchOnEnter.collectAsStateWithLifecycle(null)
SwitchPreference(
title = stringResource(R.string.preference_search_bar_launch_on_enter),
summary = stringResource(R.string.preference_search_bar_launch_on_enter_summary),
@ -296,9 +355,6 @@ fun SearchSettingsScreen() {
}
item {
PreferenceCategory {
val reverseSearchResults by viewModel.reverseSearchResults.collectAsStateWithLifecycle(
null
)
ListPreference(
title = stringResource(R.string.preference_layout_search_results),
items = listOf(

View File

@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.preferences.search.CalculatorSearchSettings
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
import de.mm20.launcher2.preferences.search.ContactSearchSettings
@ -30,10 +31,11 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
private val websiteSearchSettings: WebsiteSearchSettings by inject()
private val unitConverterSettings: UnitConverterSettings by inject()
private val calculatorSearchSettings: CalculatorSearchSettings by inject()
private val locationSearchSettings: LocationSearchSettings by inject()
private val searchFilterSettings: SearchFilterSettings by inject()
private val pluginService: PluginService by inject()
private val permissionsManager: PermissionsManager by inject()
private val locationSearchSettings: LocationSearchSettings by inject()
val favorites = searchUiSettings.favorites
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
@ -42,6 +44,14 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
searchUiSettings.setFavorites(favorites)
}
val hasCalendarPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val calendarSearch = calendarSearchSettings.isProviderEnabled("local")
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCalendarSearch(enabled: Boolean) {
calendarSearchSettings.setProviderEnabled("local", enabled)
}
val hasContactsPermission = permissionsManager.hasPermission(PermissionGroup.Contacts)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
@ -52,6 +62,22 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
contactSearchSettings.setEnabled(contacts)
}
val hasLocationPermission = permissionsManager.hasPermission(PermissionGroup.Location)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val placesSearch = locationSearchSettings.osmLocations
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setPlacesSearch(enabled: Boolean) {
locationSearchSettings.setOsmLocations(enabled)
}
fun requestLocationPermission(activity: AppCompatActivity) {
permissionsManager.requestPermission(activity, PermissionGroup.Location)
}
fun requestCalendarPermission(activity: AppCompatActivity) {
permissionsManager.requestPermission(activity, PermissionGroup.Calendar)
}
fun requestContactsPermission(activity: AppCompatActivity) {
permissionsManager.requestPermission(activity, PermissionGroup.Contacts)
}
@ -130,4 +156,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
fun setSearchFilters(searchFilters: SearchFilters) {
searchFilterSettings.setDefaultFilter(searchFilters)
}
val plugins = pluginService.getPluginsWithState(enabled = true)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
}

View File

@ -0,0 +1,10 @@
package de.mm20.launcher2.ui.utils
import android.content.Context
import android.text.format.DateFormat
import de.mm20.launcher2.preferences.TimeFormat
import de.mm20.launcher2.preferences.TimeFormat.TwentyFourHour
fun TimeFormat.isTwentyFourHours(context: Context): Boolean {
return this == TimeFormat.TwentyFourHour || this == TimeFormat.System && DateFormat.is24HourFormat(context)
}

View File

@ -291,7 +291,7 @@
<string name="preference_clockwidget_battery_part">Bateria</string>
<string name="preference_clockwidget_battery_part_summary">Mostra el nivell actual de la bateria quan la bateria està baixa o s\'està carregant</string>
<string name="preference_clockwidget_alarm_part">Alarmes</string>
<string name="preference_clockwidget_alarm_part_summary">Mostra les alarmes que sonaran durant els propers 15 minuts</string>
<string name="preference_clockwidget_alarm_part_summary">Mostra les alarmes que sonaran en les properes 8 hores</string>
<string name="preference_screen_backup">Fes una còpia de seguretat &amp; restaura</string>
<string name="preference_screen_backup_summary">Exporta i importa les dades del llançador</string>
<string name="preference_backup">Còpia de seguretat</string>
@ -755,7 +755,7 @@
<string name="poi_category_fire_station">Estació de bombers</string>
<string name="poi_category_courthouse">Jutjats</string>
<string name="poi_category_townhall">Ajuntament</string>
<string name="preference_search_locations_radius_large_radius_warning">Els grans radis de cerca poden alentir significativament la cerca.</string>
<string name="preference_search_locations_radius_large_radius_warning">Un radi de cerca gran pot alentir significativament la cerca.</string>
<string name="poi_category_asian_restaurant">Restaurant asiàtic</string>
<string name="poi_category_ramen_restaurant">Restaurant Ramen</string>
<string name="poi_category_soup_restaurant">Restaurant de sopes</string>
@ -778,4 +778,63 @@
<string name="poi_category_handball">Pista d\'handbol</string>
<string name="poi_category_volleyball">Pista de voleibol</string>
<string name="poi_category_skiing">Pistes d\'esquí</string>
<plurals name="calendar_search_enabled_lists">
<item quantity="one">La llista de %1$s seleccionada</item>
<item quantity="many">%1$s llistes seleccionades</item>
<item quantity="other">%1$s llistes seleccionades</item>
</plurals>
<plurals name="departure_time_in">
<item quantity="one">en un minut</item>
<item quantity="many">d\'aquí a %1$d minuts</item>
<item quantity="other">d\'aquí a %1$d minuts</item>
</plurals>
<string name="departure_time_now">ara</string>
<string name="weather_widget_no_provider">No s\'ha seleccionat cap proveïdor meteorològic o el proveïdor seleccionat no està disponible</string>
<string name="task_due_time">Realitzat a %1$s</string>
<string name="task_due_date">Realitzat al dia %1$s</string>
<string name="task_due_today">Realitzat avui</string>
<string name="preference_search_local_calendar_summary">Cerca calendaris en aquest dispositiu</string>
<string name="preference_calendar_hide_completed">Amaga les tasques completades</string>
<string name="profile_private_profile_state_locked">L\'espai privat està tancat</string>
<string name="music_widget_next_track">Següent títol</string>
<string name="music_widget_pause">Pausa</string>
<string name="profile_work_profile_state_locked">Les aplicacions de treball estan en pausa.</string>
<string name="weather_forecast_humidity">Humitat</string>
<string name="weather_forecast_wind">Vent</string>
<string name="reset_icon">Restabliment de la icona</string>
<string name="tag_empty_name">Una etiqueta no pot existir sense un nom. Si continueu, l\'etiqueta se suprimirà.</string>
<string name="action_done">Fet</string>
<string name="action_more_actions">Més accions</string>
<string name="action_clear">Borra el camp</string>
<string name="preference_compact_tags_summary">Amaga les etiquetes o icones d\'etiquetes per reduir l\'espai que ocupen les etiquetes</string>
<string name="preference_compact_tags">Etiquetes compactes</string>
<string name="plugin_type_calendar">Calendari</string>
<string name="hint_drag_and_drop_reorder">Manteniu premut i arrossegueu els elements per reordenar-los</string>
<string name="preference_clock_widget_time_format_24h">24h</string>
<string name="preference_clock_widget_time_format_12h">12h</string>
<string name="preference_clock_widget_time_format_system">Predeterminat</string>
<string name="preference_search_supportedunits">Unitats suportades</string>
<string name="apps_profile_private">Privat</string>
<plurals name="app_info_notifications">
<item quantity="one">%1$s notificació</item>
<item quantity="many">%1$s notificacions</item>
<item quantity="other">%1$s notificacions</item>
</plurals>
<string name="music_widget_previous_track">Títol anterior</string>
<string name="music_widget_play">Jugar</string>
<string name="music_widget_open_player">Jugador</string>
<string name="calendar_widget_previous_day">Dia anterior</string>
<string name="calendar_widget_next_day">L\'endemà</string>
<string name="calendar_widget_create_event">Crea un nou esdeveniment</string>
<string name="calendar_widget_open_calendar">Obre l\'aplicació de calendari</string>
<string name="profile_private_profile_action_lock">Tanca l\'espai privat</string>
<string name="weather_forecast_precipitation">Precipitació</string>
<string name="preference_clock_widget_time_format">Format horari</string>
<string name="profile_work_profile_action_unlock">Reactivar</string>
<string name="profile_work_profile_action_lock">Posa en pausa les aplicacions de treball</string>
<string name="profile_private_profile_action_unlock">Desbloqueja</string>
<string name="app_label_locked_profile">Bloquejat</string>
<string name="widget_removed">S\'ha eliminat el widget</string>
<string name="menu_show_filters">Mostra els filtres</string>
<string name="menu_hide_filters">Amaga els filtres</string>
</resources>

View File

@ -324,4 +324,17 @@
<item quantity="many">decímetres</item>
<item quantity="other">decímetres</item>
</plurals>
<string name="dimension_length">Longitud</string>
<string name="dimension_mass">Massa</string>
<string name="dimension_velocity">Velocitat</string>
<string name="dimension_volume">Volum</string>
<string name="dimension_area">Àrea</string>
<string name="dimension_currency">Moneda</string>
<string name="dimension_data">Dades</string>
<string name="dimension_bitrate">Taxa de bits</string>
<string name="dimension_pressure">Pressió</string>
<string name="dimension_energy">Energia</string>
<string name="dimension_frequency">Freqüència</string>
<string name="dimension_temperature">Temperatura</string>
<string name="dimension_time">Hora</string>
</resources>

View File

@ -827,4 +827,19 @@
<string name="weather_forecast_humidity">Vlhkost</string>
<string name="tag_empty_name">Štítek nemůže existovat bez názvu. Pokud budete pokračovat, bude štítek odstraněn.</string>
<string name="widget_removed">Widget odstraněn</string>
<string name="preference_clock_widget_time_format">Formát času</string>
<string name="preference_clock_widget_time_format_24h">24hodinový</string>
<string name="preference_clock_widget_time_format_12h">12hodinový</string>
<string name="preference_clock_widget_time_format_system">Podle systému</string>
<string name="departure_time_now">nyní</string>
<plurals name="departure_time_in">
<item quantity="one">za minutu</item>
<item quantity="few">za %1$d minuty</item>
<item quantity="other">za %1$d minut</item>
</plurals>
<string name="preference_search_local_calendar_summary">Hledat kalendáře na tomto zařízení</string>
<string name="missing_permission_call_contacts_settings">Pro provádění hovorů je vyžadováno oprávnění k telefonu</string>
<string name="preference_contacts_call_on_tap">Zavolat po klepnutí</string>
<string name="preference_contacts_call_on_tap_summary">Rovnou zahájit hovor po klepnutí na telefonní číslo</string>
<string name="departure_time_departed">odjel</string>
</resources>

View File

@ -437,7 +437,7 @@
<string name="preference_enforce_icon_shape_summary">Anvend form på alle ikoner, inklusive dem, der normalt ikke understøtter det</string>
<string name="preference_about_telegram">Telegram-gruppe</string>
<string name="preference_about_license_summary">Licenseret under GNU General Public License 3.0</string>
<string name="preference_clockwidget_alarm_part_summary">Vis alarmer, der vil ringe inden for de næste 15 minutter</string>
<string name="preference_clockwidget_alarm_part_summary">Vis alarmer, der ringer inden for de næste 8 timer</string>
<string name="preference_search_favorites_summary">Vis fastgjorte og ofte brugte elementer over app-gitter</string>
<string name="preference_search_unitconverter_summary">Anvendelse: 1,5 kg eller 4 cm &gt;&gt; in</string>
<string name="preference_favorites_edit_button_summary">Vis en knap for at omarrangere favoritterne</string>
@ -467,7 +467,7 @@
<string name="favorites_empty_tag">Der er ingen elementer med dette tag</string>
<string name="frequently_used_show_in_favorites">Vis i favoritter</string>
<string name="frequently_used_rows">Antal rækker</string>
<string name="customize_tags_placeholder">Tags</string>
<string name="customize_tags_placeholder">Tags</string>
<string name="preference_search_search_actions_summary">Konfigurér hurtige handlinger og søgegenveje</string>
<string name="search_action_call">Ring</string>
<string name="search_action_email">E-mail</string>
@ -675,4 +675,149 @@
<string name="poi_category_convenience">Dagligvarebutik</string>
<string name="poi_category_hairdresser">Frisør</string>
<string name="poi_category_bakery">Bageri</string>
<plurals name="calendar_search_enabled_lists">
<item quantity="one">%1$s liste valgt</item>
<item quantity="other">%1$s lister valgt</item>
</plurals>
<string name="departure_time_now">nu</string>
<string name="customize_item_label">Mærkat</string>
<string name="preference_search_supportedunits">Understøttede enheder</string>
<string name="item_visibility_hidden">Aldrig</string>
<string name="preference_calendar_hide_completed">Skjul fuldførte opgaver</string>
<plurals name="departure_time_in">
<item quantity="one">om et minut</item>
<item quantity="other">om %1$d minutter</item>
</plurals>
<string name="poi_category_american_football">Amerikansk fodboldbane</string>
<string name="contact_info_work">Arbejde</string>
<string name="poi_category_bank">Bank</string>
<string name="contact_info_mobile">Mobil</string>
<string name="poi_category_kiosk">Kiosk</string>
<string name="poi_category_church">Kirke</string>
<string name="poi_category_ramen_restaurant">Ramen-restaurant</string>
<string name="poi_category_car_rental">Biludlejning</string>
<string name="poi_category_car_sharing">Bildeling</string>
<string name="task_due_time">Forfalder kl. %1$s</string>
<string name="task_due_date">Forfalder d. %1$s</string>
<string name="task_due_today">Forfalder i dag</string>
<string name="poi_category_pet">Dyrehandel</string>
<string name="search_filter_tools">Værktøjer</string>
<string name="preference_filter_bar">Vis filterlinje</string>
<string name="poi_category_soccer">Fodboldbane</string>
<string name="poi_category_atm">Hæveautomat</string>
<string name="poi_category_mosque">Moské</string>
<string name="poi_category_buddhist_temple">Buddhistisk tempel</string>
<string name="poi_category_place_of_worship">Sted for tilbedelse</string>
<string name="poi_category_kebab_restaurant">Kebab-restaurant</string>
<string name="poi_category_stadium">Stadium</string>
<string name="poi_category_casino">Casino</string>
<string name="poi_category_discount_store">Discountbutik</string>
<string name="poi_category_fire_station">Brandstation</string>
<string name="poi_category_courthouse">Retten</string>
<string name="preference_search_locations_radius_large_radius_warning">En stor søgeradius kan gøre søgningen betydeligt langsommere.</string>
<string name="contact_info_home">Hjem</string>
<string name="music_widget_play">Afspil</string>
<string name="music_widget_pause">Pause</string>
<string name="music_widget_open_player">Åbn afspiller</string>
<string name="calendar_widget_previous_day">Forrige dag</string>
<string name="calendar_widget_create_event">Opret ny begivenhed</string>
<string name="profile_work_profile_action_lock">Sæt arbejdsapps på pause</string>
<string name="profile_private_profile_action_unlock">Oplås</string>
<string name="preference_signout">Log ud</string>
<string name="poi_category_baseball">Baseballbane</string>
<string name="reset_icon">Nulstil ikon</string>
<string name="tag_empty_name">Et tag kan ikke eksistere uden et navn. Hvis du fortsætter, slettes tagget.</string>
<string name="poi_category_tennis">Tennisbane</string>
<string name="poi_category_chinese_restaurant">Kinesisk restaurant</string>
<string name="weather_forecast_humidity">Fugtighed</string>
<plurals name="app_info_notifications">
<item quantity="one">%1$s notifikation</item>
<item quantity="other">%1$s notifikationer</item>
</plurals>
<string name="weather_widget_no_provider">Ingen vejrtjeneste valgt, eller den valgte tjeneste er ikke tilgængelig</string>
<string name="weather_forecast_wind">Vind</string>
<string name="weather_forecast_precipitation">Nedbør</string>
<string name="poi_category_museum">Museum</string>
<string name="poi_category_fitness_center">Fitnesscenter</string>
<string name="action_done">Færdig</string>
<string name="action_more_actions">Flere handlinger</string>
<string name="action_clear">Ryd</string>
<string name="search_filter_online">Online resultater</string>
<string name="search_filter_apps">Apps</string>
<string name="poi_category_soup_restaurant">Suppe-restaurant</string>
<string name="poi_category_brunch_restaurant">Brunch-restaurant</string>
<string name="poi_category_japanese_restaurant">Japansk restaurant</string>
<string name="poi_category_gymnastics">Gymnastik</string>
<string name="poi_category_golf">Golfbane</string>
<string name="customize_item_tags">Tags</string>
<string name="customize_item_visibility">Vis i</string>
<string name="music_widget_next_track">Næste sang</string>
<string name="preference_customize_filter_bar">Tilpas filterlinje</string>
<string name="preference_customize_filter_bar_summary">Tilpas hvilke elementer, der vises i filterlinjen</string>
<string name="profile_work_profile_state_locked">Arbejdsapps er sat på pause.</string>
<string name="hint_drag_and_drop_reorder">Hold og træk elementer for at omarrangere dem</string>
<string name="preference_clock_widget_time_format">Tidsformat</string>
<string name="preference_clock_widget_time_format_24h">24 timer</string>
<string name="preference_clock_widget_time_format_12h">12 timer</string>
<string name="preference_clock_widget_time_format_system">Systemstandard</string>
<string name="clock_style_custom">Brugerdefineret widget</string>
<string name="menu_show_filters">Vis filtre</string>
<string name="menu_hide_filters">Skjul filtre</string>
<string name="plugin_type_calendar">Kalender</string>
<string name="poi_category_swimming">Swimmingpool</string>
<string name="poi_category_martial_arts">Kampsport</string>
<string name="poi_category_ice_hockey">Ishockeybane</string>
<string name="poi_category_handball">Håndboldbane</string>
<plurals name="contact_email_addresses">
<item quantity="one">%1$s e-mailadresse</item>
<item quantity="other">%1$s e-mailadresser</item>
</plurals>
<plurals name="contact_postal_addresses">
<item quantity="one">%1$s postadresse</item>
<item quantity="other">%1$s postadresser</item>
</plurals>
<string name="music_widget_previous_track">Forrige sang</string>
<string name="poi_category_volleyball">Volleyballbane</string>
<string name="poi_category_cricket">Cricket-bane</string>
<string name="poi_category_park">Park</string>
<string name="poi_category_monument">Monument</string>
<string name="poi_category_government_building">Statslig bygning</string>
<string name="poi_category_townhall">Rådhus</string>
<string name="item_visibility_app_default">App-gitter og søgeresultater</string>
<string name="item_visibility_calendar_default">Kalenderwidget og søgeresultater</string>
<string name="item_visibility_search_only">Søgeresultater</string>
<plurals name="contact_phone_numbers">
<item quantity="one">%1$s telefonnummer</item>
<item quantity="other">%1$s telefonnumre</item>
</plurals>
<string name="calendar_widget_next_day">Næste dag</string>
<string name="calendar_widget_open_calendar">Åbn kalenderapp</string>
<string name="profile_work_profile_action_unlock">Fjern pause</string>
<string name="app_label_locked_profile">Låst</string>
<string name="widget_removed">Widget fjernet</string>
<string name="preference_compact_tags">Kompakte tags</string>
<string name="preference_compact_tags_summary">Skjul tag-mærkater eller -ikoner for at spare plads</string>
<string name="apps_profile_private">Privat</string>
<string name="preference_search_location_custom_tile_server_url">Tileserver-URL</string>
<string name="preference_default_filter">Standardfilter</string>
<string name="preference_default_filter_summary">Tilpas standardfilteret for søgninger</string>
<string name="preference_filter_bar_summary">Vis hurtige filtre over tastaturet</string>
<string name="preference_search_osm_locations">OpenStreetMap</string>
<string name="preference_search_osm_locations_summary">Søg på OpenStreetMap efter butikker og andre steder i lokalområdet</string>
<string name="poi_category_university">Universitet</string>
<string name="poi_category_basketball">Basketballbane</string>
<string name="poi_category_hindu_temple">Hinduistisk tempel</string>
<string name="poi_category_pizza_restaurant">Pizzaria</string>
<string name="poi_category_burger_restaurant">Burger-restaurant</string>
<string name="poi_category_car_wash">Bilvask</string>
<string name="poi_category_synagogue">Synagoge</string>
<string name="poi_category_asian_restaurant">Asiatisk restaurant</string>
<string name="poi_category_charging_station">Ladestation</string>
<string name="poi_category_motorcycle_rental">Motorcykeludlejning</string>
<string name="poi_category_gallery">Galleri</string>
<string name="poi_category_amusement_park">Forlystelsespark</string>
<string name="poi_category_concert_hall">Koncertsal</string>
<string name="poi_category_shopping">Shopping</string>
<string name="poi_category_skiing">Skiløb</string>
<string name="preference_search_local_calendar_summary">Søg i kalendere på denne enhed</string>
</resources>

View File

@ -229,4 +229,33 @@
<item quantity="other">tons</item>
</plurals>
<string name="unit_gigabyte_symbol">GB</string>
<plurals name="unit_acre">
<item quantity="one">hektar</item>
<item quantity="other">hektar</item>
</plurals>
<plurals name="unit_sqyard">
<item quantity="one">kvadratmeter</item>
<item quantity="other">kvadratmeter</item>
</plurals>
<plurals name="unit_knot">
<item quantity="one">knob</item>
<item quantity="other">knob</item>
</plurals>
<string name="dimension_length">Længde</string>
<string name="dimension_mass">Masse</string>
<string name="dimension_velocity">Hastighed</string>
<string name="dimension_volume">Volumen</string>
<string name="dimension_area">Område</string>
<string name="dimension_currency">Valuta</string>
<string name="dimension_data">Data</string>
<string name="dimension_bitrate">Bitrate</string>
<string name="dimension_pressure">Tryk</string>
<string name="dimension_energy">Energi</string>
<string name="dimension_frequency">Frekvens</string>
<string name="dimension_temperature">Temperatur</string>
<string name="dimension_time">Tid</string>
<plurals name="unit_megabyte">
<item quantity="one">megabyte</item>
<item quantity="other">megabytes</item>
</plurals>
</resources>

View File

@ -813,4 +813,12 @@
<string name="reset_icon">Symbol zurücksetzen</string>
<string name="preference_compact_tags">Kompakte Tags</string>
<string name="preference_compact_tags_summary">Beschriftungen oder Symbole von Tags ausblenden um den von Tags eingenommenen Platz zu reduzieren</string>
<string name="departure_time_now">jetzt</string>
<plurals name="departure_time_in">
<item quantity="one">in einer Minute</item>
<item quantity="other">in %1$d Minuten</item>
</plurals>
<string name="preference_contacts_call_on_tap">Zum Anruf Tippen</string>
<string name="preference_contacts_call_on_tap_summary">Beim Tippen auf eine Telefonnummer diese direkt Anrufen</string>
<string name="departure_time_departed">abgefahren</string>
</resources>

View File

@ -1,30 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="location_closed">بسته</string>
<string name="location_closes_other_day">بسته شده %1$s،%2$s</string>
<string name="location_closes_other_day">بسته شده %1$s، %2$s</string>
<string name="location_opens">بازشده در %1$s</string>
<string name="location_opens_other_day">باز شده %1$s، %2$s</string>
<string name="menu_share">هم‌رسانی</string>
<string name="menu_share_store_link">%1$s پیوند</string>
<string name="menu_share_apk_file">پرونده این بسته</string>
<string name="menu_uninstall">پاک کردن</string>
<string name="menu_favorites_pin">سنجاق در مورد علاقه ها</string>
<string name="menu_favorites_pin">سنجاق در موارد دلخواه</string>
<string name="menu_favorites_unpin">برداشتن سنجاق</string>
<string name="menu_back">بازگشت</string>
<string name="menu_launch">اجرا</string>
<string name="menu_customize">شخصی‌سازی</string>
<string name="menu_open_file">بازکردن</string>
<string name="menu_map">نمایش در نقشه</string>
<string name="menu_open_file">بازکردن پرونده</string>
<string name="menu_map">مشاهده در نقشه</string>
<string name="menu_website">بازکردن وبگاه</string>
<string name="menu_hide">پنهان</string>
<string name="menu_hide">پنهان کردن</string>
<string name="msg_item_hidden">%1$s پنهان شده.</string>
<string name="action_undo">واگرد</string>
<string name="action_import">وارد کردن</string>
<string name="menu_delete">پاک کردن</string>
<string name="menu_delete">پاککردن</string>
<string name="menu_calendar_open_externally">بازکردن در برنامه تقویم</string>
<string name="search_bar_placeholder">جستجو</string>
<string name="app_info_version">نسخه %1$s</string>
<string name="settings">پیکربندی</string>
<string name="app_info_version">نگارش %1$s</string>
<string name="settings">تنظیمات</string>
<string name="help">کمک</string>
<string name="wallpaper">پس‌زمینه</string>
<string name="wind_north">شمال</string>
@ -62,12 +62,12 @@
<string name="file_type_compressed">پرونده فشرده</string>
<string name="file_type_text">پرونده متنی</string>
<string name="file_type_ebook">رایاکتاب</string>
<string name="file_type_drawing">ظراحی</string>
<string name="file_type_drawing">طراحی</string>
<string name="file_type_form">فرم</string>
<string name="file_type_launcherbackup">%1$s پشتیبان</string>
<string name="file_type_launchertheme">%1$s پوسته</string>
<string name="file_type_generic">%1$s پرونده</string>
<string name="alert_delete_directory">پوشه %1$s و تمام محتوای آن برای همیشه پاک می شود.ادامه دهید؟</string>
<string name="alert_delete_directory">پوشه %1$s و تمام محتوای آن برای همیشه پاک می‌شود. ادامه دهید؟</string>
<string name="alert_delete_file">پرونده %1$s برای همیشه پاک می شود.ادامه دهید؟</string>
<string name="alert_delete_shortcut">میانبر %1$s برای همیشه پاک می شود.ادامه دهید؟</string>
<string name="default_websearch_2_url">https://www.youtube.com/results?search_query=${1}</string>
@ -79,20 +79,20 @@
<string name="websearch_dialog_delete_icon">پاک کردن نماد</string>
<string name="websearch_dialog_advanced">پیشرفته</string>
<string name="websearch_dialog_query_encoding">رمزنگاری</string>
<string name="websearch_dialog_query_encoding_url">Percent-encoding</string>
<string name="websearch_dialog_query_encoding_url">کدبندی درصدی</string>
<string name="websearch_dialog_query_encoding_form">application/x-www-form-urlencoded</string>
<string name="websearch_dialog_query_encoding_none">هیچکدام</string>
<string name="menu_edit_widgets">ویرایش ابزارک</string>
<string name="menu_edit_widgets">ویرایش ابزارکها</string>
<string name="widget_name_weather">آب و هوا</string>
<string name="widget_name_calendar">تقویم</string>
<string name="widget_name_music">موسیقی</string>
<string name="widget_name_favorites">مورد علاقه</string>
<string name="widget_name_favorites">دلخواه</string>
<string name="widget_name_notes">یادداشت</string>
<string name="widget_name_unknown">ابزارک برنامه ناشناخته</string>
<string name="widget_add_widget">افزودن ابزارک</string>
<string name="notes_widget_placeholder">چیزی بنویسید…</string>
<string name="notes_widget_action_new">یادداشت جدید</string>
<string name="notes_widget_action_save">نگه داشتن</string>
<string name="notes_widget_action_save">نگهداشتن</string>
<string name="notes_widget_export_filename">یادداشت_%1$s</string>
<string name="notes_widget_action_dismiss">نادیده گرفتن</string>
<string name="notes_widget_dismissed">یادداشت نادیده گرفته شد.</string>
@ -100,52 +100,52 @@
<string name="easter_egg_3">واسه آخرین بار میگم: هیچ چیز عجیب غریبی اینجا قایم نکردم</string>
<string name="easter_egg_text">تیتاب، من رو پیدا کردی. ارزشش رو داشت؟</string>
<string name="close">بستن</string>
<string name="save">نگه داشتن</string>
<string name="save">نگهداشتن</string>
<string name="duplicate">هم‌مانندسازی</string>
<string name="skip">ردکردن</string>
<string name="action_next">بعدی</string>
<string name="widget_action_remove">پاک کردن</string>
<string name="widget_action_remove">برداشتن</string>
<string name="widget_action_replace">جایگزینی</string>
<string name="menu_item_edit_favs">ویرایش مورد علاقه ها</string>
<string name="menu_item_edit_favs">ویرایش موارد دلخواه</string>
<string name="edit_favorites_dialog_pinned_unsorted">سنجاق شده - ترتیب خودکار</string>
<string name="edit_favorites_dialog_pinned_sorted">سنجاق شده - ترتیب دستی</string>
<string name="edit_favorites_dialog_empty_section">موارد را به اینجا بکشید</string>
<string name="edit_favorites_dialog_new_tag">ساختن برچسب…</string>
<string name="nextcloud_server_url">نشانی کارساز NextCloud</string>
<string name="edit_favorites_dialog_new_tag">ایجاد برچسب…</string>
<string name="nextcloud_server_url">نشانی کارساز Nextcloud</string>
<string name="nextcloud_server_url_empty">نشانی نمیتواند خالی باشد</string>
<string name="nextcloud_server_invalid_url">این نشانی به یه کارساز معتبر اشاره نمیکند</string>
<string name="owncloud_server_url">نشانی کارساز OwnCloud</string>
<string name="owncloud_server_url">نشانی کارساز Owncloud</string>
<string name="owncloud_password">گذرواژه</string>
<string name="owncloud_username">نام کاربری</string>
<string name="owncloud_login_failed">ورود ناموفق: نام کاربری یا گذرواژه نادرست.</string>
<string name="owncloud_username_empty">نام کاربری نمیتواند خالی باشد</string>
<string name="owncloud_password_empty">گذرواژه نمیتواند خالی باشد</string>
<string name="login_flow_continue">ادامه</string>
<string name="disclaimer">از سر باز کنی</string>
<string name="disclaimer">بیانیه انکار</string>
<string name="show_all">نمایش همه</string>
<string name="date_today">امروز</string>
<string name="date_tomorrow">فردا</string>
<string name="calendar_widget_pinned_events">به زودی</string>
<string name="calendar_widget_no_events">امروز کاری نداری</string>
<string name="calendar_event_allday">همه-روزها</string>
<string name="calendar_event_allday">کل-روز</string>
<string name="music_widget_default_title">%1$s در حال اجرا محتوا</string>
<string name="music_widget_no_data">محتوایی در حال اجرا نیست</string>
<string name="weather_condition_rain">بارانی</string>
<string name="weather_condition_fog">مه</string>
<string name="weather_condition_rainandthunder">باران و رعد</string>
<string name="weather_condition_partlycloudy">ابر های پراکنده</string>
<string name="weather_condition_heavysnowandthunder">برف شدید و رعد</string>
<string name="weather_condition_partlycloudy">ابری کم</string>
<string name="weather_condition_heavysnowandthunder">برف سنگین و رعد</string>
<string name="missing_permission_music_widget">دسترسی به آگاه‌ساز ها برای نمایش رسانه در حال پخش لازم است</string>
<string name="missing_permission_contact_search_settings">دسترسی مخاطبین برای جستجو در مخاطبین لازم است</string>
<string name="missing_permission_calendar_widget_settings">ابزارک نیاز به دسترسی تقویم دارد</string>
<string name="widget_config_calendar_missing_calendars_hint">تقویم تان را نمی‌یابید؟</string>
<string name="missing_permission_file_search_settings">دسترسی حافظه برای جستجو در پرونده های محلی لازم است</string>
<string name="missing_permission_file_search_settings_android10">دسترسی به کل حافظه برای جستجو در پرونده های محلی لازم است</string>
<string name="missing_permission_notification_badges">دسترسی به آگاه‌ساز ها برای نمایش نشان آگاهساز لازم است(دایره رنگی گوشه برنامه دارای آگاهساز)</string>
<string name="missing_permission_notification_badges">دسترسی به آگاه‌سازها برای نمایش نشان آگاهساز لازم است</string>
<string name="missing_permission_calendar_search">دسترسی تقویم را برای جستجو در تقویم بدهید.</string>
<string name="location_open">بازکردن</string>
<string name="edit_favorites_dialog_unpinned">سنجاق نشده - بیشتر بهره‌وری شده ها</string>
<string name="edit_favorites_dialog_tags">برچسب ها</string>
<string name="location_open">باز</string>
<string name="edit_favorites_dialog_unpinned">سنجاق نشده - بیشتر به‌کار گرفته شده‌ها</string>
<string name="edit_favorites_dialog_tags">برچسبها</string>
<string name="weather_condition_clearsky">آسمان صاف</string>
<string name="location_closes">بسته شده در %1$s</string>
<string name="menu_app_info">درباره برنامه</string>
@ -158,20 +158,676 @@
<string name="easter_egg_1">در اینجا هیچ شگفتانه ای وجود ندارد، دنبال نخود سیاه نگردید.</string>
<string name="login_flow_login">ورود</string>
<string name="weather_location_search_no_result">مکان یافت نشد.</string>
<string name="missing_permission_calendar_search_settings">دسترسی به تقویم برای جستجو در تقویم لازم است</string>
<string name="missing_permission_calendar_search_settings">دسترسی تقویم برای جستجو در تقویم لازم است</string>
<string name="missing_permission_appshortcuts_search_settings">%1$s برای جستجوی در میانبرهای برنامه لازم است به عنوان برنامه خانه انتخاب شود</string>
<string name="missing_permission_calendar_widget">برای نمایش فعالیت های پیش رو دسترسی تقویم بدهید.</string>
<string name="menu_remove">پاک کردن</string>
<string name="missing_permission_calendar_widget">دسترسی تقویم را برای نمایش فعالیت‌های پیش رو و رویدادها اینجا بدهید.</string>
<string name="menu_remove">برداشتن</string>
<string name="default_websearch_2_name">یوتیوب</string>
<string name="shortcut_summary">با %1$s</string>
<plurals name="calendar_widget_running_events">
<item quantity="one">+%1$d فعالیت در حال اجرا از روز های پیشین</item>
<item quantity="other">+%1$d فعالیت در حال اجرا از روز های پیشین</item>
<item quantity="one">+%1$d فعالیت در حال اجرا از روزهای پیشین</item>
<item quantity="other">+%1$d فعالیت در حال اجرا از روزهای پیشین</item>
</plurals>
<string name="file_meta_album">آلبوم: %1$s</string>
<string name="edit_favorites_dialog_tag_section_empty">برچسب های سنجاق شده اینجا نمایش داده میشوند</string>
<string name="edit_favorites_dialog_tag_section_empty">برچسبهای سنجاق شده اینجا نمایش داده میشوند</string>
<string name="owncloud_server_invalid_url">نشانی به یک کارساز معتبر اشاره نمیکند</string>
<string name="owncloud_login_2fa_hint">اگر ورود دو مرحله ای را روشن کرده‌اید باید از app password بهره ببرید.</string>
<string name="calendar_widget_next_events">فعالیت بعدی</string>
<string name="missing_permission_auto_location">دسترسی به مکان‌یاب برای یافتن خودکار مکان شما لازم است</string>
<string name="wind_west_north_west">غرب شمال غربی</string>
<string name="preference_category_location">مکان</string>
<string name="preference_cards_stroke_width">عرض خط دور</string>
<string name="preference_cards_opacity">شفافیت</string>
<string name="preference_cards_shape">شکل</string>
<string name="preference_cards_shape_cut">برش‌دار</string>
<string name="preference_icon_shape_square">مربع</string>
<string name="preference_icon_shape_platform">پیش‌گزیده سامانه</string>
<string name="preference_icon_shape">شکل</string>
<string name="weather_condition_lightrainandthunder">بارش باران سبک و رعد</string>
<string name="preference_icon_shape_squircle">squircle</string>
<string name="weather_condition_rainshowersandthunder">بارش باران و رعد</string>
<string name="weather_condition_sleet">باران و برف</string>
<string name="weather_condition_sleetandthunder">باران و برف و رعد</string>
<string name="weather_condition_lightssnowshowersandthunder">بارش برف سبک و رعد</string>
<string name="weather_condition_fair">معتدل</string>
<string name="preference_screen_plugins">افزونه‌ها</string>
<string name="missing_permission_call_contacts_settings">دسترسی تماس برای شروع تماس‌ها لازم است</string>
<string name="preference_version">نگارش</string>
<string name="preference_category_links">پیوندها</string>
<string name="preference_category_licenses">مجوزهای متن‌باز</string>
<string name="preference_icon_shape_rounded_square">مربع گرد</string>
<string name="preference_icon_shape_triangle">مثلث Reuleaux</string>
<string name="preference_icon_shape_circle">دایره</string>
<string name="preference_icon_shape_teardrop">قطره اشک</string>
<string name="preference_icon_shape_pebble">سنگریزه</string>
<string name="preference_dim_wallpaper_summary">در پوسته‌های تیره، پس‌زمینه را تیره کنید</string>
<string name="preference_owncloud_signin_summary">ورود به Owncloud برای جستجوی کارساز خود</string>
<string name="preference_about_telegram">گروه تلگرام</string>
<string name="preference_clockwidget_layout_vertical">پیش‌گزیده</string>
<string name="edit_search_action_title">ویرایش اقدام سریع</string>
<string name="create_search_action_pick_app">انتخاب یک برنامه برای جستجو:</string>
<string name="create_search_action_website_url">نشانی وبگاه را وارد کنید:</string>
<string name="preference_icon_pack_summary_empty">هیچ بسته نمادی نصب نشده است</string>
<string name="create_search_action_website_invalid_url">وبگاه داده شده نمی‌تواند به صورت خودکار به عنوان یک جستجوی وب وارد شود. شما می‌توانید وبگاه دیگری را امتحان کنید یا داده‌های مورد نیاز را به صورت دستی در مرحله بعدی وارد کنید.</string>
<string name="gesture_action_lock_screen">خاموش کردن صفحه</string>
<string name="poi_category_fitness_center">مرکز تناسب اندام</string>
<string name="poi_category_amusement_park">پارک تفریحی</string>
<string name="wikipedia_url">https://en.wikipedia.org</string>
<string name="provider_openweathermap">نقشه آزاد آب و هوا(OpenWeatherMap)</string>
<string name="provider_brightsky">خدمات هواشناسی آلمان (فقط آلمان)</string>
<string name="provider_here">Here</string>
<string name="preference_debug_dump_heap">تصویر حافظه</string>
<string name="preference_icon_shape_hexagon">شش‌ضلعی</string>
<string name="preference_category_searchbar">نوار جستجو</string>
<string name="preference_screen_integrations">یکپارچه‌سازی‌ها</string>
<string name="preference_force_themed_icons">اجباری کردن نماد‌های پوسته‌دار</string>
<string name="departure_time_now">اینک</string>
<string name="preference_force_themed_icons_summary">به‌کارگیری طرح رنگی برنامه برای کل نماد‌ها، شامل آن‌هایی که پشتیبانی نمی‌شوند (توصیه نمی‌شود)</string>
<string name="preference_search_gdrive_summary">جستجوی فایل‌های %1$s در گوگل درایو</string>
<string name="preference_search_bar_auto_focus">بازکردن صفحه‌کلید</string>
<string name="icon_picker_search_icon">جستجو در بسته‌های نماد</string>
<string name="icon_picker_no_packs_installed">هیچ بسته نمادی نصب نشده است</string>
<string name="grant_permission">اجازه دادن</string>
<string name="preference_screen_appearance">ظاهر</string>
<string name="preference_theme_light">روشن</string>
<string name="preference_theme_dark">تیره</string>
<string name="preference_screen_colors">طرح رنگی</string>
<string name="preference_colors_default">پیش‌گزیده</string>
<string name="preference_colors_bw">سیاه و سفید</string>
<string name="preference_custom_colors_corepalette">تخته رنگ</string>
<string name="preference_font">قلم</string>
<string name="preference_font_system">قلم پیش‌گزیده سامانه</string>
<string name="preference_screen_about">درباره</string>
<string name="preference_category_advanced">پیشرفته</string>
<string name="open_webpage">بازکردن وبگاه</string>
<string name="preference_screen_about_summary">درباره برنامه و مجوزها</string>
<string name="preference_screen_appearance_summary">سفارشی‌سازی ظاهر</string>
<string name="preference_weather_provider">ارائه‌دهنده</string>
<string name="preference_automatic_location">مکان خودکار</string>
<string name="preference_charging_animation_summary">هنگام شارژ دستگاه، پویانمایی حباب نمایش داده میشود</string>
<string name="preference_notification_badges_summary">نمایش نشان برای برنامه‌هایی که آگاه‌ساز خوانده‌نشده دارند</string>
<string name="preference_enforce_icon_shape_summary">به‌کارگیری شکل برای تمام نمادها شامل آن‌هایی که معمولاً از آن پشتیبانی نمی‌کنند</string>
<string name="preference_about_license_summary">تحت مجوز عمومی GNU نگارش 3.0 منتشر شده است</string>
<string name="preference_category_grid">شبکه</string>
<string name="preference_grid_icon_size">اندازه نماد</string>
<string name="preference_clockwidget_alarm_part">آلارم‌ها</string>
<string name="preference_search_contacts_summary">جستجوی مخاطبین روی این دستگاه</string>
<string name="preference_search_appshortcuts_summary">جستجوی میانبرهای برنامه</string>
<string name="preference_search_cloud_summary">جستجوی پرونده‌های %1$s</string>
<string name="preference_search_bar_launch_on_enter">اجرایی شدن با ورود</string>
<string name="preference_search_bar_launch_on_enter_summary">اجرای مورد برجسته یا کنش سریع هنگام فشار دادن دکمه \'برو\'</string>
<string name="preference_hidden_items">یافته‌های جستجوی مخفی</string>
<string name="preference_hidden_items_summary">مدیریت برنامه‌ها و یافته‌های جستجوی مخفی</string>
<string name="preference_screen_homescreen_summary">ساعت، نوار جستجو، تصویر زمینه، نوارهای سامانه</string>
<string name="restore_invalid_file">گمان میرود پرونده انتخاب‌شده یک پشتیبان نباشد. آیا باور دارید پرونده درست را انتخاب کرده‌اید؟</string>
<string name="note_widget_file_read_error_description">پرونده پیوند داده‌شده را نمی‌توان خواند. گمان میرود جابجا یا پاک‌شده است. یک نگارش از حافظه داخلی برنامه راه‌انداز بازیابی شده است. اگر یادداشت را ویرایش کنید، پرونده پیوند داده‌شده احتمالاً بازنویسی خواهد شد.</string>
<string name="theme_color_scheme_custom_color">سفارشی</string>
<string name="shortcut_unavailable_description">این میانبر به دلیل اینکه %1$s در جانگاه راه‌انداز پیش‌گزیده انتخاب نشده غیرفعال است</string>
<string name="customize_item_label">برچسب</string>
<string name="preference_category_animations">پویانمایی‌ها</string>
<string name="preference_charging_animation">پویانمایی شارژ</string>
<string name="customize_item_visibility">نمایش در</string>
<string name="preference_nav_bar_icons">نمادهای نوار ناوبری</string>
<string name="preference_system_bar_icons_auto">خودکار</string>
<string name="preference_system_bar_icons_light">روشن</string>
<string name="preference_system_bar_icons_dark">تیره</string>
<string name="preference_hide_status_bar">مخفی کردن نوار وضعیت</string>
<string name="preference_google">گوگل</string>
<string name="preference_hide_nav_bar">پنهان کردن نوار ناوبری</string>
<string name="weather_condition_heavyrainshowersandthunder">بارش دوره‌ای سنگین باران و رعد</string>
<string name="preference_blur_wallpaper">محو کردن پس‌زمینه</string>
<string name="preference_cloud_badges_summary">نمایش نشان برای پرونده‌هایی که در ابر نگه‌داری شده‌اند</string>
<string name="preference_category_badges">نشان‌ها</string>
<string name="preference_notification_badges">نشان‌های آگاه‌ساز</string>
<string name="preference_enforce_icon_shape">اجبار شکل</string>
<string name="preference_about_fdroid">مخزن F-Droid</string>
<string name="preference_category_license">مجوز</string>
<string name="preference_about_license">این برنامه نرم‌افزار آزاد است.</string>
<string name="preference_screen_weatherwidget">آب و هوا</string>
<string name="preference_category_media_apps">برنامه‌های رسانه‌ای</string>
<string name="preference_grid_column_count">تعداد ستون‌ها</string>
<string name="preference_screen_clockwidget">ساعت</string>
<string name="preference_screen_clockwidget_summary">پیکربندی سبک ساعت و اجزای آن</string>
<string name="preference_clockwidget_music_part">رسانه</string>
<string name="preference_clockwidget_music_part_summary">هنگامی که جلسه رسانه‌ای فعال است، نمایش کنترل‌های رسانه‌ای</string>
<string name="preference_clockwidget_battery_part">باتری</string>
<string name="preference_clockwidget_battery_part_summary">نمایش سطح کنونی باتری هنگامی که باتری ضعیف است یا در حال شارژ است</string>
<string name="preference_search_calendar">تقویم</string>
<string name="preference_search_calendar_summary">جستجوی تعهدات و رویدادهای آینده</string>
<string name="preference_search_localfiles_summary">جستجوی اسناد، عکس‌ها و سایر فایل‌های ذخیره‌شده روی این دستگاه</string>
<string name="preference_crash_reporter">گزارش‌دهنده خرابی</string>
<string name="preference_search_files_summary">جستجوی پرونده‌های محلی و ابری</string>
<string name="preference_search_contacts">مخاطبین</string>
<string name="preference_search_owncloud">اون‌کلود</string>
<string name="preference_widgets_edit_button_summary">نمایش دکمه‌ای برای افزودن، پاک‌کردن و بازچینی ابزارک‌ها</string>
<string name="preference_contacts_call_on_tap">تماس با لمس</string>
<string name="search_action_timer">شروع زمان‌سنج</string>
<string name="search_action_share">هم‌رسانی</string>
<string name="preference_contacts_call_on_tap_summary">شروع مستقیم تماس‌ها با لمس شماره‌های تلفن</string>
<string name="search_action_websearch_url">قالب URL</string>
<string name="preference_layout_search_results">چیدمان یافته‌های جستجو</string>
<string name="tag_no_items_message">هیچ موردی به این برچسب واگذار نشده. اگر ادامه دهید، برچسب پاک خواهد شد.</string>
<string name="search_results_order_top_down">از بالا به پایین</string>
<string name="widget_config_appwidget_borderless">بدون حاشیه</string>
<string name="widget_config_appwidget_background">کارت دارای پس‌زمینه</string>
<string name="app_label_locked_profile">قفل شده</string>
<string name="preference_search_locations_theme_map">پوسته نقشه</string>
<string name="plugin_weather_provider_enable">تنظیم به عنوان ارائه‌دهنده آب و هوا</string>
<string name="plugin_weather_provider_enabled">در حال حاضر به عنوان ارائه‌دهنده آب و هوا تنظیم شده است</string>
<string name="location_open_24_7">باز ۲۴/۷</string>
<string name="clock_style_binary">دودویی</string>
<string name="clock_style_analog">عقربه‌ای</string>
<string name="poi_category_car_rental">اجاره ماشین</string>
<string name="preference_filter_bar_summary">نمایش پالایش‌های سریع بالای صفحه‌کلید</string>
<string name="poi_category_hairdresser">آرایشگاه</string>
<string name="clock_style_segment">۷-بخشی</string>
<string name="search_filter_tools">ابزارها</string>
<string name="poi_category_museum">موزه</string>
<string name="poi_category_chinese_restaurant">رستوران چینی</string>
<string name="poi_category_american_football">زمین فوتبال آمریکایی</string>
<string name="weather_forecast_humidity">رطوبت</string>
<string name="disclaimer_currency_converter">نرخ ارز منتشر شده روزانه توسط بانک مرکزی اروپا. تمامی داده‌ها «به همان صورت» ارائه شده و بدون هیچ گونه ضمانتی است. مسئولیتی در قبال این اطلاعات پذیرفته نمی‌شود.\n\nآخرین به‌روزرسانی: %1$s</string>
<string name="preference_search_local_calendar_summary">جستجوی تقویم‌ها روی این دستگاه</string>
<string name="preference_google_signin">ورود با گوگل</string>
<string name="preference_search_bar_auto_focus_summary">به شیوه خودکار هنگام بازکردن کشوی برنامه‌ها، صفحه‌کلید نمایش داده شود</string>
<string name="preference_screen_icons">شبکه و نمادها</string>
<string name="preference_screen_icons_summary">شبکه، اندازه نماد، بسته‌های نماد، نشان‌ها</string>
<string name="preference_category_accounts">حساب‌ها</string>
<string name="restore_different_minor_version">این پشتیبان با نسخه متفاوتی از %1$s ایجاد شده است. ممکن است برخی از داده‌ها به درستی بازیابی نشوند.</string>
<string name="restore_incompatible_file">این پشتیبان با نگارشی ناهمسان با %1$s ساخته شده و نمی‌تواند با این نگارش بازیابی شود.</string>
<string name="search_action_label">نام</string>
<string name="search_action_app">برنامه</string>
<string name="icon_picker_suggestions">پیشنهادات</string>
<string name="tag_select_items">انتخاب موارد:</string>
<string name="search_action_websearch_url_hint">قالب نشانی وبی که برای ساخت نشانی وب جستجو به‌کار میرود. ${1} را به عنوان جایگزینی برای عبارت واقعی جستجو بنویسید، مثال: https://google.com/search?q=${1}.</string>
<string name="search_bar_position_top">بالا</string>
<string name="search_bar_position_bottom">پایین</string>
<string name="preference_search_result_ordering_weight_factor_low">پایدار</string>
<string name="preference_search_result_ordering_weight_factor_default">متوازن</string>
<string name="preference_search_result_ordering_alphabetic">الفبایی</string>
<string name="preference_search_result_ordering_weighted">بر اساس استفاده</string>
<string name="preference_search_result_ordering_weight_factor">انعطاف‌پذیری رتبه‌بندی</string>
<string name="widget_config_appwidget_configure">تنظیم ابزارک</string>
<string name="widget_config_appwidget_resize">تغییر اندازه</string>
<string name="app_widget_loading_failed">بارگذاری ابزارک برنامه با شکست مواجه شد.</string>
<string name="widget_config_music_integration_settings">تنظیمات یکپارچگی کنترل رسانه</string>
<string name="note_widget_link_file">پیوند به پرونده</string>
<string name="note_widget_linked_file_summary">پیوند داده شده به %1$s</string>
<string name="note_widget_conflict_keep_selected">نگه‌داشتن انتخاب شدگان</string>
<string name="note_widget_conflict_description">پرونده پیوند داده شده خالی نیست و محتوای آن با آخرین نگارش نگه‌داری شده این یادداشت همخوانی ندارد. کدام نگارش را می‌خواهید نگه دارید؟</string>
<string name="note_widget_conflict_file_version">محتوای فعلی پرونده:</string>
<string name="note_widget_file_read_error">خطا در خواندن پرونده پیوند داده شده</string>
<string name="note_widget_file_write_error">خطا در نگه‌داری یادداشت</string>
<string name="note_widget_file_write_error_description">یادداشت نتوانست به پرونده پیوند داده‌شده نوشته شود. گمان می‌رود جابجا یا پاک‌شده باشد. یک نگارش در حافظه داخلی برنامه راه‌انداز نگه‌داری شده است.</string>
<string name="theme_color_scheme_autogenerate">از رنگ اصلی</string>
<string name="theme_color_scheme_palette_color">پالت</string>
<string name="imperial">امپراتوری</string>
<string name="preference_search_location_custom_overpass_url">نشانی Overpass</string>
<string name="preference_search_locations_hide_uncategorized">پنهان‌کردن مکان‌های طبقه‌بندی نشده</string>
<string name="preference_search_locations_hide_uncategorized_summary">تنها یافته‌های در دسته‌بندی انتخابی را نمایش دهید، مانند کافه‌ها یا رستوران‌ها</string>
<string name="preference_search_locations_show_map">نقشه</string>
<string name="preference_search_locations_show_map_summary">نمایش پیش‌نمایش نقشه برای مکان‌ها</string>
<string name="preference_search_locations_theme_map_summary">اعمال طرح رنگی برنامه راه‌انداز به نقشه</string>
<string name="preference_search_location_custom_tile_server_url">URL سرور کاشی</string>
<string name="menu_dial">تماس</string>
<string name="menu_navigation">مسیریابی</string>
<string name="menu_bugreport">گزارش ایراد</string>
<string name="plugin_action_setup">راه‌اندازی</string>
<string name="poi_category_convenience">فروشگاه خواروبار</string>
<string name="clock_style_custom">ابزارک سفارشی</string>
<string name="clock_style_orbit">مدار</string>
<string name="clock_style_empty">بدون ساعت</string>
<string name="poi_category_university">دانشگاه</string>
<string name="poi_category_clothes">فروشگاه پوشاک</string>
<string name="poi_category_pizza_restaurant">رستوران پیتزا</string>
<string name="poi_category_burger_restaurant">رستوران همبرگر</string>
<string name="poi_category_church">کلیسا</string>
<string name="poi_category_mosque">مسجد</string>
<string name="poi_category_place_of_worship">عبادتگاه</string>
<string name="poi_category_buddhist_temple">معبد بودایی</string>
<string name="poi_category_hindu_temple">معبد هندو</string>
<string name="poi_category_swimming">استخر شنا</string>
<string name="item_visibility_hidden">هرگز</string>
<string name="contact_info_home">خانه</string>
<string name="poi_category_kebab_restaurant">رستوران کباب</string>
<string name="poi_category_asian_restaurant">رستوران آسیایی</string>
<string name="poi_category_soup_restaurant">رستوران سوپ</string>
<string name="contact_info_mobile">تلفن همراه</string>
<string name="contact_info_work">کار</string>
<plurals name="contact_phone_numbers">
<item quantity="one">%1$s شماره تلفن</item>
<item quantity="other">%1$s شماره تلفن</item>
</plurals>
<plurals name="contact_email_addresses">
<item quantity="one">%1$s رایانامه</item>
<item quantity="other">%1$s رایانامه</item>
</plurals>
<plurals name="app_info_notifications">
<item quantity="one">%1$s آگاه‌ساز</item>
<item quantity="other">%1$s آگاه‌ساز</item>
</plurals>
<string name="music_widget_next_track">عنوان بعدی</string>
<string name="music_widget_play">پخش</string>
<string name="music_widget_pause">ایست</string>
<string name="music_widget_open_player">بازکردن پخش‌کننده</string>
<string name="calendar_widget_previous_day">روز پیشین</string>
<string name="reset_icon">بازنشانی نماد</string>
<string name="calendar_widget_next_day">روز پسین</string>
<string name="calendar_widget_create_event">ایجاد رویداد جدید</string>
<string name="profile_private_profile_action_unlock">بازکردن</string>
<string name="profile_private_profile_action_lock">قفل کردن فضای شخصی</string>
<string name="hint_drag_and_drop_reorder">نگه‌داشتن و درگ کردن موارد برای بازچینی آن‌ها</string>
<string name="weather_forecast_wind">باد</string>
<string name="weather_forecast_precipitation">بارندگی</string>
<plurals name="departure_time_in">
<item quantity="one">در یک دقیقه</item>
<item quantity="other">در %1$d دقیقه</item>
</plurals>
<plurals name="calendar_search_enabled_lists">
<item quantity="one">%1$s فهرست انتخاب‌شده</item>
<item quantity="other">%1$s فهرست انتخاب‌شده</item>
</plurals>
<string name="task_due_time">سر رسید در %1$s</string>
<string name="weather_condition_snowandthunder">برف و رعد</string>
<string name="weather_condition_heavysnow">برف سنگین</string>
<string name="weather_condition_cloudy">ابری</string>
<string name="task_due_today">وظیفه امروز</string>
<string name="task_due_date">تا تاریخ %1$s</string>
<string name="weather_condition_heavysleetshowers">بارش دوره‌ای سنگین باران و برف</string>
<string name="weather_condition_snow">برفی</string>
<string name="weather_condition_heavyrainshowers">بارش دوره‌ای سنگین باران</string>
<string name="weather_condition_lightsleetandthunder">بارش سبک باران و برف و رعد</string>
<string name="weather_condition_heavysnowshowersandthunder">بارش سنگین برف و رعد</string>
<string name="weather_widget_set_location">تنظیم مکان</string>
<string name="preference_automatic_location_summary">به‌کار بردن GPS و خدمات مکان‌یابی برای تعیین مکان به‌صورت خودکار</string>
<string name="preference_location_managed">مدیریت شده توسط افزونه</string>
<string name="preference_location_managed_summary">مکان برای این ارائه‌دهنده توسط برنامه افزونه مدیریت می‌شود</string>
<string name="preference_location">مکان</string>
<string name="preference_category_debug">اشکال‌زدایی</string>
<string name="preference_category_debug_tools">ابزارها</string>
<string name="preference_imperial_units_summary">به‌کار بردن درجه فارنهایت و مایل بر ساعت</string>
<string name="preference_imperial_units">واحدهای امپریال</string>
<string name="widget_config_weather_compact">حالت فشرده</string>
<string name="preference_debug_dump_heap_summary">ثبت یک نمونه برای تحلیل مصرف حافظه. برنامه تا پایان این فرآیند متوقف می‌شود.</string>
<string name="preference_debug_dump_heap_in_progress">در حال گرفتن نگارش پشتیبان…</string>
<string name="preference_debug_cleanup_database">پاکسازی پایگاه داده</string>
<string name="preference_debug_cleanup_database_summary">پاک‌کردن ورودی‌های خراب و به‌کار نرفته از پایگاه داده برنامه</string>
<string name="preference_debug_reinstall_iconpacks">نصب دوباره بسته نمادها</string>
<string name="preference_debug_reinstall_iconpacks_summary">پاک کردن و بازسازی حافظه نهان بسته‌های نماد</string>
<string name="preference_category_icons">نمادها</string>
<string name="preference_cards">کارت‌ها</string>
<string name="preference_cards_summary">سفارشی‌سازی ظاهر کارت‌ها</string>
<string name="preference_cards_corner_radius">شعاع گوشه</string>
<string name="preference_cards_shape_rounded">گرد</string>
<string name="preference_signin_user">به عنوان %1$s وارد شده‌اید</string>
<string name="preference_signout">خروج</string>
<string name="preference_screen_integrations_summary">مدیریت حساب‌ها و خدمات متصل‌شده</string>
<string name="preference_icon_shape_pentagon">پنج‌ضلعی</string>
<string name="preference_category_wallpaper">پس‌زمینه</string>
<string name="preference_dim_wallpaper">کم‌رنگ کردن پس‌زمینه</string>
<string name="preference_summary_not_logged_in">شما هنوز وارد نشده‌اید</string>
<string name="preference_blur_wallpaper_summary">به‌کارگیری اثر محو بر روی پس‌زمینه</string>
<string name="preference_blur_wallpaper_unsupported">در این دستگاه پشتیبانی نمی‌شود</string>
<string name="preference_blur_wallpaper_radius">شعاع محو</string>
<string name="preference_wallpaper_summary">انتخاب تصویر زمینه</string>
<string name="preference_suspended_badges_summary">نمایش نشان برای برنامه‌های معلق</string>
<string name="preference_suspended_badges">برنامه‌های معلق</string>
<string name="preference_cloud_badges">نشان‌های ابری</string>
<string name="preference_shortcut_badges">نشان‌های میانبر</string>
<string name="preference_shortcut_badges_summary">نمایش نشان برای میانبرها</string>
<string name="preference_plugin_badges">نشان‌های افزونه</string>
<string name="preference_plugin_badges_summary">نشان دادن اینکه یک نتیجه جستجو توسط کدام افزونه ایجاد شده است</string>
<string name="preference_nextcloud_signin">ورود به نکست‌کلود</string>
<string name="preference_nextcloud_signin_summary">ورود برای جستجو در سرور نکست‌کلود خود</string>
<string name="preference_screen_debug">اشکال‌زدایی</string>
<string name="preference_screen_debug_summary">ابزارهای عیب‌یابی</string>
<string name="preference_category_widgets">ابزارک‌ها</string>
<string name="preference_grid_labels">نمایش برچسب‌ها</string>
<string name="preference_grid_labels_summary">نمایش نام برنامه زیر نماد</string>
<string name="preference_screen_backup">پشتیبان‌گیری و بازیابی</string>
<string name="preference_screen_backup_summary">صادرات و واردات داده‌های راه‌انداز</string>
<string name="preference_backup">پشتیبان‌گیری</string>
<string name="preference_restore">بازیابی</string>
<string name="preference_clockwidget_alarm_part_summary">نمایش آلارم‌هایی که در ۸ ساعت آینده به صدا درمی‌آیند</string>
<string name="preference_crash_reporter_summary">گزارش‌های خطا و خرابی</string>
<string name="preference_logs">گزارش‌ها</string>
<string name="preference_logs_summary">مشاهده و صدور گزارش‌های برنامه</string>
<string name="preference_calendar_hide_completed">پنهان کردن وظیفه‌های انجام‌شده</string>
<string name="preference_search_bar_style_summary">سفارشی کردن ظاهر نوار جستجو</string>
<string name="restore_meta">ساخته شده %1$s در %2$s با %3$s.</string>
<string name="open_search_swipe_right">کشیدن به راست</string>
<string name="tag_empty_name">یک برچسب بدون نام نمی‌توانید داشته باشید. اگر ادامه دهید، برچسب پاک خواهد شد.</string>
<string name="tag_name">نام برچسب</string>
<string name="preference_layout_open_search">جستجو / کشوی برنامه‌ها</string>
<string name="open_search_pull_down">کشیدن به پایین</string>
<string name="open_search_swipe_left">کشیدن به چپ</string>
<string name="gesture_action_power_menu">نمایش گزینه‌های نیرو</string>
<string name="gesture_action_recents">بازکردن برنامه‌های اخیر</string>
<string name="gesture_failed_message">شما یک حرکت \"%1$s\" انجام داده‌اید. این حرکت هم برای اجرای کنش \"%2$s\" تنظیم شده است. با این حال، کنش دلخواه به دلیل زیر قابل اجرا نبود:</string>
<string name="preference_search_result_ordering">ترتیب یافته‌های جستجو</string>
<string name="missing_permission_accessibility_gesture_failed">خدمات دسترسی برنامه راه‌انداز باید روشن باشد تا این کار انجام شود.</string>
<string name="note_widget_action_relink_file">بازپیوندی</string>
<string name="note_widget_conflict">ناسازگاری</string>
<string name="note_widget_conflict_action_resolve">حل کردن</string>
<string name="note_widget_conflict_local_version">آخرین نگارش نگه‌داری شده:</string>
<string name="preference_screen_gestures">حرکات</string>
<string name="preference_screen_gestures_summary">حرکات و اقدامات حرکتی</string>
<string name="preference_gesture_swipe_down">کشیدن به پایین</string>
<string name="preference_gesture_swipe_left">کشیدن به چپ</string>
<string name="preference_gesture_swipe_right">کشیدن به راست</string>
<string name="confirmation_delete_color_scheme">آیا واقعاً می‌خواهید طرح رنگی %1$s را پاک کنید؟</string>
<string name="new_color_scheme_name">طرح رنگی جدید</string>
<string name="theme_color_scheme_system_default">به‌کار بردن پیش‌گزیده سامانه</string>
<string name="gesture_action_open_search">بازکردن جستجو</string>
<string name="gesture_action_launch_app">اجرای برنامه</string>
<string name="gesture_action_notifications">بازکردن بخش آگاه‌سازی</string>
<string name="plugin_type_filesearch">جستجوی پرونده</string>
<string name="plugin_type_weather">ارائه‌دهنده آب و هوا</string>
<string name="preference_default_filter_summary">سفارشی کردن پالایش پیش‌گزیده برای جستجوها</string>
<string name="widget_config_weather_integration_settings">تنظیمات یکپارچگی آب و هوا</string>
<string name="widget_config_calendar_no_calendars">هیچ تقویمی پیدا نشد</string>
<string name="widget_pick_widget">انتخاب ابزارک</string>
<string name="poi_category_parking">پارکینگ</string>
<string name="poi_category_fuel">پمپ بنزین</string>
<string name="filter_settings_network_warning">پالایش کنونی به شیوه پیش‌گزیده یافته‌های برخط را روشن می‌کند. جستجوی شما شاید ناخواسته به خدمات وب خارجی فرستاده شوند. به دلایل حریم خصوصی، این کار توصیه نمی‌شود.</string>
<string name="preference_search_osm_locations_summary">جستجوی اوپن‌استریت‌مپ برای فروشگاه‌ها و مکان‌های دیگر در ناحیه بومی</string>
<string name="preference_search_locations_radius_large_radius_warning">شعاع جستجوی بزرگ می‌تواند به شکل قابل توجهی سرعت جستجو را کاهش دهد.</string>
<string name="calendar_widget_open_calendar">بازکردن برنامه تقویم</string>
<string name="profile_work_profile_state_locked">برنامه‌های کاری متوقف شده‌اند.</string>
<string name="profile_work_profile_action_unlock">بازگشایی</string>
<string name="profile_work_profile_action_lock">متوقف کردن برنامه‌های کاری</string>
<string name="profile_private_profile_state_locked">فضای شخصی قفل شده است.</string>
<string name="shortcut_label_unavailable">غیرقابل دسترس</string>
<string name="preference_restore_default">بازگردانی به پیش‌گزیده</string>
<string name="import_theme_apply">اعمال پوسته</string>
<string name="import_theme_error">پرونده انتخاب‌شده قابل خواندن نیست. لطفاً مطمئن شوید که یک پرونده پوسته معتبر (*.kvtheme) انتخاب کرده‌اید و پرونده خراب نیست.</string>
<string name="preference_plugin_enable">روشن‌کردن پلاگین</string>
<string name="length_unit">واحد طول</string>
<string name="metric">متریک</string>
<string name="plugin_state_error">این افزونه به‌درستی کار نمی‌کند</string>
<string name="plugin_state_setup_required">در آغاز باید این افزونه را تنظیم کنید</string>
<string name="unavailable_searchable">این مورد دیگر وجود ندارد.</string>
<string name="preference_search_bar_separate_work_profile">جدا کردن برنامه‌های پروپرونده کاری</string>
<string name="preference_search_bar_separate_work_profile_summary">نمایش برنامه‌های پروپرونده کاری در یک برگه جدا</string>
<string name="clock_style_digital1">برجسته</string>
<string name="clock_style_digital2">ساده</string>
<string name="poi_category_fast_food">فست‌فود</string>
<string name="poi_category_restaurant">رستوران</string>
<string name="poi_category_supermarket">سوپرمارکت</string>
<string name="poi_category_school">مدرسه</string>
<string name="poi_category_cafe">کافه</string>
<string name="poi_category_hotel">هتل</string>
<string name="poi_category_pharmacy">داروخانه</string>
<string name="poi_category_hospital">بیمارستان</string>
<string name="poi_category_post_office">دفتر پست</string>
<string name="poi_category_jewelry">جواهرفروشی</string>
<string name="poi_category_laundry">صباغی</string>
<string name="poi_category_bank">بانک</string>
<string name="poi_category_atm">خودپرداز</string>
<string name="poi_category_kiosk">کیوسک</string>
<string name="poi_category_basketball">زمین بسکتبال</string>
<string name="poi_category_tennis">زمین تنیس</string>
<string name="poi_category_monument">یادبود</string>
<string name="item_visibility_calendar_default">ابزارک و یافته‌های جستجوی تقویم</string>
<string name="item_visibility_search_only">یافته‌های جستجو</string>
<string name="customize_item_tags">برچسب‌ها</string>
<string name="item_visibility_app_default">شبکه برنامه‌ها و نتایج جستجو</string>
<string name="plugin_type_calendar">تقویم</string>
<string name="poi_category_bar">کاباره</string>
<plurals name="battery_part_remaining_charge_time">
<item quantity="one">در %1$s دقیقه کامل میشود</item>
<item quantity="other">در %1$s دقیقه کامل میشود</item>
</plurals>
<string name="weather_widget_no_provider">هیچ ارائه‌دهنده آب‌وهوایی انتخاب نشده یا ارائه‌دهنده انتخاب‌شده در دسترس نیست</string>
<string name="music_widget_previous_track">عنوان قبلی</string>
<string name="wind_south_south_west">جنوب جنوب غربی</string>
<string name="wind_south_west">جنوب غربی</string>
<string name="wind_west_south_west">غرب جنوب غربی</string>
<string name="wind_west">غرب</string>
<string name="weather_condition_hail">طوفان برف</string>
<string name="weather_condition_thunderstorm">رعد و برق</string>
<string name="weather_condition_heavysnowshowers">بارش سنگین برف</string>
<string name="weather_condition_snowshowersandthunder">بارش برف و رعد</string>
<string name="weather_condition_lightsleetshowers">بارش دوره‌ای سبک باران و برف</string>
<string name="missing_permission_location_search">دسترسی موقعیت را برای جستجوی مکان‌های نزدیک بدهید.</string>
<string name="preference_icon_pack">بسته نماد</string>
<string name="preference_search_appshortcuts">میانبرهای برنامه</string>
<string name="preference_search_calculator">ماشین‌حساب</string>
<string name="preference_compact_tags">برچسب‌های فشرده</string>
<string name="preference_compact_tags_summary">مخفی کردن برچسب‌ها یا نمادها برای کاهش فضای اشغال‌شده توسط برچسب‌ها</string>
<string name="preference_screen_homescreen">صفحه خانگی</string>
<string name="backup_complete">پشتیبان‌گیری تکمیل شد.</string>
<string name="icon_picker_default_icon">پیش‌گزیده</string>
<string name="restore_complete">پشتیبان بازیابی شد.</string>
<string name="icon_picker_title">انتخاب نماد</string>
<string name="apps_profile_work">کار</string>
<string name="icon_picker_filter_all_packs">همه بسته‌های نماد</string>
<string name="apps_profile_main">شخصی</string>
<string name="create_app_shortcut">ساخت میانبر</string>
<string name="frequently_used_rows">تعداد ردیف‌ها</string>
<string name="search_action_call">تماس</string>
<string name="action_done">انجام شده</string>
<string name="action_more_actions">کنش‌های بیشتر</string>
<string name="wind_north_west">شمال غربی</string>
<string name="preference_layout_search_bar_position">جایگاه نوار جستجو</string>
<string name="action_clear">پاک‌سازی</string>
<string name="wind_north_north_west">شمال شمال غربی</string>
<string name="wikipedia_source">از ویکی‌پدیا</string>
<string name="preference_layout_fixed_search_bar">نوار جستجوی ثابت</string>
<string name="preference_layout_fixed_search_bar_summary">نوار جستجو را بیرون از دیدره پیمایش نکنید</string>
<string name="preference_layout_fixed_rotation">چرخش ثابت صفحه</string>
<string name="preference_layout_fixed_rotation_summary">قفل کردن چرخش صفحه در حالت عمودی</string>
<string name="preference_gesture_long_press">نگه‌داشتن</string>
<string name="preference_gesture_home_button">دکمه/حرکت خانه</string>
<string name="gesture_action_none">کاری نکن</string>
<string name="note_widget_link_file_summary">هماهنگ‌سازی محتوای این ابزارک با یک پرونده خارجی</string>
<string name="note_widget_action_unlink_file">رد پیوند</string>
<string name="poi_category_car_sharing">هم‌رسانی ماشین</string>
<string name="poi_category_mobile_phone">فروشگاه تلفن همراه</string>
<string name="poi_category_mall">مرکز خرید</string>
<string name="poi_category_furniture">فروشگاه مبلمان</string>
<string name="poi_category_alcohol">فروشگاه مشروبات الکلی</string>
<string name="poi_category_florist">گل‌فروشی</string>
<plurals name="contact_postal_addresses">
<item quantity="one">%1$s نشانی</item>
<item quantity="other">%1$s نشانی</item>
</plurals>
<string name="preference_mdy_color_source">منبع برای رنگ‌های پویا</string>
<string name="preference_mdy_color_source_system">سامانه</string>
<string name="preference_mdy_color_source_wallpaper">تصویر زمینه</string>
<string name="preference_screen_plugins_summary">مدیریت افزونه‌های نصب‌شده</string>
<string name="no_plugins_installed">هیچ افزونه‌ای نصب نشده است</string>
<string name="preference_theme_system">دنبال کردن سامانه</string>
<string name="preference_themed_icons">نمادهای پوسته‌دار</string>
<string name="preference_themed_icons_summary">رنگ‌آمیزی نمادها با طرح رنگی برنامه</string>
<string name="preference_category_system_bars">نوارهای سامانه</string>
<string name="preference_status_bar_icons">نمادهای نوار وضعیت</string>
<string name="preference_owncloud">اون‌کلود (Owncloud)</string>
<string name="preference_owncloud_signin">ورود به Owncloud</string>
<string name="preference_nextcloud">نکست‌کلود (Nextcloud)</string>
<string name="preference_clockwidget_layout_horizontal">فشرده</string>
<string name="preference_clock_widget_show_seconds">نمایش ثانیه‌ها</string>
<string name="preference_clockwidget_date_part">تاریخ</string>
<string name="preference_screen_search">جستجو</string>
<string name="preference_screen_search_summary">جستجو، برچسب‌ها، موارد مخفی</string>
<string name="preference_search_favorites">موارد دلخواه</string>
<string name="preference_search_favorites_summary">نمایش موارد دلخواه و مورد استفاده بالا بالای شبکه برنامه‌ها</string>
<string name="preference_search_files">پرونده‌ها</string>
<string name="preference_search_unitconverter_summary">کاربرد: ۱.۵ کیلوگرم یا ۴ سانتی‌متر &gt;&gt; اینچ</string>
<string name="preference_search_currencyconverter">تبدیل ارز</string>
<string name="preference_search_wikipedia">ویکی‌پدیا</string>
<string name="preference_search_calculator_summary">محاسبه عبارات ریاضی</string>
<string name="preference_search_unitconverter">تبدیل واحد</string>
<string name="preference_search_nextcloud">نکست‌کلود</string>
<string name="preference_edit_button">گزینه ویرایش</string>
<string name="no_account_nextcloud">هنوز حساب نکست‌کلود را متصل نکرده‌اید</string>
<string name="preference_search_result_ordering_weight_factor_high">متغیر</string>
<string name="poi_category_synagogue">کنیسه</string>
<plurals name="tag_selected_items">
<item quantity="one">%1$d مورد انتخاب‌شده</item>
<item quantity="other">%1$d مورد انتخاب‌شده</item>
</plurals>
<string name="apps_profile_private">خصوصی</string>
<string name="favorites">موارد دلخواه</string>
<plurals name="debug_cleanup_database_result">
<item quantity="one">%1$d مورد پاک‌شده.</item>
<item quantity="other">%1$d مورد پاک‌شده.</item>
</plurals>
<string name="poi_category_park">پارک</string>
<string name="preference_search_supportedunits">واحدهای پشتیبانی شده</string>
<string name="preference_clock_widget_time_format">فرمت زمان</string>
<string name="preference_clock_widget_time_format_24h">24 ساعته</string>
<string name="preference_clock_widget_time_format_12h">12 ساعته</string>
<string name="preference_clock_widget_time_format_system">پیش‌گزیده سامانه</string>
<string name="widget_use_theme_colors">به‌کارگیری رنگ‌های پوسته</string>
<string name="preference_clock_widget_fill_height">پر کردن ارتفاع صفحه</string>
<string name="preference_clock_widget_alignment">تراز</string>
<string name="preference_clock_widget_alignment_top">بالا</string>
<string name="preference_clock_widget_alignment_center">میانه</string>
<string name="preference_clock_widget_alignment_bottom">پایین</string>
<string name="preference_clockwidget_dynamic_zone">ناجیه پویا</string>
<string name="preference_clockwidget_date_part_summary">نمایش تاریخ کنونی</string>
<string name="preference_clockwidget_favorites_part">نوار ابزار</string>
<string name="preference_clockwidget_favorites_part_summary">نمایش اولین ردیف موارد دلخواه</string>
<string name="preference_restore_summary">وارد کردن یک پشتیبان ایجاد شده قبلی</string>
<string name="preference_backup_summary">صادرات تنظیمات و داده‌های راه‌انداز</string>
<string name="preference_search_currencyconverter_summary">به شیوه دوره‌ای نرخ ارزها را برای تبدیل واحد پولی دریافت کنید</string>
<string name="preference_search_wikipedia_summary">جستجوی دانشنامه آزاد</string>
<string name="preference_search_websites">وبگاه‌ها</string>
<string name="preference_search_locations">مکان‌ها</string>
<string name="preference_search_locations_radius">شعاع جستجو</string>
<string name="preference_search_websites_summary">اگر متن جستجو شده بک آدرس وب باشد، پیش‌نمایشی از وبگاه نمایش دهید</string>
<string name="preference_search_locations_summary">جستجوی منطقه محلی برای فروشگاه‌ها و سایر مکان‌ها</string>
<string name="preference_search_localfiles">فایل‌های محلی</string>
<string name="preference_search_gdrive">گوگل درایو</string>
<string name="preference_google_signin_summary">ورود برای جستجوی گوگل درایو</string>
<string name="preference_calendar_calendars">تقویم‌ها</string>
<string name="preference_calendar_hide_allday">پنهان کردن رویدادهای کل-روز</string>
<string name="preference_search_bar_color">رنگ</string>
<string name="preference_screen_buildinfo">درباره ساخت</string>
<string name="preference_screen_buildinfo_summary">داده‌های بیشتر درباره این نگارش از برنامه</string>
<string name="preference_search_bar_style">سبک</string>
<string name="poi_category_pet">فروشگاه حیوانات</string>
<string name="poi_category_shopping">خرید</string>
<string name="poi_category_concert_hall">سالن کنسرت</string>
<string name="poi_category_stadium">ورزشگاه</string>
<string name="poi_category_golf">زمین گلف</string>
<string name="poi_category_casino">کازینو</string>
<string name="poi_category_discount_store">فروشگاه تخفیف</string>
<string name="preference_hidden_items_reveal_button">نمایش دکمه آشکارسازی</string>
<string name="preference_hidden_items_reveal_button_summary">نمایش دکمه‌ای برای آشکار کردن یافته‌های جستجوی پنهان</string>
<string name="preference_screen_tags">برچسب‌ها</string>
<string name="preference_favorites_frequently_used_summary">نمایش موارد مورد استفاده بالا در موارد دلخواه</string>
<string name="preference_favorites_edit_button_summary">نمایش دکمه‌ای برای بازچینی موارد دلخواه</string>
<string name="preference_screen_tags_summary">مدیریت برچسب‌ها و موارد برچسب‌گذاری شده</string>
<string name="preference_wikipedia_customurl">نشانی وبگاه ویکی‌پدیا</string>
<string name="preference_edit_favorites_summary">تغییر ترتیب موارد سنجاق‌شده</string>
<string name="preference_category_favorites_frequently_used">بیشتر به‌کار گرفته شده</string>
<string name="preference_weather_integration">آب و هوا</string>
<string name="preference_media_integration">هدایت رسانه</string>
<string name="connect_account">اتصال حساب</string>
<string name="no_account_owncloud">هنوز حساب اون‌کلود را متصل نکرده‌اید</string>
<string name="no_account_google">هنوز حساب گوگل را متصل نکرده‌اید</string>
<string name="battery_part_charging">در حال شارژ</string>
<string name="favorites_empty_tag">هیچ موردی با این برچسب نیست</string>
<string name="favorites_empty">موارد سنجاق شده و بیشتر استفاده شده اینجا نمایش داده خواهند شد</string>
<string name="search_action_message">پیام</string>
<string name="search_action_email">رایانامه</string>
<string name="search_action_alarm">تنظیم هشدار</string>
<string name="frequently_used_show_in_favorites">نمایش در موارد دلخواه</string>
<string name="customize_tags_placeholder">برچسب‌ها</string>
<string name="preference_screen_search_actions">کنش‌های سریع</string>
<string name="preference_search_search_actions_summary">پیکربندی کنش‌های سریع و میانبرهای جستجو</string>
<string name="edit_tag_title">ویرایش برچسب</string>
<string name="tag_exists_error">برچسبی با این نام از پیش دارید.</string>
<string name="more_information">داده‌های بیشتر</string>
<string name="experimental_feature">آزمایشی</string>
<string name="create_tag_title">برچسب جدید</string>
<string name="tag_exists_message">برچسبی با این نام از پیش دارید. اگر ادامه دهید، دو برچسب یکی خواهند شد.</string>
<string name="gesture_action_quick_settings">بازکردن تنظیمات سریع</string>
<string name="search_results_order_bottom_up">از پایین به بالا</string>
<string name="preference_gesture_double_tap">دوبار زدن</string>
<string name="missing_permission_accessibility_gesture_settings">این کار نیازمند روشن بودن خدمات دسترسی برنامه راه‌انداز است.</string>
<string name="plugin_type_locationsearch">جستجوی مکان‌ها</string>
<string name="preference_default_filter">پالایش پیش‌گزیده</string>
<string name="preference_filter_bar">نمایش نوار پالایش</string>
<string name="preference_customize_filter_bar">سفارشی کردن نوار پالایش</string>
<string name="clock_variant_outlined">مشبک</string>
<string name="menu_show_filters">نمایش پالایشها</string>
<string name="menu_hide_filters">مخفی کردن پالایشها</string>
<string name="search_filter_online">یافته‌های برخط</string>
<string name="search_filter_apps">برنامه‌ها</string>
<string name="poi_category_toilets">سرویس بهداشتی</string>
<string name="poi_category_police">پلیس</string>
<string name="poi_category_dentist">دندانپزشک</string>
<string name="poi_category_library">کتابخانه</string>
<string name="poi_category_ice_cream">مغازه بستنی</string>
<string name="poi_category_theater">تئاتر</string>
<string name="poi_category_cinema">سینما</string>
<string name="poi_category_nightclub">باشگاه شبانه</string>
<string name="poi_category_clinic">کلینیک</string>
<string name="poi_category_books">کتاب‌فروشی</string>
<string name="preference_customize_filter_bar_summary">سفارشی‌سازی موارد موجود در نوار پالایش</string>
<string name="preference_search_osm_locations">اوپن‌استریت‌مپ (OpenStreetMap)</string>
<string name="poi_category_pub">بار</string>
<string name="poi_category_doctors">پزشکان</string>
<string name="poi_category_soccer">زمین فوتبال</string>
<string name="poi_category_bakery">نانوایی</string>
<string name="poi_category_optician">عینک‌فروشی</string>
<string name="poi_category_martial_arts">هنرهای رزمی</string>
<string name="poi_category_gymnastics">ژیمناستیک</string>
<string name="poi_category_ice_hockey">زمین هاکی روی یخ</string>
<string name="poi_category_handball">زمین هندبال</string>
<string name="poi_category_volleyball">زمین والیبال</string>
<string name="poi_category_skiing">اسکی</string>
<string name="poi_category_cricket">زمین کریکت</string>
<string name="poi_category_baseball">زمین بیسبال</string>
<string name="poi_category_fire_station">ایستگاه آتش‌نشانی</string>
<string name="poi_category_courthouse">دادگاه</string>
<string name="poi_category_townhall">شهرداری</string>
<string name="poi_category_government_building">ساختمان دولتی</string>
<string name="widget_removed">ابزارک پاک شد</string>
<string name="weather_condition_sleetshowers">بارش باران و برف</string>
<string name="weather_condition_heavyrain">بارش سنگین باران</string>
<string name="weather_condition_lightsnowandthunder">برف سبک و رعد</string>
<string name="weather_condition_lightrain">بارش باران سبک</string>
<string name="weather_condition_lightsleet">بارش سبک باران و برف</string>
<string name="weather_condition_heavysleetandthunder">بارش سنگین باران و برف و رعد</string>
<string name="weather_condition_rainshowers">بارش باران</string>
<string name="weather_condition_snowshowers">بارش برف</string>
<string name="weather_condition_heavyrainandthunder">بارش سنگین باران و رعد</string>
<string name="weather_condition_wind">باد</string>
<string name="weather_condition_unknown">ناشناس</string>
<string name="weather_no_data">داده‌های آب و هوایی در دسترس نیست.</string>
<string name="weather_condition_lightrainshowers">باران‌های سبک متناوب</string>
<string name="weather_condition_lightsnow">برف سبک</string>
<string name="weather_condition_heavysleetshowersandthunder">بارش‌های تند برف و باران همراه با رعد و برق</string>
<string name="weather_condition_lightsnowshowers">بارش‌های سبک برف</string>
<string name="weather_condition_lightssleetshowersandthunder">بارش‌های سبک برف و باران همراه با رعد و برق</string>
<string name="weather_condition_sleetshowersandthunder">بارش‌های برف و باران همراه با رعد و برق</string>
<string name="weather_condition_heavysleet">بارش سنگین باران و برف</string>
<string name="weather_condition_lightrainshowersandthunder">بارش دوره‌ای باران سبک و رعد</string>
<string name="search_action_websearch">جستجوی وب</string>
<string name="search_action_contact">افزودن به مخاطبین</string>
<string name="search_action_open_url">مشاهده وبگاه</string>
<string name="search_action_event">برنامه‌ریزی رویداد</string>
<string name="create_search_action_type">چه سبک کنشی می‌خواهید ایجاد کنید؟</string>
<string name="create_search_action_type_web">جستجو در یک وبگاه</string>
<string name="create_search_action_type_app">جستجو در یک برنامه</string>
<string name="create_search_action_type_intent">intent سفارشی</string>
<string name="create_search_action_title">اقدام سریع جدید</string>
<string name="poi_category_gallery">نگارخانه</string>
<string name="poi_category_japanese_restaurant">رستوران ژاپنی</string>
<string name="poi_category_ramen_restaurant">رستوران رامن</string>
<string name="poi_category_brunch_restaurant">رستوران ناهار</string>
<string name="poi_category_car_wash">شورشویی</string>
<string name="poi_category_charging_station">ایستگاه شارژ</string>
<string name="poi_category_motorcycle_rental">اجاره موتورسیکلت</string>
<string name="missing_permission_contact_search">دسترسی به مخاطبین را برای جستجوی مخاطبین خود اعطا کنید.</string>
<string name="provider_metno">متئو نروژ</string>
<string name="missing_permission_appshortcuts_search">برای جستجوی میانبرهای برنامه، %1$s را به‌عنوان برنامه پیش‌گزیده خانگی تنظیم کنید.</string>
<string name="missing_permission_appshortcuts_create">برای ایجاد میانبرها، %1$s را به‌عنوان برنامه پیش‌گزیده خانگی تنظیم کنید.</string>
<string name="preference_theme">پوسته</string>
<string name="missing_permission_files_search">دسترسی به کل حافظه برای جستجو در پرونده‌های محلی لازم است.</string>
<string name="departure_time_departed">جدا شده</string>
</resources>

View File

@ -1,2 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>
<resources>
<string name="unit_gibibyte_symbol">گیبی‌بایت</string>
<string name="unit_tebibyte_symbol">تیبی‌بایت</string>
<string name="dimension_length">طول</string>
<string name="dimension_mass">جرم</string>
<string name="dimension_velocity">سرعت</string>
<string name="dimension_volume">حجم</string>
<string name="dimension_area">مساحت</string>
<string name="dimension_currency">ارز</string>
<string name="dimension_data">داده</string>
<string name="dimension_bitrate">نرخ بیت</string>
<string name="dimension_pressure">فشار</string>
<string name="dimension_energy">انرژی</string>
<string name="unit_kilometer_symbol">کیلومتر</string>
<string name="unit_centimeter_symbol">سانتی‌متر</string>
<string name="unit_millimeter_symbol">میلی‌متر</string>
<string name="unit_inch_symbol">اینچ</string>
<string name="unit_foot_symbol">پا</string>
<string name="unit_yard_symbol">یارد</string>
<string name="unit_mile_symbol">مایل</string>
<string name="unit_hectare_symbol">هکتار</string>
<string name="unit_acre_symbol">جریب</string>
<string name="unit_second_symbol">ثانیه</string>
<string name="unit_minute_symbol">دقیقه</string>
<string name="unit_day_symbol">روز</string>
<string name="unit_millisecond_symbol">میلی‌ثانیه</string>
<string name="unit_hour_symbol">ساعت</string>
<string name="unit_year_symbol">سال</string>
<string name="unit_kilobyte_symbol">کیلوبایت</string>
<string name="unit_knot_symbol">گره</string>
<string name="unit_kilogram_symbol">کیلوگرم</string>
<string name="unit_gram_symbol">گرم</string>
<string name="unit_metric_ton_symbol">تن متریک</string>
<string name="unit_pound_symbol">پوند</string>
<string name="unit_ounce_symbol">اونس</string>
<string name="unit_stone_symbol">استون</string>
<string name="unit_kelvin_symbol">کلوین</string>
<string name="unit_short_ton_symbol">تن کوتاه</string>
<string name="unit_meter_symbol">متر</string>
<string name="unit_megabyte_symbol">مگابایت</string>
<string name="unit_gigabyte_symbol">گیگابایت</string>
<string name="unit_kibibyte_symbol">کیبی‌بایت</string>
<string name="unit_mebibyte_symbol">میبی‌بایت</string>
<string name="unit_bit_symbol">بیت</string>
<string name="unit_long_ton_symbol">تن بلند</string>
</resources>

View File

@ -6,7 +6,7 @@
<string name="menu_favorites_pin">Kitűzés a kedvencekhez</string>
<string name="menu_favorites_unpin">Kitűzés megszüntetése</string>
<string name="menu_back">Vissza</string>
<string name="menu_app_info">Alkalmazás információ</string>
<string name="menu_app_info">Információ az alkalmazásról</string>
<string name="menu_launch">Futtatás</string>
<string name="menu_customize">Testreszabás</string>
<string name="menu_hide">Elrejtés</string>
@ -35,15 +35,15 @@
<string name="file_meta_size">Méret: %1$s</string>
<string name="file_meta_path">Hely: %1$s</string>
<string name="file_meta_type">Típus: %1$s</string>
<string name="file_meta_app_name">Alkalmazásnév: %1$s</string>
<string name="file_meta_app_name">Alkalmazás neve: %1$s</string>
<string name="file_meta_app_version">Verzió: %1$s</string>
<string name="file_meta_app_pkgname">Csomagnév: %1$s</string>
<string name="file_meta_app_min_sdk">Legrégibb SDK verzió: %1$s</string>
<string name="file_meta_app_pkgname">Csomag neve: %1$s</string>
<string name="file_meta_app_min_sdk">Minimum SDK-verzió: %1$s</string>
<string name="file_meta_owner">Tulajdonos: %1$s</string>
<string name="file_meta_location">Hely: %1$s</string>
<string name="file_type_directory">Könyvtár</string>
<string name="file_type_android">Android csomagfájl</string>
<string name="file_type_source_code">Forráskódfájl</string>
<string name="file_type_source_code">Forráskód-fájl</string>
<string name="file_type_document">Dokumentum</string>
<string name="file_type_spreadsheet">Táblázat</string>
<string name="file_type_music">Zenefájl</string>
@ -76,14 +76,14 @@
<string name="widget_action_remove">Eltávolítás</string>
<string name="menu_item_edit_favs">Kedvencek módosítása</string>
<string name="wikipedia_url">https://en.wikipedia.org</string>
<string name="file_type_archive">Archív fájl</string>
<string name="file_type_presentation">Prezentáció</string>
<string name="file_type_archive">Archivált fájl</string>
<string name="file_type_presentation">Bemutató</string>
<string name="file_type_form">Űrlap</string>
<string name="file_type_launcherbackup">%1$s biztonsági mentés</string>
<string name="alert_delete_directory">A(z) %1$s könyvtár és annak teljes tartalma véglegesen törlődni fog. Folytatja?</string>
<string name="default_websearch_2_name">YouTube</string>
<string name="default_websearch_2_url">https://www.youtube.com/results\?search_query=${1}</string>
<string name="default_websearch_3_name">Play Áruház</string>
<string name="default_websearch_3_name">Google Play</string>
<string name="widget_name_weather">Időjárás</string>
<string name="easter_egg_1">Itt nincsenek Easter Eggek, hacsak nem hozott magával.</string>
<string name="close">Bezárás</string>
@ -165,8 +165,8 @@
<string name="weather_condition_lightrainshowersandthunder">Enyhe eső záporok és mennydörgés</string>
<string name="weather_condition_lightsnow">Enyhe havazás</string>
<string name="weather_condition_heavysleetshowersandthunder">Heves havas eső záporok és mennydörgés</string>
<string name="file_type_launchertheme">%1$s színséma</string>
<string name="preference_charging_animation">Töltés animáció</string>
<string name="file_type_launchertheme">%1$s téma</string>
<string name="preference_charging_animation">Animáció töltéskor</string>
<string name="file_meta_dimensions">Méretek: %1$s</string>
<string name="websearch_dialog_url_error">A(z) „${1}” helyőrző hiányzik ebből az webcímből</string>
<string name="websearch_dialog_query_encoding_form">application/x-www-form-urlencoded</string>
@ -181,11 +181,11 @@
<string name="weather_location_search_no_result">A helyszín nem található.</string>
<string name="missing_permission_appshortcuts_search">Állítsa be a(z) %1$s alkalmazást alapértelmezett indítóként a telepített alkalmazások kereséséhez.</string>
<string name="preference_colors_default">Alapértelmezett</string>
<string name="preference_force_themed_icons">Ikonok kényszerítése a színsémához</string>
<string name="preference_force_themed_icons">Ikonok kényszerítése a témához</string>
<string name="missing_permission_music_widget">A médialejátszás vezérléséhez értesítési hozzáférés szükséges</string>
<string name="missing_permission_calendar_search">A naptárban való kereséshez szükséges a naptárhoz való hozzáférés engedélyezése.</string>
<string name="preference_debug_dump_heap_summary">Pillanatfelvétel készítése a memóriahasználat elemzéséhez. Az alkalmazás lefagy, amíg ez a folyamat be nem fejeződik.</string>
<string name="preference_debug_cleanup_database_summary">Hibás- és nem használt bejegyzések eltávolítása az indító adatbázisból</string>
<string name="preference_debug_cleanup_database_summary">Hibás- és nem használt bejegyzések eltávolítása az indító adatbázisából</string>
<plurals name="debug_cleanup_database_result">
<item quantity="one">%1$d bejegyzés el lett távolítva.</item>
<item quantity="other">%1$d bejegyzés el lett távolítva.</item>
@ -194,7 +194,7 @@
<string name="missing_permission_calendar_widget">Adjon engedélyt a naptárnak a közelgő találkozók és események megjelenítéséhez.</string>
<string name="weather_condition_heavysnowshowers">Heves hó záporok</string>
<string name="preference_colors_bw">Fekete-fehér</string>
<string name="preference_force_themed_icons_summary">Az alkalmazás színsémájának alkalmazása az összes ikonra, beleértve a nem támogatottakat is (nem ajánlott)</string>
<string name="preference_force_themed_icons_summary">Az alkalmazás színösszeállításának használata az összes ikonra, beleértve a nem támogatottakat is (nem ajánlott)</string>
<string name="weather_condition_cloudy">Felhős</string>
<string name="weather_condition_hail">Jégeső</string>
<string name="weather_condition_snowshowersandthunder">Hó záporok és mennydörgés</string>
@ -230,13 +230,13 @@
<string name="weather_condition_heavyrainshowers">Heves eső záporok</string>
<string name="preference_version">Verzió</string>
<string name="preference_category_system_bars">Rendszersávok</string>
<string name="preference_theme">Színséma</string>
<string name="preference_theme">Téma</string>
<string name="preference_status_bar_icons">Állapotsor ikonjai</string>
<string name="preference_category_links">Hivatkozások</string>
<string name="preference_hide_nav_bar">Navigációs sáv elrejtése</string>
<string name="preference_debug_reinstall_iconpacks">Ikoncsomagok újratelepítése</string>
<string name="preference_debug_reinstall_iconpacks_summary">Az ikoncsomag gyorsítótárának törlése és újraépítése</string>
<string name="preference_themed_icons_summary">Színes ikonok az alkalmazás színsémájának megfelelően</string>
<string name="preference_themed_icons_summary">Ikonok színezése az alkalmazás színösszeállításával</string>
<string name="open_webpage">Weboldal megnyitása</string>
<string name="preference_screen_about_summary">Alkalmazás- és megállapodás információk</string>
<string name="preference_screen_appearance_summary">A megjelenés testreszabása</string>
@ -290,12 +290,12 @@
<string name="preference_screen_plugins_summary">Telepített kiterjesztések kezelése</string>
<string name="no_plugins_installed">Nincsenek telepített bővítmények</string>
<string name="preference_theme_system">Kövesse a rendszer beállításait</string>
<string name="preference_themed_icons">Színsémának megfelelő ikonok</string>
<string name="preference_themed_icons">Témának megfelelő ikonok</string>
<string name="preference_icon_pack">Ikoncsomag</string>
<string name="preference_icon_pack_summary_empty">Nincs telepítve ikoncsomag</string>
<string name="preference_category_animations">Animációk</string>
<string name="preference_charging_animation_summary">Buborék animáció lejátszása a készülék töltésekor</string>
<string name="preference_nav_bar_icons">Navigációs sáv ikonok</string>
<string name="preference_charging_animation_summary">Buborék animáció lejátszása az eszköz töltésekor</string>
<string name="preference_nav_bar_icons">Navigációs sáv ikonjai</string>
<string name="preference_system_bar_icons_auto">Automatikus</string>
<string name="preference_system_bar_icons_light">Világos</string>
<string name="preference_system_bar_icons_dark">Sötét</string>
@ -311,12 +311,12 @@
<string name="missing_permission_calendar_search_settings">A naptárban való kereséshez, naptár engedélyre van szükség</string>
<string name="weather_condition_heavyrainshowersandthunder">Heves eső záporok és mennydörgés</string>
<string name="weather_condition_heavysleetandthunder">Heves havas eső és mennydörgés</string>
<string name="missing_permission_files_search">A fényképek, média és dokumentumok kereséséhez tárhely engedélyre van szükség.</string>
<string name="missing_permission_appshortcuts_create">Állítsa be a(z) %1$s alkalmazást alapértelmezett indító alkalmazásként a parancsikonok létrehozásához.</string>
<string name="import_theme_error">A kiválasztott fájlt nem lehetett beolvasni. Győződjön meg róla, hogy érvényes színsémát (*.kvtheme) választott ki és hogy a fájl nem sérült-e.</string>
<string name="preference_hidden_items_reveal_button">Kinyit gomb megjelenítése</string>
<string name="missing_permission_files_search">A fényképek, média és dokumentumok kereséséhez tárhely engedélyre van szükség ezen az eszközön.</string>
<string name="missing_permission_appshortcuts_create">Állítsa be a(z) %1$s alkalmazást alapértelmezett indítóként a parancsikonok létrehozásához.</string>
<string name="import_theme_error">A kiválasztott fájlt nem lehetett beolvasni. Győződjön meg róla, hogy érvényes témát (*.kvtheme) választott ki és hogy a fájl nem sérült-e.</string>
<string name="preference_hidden_items_reveal_button">Kibontásgomb megjelenítése</string>
<string name="favorites_empty_tag">Nincsenek elemek ilyen címkével</string>
<string name="preference_layout_open_search">Keresés / alkalmazás tár</string>
<string name="preference_layout_open_search">Keresés / alkalmazásfiók</string>
<string name="preference_category_media_apps">Médiaalkalmazások</string>
<string name="preference_search_unitconverter_summary">Használat: 1.5 kg, vagy 4 cm &gt;&gt; in</string>
<string name="preference_search_bar_launch_on_enter_summary">Ugrás a kiemelt keresési találatra vagy gyors műveletre, ha megérinti az „Enter” gombot</string>
@ -329,7 +329,7 @@
<string name="plugin_type_weather">Időjárás-előrejelzés szolgáltató</string>
<string name="plugin_weather_provider_enable">Beállítás időjárás-előrejelzés szolgáltatónak</string>
<string name="preference_clock_widget_alignment_bottom">Alulra</string>
<string name="preference_clockwidget_battery_part_summary">Az akkumulátor aktuális töltöttségi szintjének megjelenítése, amikor az akkumulátor lemerült vagy töltődik</string>
<string name="preference_clockwidget_battery_part_summary">Az akkumulátor jelenlegi töltöttségi szintjének megjelenítése, amikor az akkumulátor lemerült vagy töltődik</string>
<string name="preference_clockwidget_alarm_part_summary">Azon riasztások megjelenítése, amelyek a következő 8 órán belül megszólalnak</string>
<string name="preference_logs">Naplók</string>
<string name="preference_logs_summary">Alkalmazásnaplók megtekintése és exportálása</string>
@ -363,7 +363,7 @@
<string name="battery_part_charging">⚡︎ Töltés ⚡︎</string>
<string name="search_action_email">E-mail</string>
<string name="search_action_alarm">Riasztás beállítása</string>
<string name="create_search_action_type">Milyen akciót szeretne létrehozni?</string>
<string name="create_search_action_type">Milyen műveletet szeretne létrehozni?</string>
<string name="more_information">További információ</string>
<string name="open_search_swipe_right">Jobbra húzás</string>
<string name="gesture_action_recents">Futó alkalmazások megjelenítése</string>
@ -372,7 +372,7 @@
<string name="preference_screen_weatherwidget">Időjárás</string>
<string name="preference_shortcut_badges">Parancsikon jelvények</string>
<string name="preference_grid_labels_summary">Az alkalmazás nevének megjelenítése az ikon alatt</string>
<string name="preference_screen_clockwidget_summary">Az óra stílusának és összetevőinek konfigurálása</string>
<string name="preference_screen_clockwidget_summary">Az óra stílusának és összetevőinek beállítása</string>
<string name="preference_blur_wallpaper_summary">Használjon elmosódást a háttérképen</string>
<string name="preference_blur_wallpaper_unsupported">Nem támogatott ezen az eszközön</string>
<string name="preference_wallpaper_summary">Háttérkép kiválasztása</string>
@ -384,10 +384,10 @@
<string name="preference_category_widgets">Modulok</string>
<string name="preference_clockwidget_music_part_summary">Médiavezérlők megjelenítése, ha aktív médiamunkamenet van</string>
<string name="plugin_type_filesearch">Fájlkeresés</string>
<string name="preference_crash_reporter_summary">Hiba- és összeomlás-jelentések</string>
<string name="preference_crash_reporter_summary">Hiba- és összeomlási jelentések</string>
<string name="preference_search_result_ordering_weight_factor_high">Változtatható</string>
<string name="preference_search_currencyconverter">Valutaváltó</string>
<string name="preference_search_bar_auto_focus_summary">A billentyűzet automatikus megjelenítése az alkalmazásfiók kinyitásakor</string>
<string name="preference_search_bar_auto_focus_summary">A billentyűzet automatikus megjelenítése az alkalmazásfiók megnyitásakor</string>
<string name="note_widget_link_file_summary">Szinkronizálja a modul tartalmát egy külső fájllal</string>
<string name="plugin_weather_provider_enabled">Jelenleg időjárás-előrejelzés szolgáltatóként van beállítva</string>
<string name="preference_search_websites">Weboldalak</string>
@ -416,7 +416,7 @@
<string name="search_action_label">Név</string>
<string name="preference_layout_fixed_search_bar">Rögzített keresősáv</string>
<string name="preference_screen_tags">Címkék</string>
<string name="restore_meta">A visszaállítani kívánt biztonsági mentés %1$s-kor lett létrehozva, a(z) %2$s készüléken, ezzel: %3$s.</string>
<string name="restore_meta">Létrehozva ekkor: %1$s, a(z) %2$s nevű eszközön, ezzel: %3$s.</string>
<string name="search_action_websearch_url_hint">A webcím-sablon, az internetes keresés webcímének létrehozásához használatos. Használja a(z) „${1}” kulcsot a tényleges keresési kifejezés helyőrzőjeként, pl. https://google.com/search?q=${1}.</string>
<plurals name="battery_part_remaining_charge_time">
<item quantity="one">%1$s perc múlva feltöltődik</item>
@ -424,11 +424,11 @@
</plurals>
<string name="search_action_event">Esemény ütemezése</string>
<string name="create_search_action_website_url">Adja meg a weboldal címét:</string>
<string name="search_results_order_bottom_up">Lentről-fölfelé</string>
<string name="search_results_order_bottom_up">Lentről-felfelé</string>
<string name="tag_no_items_message">Ehhez a címkéhez nincsenek elemek hozzárendelve. Ha folytatja, a címke törlődik.</string>
<string name="tag_select_items">Elemek kiválasztása:</string>
<string name="gesture_failed_message">Egy „%1$s” gesztust hajtott végre. Ez a gesztus jelenleg egy „%2$s” művelet kiváltására van beállítva. Az akciót azonban a következő okból nem lehetett végrehajtani:</string>
<string name="search_results_order_top_down">Föntről-lefelé</string>
<string name="search_results_order_top_down">Fentről-lefelé</string>
<string name="preference_gesture_swipe_down">Lefelé húzás</string>
<string name="gesture_action_power_menu">Kikapcsolási menü megjelenítése</string>
<string name="preference_search_result_ordering">A keresési eredmények sorrendje</string>
@ -436,8 +436,8 @@
<string name="create_search_action_type_web">Keresés egy weboldalon</string>
<string name="create_search_action_type_app">Keresés egy alkalmazásban</string>
<string name="create_search_action_type_intent">Egyéni intent</string>
<string name="create_search_action_title">Új gyors művelet</string>
<string name="edit_search_action_title">Gyors művelet szerkesztése</string>
<string name="create_search_action_title">Új gyorsművelet</string>
<string name="edit_search_action_title">Gyorsművelet szerkesztése</string>
<string name="create_search_action_website_invalid_url">Az adott weboldal nem importálható automatikusan webes keresésként. A következő lépésben megpróbálhat egy másik webhelyet, vagy manuálisan megadhatja a szükséges adatokat.</string>
<string name="search_action_app">Alkalmazás</string>
<string name="search_action_websearch_url">Webcím sablon</string>
@ -449,7 +449,7 @@
<string name="preference_about_fdroid">F-Droid tároló</string>
<string name="preference_enforce_icon_shape">Kényszerített alakzat</string>
<string name="preference_search_contacts">Névjegyek</string>
<string name="preference_edit_button">Szerkesztés gomb</string>
<string name="preference_edit_button">Szerkesztésgomb</string>
<string name="preference_favorites_edit_button_summary">Egy gomb megjelenítése a kedvencek átrendezéséhez</string>
<string name="preference_screen_icons">Rács és ikonok</string>
<string name="no_account_google">Még nem kapcsolódik egyetlen Google fiókhoz sem</string>
@ -464,7 +464,7 @@
<string name="widget_config_appwidget_borderless">Szegély nélküli</string>
<string name="apps_profile_work">Munka</string>
<string name="preference_search_result_ordering_alphabetic">A-Z</string>
<string name="widget_config_weather_integration_settings">Az időjárás integráció beállításai</string>
<string name="widget_config_weather_integration_settings">Az időjárás-integráció beállításai</string>
<string name="widget_config_calendar_no_calendars">Nem található naptár</string>
<string name="widget_config_music_integration_settings">Médiavezérlő-integráció beállításai</string>
<string name="note_widget_link_file">Hivatkozás a fájlra</string>
@ -474,15 +474,15 @@
<string name="restore_different_minor_version">Ez a biztonsági mentés a(z) %1$s egy másik verziójával készült. Előfordulhat, hogy egyes adatok nem lesznek helyesen visszaállítva.</string>
<string name="frequently_used_rows">Sorok száma</string>
<string name="preference_screen_search_actions">Gyorsműveletek</string>
<string name="preference_search_search_actions_summary">Gyorsműveletek és keresési parancsikonok konfigurálása</string>
<string name="preference_search_search_actions_summary">Gyorsműveletek és keresési parancsikonok beállítása</string>
<string name="note_widget_action_relink_file">Újraválasztás</string>
<string name="note_widget_conflict">Ütközés</string>
<string name="note_widget_conflict_action_resolve">Ütközések megoldása</string>
<string name="note_widget_conflict_description">A hivatkozott fájl nem üres, és a tartalma nem egyezik a jegyzet utolsó mentett verziójával. Melyik változatot szeretné megtartani?</string>
<string name="preference_dim_wallpaper">Háttérkép homályosítása</string>
<string name="preference_dim_wallpaper_summary">Sötét színsémák esetén sötétítse a háttérképet</string>
<string name="preference_dim_wallpaper">Háttérkép sötétítése</string>
<string name="preference_dim_wallpaper_summary">Sötét téma esetén sötétítse a háttérképet</string>
<string name="preference_blur_wallpaper">Háttérkép elmosása</string>
<string name="preference_blur_wallpaper_radius">Elmosási sugár</string>
<string name="preference_blur_wallpaper_radius">Elmosás sugara</string>
<string name="preference_suspended_badges_summary">A felfüggesztett alkalmazások jelvényeinek megjelenítése</string>
<string name="preference_suspended_badges">Felfüggesztett alkalmazások</string>
<string name="preference_cloud_badges">Felhő jelvények</string>
@ -509,7 +509,7 @@
<string name="preference_clock_widget_alignment_top">Felülre</string>
<string name="preference_clockwidget_dynamic_zone">Dinamikus zóna</string>
<string name="preference_clockwidget_date_part">Dátum</string>
<string name="preference_clockwidget_date_part_summary">Az aktuális dátum megjelenítése</string>
<string name="preference_clockwidget_date_part_summary">A jelenlegi dátum megjelenítése</string>
<string name="preference_clockwidget_favorites_part">Dokk</string>
<string name="preference_clockwidget_favorites_part_summary">A kitűzött elemek első sorának megjelenítése</string>
<string name="preference_clockwidget_music_part">Média</string>
@ -521,7 +521,7 @@
<string name="preference_backup_summary">A beállítások és az indító alkalmazás adatainak exportálása</string>
<string name="preference_restore">Visszaállítás</string>
<string name="preference_restore_summary">Egy korábban létrehozott biztonsági mentés importálása</string>
<string name="preference_crash_reporter">Összeomlás-jelentő</string>
<string name="preference_crash_reporter">Összeomlásjelentő</string>
<string name="preference_search_favorites">Kedvencek</string>
<string name="preference_search_favorites_summary">Kitűzött és gyakran használt elemek megjelenítése az alkalmazásrács felett</string>
<string name="preference_search_files">Fájlok</string>
@ -531,12 +531,12 @@
<string name="preference_search_appshortcuts_summary">Keresés a telepített alkalmazások között</string>
<string name="preference_search_calculator">Számológép</string>
<string name="preference_search_calculator_summary">Matematikai kifejezések kiértékelése</string>
<string name="preference_search_unitconverter">Mértékegység váltó</string>
<string name="preference_search_unitconverter">Mértékegységváltó</string>
<string name="preference_search_currencyconverter_summary">Rendszeresen töltse le az árfolyamokat a valuták átváltásához</string>
<string name="preference_search_wikipedia">Wikipédia</string>
<string name="preference_search_wikipedia_summary">Keresés a Wikipédián</string>
<string name="preference_search_localfiles">Helyi fájlok</string>
<string name="preference_search_localfiles_summary">Dokumentumok, fényképek és egyéb, ezen az eszközön tárolt fájlok keresése</string>
<string name="preference_search_localfiles_summary">Dokumentumok, fényképek és egyéb, ezen az eszközön tárolt fájlok keresése ezen az eszközön</string>
<string name="preference_search_gdrive_summary">Keresés a(z) %1$s fájljai között a Google Drive-on</string>
<string name="preference_search_nextcloud">Nextcloud</string>
<string name="preference_search_owncloud">Owncloud</string>
@ -567,16 +567,16 @@
<string name="widget_config_appwidget_configure">Modul beállítása</string>
<string name="note_widget_linked_file_summary">Hivatkozik a következő fájlra: %1$s</string>
<string name="note_widget_conflict_local_version">Utolsó mentett verzió:</string>
<string name="note_widget_conflict_file_version">Aktuális fájl tartalma:</string>
<string name="note_widget_conflict_file_version">A jelenlegi fájl tartalma:</string>
<string name="note_widget_file_read_error">Hiba a jegyzet olvasásakor</string>
<string name="note_widget_file_write_error">Hiba a jegyzet mentésekor</string>
<string name="note_widget_file_write_error_description">A jegyzetet nem lehetett a kapcsolódó fájlba írni. Lehetséges, hogy áthelyezték vagy törölték. A másolat el lett mentve az indító alkalmazás belső tárolójába.</string>
<string name="confirmation_delete_color_scheme">Biztosan törölni szeretné a(z) %1$s színsémát?</string>
<string name="new_color_scheme_name">Új színséma</string>
<string name="confirmation_delete_color_scheme">Biztosan törölni szeretné a(z) %1$s nevű színösszeállítást?</string>
<string name="new_color_scheme_name">Új színösszeállítás</string>
<string name="theme_color_scheme_custom_color">Egyéni</string>
<string name="theme_color_scheme_palette_color">Paletta</string>
<string name="preference_restore_default">Alapértelmezett visszaállítása</string>
<string name="import_theme_apply">Színséma alkalmazása</string>
<string name="import_theme_apply">Téma alkalmazása</string>
<string name="shortcut_label_unavailable">Nem érhető el</string>
<string name="edit_tag_title">Címke szerkesztése</string>
<string name="tag_exists_error">Egy ilyen nevű címke már létezik.</string>
@ -584,9 +584,9 @@
<string name="open_search_pull_down">Lefelé húzás</string>
<string name="preference_layout_search_bar_position">Keresősáv pozíciója</string>
<string name="preference_gesture_long_press">Hosszú érintés</string>
<string name="preference_gesture_home_button">Kezdőlap-gomb/-gesztus</string>
<string name="preference_gesture_home_button">Kezdőoldalgomb/-gesztus</string>
<string name="gesture_action_none">Ne csináljon semmit</string>
<string name="gesture_action_notifications">Értesítési tár megnyitása</string>
<string name="gesture_action_notifications">Értesítési fiók megnyitása</string>
<string name="gesture_action_lock_screen">Képernyő kikapcsolása</string>
<string name="gesture_action_quick_settings">Gyorsbeállítások megnyitása</string>
<string name="missing_permission_accessibility_gesture_failed">A művelet végrehajtásához engedélyezni kell a Kvaesitso kisegítő lehetőségek szolgáltatását.</string>
@ -608,8 +608,8 @@
<string name="preference_search_locations_hide_uncategorized">Kategorizálatlan helyszínek elrejtése</string>
<string name="preference_search_locations_hide_uncategorized_summary">Csak jól meghatározott kategóriák, például kávézók, vagy éttermek eredményeit jeleníti meg</string>
<string name="preference_search_locations_show_map">Térkép</string>
<string name="preference_search_locations_theme_map">Térkép színsémája</string>
<string name="preference_search_locations_theme_map_summary">Az indító színsémájának alkalmazása a térképre</string>
<string name="preference_search_locations_theme_map">Térkép témája</string>
<string name="preference_search_locations_theme_map_summary">Az indító színösszeállításának alkalmazása a térképre</string>
<string name="preference_search_location_custom_tile_server_url">Csempekiszolgáló webcíme</string>
<string name="menu_dial">Hívás</string>
<string name="location_open">Nyitva</string>
@ -622,7 +622,7 @@
<string name="location_open_24_7">Nyitva 24/7</string>
<string name="unavailable_searchable">Ez az elem már nem létezik.</string>
<string name="clock_style_segment">7-szegmenses</string>
<string name="widget_use_theme_colors">Színséma használata</string>
<string name="widget_use_theme_colors">Téma színének használata</string>
<string name="preference_search_bar_separate_work_profile">Lap elkülönítés a munkaprofil-alkalmazásokhoz</string>
<string name="preference_search_bar_separate_work_profile_summary">Külön lapon jeleníti meg a munkaprofil-alkalmazásokat</string>
<string name="preference_clock_widget_show_seconds">Másodperc megjelenítése</string>
@ -634,7 +634,7 @@
<string name="clock_style_empty">Nincs óra</string>
<string name="clock_variant_outlined">Körvonalazott</string>
<string name="widget_pick_widget">Modul kiválasztása</string>
<string name="clock_style_custom">Modul testreszabása</string>
<string name="clock_style_custom">Egyéni modul</string>
<string name="widget_config_appwidget_resize">Átméretezés</string>
<string name="search_action_share">Megosztás</string>
<string name="search_filter_online">Internetes keresési találat</string>
@ -644,7 +644,7 @@
<string name="preference_filter_bar_summary">Gyors szűrők megjelenítése a billentyűzet felett</string>
<string name="preference_default_filter">Alapértelmezett szűrő</string>
<string name="preference_default_filter_summary">A keresések alapértelmezett szűrőjének testreszabása</string>
<string name="filter_settings_network_warning">Az aktuális szűrő alapértelmezés szerint lehetővé teszi az internetes keresési találatokat. A keresési lekérdezések véletlenül külső webes szolgáltatásokra kerülhetnek. Adatvédelmi okokból ez nem ajánlott.</string>
<string name="filter_settings_network_warning">A jelenlegi szűrő alapértelmezés szerint lehetővé teszi az internetes keresési találatokat. A keresési lekérdezések véletlenül külső webes szolgáltatásokra kerülhetnek. Adatvédelmi okokból ez nem ajánlott.</string>
<string name="preference_customize_filter_bar_summary">Adja meg, hogy mely elemek szerepeljenek a szűrősávon</string>
<string name="preference_customize_filter_bar">Szűrősáv testreszabása</string>
<string name="preference_location_managed">Bővítmény által kezelt</string>
@ -779,7 +779,7 @@
<string name="weather_widget_no_provider">Nincs időjárás-szolgáltató kiválasztva vagy a kiválasztott szolgáltató nem érhető el</string>
<string name="apps_profile_private">Privát</string>
<string name="action_done">Kész</string>
<string name="action_more_actions">További lehetőségek</string>
<string name="action_more_actions">További műveletek</string>
<string name="action_clear">Kiürítés</string>
<string name="menu_show_filters">Szűrők megjelenítése</string>
<string name="menu_hide_filters">Szűrők elrejtése</string>
@ -795,7 +795,7 @@
<string name="plugin_type_calendar">Naptár</string>
<string name="profile_work_profile_state_locked">A munka-alkalmazások szünetelnek.</string>
<string name="profile_work_profile_action_unlock">Szüneteltetés feloldása</string>
<string name="profile_work_profile_action_lock">Munka-alkalmazások szüneteltetése</string>
<string name="profile_work_profile_action_lock">Munkaalkalmazások szüneteltetése</string>
<string name="profile_private_profile_action_unlock">Feloldás</string>
<string name="profile_private_profile_action_lock">Privát szféra lezárása</string>
<string name="profile_private_profile_state_locked">Privát szféra lezárva.</string>
@ -818,4 +818,14 @@
<string name="weather_forecast_humidity">Páratartalom</string>
<string name="tag_empty_name">Egy címke nem létezhet név nélkül. Ha folytatja, a címke törlődik.</string>
<string name="widget_removed">Modul törölve</string>
<string name="preference_clock_widget_time_format_system">Rendszer alapértelmezett</string>
<string name="preference_clock_widget_time_format">Időformátum</string>
<string name="preference_clock_widget_time_format_24h">24 órás</string>
<string name="preference_clock_widget_time_format_12h">12 órás</string>
<plurals name="departure_time_in">
<item quantity="one">1 perc múlva indúl</item>
<item quantity="other">%1$d perc múlva indúl</item>
</plurals>
<string name="departure_time_now">most</string>
<string name="preference_search_local_calendar_summary">Naptárak keresése ezen az eszközön</string>
</resources>

View File

@ -827,4 +827,15 @@
<string name="weather_forecast_wind">Vento</string>
<string name="widget_removed">Widget rimosso</string>
<string name="tag_empty_name">Un tag non può esistere senza un nome. Se continui, il tag verrà eliminato.</string>
<string name="preference_clock_widget_time_format">Formato ora</string>
<string name="preference_clock_widget_time_format_24h">24 ore</string>
<string name="preference_clock_widget_time_format_12h">12 ore</string>
<string name="preference_clock_widget_time_format_system">Predefinito del sistema</string>
<plurals name="departure_time_in">
<item quantity="one">tra un minuto</item>
<item quantity="many">tra %1$d minuti</item>
<item quantity="other">tra %1$d minuti</item>
</plurals>
<string name="departure_time_now">adesso</string>
<string name="preference_search_local_calendar_summary">Cerca calendari su questo dispositivo</string>
</resources>

View File

@ -818,4 +818,8 @@
<string name="weather_forecast_precipitation">Neerslag</string>
<string name="tag_empty_name">Een label kan niet bestaan zonder een naam. Als je doorgaat, wordt het label verwijderd.</string>
<string name="widget_removed">Widget verwijderd</string>
<string name="preference_clock_widget_time_format">Tijdformaat</string>
<string name="preference_clock_widget_time_format_24h">24-uur</string>
<string name="preference_clock_widget_time_format_12h">12-uur</string>
<string name="preference_clock_widget_time_format_system">Systeemstandaard</string>
</resources>

View File

@ -378,4 +378,15 @@
<item quantity="other">Decymetrów</item>
</plurals>
<string name="unit_decimeter_symbol">dm</string>
<string name="dimension_length">Długość</string>
<string name="dimension_velocity">Prędkość</string>
<string name="dimension_volume">Głośność</string>
<string name="dimension_area">Obszar</string>
<string name="dimension_currency">Waluta</string>
<string name="dimension_data">Dane</string>
<string name="dimension_bitrate">Bitrate</string>
<string name="dimension_energy">Energia</string>
<string name="dimension_frequency">Częstotliwość</string>
<string name="dimension_temperature">Temperatura</string>
<string name="dimension_time">Czas</string>
</resources>

View File

@ -494,19 +494,19 @@
<string name="preference_search_contacts">รายชื่อผู้ติดต่อ</string>
<string name="preference_screen_homescreen">หน้าจอหลัก</string>
<string name="missing_permission_notification_badges">คุณต้องอนุญาตให้แอปเข้าถึงการแจ้งเตือนเพื่อการแสดงป้ายการแจ้งเตือน</string>
<string name="preference_search_contacts_summary">ค้นหารายชื่อผู้ติดต่อบนอุปกรณ์เครื่องนี้</string>
<string name="preference_search_contacts_summary">ค้นหารายชื่อผู้ติดต่อบนอุปกรณ์นี้</string>
<string name="preference_screen_homescreen_summary">นาฬิกา แถบค้นหา ภาพพื้นหลัง แถบระบบ</string>
<string name="missing_permission_calendar_search">คุณต้องอนุญาตให้แอปเข้าถึงปฏิทินเพื่อค้นหาในปฏิทินของคุณ</string>
<string name="preference_search_calendar_summary">ค้นหานัดหมายและกิจกรรมที่กำลังมาถึง</string>
<string name="preference_weather_integration">สภาพอากาศ</string>
<string name="missing_permission_files_search">คุณต้องอนุญาตให้แอปเข้าถึงพื้นที่จัดเก็บข้อมูลเพื่อค้นหารูปภาพ สื่อและเอกสารบนอุปกรณ์เครื่องนี้</string>
<string name="missing_permission_files_search">คุณต้องอนุญาตให้แอปเข้าถึงพื้นที่จัดเก็บข้อมูลเพื่อค้นหารูปภาพ สื่อและเอกสารบนอุปกรณ์นี้</string>
<string name="preference_search_calculator_summary">คำนวณและดำเนินการทางคณิตศาสตร์</string>
<string name="missing_permission_appshortcuts_create">กำหนดให้แอป %1$s เป็นตัวใช้งานเริ่มต้นเพื่อสร้างทางลัด</string>
<string name="preference_search_currencyconverter_summary">ดาวน์โหลดอัตราแรกเปลี่ยนเพื่อแปลงสกุลเงินเป็นระยะๆ</string>
<string name="preference_search_websites_summary">แสดงตัวอย่างของเว็บไซต์ถ้าคำค้นหาเป็นลิงก์</string>
<string name="preference_media_integration">ตัวควบคุมสื่อ</string>
<string name="preference_theme">ธีม</string>
<string name="preference_search_localfiles_summary">ค้นหาเอกสาร รูปภาพและไฟล์อื่นๆที่เก็บบนอุปกรณ์เครื่องนี้</string>
<string name="preference_search_localfiles_summary">ค้นหาเอกสาร รูปภาพและไฟล์อื่นๆที่เก็บบนอุปกรณ์นี้</string>
<string name="preference_theme_light">สว่าง</string>
<string name="no_account_owncloud">คุณยังไม่ได้เชื่อมต่อกับบัญชี Owncloud</string>
<string name="preference_colors_bw">ขาว-ดำ</string>
@ -808,4 +808,14 @@
<string name="weather_forecast_humidity">ความชื้น</string>
<string name="weather_forecast_wind">ลม</string>
<string name="tag_empty_name">ไม่สามารถตั้งแท็กที่ไม่มีชื่อได้ หากคุณดำเนินการต่อ แท็กจะถูกลบ</string>
<string name="preference_clock_widget_time_format">รูปแบบเวลา</string>
<string name="preference_clock_widget_time_format_24h">24 ชั่วโมง</string>
<string name="widget_removed">ลบวิดเจ็ตแล้ว</string>
<string name="preference_clock_widget_time_format_12h">12 ชั่วโมง</string>
<string name="preference_clock_widget_time_format_system">ค่าเริ่มต้นของระบบ</string>
<plurals name="departure_time_in">
<item quantity="other">ในอีก %1$d นาที</item>
</plurals>
<string name="departure_time_now">ตอนนี้</string>
<string name="preference_search_local_calendar_summary">ค้นหาปฏิทินบนอุปกรณ์นี้</string>
</resources>

View File

@ -798,4 +798,15 @@
</plurals>
<string name="calendar_widget_open_calendar">Takvimi aç</string>
<string name="hint_drag_and_drop_reorder">Ögeleri düzenlemek için basılı tutun ve sürükleyin</string>
<string name="weather_condition_sleetandthunder">Karla karışık yağmur ile gök gürültüsü</string>
<string name="weather_condition_partlycloudy">Parçalı bulutlu</string>
<string name="weather_condition_heavysnowandthunder">Şiddetli kar ve gök gürültüsü</string>
<string name="weather_condition_rainandthunder">Yağmur ve gök gürültüsü</string>
<string name="weather_condition_lightsleet">Hafif karla karışık yağmur</string>
<string name="widget_removed">Widget kaldırıldı</string>
<string name="weather_condition_sleet">Sulu kar</string>
<string name="weather_condition_thunderstorm">Fırtına</string>
<string name="weather_condition_heavysleetandthunder">Yoğun karla karışık yağmur ve gök gürültüsü</string>
<string name="weather_condition_rainshowers">Sağanak yağmur</string>
<string name="weather_condition_lightsleetandthunder">Hafif sulu kar ve gök gürültüsü</string>
</resources>

View File

@ -107,9 +107,9 @@
<string name="alert_delete_shortcut">捷徑 %1$s 將被永久移除。繼續?</string>
<string name="default_websearch_2_name">YouTube</string>
<string name="default_websearch_2_url">https://www.youtube.com/results\?search_query=${1}</string>
<string name="default_websearch_3_name">Google Play商店</string>
<string name="default_websearch_3_name">Google Play</string>
<string name="default_websearch_3_url">https://play.google.com/store/search\?q=${1}</string>
<string name="websearch_dialog_url_error">此 URL 中缺少預留位置${1}</string>
<string name="websearch_dialog_url_error">此 URL 中缺少預留位置『${1}』</string>
<string name="websearch_dialog_replace_icon">替換圖示</string>
<string name="websearch_dialog_delete_icon">刪除圖示</string>
<string name="websearch_dialog_custom_icon">自訂圖示</string>
@ -145,7 +145,7 @@
<string name="edit_favorites_dialog_tag_section_empty">被釘選的標籤將出現在這裡</string>
<string name="edit_favorites_dialog_new_tag">建立標籤…</string>
<string name="nextcloud_server_url">Nextcloud 伺服器 URL</string>
<string name="nextcloud_server_url_empty">伺服器URL禁止為空</string>
<string name="nextcloud_server_url_empty">伺服器 URL 禁止為空</string>
<string name="nextcloud_server_invalid_url">此 URL 不能指向一個有效的 Nextcloud 安裝器</string>
<string name="owncloud_server_url">Owncloud 伺服器 URL</string>
<string name="owncloud_server_invalid_url">此 URL 不能指向一個有效的 Owncloud 安裝器</string>
@ -376,12 +376,12 @@
<string name="preference_search_localfiles">本機檔案</string>
<string name="preference_search_localfiles_summary">搜尋儲存在此裝置上的文件、照片與其它檔案</string>
<string name="preference_search_gdrive">Google Drive</string>
<string name="preference_search_gdrive_summary">在Google Drive上搜尋 %1$s</string>
<string name="preference_search_gdrive_summary"> Google Drive 上搜尋 %1$s</string>
<string name="preference_search_nextcloud">Nextcloud</string>
<string name="preference_search_cloud_summary">搜尋%1$s的檔案</string>
<string name="preference_search_owncloud">Owncloud</string>
<string name="preference_google_signin">使用 Google 登入</string>
<string name="preference_google_signin_summary">登入以查詢 Google Drive</string>
<string name="preference_google_signin_summary">登入以搜尋 Google Drive</string>
<string name="preference_calendar_calendars">日曆</string>
<string name="preference_calendar_hide_allday">隱藏全天事件</string>
<string name="preference_screen_buildinfo_summary">有關此版本應用程式的更多資訊</string>
@ -478,10 +478,10 @@
<string name="login_flow_login">登入</string>
<string name="icon_picker_no_packs_installed">未安裝任何圖示包</string>
<plurals name="calendar_widget_running_events">
<item quantity="other">從過去%1$d天運行的事件</item>
<item quantity="other">從過去 %1$d 天運行的事件</item>
</plurals>
<plurals name="debug_cleanup_database_result">
<item quantity="other">已刪除%1$d條目。</item>
<item quantity="other">已刪除 %1$d條目。</item>
</plurals>
<string name="preference_search_bar_launch_on_enter">輸入時啟動</string>
<string name="preference_search_bar_launch_on_enter_summary">在點擊「前往」後啟動突顯顯示的匹配項或快速操作</string>
@ -489,7 +489,7 @@
<item quantity="other">%1$s 分鐘內完成</item>
</plurals>
<plurals name="tag_selected_items">
<item quantity="other">已選擇 %1$d 項目</item>
<item quantity="other">已選擇 %1$d 項目</item>
</plurals>
<string name="gesture_action_launch_app">啟動應用程式</string>
<string name="icon_picker_filter_all_packs">所有圖示包</string>
@ -576,7 +576,7 @@
<string name="length_unit">長度單位</string>
<string name="preference_search_location_custom_overpass_url">Overpass URL</string>
<plurals name="app_info_notifications">
<item quantity="other">%1$s 通知</item>
<item quantity="other">%1$s 通知</item>
</plurals>
<string name="weather_widget_no_provider">沒有天氣服務商被選擇或者選擇的服務商不可用</string>
<string name="preference_screen_plugins_summary">管理已安裝的擴充套件</string>
@ -748,7 +748,7 @@
<string name="profile_private_profile_state_locked">私人空間被鎖定。</string>
<string name="profile_work_profile_action_lock">暫停工作應用程式</string>
<plurals name="contact_phone_numbers">
<item quantity="other">%1$s電話號碼</item>
<item quantity="other">%1$s電話號碼</item>
</plurals>
<string name="poi_category_handball">手球場</string>
<string name="apps_profile_private">私人</string>
@ -782,10 +782,10 @@
<string name="item_visibility_app_default">應用程式網格 &amp; 搜尋結果</string>
<string name="calendar_widget_open_calendar">開啟日曆應用程式</string>
<plurals name="contact_email_addresses">
<item quantity="other">%1$s電子郵件位址</item>
<item quantity="other">%1$s電子郵件位址</item>
</plurals>
<plurals name="contact_postal_addresses">
<item quantity="other">%1$s郵政地址</item>
<item quantity="other">%1$s郵政地址</item>
</plurals>
<string name="poi_category_gymnastics">體操場</string>
<string name="poi_category_monument">紀念碑</string>
@ -804,6 +804,11 @@
<string name="music_widget_previous_track">上一首</string>
<string name="music_widget_next_track">下一首</string>
<plurals name="calendar_search_enabled_lists">
<item quantity="other">已選擇 %1$s 列表</item>
<item quantity="other">已選擇 %1$s 列表</item>
</plurals>
<string name="preference_clock_widget_time_format_24h">24 小時制</string>
<string name="preference_clock_widget_time_format_12h">12 小時制</string>
<string name="preference_clock_widget_time_format_system">系統預設</string>
<string name="preference_clock_widget_time_format">時間格式</string>
<string name="widget_removed">小工具已移除</string>
</resources>

View File

@ -390,6 +390,8 @@
<string name="missing_permission_music_widget">Notification access is required to control media playback</string>
<!-- Missing contact permission in search settings screen -->
<string name="missing_permission_contact_search_settings">Contact permission is required to search contacts</string>
<!-- Missing call permission in contacts settings screen -->
<string name="missing_permission_call_contacts_settings">Call permission is required to start calls</string>
<!-- Missing calendar permission in search settings screen -->
<string name="missing_permission_calendar_search_settings">Calendar permission is required to search calendar</string>
<!-- Missing calendar permission in calendar widget settings screen -->
@ -555,8 +557,12 @@
<string name="preference_category_grid">Grid</string>
<string name="preference_grid_icon_size">Icon size</string>
<string name="preference_grid_column_count">Number of columns</string>
<string name="preference_grid_list_style">Show app results in a list</string>
<string name="preference_grid_list_icons">Show app icons in list</string>
<string name="preference_grid_labels">Show labels</string>
<string name="preference_grid_labels_summary">Show the app name below the icon</string>
<string name="preference_grid_list_style_summary">Show applications in a list view instead of grid</string>
<string name="preference_grid_list_icons_summary">Show icons in the list view</string>
<string name="preference_screen_debug">Debug</string>
<string name="preference_screen_debug_summary">Troubleshooting tools</string>
<string name="preference_category_widgets">Widgets</string>
@ -567,6 +573,10 @@
<string name="preference_clockwidget_layout_vertical">Default</string>
<string name="preference_clockwidget_layout_horizontal">Compact</string>
<string name="preference_clock_widget_show_seconds">Show seconds</string>
<string name="preference_clock_widget_time_format">Time format</string>
<string name="preference_clock_widget_time_format_24h">24-hour</string>
<string name="preference_clock_widget_time_format_12h">12-hour</string>
<string name="preference_clock_widget_time_format_system">System default</string>
<string name="widget_use_theme_colors">Use theme color</string>
<string name="preference_clock_widget_fill_height">Fill screen height</string>
<string name="preference_clock_widget_alignment">Alignment</string>
@ -604,6 +614,7 @@
<string name="preference_search_contacts_summary">Search contacts on this device</string>
<string name="preference_search_calendar">Calendar</string>
<string name="preference_search_calendar_summary">Search upcoming appointments and events</string>
<string name="preference_search_local_calendar_summary">Search calendars on this device</string>
<string name="preference_search_appshortcuts">App shortcuts</string>
<string name="preference_search_appshortcuts_summary">Search app shortcuts</string>
<string name="preference_search_calculator">Calculator</string>
@ -663,6 +674,8 @@
<string name="preference_category_accounts">Accounts</string>
<string name="preference_weather_integration">Weather</string>
<string name="preference_media_integration">Media control</string>
<string name="preference_contacts_call_on_tap">Call on tap</string>
<string name="preference_contacts_call_on_tap_summary">Directly start calls when tapping phone numbers</string>
<!-- Used in an info banner if a specific feature requires a Nextcloud account -->
<string name="no_account_nextcloud">You haven\'t connected a Nextcloud account yet</string>
<!-- Used in an info banner if a specific feature requires an Owncloud account -->
@ -1010,4 +1023,10 @@
<string name="weather_forecast_humidity">Humidity</string>
<string name="weather_forecast_wind">Wind</string>
<string name="weather_forecast_precipitation">Precipitation</string>
<string name="departure_time_now">now</string>
<plurals name="departure_time_in">
<item quantity="one">in one minute</item>
<item quantity="other">in %1$d minutes</item>
</plurals>
<string name="departure_time_departed">departed</string>
</resources>

View File

@ -15,3 +15,15 @@ fun <T> List<T>.randomElementOrNull(): T? {
fun <T> List<T>?.ifNullOrEmpty(block: () -> List<T>): List<T> {
return if (this.isNullOrEmpty()) block() else this
}
fun <T> List<T>.distinctByEquality(equalityPredicate: (T, T) -> Boolean): List<T> {
if (size < 2) return this
val ret = mutableListOf<T>()
for (item in this) {
if (ret.none { equalityPredicate(it, item) }) ret.add(item)
}
return ret
}

View File

@ -65,6 +65,7 @@ enum class PermissionGroup {
AppShortcuts,
Accessibility,
ManageProfiles,
Call,
}
internal class PermissionsManagerImpl(
@ -93,6 +94,9 @@ internal class PermissionsManagerImpl(
private val manageProfilesPermissionState = MutableStateFlow(
checkPermissionOnce(PermissionGroup.ManageProfiles)
)
private val callPermissionState = MutableStateFlow(
checkPermissionOnce(PermissionGroup.Call)
)
override fun requestPermission(context: AppCompatActivity, permissionGroup: PermissionGroup) {
when (permissionGroup) {
@ -167,6 +171,14 @@ internal class PermissionsManagerImpl(
CrashReporter.logException(e)
}
}
PermissionGroup.Call -> {
ActivityCompat.requestPermissions(
context,
callPermissions,
permissionGroup.ordinal
)
}
}
}
@ -209,6 +221,10 @@ internal class PermissionsManagerImpl(
PermissionGroup.Accessibility -> {
accessibilityPermissionState.value
}
PermissionGroup.Call -> {
callPermissions.all { context.checkPermission(it) }
}
}
}
@ -222,6 +238,7 @@ internal class PermissionsManagerImpl(
PermissionGroup.AppShortcuts -> appShortcutsPermissionState
PermissionGroup.Accessibility -> accessibilityPermissionState
PermissionGroup.ManageProfiles -> manageProfilesPermissionState
PermissionGroup.Call -> callPermissionState
}
}
@ -241,6 +258,7 @@ internal class PermissionsManagerImpl(
PermissionGroup.AppShortcuts -> appShortcutsPermissionState.value = granted
PermissionGroup.Accessibility -> accessibilityPermissionState.value = granted
PermissionGroup.ManageProfiles -> manageProfilesPermissionState.value = granted
PermissionGroup.Call -> callPermissionState.value = granted
}
}
@ -269,5 +287,6 @@ internal class PermissionsManagerImpl(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
private val callPermissions = arrayOf(Manifest.permission.CALL_PHONE)
}
}

View File

@ -1,14 +1,13 @@
package de.mm20.launcher2.preferences
import android.content.Context
import de.mm20.launcher2.preferences.search.LocationSearchSettings
import de.mm20.launcher2.search.SearchFilters
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class LauncherSettingsData internal constructor(
val schemaVersion: Int = 2,
val schemaVersion: Int = 3,
val uiColorScheme: ColorScheme = ColorScheme.System,
val uiTheme: ThemeDescriptor = ThemeDescriptor.Default,
@ -34,6 +33,7 @@ data class LauncherSettingsData internal constructor(
val clockWidgetCustom: ClockWidgetStyle.Custom = ClockWidgetStyle.Custom(),
val clockWidgetColors: ClockWidgetColors = ClockWidgetColors.Auto,
val clockWidgetShowSeconds: Boolean = false,
val clockWidgetTimeFormat: TimeFormat = TimeFormat.System,
val clockWidgetUseThemeColor: Boolean = false,
val clockWidgetAlarmPart: Boolean = true,
val clockWidgetBatteryPart: Boolean = true,
@ -53,6 +53,7 @@ data class LauncherSettingsData internal constructor(
val fileSearchProviders: Set<String> = setOf("local"),
val contactSearchEnabled: Boolean = true,
val contactSearchCallOnTap: Boolean = false,
@Deprecated("Use calendarSearchProviders `local` instead")
val calendarSearchEnabled: Boolean = true,
@ -81,6 +82,8 @@ data class LauncherSettingsData internal constructor(
val gridColumnCount: Int = 5,
val gridIconSize: Int = 48,
val gridLabels: Boolean = true,
val gridList: Boolean = false,
val gridListIcons: Boolean = true,
val searchBarStyle: SearchBarStyle = SearchBarStyle.Transparent,
val searchBarColors: SearchBarColors = SearchBarColors.Auto,
@ -408,4 +411,11 @@ enum class KeyboardFilterBarItem {
@SerialName("events") Events,
@SerialName("tools") Tools,
@SerialName("hidden") HiddenResults,
}
@Serializable
enum class TimeFormat {
@SerialName("system") System,
@SerialName("12h") TwelveHour,
@SerialName("24h") TwentyFourHour
}

View File

@ -2,6 +2,7 @@ package de.mm20.launcher2.preferences
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
@ -21,6 +22,7 @@ internal object LauncherSettingsDataSerializer : Serializer<LauncherSettingsData
override val defaultValue: LauncherSettingsData
get() = LauncherSettingsData(schemaVersion = 0)
@OptIn(ExperimentalSerializationApi::class)
override suspend fun readFrom(input: InputStream): LauncherSettingsData {
try {
return json.decodeFromStream(input)
@ -36,4 +38,4 @@ internal object LauncherSettingsDataSerializer : Serializer<LauncherSettingsData
override suspend fun writeTo(t: LauncherSettingsData, output: OutputStream) {
json.encodeToStream(t, output)
}
}
}

View File

@ -12,6 +12,8 @@ class CalendarSearchSettings internal constructor(
val enabledProviders
get() = dataStore.data.map { it.calendarSearchProviders }
fun isProviderEnabled(provider: String) = dataStore.data.map { it.calendarSearchProviders.contains(provider) }
fun setProviderEnabled(provider: String, enabled: Boolean) {
dataStore.update {
if (enabled) {

View File

@ -12,4 +12,11 @@ class ContactSearchSettings internal constructor(private val dataStore: Launcher
fun setEnabled(enabled: Boolean) {
dataStore.update { it.copy(contactSearchEnabled = enabled) }
}
val callOnTap: Flow<Boolean>
get() = dataStore.data.map { it.contactSearchCallOnTap }.distinctUntilChanged()
fun setCallOnTap(callOnTap: Boolean) {
dataStore.update { it.copy(contactSearchCallOnTap = callOnTap) }
}
}

View File

@ -5,6 +5,7 @@ import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.ClockWidgetStyleEnum
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.TimeFormat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@ -137,6 +138,15 @@ class ClockWidgetSettings internal constructor(
}
}
val timeFormat
get() = launcherDataStore.data.map { it.clockWidgetTimeFormat }
fun setTimeFormat(timeFormat: TimeFormat) {
launcherDataStore.update {
it.copy(clockWidgetTimeFormat = timeFormat)
}
}
val useThemeColor
get() = launcherDataStore.data.map { it.clockWidgetUseThemeColor }

View File

@ -25,6 +25,8 @@ data class GridSettings(
val columnCount: Int = 5,
val iconSize: Int = 48,
val showLabels: Boolean = true,
val showList: Boolean = false,
val showListIcons: Boolean = true,
)
class UiSettings internal constructor(
@ -48,6 +50,8 @@ class UiSettings internal constructor(
get() = launcherDataStore.data.map {
GridSettings(
showLabels = it.gridLabels,
showList = it.gridList,
showListIcons = it.gridListIcons,
iconSize = it.gridIconSize,
columnCount = it.gridColumnCount,
)
@ -71,6 +75,17 @@ class UiSettings internal constructor(
}
}
fun setGridShowList(showList: Boolean) {
launcherDataStore.update {
it.copy(gridList = showList)
}
}
fun setGridShowListIcons(showIcons: Boolean) {
launcherDataStore.update {
it.copy(gridListIcons = showIcons)
}
}
val cardStyle
get() = launcherDataStore.data.map {
@ -342,4 +357,4 @@ class UiSettings internal constructor(
it.copy(widgetsEditButton = editButton)
}
}
}
}

View File

@ -32,16 +32,56 @@ data class Departure(
val lineColor: Color?,
)
/**
* Compares two line names. The line naems are split into parts of numbers or letters, then
* each segment is compared.
*/
object LineNameComparator : Comparator<String> {
// Split line into parts, e.g. "11A" -> ["11", "A"], "S1" -> ["S", "1"], "40-X" -> ["40", "X"]
private val regex = Regex("\\p{L}+|[0-9]+")
override fun compare(a: String, b: String): Int {
if (a == b) return 0
val aParts = regex.findAll(a).toList()
val bParts = regex.findAll(b).toList()
for (i in 0 until minOf(aParts.size, bParts.size)) {
val aPart = aParts[i].value
val bPart = bParts[i].value
if (aPart == bPart) continue
val thisPartNumber = aPart.toIntOrNull()
val otherPartNumber = bPart.toIntOrNull()
if (thisPartNumber != null && otherPartNumber != null) {
// both parts are numbers, compare them as numbers
return thisPartNumber.compareTo(otherPartNumber)
}
// one part is a number, the other is a string. numbers are automatically less than strings
return aPart.compareTo(bPart)
}
// 11 < 11A
return aParts.size.compareTo(bParts.size)
}
}
// implicit ordering by ordinal
enum class LineType {
Bus,
Tram,
Subway,
Tram,
Bus,
Boat,
Monorail,
CableCar,
CommuterTrain,
RegionalTrain,
Train,
HighSpeedTrain,
Boat,
Monorail,
CableCar,
Airplane,
}

View File

@ -27,4 +27,14 @@ sealed interface OpeningSchedule {
data object TwentyFourSeven : OpeningSchedule
@Serializable
data class Hours(@Serializable val openingHours: List<OpeningHours>) : OpeningSchedule
}
/**
* Checks whether the [OpeningSchedule] has at least one opening hour.
*/
fun OpeningSchedule.isNotEmpty(): Boolean {
return when (this) {
is OpeningSchedule.Hours -> openingHours.isNotEmpty()
OpeningSchedule.TwentyFourSeven -> true
}
}

View File

@ -2,9 +2,12 @@ package de.mm20.launcher2.contacts
import android.content.ContentUris
import android.content.Context
import android.os.Build
import android.provider.ContactsContract
import android.telephony.PhoneNumberUtils
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
import de.mm20.launcher2.ktx.distinctByEquality
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.search.ContactSearchSettings
@ -136,11 +139,10 @@ internal class ContactRepository(
}
else -> {
val mimeType = dataCursor.getStringOrNull(mimeTypeColumn) ?: continue
contactApps += ContactApp(
label = dataCursor.getStringOrNull(data3Column) ?: continue,
packageName = dataCursor.getStringOrNull(accountTypeColumn) ?: continue,
mimeType = mimeType,
mimeType = dataCursor.getStringOrNull(mimeTypeColumn) ?: continue,
uri = ContentUris.withAppendedId(
ContactsContract.Data.CONTENT_URI,
dataCursor.getLongOrNull(idColumn) ?: continue
@ -164,12 +166,25 @@ internal class ContactRepository(
}
lookupKeyCursor.close()
val mainLocaleISO3 = context.resources.configuration.locales[0].isO3Country
return@withContext AndroidContact(
id = id,
firstName = firstName,
lastName = lastName,
displayName = displayName,
phoneNumbers = phoneNumbers.distinct(),
phoneNumbers = phoneNumbers.sortedByDescending {
it.number.count { !PhoneNumberUtils.isReallyDialable(it) }
}.distinctByEquality { a, b ->
if (Build.VERSION.SDK_INT < 31) {
PhoneNumberUtils.compare(context, a.number, b.number)
} else {
PhoneNumberUtils.areSamePhoneNumber(a.number, b.number, mainLocaleISO3)
}
}.mapNotNull {
val formattedNumber = PhoneNumberUtils.formatNumber(it.number, mainLocaleISO3) ?: return@mapNotNull null
it.copy(number = formattedNumber)
},
emailAddresses = emailAddresses.distinct(),
postalAddresses = postalAddresses.distinct(),
contactApps = contactApps.distinct(),

View File

@ -1,29 +1,33 @@
<template>
<div class="layout-footer">
<div class="link-columns">
<div class="column">
<h4>Documentation</h4>
<a href="/docs/user-guide">User Guide</a>
<a href="/docs/developer-guide">Developer Guide</a>
<a href="/docs/contributor-guide">Contributor Guide</a>
<a href="/reference/index.html" target="_blank">SDK Reference</a>
</div>
<div class="column">
<h4>Legal</h4>
<a href="/privacy-policy">Privacy Policy</a>
<a href="/license">License</a>
</div>
<div class="column">
<h4>Links</h4>
<a href="https://github.com/MM2-0/Kvaesitso" target="_blank">Github</a>
<a href="https://t.me/Kvaesitso" target="_blank">Telegram</a>
<footer class="VPFooter">
<div class="layout-footer">
<div class="link-columns">
<div class="column">
<h4>Documentation</h4>
<a href="/docs/user-guide">User Guide</a>
<a href="/docs/developer-guide">Developer Guide</a>
<a href="/docs/contributor-guide">Contributor Guide</a>
<a href="/reference/index.html" target="_blank">SDK Reference</a>
</div>
<div class="column">
<h4>Legal</h4>
<a href="/privacy-policy">Privacy Policy</a>
<a href="/license">License</a>
</div>
<div class="column">
<h4>Links</h4>
<a href="https://github.com/MM2-0/Kvaesitso" target="_blank"
>Github</a
>
<a href="https://t.me/Kvaesitso" target="_blank">Telegram</a>
</div>
</div>
<p class="copyright">
Copyright © 2025 MM2-0 and the Kvaesitso contributors. Built with
VitePress.
</p>
</div>
<p class="copyright">
Copyright © 2025 MM2-0 and the Kvaesitso contributors. Built with
VitePress.
</p>
</div>
</footer>
</template>
<style lang="scss">
.layout-footer {
@ -42,6 +46,14 @@
font-weight: 600;
margin-bottom: 0.5rem;
}
a {
color: var(--vp-c-text-2);
text-decoration: none;
&:hover {
text-decoration: underline;
color: var(--vp-c-brand);
}
}
}
gap: 2rem;
}

View File

@ -17,4 +17,13 @@
width: 320px;
border-radius: 16px;
}
.credits {
font-size: 0.8rem;
text-align: center;
grid-column: 1 / -1;
a {
color: var(--vp-c-brand);
text-decoration: underline;
}
}
}

View File

@ -14,6 +14,24 @@ There are two options:
add
the favorites widget to the home screen.
## What are these categories "Personal", "Private", and "Work" above the app grid?
These categories aren't a feature of the launcher, but just a representation of Android's different
profiles. Profiles are isolated spaces on your device that allow you to have separate apps, accounts,
and data, independent of the main profile.
**Personal** is the main profile.
**Work** is a work profile. Learn more about work
profiles [here](https://www.android.com/enterprise/work-profile/).
You can also use an app
like [Insular](https://f-droid.org/de/packages/com.oasisfeng.island.fdroid/)
or [Shelter](https://f-droid.org/de/packages/net.typeblog.shelter/) to create and manage a work
profile.
**Private** is the private space profile, available on Android 15 and higher. Learn more about
private spaces [here](https://support.google.com/android/answer/15341885?hl=en).
## Can I remove / customize the clock?
Yes, you can customize the clock style by going to Settings > Home screen > Clock and selecting a
@ -31,7 +49,8 @@ Please refer to [this page](/docs/user-guide/troubleshooting/granted-permissions
## I can't update to the latest version
Please refer to the [Launcher Cannot Be Updated](/docs/user-guide/troubleshooting/update-not-installed) page.
Please refer to
the [Launcher Cannot Be Updated](/docs/user-guide/troubleshooting/update-not-installed) page.
## Why is wallpaper blur not supported on my device?

View File

@ -22,5 +22,7 @@ hero:
<img src="/img/screenshot-4.png"></img>
<img src="/img/screenshot-5.png"></img>
<img src="/img/screenshot-6.png"></img>
<div class="credits">Wallpaper by Allec Gomes on <a href="https://unsplash.com/de/fotos/ein-grunes-blatt-das-auf-einem-gewasser-schwimmt-UcWUMqIsld8" target="_blank">Unspash.com</a></div>
</div>
<Footer></Footer>

3144
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,9 +6,9 @@
},
"devDependencies": {
"qrcode": "^1.5.4",
"sass": "^1.70.0",
"vitepress": "^1.0.0-rc.41",
"vue": "^3.4.15"
"sass": "^1.86.0",
"vitepress": "^1.6.3",
"vue": "^3.5.13"
},
"dependencies": {
"material-symbols": "^0.14.7"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 838 KiB

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 587 KiB

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 888 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -6,10 +6,10 @@ minSdk = "26"
compileSdk = "35"
targetSdk = "35"
pluginSdk = "2.1.2-SNAPSHOT"
pluginSdk = "2.1.2"
gradle = "8.1.2"
android-gradle-plugin = "8.2.2"
android-gradle-plugin = "8.6.0"
ksp-gradle-plugin = "2.1.10-1.0.29"
kotlin = "2.1.10"
@ -19,19 +19,19 @@ kotlinx-serialization = "1.8.0"
jetbrains-markdown = "0.7.3"
androidx-compose = "1.8.0-beta01"
androidx-compose-material3 = "1.4.0-alpha07"
androidx-compose-materialicons = "1.7.7"
androidx-compose = "1.8.0-rc02"
androidx-compose-material3 = "1.4.0-alpha11"
androidx-compose-materialicons = "1.7.8"
androidx-lifecycle = "2.8.7"
androidx-core = "1.15.0"
androidx-appcompat = "1.7.0"
androidx-activity = "1.10.0"
androidx-work = "2.9.0"
androidx-activity = "1.10.1"
androidx-work = "2.10.0"
androidx-browser = "1.8.0"
androidx-palette = "1.0.0"
androidx-media2 = "1.3.0"
androidx-room = "2.6.1"
androidx-constraint-layout = "1.1.0"
androidx-constraint-layout = "1.1.1"
androidx-emojipicker = "1.5.0"
accompanist = "0.33.2-alpha"

View File

@ -166,9 +166,9 @@ class IconService(
}
}
fun resolveCustomIcon(searchable: SavableSearchable, size: Int, customIcon: CustomIcon?): Flow<LauncherIcon> {
fun resolveCustomIcon(searchable: SavableSearchable, size: Int, customIcon: CustomIcon?): Flow<LauncherIcon?> {
return combine(iconProviders, transformations) { providers, transformations ->
var icon = cache.get(searchable.key + customIcon.hashCode() + providers.hashCode() + transformations.hashCode())
var icon: LauncherIcon? = cache.get(searchable.key + customIcon.hashCode() + providers.hashCode() + transformations.hashCode())
if (icon != null) {
return@combine icon
}

View File

@ -277,15 +277,19 @@ class AppFilterIconPackInstaller(
override fun getInstalledIconPacks(): List<IconPack> {
val packs = mutableListOf<IconPack>()
val pm = context.packageManager
var intent = Intent("app.lawnchair.icons.THEMED_ICON")
val themedPacks = pm.queryIntentActivities(intent, 0)
packs.addAll(themedPacks.map { IconPack(context, it, true) })
intent = Intent("org.adw.ActivityStarter.THEMES")
val adwPacks = pm.queryIntentActivities(intent, 0)
packs.addAll(adwPacks.map { IconPack(context, it, false) })
intent = Intent("com.novalauncher.THEME")
val novaPacks = pm.queryIntentActivities(intent, 0)
packs.addAll(novaPacks.map { IconPack(context, it, false) })
val intents = listOf(
Intent("app.lawnchair.icons.THEMED_ICON"),
Intent("org.adw.ActivityStarter.THEMES"),
Intent("com.novalauncher.THEME"),
Intent("org.adw.launcher.THEMES")
)
for (intent in intents) {
packs.addAll(
pm.queryIntentActivities(intent, 0).map { IconPack(context, it, true) }
)
}
return packs.distinctBy { it.packageName }
}
}

View File

@ -47,6 +47,9 @@ interface PluginService {
enabled: Boolean? = null,
): Flow<List<PluginWithState>>
/**
* Get a plugin with its current state or null if the plugin is not found.
*/
fun getPluginWithState(
authority: String,
): Flow<PluginWithState?>