mirror of
https://github.com/bitfireAT/davx5-ose.git
synced 2026-01-06 13:57:54 -05:00
Compare commits
15 Commits
debug-buil
...
v3.1-beta3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0854c36792 | ||
|
|
4f9cdaff15 | ||
|
|
afaeec4810 | ||
|
|
427a24ccf6 | ||
|
|
a970872790 | ||
|
|
86667b426d | ||
|
|
e003402fa2 | ||
|
|
729f9e952b | ||
|
|
e8a7221f44 | ||
|
|
8f90ad156c | ||
|
|
938982bf82 | ||
|
|
35e2c52de2 | ||
|
|
b7377f33c2 | ||
|
|
321671c629 | ||
|
|
8df07108d7 |
@@ -20,7 +20,8 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "at.bitfire.davdroid"
|
||||
|
||||
versionCode 300000004
|
||||
versionCode 301000002
|
||||
versionName '3.1-beta3'
|
||||
buildConfigField "long", "buildTime", System.currentTimeMillis() + "L"
|
||||
buildConfigField "boolean", "customCerts", "true"
|
||||
|
||||
@@ -32,6 +33,9 @@ android {
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
// enable because ical4android requires desugaring
|
||||
coreLibraryDesugaringEnabled true
|
||||
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
@@ -39,12 +43,12 @@ android {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
dataBinding.enabled = true
|
||||
buildFeatures.dataBinding = true
|
||||
|
||||
flavorDimensions "distribution"
|
||||
productFlavors {
|
||||
standard {
|
||||
versionName "3.0-ose2"
|
||||
versionName "3.1-beta3-ose"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +110,9 @@ dependencies {
|
||||
implementation project(':ical4android')
|
||||
implementation project(':vcard4android')
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5"
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.5'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0-beta01'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
|
||||
@@ -1,35 +1,20 @@
|
||||
|
||||
# ProGuard usage for DAVx⁵:
|
||||
# shrinking yes (main reason for using ProGuard)
|
||||
# ProGuard/R8 usage for DAVx⁵:
|
||||
# shrinking yes
|
||||
# optimization yes
|
||||
# obfuscation no (DAVx⁵ is open-source)
|
||||
# preverification no
|
||||
# obfuscation no (open-source)
|
||||
|
||||
-dontobfuscate
|
||||
|
||||
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||||
-optimizationpasses 5
|
||||
-allowaccessmodification
|
||||
-dontpreverify
|
||||
|
||||
# ez-vcard
|
||||
-dontwarn ezvcard.io.json.** # JSON serializer (for jCards) not used
|
||||
-dontwarn freemarker.** # freemarker templating library (for creating hCards) not used
|
||||
-dontwarn org.jsoup.** # jsoup library (for hCard parsing) not used
|
||||
-keep class ezvcard.property.** { *; } # keep all vCard properties (created at runtime)
|
||||
|
||||
# ical4j: ignore unused dynamic libraries
|
||||
-dontwarn aQute.**
|
||||
-dontwarn groovy.** # Groovy-based ContentBuilder not used
|
||||
-dontwarn javax.cache.** # no JCache support in Android
|
||||
-dontwarn net.fortuna.ical4j.model.**
|
||||
-dontwarn org.codehaus.groovy.**
|
||||
-dontwarn org.apache.log4j.** # ignore warnings from log4j dependency
|
||||
# ical4j
|
||||
-keep class net.fortuna.ical4j.** { *; } # keep all model classes (properties/factories, created at runtime)
|
||||
-keep class org.threeten.bp.** { *; } # keep ThreeTen (for time zone processing)
|
||||
|
||||
# dnsjava
|
||||
-dontwarn sun.net.spi.nameservice.** # not available on Android
|
||||
|
||||
# DAVx⁵ + libs
|
||||
-keep class at.bitfire.** { *; } # all DAVx⁵ code is required
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.APPLICATION_PREFERENCES"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
@@ -100,6 +101,7 @@
|
||||
android:label="@string/debug_info_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BUG_REPORT"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<provider
|
||||
@@ -112,6 +114,8 @@
|
||||
android:resource="@xml/debug_paths" />
|
||||
</provider>
|
||||
|
||||
<activity android:name=".ui.PermissionsActivity" />
|
||||
|
||||
<!-- account type "DAVx⁵" -->
|
||||
<service
|
||||
android:name=".syncadapter.AccountAuthenticatorService"
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"ar_SA":["abdunnasir"],"bg":["dpa_transifex"],"ca":["Kintu","zagur"],"cs":["pavelb","tomas.odehnal"],"da":["knutztar","mjjzf","Tntdruid_","twikedk"],"de":["anestiskaci","Atalanttore","corppneq","maxkl","nicolas_git","owncube","TheName","Wyrrrd","YvanM"],"el":["anestiskaci","diamond_gr"],"es":["aluaces","Ark74","Elhea","GranPC","jcvielma","plaguna","polkhas","xphnx"],"eu":["cockeredradiation","Osoitz"],"fa":["ahangarha","amiraliakbari","maryambehzi","mtashackori","Numb","taranehsaei"],"fi_FI":["raketti","tseipii"],"fr":["AlainR","alkino2","Amadeen","boutil","callmemagnus","chfo","chrcha","Floflr","grenatrad","Jorg722","Llorc","Novick","Poussinou","Thecross","YvanM","ÉricB."],"fr_FR":["chrcha","Llorc","Poussinou"],"gl":["aluaces","pikamoku"],"hu":["jtg"],"it":["Damtux","ed0","FranzMari","noccio","nwandy","rickyroo","technezio"],"ja":["Naofumi"],"nb_NO":["elonus"],"nl":["davtemp","dehart","toonvangerwen","XtremeNova"],"pl":["gsz","mg6","oskarjakiela","TheName","TORminator"],"pt_BR":["amalvarenga","wanderlei.huttel"],"ru":["aigoshin","anm","astalavister","nick.savin","vaddd"],"sk_SK":["brango67","tiborepcek"],"sl_SI":["MrLaaky","uroszor"],"sr":["daimonion"],"szl":["chlodny"],"tr_TR":["ooguz","pultars"],"uk":["androsua","olexn","twixi007"],"uk_UA":["astalavister"],"zh_CN":["anolir","jxj2zzz79pfp9bpo","linuxbckp","mofitt2016","oksjd","phy","spice2wolf"],"zh_TW":["linuxbckp","mofitt2016","phy"]}
|
||||
{"ar_SA":["abdunnasir"],"bg":["dpa_transifex"],"ca":["Kintu","zagur"],"cs":["pavelb","tomas.odehnal"],"da":["knutztar","mjjzf","Tntdruid_","twikedk"],"de":["anestiskaci","Atalanttore","corppneq","maxkl","nicolas_git","owncube","TheName","Wyrrrd","YvanM"],"el":["anestiskaci","diamond_gr","KristinaQejvanaj"],"es":["aluaces","Ark74","Elhea","GranPC","jcvielma","plaguna","polkhas","xphnx"],"eu":["cockeredradiation","Osoitz","Thadah"],"fa":["ahangarha","amiraliakbari","joojoojoo","maryambehzi","mtashackori","Numb","taranehsaei"],"fi_FI":["raketti","tseipii"],"fr":["AlainR","alkino2","Amadeen","boutil","callmemagnus","chfo","chrcha","Floflr","grenatrad","jokx","Jorg722","Llorc","Novick","Poussinou","Thecross","YvanM","ÉricB."],"fr_FR":["chrcha","Llorc","Poussinou"],"gl":["aluaces","pikamoku"],"hu":["jtg"],"it":["Damtux","ed0","FranzMari","noccio","nwandy","rickyroo","technezio"],"ja":["Naofumi"],"nb_NO":["elonus"],"nl":["davtemp","dehart","erikhubers","toonvangerwen","XtremeNova"],"pl":["gsz","mg6","oskarjakiela","TheName","TORminator"],"pt_BR":["amalvarenga","wanderlei.huttel"],"ru":["aigoshin","anm","astalavister","nick.savin","vaddd"],"sk_SK":["brango67","tiborepcek"],"sl_SI":["MrLaaky","uroszor"],"sr":["daimonion"],"szl":["chlodny"],"tr_TR":["ooguz","pultars"],"uk":["androsua","olexn","twixi007"],"uk_UA":["astalavister"],"zh_CN":["anolir","jxj2zzz79pfp9bpo","linuxbckp","mofitt2016","oksjd","phy","spice2wolf"],"zh_TW":["linuxbckp","mofitt2016","phy"]}
|
||||
|
||||
@@ -11,7 +11,6 @@ package at.bitfire.davdroid
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.net.Uri
|
||||
@@ -22,8 +21,10 @@ import androidx.appcompat.content.res.AppCompatResources
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.ui.DebugInfoActivity
|
||||
import at.bitfire.davdroid.ui.NotificationUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@Suppress("unused")
|
||||
@@ -73,17 +74,12 @@ class App: Application(), Thread.UncaughtExceptionHandler {
|
||||
NotificationUtils.createChannels(this)
|
||||
|
||||
// don't block UI for some background checks
|
||||
thread {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
// watch installed/removed apps
|
||||
val tasksFilter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
|
||||
addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED)
|
||||
addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||
addDataScheme("package")
|
||||
}
|
||||
registerReceiver(PackageChangedReceiver(), tasksFilter)
|
||||
OpenTasksWatcher(this@App)
|
||||
|
||||
// check whether a tasks app is currently installed
|
||||
PackageChangedReceiver.updateTaskSync(this)
|
||||
OpenTasksWatcher.updateTaskSync(this@App)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,14 @@ import at.bitfire.davdroid.model.Collection
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.ui.DebugInfoActivity
|
||||
import at.bitfire.davdroid.ui.NotificationUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class DavService: android.app.Service() {
|
||||
|
||||
@@ -72,7 +74,9 @@ class DavService: android.app.Service() {
|
||||
refreshingStatusListeners.forEach { listener ->
|
||||
listener.get()?.onDavRefreshStatusChanged(id, true)
|
||||
}
|
||||
thread { refreshCollections(id) }
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
refreshCollections(id)
|
||||
}
|
||||
}
|
||||
|
||||
ACTION_FORCE_SYNC -> {
|
||||
|
||||
71
app/src/main/java/at/bitfire/davdroid/OpenTasksWatcher.kt
Normal file
71
app/src/main/java/at/bitfire/davdroid/OpenTasksWatcher.kt
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.annotation.WorkerThread
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.model.Service
|
||||
import at.bitfire.davdroid.resource.LocalTaskList
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.ical4android.TaskProvider.ProviderName.OpenTasks
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class OpenTasksWatcher(
|
||||
context: Context
|
||||
): PackageChangedReceiver(context) {
|
||||
|
||||
companion object {
|
||||
|
||||
@WorkerThread
|
||||
fun updateTaskSync(context: Context) {
|
||||
val tasksInstalled = LocalTaskList.tasksProviderAvailable(context)
|
||||
Logger.log.info("App was launched or package was (in)installed; OpenTasks provider now available = $tasksInstalled")
|
||||
|
||||
// check all accounts and (de)activate OpenTasks if a CalDAV service is defined
|
||||
val db = AppDatabase.getInstance(context)
|
||||
db.serviceDao().getByType(Service.TYPE_CALDAV).forEach { service ->
|
||||
val account = Account(service.accountName, context.getString(R.string.account_type))
|
||||
val currentSyncable = ContentResolver.getIsSyncable(account, OpenTasks.authority)
|
||||
var enabledAnyAccount = false
|
||||
if (tasksInstalled) {
|
||||
if (currentSyncable <= 0) {
|
||||
Logger.log.info("Enabling OpenTasks sync for $account")
|
||||
ContentResolver.setIsSyncable(account, OpenTasks.authority, 1)
|
||||
AccountSettings(context, account).setSyncInterval(OpenTasks.authority, Constants.DEFAULT_SYNC_INTERVAL)
|
||||
enabledAnyAccount = true
|
||||
}
|
||||
} else if (currentSyncable != 0) {
|
||||
Logger.log.info("Disabling OpenTasks sync for $account")
|
||||
ContentResolver.setIsSyncable(account, OpenTasks.authority, 0)
|
||||
}
|
||||
|
||||
if (enabledAnyAccount && !PermissionUtils.havePermissions(context, PermissionUtils.TASKS_PERMISSIONS)) {
|
||||
Logger.log.warning("Tasks sync is now enabled for at least one account, but OpenTasks permissions are not granted")
|
||||
PermissionUtils.notifyPermissions(context, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
updateTaskSync(context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +1,25 @@
|
||||
/*
|
||||
* Copyright © Ricki Hirner (bitfire web engineering).
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the GNU Public License v3.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.accounts.Account
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.WorkerThread
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.model.Service
|
||||
import at.bitfire.davdroid.resource.LocalTaskList
|
||||
import at.bitfire.ical4android.TaskProvider.ProviderName.OpenTasks
|
||||
import kotlin.concurrent.thread
|
||||
import android.content.IntentFilter
|
||||
|
||||
class PackageChangedReceiver: BroadcastReceiver() {
|
||||
abstract class PackageChangedReceiver(
|
||||
val context: Context
|
||||
): BroadcastReceiver(), AutoCloseable {
|
||||
|
||||
companion object {
|
||||
|
||||
@WorkerThread
|
||||
fun updateTaskSync(context: Context) {
|
||||
val tasksInstalled = LocalTaskList.tasksProviderAvailable(context)
|
||||
Logger.log.info("Tasks provider available = $tasksInstalled")
|
||||
|
||||
// check all accounts and (de)activate OpenTasks if a CalDAV service is defined
|
||||
val db = AppDatabase.getInstance(context)
|
||||
db.serviceDao().getByType(Service.TYPE_CALDAV).forEach { service ->
|
||||
val account = Account(service.accountName, context.getString(R.string.account_type))
|
||||
if (tasksInstalled) {
|
||||
if (ContentResolver.getIsSyncable(account, OpenTasks.authority) <= 0) {
|
||||
ContentResolver.setIsSyncable(account, OpenTasks.authority, 1)
|
||||
ContentResolver.addPeriodicSync(account, OpenTasks.authority, Bundle(), Constants.DEFAULT_SYNC_INTERVAL)
|
||||
}
|
||||
} else
|
||||
ContentResolver.setIsSyncable(account, OpenTasks.authority, 0)
|
||||
|
||||
}
|
||||
init {
|
||||
val filter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
|
||||
addAction(Intent.ACTION_PACKAGE_CHANGED)
|
||||
addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||
addDataScheme("package")
|
||||
}
|
||||
|
||||
context.registerReceiver(this, filter)
|
||||
}
|
||||
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
thread {
|
||||
updateTaskSync(context)
|
||||
}
|
||||
override fun close() {
|
||||
context.unregisterReceiver(this)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
72
app/src/main/java/at/bitfire/davdroid/PermissionUtils.kt
Normal file
72
app/src/main/java/at/bitfire/davdroid/PermissionUtils.kt
Normal file
@@ -0,0 +1,72 @@
|
||||
package at.bitfire.davdroid
|
||||
|
||||
import android.Manifest
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import at.bitfire.davdroid.ui.NotificationUtils
|
||||
import at.bitfire.davdroid.ui.PermissionsActivity
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
|
||||
object PermissionUtils {
|
||||
|
||||
val CONTACT_PERMSSIONS = arrayOf(
|
||||
Manifest.permission.READ_CONTACTS,
|
||||
Manifest.permission.WRITE_CONTACTS
|
||||
)
|
||||
val CALENDAR_PERMISSIONS = arrayOf(
|
||||
Manifest.permission.READ_CALENDAR,
|
||||
Manifest.permission.WRITE_CALENDAR
|
||||
)
|
||||
val TASKS_PERMISSIONS = arrayOf(
|
||||
TaskProvider.PERMISSION_READ_TASKS,
|
||||
TaskProvider.PERMISSION_WRITE_TASKS
|
||||
)
|
||||
|
||||
/**
|
||||
* Checks whether at least one of the given permissions is granted.
|
||||
*
|
||||
* @param context context to check
|
||||
* @param permissions array of permissions to check
|
||||
*
|
||||
* @return whether at least one of [permissions] is granted
|
||||
*/
|
||||
fun haveAnyPermission(context: Context, permissions: Array<String>) =
|
||||
permissions.any { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED }
|
||||
|
||||
/**
|
||||
* Checks whether all given permissions are granted.
|
||||
*
|
||||
* @param context context to check
|
||||
* @param permissions array of permissions to check
|
||||
*
|
||||
* @return whether all [permissions] are granted
|
||||
*/
|
||||
fun havePermissions(context: Context, permissions: Array<String>) =
|
||||
permissions.all { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED }
|
||||
|
||||
/**
|
||||
* Shows a notification about missing permissions.
|
||||
*
|
||||
* @param context notification context
|
||||
* @param intent will be set as content Intent; if null, an Intent to launch PermissionsActivity will be used
|
||||
*/
|
||||
fun notifyPermissions(context: Context, intent: Intent?) {
|
||||
val contentIntent = intent ?: Intent(context, PermissionsActivity::class.java)
|
||||
val notify = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_ERRORS)
|
||||
.setSmallIcon(R.drawable.ic_sync_problem_notify)
|
||||
.setContentTitle(context.getString(R.string.sync_error_permissions))
|
||||
.setContentText(context.getString(R.string.sync_error_permissions_text))
|
||||
.setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
NotificationManagerCompat.from(context)
|
||||
.notify(NotificationUtils.NOTIFY_PERMISSIONS, notify)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -226,7 +226,10 @@ class LocalAddressBook(
|
||||
}
|
||||
|
||||
// make sure it will still be synchronized when contacts are updated
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
||||
if (ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) <= 0)
|
||||
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1)
|
||||
if (!ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY))
|
||||
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true)
|
||||
}
|
||||
|
||||
fun delete() {
|
||||
|
||||
@@ -117,12 +117,16 @@ class LocalCalendar private constructor(
|
||||
|
||||
// get dirty events which are required to have an increased SEQUENCE value
|
||||
for (localEvent in queryEvents("${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL", null)) {
|
||||
val event = localEvent.event!!
|
||||
val sequence = event.sequence
|
||||
if (event.sequence == null) // sequence has not been assigned yet (i.e. this event was just locally created)
|
||||
event.sequence = 0
|
||||
else if (localEvent.weAreOrganizer)
|
||||
event.sequence = sequence!! + 1
|
||||
try {
|
||||
val event = requireNotNull(localEvent.event)
|
||||
val sequence = event.sequence
|
||||
if (sequence == null) // sequence has not been assigned yet (i.e. this event was just locally created)
|
||||
event.sequence = 0
|
||||
else if (localEvent.weAreOrganizer) // increase sequence only if we're the organizer (i.e. not for attendee changes)
|
||||
event.sequence = sequence + 1
|
||||
} catch(e: Exception) {
|
||||
Logger.log.log(Level.WARNING, "Couldn't check/increase sequence", e)
|
||||
}
|
||||
dirty += localEvent
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class LocalEvent: AndroidEvent, LocalResource<Event> {
|
||||
|
||||
companion object {
|
||||
init {
|
||||
ICalendar.prodId = ProdId("+//IDN bitfire.at//${BuildConfig.userAgent}/${BuildConfig.VERSION_NAME} ical4j/" + Constants.ical4jVersion)
|
||||
ICalendar.prodId = ProdId("+//IDN bitfire.at//${BuildConfig.userAgent}/${BuildConfig.VERSION_NAME} ical4j/" + Ical4Android.ical4jVersion)
|
||||
}
|
||||
|
||||
const val COLUMN_ETAG = Events.SYNC_DATA1
|
||||
@@ -52,7 +52,6 @@ class LocalEvent: AndroidEvent, LocalResource<Event> {
|
||||
}
|
||||
|
||||
override fun populateEvent(row: ContentValues) {
|
||||
super.populateEvent(row)
|
||||
val event = requireNotNull(event)
|
||||
|
||||
event.uid = row.getAsString(Events.UID_2445)
|
||||
@@ -60,6 +59,8 @@ class LocalEvent: AndroidEvent, LocalResource<Event> {
|
||||
|
||||
val isOrganizer = row.getAsInteger(Events.IS_ORGANIZER)
|
||||
weAreOrganizer = isOrganizer != null && isOrganizer != 0
|
||||
|
||||
super.populateEvent(row)
|
||||
}
|
||||
|
||||
override fun buildEvent(recurrence: Event?, builder: ContentProviderOperation.Builder) {
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.provider.ContactsContract.CommonDataKinds.GroupMembership
|
||||
import android.provider.ContactsContract.Groups
|
||||
import android.provider.ContactsContract.RawContacts
|
||||
import android.provider.ContactsContract.RawContacts.Data
|
||||
import at.bitfire.dav4jvm.Constants
|
||||
import at.bitfire.vcard4android.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
@@ -125,12 +125,16 @@ class LocalTaskList private constructor(
|
||||
override fun findDirty(): List<LocalTask> {
|
||||
val tasks = queryTasks(Tasks._DIRTY, null)
|
||||
for (localTask in tasks) {
|
||||
val task = requireNotNull(localTask.task)
|
||||
val sequence = task.sequence
|
||||
if (sequence == null) // sequence has not been assigned yet (i.e. this task was just locally created)
|
||||
task.sequence = 0
|
||||
else
|
||||
task.sequence = sequence + 1
|
||||
try {
|
||||
val task = requireNotNull(localTask.task)
|
||||
val sequence = task.sequence
|
||||
if (sequence == null) // sequence has not been assigned yet (i.e. this task was just locally created)
|
||||
task.sequence = 0
|
||||
else // task was modified, increase sequence
|
||||
task.sequence = sequence + 1
|
||||
} catch(e: Exception) {
|
||||
Logger.log.log(Level.WARNING, "Couldn't check/increase sequence", e)
|
||||
}
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.ical4android.TaskProvider.ProviderName.OpenTasks
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.dmfs.tasks.contract.TaskContract
|
||||
@@ -437,7 +436,7 @@ class AccountSettings(
|
||||
@Suppress("unused")
|
||||
private fun update_4_5() {
|
||||
// call PackageChangedReceiver which then enables/disables OpenTasks sync when it's (not) available
|
||||
PackageChangedReceiver.updateTaskSync(context)
|
||||
OpenTasksWatcher.updateTaskSync(context)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -18,8 +18,10 @@ import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.resource.LocalAddressBook
|
||||
import at.bitfire.davdroid.ui.setup.LoginActivity
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
/**
|
||||
@@ -84,8 +86,8 @@ class AccountAuthenticatorService: Service(), OnAccountsUpdateListener {
|
||||
|
||||
|
||||
override fun onAccountsUpdated(accounts: Array<out Account>?) {
|
||||
thread {
|
||||
cleanupAccounts(this)
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
cleanupAccounts(this@AccountAuthenticatorService)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ package at.bitfire.davdroid.syncadapter
|
||||
|
||||
import android.Manifest
|
||||
import android.accounts.Account
|
||||
import android.content.*
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.SyncResult
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.provider.ContactsContract
|
||||
@@ -21,7 +24,6 @@ import at.bitfire.davdroid.model.Collection
|
||||
import at.bitfire.davdroid.model.Service
|
||||
import at.bitfire.davdroid.resource.LocalAddressBook
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.ui.account.AccountActivity
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import java.util.logging.Level
|
||||
@@ -73,14 +75,8 @@ class AddressBooksSyncAdapterService : SyncAdapterService() {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
|
||||
if (remoteAddressBooks.isEmpty())
|
||||
Logger.log.info("No contacts permission, but no address book selected for synchronization")
|
||||
else {
|
||||
// no contacts permission, but address books should be synchronized -> show notification
|
||||
val intent = Intent(context, AccountActivity::class.java)
|
||||
intent.putExtra(AccountActivity.EXTRA_ACCOUNT, account)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
notifyPermissions(intent)
|
||||
}
|
||||
else
|
||||
Logger.log.warning("No contacts permission, but address books are selected for synchronization")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import at.bitfire.davdroid.resource.LocalResource
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.ical4android.Event
|
||||
import at.bitfire.ical4android.InvalidCalendarException
|
||||
import net.fortuna.ical4j.model.Dur
|
||||
import net.fortuna.ical4j.model.component.VAlarm
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
@@ -36,6 +35,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.Reader
|
||||
import java.io.StringReader
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import java.util.logging.Level
|
||||
|
||||
@@ -154,7 +154,7 @@ class CalendarSyncManager(
|
||||
// set default reminder for non-full-day events, if requested
|
||||
val defaultAlarmMinBefore = accountSettings.getDefaultAlarm()
|
||||
if (defaultAlarmMinBefore != null && !event.isAllDay() && event.alarms.isEmpty()) {
|
||||
val alarm = VAlarm(Dur(0, 0, -defaultAlarmMinBefore, 0))
|
||||
val alarm = VAlarm(Duration.ofMinutes(-defaultAlarmMinBefore.toLong()))
|
||||
Logger.log.log(Level.FINE, "${event.uid}: Adding default alarm", alarm)
|
||||
event.alarms += alarm
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ package at.bitfire.davdroid.syncadapter
|
||||
|
||||
import android.Manifest
|
||||
import android.accounts.Account
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
@@ -19,14 +18,10 @@ import android.net.NetworkCapabilities
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.PermissionUtils
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.davdroid.ui.NotificationUtils
|
||||
import at.bitfire.davdroid.ui.account.AccountActivity
|
||||
import at.bitfire.davdroid.ui.account.SettingsActivity
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
@@ -98,7 +93,6 @@ abstract class SyncAdapterService: Service() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult)
|
||||
|
||||
override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
|
||||
@@ -114,11 +108,12 @@ abstract class SyncAdapterService: Service() {
|
||||
runningSyncs += WeakReference(currentSync)
|
||||
}
|
||||
|
||||
try {
|
||||
// required for dav4jvm (ServiceLoader)
|
||||
Thread.currentThread().contextClassLoader = context.classLoader
|
||||
// required for ServiceLoader -> ical4j -> ical4android
|
||||
Thread.currentThread().contextClassLoader = context.classLoader
|
||||
|
||||
sync(account, extras, authority, provider, syncResult)
|
||||
try {
|
||||
if (true)
|
||||
sync(account, extras, authority, provider, syncResult)
|
||||
} finally {
|
||||
synchronized(runningSyncs) {
|
||||
runningSyncs.removeAll { it.get() == null || it.get() == currentSync }
|
||||
@@ -130,15 +125,19 @@ abstract class SyncAdapterService: Service() {
|
||||
|
||||
override fun onSecurityException(account: Account, extras: Bundle, authority: String, syncResult: SyncResult) {
|
||||
Logger.log.log(Level.WARNING, "Security exception when opening content provider for $authority")
|
||||
syncResult.databaseError = true
|
||||
|
||||
val intent = Intent(context, AccountActivity::class.java)
|
||||
intent.putExtra(AccountActivity.EXTRA_ACCOUNT, account)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
notifyPermissions(intent)
|
||||
}
|
||||
|
||||
override fun onSyncCanceled() {
|
||||
Logger.log.info("Sync thread cancelled! Interrupting sync")
|
||||
super.onSyncCanceled()
|
||||
}
|
||||
|
||||
override fun onSyncCanceled(thread: Thread) {
|
||||
Logger.log.info("Sync thread ${thread.id} cancelled! Interrupting sync")
|
||||
super.onSyncCanceled(thread)
|
||||
}
|
||||
|
||||
|
||||
protected fun checkSyncConditions(settings: AccountSettings): Boolean {
|
||||
if (settings.getSyncWifiOnly()) {
|
||||
// WiFi required
|
||||
@@ -173,8 +172,7 @@ abstract class SyncAdapterService: Service() {
|
||||
val intent = Intent(context, SettingsActivity::class.java)
|
||||
intent.putExtra(SettingsActivity.EXTRA_ACCOUNT, settings.account)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
notifyPermissions(intent)
|
||||
PermissionUtils.notifyPermissions(context, intent)
|
||||
}
|
||||
|
||||
val wifi = context.applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
|
||||
@@ -188,18 +186,6 @@ abstract class SyncAdapterService: Service() {
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun notifyPermissions(intent: Intent) {
|
||||
val notify = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_ERRORS)
|
||||
.setSmallIcon(R.drawable.ic_sync_problem_notify)
|
||||
.setContentTitle(context.getString(R.string.sync_error_permissions))
|
||||
.setContentText(context.getString(R.string.sync_error_permissions_text))
|
||||
.setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
NotificationManagerCompat.from(context).notify(NotificationUtils.NOTIFY_PERMISSIONS, notify)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import at.bitfire.dav4jvm.property.GetCTag
|
||||
import at.bitfire.dav4jvm.property.GetETag
|
||||
import at.bitfire.dav4jvm.property.SyncToken
|
||||
import at.bitfire.davdroid.*
|
||||
import at.bitfire.davdroid.Constants
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.model.SyncState
|
||||
import at.bitfire.davdroid.resource.*
|
||||
@@ -36,8 +35,14 @@ import at.bitfire.davdroid.ui.DebugInfoActivity
|
||||
import at.bitfire.davdroid.ui.NotificationUtils
|
||||
import at.bitfire.davdroid.ui.account.SettingsActivity
|
||||
import at.bitfire.ical4android.CalendarStorageException
|
||||
import at.bitfire.ical4android.Ical4Android
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.ical4android.UsesThreadContextClassLoader
|
||||
import at.bitfire.vcard4android.ContactsStorageException
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.RequestBody
|
||||
import org.apache.commons.lang3.exception.ContextedException
|
||||
@@ -47,13 +52,17 @@ import java.io.InterruptedIOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.security.cert.CertificateException
|
||||
import java.util.*
|
||||
import java.util.concurrent.*
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Future
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.logging.Level
|
||||
import javax.net.ssl.SSLHandshakeException
|
||||
import kotlin.math.min
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@UsesThreadContextClassLoader
|
||||
abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: LocalCollection<ResourceType>, RemoteType: DavCollection>(
|
||||
val context: Context,
|
||||
val account: Account,
|
||||
@@ -70,18 +79,21 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val MAX_PROCESSING_THREADS = // nCPU/2 (rounded up for case of 1 CPU), but max. 4
|
||||
min((Runtime.getRuntime().availableProcessors()+1)/2, 4)
|
||||
val MAX_DOWNLOAD_THREADS = // one (if one CPU), 2 otherwise
|
||||
min(Runtime.getRuntime().availableProcessors(), 2)
|
||||
const val MAX_MULTIGET_RESOURCES = 10
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
Logger.log.info("SyncManager: using up to $MAX_PROCESSING_THREADS processing threads and $MAX_DOWNLOAD_THREADS download threads")
|
||||
// required for ServiceLoader -> ical4j -> ical4android
|
||||
Ical4Android.checkThreadContextClassLoader()
|
||||
}
|
||||
/**
|
||||
* We use our own dispatcher to make sure that all threads have [Thread.getContextClassLoader] set,
|
||||
* which is required for dav4jvm and ical4j (because they rely on [ServiceLoader]).
|
||||
*/
|
||||
private val workDispatcher = Executors.newFixedThreadPool(
|
||||
// number of threads = number of CPUs, but max. 4
|
||||
min(Runtime.getRuntime().availableProcessors(), 4)
|
||||
).asCoroutineDispatcher()
|
||||
|
||||
private val mainAccount = if (localCollection is LocalAddressBook)
|
||||
localCollection.mainAccount
|
||||
@@ -113,16 +125,13 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
|
||||
Logger.log.info("No reason to synchronize, aborting")
|
||||
return@unwrapExceptions
|
||||
}
|
||||
abortIfCancelled()
|
||||
|
||||
Logger.log.info("Querying server capabilities")
|
||||
var remoteSyncState = queryCapabilities()
|
||||
abortIfCancelled()
|
||||
|
||||
Logger.log.info("Sending local deletes/updates to server")
|
||||
val modificationsSent = processLocallyDeleted() ||
|
||||
uploadDirty()
|
||||
abortIfCancelled()
|
||||
|
||||
if (extras.containsKey(SyncAdapterService.SYNC_EXTRAS_FULL_RESYNC)) {
|
||||
Logger.log.info("Forcing re-synchronization of all entries")
|
||||
@@ -276,7 +285,6 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
|
||||
// but only if they don't have changed on the server. Then finally remove them from the local address book.
|
||||
val localList = localCollection.findDeleted()
|
||||
for (local in localList) {
|
||||
abortIfCancelled()
|
||||
useLocal(local) {
|
||||
val fileName = local.fileName
|
||||
if (fileName != null) {
|
||||
@@ -315,56 +323,58 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
|
||||
local.assignNameAndUID()
|
||||
}
|
||||
|
||||
// upload dirty resources
|
||||
for (local in localCollection.findDirty())
|
||||
useLocal(local) {
|
||||
abortIfCancelled()
|
||||
// upload dirty resources (parallelized)
|
||||
runBlocking(workDispatcher) {
|
||||
for (local in localCollection.findDirty())
|
||||
launch {
|
||||
useLocal(local) {
|
||||
val fileName = local.fileName!!
|
||||
useRemote(DavResource(httpClient.okHttpClient, collectionURL.newBuilder().addPathSegment(fileName).build())) { remote ->
|
||||
// generate entity to upload (VCard, iCal, whatever)
|
||||
val body = prepareUpload(local)
|
||||
|
||||
val fileName = local.fileName!!
|
||||
useRemote(DavResource(httpClient.okHttpClient, collectionURL.newBuilder().addPathSegment(fileName).build())) { remote ->
|
||||
// generate entity to upload (VCard, iCal, whatever)
|
||||
val body = prepareUpload(local)
|
||||
var eTag: String? = null
|
||||
val processETag: (response: okhttp3.Response) -> Unit = { response ->
|
||||
response.header("ETag")?.let { getETag ->
|
||||
eTag = GetETag(getETag).eTag
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (local.eTag == null) {
|
||||
Logger.log.info("Uploading new record $fileName")
|
||||
remote.put(body, null, true, processETag)
|
||||
} else {
|
||||
Logger.log.info("Uploading locally modified record $fileName")
|
||||
remote.put(body, local.eTag, false, processETag)
|
||||
}
|
||||
numUploaded++
|
||||
} catch(e: ForbiddenException) {
|
||||
// HTTP 403 Forbidden
|
||||
// If and only if the upload failed because of missing permissions, treat it like 412.
|
||||
if (e.errors.contains(Error.NEED_PRIVILEGES))
|
||||
Logger.log.log(Level.INFO, "Couldn't upload because of missing permissions, ignoring", e)
|
||||
else
|
||||
throw e
|
||||
} catch(e: ConflictException) {
|
||||
// HTTP 409 Conflict
|
||||
// We can't interact with the user to resolve the conflict, so we treat 409 like 412.
|
||||
Logger.log.log(Level.INFO, "Edit conflict, ignoring", e)
|
||||
} catch(e: PreconditionFailedException) {
|
||||
// HTTP 412 Precondition failed: Resource has been modified on the server in the meanwhile.
|
||||
// Ignore this condition so that the resource can be downloaded and reset again.
|
||||
Logger.log.log(Level.INFO, "Resource has been modified on the server before upload, ignoring", e)
|
||||
}
|
||||
|
||||
var eTag: String? = null
|
||||
val processETag: (response: okhttp3.Response) -> Unit = { response ->
|
||||
response.header("ETag")?.let { getETag ->
|
||||
eTag = GetETag(getETag).eTag
|
||||
if (eTag != null)
|
||||
Logger.log.fine("Received new ETag=$eTag after uploading")
|
||||
else
|
||||
Logger.log.fine("Didn't receive new ETag after uploading, setting to null")
|
||||
|
||||
local.clearDirty(eTag)
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (local.eTag == null) {
|
||||
Logger.log.info("Uploading new record $fileName")
|
||||
remote.put(body, null, true, processETag)
|
||||
} else {
|
||||
Logger.log.info("Uploading locally modified record $fileName")
|
||||
remote.put(body, local.eTag, false, processETag)
|
||||
}
|
||||
numUploaded++
|
||||
} catch(e: ForbiddenException) {
|
||||
// HTTP 403 Forbidden
|
||||
// If and only if the upload failed because of missing permissions, treat it like 412.
|
||||
if (e.errors.contains(Error.NEED_PRIVILEGES))
|
||||
Logger.log.log(Level.INFO, "Couldn't upload because of missing permissions, ignoring", e)
|
||||
else
|
||||
throw e
|
||||
} catch(e: ConflictException) {
|
||||
// HTTP 409 Conflict
|
||||
// We can't interact with the user to resolve the conflict, so we treat 409 like 412.
|
||||
Logger.log.log(Level.INFO, "Edit conflict, ignoring", e)
|
||||
} catch(e: PreconditionFailedException) {
|
||||
// HTTP 412 Precondition failed: Resource has been modified on the server in the meanwhile.
|
||||
// Ignore this condition so that the resource can be downloaded and reset again.
|
||||
Logger.log.log(Level.INFO, "Resource has been modified on the server before upload, ignoring", e)
|
||||
}
|
||||
|
||||
if (eTag != null)
|
||||
Logger.log.fine("Received new ETag=$eTag after uploading")
|
||||
else
|
||||
Logger.log.fine("Didn't receive new ETag after uploading, setting to null")
|
||||
|
||||
local.clearDirty(eTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.log.info("Sent $numUploaded record(s) to server")
|
||||
return numUploaded > 0
|
||||
}
|
||||
@@ -437,116 +447,87 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
|
||||
* @param listRemote function to list remote resources (for instance, all since a certain sync-token)
|
||||
*/
|
||||
protected open fun syncRemote(listRemote: (DavResponseCallback) -> Unit) {
|
||||
// results must be processed in main thread because exceptions must be thrown in main
|
||||
// thread, so that they can be catched by SyncManager
|
||||
val results = ConcurrentLinkedQueue<Future<*>>()
|
||||
|
||||
// thread-safe sync stats
|
||||
val nInserted = AtomicInteger()
|
||||
val nUpdated = AtomicInteger()
|
||||
val nDeleted = AtomicInteger()
|
||||
val nSkipped = AtomicInteger()
|
||||
|
||||
// download queue
|
||||
val toDownload = LinkedBlockingQueue<HttpUrl>()
|
||||
runBlocking(workDispatcher) {
|
||||
// download queue
|
||||
val toDownload = LinkedBlockingQueue<HttpUrl>()
|
||||
fun download(url: HttpUrl?) {
|
||||
if (url != null)
|
||||
toDownload += url
|
||||
|
||||
// tasks from this executor create the download tasks (if necessary)
|
||||
val processor = ThreadPoolExecutor(1, MAX_PROCESSING_THREADS,
|
||||
10, TimeUnit.SECONDS,
|
||||
LinkedBlockingQueue(MAX_PROCESSING_THREADS), // accept up to MAX_PROCESSING_THREADS processing tasks
|
||||
ThreadPoolExecutor.CallerRunsPolicy() // if the queue is full, run task in submitting thread
|
||||
)
|
||||
|
||||
// this executor runs the actual download tasks
|
||||
val downloader = ThreadPoolExecutor(0, MAX_DOWNLOAD_THREADS,
|
||||
10, TimeUnit.SECONDS,
|
||||
LinkedBlockingQueue(MAX_DOWNLOAD_THREADS), // accept up to MAX_DOWNLOAD_THREADS download tasks
|
||||
ThreadPoolExecutor.CallerRunsPolicy() // if the queue is full, run task in submitting thread
|
||||
)
|
||||
fun downloadBunch() {
|
||||
val bunch = LinkedList<HttpUrl>()
|
||||
toDownload.drainTo(bunch, MAX_MULTIGET_RESOURCES)
|
||||
results += downloader.submit {
|
||||
downloadRemote(bunch)
|
||||
}
|
||||
}
|
||||
|
||||
listRemote { response, relation ->
|
||||
// ignore non-members
|
||||
if (relation != Response.HrefRelation.MEMBER)
|
||||
return@listRemote
|
||||
|
||||
// ignore collections
|
||||
if (response[at.bitfire.dav4jvm.property.ResourceType::class.java]?.types?.contains(at.bitfire.dav4jvm.property.ResourceType.COLLECTION) == true)
|
||||
return@listRemote
|
||||
|
||||
val name = response.hrefName()
|
||||
|
||||
if (response.isSuccess()) {
|
||||
Logger.log.fine("Found remote resource: $name")
|
||||
|
||||
results += processor.submit {
|
||||
useLocal(localCollection.findByName(name)) { local ->
|
||||
if (local == null) {
|
||||
Logger.log.info("$name has been added remotely")
|
||||
toDownload += response.href
|
||||
nInserted.incrementAndGet()
|
||||
} else {
|
||||
val localETag = local.eTag
|
||||
val remoteETag = response[GetETag::class.java]?.eTag ?: throw DavException("Server didn't provide ETag")
|
||||
if (localETag == remoteETag) {
|
||||
Logger.log.info("$name has not been changed on server (ETag still $remoteETag)")
|
||||
nSkipped.incrementAndGet()
|
||||
} else {
|
||||
Logger.log.info("$name has been changed on server (current ETag=$remoteETag, last known ETag=$localETag)")
|
||||
toDownload += response.href
|
||||
nUpdated.incrementAndGet()
|
||||
}
|
||||
|
||||
// mark as remotely present, so that this resource won't be deleted at the end
|
||||
local.updateFlags(LocalResource.FLAG_REMOTELY_PRESENT)
|
||||
if (toDownload.size >= MAX_MULTIGET_RESOURCES || url == null) {
|
||||
while (toDownload.size > 0) {
|
||||
val bunch = LinkedList<HttpUrl>()
|
||||
toDownload.drainTo(bunch, MAX_MULTIGET_RESOURCES)
|
||||
launch {
|
||||
downloadRemote(bunch)
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(processor) {
|
||||
if (toDownload.size >= MAX_MULTIGET_RESOURCES)
|
||||
// download another bunch of MAX_MULTIGET_RESOURCES resources
|
||||
downloadBunch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (response.status?.code == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
// collection sync: resource has been deleted on remote server
|
||||
results += processor.submit {
|
||||
useLocal(localCollection.findByName(name)) { local ->
|
||||
Logger.log.info("$name has been deleted on server, deleting locally")
|
||||
local?.delete()
|
||||
nDeleted.incrementAndGet()
|
||||
coroutineScope {
|
||||
listRemote { response, relation ->
|
||||
// ignore non-members
|
||||
if (relation != Response.HrefRelation.MEMBER)
|
||||
return@listRemote
|
||||
|
||||
// ignore collections
|
||||
if (response[at.bitfire.dav4jvm.property.ResourceType::class.java]?.types?.contains(at.bitfire.dav4jvm.property.ResourceType.COLLECTION) == true)
|
||||
return@listRemote
|
||||
|
||||
val name = response.hrefName()
|
||||
|
||||
if (response.isSuccess()) {
|
||||
Logger.log.fine("Found remote resource: $name")
|
||||
|
||||
launch {
|
||||
useLocal(localCollection.findByName(name)) { local ->
|
||||
if (local == null) {
|
||||
Logger.log.info("$name has been added remotely, queueing download")
|
||||
download(response.href)
|
||||
nInserted.incrementAndGet()
|
||||
} else {
|
||||
val localETag = local.eTag
|
||||
val remoteETag = response[GetETag::class.java]?.eTag
|
||||
?: throw DavException("Server didn't provide ETag")
|
||||
if (localETag == remoteETag) {
|
||||
Logger.log.info("$name has not been changed on server (ETag still $remoteETag)")
|
||||
nSkipped.incrementAndGet()
|
||||
} else {
|
||||
Logger.log.info("$name has been changed on server (current ETag=$remoteETag, last known ETag=$localETag)")
|
||||
download(response.href)
|
||||
nUpdated.incrementAndGet()
|
||||
}
|
||||
|
||||
// mark as remotely present, so that this resource won't be deleted at the end
|
||||
local.updateFlags(LocalResource.FLAG_REMOTELY_PRESENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (response.status?.code == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
// collection sync: resource has been deleted on remote server
|
||||
launch {
|
||||
useLocal(localCollection.findByName(name)) { local ->
|
||||
Logger.log.info("$name has been deleted on server, deleting locally")
|
||||
local?.delete()
|
||||
nDeleted.incrementAndGet()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check already available results for exceptions so that they don't become too many
|
||||
checkResults(results)
|
||||
// download remaining resources
|
||||
download(null)
|
||||
}
|
||||
|
||||
// process remaining responses
|
||||
processor.shutdown()
|
||||
if (!processor.awaitTermination(5, TimeUnit.MINUTES))
|
||||
throw TimeoutException("Processing the remote resource list took too long")
|
||||
|
||||
// download remaining resources
|
||||
if (toDownload.isNotEmpty())
|
||||
downloadBunch()
|
||||
|
||||
// signal end of queue and wait for download thread
|
||||
downloader.shutdown()
|
||||
if (!downloader.awaitTermination(5, TimeUnit.MINUTES))
|
||||
throw TimeoutException("Downloading and processing the remote resources took too long")
|
||||
|
||||
// check remaining results for exceptions
|
||||
checkResults(results)
|
||||
|
||||
// update sync stats
|
||||
with(syncResult.stats) {
|
||||
numInserts += nInserted.get()
|
||||
@@ -629,17 +610,6 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
|
||||
|
||||
// sync helpers
|
||||
|
||||
/**
|
||||
* Throws an [InterruptedException] if the current thread has been interrupted,
|
||||
* most probably because synchronization was cancelled by the user.
|
||||
*
|
||||
* @throws InterruptedException (which will be caught by [performSync])
|
||||
* */
|
||||
protected fun abortIfCancelled() {
|
||||
if (Thread.interrupted())
|
||||
throw InterruptedException("Sync was cancelled")
|
||||
}
|
||||
|
||||
protected fun syncState(dav: Response) =
|
||||
dav[SyncToken::class.java]?.token?.let {
|
||||
SyncState(SyncState.Type.SYNC_TOKEN, it)
|
||||
@@ -797,6 +767,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
|
||||
}
|
||||
|
||||
|
||||
@Deprecated("Use Kotlin coroutines instead")
|
||||
fun checkResults(results: MutableCollection<Future<*>>) {
|
||||
val iter = results.iterator()
|
||||
while (iter.hasNext()) {
|
||||
|
||||
@@ -28,7 +28,6 @@ import at.bitfire.davdroid.resource.LocalResource
|
||||
import at.bitfire.davdroid.resource.LocalTask
|
||||
import at.bitfire.davdroid.resource.LocalTaskList
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.ical4android.Constants
|
||||
import at.bitfire.ical4android.InvalidCalendarException
|
||||
import at.bitfire.ical4android.Task
|
||||
import okhttp3.HttpUrl
|
||||
@@ -115,7 +114,7 @@ class TasksSyncManager(
|
||||
|
||||
override fun postProcess() {
|
||||
val touched = localCollection.touchRelations()
|
||||
Constants.log.info("Touched $touched relations")
|
||||
Logger.log.info("Touched $touched relations")
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
@@ -26,6 +26,7 @@ import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import at.bitfire.davdroid.App
|
||||
@@ -36,12 +37,13 @@ import kotlinx.android.synthetic.main.about.*
|
||||
import kotlinx.android.synthetic.main.about_languages.*
|
||||
import kotlinx.android.synthetic.main.about_translation.view.*
|
||||
import kotlinx.android.synthetic.main.activity_about.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.json.JSONObject
|
||||
import java.text.Collator
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class AboutActivity: AppCompatActivity() {
|
||||
|
||||
@@ -108,7 +110,7 @@ class AboutActivity: AppCompatActivity() {
|
||||
inflater.inflate(R.layout.about, container, false)!!
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
app_name.text = getString(R.string.app_name)
|
||||
app_name.setText(R.string.app_name)
|
||||
app_version.text = getString(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
|
||||
build_time.text = getString(R.string.about_build_date, SimpleDateFormat.getDateInstance().format(BuildConfig.buildTime))
|
||||
|
||||
@@ -212,8 +214,9 @@ class AboutActivity: AppCompatActivity() {
|
||||
@UiThread
|
||||
fun initialize(assetName: String, html: Boolean) {
|
||||
if (initialized) return
|
||||
initialized = true
|
||||
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
getApplication<Application>().resources.assets.open(assetName).use {
|
||||
val raw = IOUtils.toString(it, Charsets.UTF_8)
|
||||
if (html) {
|
||||
@@ -223,8 +226,6 @@ class AboutActivity: AppCompatActivity() {
|
||||
plainText.postValue(raw)
|
||||
}
|
||||
}
|
||||
|
||||
initialized = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,8 +27,10 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.accounts_content.*
|
||||
import kotlinx.android.synthetic.main.activity_accounts.*
|
||||
import kotlinx.android.synthetic.main.activity_accounts.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, SyncStatusObserver {
|
||||
|
||||
@@ -49,10 +51,10 @@ class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSele
|
||||
settings = Settings.getInstance(this)
|
||||
|
||||
if (savedInstanceState == null)
|
||||
thread {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
// use a separate thread to check whether IntroActivity should be shown
|
||||
if (IntroActivity.shouldShowIntroActivity(this)) {
|
||||
val intro = Intent(this, IntroActivity::class.java)
|
||||
if (IntroActivity.shouldShowIntroActivity(this@AccountsActivity)) {
|
||||
val intro = Intent(this@AccountsActivity, IntroActivity::class.java)
|
||||
startActivityForResult(intro, REQUEST_INTRO)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import at.bitfire.davdroid.BuildConfig
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.settings.Settings
|
||||
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsFragment
|
||||
import at.bitfire.davdroid.ui.intro.IntroActivity
|
||||
import at.bitfire.davdroid.ui.intro.OpenSourceFragment
|
||||
import at.bitfire.davdroid.ui.intro.OpenTasksFragment
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
@@ -21,15 +21,17 @@ import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.databinding.ActivityCreateAddressBookBinding
|
||||
import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.model.Collection
|
||||
import at.bitfire.davdroid.model.Service
|
||||
import at.bitfire.davdroid.ui.account.AccountActivity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class CreateAddressBookActivity: AppCompatActivity() {
|
||||
|
||||
@@ -119,7 +121,7 @@ class CreateAddressBookActivity: AppCompatActivity() {
|
||||
return
|
||||
this.account = account
|
||||
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
// load account info
|
||||
val adapter = HomeSetAdapter(getApplication())
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.bitfire.davdroid.Constants
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.databinding.ActivityCreateCalendarBinding
|
||||
@@ -36,10 +37,11 @@ import at.bitfire.ical4android.DateUtils
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
import kotlinx.android.synthetic.main.activity_create_calendar.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import net.fortuna.ical4j.model.Calendar
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class CreateCalendarActivity: AppCompatActivity(), ColorPickerDialogListener {
|
||||
|
||||
@@ -228,7 +230,7 @@ class CreateCalendarActivity: AppCompatActivity(), ColorPickerDialogListener {
|
||||
supportVTODO.value = true
|
||||
supportVJOURNAL.value = true
|
||||
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
// load account info
|
||||
val adapter = HomeSetAdapter(getApplication())
|
||||
|
||||
|
||||
@@ -25,11 +25,13 @@ import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.model.Collection
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import java.io.IOException
|
||||
import java.io.StringWriter
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class CreateCollectionFragment: DialogFragment() {
|
||||
|
||||
@@ -101,8 +103,8 @@ class CreateCollectionFragment: DialogFragment() {
|
||||
|
||||
|
||||
class Model(
|
||||
application: Application
|
||||
): AndroidViewModel(application) {
|
||||
app: Application
|
||||
): AndroidViewModel(app) {
|
||||
|
||||
lateinit var account: Account
|
||||
lateinit var serviceType: String
|
||||
@@ -111,7 +113,7 @@ class CreateCollectionFragment: DialogFragment() {
|
||||
val result = MutableLiveData<Exception>()
|
||||
|
||||
fun createCollection(): LiveData<Exception> {
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.IO + NonCancellable) {
|
||||
HttpClient.Builder(getApplication(), AccountSettings(getApplication(), account))
|
||||
.setForeground(true)
|
||||
.build().use { httpClient ->
|
||||
|
||||
@@ -37,6 +37,7 @@ import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.bitfire.dav4jvm.exception.HttpException
|
||||
import at.bitfire.davdroid.BuildConfig
|
||||
import at.bitfire.davdroid.InvalidAccountException
|
||||
@@ -47,13 +48,14 @@ import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.resource.LocalAddressBook
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils
|
||||
import org.dmfs.tasks.contract.TaskContract
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class DebugInfoActivity: AppCompatActivity() {
|
||||
|
||||
@@ -128,7 +130,7 @@ class DebugInfoActivity: AppCompatActivity() {
|
||||
Logger.log.info("Generating debug info report")
|
||||
initialized = true
|
||||
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
val context = getApplication<Application>()
|
||||
val text = StringBuilder("--- BEGIN DEBUG INFO ---\n")
|
||||
|
||||
@@ -301,7 +303,9 @@ class DebugInfoActivity: AppCompatActivity() {
|
||||
accountSettings.getSyncWifiOnlySSIDs()?.let {
|
||||
text.append(", SSIDs: ${accountSettings.getSyncWifiOnlySSIDs()}")
|
||||
}
|
||||
text.append("\n [CardDAV] Contact group method: ${accountSettings.getGroupMethod()}")
|
||||
text .append("\n getIsSyncable(CalendarContract): ${ContentResolver.getIsSyncable(acct, CalendarContract.AUTHORITY)}")
|
||||
.append("\n getIsSyncable(OpenTasks): ${ContentResolver.getIsSyncable(acct, TaskProvider.ProviderName.OpenTasks.authority)}")
|
||||
.append("\n [CardDAV] Contact group method: ${accountSettings.getGroupMethod()}")
|
||||
.append("\n [CalDAV] Time range (past days): ${accountSettings.getTimeRangePastDays()}")
|
||||
.append("\n Manage calendar colors: ${accountSettings.getManageCalendarColors()}")
|
||||
.append("\n Use event colors: ${accountSettings.getEventColors()}")
|
||||
|
||||
@@ -29,6 +29,8 @@ class DefaultAccountsDrawerHandler: IAccountsDrawerHandler {
|
||||
override fun initMenu(context: Context, menu: Menu) {
|
||||
if (BuildConfig.VERSION_NAME.contains("-beta") || BuildConfig.VERSION_NAME.contains("-rc"))
|
||||
menu.findItem(R.id.nav_beta_feedback).isVisible = true
|
||||
if (/* ose */ true)
|
||||
menu.findItem(R.id.nav_donate).isVisible = true
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(activity: Activity, item: MenuItem): Boolean {
|
||||
|
||||
@@ -23,7 +23,9 @@ import at.bitfire.davdroid.databinding.DeleteCollectionBinding
|
||||
import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.model.Collection
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import kotlin.concurrent.thread
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DeleteCollectionFragment: DialogFragment() {
|
||||
|
||||
@@ -97,15 +99,15 @@ class DeleteCollectionFragment: DialogFragment() {
|
||||
this.account = account
|
||||
|
||||
if (collectionInfo == null)
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
collectionInfo = db.collectionDao().get(collectionId)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteCollection(): LiveData<Exception> {
|
||||
thread {
|
||||
val account = account ?: return@thread
|
||||
val collectionInfo = collectionInfo ?: return@thread
|
||||
viewModelScope.launch(Dispatchers.IO + NonCancellable) {
|
||||
val account = account ?: return@launch
|
||||
val collectionInfo = collectionInfo ?: return@launch
|
||||
|
||||
val context = getApplication<Application>()
|
||||
HttpClient.Builder(context, AccountSettings(context, account))
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package at.bitfire.davdroid.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
class PermissionsActivity: AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (savedInstanceState == null)
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(android.R.id.content, PermissionsFragment())
|
||||
.commit()
|
||||
}
|
||||
|
||||
}
|
||||
136
app/src/main/java/at/bitfire/davdroid/ui/PermissionsFragment.kt
Normal file
136
app/src/main/java/at/bitfire/davdroid/ui/PermissionsFragment.kt
Normal file
@@ -0,0 +1,136 @@
|
||||
package at.bitfire.davdroid.ui
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import at.bitfire.davdroid.BuildConfig
|
||||
import at.bitfire.davdroid.PackageChangedReceiver
|
||||
import at.bitfire.davdroid.PermissionUtils.CALENDAR_PERMISSIONS
|
||||
import at.bitfire.davdroid.PermissionUtils.CONTACT_PERMSSIONS
|
||||
import at.bitfire.davdroid.PermissionUtils.TASKS_PERMISSIONS
|
||||
import at.bitfire.davdroid.PermissionUtils.havePermissions
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.databinding.ActivityPermissionsBinding
|
||||
import at.bitfire.davdroid.resource.LocalTaskList
|
||||
|
||||
class PermissionsFragment: Fragment() {
|
||||
|
||||
lateinit var model: Model
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
model = ViewModelProvider(this).get(Model::class.java)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val binding = ActivityPermissionsBinding.inflate(inflater, container, false)
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
binding.model = model
|
||||
|
||||
binding.text.text = getString(R.string.permissions_text, getString(R.string.app_name))
|
||||
|
||||
model.needContactsPermissions.observe(viewLifecycleOwner, Observer { needContacts ->
|
||||
if (needContacts && model.haveContactsPermissions.value == false)
|
||||
requestPermissions(CONTACT_PERMSSIONS, 0)
|
||||
})
|
||||
model.needCalendarPermissions.observe(viewLifecycleOwner, Observer { needCalendars ->
|
||||
if (needCalendars && model.haveCalendarPermissions.value == false)
|
||||
requestPermissions(CALENDAR_PERMISSIONS, 0)
|
||||
})
|
||||
model.needTasksPermissions.observe(viewLifecycleOwner, Observer { needTasks ->
|
||||
if (needTasks == true && model.haveTasksPermissions.value == false)
|
||||
requestPermissions(TASKS_PERMISSIONS, 0)
|
||||
})
|
||||
model.needAllPermissions.observe(viewLifecycleOwner, Observer { needAll ->
|
||||
if (needAll && model.haveAllPermissions.value == false) {
|
||||
val all = CONTACT_PERMSSIONS + CALENDAR_PERMISSIONS +
|
||||
if (model.haveTasksPermissions.value != null) TASKS_PERMISSIONS else emptyArray()
|
||||
requestPermissions(all, 0)
|
||||
}
|
||||
})
|
||||
|
||||
binding.appSettings.setOnClickListener {
|
||||
val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.fromParts("package", BuildConfig.APPLICATION_ID, null))
|
||||
if (intent.resolveActivity(requireActivity().packageManager) != null)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
model.checkPermissions()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
model.checkPermissions()
|
||||
}
|
||||
|
||||
|
||||
class Model(app: Application): AndroidViewModel(app) {
|
||||
|
||||
val haveContactsPermissions = MutableLiveData<Boolean>()
|
||||
val needContactsPermissions = MutableLiveData<Boolean>()
|
||||
val haveCalendarPermissions = MutableLiveData<Boolean>()
|
||||
val needCalendarPermissions = MutableLiveData<Boolean>()
|
||||
|
||||
val haveTasksPermissions = MutableLiveData<Boolean>()
|
||||
val needTasksPermissions = MutableLiveData<Boolean>()
|
||||
val tasksWatcher = object: PackageChangedReceiver(app) {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
checkPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
val haveAllPermissions = MutableLiveData<Boolean>()
|
||||
val needAllPermissions = MutableLiveData<Boolean>()
|
||||
|
||||
init {
|
||||
checkPermissions()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
tasksWatcher.close()
|
||||
}
|
||||
|
||||
fun checkPermissions() {
|
||||
val contactPermissions = havePermissions(getApplication(), CONTACT_PERMSSIONS)
|
||||
haveContactsPermissions.value = contactPermissions
|
||||
needContactsPermissions.value = contactPermissions
|
||||
|
||||
val calendarPermissions = havePermissions(getApplication(), CALENDAR_PERMISSIONS)
|
||||
haveCalendarPermissions.value = calendarPermissions
|
||||
needCalendarPermissions.value = calendarPermissions
|
||||
|
||||
val tasksAvailable = LocalTaskList.tasksProviderAvailable(getApplication())
|
||||
var tasksPermissions: Boolean? = null
|
||||
if (tasksAvailable) {
|
||||
tasksPermissions = havePermissions(getApplication(), TASKS_PERMISSIONS)
|
||||
haveTasksPermissions.value = tasksPermissions
|
||||
needTasksPermissions.value = tasksPermissions
|
||||
} else {
|
||||
haveTasksPermissions.value = null
|
||||
needTasksPermissions.value = null
|
||||
}
|
||||
|
||||
val allPermissions = contactPermissions && calendarPermissions && (!tasksAvailable || tasksPermissions == true)
|
||||
haveAllPermissions.value = allPermissions
|
||||
needAllPermissions.value = allPermissions
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +1,18 @@
|
||||
package at.bitfire.davdroid.ui.account
|
||||
|
||||
import android.Manifest
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import androidx.lifecycle.*
|
||||
@@ -26,14 +22,14 @@ import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.model.Collection
|
||||
import at.bitfire.davdroid.model.Service
|
||||
import at.bitfire.davdroid.resource.LocalTaskList
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.davdroid.ui.PermissionsActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.activity_account.*
|
||||
import java.util.concurrent.Executors
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class AccountActivity: AppCompatActivity() {
|
||||
|
||||
@@ -50,7 +46,7 @@ class AccountActivity: AppCompatActivity() {
|
||||
model = ViewModelProvider(this).get(Model::class.java)
|
||||
(intent.getParcelableExtra(EXTRA_ACCOUNT) as? Account)?.let { account ->
|
||||
model.initialize(account)
|
||||
}
|
||||
} ?: throw IllegalArgumentException("AccountActivity requires EXTRA_ACCOUNT")
|
||||
|
||||
title = model.account.name
|
||||
setContentView(R.layout.activity_account)
|
||||
@@ -67,11 +63,6 @@ class AccountActivity: AppCompatActivity() {
|
||||
tabsAdapter.calDavSvcId = it
|
||||
})
|
||||
|
||||
model.askForPermissions.observe(this, Observer { permissions ->
|
||||
if (permissions.isNotEmpty())
|
||||
ActivityCompat.requestPermissions(this, permissions.toTypedArray(), 0)
|
||||
})
|
||||
|
||||
sync.setOnClickListener {
|
||||
DavUtils.requestSync(this, model.account)
|
||||
Snackbar.make(view_pager, R.string.account_synchronizing_now, Snackbar.LENGTH_LONG).show()
|
||||
@@ -83,11 +74,6 @@ class AccountActivity: AppCompatActivity() {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
if (grantResults.contains(PackageManager.PERMISSION_GRANTED))
|
||||
model.gotPermissions()
|
||||
}
|
||||
|
||||
|
||||
// menu actions
|
||||
|
||||
@@ -141,11 +127,19 @@ class AccountActivity: AppCompatActivity() {
|
||||
}
|
||||
|
||||
|
||||
// other actions
|
||||
|
||||
fun startPermissionsActivity(view: View) {
|
||||
startActivity(Intent(this, PermissionsActivity::class.java))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// adapter
|
||||
|
||||
class TabsAdapter(
|
||||
val activity: AppCompatActivity
|
||||
): FragmentStatePagerAdapter(activity.supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
): FragmentStatePagerAdapter(activity.supportFragmentManager, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
|
||||
var cardDavSvcId: Long? = null
|
||||
set(value) {
|
||||
@@ -234,22 +228,10 @@ class AccountActivity: AppCompatActivity() {
|
||||
private set
|
||||
|
||||
private val db = AppDatabase.getInstance(application)
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
|
||||
val cardDavService = MutableLiveData<Long>()
|
||||
val calDavService = MutableLiveData<Long>()
|
||||
|
||||
private val needContactPermissions: LiveData<Boolean> = Transformations.switchMap(cardDavService) { cardDavId ->
|
||||
if (cardDavId != null)
|
||||
db.collectionDao().observeHasSyncByService(cardDavId)
|
||||
else
|
||||
MutableLiveData<Boolean>().apply { value = false }
|
||||
}
|
||||
private val needCalendarPermissions: LiveData<Boolean> = Transformations.map(calDavService) { calDavId ->
|
||||
calDavId != null
|
||||
}
|
||||
val askForPermissions = PermissionCalculator(application, needContactPermissions, needCalendarPermissions)
|
||||
|
||||
|
||||
@MainThread
|
||||
fun initialize(account: Account) {
|
||||
@@ -259,85 +241,24 @@ class AccountActivity: AppCompatActivity() {
|
||||
|
||||
this.account = account
|
||||
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
cardDavService.postValue(db.serviceDao().getIdByAccountAndType(account.name, Service.TYPE_CARDDAV))
|
||||
calDavService.postValue(db.serviceDao().getIdByAccountAndType(account.name, Service.TYPE_CALDAV))
|
||||
}
|
||||
}
|
||||
|
||||
fun gotPermissions() {
|
||||
askForPermissions.calculate()
|
||||
}
|
||||
|
||||
fun toggleSync(item: Collection) =
|
||||
executor.execute {
|
||||
val newItem = item.copy(sync = !item.sync)
|
||||
db.collectionDao().update(newItem)
|
||||
}
|
||||
|
||||
fun toggleReadOnly(item: Collection) =
|
||||
executor.execute {
|
||||
val newItem = item.copy(forceReadOnly = !item.forceReadOnly)
|
||||
db.collectionDao().update(newItem)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PermissionCalculator(
|
||||
val context: Context,
|
||||
needContactPermissions: LiveData<Boolean>,
|
||||
needCalendarPermissions: LiveData<Boolean>
|
||||
): MediatorLiveData<List<String>>() {
|
||||
|
||||
companion object {
|
||||
val contactPermissions = arrayOf(
|
||||
Manifest.permission.READ_CONTACTS,
|
||||
Manifest.permission.WRITE_CONTACTS
|
||||
)
|
||||
val calendarPermissions = arrayOf(
|
||||
Manifest.permission.READ_CALENDAR,
|
||||
Manifest.permission.WRITE_CALENDAR
|
||||
)
|
||||
val taskPermissions = arrayOf(
|
||||
TaskProvider.PERMISSION_READ_TASKS,
|
||||
TaskProvider.PERMISSION_WRITE_TASKS
|
||||
)
|
||||
}
|
||||
|
||||
private var usesContacts: Boolean? = null
|
||||
private var usesCalendars: Boolean? = null
|
||||
|
||||
init {
|
||||
addSource(needContactPermissions) {
|
||||
usesContacts = it
|
||||
calculate()
|
||||
}
|
||||
addSource(needCalendarPermissions) {
|
||||
usesCalendars = it
|
||||
calculate()
|
||||
fun toggleSync(item: Collection) {
|
||||
viewModelScope.launch(Dispatchers.IO + NonCancellable) {
|
||||
val newItem = item.copy(sync = !item.sync)
|
||||
db.collectionDao().update(newItem)
|
||||
}
|
||||
}
|
||||
|
||||
fun calculate() {
|
||||
val contacts = usesContacts ?: return
|
||||
val calendar = usesCalendars ?: return
|
||||
|
||||
val required = mutableListOf<String>()
|
||||
if (contacts)
|
||||
required.addAll(contactPermissions)
|
||||
|
||||
if (calendar) {
|
||||
required.addAll(calendarPermissions)
|
||||
if (LocalTaskList.tasksProviderAvailable(context))
|
||||
required.addAll(taskPermissions)
|
||||
fun toggleReadOnly(item: Collection) {
|
||||
viewModelScope.launch(Dispatchers.IO + NonCancellable) {
|
||||
val newItem = item.copy(forceReadOnly = !item.forceReadOnly)
|
||||
db.collectionDao().update(newItem)
|
||||
}
|
||||
|
||||
// only ask for permissions which are not granted
|
||||
val askFor = required.filter {
|
||||
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_DENIED
|
||||
}
|
||||
if (value != askFor)
|
||||
value = askFor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ package at.bitfire.davdroid.ui.account
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.*
|
||||
import at.bitfire.davdroid.PermissionUtils
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.model.Collection
|
||||
import at.bitfire.davdroid.ui.CreateAddressBookActivity
|
||||
import kotlinx.android.synthetic.main.account_carddav_item.view.*
|
||||
import kotlinx.android.synthetic.main.account_collections.*
|
||||
|
||||
class AddressBooksFragment: CollectionsFragment() {
|
||||
|
||||
@@ -28,6 +30,15 @@ class AddressBooksFragment: CollectionsFragment() {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun checkPermissions() {
|
||||
if (PermissionUtils.havePermissions(requireActivity(), PermissionUtils.CONTACT_PERMSSIONS))
|
||||
permissionsCard.visibility = View.GONE
|
||||
else {
|
||||
permissionsText.setText(R.string.account_carddav_missing_permissions)
|
||||
permissionsCard.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun createAdapter() = AddressBookAdapter(accountModel)
|
||||
|
||||
|
||||
|
||||
@@ -3,10 +3,13 @@ package at.bitfire.davdroid.ui.account
|
||||
import android.content.Intent
|
||||
import android.view.*
|
||||
import at.bitfire.davdroid.Constants
|
||||
import at.bitfire.davdroid.PermissionUtils
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.model.Collection
|
||||
import at.bitfire.davdroid.resource.LocalTaskList
|
||||
import at.bitfire.davdroid.ui.CreateCalendarActivity
|
||||
import kotlinx.android.synthetic.main.account_caldav_item.view.*
|
||||
import kotlinx.android.synthetic.main.account_collections.*
|
||||
|
||||
class CalendarsFragment: CollectionsFragment() {
|
||||
|
||||
@@ -29,6 +32,22 @@ class CalendarsFragment: CollectionsFragment() {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
override fun checkPermissions() {
|
||||
val calendarPermissions = PermissionUtils.havePermissions(requireActivity(), PermissionUtils.CALENDAR_PERMISSIONS)
|
||||
val tasksPermissions = !LocalTaskList.tasksProviderAvailable(requireActivity()) ||
|
||||
PermissionUtils.havePermissions(requireActivity(), PermissionUtils.TASKS_PERMISSIONS)
|
||||
if (calendarPermissions && tasksPermissions)
|
||||
permissionsCard.visibility = View.GONE
|
||||
else {
|
||||
permissionsText.setText(when {
|
||||
!calendarPermissions && tasksPermissions -> R.string.account_caldav_missing_calendar_permissions
|
||||
calendarPermissions && !tasksPermissions -> R.string.account_caldav_missing_tasks_permissions
|
||||
else -> R.string.account_caldav_missing_permissions
|
||||
})
|
||||
permissionsCard.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
override fun createAdapter(): CollectionAdapter = CalendarAdapter(accountModel)
|
||||
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@ import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.bitfire.davdroid.databinding.CollectionPropertiesBinding
|
||||
import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.model.Collection
|
||||
import kotlin.concurrent.thread
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CollectionInfoFragment: DialogFragment() {
|
||||
|
||||
@@ -67,7 +69,7 @@ class CollectionInfoFragment: DialogFragment() {
|
||||
return
|
||||
initialized = true
|
||||
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val db = AppDatabase.getInstance(getApplication())
|
||||
collection.postValue(db.collectionDao().get(collectionId))
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ import at.bitfire.davdroid.resource.LocalTaskList
|
||||
import at.bitfire.davdroid.ui.DeleteCollectionFragment
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import kotlinx.android.synthetic.main.account_collections.*
|
||||
import java.util.concurrent.Executors
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class CollectionsFragment: Fragment(), SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
@@ -112,8 +113,6 @@ abstract class CollectionsFragment: Fragment(), SwipeRefreshLayout.OnRefreshList
|
||||
no_collections.setText(noCollectionsStringId)
|
||||
}
|
||||
|
||||
protected abstract fun createAdapter(): CollectionAdapter
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) =
|
||||
when (item.itemId) {
|
||||
R.id.refresh -> {
|
||||
@@ -128,6 +127,13 @@ abstract class CollectionsFragment: Fragment(), SwipeRefreshLayout.OnRefreshList
|
||||
model.refresh()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
checkPermissions()
|
||||
}
|
||||
|
||||
protected abstract fun checkPermissions()
|
||||
protected abstract fun createAdapter(): CollectionAdapter
|
||||
|
||||
|
||||
abstract class CollectionViewHolder(
|
||||
@@ -207,7 +213,6 @@ abstract class CollectionsFragment: Fragment(), SwipeRefreshLayout.OnRefreshList
|
||||
class Model(application: Application): AndroidViewModel(application), DavService.RefreshingStatusListener, SyncStatusObserver {
|
||||
|
||||
private val db = AppDatabase.getInstance(application)
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
|
||||
private lateinit var accountModel: AccountActivity.Model
|
||||
val serviceId = MutableLiveData<Long>()
|
||||
@@ -250,8 +255,8 @@ abstract class CollectionsFragment: Fragment(), SwipeRefreshLayout.OnRefreshList
|
||||
if (context.bindService(Intent(context, DavService::class.java), svcConn, Context.BIND_AUTO_CREATE))
|
||||
davServiceConn = svcConn
|
||||
|
||||
executor.submit {
|
||||
syncStatusHandle = ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_PENDING + ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, this)
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
syncStatusHandle = ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_PENDING + ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, this@Model)
|
||||
checkSyncStatus()
|
||||
}
|
||||
}
|
||||
@@ -281,7 +286,7 @@ abstract class CollectionsFragment: Fragment(), SwipeRefreshLayout.OnRefreshList
|
||||
}
|
||||
|
||||
override fun onStatusChanged(which: Int) {
|
||||
executor.submit {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
checkSyncStatus()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,18 +11,15 @@ import android.content.DialogInterface
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Looper
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.ContactsContract
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.*
|
||||
import at.bitfire.davdroid.DavUtils
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.closeCompat
|
||||
@@ -33,8 +30,10 @@ import at.bitfire.davdroid.resource.LocalTaskList
|
||||
import at.bitfire.davdroid.settings.AccountSettings
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
class RenameAccountFragment: DialogFragment() {
|
||||
@@ -77,11 +76,12 @@ class RenameAccountFragment: DialogFragment() {
|
||||
.setView(layout)
|
||||
.setPositiveButton(R.string.account_rename_rename, DialogInterface.OnClickListener { _, _ ->
|
||||
val newName = editText.text.toString()
|
||||
|
||||
if (newName == oldAccount.name)
|
||||
return@OnClickListener
|
||||
|
||||
model.renameAccount(oldAccount, newName)
|
||||
|
||||
requireActivity().finish()
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.create()
|
||||
@@ -97,26 +97,25 @@ class RenameAccountFragment: DialogFragment() {
|
||||
fun renameAccount(oldAccount: Account, newName: String) {
|
||||
val context = getApplication<Application>()
|
||||
|
||||
thread {
|
||||
// remember sync intervals
|
||||
val oldSettings = AccountSettings(context, oldAccount)
|
||||
val authorities = arrayOf(
|
||||
context.getString(R.string.address_books_authority),
|
||||
CalendarContract.AUTHORITY,
|
||||
TaskProvider.ProviderName.OpenTasks.authority
|
||||
)
|
||||
val syncIntervals = authorities.map { Pair(it, oldSettings.getSyncInterval(it)) }
|
||||
// remember sync intervals
|
||||
val oldSettings = AccountSettings(context, oldAccount)
|
||||
val authorities = arrayOf(
|
||||
context.getString(R.string.address_books_authority),
|
||||
CalendarContract.AUTHORITY,
|
||||
TaskProvider.ProviderName.OpenTasks.authority
|
||||
)
|
||||
val syncIntervals = authorities.map { Pair(it, oldSettings.getSyncInterval(it)) }
|
||||
|
||||
val accountManager = AccountManager.get(context)
|
||||
accountManager.renameAccount(oldAccount, newName, {
|
||||
thread {
|
||||
onAccountRenamed(accountManager, oldAccount, newName, syncIntervals)
|
||||
}
|
||||
}, null)
|
||||
}
|
||||
val accountManager = AccountManager.get(context)
|
||||
accountManager.renameAccount(oldAccount, newName, {
|
||||
viewModelScope.launch(Dispatchers.Default + NonCancellable) {
|
||||
onAccountRenamed(accountManager, oldAccount, newName, syncIntervals)
|
||||
}
|
||||
}, null)
|
||||
}
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
@WorkerThread
|
||||
fun onAccountRenamed(accountManager: AccountManager, oldAccount: Account, newName: String, syncIntervals: List<Pair<String, Long?>>) {
|
||||
// account has now been renamed
|
||||
Logger.log.info("Updating account name references")
|
||||
@@ -129,8 +128,6 @@ class RenameAccountFragment: DialogFragment() {
|
||||
|
||||
// update account name references in database
|
||||
val db = AppDatabase.getInstance(context)
|
||||
Logger.log.log(Level.INFO, "Main thread", Looper.getMainLooper().thread)
|
||||
Logger.log.log(Level.INFO, "Current thread", Thread.currentThread())
|
||||
db.serviceDao().renameAccount(oldAccount.name, newName)
|
||||
|
||||
// update main account of address book accounts
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.database.ContentObserver
|
||||
@@ -20,10 +19,14 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.*
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.room.Transaction
|
||||
import at.bitfire.dav4jvm.UrlUtils
|
||||
import at.bitfire.davdroid.Constants
|
||||
import at.bitfire.davdroid.PermissionUtils
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.closeCompat
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
@@ -31,9 +34,13 @@ import at.bitfire.davdroid.model.AppDatabase
|
||||
import at.bitfire.davdroid.model.Collection
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.account_caldav_item.view.*
|
||||
import kotlinx.android.synthetic.main.account_collections.*
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import java.util.logging.Level
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlin.collections.set
|
||||
|
||||
class WebcalFragment: CollectionsFragment() {
|
||||
|
||||
@@ -45,10 +52,6 @@ class WebcalFragment: CollectionsFragment() {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
webcalModel = ViewModelProvider(this).get(WebcalModel::class.java)
|
||||
webcalModel.calendarPermission.observe(this, Observer { granted ->
|
||||
if (!granted)
|
||||
requestPermissions(arrayOf(Manifest.permission.READ_CALENDAR), 0)
|
||||
})
|
||||
webcalModel.subscribedUrls.observe(this, Observer { urls ->
|
||||
Logger.log.log(Level.FINE, "Got Android calendar list", urls.keys)
|
||||
})
|
||||
@@ -56,9 +59,6 @@ class WebcalFragment: CollectionsFragment() {
|
||||
webcalModel.initialize(arguments?.getLong(EXTRA_SERVICE_ID) ?: throw IllegalArgumentException("EXTRA_SERVICE_ID required"))
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) =
|
||||
webcalModel.calendarPermission.check()
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) =
|
||||
inflater.inflate(R.menu.caldav_actions, menu)
|
||||
|
||||
@@ -68,6 +68,15 @@ class WebcalFragment: CollectionsFragment() {
|
||||
}
|
||||
|
||||
|
||||
override fun checkPermissions() {
|
||||
if (PermissionUtils.havePermissions(requireActivity(), PermissionUtils.CALENDAR_PERMISSIONS))
|
||||
permissionsCard.visibility = View.GONE
|
||||
else {
|
||||
permissionsText.setText(R.string.account_webcal_missing_calendar_permissions)
|
||||
permissionsCard.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun createAdapter(): CollectionAdapter = WebcalAdapter(accountModel, webcalModel)
|
||||
|
||||
|
||||
@@ -159,18 +168,16 @@ class WebcalFragment: CollectionsFragment() {
|
||||
private val db = AppDatabase.getInstance(application)
|
||||
private val resolver = application.contentResolver
|
||||
|
||||
val calendarPermission = CalendarPermission(application)
|
||||
private var calendarPermission = false
|
||||
private val calendarProvider = object: MediatorLiveData<ContentProviderClient>() {
|
||||
var havePermission = false
|
||||
|
||||
init {
|
||||
addSource(calendarPermission) { granted ->
|
||||
havePermission = granted
|
||||
if (granted)
|
||||
connect()
|
||||
else
|
||||
disconnect()
|
||||
}
|
||||
init()
|
||||
}
|
||||
|
||||
fun init() {
|
||||
calendarPermission = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED
|
||||
if (calendarPermission)
|
||||
connect()
|
||||
}
|
||||
|
||||
override fun onActive() {
|
||||
@@ -179,7 +186,7 @@ class WebcalFragment: CollectionsFragment() {
|
||||
}
|
||||
|
||||
fun connect() {
|
||||
if (havePermission && value == null)
|
||||
if (calendarPermission && value == null)
|
||||
value = resolver.acquireContentProviderClient(CalendarContract.AUTHORITY)
|
||||
}
|
||||
|
||||
@@ -274,13 +281,12 @@ class WebcalFragment: CollectionsFragment() {
|
||||
initialized = true
|
||||
|
||||
serviceId = dbServiceId
|
||||
calendarPermission.check()
|
||||
}
|
||||
|
||||
fun unsubscribe(webcal: Collection) {
|
||||
workerHandler.post {
|
||||
// find first matching source (Webcal) URL
|
||||
subscribedUrls.value?.entries?.firstOrNull { (id, source) ->
|
||||
subscribedUrls.value?.entries?.firstOrNull { (_, source) ->
|
||||
UrlUtils.equals(source, webcal.source!!)
|
||||
}?.key?.let { id ->
|
||||
// delete first matching subscription from Android calendar list
|
||||
@@ -292,14 +298,4 @@ class WebcalFragment: CollectionsFragment() {
|
||||
|
||||
}
|
||||
|
||||
class CalendarPermission(val context: Context): LiveData<Boolean>() {
|
||||
init {
|
||||
check()
|
||||
}
|
||||
|
||||
fun check() {
|
||||
value = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,11 +57,11 @@ class BatteryOptimizationsFragment: Fragment() {
|
||||
binding.batteryText.text = getString(R.string.intro_battery_text, getString(R.string.app_name))
|
||||
|
||||
binding.autostartHeading.text = getString(R.string.intro_autostart_title, WordUtils.capitalize(Build.MANUFACTURER))
|
||||
binding.autostartText.text = getString(R.string.intro_autostart_text)
|
||||
binding.autostartText.setText(R.string.intro_autostart_text)
|
||||
binding.autostartMoreInfo.setOnClickListener {
|
||||
UiUtils.launchUri(requireActivity(), App.homepageUrl(requireActivity()).buildUpon()
|
||||
.appendPath("faq").appendPath("synchronization-is-not-run-as-expected")
|
||||
.appendQueryParameter("manufacturer", Build.MANUFACTURER).build())
|
||||
.appendQueryParameter("manufacturer", Build.MANUFACTURER.toLowerCase()).build())
|
||||
}
|
||||
|
||||
binding.infoLeaveUnchecked.text = getString(R.string.intro_leave_unchecked, getString(R.string.app_settings_reset_hints))
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package at.bitfire.davdroid.ui.intro
|
||||
|
||||
import android.app.Application
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.LayoutInflater
|
||||
@@ -19,11 +18,13 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import at.bitfire.davdroid.App
|
||||
import at.bitfire.davdroid.PackageChangedReceiver
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.databinding.IntroOpentasksBinding
|
||||
import at.bitfire.davdroid.resource.LocalTaskList
|
||||
import at.bitfire.davdroid.settings.Settings
|
||||
import at.bitfire.davdroid.ui.UiUtils
|
||||
import at.bitfire.davdroid.ui.intro.IIntroFragmentFactory.ShowMode
|
||||
import at.bitfire.davdroid.ui.intro.OpenTasksFragment.Model.Companion.HINT_OPENTASKS_NOT_INSTALLED
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
@@ -88,8 +89,8 @@ class OpenTasksFragment: Fragment() {
|
||||
|
||||
var isInstalled = MutableLiveData<Boolean>()
|
||||
val shallBeInstalled = MutableLiveData<Boolean>()
|
||||
val openTasksInstalledReceiver = object: BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val tasksWatcher = object: PackageChangedReceiver(app) {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
checkInstalled()
|
||||
}
|
||||
}
|
||||
@@ -106,17 +107,11 @@ class OpenTasksFragment: Fragment() {
|
||||
}
|
||||
|
||||
init {
|
||||
val filter = IntentFilter(Intent.ACTION_PACKAGE_ADDED).apply {
|
||||
addAction(Intent.ACTION_PACKAGE_CHANGED)
|
||||
addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||
addDataScheme("package")
|
||||
}
|
||||
app.registerReceiver(openTasksInstalledReceiver, filter)
|
||||
checkInstalled()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
getApplication<Application>().unregisterReceiver(openTasksInstalledReceiver)
|
||||
tasksWatcher.close()
|
||||
}
|
||||
|
||||
fun checkInstalled() {
|
||||
@@ -130,11 +125,16 @@ class OpenTasksFragment: Fragment() {
|
||||
|
||||
class Factory: IIntroFragmentFactory {
|
||||
|
||||
override fun shouldBeShown(context: Context, settings: Settings) =
|
||||
if (!LocalTaskList.tasksProviderAvailable(context) && settings.getBoolean(HINT_OPENTASKS_NOT_INSTALLED) != false)
|
||||
IIntroFragmentFactory.ShowMode.SHOW
|
||||
else
|
||||
IIntroFragmentFactory.ShowMode.DONT_SHOW
|
||||
override fun shouldBeShown(context: Context, settings: Settings): ShowMode {
|
||||
// On Android <6, OpenTasks must be installed before DAVx5, so this fragment is not useful.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||
return ShowMode.DONT_SHOW
|
||||
|
||||
return if (!LocalTaskList.tasksProviderAvailable(context) && settings.getBoolean(HINT_OPENTASKS_NOT_INSTALLED) != false)
|
||||
ShowMode.SHOW
|
||||
else
|
||||
ShowMode.DONT_SHOW
|
||||
}
|
||||
|
||||
override fun create() = OpenTasksFragment()
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package at.bitfire.davdroid.ui.intro
|
||||
|
||||
import android.content.Context
|
||||
import at.bitfire.davdroid.PermissionUtils
|
||||
import at.bitfire.davdroid.PermissionUtils.CALENDAR_PERMISSIONS
|
||||
import at.bitfire.davdroid.PermissionUtils.CONTACT_PERMSSIONS
|
||||
import at.bitfire.davdroid.PermissionUtils.TASKS_PERMISSIONS
|
||||
import at.bitfire.davdroid.settings.Settings
|
||||
import at.bitfire.davdroid.ui.PermissionsFragment
|
||||
import at.bitfire.davdroid.ui.intro.IIntroFragmentFactory.ShowMode
|
||||
|
||||
class PermissionsFragmentFactory: IIntroFragmentFactory {
|
||||
|
||||
override fun shouldBeShown(context: Context, settings: Settings): IIntroFragmentFactory.ShowMode {
|
||||
// show PermissionsFragment as intro fragment when no permissions are granted
|
||||
val permissions = CONTACT_PERMSSIONS + CALENDAR_PERMISSIONS + TASKS_PERMISSIONS
|
||||
return if (PermissionUtils.haveAnyPermission(context, permissions))
|
||||
ShowMode.DONT_SHOW
|
||||
else
|
||||
ShowMode.SHOW
|
||||
}
|
||||
|
||||
override fun create() = PermissionsFragment()
|
||||
|
||||
}
|
||||
@@ -36,8 +36,10 @@ import at.bitfire.davdroid.settings.Settings
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import at.bitfire.vcard4android.GroupMethod
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.logging.Level
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class AccountDetailsFragment: Fragment() {
|
||||
|
||||
@@ -129,7 +131,7 @@ class AccountDetailsFragment: Fragment() {
|
||||
fun createAccount(name: String, credentials: Credentials, config: DavResourceFinder.Configuration, groupMethod: GroupMethod): LiveData<Boolean> {
|
||||
val result = MutableLiveData<Boolean>()
|
||||
val context = getApplication<Application>()
|
||||
thread {
|
||||
viewModelScope.launch(Dispatchers.Default + NonCancellable) {
|
||||
val account = Account(name, context.getString(R.string.account_type))
|
||||
|
||||
// create Android account
|
||||
@@ -139,7 +141,7 @@ class AccountDetailsFragment: Fragment() {
|
||||
val accountManager = AccountManager.get(context)
|
||||
if (!accountManager.addAccountExplicitly(account, credentials.password, userData)) {
|
||||
result.postValue(false)
|
||||
return@thread
|
||||
return@launch
|
||||
}
|
||||
|
||||
// add entries for account to service DB
|
||||
@@ -163,10 +165,11 @@ class AccountDetailsFragment: Fragment() {
|
||||
refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id)
|
||||
context.startService(refreshIntent)
|
||||
|
||||
// contact sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_address_books.xml
|
||||
accountSettings.setSyncInterval(context.getString(R.string.address_books_authority), Constants.DEFAULT_SYNC_INTERVAL)
|
||||
} else
|
||||
ContentResolver.setIsSyncable(account, context.getString(R.string.address_books_authority), 0)
|
||||
// set default sync interval and enable sync regardless of permissions
|
||||
val addrBookAuthority = context.getString(R.string.address_books_authority)
|
||||
ContentResolver.setIsSyncable(account, addrBookAuthority, 1)
|
||||
accountSettings.setSyncInterval(addrBookAuthority, Constants.DEFAULT_SYNC_INTERVAL)
|
||||
}
|
||||
|
||||
if (config.calDAV != null) {
|
||||
// insert CalDAV service
|
||||
@@ -176,24 +179,21 @@ class AccountDetailsFragment: Fragment() {
|
||||
refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id)
|
||||
context.startService(refreshIntent)
|
||||
|
||||
// calendar sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_calendars.xml
|
||||
// set default sync interval and enable sync regardless of permissions
|
||||
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1)
|
||||
accountSettings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL)
|
||||
|
||||
// enable task sync if OpenTasks is installed
|
||||
// further changes will be handled by PackageChangedReceiver
|
||||
if (LocalTaskList.tasksProviderAvailable(context)) {
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1)
|
||||
accountSettings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, Constants.DEFAULT_SYNC_INTERVAL)
|
||||
// further changes will be handled by OpenTasksWatcher on app start or when OpenTasks is (un)installed
|
||||
}
|
||||
} else {
|
||||
ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 0)
|
||||
ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 0)
|
||||
}
|
||||
|
||||
} catch(e: InvalidAccountException) {
|
||||
Logger.log.log(Level.SEVERE, "Couldn't access account settings", e)
|
||||
result.postValue(false)
|
||||
return@thread
|
||||
return@launch
|
||||
}
|
||||
result.postValue(true)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.util.AttributeSet
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.log.Logger.log
|
||||
|
||||
/**
|
||||
* [android.widget.ImageView] that supports directional cropping in both vertical and
|
||||
|
||||
346
app/src/main/res/drawable/intro_permissions.xml
Normal file
346
app/src/main/res/drawable/intro_permissions.xml
Normal file
@@ -0,0 +1,346 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="1144dp"
|
||||
android:height="640.99dp"
|
||||
android:viewportWidth="1144"
|
||||
android:viewportHeight="640.99">
|
||||
<path
|
||||
android:pathData="m1144,372.81a180.56,168.7 0,0 1,-26.12 87.71c0,32.35 -16.14,61.67 -42.32,83.03a144.74,135.23 0,0 1,-21.08 14.28c-0.3,0.18 -0.61,0.35 -0.92,0.51 -0.56,0.31 -1.12,0.62 -1.69,0.93l-0.53,0.29a164.61,153.8 0,0 1,-81.1 19.43h-753.07q-9,0 -17.84,-0.67a211.15,197.28 0,0 1,-61.06 -13.21q-5.72,-2.11 -11.22,-4.54 -2.62,-1.15 -5.19,-2.36c-1.07,-0.5 -2.14,-1.03 -3.2,-1.55a187.42,175.11 0,0 1,-34.46 -21.85c-34,-27.31 -55.08,-65.04 -55.08,-106.71a180.5,168.64 0,0 1,-29.12 -92.19c0,-92.63 79.24,-167.72 177,-167.72 3,0 6,0.08 9,0.23 0.43,0 0.85,0 1.28,0.06 16.48,-26.25 38.51,-50.36 65,-71.59 72.24,-57.83 177.82,-94.29 295.43,-94.29 98.82,0 189.15,25.74 258.34,68.27a174.18,162.74 0,0 1,85.95 -21.07c97.74,0 177,75.09 177,167.72a184.9,172.76 0,0 1,-1 17.55,180 168.18,0 0,1 76,137.74z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:strokeWidth=".9666"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="m138.37,562.79c0,0.46 -0.05,0.91 -0.1,1.35q-5.72,-2.26 -11.22,-4.86a9.1,9.1 0,0 1,-5.19 -2.53c-1.07,-0.54 -2.14,-1.1 -3.2,-1.66a13.68,13.68 0,0 1,0.81 -1.46c2.06,-3.26 5.19,-5.3 8.64,-5.19s6.44,2.34 8.29,5.71a16.87,16.87 0,0 1,1.97 8.64z"
|
||||
android:strokeAlpha="0.2"
|
||||
android:fillColor="#3f3d56"
|
||||
android:fillAlpha="0.2"/>
|
||||
<path
|
||||
android:pathData="m138.91,545.67a16.88,16.88 0,0 1,-2.51 8.48c-2.06,3.25 -5.19,5.29 -8.64,5.18l-0.71,-0.05a9.1,9.1 0,0 1,-5.19 -2.53,12.53 12.53,0 0,1 -2.39,-3.12 17.55,17.55 0,0 1,0.54 -17.12c2.06,-3.26 5.19,-5.3 8.64,-5.19s6.44,2.34 8.29,5.71a16.87,16.87 0,0 1,1.97 8.64z"
|
||||
android:strokeAlpha="0.2"
|
||||
android:fillColor="#3f3d56"
|
||||
android:fillAlpha="0.2"/>
|
||||
<path
|
||||
android:pathData="M128.32,542.24a14.01,10.7 91.81,1 0,0.89 -28.01a14.01,10.7 91.81,1 0,-0.89 28.01z"
|
||||
android:strokeAlpha="0.2"
|
||||
android:fillColor="#3f3d56"
|
||||
android:fillAlpha="0.2"/>
|
||||
<path
|
||||
android:pathData="M128.85,525.12a14.01,10.7 91.81,1 0,0.89 -28.01a14.01,10.7 91.81,1 0,-0.89 28.01z"
|
||||
android:strokeAlpha="0.2"
|
||||
android:fillColor="#3f3d56"
|
||||
android:fillAlpha="0.2"/>
|
||||
<path
|
||||
android:pathData="M129.49,506.9a14.02,10.7 91.72,1 0,0.84 -28.02a14.02,10.7 91.72,1 0,-0.84 28.02z"
|
||||
android:strokeAlpha="0.2"
|
||||
android:fillColor="#3f3d56"
|
||||
android:fillAlpha="0.2"/>
|
||||
<path
|
||||
android:pathData="m93.47,375.55a49.66,49.66 0,0 1,-3.8 -6l28.26,-3.73 -30.42,-0.73a51.38,51.38 0,0 1,0.31 -40.64l40.12,22.45 -36.75,-28.85a51.28,51.28 0,1 1,82.84 60,51.12 51.12,0 0,1 5.55,9.53l-37.09,17.81 39.33,-11.83a51.34,51.34 0,0 1,-9.82 47.91,51.28 51.28,0 1,1 -80.56,-2.54 51.28,51.28 0,0 1,2 -63.38z"
|
||||
android:strokeAlpha="0.2"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.2"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m184,410.13a51.06,51.06 0,0 1,-12 31.34,51.28 51.28,0 1,1 -80.56,-2.54c-6.57,-8.93 92.74,-34.56 92.56,-28.8z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="m958,81.41v479.18a18.41,18.41 0,0 1,-18.41 18.41h-735.18a18.13,18.13 0,0 1,-5.08 -0.72,18.38 18.38,0 0,1 -13.33,-17.69v-213.84,-3.25 -203.94c0.43,0 0.85,0 1.28,0.06 16.48,-28.09 38.51,-53.9 65,-76.62h687.31a18.41,18.41 0,0 1,18.41 18.41z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M730.15,125.23L832.02,125.23A17.98,17.98 0,0 1,850 143.21L850,143.21A17.98,17.98 0,0 1,832.02 161.19L730.15,161.19A17.98,17.98 0,0 1,712.17 143.21L712.17,143.21A17.98,17.98 0,0 1,730.15 125.23z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M733.12,131.24L829.04,131.24A11.97,11.97 0,0 1,841.01 143.21L841.01,143.21A11.97,11.97 0,0 1,829.04 155.17L733.12,155.17A11.97,11.97 0,0 1,721.15 143.21L721.15,143.21A11.97,11.97 0,0 1,733.12 131.24z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M805.38,132.55L827.36,132.55A10.65,10.65 0,0 1,838.01 143.2L838.01,143.21A10.65,10.65 0,0 1,827.36 153.86L805.38,153.86A10.65,10.65 0,0 1,794.73 143.21L794.73,143.2A10.65,10.65 0,0 1,805.38 132.55z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M816.04,143.21m-6.66,0a6.66,6.66 0,1 1,13.32 0a6.66,6.66 0,1 1,-13.32 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M730.15,362.28L832.02,362.28A17.98,17.98 0,0 1,850 380.26L850,380.26A17.98,17.98 0,0 1,832.02 398.24L730.15,398.24A17.98,17.98 0,0 1,712.17 380.26L712.17,380.26A17.98,17.98 0,0 1,730.15 362.28z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M733.12,368.29L829.04,368.29A11.97,11.97 0,0 1,841.01 380.26L841.01,380.26A11.97,11.97 0,0 1,829.04 392.22L733.12,392.22A11.97,11.97 0,0 1,721.15 380.26L721.15,380.26A11.97,11.97 0,0 1,733.12 368.29z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M805.38,369.6L827.36,369.6A10.65,10.65 0,0 1,838.01 380.25L838.01,380.26A10.65,10.65 0,0 1,827.36 390.91L805.38,390.91A10.65,10.65 0,0 1,794.73 380.26L794.73,380.25A10.65,10.65 0,0 1,805.38 369.6z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M816.04,380.26m-6.66,0a6.66,6.66 0,1 1,13.32 0a6.66,6.66 0,1 1,-13.32 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M730.15,480.8L832.02,480.8A17.98,17.98 0,0 1,850 498.78L850,498.78A17.98,17.98 0,0 1,832.02 516.76L730.15,516.76A17.98,17.98 0,0 1,712.17 498.78L712.17,498.78A17.98,17.98 0,0 1,730.15 480.8z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M733.12,486.82L829.04,486.82A11.97,11.97 0,0 1,841.01 498.79L841.01,498.79A11.97,11.97 0,0 1,829.04 510.75L733.12,510.75A11.97,11.97 0,0 1,721.15 498.79L721.15,498.79A11.97,11.97 0,0 1,733.12 486.82z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M805.38,488.13L827.36,488.13A10.65,10.65 0,0 1,838.01 498.78L838.01,498.79A10.65,10.65 0,0 1,827.36 509.44L805.38,509.44A10.65,10.65 0,0 1,794.73 498.79L794.73,498.78A10.65,10.65 0,0 1,805.38 488.13z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M816.04,498.78m-6.66,0a6.66,6.66 0,1 1,13.32 0a6.66,6.66 0,1 1,-13.32 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M304.65,102.59L363.92,102.59A10.65,10.65 0,0 1,374.57 113.24L374.57,113.25A10.65,10.65 0,0 1,363.92 123.9L304.65,123.9A10.65,10.65 0,0 1,294 113.25L294,113.24A10.65,10.65 0,0 1,304.65 102.59z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M304.65,132.55L507.08,132.55A10.65,10.65 0,0 1,517.73 143.2L517.73,143.21A10.65,10.65 0,0 1,507.08 153.86L304.65,153.86A10.65,10.65 0,0 1,294 143.21L294,143.2A10.65,10.65 0,0 1,304.65 132.55z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M304.65,162.52L507.08,162.52A10.65,10.65 0,0 1,517.73 173.17L517.73,173.18A10.65,10.65 0,0 1,507.08 183.83L304.65,183.83A10.65,10.65 0,0 1,294 173.18L294,173.17A10.65,10.65 0,0 1,304.65 162.52z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M304.65,221.11L363.92,221.11A10.65,10.65 0,0 1,374.57 231.76L374.57,231.77A10.65,10.65 0,0 1,363.92 242.42L304.65,242.42A10.65,10.65 0,0 1,294 231.77L294,231.76A10.65,10.65 0,0 1,304.65 221.11z"
|
||||
android:strokeAlpha="0.5"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.5"/>
|
||||
<path
|
||||
android:pathData="M304.65,251.08L450.35,251.08A10.65,10.65 0,0 1,461 261.73L461,261.74A10.65,10.65 0,0 1,450.35 272.39L304.65,272.39A10.65,10.65 0,0 1,294 261.74L294,261.73A10.65,10.65 0,0 1,304.65 251.08z"
|
||||
android:strokeAlpha="0.5"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.5"/>
|
||||
<path
|
||||
android:pathData="M304.65,281.04L507.08,281.04A10.65,10.65 0,0 1,517.73 291.69L517.73,291.7A10.65,10.65 0,0 1,507.08 302.35L304.65,302.35A10.65,10.65 0,0 1,294 291.7L294,291.69A10.65,10.65 0,0 1,304.65 281.04z"
|
||||
android:strokeAlpha="0.5"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.5"/>
|
||||
<path
|
||||
android:pathData="M304.65,339.64L363.92,339.64A10.65,10.65 0,0 1,374.57 350.29L374.57,350.3A10.65,10.65 0,0 1,363.92 360.95L304.65,360.95A10.65,10.65 0,0 1,294 350.3L294,350.29A10.65,10.65 0,0 1,304.65 339.64z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M304.65,369.6L507.08,369.6A10.65,10.65 0,0 1,517.73 380.25L517.73,380.26A10.65,10.65 0,0 1,507.08 390.91L304.65,390.91A10.65,10.65 0,0 1,294 380.26L294,380.25A10.65,10.65 0,0 1,304.65 369.6z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M304.65,399.57L422.35,399.57A10.65,10.65 0,0 1,433 410.22L433,410.23A10.65,10.65 0,0 1,422.35 420.88L304.65,420.88A10.65,10.65 0,0 1,294 410.23L294,410.22A10.65,10.65 0,0 1,304.65 399.57z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M304.65,458.16L363.92,458.16A10.65,10.65 0,0 1,374.57 468.81L374.57,468.82A10.65,10.65 0,0 1,363.92 479.47L304.65,479.47A10.65,10.65 0,0 1,294 468.82L294,468.81A10.65,10.65 0,0 1,304.65 458.16z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M304.65,488.13L507.08,488.13A10.65,10.65 0,0 1,517.73 498.78L517.73,498.79A10.65,10.65 0,0 1,507.08 509.44L304.65,509.44A10.65,10.65 0,0 1,294 498.79L294,498.78A10.65,10.65 0,0 1,304.65 488.13z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M304.65,518.09L463.35,518.09A10.65,10.65 0,0 1,474 528.74L474,528.75A10.65,10.65 0,0 1,463.35 539.4L304.65,539.4A10.65,10.65 0,0 1,294 528.75L294,528.74A10.65,10.65 0,0 1,304.65 518.09z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M260,202.99L912,202.99"
|
||||
android:strokeAlpha="0.7"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#3f3d56"
|
||||
android:fillAlpha="0.7"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M260,323.49L912,323.49"
|
||||
android:strokeAlpha="0.7"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#3f3d56"
|
||||
android:fillAlpha="0.7"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M260,443.99L912,443.99"
|
||||
android:strokeAlpha="0.7"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#3f3d56"
|
||||
android:fillAlpha="0.7"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M260,567.49L912,567.49"
|
||||
android:strokeAlpha="0.7"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#3f3d56"
|
||||
android:fillAlpha="0.7"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M647.32,630.49a147.68,10.5 0,1 0,295.36 0a147.68,10.5 0,1 0,-295.36 0z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M48.89,116.68a21.63,20.21 0,1 0,43.26 0a21.63,20.21 0,1 0,-43.26 0z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:strokeWidth=".9666"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M59.04,44.98a21.63,20.21 0,1 0,43.26 0a21.63,20.21 0,1 0,-43.26 0z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:strokeWidth=".9666"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M117.09,81.72a36.25,33.87 0,1 0,72.5 0a36.25,33.87 0,1 0,-72.5 0z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:strokeWidth=".9666"
|
||||
android:fillColor="#ff9e40"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M730.15,243.75L832.02,243.75A17.98,17.98 0,0 1,850 261.73L850,261.73A17.98,17.98 0,0 1,832.02 279.71L730.15,279.71A17.98,17.98 0,0 1,712.17 261.73L712.17,261.73A17.98,17.98 0,0 1,730.15 243.75z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M733.12,249.77L829.04,249.77A11.97,11.97 0,0 1,841.01 261.74L841.01,261.74A11.97,11.97 0,0 1,829.04 273.7L733.12,273.7A11.97,11.97 0,0 1,721.15 261.74L721.15,261.74A11.97,11.97 0,0 1,733.12 249.77z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="m771.68,257.4c-0.49,-2.31 -0.85,-4.71 -2.12,-6.69a15.16,15.16 0,0 0,-3.12 -3.28,20.74 20.74,0 0,0 -6.68,-4.17 8.34,8.34 0,0 0,-7.56 0.87c-2.11,1.6 -3,4.34 -3.33,7a17.07,17.07 0,0 0,1.78 10.55c2.05,3.56 5.59,5.94 8.56,8.78a34.39,34.39 0,0 1,5.58 7,20.21 20.21,0 0,0 2.54,3.72 4.84,4.84 0,0 0,4 1.61,7 7,0 0,0 3.27,-1.93c1.22,-1.07 4.5,-3 4.6,-4.67 0.08,-1.37 -2.48,-4.47 -3.11,-5.76a65.41,65.41 0,0 1,-4.41 -13.03z"
|
||||
android:fillColor="#fbbebe"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m771.68,257.4c-0.49,-2.31 -0.85,-4.71 -2.12,-6.69a15.16,15.16 0,0 0,-3.12 -3.28,20.74 20.74,0 0,0 -6.68,-4.17 8.34,8.34 0,0 0,-7.56 0.87c-2.11,1.6 -3,4.34 -3.33,7a17.07,17.07 0,0 0,1.78 10.55c2.05,3.56 5.59,5.94 8.56,8.78a34.39,34.39 0,0 1,5.58 7,20.21 20.21,0 0,0 2.54,3.72 4.84,4.84 0,0 0,4 1.61,7 7,0 0,0 3.27,-1.93c1.22,-1.07 4.5,-3 4.6,-4.67 0.08,-1.37 -2.48,-4.47 -3.11,-5.76a65.41,65.41 0,0 1,-4.41 -13.03z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="m840.85,323.23a58.88,58.88 0,0 0,-16.18 -2c-9.11,0.13 -18.49,2.34 -27.17,-0.46a47.4,47.4 0,0 1,-6.5 -2.77,16.58 16.58,0 0,1 -4.86,-3.19c-4.32,-4.57 -6.18,-11.46 -7.52,-17.6a117.44,117.44 0,0 1,-2.05 -18.73c-2.25,1.05 -4.63,2.08 -7.1,1.87s-5,-2.11 -5,-4.59c0.37,5.82 0.64,11.71 -0.33,17.45 -0.38,2.28 -1,4.53 -1.17,6.83 -0.74,8.33 3.63,16.65 2.07,24.86a6,6 0,0 0,2.83 6.42c2.24,1.21 4.87,1.36 7.33,2 4.83,1.23 9,4.3 13.9,5.45 2.25,0.53 4.6,0.63 6.84,1.23 3.19,0.85 6.1,2.68 9.36,3.25 2.44,0.44 5,0.14 7.43,0.35 2.15,0.18 4.25,0.74 6.41,0.8 5.28,0.13 10.26,-2.85 13.76,-6.81s5.73,-9.59 7.95,-14.36z"
|
||||
android:fillColor="#f86d70"/>
|
||||
<path
|
||||
android:pathData="m840.85,323.23a58.88,58.88 0,0 0,-16.18 -2c-9.11,0.13 -18.49,2.34 -27.17,-0.46a47.4,47.4 0,0 1,-6.5 -2.77,16.58 16.58,0 0,1 -4.86,-3.19c-4.32,-4.57 -6.18,-11.46 -7.52,-17.6a117.44,117.44 0,0 1,-2.05 -18.73c-2.25,1.05 -4.63,2.08 -7.1,1.87s-5,-2.11 -5,-4.59c0.37,5.82 0.64,11.71 -0.33,17.45 -0.38,2.28 -1,4.53 -1.17,6.83 -0.74,8.33 3.63,16.65 2.07,24.86a6,6 0,0 0,2.83 6.42c2.24,1.21 4.87,1.36 7.33,2 4.83,1.23 9,4.3 13.9,5.45 2.25,0.53 4.6,0.63 6.84,1.23 3.19,0.85 6.1,2.68 9.36,3.25 2.44,0.44 5,0.14 7.43,0.35 2.15,0.18 4.25,0.74 6.41,0.8 5.28,0.13 10.26,-2.85 13.76,-6.81s5.73,-9.59 7.95,-14.36z"
|
||||
android:strokeAlpha="0.749"
|
||||
android:fillColor="#6569f7"
|
||||
android:fillAlpha="0.749"/>
|
||||
<path
|
||||
android:pathData="m855.31,580.67a13.69,13.69 0,0 1,4.67 1.46,11 11,0 0,1 4.06,5.48c2.18,5.38 2.63,11.3 2.73,17.11 0.08,4.69 -0.08,9.49 -1.76,13.87a8.83,8.83 0,0 1,-2.31 3.63,8.33 8.33,0 0,1 -3.18,1.54c-2.51,0.69 -5.41,0.68 -7.46,-0.92 -2.28,-1.78 -2.85,-4.93 -3.33,-7.78 -0.65,-3.84 -1.57,-7.93 -4.43,-10.58 -2.12,-2 -5.16,-3 -6.7,-5.42 -1.84,-2.91 -0.87,-6.71 0.3,-9.95 1,-2.81 2.51,-8.5 5.45,-9.94s8.91,0.95 11.96,1.5z"
|
||||
android:fillColor="#fbbebe"/>
|
||||
<path
|
||||
android:pathData="m747,594.47a22.15,22.15 0,0 0,4.58 8.51c1.3,1.5 2.93,3.32 2.25,5.18a4.82,4.82 0,0 1,-1.23 1.65c-9.66,9.44 -23.17,14 -36.57,15.72a1.29,1.29 0,0 1,-0.65 0c-0.39,-0.17 -0.49,-0.67 -0.53,-1.1a13.51,13.51 0,0 1,0 -4.21c0.65,-3.27 3.52,-5.58 6.33,-7.39s5.88,-3.55 7.44,-6.5c1.95,-3.66 1.13,-8.43 3.45,-11.86 1.88,-2.79 5.38,-4 8.71,-4.5 1.15,-0.18 3.6,-1.44 4.58,-0.86s1.3,4.21 1.64,5.36z"
|
||||
android:fillColor="#fbbebe"/>
|
||||
<path
|
||||
android:pathData="m856.63,580.33c-0,-0.67 0.5,-1.23 1.16,-1.32 0.65,-0.06 1.3,0.02 1.91,0.24 1.5,0.42 3.04,0.7 4.59,0.85 1.86,0.06 3.71,0.28 5.54,0.65 1.85,0.39 3.43,1.59 4.29,3.27 0.36,0.99 0.52,2.05 0.47,3.1 0.04,4.04 -0.44,8.08 -1.43,12 -0.08,0.3 0.15,0.76 -0.14,0.87s-0.17,0.52 -0.4,0.63c-0.04,0.94 -0.81,1.45 -0.88,1.68 -0.97,2.56 -1.76,3.49 -1.9,6.23 -0.44,4 -1,8.06 -1.67,12.06 -0.39,2.34 -0.88,4.8 -2.42,6.6 -1.69,2 -4.36,2.83 -6.94,3.18 -5.44,0.72 -10.93,-0.37 -16.3,-1.49 -1.03,-0.1 -1.99,-0.53 -2.75,-1.23 -0.49,-0.61 -0.73,-1.39 -0.67,-2.18 0.12,-2.69 2.6,-4.59 4.83,-6.09l3.09,-2.07c0.22,-0.12 0.4,-0.29 0.54,-0.49 0.15,-0.31 0.2,-0.66 0.15,-1l-0.2,-4c4.12,-0.72 5.93,-0.28 7.99,-2.52 3.01,-4.06 4.15,-19.59 3.71,-23.65 -0.07,-1.1 -0.41,-2.17 -1,-3.1 -0.47,-0.67 -1.46,-1.32 -1.57,-2.22z"
|
||||
android:fillColor="#333"/>
|
||||
<path
|
||||
android:pathData="M756.78,272.39L734.8,272.39A10.65,10.65 0,0 1,724.15 261.74L724.15,261.73A10.65,10.65 0,0 1,734.8 251.08L756.78,251.08A10.65,10.65 0,0 1,767.43 261.73L767.43,261.74A10.65,10.65 0,0 1,756.78 272.39z"
|
||||
android:fillColor="#ff9e40"/>
|
||||
<path
|
||||
android:pathData="M746.12,261.73m-6.66,0a6.66,6.66 0,1 1,13.32 0a6.66,6.66 0,1 1,-13.32 0"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m726.88,616.51c12.16,-1.99 19.92,-4.57 21.42,-5.88 1.21,-1.02 2.28,-2.19 3.19,-3.49 0.85,-1.29 1.57,-2.87 3,-3.41 0.87,3.76 1.32,7.6 1.34,11.45 0,2.54 -0.36,5.43 -2.43,6.9 -0.83,0.52 -1.74,0.89 -2.7,1.1l-4.52,1.28c-0.8,0.3 -1.67,0.38 -2.51,0.24 -0.53,-0.14 -0.86,0.15 -1.41,0.05 -0.6,-0.06 -1.23,0.04 -1.79,0.26l-3.91,1.14c-1.32,0.34 -2.61,0.79 -3.86,1.35 -1.12,0.53 -2.26,0.56 -3.35,1.15 -1.82,0.91 -3.72,1.68 -5.66,2.3l-6.87,2.39c-1.88,0.74 -3.85,1.24 -5.85,1.5 -1.38,0.12 -2.79,0 -4.16,0.23 -1,0.18 -2,0.56 -3.1,0.64 -1.74,0.05 -3.44,-0.45 -4.87,-1.44 -0.56,-0.31 -1.04,-0.74 -1.42,-1.26 -1,-1.53 -0.13,-3.55 0.83,-5.1 1.69,-2.94 4.1,-5.41 7,-7.18 1.2,-0.66 2.49,-1.14 3.72,-1.74 3.51,-1.7 6.16,-4.54 9.47,-6.48 3.13,-1.83 3.45,2.75 4.21,4.01z"
|
||||
android:fillColor="#333"/>
|
||||
<path
|
||||
android:pathData="m818.69,340.94a28.57,28.57 0,0 0,12.16 4.73,49.5 49.5,0 0,0 6.88,0.37 174.85,174.85 0,0 0,28.63 -2.39c-3.76,-4.15 -7.57,-8.59 -9,-14a23.11,23.11 0,0 1,-0.44 -8.77,66 66,0 0,1 2.68,-11 77.63,77.63 0,0 1,-12.1 -0.31c-2.29,-0.24 -7.91,-2.32 -9.9,-0.74s-0.46,6.67 -0.43,9.06a2.26,2.26 0,0 1,0 0.26,28.93 28.93,0 0,1 -2.31,10.58c-2.86,6.46 -9.06,11.78 -16.17,12.21z"
|
||||
android:fillColor="#fbbebe"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m837.18,317.84a24.06,24.06 0,0 0,19.75 3,66 66,0 0,1 2.68,-11 77.63,77.63 0,0 1,-12.1 -0.31c-2.29,-0.24 -7.91,-2.32 -9.9,-0.74s-0.46,6.66 -0.43,9.05z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M850.38,296.54m-24.11,0a24.11,24.11 0,1 1,48.22 0a24.11,24.11 0,1 1,-48.22 0"
|
||||
android:fillColor="#fbbebe"/>
|
||||
<path
|
||||
android:pathData="m748.09,418.53c-0.02,3.96 2.05,7.63 5.44,9.67 1.86,0.97 3.84,1.68 5.9,2.08 11,2.66 22.32,4.8 33.65,4.15 2.46,-0.02 4.88,-0.53 7.14,-1.5 2.28,-1.01 3.92,-3.07 4.41,-5.51 0.01,-0.6 0.13,-1.19 0.34,-1.75 0.44,-0.61 1.01,-1.12 1.67,-1.49 2.1,-1.58 3.11,-4.24 2.59,-6.82 1.37,0.62 2.81,-0.77 3.46,-2.13s1.15,-3 2.53,-3.59c0.94,-0.4 2,-0.16 3,-0.42 1.59,-0.41 2.56,-2 3.68,-3.15 2.28,-2.44 5.53,-3.7 8.51,-5.21 8.88,-4.47 16.11,-11.56 23.2,-18.53 3.69,-3.4 7.03,-7.14 10,-11.18 0.79,-1.16 1.53,-2.36 2.2,-3.6 2.14,-4.11 3.99,-8.35 5.55,-12.71 0.9,-2.34 1.81,-4.8 1.44,-7.28 0,-0.22 -0.08,-0.44 -0.13,-0.67 -0.46,-1.59 -1.2,-3.09 -2.19,-4.42 -2.45,-3.6 -5.66,-7 -9.88,-8.12s-8.42,0.23 -12.71,1.14c-1.42,0.32 -2.86,0.54 -4.31,0.66 -4.54,0.32 -9,-0.9 -13.42,-2.11l-4.78,-1.32c-2.39,-0.66 -3.59,-3.55 -6,-4.06 -1.08,-0.16 -2.19,-0.11 -3.25,0.17 -4.91,0.93 -4.33,3.22 -8.66,5.72 -0.97,0.55 -2.28,2.11 -3.14,2.8 0.7,3.12 -2.25,5.07 -3.27,8.85 -1,4.17 -4.33,8.98 -7.64,12.53 -1.7,2.79 -4.62,5.32 -6.64,8.22l-13.01,13.79c-2.63,3.95 -5.21,7.11 -8.35,10.66 -6.34,6.89 -17.16,15.76 -17.33,25.11z"
|
||||
android:fillColor="#b36df8"/>
|
||||
<path
|
||||
android:pathData="m779.53,257.4c-0.49,-2.31 -0.85,-4.71 -2.12,-6.69a15.16,15.16 0,0 0,-3.12 -3.28,20.74 20.74,0 0,0 -6.68,-4.17 8.34,8.34 0,0 0,-7.56 0.87c-2.11,1.6 -3,4.34 -3.33,7a17.07,17.07 0,0 0,1.78 10.55c2,3.56 5.59,5.94 8.56,8.78a34.39,34.39 0,0 1,5.58 7,20.21 20.21,0 0,0 2.54,3.72 4.84,4.84 0,0 0,4 1.61,7 7,0 0,0 3.27,-1.93c1.22,-1.07 4.5,-3 4.6,-4.67 0.08,-1.37 -2.48,-4.47 -3.11,-5.76a65.41,65.41 0,0 1,-4.41 -13.03z"
|
||||
android:fillColor="#fbbebe"/>
|
||||
<path
|
||||
android:pathData="m694.81,536.64c3,5.6 6.34,11 9.05,16.8 1.57,3.37 2.93,6.83 4.68,10.11 1.91,3.57 4.26,6.89 6.26,10.41 5,8.9 7.83,18.94 10.42,28.83a57.39,57.39 0,0 0,20.52 -7.22,12.9 12.9,0 0,0 1.23,-0.79 14.52,14.52 0,0 0,4.14 -4.57c-2.39,-9 -4.84,-18.23 -9.71,-26.22 -1.59,-2.6 -3.41,-5 -5,-7.61 -4.73,-7.45 -5.71,-16.84 -11.12,-23.81a4.08,4.08 0,0 1,0 -4.84c0.62,-0.81 1.62,-1.58 1.43,-2.59 -0.14,-0.73 -0.9,-1.39 -0.61,-2.07 0.14,-0.33 0.48,-0.51 0.74,-0.75 0.92,-0.84 0.8,-2.29 0.89,-3.53 0.2,-3.16 2.09,-5.93 3.92,-8.51a5.34,5.34 0,0 1,1.44 -1.55c0.49,-0.3 1.07,-0.44 1.56,-0.74 1.72,-1.07 1.6,-3.59 2.43,-5.46 1.08,-2.46 3.8,-3.71 5.59,-5.7 2.27,-2.54 2.84,-6.07 5.24,-8.29a7.11,7.11 0,0 1,0.79 -0.64,39.7 39.7,0 0,0 -1.41,11.61c-0.23,13.44 3.13,27.34 1.56,40.69 -1.24,10.58 5.28,21.14 14.52,26.44a70.28,70.28 0,0 0,11.31 4.76c5.78,2.05 11.56,4.16 17.23,6.53 4,1.66 8,3.46 11.86,5.37a146,146 0,0 1,14 7.76c5.57,3.54 11.15,7.14 15.1,12.43 3.83,-0.1 7.34,-2.4 9.76,-5.37s3.93,-6.56 5.34,-10.11c1.23,-3.07 2.45,-6.22 2.54,-9.53a1.9,1.9 0,0 0,-1.06 -1.71l-26.46,-14 -5,-2.62c-10.83,-5.75 -22,-11.07 -32.89,-16.81 -0.73,0.21 -1.33,-0.62 -1.54,-1.34s-0.36,-1.62 -1.05,-1.94c-0.35,-0.16 -0.79,-0.16 -1,-0.44a1.2,1.2 0,0 1,-0.18 -0.88c0.35,-5.24 4.13,-10.06 3.37,-15.25 -0.32,-2.22 -1.47,-4.4 -1,-6.57 0.35,-1.51 1.43,-2.73 2,-4.16 1.29,-3.2 0,-7 1.42,-10.17a23.34,23.34 0,0 0,1.91 -6.48,43.88 43.88,0 0,1 4.05,-12.84 23.18,23.18 0,0 1,3.72 -5.64c0.8,-0.85 1.69,-1.62 2.44,-2.5 2.31,-2.73 3.15,-6.37 3.81,-9.88 1.18,-6.37 2,-13 0.52,-19.29 -0.5,-2.15 -1.26,-4.28 -1.23,-6.49 0,-2.91 1.31,-6.32 -0.71,-8.43a6.6,6.6 0,0 0,-3 -1.52,90.43 90.43,0 0,0 -14.35,-2.65l-22.06,-2.87a20.28,20.28 0,0 1,-7.35 -1.91c-3,-1.65 -5,-4.72 -6.2,-7.92 -3.88,2.83 -5.34,8.1 -8.43,11.76 -1.51,1.79 -3.37,3.23 -5,4.87a55.11,55.11 0,0 0,-5.54 6.83c-4.61,6.33 -9.25,12.71 -12.55,19.8 -1.71,3.69 -3,7.54 -4.42,11.36 -5.39,15.07 -14.18,29.37 -17.83,44.94 -0.9,3.81 -3.23,7.24 -3.58,11.14 -0.41,4.13 1.5,8.13 3.49,11.77z"
|
||||
android:fillColor="#434175"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m810.53,411.54a6.85,6.85 0,0 1,0.81 3.26c-0.49,-2.44 -3.85,-3.79 -4.06,-6.2 -0.1,-1.11 0.1,-1.06 0.79,-0.47 1.05,0.87 1.5,2.45 2.46,3.41z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m800.32,418.94a5.15,5.15 0,0 1,1.53 2.26,5.75 5.75,0 0,1 0.3,3.93c-0.49,-0.23 -0.66,-0.83 -0.81,-1.36a9.38,9.38 0,0 0,-1 -2.33c-0.4,-0.63 -2.23,-2.13 -2.21,-2.82 -0.03,-1.04 1.7,-0.05 2.19,0.32z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m804.5,340.76c1.55,1.33 -2.1,-0.95 -0.28,-0.03 4.78,2.42 10.07,3.69 14.88,6 2.37,1.21 4.68,2.54 6.9,4 4.77,3 9.53,6 14.29,9.05 1.15,0.66 2.2,1.46 3.15,2.39 1.72,1.84 1.71,5 3.28,7 1.75,2 4.27,3.16 6.93,3.2 2.63,0.02 5.25,-0.42 7.73,-1.3 1.5,-0.45 2.98,-0.98 4.42,-1.61 1.5,-0.63 0.62,-1.91 1.93,-2.88 2.65,-1.86 6.75,-4.24 7.31,-7.43 0.17,-2.18 -0.12,-4.38 -0.85,-6.44 -0.4,-1.34 -0.91,-2.65 -1.52,-3.91 -0.35,-0.71 -0.76,-1.4 -1.22,-2.05 -3.28,-4.65 -9.06,-6.84 -14.65,-7.93 -2.95,-0.57 -5.95,-0.92 -8.91,-1.42 -1.42,0.32 -2.86,0.54 -4.31,0.66 -4.54,0.32 -9,-0.9 -13.42,-2.11l-4.78,-1.32c-2.39,-0.66 -3.59,-3.55 -6,-4.06 -1.08,-0.16 -2.19,-0.11 -3.25,0.17 -4.91,0.93 -5.25,3.52 -9.58,6.02 -0.98,0.58 -2.23,2.07 -2.05,4z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="m787.49,273.36c0.61,5.83 5.53,10.11 8.53,15.14 1.46,2.45 2.48,5.13 3.79,7.66a75,75 0,0 0,5 8l5.35,7.77c3.15,4.57 6.33,9.18 10.35,13 7,1.19 12.9,6.4 19.55,8.93 5.33,2 11.13,2.3 16.72,3.39s11.37,3.27 14.65,7.93a20.83,20.83 0,0 1,2.74 6,15.54 15.54,0 0,1 0.85,6.43 12.19,12.19 0,0 1,-5 7.9,27.71 27.71,0 0,1 -8.65,4 22.74,22.74 0,0 1,-7.73 1.3,9.44 9.44,0 0,1 -6.94,-3.2c-1.56,-2 -1.55,-5.19 -3.27,-7a15.75,15.75 0,0 0,-3.15 -2.39l-14.29,-9a72.17,72.17 0,0 0,-6.9 -4c-4.81,-2.35 -10.1,-3.62 -14.88,-6s-9.23,-6.41 -10.17,-11.68c-2.91,-16.19 -14.12,-29.84 -23.7,-43.21 0.31,-0.06 0.22,-0.78 0.33,-1.08a14,14 0,0 1,7 -7.08c3.08,-1.67 6.5,-2.1 9.82,-2.81z"
|
||||
android:fillColor="#b36df8"/>
|
||||
<path
|
||||
android:pathData="m838,273.48c-0.18,-1.76 0.17,-3.52 1,-5.08 1.59,-2.64 4.91,-3.57 7.93,-4.19s5.8,-1.1 8.72,-1.52c2.38,-0.48 4.82,-0.52 7.21,-0.13 3.26,0.72 6,2.91 9,4.26 3.6,1.61 7.6,2 11.44,2.92s7.79,2.39 10.2,5.52c1.83,2.37 2.56,5.4 3.11,8.35 4.57,24.63 -13.75,45.5 -19.98,47.75 -3.96,4.38 -1.19,2.87 -16.6,6.26 -3.32,-2.83 -7.1,-5.3 -9.83,-8.75 -4.85,-6.15 -5.78,-14.54 -5.18,-22.35 0.36,-4.64 1.2,-9.29 0.79,-13.93 -0.21,-3.55 -1.49,-6.95 -3.67,-9.76 -2.55,-3.08 -3.9,-5 -4.14,-9.35z"
|
||||
android:fillColor="#434175"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m839,268.4c1.56,-2.6 4.8,-3.54 7.77,-4.15 -1.58,0.55 -2.93,1.61 -3.85,3 -0.83,1.56 -1.17,3.33 -1,5.09 0.22,4.35 1.57,6.27 4.15,9.35 2.19,2.8 3.48,6.21 3.68,9.76 0.4,4.64 -0.43,9.28 -0.79,13.93 -0.61,7.8 0.32,16.2 5.18,22.35 2.72,3.45 6.47,5.91 9.83,8.75 -1.33,0.42 -2.42,1.09 -3.93,1.12 -3.32,-2.83 -7.1,-5.3 -9.83,-8.75 -4.85,-6.15 -5.78,-14.54 -5.18,-22.35 0.36,-4.64 1.2,-9.29 0.79,-13.93 -0.21,-3.55 -1.49,-6.95 -3.67,-9.76 -2.58,-3.08 -3.93,-5 -4.16,-9.35 -0.17,-1.75 0.18,-3.51 1.01,-5.06z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="m717.22,522.23 l29.78,72.55a14.52,14.52 0,0 0,4.14 -4.57c-2.39,-9 -4.84,-18.23 -9.71,-26.22 -1.59,-2.6 -3.41,-5 -5,-7.61 -4.73,-7.45 -5.71,-16.84 -11.12,-23.81a4.08,4.08 0,0 1,0 -4.84c0.62,-0.81 1.62,-1.58 1.43,-2.59 -0.14,-0.73 -0.9,-1.39 -0.61,-2.07 0.14,-0.33 0.48,-0.51 0.74,-0.75 0.92,-0.84 0.8,-2.29 0.89,-3.53 0.2,-3.16 2.09,-5.93 3.92,-8.51a5.34,5.34 0,0 1,1.44 -1.55c0.49,-0.3 1.07,-0.44 1.56,-0.74 1.72,-1.07 1.6,-3.59 2.43,-5.46 1.08,-2.46 3.8,-3.71 5.59,-5.7 2.27,-2.54 2.84,-6.07 5.24,-8.29l0.21,-0.52 -2.25,-14z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M943.1,602.35a33.95,6.53 0,1 0,67.9 0a33.95,6.53 0,1 0,-67.9 0z"
|
||||
android:fillColor="#338000"/>
|
||||
<path
|
||||
android:pathData="M973.67,599.57a3.95,5.17 0,1 0,7.9 0a3.95,5.17 0,1 0,-7.9 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M973.67,593.25a3.95,5.17 0,1 0,7.9 0a3.95,5.17 0,1 0,-7.9 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M973.67,586.92a3.95,5.17 0,1 0,7.9 0a3.95,5.17 0,1 0,-7.9 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M973.67,580.6a3.95,5.17 0,1 0,7.9 0a3.95,5.17 0,1 0,-7.9 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M973.67,574.27a3.95,5.17 0,1 0,7.9 0a3.95,5.17 0,1 0,-7.9 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M973.67,567.95a3.95,5.17 0,1 0,7.9 0a3.95,5.17 0,1 0,-7.9 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M973.67,561.63a3.95,5.17 0,1 0,7.9 0a3.95,5.17 0,1 0,-7.9 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="m962.82,518.35a18.78,18.78 0,0 1,-1.47 -2.17l10.38,-1.71 -11.23,0.09a19,19 0,0 1,-0.36 -15l15.07,7.81 -13.9,-10.21a18.94,18.94 0,1 1,31.27 21.19,18.34 18.34,0 0,1 2.16,3.45l-13.48,7 14.38,-4.82a19.07,19.07 0,0 1,1 6.07,18.85 18.85,0 0,1 -4.06,11.71 18.93,18.93 0,1 1,-29.76 0,18.94 18.94,0 0,1 0,-23.41z"
|
||||
android:fillColor="#abc837"/>
|
||||
<path
|
||||
android:pathData="m996.64,530.05a18.85,18.85 0,0 1,-4.06 11.71,18.93 18.93,0 1,1 -29.76,0c-2.54,-3.22 33.82,-13.83 33.82,-11.71z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#55d400"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M1020.37,529.83a22.83,4.39 0,1 0,45.66 0a22.83,4.39 0,1 0,-45.66 0z"
|
||||
android:fillColor="#338000"/>
|
||||
<path
|
||||
android:pathData="M1040.94,527.97a2.66,3.48 0,1 0,5.32 0a2.66,3.48 0,1 0,-5.32 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M1040.94,523.72a2.66,3.48 0,1 0,5.32 0a2.66,3.48 0,1 0,-5.32 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M1040.94,519.46a2.66,3.48 0,1 0,5.32 0a2.66,3.48 0,1 0,-5.32 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M1040.94,515.21a2.66,3.48 0,1 0,5.32 0a2.66,3.48 0,1 0,-5.32 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M1040.94,510.96a2.66,3.48 0,1 0,5.32 0a2.66,3.48 0,1 0,-5.32 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M1040.94,506.71a2.66,3.48 0,1 0,5.32 0a2.66,3.48 0,1 0,-5.32 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="M1040.94,502.45a2.66,3.48 0,1 0,5.32 0a2.66,3.48 0,1 0,-5.32 0z"
|
||||
android:fillColor="#3f3d56"/>
|
||||
<path
|
||||
android:pathData="m1033.6,473.36a11,11 0,0 1,-1 -1.46l7,-1.15 -7.55,0.06a12.62,12.62 0,0 1,-1.17 -5.32,12.78 12.78,0 0,1 0.92,-4.77l10.14,5.26 -9.35,-6.87a12.73,12.73 0,1 1,21 14.25,12.56 12.56,0 0,1 1.45,2.32l-9.06,4.71 9.66,-3.25a12.77,12.77 0,0 1,-2.05 12,12.74 12.74,0 1,1 -20,0 12.71,12.71 0,0 1,0 -15.74z"
|
||||
android:fillColor="#abc837"/>
|
||||
<path
|
||||
android:pathData="m1056.3,481.23a12.72,12.72 0,0 1,-2.72 7.87,12.74 12.74,0 1,1 -20,0c-1.72,-2.17 22.72,-9.3 22.72,-7.87z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#55d400"
|
||||
android:fillAlpha="0.1"/>
|
||||
</vector>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/swipe_refresh"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
@@ -19,6 +20,43 @@
|
||||
android:visibility="invisible"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/permissionsCard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/activity_margin"
|
||||
android:layout_marginRight="@dimen/activity_margin"
|
||||
android:layout_marginBottom="@dimen/card_padding"
|
||||
android:visibility="gone">
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/card_padding"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:id="@+id/permissionsText"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/permissionsBtn"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:text="@string/account_caldav_missing_permissions" />
|
||||
<Button
|
||||
android:id="@+id/permissionsBtn"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/permissionsText"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:text="@string/account_permissions_action"
|
||||
android:onClick="startPermissionsActivity"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<!-- paddingBottom and clipToPadding are needed to make space for the FAB at the end -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
|
||||
235
app/src/main/res/layout/activity_permissions.xml
Normal file
235
app/src/main/res/layout/activity_permissions.xml
Normal file
@@ -0,0 +1,235 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<variable name="model" type="at.bitfire.davdroid.ui.PermissionsFragment.Model"/>
|
||||
</data>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/appIntro2BottomBarHeight">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/colorBackground">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/card_padding">
|
||||
|
||||
<at.bitfire.davdroid.ui.widget.CropImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintVertical_bias="0"
|
||||
android:maxHeight="@dimen/card_theme_max_height"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/heading"
|
||||
android:adjustViewBounds="true"
|
||||
app:verticalOffsetPercent=".45"
|
||||
app:srcCompat="@drawable/intro_permissions"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/start"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="@dimen/card_padding" />
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_end="@dimen/card_padding" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/heading"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/image"
|
||||
app:layout_constraintBottom_toTopOf="@id/text"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/end"
|
||||
android:layout_marginTop="@dimen/card_margin_title_text"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:text="@string/permissions_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/heading"
|
||||
app:layout_constraintBottom_toTopOf="@id/contactsHeading"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/end"
|
||||
android:layout_marginTop="@dimen/card_margin_title_text"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:text="@string/permissions_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/allHeading"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/text"
|
||||
app:layout_constraintBottom_toTopOf="@id/allStatus"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/allSwitch"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:text="@string/permissions_all_title" />
|
||||
<TextView
|
||||
android:id="@+id/allStatus"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/allHeading"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/allSwitch"
|
||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:text="@{model.haveAllPermissions ? @string/permissions_all_status_on : @string/permissions_all_status_off}" />
|
||||
<Switch
|
||||
android:id="@+id/allSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="@id/allHeading"
|
||||
app:layout_constraintBottom_toBottomOf="@id/allStatus"
|
||||
app:layout_constraintEnd_toStartOf="@id/end"
|
||||
android:clickable="@{!model.haveAllPermissions}"
|
||||
android:checked="@={model.needAllPermissions}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contactsHeading"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/allStatus"
|
||||
app:layout_constraintBottom_toTopOf="@id/contactsStatus"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/contactsSwitch"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:text="@string/permissions_contacts_title" />
|
||||
<TextView
|
||||
android:id="@+id/contactsStatus"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/contactsHeading"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/contactsSwitch"
|
||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:text="@{model.haveContactsPermissions ? @string/permissions_contacts_status_on : @string/permissions_contacts_status_off}" />
|
||||
<Switch
|
||||
android:id="@+id/contactsSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="@id/contactsHeading"
|
||||
app:layout_constraintBottom_toBottomOf="@id/contactsStatus"
|
||||
app:layout_constraintEnd_toStartOf="@id/end"
|
||||
android:clickable="@{!model.haveContactsPermissions}"
|
||||
android:checked="@={model.needContactsPermissions}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/calendarHeading"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/card_margin_title_text"
|
||||
app:layout_constraintTop_toBottomOf="@id/contactsStatus"
|
||||
app:layout_constraintBottom_toTopOf="@id/calendarStatus"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/calendarSwitch"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:text="@string/permissions_calendar_title" />
|
||||
<TextView
|
||||
android:id="@+id/calendarStatus"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/calendarHeading"
|
||||
app:layout_constraintBottom_toTopOf="@id/tasksHeading"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/calendarSwitch"
|
||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:text="@{model.haveCalendarPermissions ? @string/permissions_calendar_status_on : @string/permissions_calendar_status_off}" />
|
||||
<Switch
|
||||
android:id="@+id/calendarSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="@id/calendarHeading"
|
||||
app:layout_constraintBottom_toBottomOf="@id/calendarStatus"
|
||||
app:layout_constraintEnd_toStartOf="@id/end"
|
||||
android:clickable="@{!model.haveCalendarPermissions}"
|
||||
android:checked="@={model.needCalendarPermissions}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tasksHeading"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/card_margin_title_text"
|
||||
app:layout_constraintTop_toBottomOf="@id/calendarStatus"
|
||||
app:layout_constraintBottom_toTopOf="@id/tasksStatus"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/tasksSwitch"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:text="@string/permissions_tasks_title" />
|
||||
<TextView
|
||||
android:id="@+id/tasksStatus"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/tasksHeading"
|
||||
app:layout_constraintStart_toEndOf="@id/start"
|
||||
app:layout_constraintEnd_toStartOf="@id/tasksSwitch"
|
||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:text="@{model.haveTasksPermissions != null ? (model.haveTasksPermissions ? @string/permissions_tasks_status_on : @string/permissions_tasks_status_off) : @string/permissions_tasks_status_not_installed}" />
|
||||
<Switch
|
||||
android:id="@+id/tasksSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tasksHeading"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tasksStatus"
|
||||
app:layout_constraintEnd_toStartOf="@id/end"
|
||||
android:enabled="@{model.haveTasksPermissions != null}"
|
||||
android:clickable="@{!model.haveTasksPermissions}"
|
||||
android:checked="@={model.needTasksPermissions}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/appSettingsHint"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/tasksSwitch"
|
||||
app:layout_constraintStart_toStartOf="@id/start"
|
||||
app:layout_constraintEnd_toEndOf="@id/end"
|
||||
android:layout_marginTop="16dp"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:text="@string/permissions_app_settings_hint"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/appSettings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/appSettingsHint"
|
||||
app:layout_constraintStart_toStartOf="@id/start"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:text="@string/permissions_app_settings" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
|
||||
</layout>
|
||||
@@ -38,7 +38,7 @@
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Regular sync intervals"
|
||||
android:text="@string/intro_battery_title"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/batteryStatus"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
app:layout_constraintEnd_toStartOf="@id/install"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:text="@string/intro_tasks_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="0dp"
|
||||
@@ -79,8 +78,7 @@
|
||||
app:layout_constraintStart_toStartOf="@id/heading"
|
||||
app:layout_constraintEnd_toEndOf="@id/heading"
|
||||
style="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
android:text="@string/intro_tasks_not_installed" />
|
||||
|
||||
android:text="@{model.isInstalled() ? @string/intro_tasks_opentasks_installed : @string/intro_tasks_not_installed}" />
|
||||
<Switch
|
||||
android:id="@+id/install"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -89,7 +87,6 @@
|
||||
android:checked="@={model.shallBeInstalled}"
|
||||
app:layout_constraintTop_toTopOf="@id/heading"
|
||||
app:layout_constraintBottom_toBottomOf="@id/status"
|
||||
app:layout_constraintStart_toEndOf="@id/heading"
|
||||
app:layout_constraintEnd_toEndOf="@id/end"/>
|
||||
|
||||
<TextView
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
<item
|
||||
android:id="@+id/nav_donate"
|
||||
android:icon="@drawable/ic_attach_money_dark"
|
||||
android:title="@string/navigation_drawer_donate"/>
|
||||
android:title="@string/navigation_drawer_donate"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/nav_privacy"
|
||||
android:icon="@drawable/ic_cloud_off_dark"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">أخطاء الشبكة و عمليات الإدخال/الإخراج</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">أوقات المهل، مشاكل الاتصال ، …الخ (مؤقتة في العادة)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">المكتبات</string>
|
||||
<string name="about_version">النسخة %1$s (%2$d)</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Грешки с входа/изхода и мрежата.</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Проточване на времето, проблеми с връзката, и т.н. (обикновенно временни)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Библиотеки</string>
|
||||
<string name="about_version">Версия %1$s (%2$d)</string>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<string name="notification_channel_sync">Sincronització</string>
|
||||
<string name="notification_channel_sync_io_errors">Xarxa i errors E/S</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<!--global settings-->
|
||||
<!--AccountsActivity-->
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="intro_autostart_text">Toto zařízení nejspíš synchronizaci blokuje. Pokud se vás to týká, je možné to vyřešit pouze ručně.</string>
|
||||
<string name="intro_autostart_dont_show">Potřebná nastavení provedena, už nepřipomínat.*</string>
|
||||
<string name="intro_leave_unchecked">* Pro připomenutí později nezaškrtávejte. Je možné resetovat v nastavení aplikace / %s.</string>
|
||||
<string name="intro_more_info">Další informace</string>
|
||||
<string name="intro_tasks_title">Podpora úkolů</string>
|
||||
<string name="intro_tasks_not_installed">Nenainstalováno</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks nainstalováno</string>
|
||||
@@ -39,7 +40,24 @@
|
||||
<string name="intro_open_source_text">Jsme rádi, že %s používáte. Jde o opensource software. Vývoj, údržba a podpora je ale těžká práce. Prosím zvažte zapojení se (je mnoho způsobů jak) nebo podpoření vývoje darem. Bude to velmi oceněno!</string>
|
||||
<string name="intro_open_source_details">Jak se zapojit / podpořit vývoj darem</string>
|
||||
<string name="intro_open_source_dont_show">Příště už nezobrazovat</string>
|
||||
<string name="intro_more_info">Další informace</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Oprávnění</string>
|
||||
<string name="permissions_text">Aby fungovalo správně, %s vyžaduje oprávnění.</string>
|
||||
<string name="permissions_all_title">Vše níže uvedené</string>
|
||||
<string name="permissions_all_status_off">Použít toto pro zapnutí všech funkcí (doporučeno)</string>
|
||||
<string name="permissions_all_status_on">Všechna oprávnění udělena</string>
|
||||
<string name="permissions_contacts_title">Oprávnění pro přístup ke kontaktům</string>
|
||||
<string name="permissions_contacts_status_off">Bez synchronizace kontaktů (nedoporučeno)</string>
|
||||
<string name="permissions_contacts_status_on">Synchronizace kontaktů možná</string>
|
||||
<string name="permissions_calendar_title">Oprávnění pro přístup ke kalendáři</string>
|
||||
<string name="permissions_calendar_status_off">Bez synchronizace kalendáře (nedoporučeno)</string>
|
||||
<string name="permissions_calendar_status_on">Synchronizace kalendáře možná</string>
|
||||
<string name="permissions_tasks_title">Oprávnění pro OpenTasks</string>
|
||||
<string name="permissions_tasks_status_not_installed">Bez synchronizace úkolů (nenainstalováno)</string>
|
||||
<string name="permissions_tasks_status_off">Bez synchronizace úkolů (nedoporučeno)</string>
|
||||
<string name="permissions_tasks_status_on">Synchronizace úkolů možná</string>
|
||||
<string name="permissions_app_settings_hint">Pokud přepínač nefunguje, použijte nastavení aplikací / Oprávnění</string>
|
||||
<string name="permissions_app_settings">Nastavení aplikace</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Překlady</string>
|
||||
<string name="about_libraries">Knihovny</string>
|
||||
@@ -108,6 +126,12 @@
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_carddav_missing_permissions">Bez synchronizace kontaktů (chybí oprávnění)</string>
|
||||
<string name="account_caldav_missing_calendar_permissions">Bez synchronizace kalendáře (chybí oprávnění)</string>
|
||||
<string name="account_caldav_missing_tasks_permissions">Žádné úkoly k synchronizaci (chybí oprávnění)</string>
|
||||
<string name="account_caldav_missing_permissions">Bez synchronizace kalendáře a úkolů (chybí oprávnění)</string>
|
||||
<string name="account_webcal_missing_calendar_permissions">Nedaří se přistupovat ke kalendářům (chybí oprávnění)</string>
|
||||
<string name="account_permissions_action">Oprávnění</string>
|
||||
<string name="account_no_address_books">(Zatím) zde nejsou žádné adresáře kontaktů.</string>
|
||||
<string name="account_no_calendars">(Zatím) zde nejsou žádné kalendáře.</string>
|
||||
<string name="account_no_webcals">Nejsou zde žádná přihlášení k odběru kalendáře (zatím).</string>
|
||||
|
||||
@@ -17,25 +17,51 @@
|
||||
<string name="notification_channel_sync_io_errors">Netværks- og I/O-fejl</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Tidsudløb, forbindelse problemer m.m. (ofte midlertidig)</string>
|
||||
<!--IntroActivity-->
|
||||
<string name="intro_slogan1">Dine data. Dit valg.</string>
|
||||
<string name="intro_slogan2">Tag kontrol</string>
|
||||
<string name="intro_battery_title">Regelmæssigt synkroniseringsinterval</string>
|
||||
<string name="intro_battery_not_whitelisted">Deaktiveret (ikke anbefalet)</string>
|
||||
<string name="intro_battery_whitelisted">Aktiveret (anbefalet)</string>
|
||||
<string name="intro_battery_text">For regelmæssig synkroniseringsinterval, %s skal have tilladelse til at køre i baggrunden. Ellers kan Android til enhver tid pause synkronisering.</string>
|
||||
<string name="intro_battery_dont_show">Ingen regelmæssigt synkroniseringsinterval.*</string>
|
||||
<string name="intro_autostart_title">%s kompatibilitet</string>
|
||||
<string name="intro_autostart_text">Enheden blokerer sandsynligvis synkronisering. Hvis dette er tilfældet kan det kun løses manuelt.</string>
|
||||
<string name="intro_autostart_dont_show">Den krævede opsætning er udført. Stop påmindelser. *</string>
|
||||
<string name="intro_leave_unchecked">* Efterlad åben for senere påmindelse. Kan nulstilles i program opsætning / %s.</string>
|
||||
<string name="intro_more_info">Mere information</string>
|
||||
<string name="intro_tasks_title">Opgaver understøttelse</string>
|
||||
<string name="intro_tasks_not_installed">Ikke installeret</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks installeret</string>
|
||||
<string name="intro_tasks_no_app_store">ingen program-butik tilgængelig</string>
|
||||
<string name="intro_tasks_text1"><![CDATA[Android har ikke en indbygget opgaver understøttelse. Hvis opgaver er server understøttet, %s kan synkronisere dem va <a href="https://opentasks.app/">OpenTasks</a>.]]></string>
|
||||
<string name="intro_tasks_text2">For avancerede funktioner som underopgaver eller gentagne opgaver, bruges 3. part program.</string>
|
||||
<string name="intro_tasks_dont_show">Ingen opgaver understøttelse.*</string>
|
||||
<string name="intro_open_source_title">Åben kilde program</string>
|
||||
<string name="intro_open_source_text">Vi er glade for at %s bruges, som er åben kilde program. Udvikling, vedligehold og understøttelse er hårdt arbejde. Overvej at bidrage (der er flere måder) eller donere. Det vil blive meget værdsat!</string>
|
||||
<string name="intro_open_source_details">Hvordan man bidrager/donerer</string>
|
||||
<string name="intro_open_source_dont_show">Hvis ikke næste gang</string>
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Oversættelser</string>
|
||||
<string name="about_libraries">Biblioteker</string>
|
||||
<string name="about_version">Version %1$s (%2$d)</string>
|
||||
<string name="about_build_date">Oversat den %s</string>
|
||||
<string name="about_flavor_info">Denne version er kun berettiget til distribution via Google Play.</string>
|
||||
<string name="about_license_info_no_warranty">Dette program leveres ABSOLUT UDEN GARANTI. Det er fri software, og du er velkommen til at videredistribuere det under visse betingelse.</string>
|
||||
<string name="about_translations_thanks"><![CDATA[<i>tak til: </i> %s]]></string>
|
||||
<!--global settings-->
|
||||
<string name="logging_couldnt_create_file">Kan ikke oprette log fil</string>
|
||||
<string name="logging_notification_title">DAVx⁵ logning</string>
|
||||
<string name="logging_notification_text">Logger nu alle DAVx⁵ aktiviteter</string>
|
||||
<string name="logging_notification_send_log">Sende log</string>
|
||||
<string name="logging_notification_send_log">Send log</string>
|
||||
<!--AccountsActivity-->
|
||||
<string name="navigation_drawer_open">Åbne navigationsvindue</string>
|
||||
<string name="navigation_drawer_close">Luk navigationsvindue</string>
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV synkroniseringsadapter</string>
|
||||
<string name="navigation_drawer_about">Om / licens</string>
|
||||
<string name="navigation_drawer_beta_feedback">Beta tilbagemelding</string>
|
||||
<string name="install_email_client">Installere en e-post klient</string>
|
||||
<string name="install_browser">Installere en netlæser</string>
|
||||
<string name="install_email_client">Installere e-post klient</string>
|
||||
<string name="install_browser">Installere netlæser</string>
|
||||
<string name="navigation_drawer_settings">Opsætning</string>
|
||||
<string name="navigation_drawer_news_updates">Nyheder & opdateringer</string>
|
||||
<string name="navigation_drawer_external_links">Eksterne henvisninger</string>
|
||||
@@ -55,7 +81,7 @@
|
||||
<!--AppSettingsActivity-->
|
||||
<string name="app_settings">Indstillinger</string>
|
||||
<string name="app_settings_debug">Fejlsøgning</string>
|
||||
<string name="app_settings_show_debug_info">Vis fejlsøgnings information</string>
|
||||
<string name="app_settings_show_debug_info">Vis fejlsøgning information</string>
|
||||
<string name="app_settings_show_debug_info_details">Vis/del software og opsætningsoplysninger</string>
|
||||
<string name="app_settings_logging">Uddybende logning</string>
|
||||
<string name="app_settings_logging_on">Logning er aktiv</string>
|
||||
@@ -131,6 +157,8 @@
|
||||
<string name="login_account_name_required">Kontonavn påkrævet</string>
|
||||
<string name="login_account_name_already_taken">Konto navn er taget</string>
|
||||
<string name="login_account_not_created">Konto kunne ikke oprettes</string>
|
||||
<string name="login_webview_tlserror">Forbindelse sikkerheds problem (fejlkode %d)</string>
|
||||
<string name="login_webview_retry">Prøv igen</string>
|
||||
<string name="login_configuration_detection">Check konfiguration</string>
|
||||
<string name="login_querying_server">Vent, forespørger serveren…</string>
|
||||
<string name="login_no_caldav_carddav">Kunne ikke finde CalDAV- eller CardDAV-tjeneste.</string>
|
||||
|
||||
@@ -26,20 +26,38 @@
|
||||
<string name="intro_battery_dont_show">Ich brauche keine regelmäßigen Sync-Intervalle.*</string>
|
||||
<string name="intro_autostart_title">%s-Kompatibilität</string>
|
||||
<string name="intro_autostart_text">Dieses Gerät blockiert die Synchronisierung wahrscheinlich. Dies kann nur manuell behoben werden.</string>
|
||||
<string name="intro_autostart_dont_show">Ich habe die Einstellungen gemacht, nicht mehr erinnern.</string>
|
||||
<string name="intro_autostart_dont_show">Ich habe die Einstellungen gemacht, nicht mehr erinnern.*</string>
|
||||
<string name="intro_leave_unchecked">* Nicht anwählen, um später erinnert zu werden. Kann unter App-Einstellungen / %s zurückgesetzt werden.</string>
|
||||
<string name="intro_more_info">Mehr Infos</string>
|
||||
<string name="intro_tasks_title">Unterstützung für Aufgaben</string>
|
||||
<string name="intro_tasks_not_installed">Nicht installiert</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks installiert</string>
|
||||
<string name="intro_tasks_opentasks_installed">Aufgaben installiert</string>
|
||||
<string name="intro_tasks_no_app_store">Kein App-Store verfügbar</string>
|
||||
<string name="intro_tasks_text1"><![CDATA[Android unterstützt standardmäßig keine Aufgaben. Wenn Aufgaben vom Server unterstützt werden, kann %s sie über <a href="https://opentasks.app/">OpenTasks</a> synchronisieren.]]></string>
|
||||
<string name="intro_tasks_text1"><![CDATA[Android unterstützt standardmäßig keine Aufgaben. Wenn Aufgaben vom Server unterstützt werden, kann %s sie über <a href="https://opentasks.app/">OpenTasks</a> (Aufgaben) synchronisieren.]]></string>
|
||||
<string name="intro_tasks_text2">Für bestimmte Funktionen wie Unteraufgaben oder Wiederholungen benötigen Sie zusätzliche Apps.</string>
|
||||
<string name="intro_tasks_dont_show">Ich brauche keine Unterstützung für Aufgaben.</string>
|
||||
<string name="intro_open_source_title">Open-Source-Software</string>
|
||||
<string name="intro_open_source_text">Wir freuen uns, dass sie die Open-Source-Software %s verwenden. Entwicklung, Wartung und Support sind viel Arbeit. Ziehen Sie daher bitte in Betracht, mitzuhelfen (dazu gibt es viele Möglichkeiten) oder zu spenden. Vielen Dank!</string>
|
||||
<string name="intro_open_source_text">Wir freuen uns, dass Sie die Open-Source-Software %s verwenden. Entwicklung, Wartung und Support sind viel Arbeit. Ziehen Sie daher bitte in Betracht, mitzuhelfen (dazu gibt es viele Möglichkeiten) oder zu spenden. Vielen Dank!</string>
|
||||
<string name="intro_open_source_details">Infos zum Mithelfen/Spenden</string>
|
||||
<string name="intro_open_source_dont_show">In nächster Zeit nicht anzeigen</string>
|
||||
<string name="intro_more_info">Mehr Infos</string>
|
||||
<string name="intro_open_source_dont_show">In nächster Zeit nicht mehr anzeigen</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Rechteverwaltung</string>
|
||||
<string name="permissions_text">%s benötigt Rechte, um ordnungsgemäß zu funktionieren</string>
|
||||
<string name="permissions_all_title">Alles darunter</string>
|
||||
<string name="permissions_all_status_off">Benutze dies, um alle Funktionen einzuschalten (empfohlen)</string>
|
||||
<string name="permissions_all_status_on">Allen Rechten zugestimmt</string>
|
||||
<string name="permissions_contacts_title">Kontakte Rechteverwaltung</string>
|
||||
<string name="permissions_contacts_status_off">Keine Kontaktsynchronisierung (nicht empfohlen)</string>
|
||||
<string name="permissions_contacts_status_on">Kontaktsynchronisierung möglich</string>
|
||||
<string name="permissions_calendar_title">Kalender Rechteverwaltung</string>
|
||||
<string name="permissions_calendar_status_off">Keine Kalendersynchronisierung (nicht empfohlen)</string>
|
||||
<string name="permissions_calendar_status_on">Kalendersynchronisierung möglich</string>
|
||||
<string name="permissions_tasks_title">Aufgaben Rechteverwaltung</string>
|
||||
<string name="permissions_tasks_status_not_installed">Keine Aufgabensynchronisierung (nicht installiert)</string>
|
||||
<string name="permissions_tasks_status_off">Keine Aufgabensynchronisierung (nicht empfohlen)</string>
|
||||
<string name="permissions_tasks_status_on">Aufgabensynchronisierung möglich</string>
|
||||
<string name="permissions_app_settings_hint">Wenn ein Schalter nicht funktioniert, versuche es unter Einstellungen -> Apps & Benachrichtigungen</string>
|
||||
<string name="permissions_app_settings">App Einstellungen</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Übersetzungen</string>
|
||||
<string name="about_libraries">Bibliotheken</string>
|
||||
@@ -59,7 +77,7 @@
|
||||
<string name="navigation_drawer_subtitle">CalDAV/CardDAV-Sync-Adapter</string>
|
||||
<string name="navigation_drawer_about">Über / Lizenz</string>
|
||||
<string name="navigation_drawer_beta_feedback">Beta-Rückmeldung</string>
|
||||
<string name="install_email_client">Bitte installieren Sie eine Email-Anwendung</string>
|
||||
<string name="install_email_client">Bitte installieren Sie eine E-Mail-Anwendung</string>
|
||||
<string name="install_browser">Bitte installieren Sie einen Web-Browser</string>
|
||||
<string name="navigation_drawer_settings">Einstellungen</string>
|
||||
<string name="navigation_drawer_news_updates">Aktuelles</string>
|
||||
@@ -108,6 +126,12 @@
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_carddav_missing_permissions">Keine Kontaktsynchronisierung (fehlende Rechte)</string>
|
||||
<string name="account_caldav_missing_calendar_permissions">Keine Kalendersynchronisierung (fehlende Rechte)</string>
|
||||
<string name="account_caldav_missing_tasks_permissions">Keine Aufgabensynchronisierung (fehlende Rechte)</string>
|
||||
<string name="account_caldav_missing_permissions">Keine Kalender- und Kontaktsynchronisierung (fehlende Rechte)</string>
|
||||
<string name="account_webcal_missing_calendar_permissions">Fehlender Zugang zu Kalendern (fehlende Rechte)</string>
|
||||
<string name="account_permissions_action">Rechteverwaltung</string>
|
||||
<string name="account_no_address_books">(Noch) keine Adressbücher vorhanden</string>
|
||||
<string name="account_no_calendars">(Noch) keine Kalender vorhanden</string>
|
||||
<string name="account_no_webcals">(Noch) keine Kalender-Abos vorhanden</string>
|
||||
@@ -121,7 +145,7 @@
|
||||
<string name="account_delete">Konto löschen</string>
|
||||
<string name="account_delete_confirmation_title">Konto wirklich löschen?</string>
|
||||
<string name="account_delete_confirmation_text">Alle Adressbücher, Kalender und Aufgabenlisten werden vom Gerät (nicht am Server) gelöscht.</string>
|
||||
<string name="account_synchronize_this_collection">diesen Ordner synchronisieren</string>
|
||||
<string name="account_synchronize_this_collection">Diesen Ordner synchronisieren</string>
|
||||
<string name="account_read_only">schreibgeschützt</string>
|
||||
<string name="account_calendar">Kalender</string>
|
||||
<string name="account_task_list">Aufgabenliste</string>
|
||||
@@ -186,9 +210,9 @@
|
||||
<string name="settings_sync_wifi_only_ssids_on">Synchronisierung nur über %s</string>
|
||||
<string name="settings_sync_wifi_only_ssids_on_location_services">Synchronisierung nur über %s (benötigt aktive Standortdienste)</string>
|
||||
<string name="settings_sync_wifi_only_ssids_off">Alle WLAN-Verbindungen werden verwendet</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">Erlaubte WLAN-Namen (SSIDs, mit Beistrich getrennt, leer lassen für alle)</string>
|
||||
<string name="settings_sync_wifi_only_ssids_message">Erlaubte WLAN-Namen (SSIDs, mit Komma getrennt, leer lassen für alle)</string>
|
||||
<string name="settings_sync_wifi_only_ssids_location_permission">Zum Auslesen von WLAN-Namen sind die Standort-Berechtigung und die ständige Aktivierung der Standortdienste notwendig.</string>
|
||||
<string name="settings_more_info_faq">Mehr Info (FAQ)</string>
|
||||
<string name="settings_more_info_faq">Mehr Infos (FAQ)</string>
|
||||
<string name="settings_authentication">Anmeldeinformationen</string>
|
||||
<string name="settings_username">Benutzername</string>
|
||||
<string name="settings_enter_username">Benutzername eingeben:</string>
|
||||
@@ -263,7 +287,7 @@
|
||||
<string name="sync_contacts_read_only_address_book">Schreibgeschütztes Adressbuch</string>
|
||||
<string name="sync_error_permissions">DAVx⁵-Berechtigungen</string>
|
||||
<string name="sync_error_permissions_text">Zusätzliche Berechtigungen benötigt</string>
|
||||
<string name="sync_error_opentasks_too_old">OpenTasks zu alt</string>
|
||||
<string name="sync_error_opentasks_too_old">OpenTasks (Aufgaben) zu alt</string>
|
||||
<string name="sync_error_opentasks_required_version">Version %1$s benötigt (derzeit %2$s)</string>
|
||||
<string name="sync_error_authentication_failed">Anmeldungsfehler (Login-Daten überprüfen)</string>
|
||||
<string name="sync_error_io">Netzwerk- oder E/A-Fehler – %s</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Σφάλματα δικτύου και I/O</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Χρονικά όρια, προβλήματα σύνδεσης, κλπ. (συχνά προσωρινά)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Βιβλιοθήκες</string>
|
||||
<string name="about_version">Έκδοση %1$s (%2$d)</string>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="intro_autostart_text">Este dispositivo bloquea la sincronización. Si está afectado, sólo lo puede resolver manualmente.</string>
|
||||
<string name="intro_autostart_dont_show">No tengo los ajustes requeridos. No volver a recordar.*</string>
|
||||
<string name="intro_leave_unchecked">* Déjelo desmarcado para que se le recuerde más tarde. Se puede reconfigurar en los ajustes de la aplicación / %s</string>
|
||||
<string name="intro_more_info">Información adicional</string>
|
||||
<string name="intro_tasks_title">Soporte de tareas</string>
|
||||
<string name="intro_tasks_not_installed">No instalado</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks instalado</string>
|
||||
@@ -39,7 +40,24 @@
|
||||
<string name="intro_open_source_text">Nos complace que use%s, que es software open-source. Desarrollar, mantener y asistir a usuarios es un trabajo duro. Por favor, considere contribuir (hay muchas maneras) o hacer una donación. ¡Se agradecería mucho!</string>
|
||||
<string name="intro_open_source_details">Cómo contribuir o donar</string>
|
||||
<string name="intro_open_source_dont_show">No mostrar la próxima vez</string>
|
||||
<string name="intro_more_info">Información adicional</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permisos</string>
|
||||
<string name="permissions_text">%s necesita permisos para funcionar correctamente.</string>
|
||||
<string name="permissions_all_title">Todos los siguientes</string>
|
||||
<string name="permissions_all_status_off">Usa esto para activar todas las características (recomendado)</string>
|
||||
<string name="permissions_all_status_on">Todos los permisos concedidos</string>
|
||||
<string name="permissions_contacts_title">Permisos de contactos</string>
|
||||
<string name="permissions_contacts_status_off">No sincronizar contactos (no recomendado)</string>
|
||||
<string name="permissions_contacts_status_on">Sincronización de contactos permitida</string>
|
||||
<string name="permissions_calendar_title">Permisos de calendario</string>
|
||||
<string name="permissions_calendar_status_off">Sin sincronización de calendario (no recomendado)</string>
|
||||
<string name="permissions_calendar_status_on">Sincronización de calendario permitida</string>
|
||||
<string name="permissions_tasks_title">Permisos de OpenTasks</string>
|
||||
<string name="permissions_tasks_status_not_installed">Sin sincronización de tareas (no instalado)</string>
|
||||
<string name="permissions_tasks_status_off">Sin sincronización de tareas (no recomendado)</string>
|
||||
<string name="permissions_tasks_status_on">Sincronización de tareas permitidas</string>
|
||||
<string name="permissions_app_settings_hint">Si un interruptor no funciona usa Configuraciones de la app / Permisos.</string>
|
||||
<string name="permissions_app_settings">Configuraciones de la app</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Traducciones</string>
|
||||
<string name="about_libraries">Bibliotecas</string>
|
||||
@@ -108,6 +126,12 @@
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_carddav_missing_permissions">Sin sincronización de contactos (faltan permisos)</string>
|
||||
<string name="account_caldav_missing_calendar_permissions">Sin sincronización de calendario (faltan permisos)</string>
|
||||
<string name="account_caldav_missing_tasks_permissions">Sin sincronización de tareas (faltan permisos)</string>
|
||||
<string name="account_caldav_missing_permissions">Sin sincronización de calendario y tareas (faltan permisos)</string>
|
||||
<string name="account_webcal_missing_calendar_permissions">No se puede acceder a los calendarios (faltan permisos)</string>
|
||||
<string name="account_permissions_action">Permisos</string>
|
||||
<string name="account_no_address_books">No hay libretas de direciones (todavía).</string>
|
||||
<string name="account_no_calendars">No hay calendarios (todavía). </string>
|
||||
<string name="account_no_webcals">No hay suscripciones a calendarios (todavía).</string>
|
||||
@@ -156,6 +180,8 @@
|
||||
<string name="login_account_name_required">Nombre de cuenta requerido</string>
|
||||
<string name="login_account_name_already_taken">El nombre de la cuenta ya está siendo utilizado</string>
|
||||
<string name="login_account_not_created">La cuenta no pudo ser creada</string>
|
||||
<string name="login_webview_tlserror">Problema de seguridad en la conexión (código de error %d)</string>
|
||||
<string name="login_webview_retry">Reintentar</string>
|
||||
<string name="login_configuration_detection">Detectar configuración</string>
|
||||
<string name="login_querying_server">Por favor espera, consultando al servidor…</string>
|
||||
<string name="login_no_caldav_carddav">No se pudo encontrar el servicio CalDAV o CardDAV.</string>
|
||||
|
||||
@@ -15,7 +15,28 @@
|
||||
<string name="notification_channel_sync_warnings">Sinkronizazio abisuak</string>
|
||||
<string name="notification_channel_sync_io_errors">Sare eta S/I erroreak</string>
|
||||
<!--IntroActivity-->
|
||||
<string name="intro_slogan1">Zure datuak. Zure aukera.</string>
|
||||
<string name="intro_slogan2">Har ezazu kontrola.</string>
|
||||
<string name="intro_battery_not_whitelisted">Desgaituta (ez gomendatuta)</string>
|
||||
<string name="intro_battery_whitelisted">Gaituta (gomendatuta)</string>
|
||||
<string name="intro_more_info">Informazio gehiago</string>
|
||||
<string name="intro_tasks_not_installed">Instalatu gabe</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks instalatuta</string>
|
||||
<string name="intro_tasks_no_app_store">Ez dago denda aplikaziorik eskuragarri </string>
|
||||
<string name="intro_open_source_title">Kode irekiko softwarea</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Baimenak</string>
|
||||
<string name="permissions_all_status_on">Baimen guztiak eman dira</string>
|
||||
<string name="permissions_contacts_title">Kontaktuen baimenak</string>
|
||||
<string name="permissions_contacts_status_off">Kontaktu sinkronizaziorik ez (ez gomendatuta)</string>
|
||||
<string name="permissions_contacts_status_on">Kontaktuen sinkronizazioa posible</string>
|
||||
<string name="permissions_calendar_title">Egutegiaren baimenak</string>
|
||||
<string name="permissions_calendar_status_off">Egutegi sinkronizaziorik ez (ez gomendatuta)</string>
|
||||
<string name="permissions_calendar_status_on">Egutegiaren sinkronizazioa posible</string>
|
||||
<string name="permissions_tasks_title">OpenTasks baimenak</string>
|
||||
<string name="permissions_app_settings">Aplikazioaren ezarpenak</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Itzulpenak</string>
|
||||
<string name="about_libraries">Liburutegiak</string>
|
||||
<string name="about_version">%1$s (%2$d) bertsioa</string>
|
||||
<string name="about_build_date">%s(e)an konpilatuta</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">خطاهای شبکه</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">اتمام وقت، مشکل اتصال، غیره(اغلب موقت)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">کتابخانه ها</string>
|
||||
<string name="about_version">نسخه %1$s (%2$d)</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Verkko ja I/O virheet</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Aikakatkaisut, yhteysvirheet, yms. (usein väliaikaisia)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<!--global settings-->
|
||||
<!--AccountsActivity-->
|
||||
|
||||
@@ -17,12 +17,38 @@
|
||||
<string name="notification_channel_sync_io_errors">Erreurs de réseau et d\'entrée / sortie</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Délais d\'attente, problèmes de connexion, etc. (souvent temporaires)</string>
|
||||
<!--IntroActivity-->
|
||||
<string name="intro_slogan1">Vos données, votre choix.</string>
|
||||
<string name="intro_slogan2">Prenez le contrôle.</string>
|
||||
<string name="intro_battery_title">Intervalles de synchronisation régulières</string>
|
||||
<string name="intro_battery_not_whitelisted">Désactiver (non recommandé)</string>
|
||||
<string name="intro_battery_whitelisted">Activé (recommandé)</string>
|
||||
<string name="intro_battery_text">Pour une synchronisation à intervalles régulières, %s doit être autorisé à fonctionner en arrière-plan. Sinon, Android peut interrompre la synchronisation à tout moment.</string>
|
||||
<string name="intro_battery_dont_show">Je n\'ai pas besoin d\'intervalles de synchronisation régulières.*</string>
|
||||
<string name="intro_autostart_title">%s compatibilité</string>
|
||||
<string name="intro_autostart_text">Cet appareil bloque probablement la synchronisation. Si vous êtes affecté, vous ne pouvez résoudre ce problème que manuellement.</string>
|
||||
<string name="intro_autostart_dont_show">J\'ai fait les réglages nécessaires. Ne me le rappelez plus.*</string>
|
||||
<string name="intro_leave_unchecked">* Laisser non coché pour un rappel ultérieur. Peut être réinitialisé dans les paramètres de l\'application / %s.</string>
|
||||
<string name="intro_more_info">Plus d\'informations</string>
|
||||
<string name="intro_tasks_title">Support des tâches</string>
|
||||
<string name="intro_tasks_not_installed">Non installé</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks installé</string>
|
||||
<string name="intro_tasks_no_app_store">Pas de magasin d\'application disponible</string>
|
||||
<string name="intro_tasks_text1"><![CDATA[Android n\'a pas de support intégré pour les tâches. Si les tâches sont prises en charge par votre serveur, %s peut les synchroniser en utilisant <a href="https://opentasks.app/">OpenTasks</a>.]]></string>
|
||||
<string name="intro_tasks_text2">Pour les fonctionnalités avancées comme les sous-tâches ou les tâches récurrentes, vous aurez besoin d\'applications supplémentaires.</string>
|
||||
<string name="intro_tasks_dont_show">Je n\'ai pas besoin de support des tâches.*</string>
|
||||
<string name="intro_open_source_title">Logiciels open-source</string>
|
||||
<string name="intro_open_source_text">Nous sommes heureux que vous utilisiez %s, qui est un logiciel open-source. Le développement, la maintenance et l\'assistance sont un travail difficile. Veuillez envisager de contribuer (il y a plusieurs façons de le faire) ou de faire un don. Ce serait très apprécié !</string>
|
||||
<string name="intro_open_source_details">Comment contribuer/donner</string>
|
||||
<string name="intro_open_source_dont_show">Ne pas afficher la prochaine fois</string>
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Traductions</string>
|
||||
<string name="about_libraries">Librairies</string>
|
||||
<string name="about_version">Version %1$s (%2$d)</string>
|
||||
<string name="about_build_date">Compilé sur %s</string>
|
||||
<string name="about_flavor_info">Cette version ne peut être distribuée que sur Google Play.</string>
|
||||
<string name="about_license_info_no_warranty">Ce programme est fourni sans AUCUNE GARANTIE. C\'est un logiciel libre, et vous êtes en droit de le redistribuer sous certaines conditions.</string>
|
||||
<string name="about_translations_thanks"><![CDATA[<i>Merci à : </i> %s]]></string>
|
||||
<!--global settings-->
|
||||
<string name="logging_couldnt_create_file">Impossible de créer le fichier journal</string>
|
||||
<string name="logging_notification_title">Journalisation de DAVx⁵</string>
|
||||
@@ -131,6 +157,8 @@
|
||||
<string name="login_account_name_required">Nom du compte requis</string>
|
||||
<string name="login_account_name_already_taken">Le nom du compte est déjà pris</string>
|
||||
<string name="login_account_not_created">Le compte n\'a pas pu être créé</string>
|
||||
<string name="login_webview_tlserror">Problème de sécurité de la connexion (code d\'erreur %d)</string>
|
||||
<string name="login_webview_retry">Réessayer</string>
|
||||
<string name="login_configuration_detection">Détection de la configuration</string>
|
||||
<string name="login_querying_server">Veuillez patienter, nous interrogeons le serveur …</string>
|
||||
<string name="login_no_caldav_carddav">Aucun accès possible au service CalDAV ou CardDAV.</string>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="intro_autostart_text">Este dispositivo probablemente está a bloquear a sincronización. Esto só se pode solucionar de xeito manual.</string>
|
||||
<string name="intro_autostart_dont_show">Xa fixen o que me pediades. Non mo lembres máis.*</string>
|
||||
<string name="intro_leave_unchecked">* Deixar sen marcar para lembrar máis tarde. Pode restablecerse nos axustes da app / %s.</string>
|
||||
<string name="intro_more_info">Máis información</string>
|
||||
<string name="intro_tasks_title">Soporte para Tasks</string>
|
||||
<string name="intro_tasks_not_installed">Non instalada</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks instalada</string>
|
||||
@@ -39,7 +40,24 @@
|
||||
<string name="intro_open_source_text">Encántanos que uses %s, que é software de código aberto. O desenvolvemento, mantemento e soporte son un traballo difícil. Considera contribuír (hai moitos xeitos) ou facer unha doazón. Sería de agradecer!</string>
|
||||
<string name="intro_open_source_details">Como contribuír/doar</string>
|
||||
<string name="intro_open_source_dont_show">Non mostar de novo</string>
|
||||
<string name="intro_more_info">Máis información</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permisos</string>
|
||||
<string name="permissions_text">1%s require permisos para funcionar axeitadamente.</string>
|
||||
<string name="permissions_all_title">Todos os de arriba</string>
|
||||
<string name="permissions_all_status_off">Use isto para habilitar todas as características (recomendado)</string>
|
||||
<string name="permissions_all_status_on">Todos os permisos concedidos</string>
|
||||
<string name="permissions_contacts_title">Permisos de contactos</string>
|
||||
<string name="permissions_contacts_status_off">Sen sincronización de contactos (non recomendado) </string>
|
||||
<string name="permissions_contacts_status_on">É posible sincronizar os contactos</string>
|
||||
<string name="permissions_calendar_title">Permisos de calendario</string>
|
||||
<string name="permissions_calendar_status_off">Sen sincronización de calendario (non se recomenda)</string>
|
||||
<string name="permissions_calendar_status_on">É posible sincronizar o calendario</string>
|
||||
<string name="permissions_tasks_title">Permisos de OpenTasks</string>
|
||||
<string name="permissions_tasks_status_not_installed">Sen sincronización de tarefas (non instalado) </string>
|
||||
<string name="permissions_tasks_status_off">Sen sincronización de tarefas (non se recomenda)</string>
|
||||
<string name="permissions_tasks_status_on">É posible sincronizar as tarefas</string>
|
||||
<string name="permissions_app_settings_hint">Se unha opción non funciona, use configuracións da aplicación / Permisos.</string>
|
||||
<string name="permissions_app_settings">Permisos da aplicación</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Traducións</string>
|
||||
<string name="about_libraries">Bibliotecas</string>
|
||||
@@ -108,6 +126,12 @@
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_carddav_missing_permissions">Sen sincronización de contactos (faltan permisos)</string>
|
||||
<string name="account_caldav_missing_calendar_permissions">Sen sincronización de calendario (faltan permisos)</string>
|
||||
<string name="account_caldav_missing_tasks_permissions">Sen sincronización de tarefas (faltan permisos)</string>
|
||||
<string name="account_caldav_missing_permissions">Sen sincronización de calendario e tarefas (faltan permisos)</string>
|
||||
<string name="account_webcal_missing_calendar_permissions">Non se pode acceder aos calendarios (faltan permisos)</string>
|
||||
<string name="account_permissions_action">Permisos</string>
|
||||
<string name="account_no_address_books">No existen libretas de enderezos (aínda).</string>
|
||||
<string name="account_no_calendars">Non existen calendarios (aínda).</string>
|
||||
<string name="account_no_webcals">Non tes subscricións a calendario (aínda).</string>
|
||||
@@ -156,6 +180,8 @@
|
||||
<string name="login_account_name_required">Nome de conta requerido</string>
|
||||
<string name="login_account_name_already_taken">O nome de conta xa está a ser utilizado</string>
|
||||
<string name="login_account_not_created">Non se creou a conta</string>
|
||||
<string name="login_webview_tlserror">Problema de seguridade de conexión (código de erro %d)</string>
|
||||
<string name="login_webview_retry">Reintentar</string>
|
||||
<string name="login_configuration_detection">Detección da configuración</string>
|
||||
<string name="login_querying_server">Agarda por favor, consultando o servidor…</string>
|
||||
<string name="login_no_caldav_carddav">Non se atopou servizo CalDAV ou CardDAV.</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Hálózati és I/O hibák</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Időtúllépésék, kapcsolódási problémák, stb. (gyakran átmeneti problémák)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Könyvtárak</string>
|
||||
<string name="about_version">Verziószám:%1$s (%2$d)</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Errori di Rete e di I/O</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Timeouts, problemi di connessione, ecc. (spesso temporanei)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Librerie</string>
|
||||
<string name="about_version">Versione %1$s (%2$d)</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">ネットワークおよび I/O エラー</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">タイムアウト、接続の問題など (多くの場合、一時的なもの)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">ライブラリー</string>
|
||||
<string name="about_version">バージョン %1$s (%2$d)</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="notification_channel_sync_warnings_desc">Ikke-kritiske synkroniseringsproblem, som enkelte ugyldige filer</string>
|
||||
<string name="notification_channel_sync_io_errors">Nettverk- og I/O-feil</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Bibliotek</string>
|
||||
<string name="about_version">Versjon %1$s(%2$d)</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Network en I/O errors</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Timeouts, connectie problemen, etc. (vaak tijdelijk).</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Libraries</string>
|
||||
<string name="about_version">Versie%1$s(%2$d)</string>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="intro_autostart_text">To urządzenie prawdopodobnie blokuje synchronizację. Jeśli ten problem Cię dotyczy, możesz go jedynie rozwiązać ręcznie.</string>
|
||||
<string name="intro_autostart_dont_show">Wprowadziłem potrzebne ustawienia. Nie przypominaj mi ponownie.*</string>
|
||||
<string name="intro_leave_unchecked">* Pozostaw nie zaznaczone aby otrzymać przypomnienie później. Możesz to zresetować w ustawieniach aplikacji / %s. </string>
|
||||
<string name="intro_more_info">Więcej informacji</string>
|
||||
<string name="intro_tasks_title">Obsługa zadań</string>
|
||||
<string name="intro_tasks_not_installed">Nie zainstalowane</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks zainstalowane</string>
|
||||
@@ -39,7 +40,24 @@
|
||||
<string name="intro_open_source_text">Jesteśmy szczęśliwi, że używasz %s, który jest oprogramowaniem open-source. Rozwój, utrzymanie i wsparcie są ciężką pracą. Proszę rozważ wsparcie (jest na to wiele sposobów) lub dotację. Będziemy bardzo wdzięczni.</string>
|
||||
<string name="intro_open_source_details">Jak wspomóc/wesprzeć</string>
|
||||
<string name="intro_open_source_dont_show">Nie pokazuj za kolejnym razem</string>
|
||||
<string name="intro_more_info">Więcej informacji</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Uprawnienia</string>
|
||||
<string name="permissions_text">%s wymaga uprawnień aby działać prawidłowo.</string>
|
||||
<string name="permissions_all_title">Wszystkie poniższe</string>
|
||||
<string name="permissions_all_status_off">Użyj tego aby odblokować wszystkie funkcje (zalecane)</string>
|
||||
<string name="permissions_all_status_on">Wszystkie uprawnienia nadane</string>
|
||||
<string name="permissions_contacts_title">Uprawnienia kontaktów</string>
|
||||
<string name="permissions_contacts_status_off">Bez synchronizacji kontaktów (nie zalecane)</string>
|
||||
<string name="permissions_contacts_status_on">Synchronizacja kontaktów możliwa</string>
|
||||
<string name="permissions_calendar_title">Uprawnienia kalendarza</string>
|
||||
<string name="permissions_calendar_status_off">Bez synchronizacji kalendarza (nie zalecane)</string>
|
||||
<string name="permissions_calendar_status_on">Synchronizacja kalendarza możliwa</string>
|
||||
<string name="permissions_tasks_title">Uprawnienia OpenTasks</string>
|
||||
<string name="permissions_tasks_status_not_installed">Bez synchronizacji zadań (nie zainstalowane)</string>
|
||||
<string name="permissions_tasks_status_off">Bez synchronizacji zadań (nie zalecane)</string>
|
||||
<string name="permissions_tasks_status_on">Synchronizacja zadań możliwa</string>
|
||||
<string name="permissions_app_settings_hint">Jeśli przełącznik nie działa użyj ustawień aplikacji / Uprawnienia.</string>
|
||||
<string name="permissions_app_settings">Ustawienia aplikacji</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Tłumaczenia</string>
|
||||
<string name="about_libraries">Biblioteki</string>
|
||||
@@ -108,6 +126,12 @@
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_carddav_missing_permissions">Bez synchronizacji kontaktów (brakuje uprawnień)</string>
|
||||
<string name="account_caldav_missing_calendar_permissions">Bez synchronizacji kalendarza (brakuje uprawnień)</string>
|
||||
<string name="account_caldav_missing_tasks_permissions">Bez synchronizacji zadań (brakuje uprawnień)</string>
|
||||
<string name="account_caldav_missing_permissions">Bez synchronizacji kalendarza i zadań (brakuje uprawnień)</string>
|
||||
<string name="account_webcal_missing_calendar_permissions">Brak dostępu do kalendarzy (brakuje uprawnień)</string>
|
||||
<string name="account_permissions_action">Uprawnienia</string>
|
||||
<string name="account_no_address_books">Nie ma (jeszcze) książek adresowych.</string>
|
||||
<string name="account_no_calendars">Nie ma (jeszcze) kalendarzy.</string>
|
||||
<string name="account_no_webcals">Nie ma (jeszcze) subskrypcji kalendarzy.</string>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="intro_autostart_text">Este dispositivo provavelmente bloqueia a sincronização. Se for afetado por isso, você só poderá resolver isso manualmente.</string>
|
||||
<string name="intro_autostart_dont_show">Fiz as configurações necessárias. Não me lembre novamente.*</string>
|
||||
<string name="intro_leave_unchecked">* Deixe desmarcado para ser lembrado mais tarde. Pode ser redefinido nas configurações do aplicativo / %s.</string>
|
||||
<string name="intro_more_info">Mais informações</string>
|
||||
<string name="intro_tasks_title">Suporte a tarefas</string>
|
||||
<string name="intro_tasks_not_installed">Não instalado</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks instalado</string>
|
||||
@@ -39,7 +40,24 @@
|
||||
<string name="intro_open_source_text">Estamos felizes por usar o %s, que é um software de código aberto. Desenvolvimento, manutenção e suporte são um trabalho árduo. Considere contribuir (existem várias maneiras) ou fazer uma doação. Seria muito apreciado!</string>
|
||||
<string name="intro_open_source_details">Como contribuir/doar</string>
|
||||
<string name="intro_open_source_dont_show">Não exibir na próxima vez</string>
|
||||
<string name="intro_more_info">Mais informações</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Permissões</string>
|
||||
<string name="permissions_text">%s requer permissões para trabalhar corretamente.</string>
|
||||
<string name="permissions_all_title">Todos os abaixo</string>
|
||||
<string name="permissions_all_status_off">Use isto para ativar todas as funcionalidades (recomendado)</string>
|
||||
<string name="permissions_all_status_on">Todas as permissões concedidas</string>
|
||||
<string name="permissions_contacts_title">Permissões de contatos</string>
|
||||
<string name="permissions_contacts_status_off">Nenhum contato sincronizado (não recomendado)</string>
|
||||
<string name="permissions_contacts_status_on">Possível sincronização de contatos</string>
|
||||
<string name="permissions_calendar_title">Permissões de calendário</string>
|
||||
<string name="permissions_calendar_status_off">Nenhum calendário sincronizado (não recomendado)</string>
|
||||
<string name="permissions_calendar_status_on">Possível sincronização de calendário</string>
|
||||
<string name="permissions_tasks_title">Permissões do OpenTasks</string>
|
||||
<string name="permissions_tasks_status_not_installed">Nenhuma tarefa sincronizada (não instalado)</string>
|
||||
<string name="permissions_tasks_status_off">Nenhuma tarefa sincronizada (não recomendado)</string>
|
||||
<string name="permissions_tasks_status_on">Possível sincronização de tarefa</string>
|
||||
<string name="permissions_app_settings_hint">Se uma opção não funcionar, use as configurações / permissões do aplicativo.</string>
|
||||
<string name="permissions_app_settings">Configurações do aplicativo</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Traduções</string>
|
||||
<string name="about_libraries">Bibliotecas</string>
|
||||
@@ -108,6 +126,12 @@
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_carddav_missing_permissions">Nenhuma sincronização de contatos (falta de permissões)</string>
|
||||
<string name="account_caldav_missing_calendar_permissions">Nenhuma sincronização de calendário (falta de permissões)</string>
|
||||
<string name="account_caldav_missing_tasks_permissions">Nenhuma sincronização de tarefas (falta de permissões)</string>
|
||||
<string name="account_caldav_missing_permissions">Nenhuma sincronização de calendário e tarefas (falta de permissões)</string>
|
||||
<string name="account_webcal_missing_calendar_permissions">Não é possível acessar calendários (falta de permissões)</string>
|
||||
<string name="account_permissions_action">Permissões</string>
|
||||
<string name="account_no_address_books">Não há livros de endereços (ainda).</string>
|
||||
<string name="account_no_calendars">Não há calendários (ainda).</string>
|
||||
<string name="account_no_webcals">Não há assinaturas de calendário (ainda).</string>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<string name="app_name">DAVDroid</string>
|
||||
<string name="help">Ajuda</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<!--global settings-->
|
||||
<!--AccountsActivity-->
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="intro_autostart_text">Это устройство, вероятно, блокирует синхронизацию. В этом случае, вы можете решить проблему только самостоятельно.</string>
|
||||
<string name="intro_autostart_dont_show">Я выполнил необходимые настройки. Больше не напоминать.*</string>
|
||||
<string name="intro_leave_unchecked">* Не отмечайте, чтобы получить напоминание повторно. Можно сбросить в настройках приложения / %s.</string>
|
||||
<string name="intro_more_info">Дополнительная информация</string>
|
||||
<string name="intro_tasks_title">Поддержка задач</string>
|
||||
<string name="intro_tasks_not_installed">Не установлено</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks установлен</string>
|
||||
@@ -39,7 +40,24 @@
|
||||
<string name="intro_open_source_text">Мы рады, что вы используете %s, программное обеспечение с открытым исходным кодом. Разработка, сопровождение и поддержка - это тяжелая работа. Пожалуйста, подумайте о том, чтобы внести свой вклад (существует множество способов) или сделать пожертвование. Будем очень признательны!</string>
|
||||
<string name="intro_open_source_details">Как внести свой вклад/пожертвовать</string>
|
||||
<string name="intro_open_source_dont_show">Не показывать в следующий раз</string>
|
||||
<string name="intro_more_info">Дополнительная информация</string>
|
||||
<!--PermissionsActivity-->
|
||||
<string name="permissions_title">Разрешения</string>
|
||||
<string name="permissions_text">%s необходимы разрешения для корректной работы.</string>
|
||||
<string name="permissions_all_title">Все нижеперечисленное</string>
|
||||
<string name="permissions_all_status_off">Используйте это для включения всех возможностей (рекомендуется)</string>
|
||||
<string name="permissions_all_status_on">Все разрешения предоставлены</string>
|
||||
<string name="permissions_contacts_title">Разрешения для контактов</string>
|
||||
<string name="permissions_contacts_status_off">Не синхронизировать календарь (не рекомендуется)</string>
|
||||
<string name="permissions_contacts_status_on">Возможна синхронизация контактов</string>
|
||||
<string name="permissions_calendar_title">Разрешения для календаря</string>
|
||||
<string name="permissions_calendar_status_off">Не синхронизировать календарь (не рекомендуется)</string>
|
||||
<string name="permissions_calendar_status_on">Возможна синхронизация календаря</string>
|
||||
<string name="permissions_tasks_title">Разрешения OpenTasks</string>
|
||||
<string name="permissions_tasks_status_not_installed">Не синхронизировать задачи (не установлены)</string>
|
||||
<string name="permissions_tasks_status_off">Не синхронизировать задачи (не рекомендуется)</string>
|
||||
<string name="permissions_tasks_status_on">Возможна синхронизация задач</string>
|
||||
<string name="permissions_app_settings_hint">Если переключатель не работает, используйте настройки приложения / разрешения.</string>
|
||||
<string name="permissions_app_settings">Настройки приложения</string>
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Переводы</string>
|
||||
<string name="about_libraries">Библиотеки</string>
|
||||
@@ -108,6 +126,12 @@
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">WebСal</string>
|
||||
<string name="account_carddav_missing_permissions">Не синхронизировать контакты (отсутствуют разрешения)</string>
|
||||
<string name="account_caldav_missing_calendar_permissions">Не синхронизировать календарь (отсутствуют разрешения)</string>
|
||||
<string name="account_caldav_missing_tasks_permissions">Не синхронизировать задачи (отсутствуют разрешения)</string>
|
||||
<string name="account_caldav_missing_permissions">Не синхронизировать календарь и задачи (отсутствуют разрешения)</string>
|
||||
<string name="account_webcal_missing_calendar_permissions">Нет доступа к календарям (отсутствуют разрешения)</string>
|
||||
<string name="account_permissions_action">Разрешения</string>
|
||||
<string name="account_no_address_books">Нет адресных книг (пока).</string>
|
||||
<string name="account_no_calendars">Нет календарей (пока).</string>
|
||||
<string name="account_no_webcals">Нет подписок на календарь (пока).</string>
|
||||
@@ -133,29 +157,31 @@
|
||||
<string name="account_install_icsx5">Установить ICSx⁵</string>
|
||||
<!--AddAccountActivity-->
|
||||
<string name="login_title">Добавить аккаунт</string>
|
||||
<string name="login_type_email">Войти по email</string>
|
||||
<string name="login_email_address">Адрес электронной почты</string>
|
||||
<string name="login_email_address_error">Требуется действующий адрес электронной почты</string>
|
||||
<string name="login_type_email">Войти c адресом email</string>
|
||||
<string name="login_email_address">Адрес email</string>
|
||||
<string name="login_email_address_error">Требуется действительный адрес email</string>
|
||||
<string name="login_password">Пароль</string>
|
||||
<string name="login_password_required">Требуется пароль</string>
|
||||
<string name="login_type_url">Войти по URL-адресу и имени пользователя</string>
|
||||
<string name="login_type_url">Войти с URL и именем пользователя</string>
|
||||
<string name="login_url_must_be_http_or_https">URL должен начинаться с http(s)://</string>
|
||||
<string name="login_url_must_be_https">URL-адрес должен начинаться с https://</string>
|
||||
<string name="login_url_must_be_https">URL должен начинаться с https://</string>
|
||||
<string name="login_url_host_name_required">Требуется имя хоста</string>
|
||||
<string name="login_user_name">Имя пользователя</string>
|
||||
<string name="login_user_name_required">Требуется Имя пользователя</string>
|
||||
<string name="login_base_url">Базовый URL</string>
|
||||
<string name="login_type_url_certificate">Войти по URL-адресу и клиентскому сертификату</string>
|
||||
<string name="login_type_url_certificate">Войти с URL и сертификатом клиента</string>
|
||||
<string name="login_select_certificate">Выберите сертификат</string>
|
||||
<string name="login_login">Войти</string>
|
||||
<string name="login_back">Назад</string>
|
||||
<string name="login_create_account">Создать аккаунт</string>
|
||||
<string name="login_account_name">Имя аккаунта</string>
|
||||
<string name="login_account_name_info">Используйте ваш адрес адрес электронной почты в качестве имени аккаунта, поскольку Android будет использовать имя аккаунта в поле ORGANIZER для событий, которые вы создаете. У вас не может быть двух аккаунтов с тем же именем.</string>
|
||||
<string name="login_account_name_info">Укажите ваш адрес email в качестве названия аккаунта, поскольку Android будет его использовать в поле ORGANIZER для создаваемых событий. У вас не может быть двух аккаунтов с тем же именем.</string>
|
||||
<string name="login_account_contact_group_method">Метод группировки контактов:</string>
|
||||
<string name="login_account_name_required">Требуется имя аккаунта</string>
|
||||
<string name="login_account_name_already_taken">Имя аккаунта уже используется</string>
|
||||
<string name="login_account_not_created">Аккаунт не может быть создан</string>
|
||||
<string name="login_webview_tlserror">Проблема безопасности соединения (код ошибки %d)</string>
|
||||
<string name="login_webview_retry">Повторить</string>
|
||||
<string name="login_configuration_detection">Обнаружение конфигурации</string>
|
||||
<string name="login_querying_server">Ожидайте, выполняется запрос к серверу…</string>
|
||||
<string name="login_no_caldav_carddav">Не удалось найти службу CalDAV или CardDAV.</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Sieťové a V/V chyby</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Vypršanie času, problémy spojenia, atď. (často dočasné)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Knižnice</string>
|
||||
<string name="about_version">Verzia %1$s (%2$d)</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Omrežje in I/O napake</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Pavze, povezave v povezavi z omrežjem, itd. (ponavadi začasno)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Knjižnice </string>
|
||||
<string name="about_version">Verzija %1$s (%2$d)</string>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<string name="notification_channel_sync_errors">Грешке синхронизације</string>
|
||||
<string name="notification_channel_sync_io_errors">Мрежне и У/И грешке</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Библиотеке</string>
|
||||
<string name="about_version">Издање %1$s (%2$d)</string>
|
||||
|
||||
@@ -17,12 +17,38 @@
|
||||
<string name="notification_channel_sync_io_errors">Błyndy necu i wchodu/wychodu</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Braki ôdpowiedzi, problymy połōnczynio, itp. (z wiynksza tymczasowe)</string>
|
||||
<!--IntroActivity-->
|
||||
<string name="intro_slogan1">Twoje dane. Twōj wybōr.</string>
|
||||
<string name="intro_slogan2">Przejmij kōntrola.</string>
|
||||
<string name="intro_battery_title">Regularne interwały synchrōnizacyje</string>
|
||||
<string name="intro_battery_not_whitelisted">Zastawiōne (niyrekōmyndowane)</string>
|
||||
<string name="intro_battery_whitelisted">Włōnczōne (rekōmyndowane)</string>
|
||||
<string name="intro_battery_text">Żeby regularnie synchrōnizować, %s musi mieć zwolo na fungowanie na zadku. W inkszym przipadku Android może w kożdyj chwili zastawić synchrōnizacyjo.</string>
|
||||
<string name="intro_battery_dont_show">Niy potrzebuja regularnyj synchrōnizacyje.*</string>
|
||||
<string name="intro_autostart_title">Zgodność %s</string>
|
||||
<string name="intro_autostart_text">Ta maszina może blokować synchrōnizacyjo. Jeźli cie to tyko, możesz to rozwiōnzać ino ryncznie.</string>
|
||||
<string name="intro_autostart_dont_show">Już mōm wymogane sztelōnki. Niy spōminej mi wiyncyj.*</string>
|
||||
<string name="intro_leave_unchecked">* Ôstow niyzaznaczōne, żeby spōmniało ô sobie niyskorzij. Idzie to zmiynić we sztelōnkach aplikacyje / %s</string>
|
||||
<string name="intro_more_info">Wiyncyj informacyji</string>
|
||||
<string name="intro_tasks_title">Sparcie Zadań</string>
|
||||
<string name="intro_tasks_not_installed">Niyzainstalowane</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks zainstalowany</string>
|
||||
<string name="intro_tasks_no_app_store">Niy je dostympny żodyn sklep ze aplikacyjami</string>
|
||||
<string name="intro_tasks_text1"><![CDATA[Android niy mo wbudowanego sparcio zadań. Jeźli zadania sōm spiyrane ôd twojigo serwera, to %s może je synchrōnizować bez <a href="https://opentasks.app/">OpenTasks</a>.]]></string>
|
||||
<string name="intro_tasks_text2">Do szyrszych działań jak podzadania abo zadania, co sie powtorzajōm, potrzebujesz ekstra aplikacyji.</string>
|
||||
<string name="intro_tasks_dont_show">Niy potrzebuja sparcio zadań.*</string>
|
||||
<string name="intro_open_source_title">Ôprogramowanie Open-Source</string>
|
||||
<string name="intro_open_source_text">Sōm my radzi, że używosz %s, ôprogramowanie open-source. Tworzynie, utrzimanie i sparcie to ciynżko robota. Pōmyśl nad pōmocōm (sōm rozmajte spusoby) abo dowkōm. Fest by nos to ucieszyło!</string>
|
||||
<string name="intro_open_source_details">Jak pōmōc/dociepnōńć sie</string>
|
||||
<string name="intro_open_source_dont_show">Niy pokazuj zaś</string>
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">Przekłady</string>
|
||||
<string name="about_libraries">Bibliotyki</string>
|
||||
<string name="about_version">Wersyjo %1$s (%2$d)</string>
|
||||
<string name="about_build_date">Skōmpilowano %s</string>
|
||||
<string name="about_flavor_info">Ta wersyjo je do dystrybucyje ino na Google Play.</string>
|
||||
<string name="about_license_info_no_warranty">Tyn program przichodzi BEZ ŻODNYJ GWARANCYJE. To je wolne ôprogramowanie i możesz je rozkludzać pod ôkryślōnymi warōnkami.</string>
|
||||
<string name="about_translations_thanks"><![CDATA[<i>Dziynki: </i> %s]]></string>
|
||||
<!--global settings-->
|
||||
<string name="logging_couldnt_create_file">Niy szło stworzić zbioru dziynnika</string>
|
||||
<string name="logging_notification_title">Dziynnik DAVx⁵</string>
|
||||
@@ -34,6 +60,8 @@
|
||||
<string name="navigation_drawer_subtitle">Adapter synchrōnizacyje CalDAV/CardDAV</string>
|
||||
<string name="navigation_drawer_about">Ô DAVx⁵ / Licyncyjo</string>
|
||||
<string name="navigation_drawer_beta_feedback">Przekoż ôpinijo</string>
|
||||
<string name="install_email_client">Zainstaluj klijynt emaila</string>
|
||||
<string name="install_browser">Zainstaluj przeglōndarka internetowo</string>
|
||||
<string name="navigation_drawer_settings">Sztelōnki</string>
|
||||
<string name="navigation_drawer_news_updates">Nowości i aktualizacyje</string>
|
||||
<string name="navigation_drawer_external_links">Zewnyntrzne linki</string>
|
||||
@@ -42,6 +70,8 @@
|
||||
<string name="navigation_drawer_faq">Pytania i ôdpowiedzi</string>
|
||||
<string name="navigation_drawer_forums">Pōmoc / Forum</string>
|
||||
<string name="navigation_drawer_donate">Dowka</string>
|
||||
<string name="navigation_drawer_privacy_policy">Polityka prywatności</string>
|
||||
<string name="account_list_no_internet">Brak połōnczynio z internetym. Android niy bydzie synchrōnizować.</string>
|
||||
<string name="account_list_empty">Witōmy w DAVx⁵!\n\nMożesz teroz przidać kōnto CalDAV/CardDAV.</string>
|
||||
<string name="accounts_global_sync_disabled">Autōmatyczno synchrōnizacyjo dlo cołkigo systymu je zastawiōno</string>
|
||||
<string name="accounts_global_sync_enable">Włōncz</string>
|
||||
@@ -127,6 +157,8 @@
|
||||
<string name="login_account_name_required">Wymogane miano kōnta</string>
|
||||
<string name="login_account_name_already_taken">Miano kōnta je już zajynte</string>
|
||||
<string name="login_account_not_created">Kōnto niy mogło być stworzōne</string>
|
||||
<string name="login_webview_tlserror">Problym bezpieczyństwa połōnczynio (kod felera %d)</string>
|
||||
<string name="login_webview_retry">Sprōbuj zaś</string>
|
||||
<string name="login_configuration_detection">Wykrywanie kōnfiguracyje</string>
|
||||
<string name="login_querying_server">Czekej, ôdpytowanie serwera…</string>
|
||||
<string name="login_no_caldav_carddav">Niy idzie znojść usugi CalDAV abo CardDAV.</string>
|
||||
@@ -174,6 +206,14 @@
|
||||
<item quantity="other">Zdarzynia starsze aniżeli %d dni bydōm zignorowane.</item>
|
||||
</plurals>
|
||||
<string name="settings_sync_time_range_past_message">Zdarzynia, co sōm starsze aniżeli podano wielość dni, bydōm zignorowane (może być 0). Ôstow prōzne, coby synchrōnizować wszyjske zdarzynia.</string>
|
||||
<string name="settings_default_alarm">Wychodne spōmniynie</string>
|
||||
<plurals name="settings_default_alarm_on">
|
||||
<item quantity="one">Wychodne spōmniynie minuta przed zdarzyniym</item>
|
||||
<item quantity="few">Wychodne spōmniynie %d minuty przed zdarzyniym</item>
|
||||
<item quantity="other">Wychodne spōmniynie %d minut przed zdarzyniym</item>
|
||||
</plurals>
|
||||
<string name="settings_default_alarm_off">Niy były stworzōne żodne wychodne spōmniynia</string>
|
||||
<string name="settings_default_alarm_message">Jeźli wychodne spōmniynia bydōm tworzōne do zdarzyń bez spōmniynio: liczba minut przed zdarzyniym. Ôstow prōzne, żeby zastawić wychodne spōmniynia.</string>
|
||||
<string name="settings_manage_calendar_colors">Zarzōndzej farbami kalyndorza</string>
|
||||
<string name="settings_manage_calendar_colors_on">Farby kalyndorza sōm zarzōndzane ôd DAVx⁵</string>
|
||||
<string name="settings_manage_calendar_colors_off">Farby kalyndorza niy sōm sztelowane ôd DAVx⁵</string>
|
||||
@@ -182,6 +222,10 @@
|
||||
<string name="settings_event_colors_off">Niy synchrōnizuj farbōw zdarzyń</string>
|
||||
<string name="settings_carddav">CardDAV</string>
|
||||
<string name="settings_contact_group_method">Spusōb grupy kōntaktōw</string>
|
||||
<string-array name="settings_contact_group_method_entries">
|
||||
<item>Grupy to sōm ôddzielne VCards</item>
|
||||
<item>Grupy to sōm kategoryje co kōntakt</item>
|
||||
</string-array>
|
||||
<string name="settings_contact_group_method_change">Zmiyń spusōb grupowy</string>
|
||||
<!--collection management-->
|
||||
<string name="create_addressbook">Stwōrz ksiōnżka adresowo</string>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<string name="manage_accounts">Hesapları yönet</string>
|
||||
<string name="send">Gönder</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_license_info_no_warranty">Bu uygulama HİÇ BİR GARANTİ ile gelmemektedir. Bedava bir yazılımdır ve belli koşullar altında dağıtabilirsiniz.</string>
|
||||
<!--global settings-->
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">Помилка мережі та вводу/виводу</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">Спливання часу відклику, проблеми зв\'язку, і т.п. (часто тимчасові)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">Бібліотеки</string>
|
||||
<string name="about_version">Версія %1$s (%2$d)</string>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<string name="intro_autostart_text">该设备可能会阻止同步。如果您受到影响,则只能手动解决。</string>
|
||||
<string name="intro_autostart_dont_show">我已完成所需的设置。不再提醒我。*</string>
|
||||
<string name="intro_leave_unchecked">*取消选中以供稍后提醒。可以在应用设置中重置/%s。</string>
|
||||
<string name="intro_more_info">更多信息</string>
|
||||
<string name="intro_tasks_title">任务支持</string>
|
||||
<string name="intro_tasks_not_installed">未安装</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks 已安装</string>
|
||||
@@ -39,7 +40,7 @@
|
||||
<string name="intro_open_source_text">我们很高兴您使用%s开源软件。开发,维护和支持是艰苦的工作。请考虑捐款(有很多方式)或捐款。将不胜感激!</string>
|
||||
<string name="intro_open_source_details">如何捐款/捐款</string>
|
||||
<string name="intro_open_source_dont_show">下次不再显示</string>
|
||||
<string name="intro_more_info">更多信息</string>
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_translations">翻译</string>
|
||||
<string name="about_libraries">程序库</string>
|
||||
@@ -156,6 +157,8 @@
|
||||
<string name="login_account_name_required">请输入账户名</string>
|
||||
<string name="login_account_name_already_taken">账户名已被占用</string>
|
||||
<string name="login_account_not_created">账户无法创建</string>
|
||||
<string name="login_webview_tlserror">连接安全问题 (错误码%d)</string>
|
||||
<string name="login_webview_retry">重试</string>
|
||||
<string name="login_configuration_detection">正在配置</string>
|
||||
<string name="login_querying_server">正在与服务器通信,请稍等…</string>
|
||||
<string name="login_no_caldav_carddav">找不到 CalDAV 或 CardDAV 服务。</string>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<string name="notification_channel_sync_io_errors">網際網絡和輸入輸出錯誤</string>
|
||||
<string name="notification_channel_sync_io_errors_desc">超時,連接問題等(臨時問題)</string>
|
||||
<!--IntroActivity-->
|
||||
<!--PermissionsActivity-->
|
||||
<!--AboutActivity-->
|
||||
<string name="about_libraries">庫</string>
|
||||
<string name="about_version">版本號%1$s(%2$d)</string>
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<string name="intro_autostart_text">This device probably blocks synchronization. If you\'re affected, you can only resolve this manually.</string>
|
||||
<string name="intro_autostart_dont_show">I have done the required settings. Don\'t remind me anymore.*</string>
|
||||
<string name="intro_leave_unchecked">* Leave unchecked to be reminded later. Can be reset in app settings / %s.</string>
|
||||
<string name="intro_more_info">More information</string>
|
||||
<string name="intro_tasks_title">Tasks support</string>
|
||||
<string name="intro_tasks_not_installed">Not installed</string>
|
||||
<string name="intro_tasks_opentasks_installed">OpenTasks installed</string>
|
||||
@@ -54,7 +55,25 @@
|
||||
<string name="intro_open_source_text">We\'re happy that you use %s, which is open-source software. Development, maintenance and support are hard work. Please consider contributing (there are many ways) or a donation. It would be highly appreciated!</string>
|
||||
<string name="intro_open_source_details">How to contribute/donate</string>
|
||||
<string name="intro_open_source_dont_show">Don\'t show in the next time</string>
|
||||
<string name="intro_more_info">More information</string>
|
||||
|
||||
<!-- PermissionsActivity -->
|
||||
<string name="permissions_title">Permissions</string>
|
||||
<string name="permissions_text">%s requires permissions to work properly.</string>
|
||||
<string name="permissions_all_title">All of the below</string>
|
||||
<string name="permissions_all_status_off">Use this to enable all features (recommended)</string>
|
||||
<string name="permissions_all_status_on">All permissions granted</string>
|
||||
<string name="permissions_contacts_title">Contacts permissions</string>
|
||||
<string name="permissions_contacts_status_off">No contact sync (not recommended)</string>
|
||||
<string name="permissions_contacts_status_on">Contact sync possible</string>
|
||||
<string name="permissions_calendar_title">Calendar permissions</string>
|
||||
<string name="permissions_calendar_status_off">No calendar sync (not recommended)</string>
|
||||
<string name="permissions_calendar_status_on">Calendar sync possible</string>
|
||||
<string name="permissions_tasks_title">OpenTasks permissions</string>
|
||||
<string name="permissions_tasks_status_not_installed">No task sync (not installed)</string>
|
||||
<string name="permissions_tasks_status_off">No task sync (not recommended)</string>
|
||||
<string name="permissions_tasks_status_on">Task sync possible</string>
|
||||
<string name="permissions_app_settings_hint">If a switch doesn\'t work, use app settings / Permissions.</string>
|
||||
<string name="permissions_app_settings">App settings</string>
|
||||
|
||||
<!-- AboutActivity -->
|
||||
<string name="about_translations">Translations</string>
|
||||
@@ -130,6 +149,12 @@
|
||||
<string name="account_carddav">CardDAV</string>
|
||||
<string name="account_caldav">CalDAV</string>
|
||||
<string name="account_webcal">Webcal</string>
|
||||
<string name="account_carddav_missing_permissions">No contacts sync (missing permissions)</string>
|
||||
<string name="account_caldav_missing_calendar_permissions">No calendar sync (missing permissions)</string>
|
||||
<string name="account_caldav_missing_tasks_permissions">No tasks sync (missing permissions)</string>
|
||||
<string name="account_caldav_missing_permissions">No calendar and tasks sync (missing permissions)</string>
|
||||
<string name="account_webcal_missing_calendar_permissions">Can\'t access calendars (missing permissions)</string>
|
||||
<string name="account_permissions_action">Permissions</string>
|
||||
<string name="account_no_address_books">There are no address books (yet).</string>
|
||||
<string name="account_no_calendars">There are no calendars (yet).</string>
|
||||
<string name="account_no_webcals">There are no calendar subscriptions (yet).</string>
|
||||
|
||||
@@ -9,6 +9,5 @@
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="@string/account_type"
|
||||
android:contentAuthority="@string/address_books_authority"
|
||||
android:isAlwaysSyncable="true"
|
||||
android:allowParallelSyncs="true"
|
||||
android:supportsUploading="false" />
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="@string/account_type"
|
||||
android:contentAuthority="com.android.calendar"
|
||||
android:supportsUploading="true"
|
||||
android:isAlwaysSyncable="true" />
|
||||
android:allowParallelSyncs="true"
|
||||
android:supportsUploading="true" />
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="@string/account_type_address_book"
|
||||
android:contentAuthority="com.android.contacts"
|
||||
android:supportsUploading="true"
|
||||
android:isAlwaysSyncable="true" />
|
||||
android:allowParallelSyncs="true"
|
||||
android:supportsUploading="true" />
|
||||
|
||||
@@ -9,4 +9,5 @@
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accountType="@string/account_type"
|
||||
android:contentAuthority="org.dmfs.tasks"
|
||||
android:allowParallelSyncs="true"
|
||||
android:supportsUploading="true" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
at.bitfire.davdroid.ui.intro.WelcomeFragment$Factory
|
||||
at.bitfire.davdroid.ui.intro.BatteryOptimizationsFragment$Factory
|
||||
at.bitfire.davdroid.ui.intro.OpenTasksFragment$Factory
|
||||
at.bitfire.davdroid.ui.intro.PermissionsFragmentFactory
|
||||
at.bitfire.davdroid.ui.intro.BatteryOptimizationsFragment$Factory
|
||||
at.bitfire.davdroid.ui.intro.OpenSourceFragment$Factory
|
||||
|
||||
@@ -10,10 +10,10 @@ buildscript {
|
||||
ext.versions = [
|
||||
aboutLibraries: '8.1.2',
|
||||
appIntro: '5.1.0',
|
||||
dav4jvm: '2.0',
|
||||
dav4jvm: 'd0422e78',
|
||||
dokka: '0.10.0',
|
||||
kotlin: '1.3.71',
|
||||
okhttp: '4.5.0'
|
||||
kotlin: '1.3.72',
|
||||
okhttp: '4.6.0'
|
||||
]
|
||||
|
||||
repositories {
|
||||
@@ -21,7 +21,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||
classpath 'com.android.tools.build:gradle:4.0.0-beta05'
|
||||
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${versions.aboutLibraries}"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
|
||||
|
||||
1
fastlane/metadata/android/eu/short_description.txt
Normal file
1
fastlane/metadata/android/eu/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
CalDAV/CardDAV sinkronizazio eta bezeroa
|
||||
Submodule ical4android updated: cbad9e5dfc...ef153fc01c
Reference in New Issue
Block a user