From fb90955b2a59684d480c88d70fae056f8b6f1e50 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Thu, 29 Nov 2018 23:00:43 +0100 Subject: [PATCH 001/382] Switch to AndroidX --- app/build.gradle | 17 +++---- .../davdroid/CustomTlsSocketFactoryTest.kt | 4 +- .../davdroid/model/CollectionInfoTest.kt | 2 +- .../bitfire/davdroid/settings/SettingsTest.kt | 6 +-- .../ui/setup/DavResourceFinderTest.kt | 6 +-- app/src/main/AndroidManifest.xml | 2 +- app/src/main/java/at/bitfire/davdroid/App.kt | 2 +- .../java/at/bitfire/davdroid/DavService.kt | 4 +- .../java/at/bitfire/davdroid/log/Logger.kt | 4 +- .../AddressBooksSyncAdapterService.kt | 2 +- .../syncadapter/ContactsSyncManager.kt | 2 +- .../syncadapter/SyncAdapterService.kt | 6 +-- .../davdroid/syncadapter/SyncManager.kt | 4 +- .../syncadapter/TasksSyncAdapterService.kt | 4 +- .../at/bitfire/davdroid/ui/AboutActivity.kt | 18 +++---- .../at/bitfire/davdroid/ui/AccountActivity.kt | 31 +++++++----- .../davdroid/ui/AccountListFragment.kt | 16 +++---- .../davdroid/ui/AccountSettingsActivity.kt | 48 +++++++++---------- .../bitfire/davdroid/ui/AccountsActivity.kt | 16 +++---- .../davdroid/ui/AppSettingsActivity.kt | 12 ++--- .../davdroid/ui/CollectionInfoFragment.kt | 4 +- .../davdroid/ui/CreateAddressBookActivity.kt | 12 ++--- .../davdroid/ui/CreateCalendarActivity.kt | 14 +++--- .../davdroid/ui/CreateCollectionFragment.kt | 14 +++--- .../bitfire/davdroid/ui/DebugInfoActivity.kt | 14 +++--- .../davdroid/ui/DeleteCollectionFragment.kt | 16 +++---- .../davdroid/ui/ExceptionInfoFragment.kt | 4 +- .../bitfire/davdroid/ui/NotificationUtils.kt | 2 +- .../at/bitfire/davdroid/ui/SettingsLoader.kt | 2 +- .../davdroid/ui/StartupDialogFragment.kt | 12 ++--- .../ui/setup/AccountDetailsFragment.kt | 10 ++-- .../setup/DefaultLoginCredentialsFragment.kt | 2 +- .../ui/setup/DetectConfigurationFragment.kt | 16 +++---- .../davdroid/ui/setup/LoginActivity.kt | 2 +- .../ui/widget/IntEditTextPreference.kt | 2 +- app/src/main/res/layout/account_list_item.xml | 4 +- app/src/main/res/layout/accounts_content.xml | 12 ++--- app/src/main/res/layout/activity_about.xml | 22 ++++----- app/src/main/res/layout/activity_account.xml | 18 +++---- app/src/main/res/layout/activity_accounts.xml | 6 +-- .../res/layout/login_credentials_fragment.xml | 36 +++++++------- build.gradle | 2 +- cert4android | 2 +- dav4android | 2 +- gradle.properties | 4 ++ ical4android | 2 +- vcard4android | 2 +- 47 files changed, 226 insertions(+), 218 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 23ee887aa..0ca7e8f50 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,7 +63,7 @@ android { } defaultConfig { - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } } @@ -75,13 +75,14 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support:cardview-v7:28.0.0' - implementation 'com.android.support:design:28.0.0' - implementation 'com.android.support:preference-v14:28.0.0' + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.fragment:fragment:1.0.0' + implementation 'androidx.legacy:legacy-preference-v14:1.0.0' + implementation 'com.google.android.material:material:1.0.0' implementation 'com.github.yukuku:ambilwarna:2.0.1' - implementation 'com.mikepenz:aboutlibraries:6.0.9' + implementation 'com.mikepenz:aboutlibraries:6.2.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' implementation 'commons-io:commons-io:2.6' @@ -90,8 +91,8 @@ dependencies { implementation 'org.apache.commons:commons-collections4:4.1' // for tests - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test:rules:1.0.2' + androidTestImplementation 'androidx.test:runner:1.1.0' + androidTestImplementation 'androidx.test:rules:1.1.0' androidTestImplementation 'junit:junit:4.12' androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.11.0' diff --git a/app/src/androidTest/java/at/bitfire/davdroid/CustomTlsSocketFactoryTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/CustomTlsSocketFactoryTest.kt index 30d9b6441..1704d06b7 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/CustomTlsSocketFactoryTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/CustomTlsSocketFactoryTest.kt @@ -8,7 +8,7 @@ package at.bitfire.davdroid -import android.support.test.InstrumentationRegistry.getInstrumentation +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import at.bitfire.cert4android.CustomCertManager import okhttp3.OkHttpClient import okhttp3.Request @@ -54,7 +54,7 @@ class CustomTlsSocketFactoryTest { @Test fun testSendClientCertificate() { var public: X509Certificate? = null - javaClass.classLoader.getResourceAsStream("sample.crt").use { + javaClass.classLoader!!.getResourceAsStream("sample.crt").use { public = CertificateFactory.getInstance("X509").generateCertificate(it) as? X509Certificate } assertNotNull(public) diff --git a/app/src/androidTest/java/at/bitfire/davdroid/model/CollectionInfoTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/model/CollectionInfoTest.kt index 481d156f5..68a2505b9 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/model/CollectionInfoTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/model/CollectionInfoTest.kt @@ -9,7 +9,7 @@ package at.bitfire.davdroid.model import android.content.ContentValues -import android.support.test.filters.SmallTest +import androidx.test.filters.SmallTest import at.bitfire.dav4android.DavResource import at.bitfire.dav4android.property.ResourceType import at.bitfire.davdroid.HttpClient diff --git a/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt index bc986bb61..3c7a37dd4 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt @@ -8,8 +8,7 @@ package at.bitfire.davdroid.settings -import android.support.test.InstrumentationRegistry -import android.support.test.InstrumentationRegistry.getTargetContext +import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.App import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -23,8 +22,7 @@ class SettingsTest { @Before fun init() { - InstrumentationRegistry.getContext().isRestricted - settings = Settings.getInstance(getTargetContext())!! + settings = Settings.getInstance(InstrumentationRegistry.getInstrumentation().targetContext)!! } @After diff --git a/app/src/androidTest/java/at/bitfire/davdroid/ui/setup/DavResourceFinderTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/ui/setup/DavResourceFinderTest.kt index 9a911950b..e978a9240 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/ui/setup/DavResourceFinderTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/ui/setup/DavResourceFinderTest.kt @@ -8,8 +8,8 @@ package at.bitfire.davdroid.ui.setup -import android.support.test.InstrumentationRegistry.getTargetContext -import android.support.test.filters.SmallTest +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.dav4android.DavResource import at.bitfire.dav4android.property.AddressbookHomeSet import at.bitfire.dav4android.property.ResourceType @@ -52,7 +52,7 @@ class DavResourceFinderTest { server.start() loginInfo = LoginInfo(URI.create("/"), Credentials("mock", "12345")) - finder = DavResourceFinder(getTargetContext(), loginInfo) + finder = DavResourceFinder(InstrumentationRegistry.getInstrumentation().targetContext, loginInfo) client = HttpClient.Builder() .addAuthentication(null, loginInfo.credentials) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2dbdfb981..918503ca3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -106,7 +106,7 @@ android:label="@string/debug_info_title"> diff --git a/app/src/main/java/at/bitfire/davdroid/App.kt b/app/src/main/java/at/bitfire/davdroid/App.kt index da9185dba..55a3f81e8 100644 --- a/app/src/main/java/at/bitfire/davdroid/App.kt +++ b/app/src/main/java/at/bitfire/davdroid/App.kt @@ -17,7 +17,7 @@ import android.graphics.drawable.BitmapDrawable import android.net.Uri import android.os.Build import android.os.StrictMode -import android.support.v7.app.AppCompatDelegate +import androidx.appcompat.app.AppCompatDelegate import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.ui.NotificationUtils import kotlin.concurrent.thread diff --git a/app/src/main/java/at/bitfire/davdroid/DavService.kt b/app/src/main/java/at/bitfire/davdroid/DavService.kt index aa93d829f..bd5bb4fa5 100644 --- a/app/src/main/java/at/bitfire/davdroid/DavService.kt +++ b/app/src/main/java/at/bitfire/davdroid/DavService.kt @@ -18,8 +18,8 @@ import android.database.DatabaseUtils import android.database.sqlite.SQLiteDatabase import android.os.Binder import android.os.Bundle -import android.support.v4.app.NotificationCompat -import android.support.v4.app.NotificationManagerCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat import at.bitfire.dav4android.DavResource import at.bitfire.dav4android.Response import at.bitfire.dav4android.UrlUtils diff --git a/app/src/main/java/at/bitfire/davdroid/log/Logger.kt b/app/src/main/java/at/bitfire/davdroid/log/Logger.kt index ed280e877..b09a7a9df 100644 --- a/app/src/main/java/at/bitfire/davdroid/log/Logger.kt +++ b/app/src/main/java/at/bitfire/davdroid/log/Logger.kt @@ -14,8 +14,8 @@ import android.content.Intent import android.content.SharedPreferences import android.os.Process import android.preference.PreferenceManager -import android.support.v4.app.NotificationCompat -import android.support.v4.app.NotificationManagerCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat import android.util.Log import at.bitfire.davdroid.R import at.bitfire.davdroid.ui.AppSettingsActivity diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt index 2694ee5b2..d7a47bd11 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt @@ -15,7 +15,7 @@ import android.database.DatabaseUtils import android.os.Build import android.os.Bundle import android.provider.ContactsContract -import android.support.v4.content.ContextCompat +import androidx.core.content.ContextCompat import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.CollectionInfo diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt index 54409f403..405ad3cd2 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt @@ -13,7 +13,7 @@ import android.content.* import android.os.Build import android.os.Bundle import android.provider.ContactsContract.Groups -import android.support.v4.app.NotificationCompat +import androidx.core.app.NotificationCompat import at.bitfire.dav4android.DavAddressBook import at.bitfire.dav4android.DavResource import at.bitfire.dav4android.DavResponseCallback diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt index 5c72c7a71..b099a1166 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt @@ -18,9 +18,9 @@ import android.net.ConnectivityManager import android.net.wifi.WifiManager import android.os.Build import android.os.Bundle -import android.support.v4.app.NotificationCompat -import android.support.v4.app.NotificationManagerCompat -import android.support.v4.content.ContextCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt index b18c29761..b13f80544 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt @@ -16,8 +16,8 @@ import android.os.Bundle import android.os.RemoteException import android.provider.CalendarContract import android.provider.ContactsContract -import android.support.v4.app.NotificationCompat -import android.support.v4.app.NotificationManagerCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat import at.bitfire.dav4android.* import at.bitfire.dav4android.exception.* import at.bitfire.dav4android.property.GetCTag diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt index 6a2c3d351..b8fa587d3 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt @@ -15,8 +15,8 @@ import android.database.DatabaseUtils import android.graphics.drawable.BitmapDrawable import android.net.Uri import android.os.Bundle -import android.support.v4.app.NotificationCompat -import android.support.v4.app.NotificationManagerCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AboutActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AboutActivity.kt index 2ea61897b..ed265ba97 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AboutActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AboutActivity.kt @@ -10,16 +10,16 @@ package at.bitfire.davdroid.ui import android.content.Context import android.os.Bundle -import android.support.v4.app.Fragment -import android.support.v4.app.FragmentManager -import android.support.v4.app.FragmentPagerAdapter -import android.support.v4.app.LoaderManager -import android.support.v4.content.AsyncTaskLoader -import android.support.v4.content.Loader -import android.support.v7.app.AppCompatActivity import android.text.Html import android.text.Spanned import android.view.* +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentPagerAdapter +import androidx.loader.app.LoaderManager +import androidx.loader.content.AsyncTaskLoader +import androidx.loader.content.Loader import at.bitfire.davdroid.App import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R @@ -70,11 +70,11 @@ class AboutActivity: AppCompatActivity() { override fun getCount() = 2 - override fun getPageTitle(position: Int) = + override fun getPageTitle(position: Int): String = when (position) { 1 -> getString(R.string.about_libraries) else -> getString(R.string.app_name) - }!! + } override fun getItem(position: Int) = when (position) { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt index ffc49f88e..10f1c61bc 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt @@ -13,7 +13,6 @@ import android.accounts.Account import android.accounts.AccountManager import android.annotation.SuppressLint import android.app.Dialog -import android.app.LoaderManager import android.content.* import android.content.pm.PackageManager import android.database.DatabaseUtils @@ -25,15 +24,17 @@ import android.os.Bundle import android.os.IBinder import android.provider.CalendarContract import android.provider.ContactsContract -import android.support.design.widget.Snackbar -import android.support.v4.app.ActivityCompat -import android.support.v4.app.DialogFragment -import android.support.v4.content.ContextCompat -import android.support.v7.app.AlertDialog -import android.support.v7.app.AppCompatActivity -import android.support.v7.widget.Toolbar import android.view.* import android.widget.* +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.fragment.app.DialogFragment +import androidx.loader.app.LoaderManager +import androidx.loader.content.AsyncTaskLoader +import androidx.loader.content.Loader import at.bitfire.davdroid.DavService import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger @@ -44,6 +45,7 @@ import at.bitfire.davdroid.model.ServiceDB.Collections import at.bitfire.davdroid.resource.LocalAddressBook import at.bitfire.davdroid.resource.LocalTaskList import at.bitfire.ical4android.TaskProvider +import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.account_caldav_item.view.* import kotlinx.android.synthetic.main.activity_account.* import java.lang.ref.WeakReference @@ -107,7 +109,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop webcal_menu.setOnMenuItemClickListener(this) // load CardDAV/CalDAV collections - loaderManager.initLoader(0, null, this) + LoaderManager.getInstance(this).initLoader(0, null, this) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { @@ -355,7 +357,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop AccountLoader(this, account) fun reload() { - loaderManager.restartLoader(0, null, this) + LoaderManager.getInstance(this).restartLoader(0, null, this) } override fun onLoadFinished(loader: Loader, info: AccountInfo?) { @@ -582,7 +584,10 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop } } } finally { - provider.release() + if (Build.VERSION.SDK_INT >= 24) + provider.close() + else + provider.release() } } @@ -599,7 +604,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop ): ArrayAdapter(context, R.layout.account_carddav_item) { override fun getView(position: Int, v: View?, parent: ViewGroup?): View { val v = v ?: LayoutInflater.from(context).inflate(R.layout.account_carddav_item, parent, false) - val info = getItem(position) + val info = getItem(position)!! val checked: CheckBox = v.findViewById(R.id.checked) checked.isChecked = info.selected @@ -703,7 +708,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val oldAccount: Account = arguments!!.getParcelable(ARG_ACCOUNT) + val oldAccount: Account = arguments!!.getParcelable(ARG_ACCOUNT)!! val editText = EditText(activity) editText.setText(oldAccount.name) diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt index afd7f7dcd..659175066 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt @@ -11,12 +11,8 @@ package at.bitfire.davdroid.ui import android.accounts.Account import android.accounts.AccountManager import android.accounts.OnAccountsUpdateListener -import android.app.ListFragment -import android.app.LoaderManager -import android.content.AsyncTaskLoader import android.content.Context import android.content.Intent -import android.content.Loader import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -24,20 +20,24 @@ import android.view.ViewGroup import android.widget.AbsListView import android.widget.AdapterView import android.widget.ArrayAdapter +import androidx.fragment.app.ListFragment +import androidx.loader.app.LoaderManager +import androidx.loader.content.AsyncTaskLoader +import androidx.loader.content.Loader import at.bitfire.davdroid.R import kotlinx.android.synthetic.main.account_list_item.view.* class AccountListFragment: ListFragment(), LoaderManager.LoaderCallbacks> { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - listAdapter = AccountListAdapter(activity) + listAdapter = AccountListAdapter(requireActivity()) return inflater.inflate(R.layout.account_list, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - loaderManager.initLoader(0, arguments, this) + LoaderManager.getInstance(this).initLoader(0, arguments, this) listView.choiceMode = AbsListView.CHOICE_MODE_SINGLE listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> @@ -52,7 +52,7 @@ class AccountListFragment: ListFragment(), LoaderManager.LoaderCallbacks>, accounts: Array) { val adapter = listAdapter as AccountListAdapter @@ -102,7 +102,7 @@ class AccountListFragment: ListFragment(), LoaderManager.LoaderCallbacks(context, R.layout.account_list_item) { override fun getView(position: Int, v: View?, parent: ViewGroup?): View { - val account = getItem(position) + val account = getItem(position)!! val v = v ?: LayoutInflater.from(context).inflate(R.layout.account_list_item, parent, false) v.account_name.text = account.name diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt index 945c978a0..5edcd6605 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt @@ -21,16 +21,16 @@ import android.os.Handler import android.os.Looper import android.provider.CalendarContract import android.security.KeyChain -import android.support.v4.app.ActivityCompat -import android.support.v4.app.DialogFragment -import android.support.v4.app.LoaderManager -import android.support.v4.app.NavUtils -import android.support.v4.content.ContextCompat -import android.support.v4.content.Loader -import android.support.v7.app.AlertDialog -import android.support.v7.app.AppCompatActivity -import android.support.v7.preference.* import android.view.MenuItem +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.app.NavUtils +import androidx.core.content.ContextCompat +import androidx.fragment.app.DialogFragment +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader +import androidx.preference.* import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.InvalidAccountException import at.bitfire.davdroid.R @@ -81,8 +81,8 @@ class AccountSettingsActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - account = arguments!!.getParcelable(EXTRA_ACCOUNT) - loaderManager.initLoader(0, arguments, this) + account = arguments!!.getParcelable(EXTRA_ACCOUNT)!! + LoaderManager.getInstance(this).initLoader(0, arguments, this) } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -108,14 +108,14 @@ class AccountSettingsActivity: AppCompatActivity() { prefUserName.text = credentials.userName prefUserName.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.credentials(Credentials(newValue as String, credentials.password)) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } prefPassword.isVisible = true prefPassword.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.credentials(Credentials(credentials.userName, newValue as String)) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } @@ -131,7 +131,7 @@ class AccountSettingsActivity: AppCompatActivity() { KeyChain.choosePrivateKeyAlias(activity, { alias -> accountSettings.credentials(Credentials(certificateAlias = alias)) Handler(Looper.getMainLooper()).post { - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) } }, null, null, null, -1, credentials.certificateAlias) true @@ -155,7 +155,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.summary = getString(R.string.settings_sync_summary_periodically, syncIntervalContacts / 60) it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setSyncInterval(getString(R.string.address_books_authority), (newValue as String).toLong()) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } } else @@ -172,7 +172,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.summary = getString(R.string.settings_sync_summary_periodically, syncIntervalCalendars / 60) it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setSyncInterval(CalendarContract.AUTHORITY, (newValue as String).toLong()) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } } else @@ -189,7 +189,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.summary = getString(R.string.settings_sync_summary_periodically, syncIntervalTasks / 60) it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, (newValue as String).toLong()) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } } else @@ -201,7 +201,7 @@ class AccountSettingsActivity: AppCompatActivity() { prefWifiOnly.isChecked = accountSettings.getSyncWifiOnly() prefWifiOnly.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, wifiOnly -> accountSettings.setSyncWiFiOnly(wifiOnly as Boolean) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } @@ -214,7 +214,7 @@ class AccountSettingsActivity: AppCompatActivity() { prefWifiOnlySSIDs.setSummary(R.string.settings_sync_wifi_only_ssids_off) prefWifiOnlySSIDs.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setSyncWifiOnlySSIDs((newValue as String).split(',').mapNotNull { StringUtils.trimToNull(it) }.distinct()) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } @@ -243,7 +243,7 @@ class AccountSettingsActivity: AppCompatActivity() { .setPositiveButton(android.R.string.ok) { _, _ -> // change group method accountSettings.setGroupMethod(GroupMethod.valueOf(groupMethod as String)) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) // reload all contacts val args = Bundle(1) @@ -295,7 +295,7 @@ class AccountSettingsActivity: AppCompatActivity() { } } - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } } else @@ -309,7 +309,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.isChecked = accountSettings.getManageCalendarColors() it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setManageCalendarColors(newValue as Boolean) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) false } } else @@ -324,7 +324,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> if (newValue as Boolean) { accountSettings.setEventColors(true) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) } else AlertDialog.Builder(requireActivity()) .setIcon(R.drawable.ic_error_dark) @@ -333,7 +333,7 @@ class AccountSettingsActivity: AppCompatActivity() { .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(android.R.string.ok) { _, _ -> accountSettings.setEventColors(false) - loaderManager.restartLoader(0, arguments, this) + LoaderManager.getInstance(this).restartLoader(0, arguments, this) } .show() false diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt index 57014dc3c..c31921097 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt @@ -14,18 +14,18 @@ import android.content.Context import android.content.Intent import android.content.SyncStatusObserver import android.os.Bundle -import android.support.design.widget.NavigationView -import android.support.design.widget.Snackbar -import android.support.v4.app.LoaderManager -import android.support.v4.content.Loader -import android.support.v4.view.GravityCompat -import android.support.v7.app.ActionBarDrawerToggle -import android.support.v7.app.AppCompatActivity import android.view.MenuItem +import androidx.appcompat.app.ActionBarDrawerToggle +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.GravityCompat +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader import at.bitfire.davdroid.App import at.bitfire.davdroid.R import at.bitfire.davdroid.settings.ISettings import at.bitfire.davdroid.ui.setup.LoginActivity +import com.google.android.material.navigation.NavigationView +import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.accounts_content.* import kotlinx.android.synthetic.main.activity_accounts.* @@ -65,7 +65,7 @@ class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSele startService(settingsIntent) val args = Bundle(1) - supportLoaderManager.initLoader(0, args, this) + LoaderManager.getInstance(this).initLoader(0, args, this) } override fun onCreateLoader(code: Int, args: Bundle?) = diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt index 214bf38b2..887adfe5b 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt @@ -16,12 +16,11 @@ import android.content.ServiceConnection import android.os.Bundle import android.os.IBinder import android.os.Process -import android.support.design.widget.Snackbar -import android.support.v7.app.AppCompatActivity -import android.support.v7.preference.EditTextPreference -import android.support.v7.preference.Preference -import android.support.v7.preference.PreferenceFragmentCompat -import android.support.v7.preference.SwitchPreferenceCompat +import androidx.appcompat.app.AppCompatActivity +import androidx.preference.EditTextPreference +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreferenceCompat import at.bitfire.cert4android.CustomCertManager import at.bitfire.davdroid.App import at.bitfire.davdroid.BuildConfig @@ -30,6 +29,7 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.ISettings import at.bitfire.davdroid.settings.ISettingsObserver import at.bitfire.davdroid.settings.Settings +import com.google.android.material.snackbar.Snackbar import java.net.URI import java.net.URISyntaxException diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CollectionInfoFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/CollectionInfoFragment.kt index e241e95f6..e87d7bdc7 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CollectionInfoFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/CollectionInfoFragment.kt @@ -11,8 +11,8 @@ package at.bitfire.davdroid.ui import android.annotation.SuppressLint import android.app.Dialog import android.os.Bundle -import android.support.v4.app.DialogFragment -import android.support.v7.app.AlertDialog +import androidx.fragment.app.DialogFragment +import androidx.appcompat.app.AlertDialog import at.bitfire.davdroid.R import at.bitfire.davdroid.model.CollectionInfo import kotlinx.android.synthetic.main.collection_properties.view.* diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CreateAddressBookActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/CreateAddressBookActivity.kt index 422219594..c03aeaf57 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CreateAddressBookActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/CreateAddressBookActivity.kt @@ -9,17 +9,17 @@ package at.bitfire.davdroid.ui import android.accounts.Account -import android.app.LoaderManager -import android.content.AsyncTaskLoader import android.content.Context import android.content.Intent -import android.content.Loader import android.os.Bundle -import android.support.v4.app.NavUtils -import android.support.v7.app.AppCompatActivity import android.view.Menu import android.view.MenuItem import android.widget.ArrayAdapter +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.NavUtils +import androidx.loader.app.LoaderManager +import androidx.loader.content.AsyncTaskLoader +import androidx.loader.content.Loader import at.bitfire.davdroid.R import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.ServiceDB @@ -45,7 +45,7 @@ class CreateAddressBookActivity: AppCompatActivity(), LoaderManager.LoaderCallba supportActionBar?.setDisplayHomeAsUpEnabled(true) setContentView(R.layout.activity_create_address_book) - loaderManager.initLoader(0, intent.extras, this) + LoaderManager.getInstance(this).initLoader(0, intent.extras, this) } override fun onCreateOptionsMenu(menu: Menu): Boolean { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.kt index c1c25a093..3bbb611fd 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.kt @@ -9,18 +9,18 @@ package at.bitfire.davdroid.ui import android.accounts.Account -import android.app.LoaderManager -import android.content.AsyncTaskLoader import android.content.Context import android.content.Intent -import android.content.Loader import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.support.v4.app.NavUtils -import android.support.v7.app.AppCompatActivity +import androidx.core.app.NavUtils +import androidx.appcompat.app.AppCompatActivity import android.view.Menu import android.view.MenuItem import android.widget.ArrayAdapter +import androidx.loader.app.LoaderManager +import androidx.loader.content.AsyncTaskLoader +import androidx.loader.content.Loader import at.bitfire.davdroid.R import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.ServiceDB @@ -43,7 +43,7 @@ class CreateCalendarActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - account = intent.extras.getParcelable(EXTRA_ACCOUNT) + account = intent.extras.getParcelable(EXTRA_ACCOUNT)!! supportActionBar?.setDisplayHomeAsUpEnabled(true) @@ -56,7 +56,7 @@ class CreateCalendarActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks }).show() } - loaderManager.initLoader(0, null, this) + LoaderManager.getInstance(this).initLoader(0, null, this) } override fun onCreateOptionsMenu(menu: Menu): Boolean { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt index fde9dd610..eb6610929 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt @@ -13,10 +13,10 @@ import android.app.Dialog import android.app.ProgressDialog import android.content.Context import android.os.Bundle -import android.support.v4.app.DialogFragment -import android.support.v4.app.LoaderManager -import android.support.v4.content.AsyncTaskLoader -import android.support.v4.content.Loader +import androidx.fragment.app.DialogFragment +import androidx.loader.app.LoaderManager +import androidx.loader.content.AsyncTaskLoader +import androidx.loader.content.Loader import at.bitfire.dav4android.DavResource import at.bitfire.dav4android.XmlUtils import at.bitfire.davdroid.AccountSettings @@ -57,10 +57,10 @@ class CreateCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks< super.onCreate(savedInstanceState) val args = requireNotNull(arguments) - account = args.getParcelable(ARG_ACCOUNT) - info = args.getParcelable(ARG_COLLECTION_INFO) + account = args.getParcelable(ARG_ACCOUNT)!! + info = args.getParcelable(ARG_COLLECTION_INFO)!! - loaderManager.initLoader(0, null, this) + LoaderManager.getInstance(this).initLoader(0, null, this) } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt index ce9d2219c..cfb157aff 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt @@ -21,15 +21,15 @@ import android.os.Bundle import android.os.PowerManager import android.provider.CalendarContract import android.provider.ContactsContract -import android.support.v4.app.LoaderManager -import android.support.v4.content.AsyncTaskLoader -import android.support.v4.content.ContextCompat -import android.support.v4.content.FileProvider -import android.support.v4.content.Loader -import android.support.v7.app.AppCompatActivity import android.util.Log import android.view.Menu import android.view.MenuItem +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider +import androidx.loader.app.LoaderManager +import androidx.loader.content.AsyncTaskLoader +import androidx.loader.content.Loader import at.bitfire.dav4android.exception.HttpException import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.BuildConfig @@ -66,7 +66,7 @@ class DebugInfoActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks AlertDialog.Builder(activity) diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt index 16c784235..8f41e521f 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt @@ -20,13 +20,12 @@ import android.database.sqlite.SQLiteDatabase import android.os.AsyncTask import android.os.Bundle import android.provider.CalendarContract -import android.support.design.widget.Snackbar -import android.support.v4.app.Fragment -import android.support.v4.app.LoaderManager -import android.support.v4.content.Loader import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader import at.bitfire.davdroid.* import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.ServiceDB.* @@ -36,6 +35,7 @@ import at.bitfire.davdroid.ui.SettingsLoader import at.bitfire.davdroid.ui.setup.AccountDetailsFragment.CreateSettings import at.bitfire.ical4android.TaskProvider import at.bitfire.vcard4android.GroupMethod +import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.login_account_details.* import kotlinx.android.synthetic.main.login_account_details.view.* import java.lang.ref.WeakReference @@ -99,7 +99,7 @@ class AccountDetailsFragment: Fragment(), LoaderManager.LoaderCallbacks(0)?.cancelLoad() + LoaderManager.getInstance(this).getLoader(0)?.cancelLoad() } override fun onCreateLoader(id: Int, args: Bundle?) = - ServerConfigurationLoader(requireActivity(), args!!.getParcelable(ARG_LOGIN_CREDENTIALS)) + ServerConfigurationLoader(requireActivity(), args!!.getParcelable(ARG_LOGIN_CREDENTIALS)!!) override fun onLoadFinished(loader: Loader, data: Configuration?) { data?.let { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginActivity.kt index 84d2e5ba9..8cbbcaa70 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginActivity.kt @@ -9,7 +9,7 @@ package at.bitfire.davdroid.ui.setup import android.os.Bundle -import android.support.v7.app.AppCompatActivity +import androidx.appcompat.app.AppCompatActivity import android.view.Menu import android.view.MenuItem import at.bitfire.davdroid.App diff --git a/app/src/main/java/at/bitfire/davdroid/ui/widget/IntEditTextPreference.kt b/app/src/main/java/at/bitfire/davdroid/ui/widget/IntEditTextPreference.kt index 20862e97f..4e0908e57 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/widget/IntEditTextPreference.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/widget/IntEditTextPreference.kt @@ -9,7 +9,7 @@ package at.bitfire.davdroid.ui.widget import android.content.Context -import android.support.v7.preference.EditTextPreference +import androidx.preference.EditTextPreference import android.util.AttributeSet class IntEditTextPreference( diff --git a/app/src/main/res/layout/account_list_item.xml b/app/src/main/res/layout/account_list_item.xml index 0558dcb1a..c8c058545 100644 --- a/app/src/main/res/layout/account_list_item.xml +++ b/app/src/main/res/layout/account_list_item.xml @@ -16,7 +16,7 @@ android:gravity="center_horizontal" android:descendantFocusability="blocksDescendants"> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/accounts_content.xml b/app/src/main/res/layout/accounts_content.xml index 62546ae84..753d5abca 100644 --- a/app/src/main/res/layout/accounts_content.xml +++ b/app/src/main/res/layout/accounts_content.xml @@ -7,7 +7,7 @@ ~ http://www.gnu.org/licenses/gpl.html --> - - - - + - - + diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index d8fc57fb9..751631d4e 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -7,23 +7,23 @@ ~ http://www.gnu.org/licenses/gpl.html --> - + - - - - + - - + diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index 5d2ed1701..0f6bc632a 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -32,7 +32,7 @@ android:visibility="gone" android:text="@string/account_select_collections_hint"/> - - - + - - - + - - - + diff --git a/app/src/main/res/layout/activity_accounts.xml b/app/src/main/res/layout/activity_accounts.xml index 8d000c4cb..977ecaf9f 100644 --- a/app/src/main/res/layout/activity_accounts.xml +++ b/app/src/main/res/layout/activity_accounts.xml @@ -7,7 +7,7 @@ ~ http://www.gnu.org/licenses/gpl.html --> - - - + diff --git a/app/src/main/res/layout/login_credentials_fragment.xml b/app/src/main/res/layout/login_credentials_fragment.xml index ef4637aa1..30fa2aad7 100644 --- a/app/src/main/res/layout/login_credentials_fragment.xml +++ b/app/src/main/res/layout/login_credentials_fragment.xml @@ -51,28 +51,28 @@ android:paddingBottom="16dp" android:orientation="vertical"> - - - - + - - + - - - - + - - - + - - + - - - + Date: Thu, 29 Nov 2018 23:23:19 +0100 Subject: [PATCH 002/382] Update to okhttp 3.12.0 --- app/build.gradle | 4 ++-- app/src/main/java/at/bitfire/davdroid/HttpClient.kt | 2 +- dav4android | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0ca7e8f50..678811b5c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,8 +94,8 @@ dependencies { androidTestImplementation 'androidx.test:runner:1.1.0' androidTestImplementation 'androidx.test:rules:1.1.0' androidTestImplementation 'junit:junit:4.12' - androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.11.0' + androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.0' testImplementation 'junit:junit:4.12' - testImplementation 'com.squareup.okhttp3:mockwebserver:3.11.0' + testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.0' } diff --git a/app/src/main/java/at/bitfire/davdroid/HttpClient.kt b/app/src/main/java/at/bitfire/davdroid/HttpClient.kt index 99d555696..aca14b97e 100644 --- a/app/src/main/java/at/bitfire/davdroid/HttpClient.kt +++ b/app/src/main/java/at/bitfire/davdroid/HttpClient.kt @@ -58,7 +58,7 @@ class HttpClient private constructor( // add User-Agent to every request .addNetworkInterceptor(UserAgentInterceptor) - .build()!! + .build() } override fun close() { diff --git a/dav4android b/dav4android index 626287e7d..f76364207 160000 --- a/dav4android +++ b/dav4android @@ -1 +1 @@ -Subproject commit 626287e7d7d31b293ecabacb6420d511a9b814fd +Subproject commit f76364207c07a017cbfb8d658d850b68f0d6f6da -- GitLab From f5b11945997c70bc1fabffd5df018c2c086105ed Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Thu, 29 Nov 2018 23:25:22 +0100 Subject: [PATCH 003/382] Version bump to 2.0.6-beta1 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 678811b5c..333329ea3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,7 +18,7 @@ android { defaultConfig { applicationId "at.bitfire.davdroid" - versionCode 248 + versionCode 249 buildConfigField "long", "buildTime", System.currentTimeMillis() + "L" buildConfigField "boolean", "customCerts", "true" @@ -32,7 +32,7 @@ android { flavorDimensions "type" productFlavors { standard { - versionName "2.0.5-ose" + versionName "2.0.6-beta1-ose" buildConfigField "boolean", "customCerts", "true" } -- GitLab From e454fa398a3485cc6ff1eb5b4d2a787d8998330f Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Thu, 29 Nov 2018 23:37:42 +0100 Subject: [PATCH 004/382] Update ProGuard rules --- app/proguard-rules.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt index 2b5c8fcda..c8a97e0d8 100644 --- a/app/proguard-rules.txt +++ b/app/proguard-rules.txt @@ -32,9 +32,9 @@ -keep class org.threeten.bp.** { *; } # keep ThreeTen (for time zone processing) # okhttp +-dontwarn javax.annotation.** -dontwarn okio.** --dontwarn javax.annotation.Nullable --dontwarn javax.annotation.ParametersAreNonnullByDefault +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement -dontwarn org.conscrypt.** # dnsjava -- GitLab From 8b43677d013a713ed4407f2d0ed45035018d07fc Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Fri, 30 Nov 2018 11:31:30 +0100 Subject: [PATCH 005/382] Don't do emulator checks (because we can only use shared runners at the moment) --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a5c8b103a..c9cffdd9c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,12 +7,12 @@ before_script: cache: paths: - .gradle/ - - apk/ test: script: - - (cd /sdk/emulator; ./emulator @test -no-audio -no-window & wait-for-emulator.sh) - - ./gradlew check mergeAndroidReports +# - (cd /sdk/emulator; ./emulator @test -no-audio -no-window & wait-for-emulator.sh) +# - ./gradlew check mergeAndroidReports + - ./gradlew check artifacts: paths: - app/build/outputs/lint-results-debug.html -- GitLab From 8bdf03bfc57c8bca502d80d58b29b11145da69dd Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Fri, 30 Nov 2018 13:12:50 +0100 Subject: [PATCH 006/382] Minor changes (lint/remove warnings) --- app/build.gradle | 9 ++--- .../davdroid/CustomTlsSocketFactoryTest.kt | 2 +- .../davdroid/settings/DefaultsProviderTest.kt | 4 +- .../bitfire/davdroid/settings/SettingsTest.kt | 4 +- .../at/bitfire/davdroid/AccountSettings.kt | 11 ++++-- app/src/main/java/at/bitfire/davdroid/App.kt | 1 + .../davdroid/CustomTlsSocketFactory.kt | 22 +++++------ .../java/at/bitfire/davdroid/DavService.kt | 33 ++++++++-------- .../java/at/bitfire/davdroid/HttpClient.kt | 2 +- .../bitfire/davdroid/model/CollectionInfo.kt | 2 +- .../at/bitfire/davdroid/model/ServiceDB.kt | 1 + .../davdroid/resource/LocalAddressBook.kt | 16 ++++---- .../davdroid/resource/LocalCalendar.kt | 2 +- .../at/bitfire/davdroid/resource/LocalTask.kt | 1 - .../davdroid/resource/LocalTaskList.kt | 3 ++ .../AddressBooksSyncAdapterService.kt | 1 + .../syncadapter/CalendarSyncManager.kt | 4 +- .../syncadapter/ContactsSyncManager.kt | 26 +++++-------- .../davdroid/syncadapter/SyncManager.kt | 6 +-- .../at/bitfire/davdroid/ui/AboutActivity.kt | 2 +- .../at/bitfire/davdroid/ui/AccountActivity.kt | 27 ++++++++----- .../davdroid/ui/AccountListFragment.kt | 4 +- .../davdroid/ui/AccountSettingsActivity.kt | 7 +++- .../bitfire/davdroid/ui/AccountsActivity.kt | 5 ++- .../davdroid/ui/AppSettingsActivity.kt | 28 +++++++------- .../davdroid/ui/CreateCollectionFragment.kt | 2 +- .../davdroid/ui/DeleteCollectionFragment.kt | 2 +- .../at/bitfire/davdroid/ui/SettingsLoader.kt | 5 ++- .../davdroid/ui/StartupDialogFragment.kt | 3 +- .../ui/setup/AccountDetailsFragment.kt | 38 ++++++++++--------- .../davdroid/ui/setup/DavResourceFinder.kt | 10 ++--- .../setup/DefaultLoginCredentialsFragment.kt | 2 +- .../ui/setup/DetectConfigurationFragment.kt | 2 +- cert4android | 2 +- dav4android | 2 +- ical4android | 2 +- vcard4android | 2 +- 37 files changed, 158 insertions(+), 137 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 333329ea3..181ef1c0d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,11 +29,10 @@ android { vectorDrawables.useSupportLibrary = true } - flavorDimensions "type" + flavorDimensions "distribution" productFlavors { standard { versionName "2.0.6-beta1-ose" - buildConfigField "boolean", "customCerts", "true" } } @@ -84,11 +83,11 @@ dependencies { implementation 'com.github.yukuku:ambilwarna:2.0.1' implementation 'com.mikepenz:aboutlibraries:6.2.0' - implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' + implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0' implementation 'commons-io:commons-io:2.6' implementation 'dnsjava:dnsjava:2.1.8' - implementation 'org.apache.commons:commons-lang3:3.7' - implementation 'org.apache.commons:commons-collections4:4.1' + implementation 'org.apache.commons:commons-lang3:3.8.1' + implementation 'org.apache.commons:commons-collections4:4.2' // for tests androidTestImplementation 'androidx.test:runner:1.1.0' diff --git a/app/src/androidTest/java/at/bitfire/davdroid/CustomTlsSocketFactoryTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/CustomTlsSocketFactoryTest.kt index 1704d06b7..ba1fe4d6f 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/CustomTlsSocketFactoryTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/CustomTlsSocketFactoryTest.kt @@ -120,7 +120,7 @@ class CustomTlsSocketFactoryTest { private fun readResource(name: String): ByteArray { - this.javaClass.classLoader.getResourceAsStream(name).use { + javaClass.classLoader!!.getResourceAsStream(name).use { return IOUtils.toByteArray(it) } } diff --git a/app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsProviderTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsProviderTest.kt index 96081ba9d..a8c2dce10 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsProviderTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsProviderTest.kt @@ -9,8 +9,8 @@ package at.bitfire.davdroid.settings import at.bitfire.davdroid.App -import junit.framework.Assert.assertEquals -import junit.framework.Assert.assertFalse +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Test class DefaultsProviderTest { diff --git a/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt index 3c7a37dd4..391a2cb39 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt @@ -10,9 +10,9 @@ package at.bitfire.davdroid.settings import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.App -import junit.framework.Assert.assertFalse -import junit.framework.Assert.assertTrue import org.junit.After +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test diff --git a/app/src/main/java/at/bitfire/davdroid/AccountSettings.kt b/app/src/main/java/at/bitfire/davdroid/AccountSettings.kt index ab254f6b6..33d62b17d 100644 --- a/app/src/main/java/at/bitfire/davdroid/AccountSettings.kt +++ b/app/src/main/java/at/bitfire/davdroid/AccountSettings.kt @@ -240,6 +240,7 @@ class AccountSettings( } @Suppress("unused") + @SuppressLint("Recycle") /** * There is a mistake in this method. [TaskContract.Tasks.SYNC_VERSION] is used to store the * SEQUENCE and should not be used for the eTag. @@ -251,7 +252,7 @@ class AccountSettings( provider.client.query(TaskProvider.syncAdapterUri(provider.tasksUri(), account), arrayOf(TaskContract.Tasks._ID, TaskContract.Tasks.SYNC1, TaskContract.Tasks.SYNC2), "${TaskContract.Tasks.ACCOUNT_TYPE}=? AND ${TaskContract.Tasks.ACCOUNT_NAME}=?", - arrayOf(account.type, account.name), null).use { cursor -> + arrayOf(account.type, account.name), null)!!.use { cursor -> while (cursor.moveToNext()) { val id = cursor.getLong(0) val eTag = cursor.getString(1) @@ -271,12 +272,14 @@ class AccountSettings( } @Suppress("unused") + @SuppressLint("Recycle") private fun update_6_7() { // add calendar colors context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)?.let { provider -> try { AndroidCalendar.insertColors(provider, account) } finally { + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= 24) provider.close() else @@ -291,7 +294,7 @@ class AccountSettings( } @Suppress("unused") - @SuppressLint("ParcelClassLoader") + @SuppressLint("Recycle", "ParcelClassLoader") private fun update_5_6() { context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)?.let { provider -> val parcel = Parcel.obtain() @@ -308,7 +311,7 @@ class AccountSettings( else { parcel.unmarshall(raw, 0, raw.size) parcel.setDataPosition(0) - val params = parcel.readBundle() + val params = parcel.readBundle()!! val url = params.getString("url")?.let { HttpUrl.parse(it) } if (url == null) Logger.log.info("No address book URL, ignoring account") @@ -343,6 +346,7 @@ class AccountSettings( throw ContactsStorageException("Couldn't migrate contacts to new address book", e) } finally { parcel.recycle() + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= 24) provider.close() else @@ -371,6 +375,7 @@ class AccountSettings( } @Suppress("unused") + @SuppressLint("Recycle") private fun update_2_3() { // Don't show a warning for Android updates anymore accountManager.setUserData(account, "last_android_version", null) diff --git a/app/src/main/java/at/bitfire/davdroid/App.kt b/app/src/main/java/at/bitfire/davdroid/App.kt index 55a3f81e8..7aa8cff02 100644 --- a/app/src/main/java/at/bitfire/davdroid/App.kt +++ b/app/src/main/java/at/bitfire/davdroid/App.kt @@ -22,6 +22,7 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.ui.NotificationUtils import kotlin.concurrent.thread +@Suppress("unused") class App: Application() { companion object { diff --git a/app/src/main/java/at/bitfire/davdroid/CustomTlsSocketFactory.kt b/app/src/main/java/at/bitfire/davdroid/CustomTlsSocketFactory.kt index 9ade6eed1..10cb774a9 100644 --- a/app/src/main/java/at/bitfire/davdroid/CustomTlsSocketFactory.kt +++ b/app/src/main/java/at/bitfire/davdroid/CustomTlsSocketFactory.kt @@ -50,11 +50,11 @@ class CustomTlsSocketFactory( /* set reasonable protocol versions */ // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0) // - remove all SSL versions (especially SSLv3) because they're insecure now - val _protocols = LinkedList() + val whichProtocols = LinkedList() for (protocol in socket.supportedProtocols.filterNot { it.contains("SSL", true) }) - _protocols += protocol - Logger.log.info("Enabling (only) these TLS protocols: ${_protocols.joinToString(", ")}") - protocols = _protocols.toTypedArray() + whichProtocols += protocol + Logger.log.info("Enabling (only) these TLS protocols: ${whichProtocols.joinToString(", ")}") + protocols = whichProtocols.toTypedArray() /* set up reasonable cipher suites */ val knownCiphers = arrayOf( @@ -86,16 +86,16 @@ class CustomTlsSocketFactory( * ciphers should be a server-side task */ // for the final set of enabled ciphers, take the ciphers enabled by default, ... - val _cipherSuites = LinkedList() - _cipherSuites.addAll(socket.enabledCipherSuites) - Logger.log.fine("Cipher suites enabled by default: ${_cipherSuites.joinToString(", ")}") + val whichCiphers = LinkedList() + whichCiphers.addAll(socket.enabledCipherSuites) + Logger.log.fine("Cipher suites enabled by default: ${whichCiphers.joinToString(", ")}") // ... add explicitly allowed ciphers ... - _cipherSuites.addAll(knownCiphers) + whichCiphers.addAll(knownCiphers) // ... and keep only those which are actually available - _cipherSuites.retainAll(availableCiphers) + whichCiphers.retainAll(availableCiphers) - Logger.log.info("Enabling (only) these TLS ciphers: " + _cipherSuites.joinToString(", ")) - cipherSuites = _cipherSuites.toTypedArray() + Logger.log.info("Enabling (only) these TLS ciphers: " + whichCiphers.joinToString(", ")) + cipherSuites = whichCiphers.toTypedArray() } catch (e: IOException) { Logger.log.severe("Couldn't determine default TLS settings") } diff --git a/app/src/main/java/at/bitfire/davdroid/DavService.kt b/app/src/main/java/at/bitfire/davdroid/DavService.kt index bd5bb4fa5..9fb122c41 100644 --- a/app/src/main/java/at/bitfire/davdroid/DavService.kt +++ b/app/src/main/java/at/bitfire/davdroid/DavService.kt @@ -66,14 +66,17 @@ class DavService: Service() { ACTION_REFRESH_COLLECTIONS -> if (runningRefresh.add(id)) { thread { refreshCollections(id) } - refreshingStatusListeners.forEach { it.get()?.onDavRefreshStatusChanged(id, true) } + refreshingStatusListeners.forEach { listener -> + listener.get()?.onDavRefreshStatusChanged(id, true) + } } ACTION_FORCE_SYNC -> { - val authority = intent.data.authority + val uri = intent.data!! + val authority = uri.authority!! val account = Account( - intent.data.pathSegments[1], - intent.data.pathSegments[0] + uri.pathSegments[1], + uri.pathSegments[0] ) forceSync(authority, account) } @@ -194,16 +197,16 @@ class DavService: Service() { dav[CalendarProxyReadFor::class.java]?.let { for (href in it.hrefs) { Logger.log.fine("Principal is a read-only proxy for $href, checking for home sets") - root.resolve(href)?.let { - related += it + root.resolve(href)?.let { proxyReadFor -> + related += proxyReadFor } } } dav[CalendarProxyWriteFor::class.java]?.let { for (href in it.hrefs) { Logger.log.fine("Principal is a read/write proxy for $href, checking for home sets") - root.resolve(href)?.let { - related += it + root.resolve(href)?.let { proxyWriteFor -> + related += proxyWriteFor } } } @@ -212,8 +215,8 @@ class DavService: Service() { dav[GroupMembership::class.java]?.let { for (href in it.hrefs) { Logger.log.fine("Principal is member of group $href, checking for home sets") - root.resolve(href)?.let { - related += it + root.resolve(href)?.let { groupMembership -> + related += groupMembership } } } @@ -341,13 +344,13 @@ class DavService: Service() { if (!response.isSuccess()) return@propfind - val info = CollectionInfo(response) - info.confirmed = true + val collectionInfo = CollectionInfo(response) + collectionInfo.confirmed = true // remove unusable collections - if ((serviceType == Services.SERVICE_CARDDAV && info.type != CollectionInfo.Type.ADDRESS_BOOK) || - (serviceType == Services.SERVICE_CALDAV && !arrayOf(CollectionInfo.Type.CALENDAR, CollectionInfo.Type.WEBCAL).contains(info.type)) || - (info.type == CollectionInfo.Type.WEBCAL && info.source == null)) + if ((serviceType == Services.SERVICE_CARDDAV && collectionInfo.type != CollectionInfo.Type.ADDRESS_BOOK) || + (serviceType == Services.SERVICE_CALDAV && !arrayOf(CollectionInfo.Type.CALENDAR, CollectionInfo.Type.WEBCAL).contains(collectionInfo.type)) || + (collectionInfo.type == CollectionInfo.Type.WEBCAL && collectionInfo.source == null)) itCollections.remove() } } catch(e: HttpException) { diff --git a/app/src/main/java/at/bitfire/davdroid/HttpClient.kt b/app/src/main/java/at/bitfire/davdroid/HttpClient.kt index aca14b97e..927bfbf18 100644 --- a/app/src/main/java/at/bitfire/davdroid/HttpClient.kt +++ b/app/src/main/java/at/bitfire/davdroid/HttpClient.kt @@ -124,7 +124,7 @@ class HttpClient private constructor( fun withDiskCache(): Builder { val context = context ?: throw IllegalArgumentException("Context is required to find the cache directory") - for (dir in arrayOf(context.externalCacheDir, context.cacheDir)) { + for (dir in arrayOf(context.externalCacheDir, context.cacheDir).filterNotNull()) { if (dir.exists() && dir.canWrite()) { val cacheDir = File(dir, "HttpClient") cacheDir.mkdir() diff --git a/app/src/main/java/at/bitfire/davdroid/model/CollectionInfo.kt b/app/src/main/java/at/bitfire/davdroid/model/CollectionInfo.kt index eaa942f63..c452c165a 100644 --- a/app/src/main/java/at/bitfire/davdroid/model/CollectionInfo.kt +++ b/app/src/main/java/at/bitfire/davdroid/model/CollectionInfo.kt @@ -218,7 +218,7 @@ data class CollectionInfo( } return CollectionInfo( - HttpUrl.parse(parcel.readString())!!, + HttpUrl.parse(parcel.readString()!!)!!, readOrNull(parcel) { parcel.readLong() }, readOrNull(parcel) { parcel.readLong() }, diff --git a/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt b/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt index c7422363d..0c4abbe2a 100644 --- a/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt +++ b/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt @@ -19,6 +19,7 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.ui.StartupDialogFragment import java.util.logging.Level +@Suppress("ObjectPropertyName") class ServiceDB { object Services { diff --git a/app/src/main/java/at/bitfire/davdroid/resource/LocalAddressBook.kt b/app/src/main/java/at/bitfire/davdroid/resource/LocalAddressBook.kt index 5cfa048d8..171fc3a5e 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/LocalAddressBook.kt +++ b/app/src/main/java/at/bitfire/davdroid/resource/LocalAddressBook.kt @@ -78,7 +78,12 @@ class LocalAddressBook( baos.write(info.url.hashCode()) val hash = Base64.encodeToString(baos.toByteArray(), Base64.NO_WRAP or Base64.NO_PADDING) - val sb = StringBuilder(if (info.displayName.isNullOrEmpty()) DavUtils.lastSegmentOfUrl(info.url) else info.displayName) + val sb = StringBuilder(info.displayName.let { + if (it.isNullOrEmpty()) + DavUtils.lastSegmentOfUrl(info.url) + else + it + }) sb.append(" (${mainAccount.name} $hash)") return sb.toString() } @@ -200,6 +205,7 @@ class LocalAddressBook( fun delete() { val accountManager = AccountManager.get(context) + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= 22) accountManager.removeAccount(account, null, null, null) else @@ -244,14 +250,6 @@ class LocalAddressBook( fun findDirtyContacts() = queryContacts("${RawContacts.DIRTY}!=0", null) fun findDirtyGroups() = queryGroups("${Groups.DIRTY}!=0", null) - private fun queryContactsGroups(whereContacts: String?, whereArgsContacts: Array?, whereGroups: String?, whereArgsGroups: Array?): List { - val contacts = queryContacts(whereContacts, whereArgsContacts) - return if (includeGroups) - contacts + queryGroups(whereGroups, whereArgsGroups) - else - contacts - } - /** * Queries all contacts with DIRTY flag and checks whether their data checksum has changed, i.e. diff --git a/app/src/main/java/at/bitfire/davdroid/resource/LocalCalendar.kt b/app/src/main/java/at/bitfire/davdroid/resource/LocalCalendar.kt index 1ff5dc600..183a9a143 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/LocalCalendar.kt +++ b/app/src/main/java/at/bitfire/davdroid/resource/LocalCalendar.kt @@ -88,7 +88,7 @@ class LocalCalendar private constructor( get() = displayName ?: id.toString() override var lastSyncState: SyncState? - get() = provider.query(calendarSyncURI(), arrayOf(COLUMN_SYNC_STATE), null, null, null)?.let { cursor -> + get() = provider.query(calendarSyncURI(), arrayOf(COLUMN_SYNC_STATE), null, null, null)?.use { cursor -> if (cursor.moveToNext()) return SyncState.fromString(cursor.getString(0)) else diff --git a/app/src/main/java/at/bitfire/davdroid/resource/LocalTask.kt b/app/src/main/java/at/bitfire/davdroid/resource/LocalTask.kt index 638b9d0ec..75c3cc09d 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/LocalTask.kt +++ b/app/src/main/java/at/bitfire/davdroid/resource/LocalTask.kt @@ -50,7 +50,6 @@ class LocalTask: AndroidTask, LocalResource { override fun buildTask(builder: ContentProviderOperation.Builder, update: Boolean) { super.buildTask(builder, update) - val task = requireNotNull(task) builder .withValue(Tasks._SYNC_ID, fileName) .withValue(COLUMN_ETAG, eTag) diff --git a/app/src/main/java/at/bitfire/davdroid/resource/LocalTaskList.kt b/app/src/main/java/at/bitfire/davdroid/resource/LocalTaskList.kt index 49278134e..a3d027610 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/LocalTaskList.kt +++ b/app/src/main/java/at/bitfire/davdroid/resource/LocalTaskList.kt @@ -9,6 +9,7 @@ package at.bitfire.davdroid.resource import android.accounts.Account +import android.annotation.SuppressLint import android.content.ContentProviderClient import android.content.ContentResolver import android.content.ContentValues @@ -53,6 +54,7 @@ class LocalTaskList private constructor( return create(account, provider, values) } + @SuppressLint("Recycle") @Throws(Exception::class) fun onRenameAccount(resolver: ContentResolver, oldName: String, newName: String) { var client: ContentProviderClient? = null @@ -64,6 +66,7 @@ class LocalTaskList private constructor( it.update(Tasks.getContentUri(TaskProvider.ProviderName.OpenTasks.authority), values, "${Tasks.ACCOUNT_NAME}=?", arrayOf(oldName)) } } finally { + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= 24) client?.close() else diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt index d7a47bd11..2dadeb03b 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt @@ -145,6 +145,7 @@ class AddressBooksSyncAdapterService : SyncAdapterService() { LocalAddressBook.create(context, contactsProvider, account, info) } } finally { + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= 24) contactsProvider?.close() else diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt index aa56d43f5..bdaf6a438 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt @@ -65,8 +65,8 @@ class CalendarSyncManager( var syncState: SyncState? = null it.propfind(0, SupportedReportSet.NAME, GetCTag.NAME, SyncToken.NAME) { response, relation -> if (relation == Response.HrefRelation.SELF) { - response[SupportedReportSet::class.java]?.let { - hasCollectionSync = it.reports.contains(SupportedReportSet.SYNC_COLLECTION) + response[SupportedReportSet::class.java]?.let { supported -> + hasCollectionSync = supported.reports.contains(SupportedReportSet.SYNC_COLLECTION) } syncState = syncState(response) diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt index 405ad3cd2..210941b07 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt @@ -130,12 +130,12 @@ class ContactsSyncManager( var syncState: SyncState? = null it.propfind(0, SupportedAddressData.NAME, SupportedReportSet.NAME, GetCTag.NAME, SyncToken.NAME) { response, relation -> if (relation == Response.HrefRelation.SELF) { - response[SupportedAddressData::class.java]?.let { - hasVCard4 = it.hasVCard4() + response[SupportedAddressData::class.java]?.let { supported -> + hasVCard4 = supported.hasVCard4() } - response[SupportedReportSet::class.java]?.let { - hasCollectionSync = it.reports.contains(SupportedReportSet.SYNC_COLLECTION) + response[SupportedReportSet::class.java]?.let { supported -> + hasCollectionSync = supported.reports.contains(SupportedReportSet.SYNC_COLLECTION) } syncState = syncState(response) @@ -397,15 +397,15 @@ class ContactsSyncManager( if (local == null) { if (newData.group) { Logger.log.log(Level.INFO, "Creating local group", newData) - useLocal(LocalGroup(localCollection, newData, fileName, eTag, LocalResource.FLAG_REMOTELY_PRESENT)) { - it.add() - local = it + useLocal(LocalGroup(localCollection, newData, fileName, eTag, LocalResource.FLAG_REMOTELY_PRESENT)) { group -> + group.add() + local = group } } else { Logger.log.log(Level.INFO, "Creating local contact", newData) - useLocal(LocalContact(localCollection, newData, fileName, eTag, LocalResource.FLAG_REMOTELY_PRESENT)) { - it.add() - local = it + useLocal(LocalContact(localCollection, newData, fileName, eTag, LocalResource.FLAG_REMOTELY_PRESENT)) { contact -> + contact.add() + local = contact } } syncResult.stats.numInserts++ @@ -447,12 +447,6 @@ class ContactsSyncManager( return null } - val host = httpUrl.host() - if (host == null) { - Logger.log.log(Level.SEVERE, "External resource URL doesn't specify a host name", url) - return null - } - // authenticate only against a certain host, and only upon request val builder = HttpClient.Builder(context, baseUrl.host(), accountSettings.credentials()) diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt index b13f80544..64944efb7 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt @@ -312,9 +312,9 @@ abstract class SyncManager, out CollectionType: L val body = prepareUpload(local) var eTag: String? = null - val processETag: (response: okhttp3.Response) -> Unit = { - it.header("ETag")?.let { - eTag = GetETag(it).eTag + val processETag: (response: okhttp3.Response) -> Unit = { response -> + response.header("ETag")?.let { getETag -> + eTag = GetETag(getETag).eTag } } try { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AboutActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AboutActivity.kt index ed265ba97..84921d537 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AboutActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AboutActivity.kt @@ -102,7 +102,7 @@ class AboutActivity: AppCompatActivity() { if (true /* open-source version */) { warranty.text = Html.fromHtml(getString(R.string.about_license_info_no_warranty)) - loaderManager.initLoader(0, null, this) + LoaderManager.getInstance(this).initLoader(0, null, this) } } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt index 10f1c61bc..3fbe07054 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt @@ -195,7 +195,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop val list = parent as ListView val adapter = list.adapter as ArrayAdapter - val info = adapter.getItem(position) + val info = adapter.getItem(position)!! val nowChecked = !info.selected SelectCollectionTask(applicationContext, info, nowChecked, WeakReference(adapter), WeakReference(view)).execute() @@ -269,7 +269,11 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop provider.delete(CalendarContract.Calendars.CONTENT_URI, "${CalendarContract.Calendars.NAME}=?", arrayOf(info.source)) reload() } finally { - provider.release() + @Suppress("DEPRECATION") + if (Build.VERSION.SDK_INT >= 24) + provider.close() + else + provider.release() } } } @@ -464,7 +468,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop // bind to DavService to get notified when it's running if (davServiceConn == null) { - davServiceConn = object: ServiceConnection { + val serviceConn = object: ServiceConnection { override fun onServiceConnected(name: ComponentName, service: IBinder) { // get notified when DavService is running davService = service as DavService.InfoBinder @@ -477,7 +481,8 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop davService = null } } - context.bindService(Intent(context, DavService::class.java), davServiceConn, Context.BIND_AUTO_CREATE) + if (context.bindService(Intent(context, DavService::class.java), serviceConn, Context.BIND_AUTO_CREATE)) + davServiceConn = serviceConn } else forceLoad() } @@ -559,6 +564,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop return false } + @SuppressLint("Recycle") private fun readCollections(db: SQLiteDatabase, service: Long): List { val collections = LinkedList() db.query(Collections._TABLE, null, Collections.SERVICE_ID + "=?", arrayOf(service.toString()), @@ -584,6 +590,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop } } } finally { + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= 24) provider.close() else @@ -602,8 +609,8 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop class AddressBookAdapter( context: Context ): ArrayAdapter(context, R.layout.account_carddav_item) { - override fun getView(position: Int, v: View?, parent: ViewGroup?): View { - val v = v ?: LayoutInflater.from(context).inflate(R.layout.account_carddav_item, parent, false) + override fun getView(position: Int, _v: View?, parent: ViewGroup?): View { + val v = _v ?: LayoutInflater.from(context).inflate(R.layout.account_carddav_item, parent, false) val info = getItem(position)!! val checked: CheckBox = v.findViewById(R.id.checked) @@ -637,9 +644,9 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop class CalendarAdapter( context: Context ): ArrayAdapter(context, R.layout.account_caldav_item) { - override fun getView(position: Int, v: View?, parent: ViewGroup?): View { - val v = v ?: LayoutInflater.from(context).inflate(R.layout.account_caldav_item, parent, false) - val info = getItem(position) + override fun getView(position: Int, _v: View?, parent: ViewGroup?): View { + val v = _v ?: LayoutInflater.from(context).inflate(R.layout.account_caldav_item, parent, false) + val info = getItem(position)!! val enabled = info.selected || info.supportsVEVENT || info.supportsVTODO v.isEnabled = enabled @@ -707,6 +714,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop } + @SuppressLint("Recycle") override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val oldAccount: Account = arguments!!.getParcelable(ARG_ACCOUNT)!! @@ -748,6 +756,7 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop if (oldAccount == addressBook.mainAccount) addressBook.mainAccount = Account(newName, oldAccount.type) } finally { + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= 24) provider.close() else diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt index 659175066..671560a9d 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountListFragment.kt @@ -101,10 +101,10 @@ class AccountListFragment: ListFragment(), LoaderManager.LoaderCallbacks(context, R.layout.account_list_item) { - override fun getView(position: Int, v: View?, parent: ViewGroup?): View { + override fun getView(position: Int, _v: View?, parent: ViewGroup?): View { val account = getItem(position)!! - val v = v ?: LayoutInflater.from(context).inflate(R.layout.account_list_item, parent, false) + val v = _v ?: LayoutInflater.from(context).inflate(R.layout.account_list_item, parent, false) v.account_name.text = account.name return v diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt index 5edcd6605..084bdcfc9 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt @@ -10,6 +10,7 @@ package at.bitfire.davdroid.ui import android.Manifest import android.accounts.Account +import android.annotation.SuppressLint import android.content.ContentResolver import android.content.Context import android.content.Intent @@ -90,8 +91,9 @@ class AccountSettingsActivity: AppCompatActivity() { } override fun onCreateLoader(id: Int, args: Bundle?) = - AccountSettingsLoader(requireActivity(), args!!.getParcelable(EXTRA_ACCOUNT)) + AccountSettingsLoader(requireActivity(), args!!.getParcelable(EXTRA_ACCOUNT)!!) + @SuppressLint("Recycle") override fun onLoadFinished(loader: Loader>, result: Pair?) { val (settings, accountSettings) = result ?: return @@ -128,7 +130,7 @@ class AccountSettingsActivity: AppCompatActivity() { prefCertAlias.isVisible = true prefCertAlias.summary = credentials.certificateAlias prefCertAlias.setOnPreferenceClickListener { - KeyChain.choosePrivateKeyAlias(activity, { alias -> + KeyChain.choosePrivateKeyAlias(requireActivity(), { alias -> accountSettings.credentials(Credentials(certificateAlias = alias)) Handler(Looper.getMainLooper()).post { LoaderManager.getInstance(this).restartLoader(0, arguments, this) @@ -287,6 +289,7 @@ class AccountSettingsActivity: AppCompatActivity() { calendar.lastSyncState = null } } finally { + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= 24) provider.close() else diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt index c31921097..76614de43 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt @@ -53,7 +53,7 @@ class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSele val toggle = ActionBarDrawerToggle( this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) - drawer_layout.setDrawerListener(toggle) + drawer_layout.addDrawerListener(toggle) toggle.syncState() nav_view.setNavigationItemSelectedListener(this) @@ -72,7 +72,8 @@ class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSele SettingsLoader(this) override fun onLoadFinished(loader: Loader, result: Settings?) { - val result = result ?: return + if (result == null) + return if (supportFragmentManager.findFragmentByTag(fragTagStartup) == null) { val ft = supportFragmentManager.beginTransaction() diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt index 887adfe5b..fef5fdef4 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt @@ -61,23 +61,25 @@ class AppSettingsActivity: AppCompatActivity() { } var settings: ISettings? = null - var settingsSvc: ServiceConnection? = object: ServiceConnection { - override fun onServiceConnected(name: ComponentName, binder: IBinder) { - settings = ISettings.Stub.asInterface(binder) - settings?.registerObserver(observer) - loadSettings() - } - override fun onServiceDisconnected(name: ComponentName) { - settings?.unregisterObserver(observer) - settings = null - } - } + var settingsSvc: ServiceConnection? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (!activity!!.bindService(Intent(activity, Settings::class.java), settingsSvc, Context.BIND_AUTO_CREATE)) - settingsSvc = null + val serviceConn = object: ServiceConnection { + override fun onServiceConnected(name: ComponentName, binder: IBinder) { + settings = ISettings.Stub.asInterface(binder) + settings?.registerObserver(observer) + loadSettings() + } + + override fun onServiceDisconnected(name: ComponentName) { + settings?.unregisterObserver(observer) + settings = null + } + } + if (activity!!.bindService(Intent(activity, Settings::class.java), serviceConn, Context.BIND_AUTO_CREATE)) + settingsSvc = serviceConn } override fun onDestroy() { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt index eb6610929..537cbce61 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt @@ -31,7 +31,6 @@ import java.io.IOException import java.io.StringWriter import java.util.logging.Level -@Suppress("DEPRECATION") class CreateCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks { companion object { @@ -63,6 +62,7 @@ class CreateCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks< LoaderManager.getInstance(this).initLoader(0, null, this) } + @Suppress("DEPRECATION") override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val progress = ProgressDialog(context) progress.setTitle(R.string.create_collection_creating) diff --git a/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.kt index e1625c287..2638b2345 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.kt @@ -26,7 +26,6 @@ import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.ServiceDB import at.bitfire.davdroid.settings.Settings -@Suppress("DEPRECATION") class DeleteCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks { companion object { @@ -46,6 +45,7 @@ class DeleteCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks< LoaderManager.getInstance(this).initLoader(0, null, this) } + @Suppress("DEPRECATION") override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val progress = ProgressDialog(context) progress.setTitle(R.string.delete_collection_deleting_collection) diff --git a/app/src/main/java/at/bitfire/davdroid/ui/SettingsLoader.kt b/app/src/main/java/at/bitfire/davdroid/ui/SettingsLoader.kt index 99dd94f48..c412b7751 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/SettingsLoader.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/SettingsLoader.kt @@ -39,7 +39,7 @@ abstract class SettingsLoader( if (settingsSvc != null) forceLoad() else { - settingsSvc = object: ServiceConnection { + val serviceConn = object: ServiceConnection { override fun onServiceConnected(name: ComponentName?, binder: IBinder) { settings = ISettings.Stub.asInterface(binder) settings!!.registerObserver(settingsObserver) @@ -51,7 +51,8 @@ abstract class SettingsLoader( settings = null } } - context.bindService(Intent(context, Settings::class.java), settingsSvc, Context.BIND_AUTO_CREATE) + if (context.bindService(Intent(context, Settings::class.java), serviceConn, Context.BIND_AUTO_CREATE)) + settingsSvc = serviceConn } } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt index dc378b4eb..1e1bdc8d7 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt @@ -29,7 +29,6 @@ import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.resource.LocalTaskList import at.bitfire.davdroid.settings.ISettings -import org.apache.commons.lang3.text.WordUtils import java.util.* import java.util.logging.Level @@ -130,7 +129,7 @@ class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks UiUtils.launchUri(requireActivity(), App.homepageUrl(requireActivity()).buildUpon() .appendPath("faq").appendEncodedPath("synchronization-is-not-run-as-expected/").build()) diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt index 8f41e521f..93b1a1eab 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt @@ -136,13 +136,13 @@ class AccountDetailsFragment: Fragment(), LoaderManager.LoaderCallbacks, - val settings: ISettings, + private val applicationContext: Context, + private val activityRef: WeakReference, + private val settings: ISettings, - val accountName: String, - val config: DavResourceFinder.Configuration, - val groupMethod: GroupMethod + private val accountName: String, + private val config: DavResourceFinder.Configuration, + private val groupMethod: GroupMethod ): AsyncTask() { override fun doInBackground(vararg params: Void?): Boolean { @@ -226,25 +226,27 @@ class AccountDetailsFragment: Fragment(), LoaderManager.LoaderCallbacks(size) (1..size).forEach { - val url = HttpUrl.parse(source.readString())!! - map[url] = source.readParcelable(Thread.currentThread().contextClassLoader) + val url = HttpUrl.parse(source.readString()!!)!! + map[url] = source.readParcelable(Thread.currentThread().contextClassLoader)!! } return map } @@ -494,7 +494,7 @@ class DavResourceFinder( else ServiceInfo( source.readString()?.let { HttpUrl.parse(it) }, - (1..source.readInt()).map { HttpUrl.parse(source.readString())!! }.toMutableSet(), + (1..source.readInt()).map { HttpUrl.parse(source.readString()!!)!! }.toMutableSet(), readCollections() ) } @@ -503,7 +503,7 @@ class DavResourceFinder( source.readSerializable() as Credentials, readServiceInfo(), readServiceInfo(), - source.readString() + source.readString()!! ) } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/DefaultLoginCredentialsFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/DefaultLoginCredentialsFragment.kt index 313ebf96f..724a7a192 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/DefaultLoginCredentialsFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/DefaultLoginCredentialsFragment.kt @@ -53,7 +53,7 @@ class DefaultLoginCredentialsFragment: Fragment(), CompoundButton.OnCheckedChang } v.urlcert_select_cert.setOnClickListener { - KeyChain.choosePrivateKeyAlias(activity, { alias -> + KeyChain.choosePrivateKeyAlias(requireActivity(), { alias -> Handler(Looper.getMainLooper()).post { v.urlcert_cert_alias.text = alias v.urlcert_cert_alias.error = null diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/DetectConfigurationFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/DetectConfigurationFragment.kt index da655cc1c..f471e292d 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/DetectConfigurationFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/DetectConfigurationFragment.kt @@ -25,7 +25,6 @@ import at.bitfire.davdroid.ui.DebugInfoActivity import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration import java.lang.ref.WeakReference -@Suppress("DEPRECATION") class DetectConfigurationFragment: DialogFragment(), LoaderManager.LoaderCallbacks { companion object { @@ -41,6 +40,7 @@ class DetectConfigurationFragment: DialogFragment(), LoaderManager.LoaderCallbac } + @Suppress("DEPRECATION") override fun onCreateDialog(savedInstancebState: Bundle?): Dialog { val progress = ProgressDialog(activity) progress.setTitle(R.string.login_configuration_detection) diff --git a/cert4android b/cert4android index 4fac9b14b..8a9b0acd4 160000 --- a/cert4android +++ b/cert4android @@ -1 +1 @@ -Subproject commit 4fac9b14bc4e35fd317bcb75ae5e7f39c22b3c75 +Subproject commit 8a9b0acd4bbfb6240d4bf625afab214655fb7a8e diff --git a/dav4android b/dav4android index f76364207..cad85fee5 160000 --- a/dav4android +++ b/dav4android @@ -1 +1 @@ -Subproject commit f76364207c07a017cbfb8d658d850b68f0d6f6da +Subproject commit cad85fee5d0d3649df3e1cbc6a03c4a1645560d8 diff --git a/ical4android b/ical4android index 913105c5f..3450b0d40 160000 --- a/ical4android +++ b/ical4android @@ -1 +1 @@ -Subproject commit 913105c5f33528b64b34ad4273050d149c750234 +Subproject commit 3450b0d40ce2abb2ab6303571ec96bec750750ce diff --git a/vcard4android b/vcard4android index c13002d43..b63883b21 160000 --- a/vcard4android +++ b/vcard4android @@ -1 +1 @@ -Subproject commit c13002d43c0912e231352a9be034a4b19cdabcf9 +Subproject commit b63883b21c0680b3b41bc4d067ca2985c58a15c8 -- GitLab From 4bbb2b8419544571a9293913262fc965c25c03a3 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sat, 1 Dec 2018 22:00:57 +0100 Subject: [PATCH 007/382] Ignore non-successful multiget responses --- .../at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt | 5 +++++ .../at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt | 5 +++++ .../java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt index bdaf6a438..9fa15544e 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt @@ -131,6 +131,11 @@ class CalendarSyncManager( useRemoteCollection { it.multiget(bunch) { response, _ -> useRemote(response) { + if (!response.isSuccess()) { + Logger.log.warning("Received non-successful multiget response for ${response.href}") + return@useRemote + } + val eTag = response[GetETag::class.java]?.eTag ?: throw DavException("Received multi-get response without ETag") diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt index 210941b07..a074ba7c7 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt @@ -317,6 +317,11 @@ class ContactsSyncManager( useRemoteCollection { it.multiget(bunch, hasVCard4) { response, _ -> useRemote(response) { + if (!response.isSuccess()) { + Logger.log.warning("Received non-successful multiget response for ${response.href}") + return@useRemote + } + val eTag = response[GetETag::class.java]?.eTag ?: throw DavException("Received multi-get response without ETag") diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt index a55001c2b..d4e88f02f 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt @@ -112,6 +112,11 @@ class TasksSyncManager( useRemoteCollection { it.multiget(bunch) { response, _ -> useRemote(response) { + if (!response.isSuccess()) { + Logger.log.warning("Received non-successful multiget response for ${response.href}") + return@useRemote + } + val eTag = response[GetETag::class.java]?.eTag ?: throw DavException("Received multi-get response without ETag") -- GitLab From 67b1685d01dbea7e89fb2e938a3c5a0d7509219e Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Mon, 3 Dec 2018 11:17:06 +0100 Subject: [PATCH 008/382] Version bump to 2.0.6-beta2 --- app/build.gradle | 4 ++-- cert4android | 2 +- ical4android | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 181ef1c0d..00de0de3a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,7 +18,7 @@ android { defaultConfig { applicationId "at.bitfire.davdroid" - versionCode 249 + versionCode 250 buildConfigField "long", "buildTime", System.currentTimeMillis() + "L" buildConfigField "boolean", "customCerts", "true" @@ -32,7 +32,7 @@ android { flavorDimensions "distribution" productFlavors { standard { - versionName "2.0.6-beta1-ose" + versionName "2.0.6-beta2-ose" buildConfigField "boolean", "customCerts", "true" } } diff --git a/cert4android b/cert4android index 8a9b0acd4..960d33c71 160000 --- a/cert4android +++ b/cert4android @@ -1 +1 @@ -Subproject commit 8a9b0acd4bbfb6240d4bf625afab214655fb7a8e +Subproject commit 960d33c71956e96decb9699b589427989e501f51 diff --git a/ical4android b/ical4android index 3450b0d40..5b0cad59f 160000 --- a/ical4android +++ b/ical4android @@ -1 +1 @@ -Subproject commit 3450b0d40ce2abb2ab6303571ec96bec750750ce +Subproject commit 5b0cad59f2231b98185643e90edfa9af7ade53b4 -- GitLab From 6abbd019ad45028309d8d5023b6ef8cb26179eb8 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Wed, 5 Dec 2018 12:55:35 +0100 Subject: [PATCH 009/382] Version bump to 2.0.6 --- app/build.gradle | 4 ++-- app/proguard-rules.txt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 00de0de3a..98709a7ed 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,7 +18,7 @@ android { defaultConfig { applicationId "at.bitfire.davdroid" - versionCode 250 + versionCode 251 buildConfigField "long", "buildTime", System.currentTimeMillis() + "L" buildConfigField "boolean", "customCerts", "true" @@ -32,7 +32,7 @@ android { flavorDimensions "distribution" productFlavors { standard { - versionName "2.0.6-beta2-ose" + versionName "2.0.6-ose" buildConfigField "boolean", "customCerts", "true" } } diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt index c8a97e0d8..86cd96ccd 100644 --- a/app/proguard-rules.txt +++ b/app/proguard-rules.txt @@ -15,6 +15,9 @@ # Kotlin -dontwarn kotlin.** +# Apache Commons +-dontwarn javax.script.** + # ez-vcard -dontwarn ezvcard.io.json.** # JSON serializer (for jCards) not used -dontwarn freemarker.** # freemarker templating library (for creating hCards) not used -- GitLab From 72749addcd9842caba2a5042de3eb8f559997179 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sat, 8 Dec 2018 20:02:09 +0100 Subject: [PATCH 010/382] Settings: add some icons; rearrange account settings --- app/build.gradle | 2 +- app/src/main/res/drawable/ic_adb_dark.xml | 5 ++ .../main/res/drawable/ic_bug_report_dark.xml | 5 ++ .../main/res/drawable/ic_contacts_dark.xml | 5 ++ .../main/res/drawable/ic_date_range_dark.xml | 5 ++ app/src/main/res/drawable/ic_group_dark.xml | 5 ++ .../res/drawable/ic_network_wifi_dark.xml | 6 ++ app/src/main/res/xml/settings_account.xml | 72 ++++++++++--------- app/src/main/res/xml/settings_app.xml | 40 ++++++----- 9 files changed, 92 insertions(+), 53 deletions(-) create mode 100644 app/src/main/res/drawable/ic_adb_dark.xml create mode 100644 app/src/main/res/drawable/ic_bug_report_dark.xml create mode 100644 app/src/main/res/drawable/ic_contacts_dark.xml create mode 100644 app/src/main/res/drawable/ic_date_range_dark.xml create mode 100644 app/src/main/res/drawable/ic_group_dark.xml create mode 100644 app/src/main/res/drawable/ic_network_wifi_dark.xml diff --git a/app/build.gradle b/app/build.gradle index 98709a7ed..5e9b94f08 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.fragment:fragment:1.0.0' - implementation 'androidx.legacy:legacy-preference-v14:1.0.0' + implementation 'androidx.preference:preference:1.0.0' implementation 'com.google.android.material:material:1.0.0' implementation 'com.github.yukuku:ambilwarna:2.0.1' diff --git a/app/src/main/res/drawable/ic_adb_dark.xml b/app/src/main/res/drawable/ic_adb_dark.xml new file mode 100644 index 000000000..3be00f0a5 --- /dev/null +++ b/app/src/main/res/drawable/ic_adb_dark.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_bug_report_dark.xml b/app/src/main/res/drawable/ic_bug_report_dark.xml new file mode 100644 index 000000000..948a5be51 --- /dev/null +++ b/app/src/main/res/drawable/ic_bug_report_dark.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_contacts_dark.xml b/app/src/main/res/drawable/ic_contacts_dark.xml new file mode 100644 index 000000000..aee4a2c7c --- /dev/null +++ b/app/src/main/res/drawable/ic_contacts_dark.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_date_range_dark.xml b/app/src/main/res/drawable/ic_date_range_dark.xml new file mode 100644 index 000000000..ded97e98c --- /dev/null +++ b/app/src/main/res/drawable/ic_date_range_dark.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_group_dark.xml b/app/src/main/res/drawable/ic_group_dark.xml new file mode 100644 index 000000000..2a6a0c4eb --- /dev/null +++ b/app/src/main/res/drawable/ic_group_dark.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_network_wifi_dark.xml b/app/src/main/res/drawable/ic_network_wifi_dark.xml new file mode 100644 index 000000000..1ad040383 --- /dev/null +++ b/app/src/main/res/drawable/ic_network_wifi_dark.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/xml/settings_account.xml b/app/src/main/res/xml/settings_account.xml index b491f9abd..00cca5e27 100644 --- a/app/src/main/res/xml/settings_account.xml +++ b/app/src/main/res/xml/settings_account.xml @@ -7,30 +7,7 @@ ~ http://www.gnu.org/licenses/gpl.html --> - - - - - - - - - - - + @@ -47,6 +25,7 @@ android:key="sync_interval_calendars" android:persistent="false" android:title="@string/settings_sync_interval_calendars" + android:icon="@drawable/ic_today_dark" android:entries="@array/settings_sync_interval_names" android:entryValues="@array/settings_sync_interval_seconds"/> @@ -54,6 +33,7 @@ android:key="sync_interval_tasks" android:persistent="false" android:title="@string/settings_sync_interval_tasks" + android:icon="@drawable/ic_playlist_add_check_dark" android:entries="@array/settings_sync_interval_names" android:entryValues="@array/settings_sync_interval_seconds"/> @@ -61,6 +41,7 @@ android:key="sync_wifi_only" android:persistent="false" android:title="@string/settings_sync_wifi_only" + android:icon="@drawable/ic_network_wifi_dark" android:summaryOn="@string/settings_sync_wifi_only_on" android:summaryOff="@string/settings_sync_wifi_only_off" /> @@ -73,16 +54,26 @@ - + - + android:dialogTitle="@string/settings_enter_username"/> + + + + @@ -94,6 +85,7 @@ android:key="time_range_past_days" android:persistent="false" android:title="@string/settings_sync_time_range_past" + android:icon="@drawable/ic_date_range_dark" android:dialogMessage="@string/settings_sync_time_range_past_message" android:inputType="number"/> @@ -113,4 +105,18 @@ - + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_app.xml b/app/src/main/res/xml/settings_app.xml index 8fac176d3..5e195e31d 100644 --- a/app/src/main/res/xml/settings_app.xml +++ b/app/src/main/res/xml/settings_app.xml @@ -7,14 +7,25 @@ ~ http://www.gnu.org/licenses/gpl.html --> - + - + + + + android:title="@string/app_settings_show_debug_info" + android:summary="@string/app_settings_show_debug_info_details" + android:icon="@drawable/ic_bug_report_dark"> + + @@ -55,22 +66,13 @@ - - - + - - + android:key="reset_hints" + android:title="@string/app_settings_reset_hints" + android:summary="@string/app_settings_reset_hints_summary"/> - \ No newline at end of file + -- GitLab From bc5f1e935eac46d1e7cde69dbc8acc1e2053ad8f Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Fri, 21 Dec 2018 16:58:45 +0100 Subject: [PATCH 011/382] Ignore HTTP 403 when the resource upload fails because of missing permissions (server will win) --- app/build.gradle | 6 +++--- .../at/bitfire/davdroid/syncadapter/SyncManager.kt | 14 ++++++++++++-- build.gradle | 2 +- dav4android | 2 +- ical4android | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5e9b94f08..94f378521 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,7 +18,7 @@ android { defaultConfig { applicationId "at.bitfire.davdroid" - versionCode 251 + versionCode 252 buildConfigField "long", "buildTime", System.currentTimeMillis() + "L" buildConfigField "boolean", "customCerts", "true" @@ -90,8 +90,8 @@ dependencies { implementation 'org.apache.commons:commons-collections4:4.2' // for tests - androidTestImplementation 'androidx.test:runner:1.1.0' - androidTestImplementation 'androidx.test:rules:1.1.0' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test:rules:1.1.1' androidTestImplementation 'junit:junit:4.12' androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.0' diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt index 64944efb7..9308e1218 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt @@ -170,7 +170,7 @@ abstract class SyncManager, out CollectionType: L syncState = SyncState.fromSyncToken(result.first, initialSync) furtherChanges = result.second } catch(e: HttpException) { - if (e.errors.any { it.name == Property.Name(XmlUtils.NS_WEBDAV, "valid-sync-token") }) { + if (e.errors.contains(Error.VALID_SYNC_TOKEN)) { Logger.log.info("Sync token invalid, performing initial sync") initialSync = true resetPresentRemotely() @@ -326,10 +326,20 @@ abstract class SyncManager, out CollectionType: L 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) { - // we can't interact with the user to resolve the conflict, so we treat 409 like 412 + // 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) } diff --git a/build.gradle b/build.gradle index 524514671..0aebf9a8c 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { ext.dokka_version = '0.9.17' - ext.kotlin_version = '1.3.10' + ext.kotlin_version = '1.3.11' repositories { jcenter() diff --git a/dav4android b/dav4android index cad85fee5..2c7523be3 160000 --- a/dav4android +++ b/dav4android @@ -1 +1 @@ -Subproject commit cad85fee5d0d3649df3e1cbc6a03c4a1645560d8 +Subproject commit 2c7523be31985e683cedd342c48829dbee096c67 diff --git a/ical4android b/ical4android index 5b0cad59f..e04df0701 160000 --- a/ical4android +++ b/ical4android @@ -1 +1 @@ -Subproject commit 5b0cad59f2231b98185643e90edfa9af7ade53b4 +Subproject commit e04df070148adaf4ddc454a97350ffe46280ee2b -- GitLab From ea071bbd1ace4ff2e310cee1e0202083e8392f63 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Fri, 21 Dec 2018 18:02:34 +0100 Subject: [PATCH 012/382] Debug info: show details about DAVdroid, contact/calendar providers and contact/calendar/task apps --- app/build.gradle | 2 +- .../bitfire/davdroid/ui/DebugInfoActivity.kt | 52 ++++++++++++++----- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 94f378521..b248f2cd8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,7 +18,7 @@ android { defaultConfig { applicationId "at.bitfire.davdroid" - versionCode 252 + versionCode 253 buildConfigField "long", "buildTime", System.currentTimeMillis() + "L" buildConfigField "boolean", "customCerts", "true" diff --git a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt index cfb157aff..76f27d0de 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt @@ -12,8 +12,10 @@ import android.Manifest import android.accounts.Account import android.accounts.AccountManager import android.content.ContentResolver +import android.content.ContentUris import android.content.Context import android.content.Intent +import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.net.ConnectivityManager import android.os.Build @@ -27,6 +29,7 @@ import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.content.FileProvider +import androidx.core.content.pm.PackageInfoCompat import androidx.loader.app.LoaderManager import androidx.loader.content.AsyncTaskLoader import androidx.loader.content.Loader @@ -41,11 +44,10 @@ import at.bitfire.davdroid.resource.LocalAddressBook import at.bitfire.davdroid.settings.Settings import at.bitfire.ical4android.TaskProvider import kotlinx.android.synthetic.main.activity_debug_info.* +import org.dmfs.tasks.contract.TaskContract import java.io.File import java.io.FileWriter import java.io.IOException -import java.text.SimpleDateFormat -import java.util.* import java.util.logging.Level class DebugInfoActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks { @@ -182,25 +184,47 @@ class DebugInfoActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks + report.append(" from ").append(installer) + } + info.applicationInfo?.let { applicationInfo -> + if (!applicationInfo.enabled) + report.append(" disabled!") + if (applicationInfo.flags.and(ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) + report.append(" on external storage!") + } + report.append("\n") + } catch(e: PackageManager.NameNotFoundException) { + } } catch(e: Exception) { Logger.log.log(Level.SEVERE, "Couldn't get software information", e) } // connectivity - report.append("CONNECTIVITY (at the moment)\n") + report.append("\nCONNECTIVITY (at the moment)\n") val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager connectivityManager.activeNetworkInfo?.let { networkInfo -> val type = when (networkInfo.type) { -- GitLab From 21fdf2cebcfaee9a6ff791e6b8bc83a6b2fa7238 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sat, 22 Dec 2018 11:36:14 +0100 Subject: [PATCH 013/382] Version bump to 2.0.7, fix small NPE --- app/build.gradle | 4 ++-- app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b248f2cd8..755447e20 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,7 +18,7 @@ android { defaultConfig { applicationId "at.bitfire.davdroid" - versionCode 253 + versionCode 254 buildConfigField "long", "buildTime", System.currentTimeMillis() + "L" buildConfigField "boolean", "customCerts", "true" @@ -32,7 +32,7 @@ android { flavorDimensions "distribution" productFlavors { standard { - versionName "2.0.6-ose" + versionName "2.0.7-ose" buildConfigField "boolean", "customCerts", "true" } } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt index 3fbe07054..9006a31f7 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountActivity.kt @@ -488,7 +488,10 @@ class AccountActivity: AppCompatActivity(), Toolbar.OnMenuItemClickListener, Pop } override fun onReset() { - ContentResolver.removeStatusChangeListener(syncStatusListener) + syncStatusListener?.let { + ContentResolver.removeStatusChangeListener(it) + syncStatusListener = null + } davService?.removeRefreshingStatusListener(this) davServiceConn?.let { -- GitLab From bcd468f2e5a1025513810eadbf44d04be354920d Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sat, 22 Dec 2018 11:40:34 +0100 Subject: [PATCH 014/382] DAV service detection: cancel previous notification (MR #15 thanks @mbiebl) --- app/src/main/java/at/bitfire/davdroid/DavService.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/at/bitfire/davdroid/DavService.kt b/app/src/main/java/at/bitfire/davdroid/DavService.kt index 9fb122c41..1813d33af 100644 --- a/app/src/main/java/at/bitfire/davdroid/DavService.kt +++ b/app/src/main/java/at/bitfire/davdroid/DavService.kt @@ -289,6 +289,10 @@ class DavService: Service() { try { Logger.log.info("Refreshing $serviceType collections of service #$service") + // cancel previous notification + NotificationManagerCompat.from(this) + .cancel(service.toString(), NotificationUtils.NOTIFY_REFRESH_COLLECTIONS) + Settings.getInstance(this)?.use { settings -> // create authenticating OkHttpClient (credentials taken from account settings) HttpClient.Builder(this, settings, AccountSettings(this, settings, account)) -- GitLab From b863d355f60d945c37d4c765043dd4d1e5de4feb Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sat, 22 Dec 2018 11:44:32 +0100 Subject: [PATCH 015/382] Fetch translations from Transifex --- app/src/main/res/values-fr/strings.xml | 46 ++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 80c5aefd5..6c3e0fff7 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -8,19 +8,36 @@ Gestion des comptes patientez … Envoyer + Débogage + Autres messages importants + Syncronisation + Erreurs de synchronisation + Erreurs de réseau et d\'entrée / sortie + Messages d\'état + Synchronisation automatique + %s le matériel bloque souvent la synchronisation automatique. Dans ce cas, autorisez la synchronisation automatique dans vos paramètres d\'Android. + Synchronisation planifiée + Votre appareil va restreindre la synchronisation DAVdroid. Pour forcer des intervalles de synchronisation DAVdroid plus réguliers, enlever l\'option \"optimisation de batterie\" Désactiver pour DAVdroid Ne plus afficher + Pas maintenant Open-Source Information - Nous sommes heureux que vous utilisez DAVdroid, qui est un logiciel open-source (GPLv3). Parce que développer DAVdroid est un travail difficile et nous a pris de nombreuses heures, s\'il vous plaît envisager de faire un don. + Nous sommes heureux que vous utilisiez DAVdroid, qui est un logiciel open-source (GPLv3). Parce que développer DAVdroid est un travail difficile, qui nous a pris de nombreuses heures, nous vous invitons à faire un don. Faire un don Plus tard Erreur information Play Store DRM - Dans certaines conditions, Play Store DRM peut provoquer la disparition de tous les comptes DAVdroid après un redémarrage ou après la mise à niveau de DAVdroid. Si vous êtes concerné par ce problème (et seulement alors), s\'il vous plaît installer \"DAVdroid JB Solution\" du Play Store. + Dans certaines conditions, Play Store DRM peut provoquer la disparition de tous les comptes DAVdroid après un redémarrage ou après la mise à niveau de DAVdroid. Si vous êtes concerné par ce problème (et seulement dans ce cas), s\'il vous plaît installer \"DAVdroid JB Solution\" du Play Store. + Plus d\'informations L\'application OpenTasks n\'est pas installée + Pour synchroniser les tâches, l\'application gratuite OpenTasks est nécessaire (elle ne l\'est pas pour les contacts et les événements). Après l\'installation OpenTasks, vous devez RE-INSTALLER DAVdroid et ajoutez vos comptes à nouveau (bug Android). Installer OpenTasks + Librairies + Version %1s(%2d) + Compilé sur %s + Cette version ne peut être distribuée que sur Google Play. Ce programme est fourni sans AUCUNE GARANTIE. C\'est un logiciel libre, et vous êtes en droit de le redistribuer sous certaines conditions. DAVdroid fichier de journalisation @@ -32,10 +49,12 @@ Fermer le tiroir de navigation Adaptateur de synchronisation CalDAV/CardDAV A propos / Licence + Commentaire pour la version Beta Paramètres Actualités & mises à jour Liens externes Site Web + Manuel Foire aux questions Aide/Forum Faire un don @@ -80,9 +99,12 @@ Supprimer le compte Voulez-vous vraiment supprimer le compte? Toutes les copies locales des carnets d\'adresses, des calendriers et des listes de tâches seront supprimées. + Choisir la collection à synchroniser CardDAV (les carnets d\'adresse) CalDAV (les agendas) WebCal (les anciens agenda) + Synchronise cette collection + En lecture seulement calendrier liste de tâche Actualiser le carnet d\'adresses @@ -100,10 +122,13 @@ Mot de passe requis Connexion avec une URL et un nom d\'utilisateur L\'URL doit commencer par http(s):// + L\'URL doit commencer par https:// Nom d\'hôte requis Nom d\'utilisateur Nom d\'utilisateur requis URL de base + Se connecter avec l\'URL et le certificat client + Choisir le certificat Se connecter Retour Créer un compte @@ -115,6 +140,7 @@ Détection de la configuration Veuillez patienter, nous interrogeons le serveur … Aucun accès possible au service CalDAV ou CardDAV. + Montrer les détails Paramètres: %s Authentification @@ -123,6 +149,7 @@ Mot de passe Mettre à jour le mot de passe Saisissez votre mot de passe : + Alias ​​du certificat client Synchronisation Intervalle de synchronisation des carnets d\'adresses Manuellement @@ -155,6 +182,8 @@ Les groupes sont des VCards indépendantes Les groupes sont des catégories pour chacun des contacts + Changer la méthode de groupe + Cela demander de recharger tous les contacts. Les changements locaux non sauvegardés seront perdus. CalDAV Limite des événements passés Tous les événements seront synchronisés @@ -191,6 +220,10 @@ Êtes-vous sur? Cette collection (%s) et toutes ses données seront supprimées du serveur. Suppression de la collection + Forcer la lecture seulement + Propriétés + Adresse (URL) : + Copier l\'URL Une erreur est survenue. Une erreur HTTP est survenue. @@ -198,8 +231,17 @@ Voir détails Infos de débogage + Carnet d\'adresse en lecture seulement Autorisations DAVdroid Autorisations supplémentaires demandées + Version d\'OpenTask trop ancienne + Version requise %1$s (actuellement %2$s) + Echec d\'authentification (contrôler vos identifiants de connexion) + Erreur de réseau ou d\'entrée / sortie - %s + Erreur de serveur HTTP - %s + Erreur de stockage local - %s + Essayez à nouveau + Voir l\'élément DAVdroid : Sécurité de la connexion DAVdroid a rencontré un certificat inconnu. Voulez-vous lui faire confiance? -- GitLab From 7d4689969a6cbb30c5cb1196eb74d35a48042107 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Wed, 26 Dec 2018 11:51:33 +0100 Subject: [PATCH 016/382] Refactor Settings provider * don't use a separate :sync process anymore, so that settings management doesn't need IPC * remove Settings service and IPC, use singleton with application Context instead * adapt default number of sync worker threads * library updates --- Settings.kt | 185 ++++++++++++++++ app/build.gradle | 7 +- ...est.kt => DefaultsSettingsProviderTest.kt} | 17 +- .../bitfire/davdroid/settings/SettingsTest.kt | 16 +- app/src/main/AndroidManifest.xml | 5 - .../bitfire/davdroid/settings/ISettings.aidl | 28 --- .../davdroid/settings/ISettingsObserver.aidl | 7 - app/src/main/java/at/bitfire/davdroid/App.kt | 18 +- .../java/at/bitfire/davdroid/DavService.kt | 127 ++++++----- .../java/at/bitfire/davdroid/HttpClient.kt | 36 ++-- .../java/at/bitfire/davdroid/log/Logger.kt | 10 +- .../at/bitfire/davdroid/model/ServiceDB.kt | 10 +- .../{ => settings}/AccountSettings.kt | 25 +-- .../davdroid/settings/DefaultsProvider.kt | 22 +- .../settings/ISettingsProviderFactory.kt | 17 ++ .../at/bitfire/davdroid/settings/Settings.kt | 198 +++++------------- .../{Provider.kt => SettingsProvider.kt} | 10 +- .../settings/SharedPreferencesProvider.kt | 18 +- .../AddressBooksSyncAdapterService.kt | 7 +- .../syncadapter/CalendarSyncManager.kt | 6 +- .../CalendarsSyncAdapterService.kt | 9 +- .../syncadapter/ContactsSyncAdapterService.kt | 9 +- .../syncadapter/ContactsSyncManager.kt | 6 +- .../syncadapter/SyncAdapterService.kt | 21 +- .../davdroid/syncadapter/SyncManager.kt | 9 +- .../syncadapter/TasksSyncAdapterService.kt | 9 +- .../davdroid/syncadapter/TasksSyncManager.kt | 6 +- .../davdroid/ui/AccountSettingsActivity.kt | 116 ++++------ .../bitfire/davdroid/ui/AccountsActivity.kt | 79 ++----- .../davdroid/ui/AppSettingsActivity.kt | 103 +++------ .../davdroid/ui/CollectionInfoFragment.kt | 2 +- .../davdroid/ui/CreateCalendarActivity.kt | 6 +- .../davdroid/ui/CreateCollectionFragment.kt | 69 +++--- .../bitfire/davdroid/ui/DebugInfoActivity.kt | 38 ++-- .../ui/DefaultAccountsDrawerHandler.kt | 4 +- .../davdroid/ui/DeleteCollectionFragment.kt | 35 ++-- .../davdroid/ui/ExceptionInfoFragment.kt | 2 +- .../davdroid/ui/IAccountsDrawerHandler.kt | 4 +- .../at/bitfire/davdroid/ui/SettingsLoader.kt | 66 ------ .../davdroid/ui/StartupDialogFragment.kt | 65 ++---- .../ui/setup/AccountDetailsFragment.kt | 144 ++++--------- .../davdroid/ui/setup/DavResourceFinder.kt | 5 +- .../davdroid/ui/setup/LoginActivity.kt | 2 +- .../ui/widget/IntEditTextPreference.kt | 2 +- .../main/res/layout/login_account_details.xml | 1 - ...davdroid.settings.ISettingsProviderFactory | 2 + dav4android | 2 +- vcard4android | 2 +- 48 files changed, 667 insertions(+), 920 deletions(-) create mode 100644 Settings.kt rename app/src/androidTest/java/at/bitfire/davdroid/settings/{DefaultsProviderTest.kt => DefaultsSettingsProviderTest.kt} (57%) delete mode 100644 app/src/main/aidl/at/bitfire/davdroid/settings/ISettings.aidl delete mode 100644 app/src/main/aidl/at/bitfire/davdroid/settings/ISettingsObserver.aidl rename app/src/main/java/at/bitfire/davdroid/{ => settings}/AccountSettings.kt (97%) create mode 100644 app/src/main/java/at/bitfire/davdroid/settings/ISettingsProviderFactory.kt rename app/src/main/java/at/bitfire/davdroid/settings/{Provider.kt => SettingsProvider.kt} (89%) delete mode 100644 app/src/main/java/at/bitfire/davdroid/ui/SettingsLoader.kt create mode 100644 app/src/main/resources/META-INF/services/at.bitfire.davdroid.settings.ISettingsProviderFactory diff --git a/Settings.kt b/Settings.kt new file mode 100644 index 000000000..39341e0db --- /dev/null +++ b/Settings.kt @@ -0,0 +1,185 @@ +/* + * 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.settings + +import android.content.Context +import at.bitfire.davdroid.log.Logger +import java.lang.ref.WeakReference +import java.util.* +import java.util.logging.Level + +class Settings( + appContext: Context +) { + + companion object { + + // settings keys and default values + const val DISTRUST_SYSTEM_CERTIFICATES = "distrust_system_certs" + const val DISTRUST_SYSTEM_CERTIFICATES_DEFAULT = false + const val OVERRIDE_PROXY = "override_proxy" + const val OVERRIDE_PROXY_DEFAULT = false + const val OVERRIDE_PROXY_HOST = "override_proxy_host" + const val OVERRIDE_PROXY_PORT = "override_proxy_port" + + const val OVERRIDE_PROXY_HOST_DEFAULT = "localhost" + const val OVERRIDE_PROXY_PORT_DEFAULT = 8118 + + + private var singleton: Settings? = null + + fun getInstance(context: Context): Settings { + singleton?.let { return it } + + val newInstance = Settings(context.applicationContext) + singleton = newInstance + return newInstance + } + + } + + private val providers = LinkedList() + private val observers = LinkedList>() + + init { + ServiceLoader.load(ISettingsProviderFactory::class.java).forEach { factory -> + providers.addAll(factory.getProviders(appContext)) + } + } + + fun forceReload() { + providers.forEach { + it.forceReload() + } + onSettingsChanged() + } + + + /*** OBSERVERS ***/ + + fun addOnChangeListener(observer: OnChangeListener) { + observers += WeakReference(observer) + } + + fun removeOnChangeListener(observer: OnChangeListener) { + observers.removeAll { it.get() == null || it.get() == observer } + } + + fun onSettingsChanged() { + observers.mapNotNull { it.get() }.forEach { + it.onSettingsChanged() + } + } + + + /*** SETTINGS ACCESS ***/ + + fun has(key: String): Boolean { + Logger.log.fine("Looking for setting $key") + var result = false + for (provider in providers) + try { + val (value, further) = provider.has(key) + Logger.log.finer("${provider::class.java.simpleName}: has $key = $value, continue: $further") + if (value) { + result = true + break + } + if (!further) + break + } catch(e: Exception) { + Logger.log.log(Level.SEVERE, "Couldn't look up setting in $provider", e) + } + Logger.log.fine("Looking for setting $key -> $result") + return result + } + + private fun getValue(key: String, reader: (SettingsProvider) -> Pair): T? { + Logger.log.fine("Looking up setting $key") + var result: T? = null + for (provider in providers) + try { + val (value, further) = reader(provider) + Logger.log.finer("${provider::class.java.simpleName}: value = $value, continue: $further") + value?.let { result = it } + if (!further) + break + } catch(e: Exception) { + Logger.log.log(Level.SEVERE, "Couldn't read setting from $provider", e) + } + Logger.log.fine("Looked up setting $key -> $result") + return result + } + + fun getBoolean(key: String) = + getValue(key) { provider -> provider.getBoolean(key) } + + fun getInt(key: String) = + getValue(key) { provider -> provider.getInt(key) } + + fun getLong(key: String) = + getValue(key) { provider -> provider.getLong(key) } + + fun getString(key: String) = + getValue(key) { provider -> provider.getString(key) } + + + fun isWritable(key: String): Boolean { + for (provider in providers) { + val (value, further) = provider.isWritable(key) + if (value) + return true + if (!further) + return false + } + return false + } + + private fun putValue(key: String, value: T?, writer: (SettingsProvider) -> Boolean): Boolean { + Logger.log.fine("Trying to write setting $key = $value") + for (provider in providers) { + val (writable, further) = provider.isWritable(key) + Logger.log.finer("${provider::class.java.simpleName}: writable = $writable, continue: $further") + if (writable) + return try { + writer(provider) + } catch (e: Exception) { + Logger.log.log(Level.SEVERE, "Couldn't write setting to $provider", e) + false + } + if (!further) + return false + } + return false + } + + fun putBoolean(key: String, value: Boolean?) = + putValue(key, value) { provider -> provider.putBoolean(key, value) } + + fun putInt(key: String, value: Int?) = + putValue(key, value) { provider -> provider.putInt(key, value) } + + fun putLong(key: String, value: Long?) = + putValue(key, value) { provider -> provider.putLong(key, value) } + + fun putString(key: String, value: String?) = + putValue(key, value) { provider -> provider.putString(key, value) } + + fun remove(key: String): Boolean { + var deleted = false + providers.forEach { deleted = deleted || it.remove(key) } + return deleted + } + + + interface OnChangeListener { + fun onSettingsChanged() + } + +} diff --git a/app/build.gradle b/app/build.gradle index 755447e20..e781dc41e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,7 +33,6 @@ android { productFlavors { standard { versionName "2.0.7-ose" - buildConfigField "boolean", "customCerts", "true" } } @@ -83,7 +82,7 @@ dependencies { implementation 'com.github.yukuku:ambilwarna:2.0.1' implementation 'com.mikepenz:aboutlibraries:6.2.0' - implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0' + implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1' implementation 'commons-io:commons-io:2.6' implementation 'dnsjava:dnsjava:2.1.8' implementation 'org.apache.commons:commons-lang3:3.8.1' @@ -93,8 +92,8 @@ dependencies { androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:rules:1.1.1' androidTestImplementation 'junit:junit:4.12' - androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.0' + androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1' testImplementation 'junit:junit:4.12' - testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.0' + testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1' } diff --git a/app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsProviderTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsSettingsProviderTest.kt similarity index 57% rename from app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsProviderTest.kt rename to app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsSettingsProviderTest.kt index a8c2dce10..2d84e147f 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsProviderTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/settings/DefaultsSettingsProviderTest.kt @@ -8,32 +8,31 @@ package at.bitfire.davdroid.settings -import at.bitfire.davdroid.App import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Test -class DefaultsProviderTest { +class DefaultsSettingsProviderTest { - private val provider: Provider = DefaultsProvider() + private val provider: SettingsProvider = DefaultsProvider() @Test fun testHas() { assertEquals(Pair(false, true), provider.has("notExisting")) - assertEquals(Pair(true, true), provider.has(App.OVERRIDE_PROXY)) + assertEquals(Pair(true, true), provider.has(Settings.OVERRIDE_PROXY)) } @Test fun testGet() { - assertEquals(Pair("localhost", true), provider.getString(App.OVERRIDE_PROXY_HOST)) - assertEquals(Pair(8118, true), provider.getInt(App.OVERRIDE_PROXY_PORT)) + assertEquals(Pair("localhost", true), provider.getString(Settings.OVERRIDE_PROXY_HOST)) + assertEquals(Pair(8118, true), provider.getInt(Settings.OVERRIDE_PROXY_PORT)) } @Test fun testPutRemove() { - assertEquals(Pair(false, true), provider.isWritable(App.OVERRIDE_PROXY)) - assertFalse(provider.putBoolean(App.OVERRIDE_PROXY, true)) - assertFalse(provider.remove(App.OVERRIDE_PROXY)) + assertEquals(Pair(false, true), provider.isWritable(Settings.OVERRIDE_PROXY)) + assertFalse(provider.putBoolean(Settings.OVERRIDE_PROXY, true)) + assertFalse(provider.remove(Settings.OVERRIDE_PROXY)) } } \ No newline at end of file diff --git a/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt index 391a2cb39..7e7d7feee 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/settings/SettingsTest.kt @@ -9,8 +9,6 @@ package at.bitfire.davdroid.settings import androidx.test.platform.app.InstrumentationRegistry -import at.bitfire.davdroid.App -import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before @@ -18,25 +16,19 @@ import org.junit.Test class SettingsTest { - lateinit var settings: Settings.Stub + lateinit var settings: Settings @Before - fun init() { - settings = Settings.getInstance(InstrumentationRegistry.getInstrumentation().targetContext)!! + fun initialize() { + settings = Settings.getInstance(InstrumentationRegistry.getInstrumentation().targetContext) } - @After - fun shutdown() { - settings.close() - } - - @Test fun testHas() { assertFalse(settings.has("notExisting")) // provided by DefaultsProvider - assertTrue(settings.has(App.OVERRIDE_PROXY)) + assertTrue(settings.has(Settings.OVERRIDE_PROXY)) } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 918503ca3..56d7d91ca 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -57,7 +57,6 @@ tools:ignore="UnusedAttribute"> - @@ -143,7 +141,6 @@ @@ -175,7 +172,6 @@ @@ -188,7 +184,6 @@ diff --git a/app/src/main/aidl/at/bitfire/davdroid/settings/ISettings.aidl b/app/src/main/aidl/at/bitfire/davdroid/settings/ISettings.aidl deleted file mode 100644 index 5d0e05ded..000000000 --- a/app/src/main/aidl/at/bitfire/davdroid/settings/ISettings.aidl +++ /dev/null @@ -1,28 +0,0 @@ -package at.bitfire.davdroid.settings; - -import at.bitfire.davdroid.settings.ISettingsObserver; - -interface ISettings { - - void forceReload(); - - boolean has(String key); - - boolean getBoolean(String key, boolean defaultValue); - int getInt(String key, int defaultValue); - long getLong(String key, long defaultValue); - String getString(String key, String defaultValue); - - boolean isWritable(String key); - - boolean putBoolean(String key, boolean value); - boolean putInt(String key, int value); - boolean putLong(String key, long value); - boolean putString(String key, String value); - - boolean remove(String key); - - void registerObserver(ISettingsObserver observer); - void unregisterObserver(ISettingsObserver observer); - -} diff --git a/app/src/main/aidl/at/bitfire/davdroid/settings/ISettingsObserver.aidl b/app/src/main/aidl/at/bitfire/davdroid/settings/ISettingsObserver.aidl deleted file mode 100644 index ba122edc3..000000000 --- a/app/src/main/aidl/at/bitfire/davdroid/settings/ISettingsObserver.aidl +++ /dev/null @@ -1,7 +0,0 @@ -package at.bitfire.davdroid.settings; - -interface ISettingsObserver { - - void onSettingsChanged(); - -} diff --git a/app/src/main/java/at/bitfire/davdroid/App.kt b/app/src/main/java/at/bitfire/davdroid/App.kt index 7aa8cff02..7e95a2a69 100644 --- a/app/src/main/java/at/bitfire/davdroid/App.kt +++ b/app/src/main/java/at/bitfire/davdroid/App.kt @@ -27,15 +27,6 @@ class App: Application() { companion object { - const val DISTRUST_SYSTEM_CERTIFICATES = "distrust_system_certs" - const val OVERRIDE_PROXY = "override_proxy" - const val OVERRIDE_PROXY_HOST = "override_proxy_host" - const val OVERRIDE_PROXY_PORT = "override_proxy_port" - - const val OVERRIDE_PROXY_HOST_DEFAULT = "localhost" - const val OVERRIDE_PROXY_PORT_DEFAULT = 8118 - - fun getLauncherBitmap(context: Context): Bitmap? { val drawableLogo = if (android.os.Build.VERSION.SDK_INT >= 21) context.getDrawable(R.mipmap.ic_launcher) @@ -62,7 +53,7 @@ class App: Application() { super.onCreate() Logger.initialize(this) - if (BuildConfig.DEBUG) { + if (BuildConfig.DEBUG) StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder() .detectActivityLeaks() .detectFileUriExposure() @@ -72,13 +63,6 @@ class App: Application() { .penaltyLog() .build()) - // main thread - StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyLog() - .build()) - } - if (Build.VERSION.SDK_INT <= 21) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) diff --git a/app/src/main/java/at/bitfire/davdroid/DavService.kt b/app/src/main/java/at/bitfire/davdroid/DavService.kt index 1813d33af..d15bb8518 100644 --- a/app/src/main/java/at/bitfire/davdroid/DavService.kt +++ b/app/src/main/java/at/bitfire/davdroid/DavService.kt @@ -30,7 +30,7 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.ServiceDB.* import at.bitfire.davdroid.model.ServiceDB.Collections -import at.bitfire.davdroid.settings.Settings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.DebugInfoActivity import at.bitfire.davdroid.ui.NotificationUtils import okhttp3.HttpUrl @@ -293,84 +293,81 @@ class DavService: Service() { NotificationManagerCompat.from(this) .cancel(service.toString(), NotificationUtils.NOTIFY_REFRESH_COLLECTIONS) - Settings.getInstance(this)?.use { settings -> - // create authenticating OkHttpClient (credentials taken from account settings) - HttpClient.Builder(this, settings, AccountSettings(this, settings, account)) - .setForeground(true) - .build().use { client -> - val httpClient = client.okHttpClient - - // refresh home set list (from principal) - readPrincipal()?.let { principalUrl -> - Logger.log.fine("Querying principal $principalUrl for home sets") - queryHomeSets(httpClient, principalUrl) - } + // create authenticating OkHttpClient (credentials taken from account settings) + HttpClient.Builder(this, AccountSettings(this, account)) + .setForeground(true) + .build().use { client -> + val httpClient = client.okHttpClient + + // refresh home set list (from principal) + readPrincipal()?.let { principalUrl -> + Logger.log.fine("Querying principal $principalUrl for home sets") + queryHomeSets(httpClient, principalUrl) + } - // remember selected collections - val selectedCollections = HashSet() - collections.values - .filter { it.selected } - .forEach { (url, _) -> selectedCollections += url } + // remember selected collections + val selectedCollections = HashSet() + collections.values + .filter { it.selected } + .forEach { (url, _) -> selectedCollections += url } - // now refresh collections (taken from home sets) - val itHomeSets = homeSets.iterator() - while (itHomeSets.hasNext()) { - val homeSetUrl = itHomeSets.next() - Logger.log.fine("Listing home set $homeSetUrl") + // now refresh collections (taken from home sets) + val itHomeSets = homeSets.iterator() + while (itHomeSets.hasNext()) { + val homeSetUrl = itHomeSets.next() + Logger.log.fine("Listing home set $homeSetUrl") + + try { + DavResource(httpClient, homeSetUrl).propfind(1, *CollectionInfo.DAV_PROPERTIES) { response, _ -> + if (!response.isSuccess()) + return@propfind + val info = CollectionInfo(response) + info.confirmed = true + Logger.log.log(Level.FINE, "Found collection", info) + + if ((serviceType == Services.SERVICE_CARDDAV && info.type == CollectionInfo.Type.ADDRESS_BOOK) || + (serviceType == Services.SERVICE_CALDAV && arrayOf(CollectionInfo.Type.CALENDAR, CollectionInfo.Type.WEBCAL).contains(info.type))) + collections[response.href] = info + } + } catch(e: HttpException) { + if (e.code in arrayOf(403, 404, 410)) + // delete home set only if it was not accessible (40x) + itHomeSets.remove() + } + } + + // check/refresh unconfirmed collections + val itCollections = collections.entries.iterator() + while (itCollections.hasNext()) { + val (url, info) = itCollections.next() + if (!info.confirmed) try { - DavResource(httpClient, homeSetUrl).propfind(1, *CollectionInfo.DAV_PROPERTIES) { response, _ -> + DavResource(httpClient, url).propfind(0, *CollectionInfo.DAV_PROPERTIES) { response, _ -> if (!response.isSuccess()) return@propfind - val info = CollectionInfo(response) - info.confirmed = true - Logger.log.log(Level.FINE, "Found collection", info) + val collectionInfo = CollectionInfo(response) + collectionInfo.confirmed = true - if ((serviceType == Services.SERVICE_CARDDAV && info.type == CollectionInfo.Type.ADDRESS_BOOK) || - (serviceType == Services.SERVICE_CALDAV && arrayOf(CollectionInfo.Type.CALENDAR, CollectionInfo.Type.WEBCAL).contains(info.type))) - collections[response.href] = info + // remove unusable collections + if ((serviceType == Services.SERVICE_CARDDAV && collectionInfo.type != CollectionInfo.Type.ADDRESS_BOOK) || + (serviceType == Services.SERVICE_CALDAV && !arrayOf(CollectionInfo.Type.CALENDAR, CollectionInfo.Type.WEBCAL).contains(collectionInfo.type)) || + (collectionInfo.type == CollectionInfo.Type.WEBCAL && collectionInfo.source == null)) + itCollections.remove() } } catch(e: HttpException) { if (e.code in arrayOf(403, 404, 410)) - // delete home set only if it was not accessible (40x) - itHomeSets.remove() + // delete collection only if it was not accessible (40x) + itCollections.remove() + else + throw e } - } - - // check/refresh unconfirmed collections - val itCollections = collections.entries.iterator() - while (itCollections.hasNext()) { - val (url, info) = itCollections.next() - if (!info.confirmed) - try { - DavResource(httpClient, url).propfind(0, *CollectionInfo.DAV_PROPERTIES) { response, _ -> - if (!response.isSuccess()) - return@propfind - - val collectionInfo = CollectionInfo(response) - collectionInfo.confirmed = true - - // remove unusable collections - if ((serviceType == Services.SERVICE_CARDDAV && collectionInfo.type != CollectionInfo.Type.ADDRESS_BOOK) || - (serviceType == Services.SERVICE_CALDAV && !arrayOf(CollectionInfo.Type.CALENDAR, CollectionInfo.Type.WEBCAL).contains(collectionInfo.type)) || - (collectionInfo.type == CollectionInfo.Type.WEBCAL && collectionInfo.source == null)) - itCollections.remove() - } - } catch(e: HttpException) { - if (e.code in arrayOf(403, 404, 410)) - // delete collection only if it was not accessible (40x) - itCollections.remove() - else - throw e - } - } - - // restore selections - for (url in selectedCollections) - collections[url]?.let { it.selected = true } } + // restore selections + for (url in selectedCollections) + collections[url]?.let { it.selected = true } } db.beginTransactionNonExclusive() diff --git a/app/src/main/java/at/bitfire/davdroid/HttpClient.kt b/app/src/main/java/at/bitfire/davdroid/HttpClient.kt index 927bfbf18..a6f2fb1c8 100644 --- a/app/src/main/java/at/bitfire/davdroid/HttpClient.kt +++ b/app/src/main/java/at/bitfire/davdroid/HttpClient.kt @@ -17,7 +17,8 @@ import at.bitfire.dav4android.Constants import at.bitfire.dav4android.UrlUtils import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.Credentials -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings +import at.bitfire.davdroid.settings.Settings import okhttp3.Cache import okhttp3.Interceptor import okhttp3.OkHttpClient @@ -67,7 +68,6 @@ class HttpClient private constructor( class Builder( val context: Context? = null, - val settings: ISettings? = null, accountSettings: AccountSettings? = null, val logger: java.util.logging.Logger = Logger.log ) { @@ -89,32 +89,36 @@ class HttpClient private constructor( orig.addInterceptor(loggingInterceptor) } - settings?.let { + context?.let { + val settings = Settings.getInstance(context) + // custom proxy support try { - if (settings.getBoolean(App.OVERRIDE_PROXY, false)) { + if (settings.getBoolean(Settings.OVERRIDE_PROXY) == true) { val address = InetSocketAddress( - settings.getString(App.OVERRIDE_PROXY_HOST, App.OVERRIDE_PROXY_HOST_DEFAULT), - settings.getInt(App.OVERRIDE_PROXY_PORT, App.OVERRIDE_PROXY_PORT_DEFAULT) + settings.getString(Settings.OVERRIDE_PROXY_HOST) + ?: Settings.OVERRIDE_PROXY_HOST_DEFAULT, + settings.getInt(Settings.OVERRIDE_PROXY_PORT) + ?: Settings.OVERRIDE_PROXY_PORT_DEFAULT ) val proxy = Proxy(Proxy.Type.HTTP, address) orig.proxy(proxy) Logger.log.log(Level.INFO, "Using proxy", proxy) } - } catch(e: Exception) { + } catch (e: Exception) { Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e) } - context?.let { - if (BuildConfig.customCerts) - customCertManager(CustomCertManager(context, true, !settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false))) + //if (BuildConfig.customCerts) + customCertManager(CustomCertManager(context, true, + !(settings.getBoolean(Settings.DISTRUST_SYSTEM_CERTIFICATES) + ?: Settings.DISTRUST_SYSTEM_CERTIFICATES_DEFAULT))) + } - // use account settings for authentication - accountSettings?.let { - addAuthentication(null, it.credentials()) - } - } + // use account settings for authentication + accountSettings?.let { + addAuthentication(null, it.credentials()) } } @@ -176,6 +180,8 @@ class HttpClient private constructor( var keyManager: KeyManager? = null try { certificateAlias?.let { alias -> + val context = requireNotNull(context) + // get client certificate and private key val certs = KeyChain.getCertificateChain(context, alias) ?: return@let val key = KeyChain.getPrivateKey(context, alias) ?: return@let diff --git a/app/src/main/java/at/bitfire/davdroid/log/Logger.kt b/app/src/main/java/at/bitfire/davdroid/log/Logger.kt index b09a7a9df..b3d857083 100644 --- a/app/src/main/java/at/bitfire/davdroid/log/Logger.kt +++ b/app/src/main/java/at/bitfire/davdroid/log/Logger.kt @@ -14,9 +14,9 @@ import android.content.Intent import android.content.SharedPreferences import android.os.Process import android.preference.PreferenceManager +import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import android.util.Log import at.bitfire.davdroid.R import at.bitfire.davdroid.ui.AppSettingsActivity import at.bitfire.davdroid.ui.NotificationUtils @@ -35,14 +35,14 @@ object Logger { private lateinit var preferences: SharedPreferences - fun initialize(context: Context) { - preferences = PreferenceManager.getDefaultSharedPreferences(context) + fun initialize(appContext: Context) { + preferences = PreferenceManager.getDefaultSharedPreferences(appContext) preferences.registerOnSharedPreferenceChangeListener { _, s -> if (s == LOG_TO_EXTERNAL_STORAGE) - reinitialize(context.applicationContext) + reinitialize(appContext.applicationContext) } - reinitialize(context.applicationContext) + reinitialize(appContext) } private fun reinitialize(context: Context) { diff --git a/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt b/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt index 0c4abbe2a..634258e42 100644 --- a/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt +++ b/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt @@ -14,8 +14,8 @@ import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteException import android.database.sqlite.SQLiteOpenHelper import android.preference.PreferenceManager -import at.bitfire.davdroid.App import at.bitfire.davdroid.log.Logger +import at.bitfire.davdroid.settings.Settings import at.bitfire.davdroid.ui.StartupDialogFragment import java.util.logging.Level @@ -156,10 +156,10 @@ class ServiceDB { db.query("settings", arrayOf("setting", "value"), null, null, null, null, null).use { cursor -> while (cursor.moveToNext()) { when (cursor.getString(0)) { - "distrustSystemCerts" -> edit.putBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, cursor.getInt(1) != 0) - "overrideProxy" -> edit.putBoolean(App.OVERRIDE_PROXY, cursor.getInt(1) != 0) - "overrideProxyHost" -> edit.putString(App.OVERRIDE_PROXY_HOST, cursor.getString(1)) - "overrideProxyPort" -> edit.putInt(App.OVERRIDE_PROXY_PORT, cursor.getInt(1)) + "distrustSystemCerts" -> edit.putBoolean(Settings.DISTRUST_SYSTEM_CERTIFICATES, cursor.getInt(1) != 0) + "overrideProxy" -> edit.putBoolean(Settings.OVERRIDE_PROXY, cursor.getInt(1) != 0) + "overrideProxyHost" -> edit.putString(Settings.OVERRIDE_PROXY_HOST, cursor.getString(1)) + "overrideProxyPort" -> edit.putInt(Settings.OVERRIDE_PROXY_PORT, cursor.getInt(1)) StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED -> edit.putBoolean(StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, cursor.getInt(1) != 0) diff --git a/app/src/main/java/at/bitfire/davdroid/AccountSettings.kt b/app/src/main/java/at/bitfire/davdroid/settings/AccountSettings.kt similarity index 97% rename from app/src/main/java/at/bitfire/davdroid/AccountSettings.kt rename to app/src/main/java/at/bitfire/davdroid/settings/AccountSettings.kt index 33d62b17d..beba0b393 100644 --- a/app/src/main/java/at/bitfire/davdroid/AccountSettings.kt +++ b/app/src/main/java/at/bitfire/davdroid/settings/AccountSettings.kt @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package at.bitfire.davdroid +package at.bitfire.davdroid.settings import android.accounts.Account import android.accounts.AccountManager @@ -17,6 +17,7 @@ import android.os.Parcel import android.os.RemoteException import android.provider.CalendarContract import android.provider.ContactsContract +import at.bitfire.davdroid.* import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.Credentials @@ -27,7 +28,6 @@ import at.bitfire.davdroid.model.SyncState import at.bitfire.davdroid.resource.LocalAddressBook import at.bitfire.davdroid.resource.LocalCalendar import at.bitfire.davdroid.resource.LocalTaskList -import at.bitfire.davdroid.settings.ISettings import at.bitfire.ical4android.AndroidCalendar import at.bitfire.ical4android.AndroidTaskList import at.bitfire.ical4android.CalendarStorageException @@ -47,7 +47,6 @@ import java.util.logging.Level */ class AccountSettings( val context: Context, - val settings: ISettings, val account: Account ) { @@ -60,6 +59,7 @@ class AccountSettings( const val KEY_CERTIFICATE_ALIAS = "certificate_alias" const val KEY_WIFI_ONLY = "wifi_only" // sync on WiFi only (default: false) + const val WIFI_ONLY_DEFAULT = false const val KEY_WIFI_ONLY_SSIDS = "wifi_only_ssids" // restrict sync to specific WiFi SSIDs /** Time range limitation to the past [in days] @@ -103,9 +103,10 @@ class AccountSettings( } } - - + + val accountManager: AccountManager = AccountManager.get(context) + val settings = Settings.getInstance(context) init { synchronized(AccountSettings::class.java) { @@ -160,14 +161,14 @@ class AccountSettings( } fun getSyncWifiOnly() = if (settings.has(KEY_WIFI_ONLY)) - settings.getBoolean(KEY_WIFI_ONLY, false) + settings.getBoolean(KEY_WIFI_ONLY) ?: WIFI_ONLY_DEFAULT else accountManager.getUserData(account, KEY_WIFI_ONLY) != null fun setSyncWiFiOnly(wiFiOnly: Boolean) = accountManager.setUserData(account, KEY_WIFI_ONLY, if (wiFiOnly) "1" else null) fun getSyncWifiOnlySSIDs(): List? = (if (settings.has(KEY_WIFI_ONLY_SSIDS)) - settings.getString(KEY_WIFI_ONLY_SSIDS, null) + settings.getString(KEY_WIFI_ONLY_SSIDS) else accountManager.getUserData(account, KEY_WIFI_ONLY_SSIDS))?.split(',') fun setSyncWifiOnlySSIDs(ssids: List?) = @@ -189,14 +190,14 @@ class AccountSettings( accountManager.setUserData(account, KEY_TIME_RANGE_PAST_DAYS, (days ?: -1).toString()) fun getManageCalendarColors() = if (settings.has(KEY_MANAGE_CALENDAR_COLORS)) - settings.getBoolean(KEY_MANAGE_CALENDAR_COLORS, false) + settings.getBoolean(KEY_MANAGE_CALENDAR_COLORS) ?: false else accountManager.getUserData(account, KEY_MANAGE_CALENDAR_COLORS) == null fun setManageCalendarColors(manage: Boolean) = accountManager.setUserData(account, KEY_MANAGE_CALENDAR_COLORS, if (manage) null else "0") fun getEventColors() = if (settings.has(KEY_EVENT_COLORS)) - settings.getBoolean(KEY_EVENT_COLORS, false) + settings.getBoolean(KEY_EVENT_COLORS) ?: false else accountManager.getUserData(account, KEY_EVENT_COLORS) != null fun setEventColors(useColors: Boolean) = @@ -205,7 +206,7 @@ class AccountSettings( // CardDAV settings fun getGroupMethod(): GroupMethod { - val name = settings.getString(KEY_CONTACT_GROUP_METHOD, null) ?: + val name = settings.getString(KEY_CONTACT_GROUP_METHOD) ?: accountManager.getUserData(account, KEY_CONTACT_GROUP_METHOD) if (name != null) try { @@ -224,7 +225,7 @@ class AccountSettings( // update from previous account settings private fun update(baseVersion: Int) { - for (toVersion in baseVersion+1 .. CURRENT_VERSION) { + for (toVersion in baseVersion+1 ..CURRENT_VERSION) { val fromVersion = toVersion-1 Logger.log.info("Updating account ${account.name} from version $fromVersion to $toVersion") try { @@ -519,7 +520,7 @@ class AccountSettings( try { val addr = LocalAddressBook(context, account, provider) - // until now, ContactsContract.Settings.UNGROUPED_VISIBLE was not set explicitly + // until now, ContactsContract.settings.UNGROUPED_VISIBLE was not set explicitly val values = ContentValues() values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, 1) addr.settings = values diff --git a/app/src/main/java/at/bitfire/davdroid/settings/DefaultsProvider.kt b/app/src/main/java/at/bitfire/davdroid/settings/DefaultsProvider.kt index 40c87c8a8..4dfe65fc3 100644 --- a/app/src/main/java/at/bitfire/davdroid/settings/DefaultsProvider.kt +++ b/app/src/main/java/at/bitfire/davdroid/settings/DefaultsProvider.kt @@ -8,32 +8,31 @@ package at.bitfire.davdroid.settings -import at.bitfire.davdroid.App +import android.content.Context open class DefaultsProvider( private val allowOverride: Boolean = true -): Provider { +): SettingsProvider { open val booleanDefaults = mapOf( - Pair(App.DISTRUST_SYSTEM_CERTIFICATES, false), - Pair(App.OVERRIDE_PROXY, false) + Pair(Settings.DISTRUST_SYSTEM_CERTIFICATES, Settings.DISTRUST_SYSTEM_CERTIFICATES_DEFAULT), + Pair(Settings.OVERRIDE_PROXY, Settings.OVERRIDE_PROXY_DEFAULT) ) open val intDefaults = mapOf( - Pair(App.OVERRIDE_PROXY_PORT, App.OVERRIDE_PROXY_PORT_DEFAULT) + Pair(Settings.OVERRIDE_PROXY_PORT, Settings.OVERRIDE_PROXY_PORT_DEFAULT) ) open val longDefaults = mapOf() open val stringDefaults = mapOf( - Pair(App.OVERRIDE_PROXY_HOST, App.OVERRIDE_PROXY_HOST_DEFAULT) + Pair(Settings.OVERRIDE_PROXY_HOST, Settings.OVERRIDE_PROXY_HOST_DEFAULT) ) - - override fun close() { + override fun forceReload() { } - override fun forceReload() { + override fun close() { } @@ -71,4 +70,9 @@ open class DefaultsProvider( override fun remove(key: String) = false + + class Factory : ISettingsProviderFactory { + override fun getProviders(context: Context) = listOf(DefaultsProvider()) + } + } \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/davdroid/settings/ISettingsProviderFactory.kt b/app/src/main/java/at/bitfire/davdroid/settings/ISettingsProviderFactory.kt new file mode 100644 index 000000000..9615f9c51 --- /dev/null +++ b/app/src/main/java/at/bitfire/davdroid/settings/ISettingsProviderFactory.kt @@ -0,0 +1,17 @@ +/* + * 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.settings + +import android.content.Context + +interface ISettingsProviderFactory { + + fun getProviders(context: Context): List + +} \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/davdroid/settings/Settings.kt b/app/src/main/java/at/bitfire/davdroid/settings/Settings.kt index 93c657dba..39341e0db 100644 --- a/app/src/main/java/at/bitfire/davdroid/settings/Settings.kt +++ b/app/src/main/java/at/bitfire/davdroid/settings/Settings.kt @@ -8,62 +8,79 @@ package at.bitfire.davdroid.settings -import android.annotation.TargetApi -import android.app.Service -import android.content.ComponentName import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.os.Build -import android.os.Handler -import android.os.IBinder -import android.os.Looper import at.bitfire.davdroid.log.Logger import java.lang.ref.WeakReference import java.util.* import java.util.logging.Level -class Settings: Service(), Provider.Observer { +class Settings( + appContext: Context +) { + + companion object { + + // settings keys and default values + const val DISTRUST_SYSTEM_CERTIFICATES = "distrust_system_certs" + const val DISTRUST_SYSTEM_CERTIFICATES_DEFAULT = false + const val OVERRIDE_PROXY = "override_proxy" + const val OVERRIDE_PROXY_DEFAULT = false + const val OVERRIDE_PROXY_HOST = "override_proxy_host" + const val OVERRIDE_PROXY_PORT = "override_proxy_port" + + const val OVERRIDE_PROXY_HOST_DEFAULT = "localhost" + const val OVERRIDE_PROXY_PORT_DEFAULT = 8118 - private val providers = LinkedList() - private val observers = LinkedList>() - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - override fun onCreate() { - Logger.log.info("Initializing Settings service") + private var singleton: Settings? = null - // always add a defaults provider first - providers.add(DefaultsProvider()) + fun getInstance(context: Context): Settings { + singleton?.let { return it } + + val newInstance = Settings(context.applicationContext) + singleton = newInstance + return newInstance + } - // always add a provider for local preferences - providers.add(SharedPreferencesProvider(this)) } - override fun onDestroy() { - Logger.log.info("Shutting down Settings service") - providers.forEach { it.close() } - providers.clear() + private val providers = LinkedList() + private val observers = LinkedList>() + + init { + ServiceLoader.load(ISettingsProviderFactory::class.java).forEach { factory -> + providers.addAll(factory.getProviders(appContext)) + } } - override fun onTrimMemory(level: Int) { - stopSelf() + fun forceReload() { + providers.forEach { + it.forceReload() + } + onSettingsChanged() } - fun forceReload() { - providers.forEach { it.forceReload() } + /*** OBSERVERS ***/ + + fun addOnChangeListener(observer: OnChangeListener) { + observers += WeakReference(observer) } - override fun onReload() { - observers.forEach { - Handler(Looper.getMainLooper()).post { - it.get()?.onSettingsChanged() - } + fun removeOnChangeListener(observer: OnChangeListener) { + observers.removeAll { it.get() == null || it.get() == observer } + } + + fun onSettingsChanged() { + observers.mapNotNull { it.get() }.forEach { + it.onSettingsChanged() } } - private fun has(key: String): Boolean { + /*** SETTINGS ACCESS ***/ + + fun has(key: String): Boolean { Logger.log.fine("Looking for setting $key") var result = false for (provider in providers) @@ -83,7 +100,7 @@ class Settings: Service(), Provider.Observer { return result } - private fun getValue(key: String, reader: (Provider) -> Pair): T? { + private fun getValue(key: String, reader: (SettingsProvider) -> Pair): T? { Logger.log.fine("Looking up setting $key") var result: T? = null for (provider in providers) @@ -124,7 +141,7 @@ class Settings: Service(), Provider.Observer { return false } - private fun putValue(key: String, value: T?, writer: (Provider) -> Boolean): Boolean { + private fun putValue(key: String, value: T?, writer: (SettingsProvider) -> Boolean): Boolean { Logger.log.fine("Trying to write setting $key = $value") for (provider in providers) { val (writable, further) = provider.isWritable(key) @@ -161,115 +178,8 @@ class Settings: Service(), Provider.Observer { } - val binder = object: ISettings.Stub() { - - override fun forceReload() = - this@Settings.forceReload() - - override fun has(key: String) = - this@Settings.has(key) - - override fun getBoolean(key: String, defaultValue: Boolean) = - this@Settings.getBoolean(key) ?: defaultValue - - override fun getInt(key: String, defaultValue: Int) = - this@Settings.getInt(key) ?: defaultValue - - override fun getLong(key: String, defaultValue: Long) = - this@Settings.getLong(key) ?: defaultValue - - override fun getString(key: String, defaultValue: String?) = - this@Settings.getString(key) ?: defaultValue - - override fun isWritable(key: String) = - this@Settings.isWritable(key) - - override fun remove(key: String) = - this@Settings.remove(key) - - override fun putBoolean(key: String, value: Boolean) = - this@Settings.putBoolean(key, value) - - override fun putString(key: String, value: String?) = - this@Settings.putString(key, value) - - override fun putInt(key: String, value: Int) = - this@Settings.putInt(key, value) - - override fun putLong(key: String, value: Long) = - this@Settings.putLong(key, value) - - override fun registerObserver(observer: ISettingsObserver) { - observers += WeakReference(observer) - } - - override fun unregisterObserver(observer: ISettingsObserver) { - observers.removeAll { it.get() == observer } - } - - } - - override fun onBind(intent: Intent?) = binder - - - class Stub( - delegate: ISettings, - private val context: Context, - private val serviceConn: ServiceConnection? - ): ISettings by delegate, AutoCloseable { - - override fun close() { - try { - context.unbindService(serviceConn) - } catch(e: Exception) { - Logger.log.log(Level.SEVERE, "Couldn't unbind Settings service", e) - } - } - - } - - companion object { - - fun getInstance(context: Context): Stub? { - if (Looper.getMainLooper().thread == Thread.currentThread()) - throw IllegalStateException("Must not be called from main thread") - - var service: ISettings? = null - val serviceLock = Object() - val serviceConn = object: ServiceConnection { - override fun onServiceConnected(name: ComponentName, binder: IBinder) { - synchronized(serviceLock) { - service = ISettings.Stub.asInterface(binder) - serviceLock.notify() - } - } - override fun onServiceDisconnected(name: ComponentName) { - service = null - } - } - - if (!context.bindService(Intent(context, Settings::class.java), serviceConn, Context.BIND_AUTO_CREATE or Context.BIND_IMPORTANT)) - return null - - synchronized(serviceLock) { - if (service == null) - try { - serviceLock.wait() - } catch(e: InterruptedException) { - } - - if (service == null) { - try { - context.unbindService(serviceConn) - } catch (e: IllegalArgumentException) { - } - return null - } - } - - return Stub(service!!, context, serviceConn) - } - + interface OnChangeListener { + fun onSettingsChanged() } } diff --git a/app/src/main/java/at/bitfire/davdroid/settings/Provider.kt b/app/src/main/java/at/bitfire/davdroid/settings/SettingsProvider.kt similarity index 89% rename from app/src/main/java/at/bitfire/davdroid/settings/Provider.kt rename to app/src/main/java/at/bitfire/davdroid/settings/SettingsProvider.kt index 7819a1ecb..9d89b7e1a 100644 --- a/app/src/main/java/at/bitfire/davdroid/settings/Provider.kt +++ b/app/src/main/java/at/bitfire/davdroid/settings/SettingsProvider.kt @@ -8,11 +8,10 @@ package at.bitfire.davdroid.settings -import java.io.Closeable - -interface Provider: Closeable { +interface SettingsProvider { fun forceReload() + fun close() fun has(key: String): Pair @@ -30,9 +29,4 @@ interface Provider: Closeable { fun remove(key: String): Boolean - - interface Observer { - fun onReload() - } - } \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/davdroid/settings/SharedPreferencesProvider.kt b/app/src/main/java/at/bitfire/davdroid/settings/SharedPreferencesProvider.kt index e13b8f60b..721ecfe1d 100644 --- a/app/src/main/java/at/bitfire/davdroid/settings/SharedPreferencesProvider.kt +++ b/app/src/main/java/at/bitfire/davdroid/settings/SharedPreferencesProvider.kt @@ -16,8 +16,8 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.ServiceDB class SharedPreferencesProvider( - context: Context -): Provider { + val context: Context +): SettingsProvider, SharedPreferences.OnSharedPreferenceChangeListener { companion object { private const val META_VERSION = "version" @@ -34,12 +34,19 @@ class SharedPreferencesProvider( firstCall(context) meta.edit().putInt(META_VERSION, CURRENT_VERSION).apply() } + + preferences.registerOnSharedPreferenceChangeListener(this) + } + + override fun forceReload() { } override fun close() { + preferences.unregisterOnSharedPreferenceChangeListener(this) } - override fun forceReload() { + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + Settings.getInstance(context).onSettingsChanged() } @@ -117,4 +124,9 @@ class SharedPreferencesProvider( ServiceDB.OpenHelper(context).use { it.readableDatabase } } + + class Factory : ISettingsProviderFactory { + override fun getProviders(context: Context) = listOf(SharedPreferencesProvider(context)) + } + } \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt index 2dadeb03b..ae206fac9 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt @@ -16,13 +16,12 @@ import android.os.Build import android.os.Bundle import android.provider.ContactsContract import androidx.core.content.ContextCompat -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.ServiceDB import at.bitfire.davdroid.model.ServiceDB.Collections import at.bitfire.davdroid.resource.LocalAddressBook -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.AccountActivity import okhttp3.HttpUrl import java.util.logging.Level @@ -36,9 +35,9 @@ class AddressBooksSyncAdapterService : SyncAdapterService() { context: Context ) : SyncAdapter(context) { - override fun sync(settings: ISettings, account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { try { - val accountSettings = AccountSettings(context, settings, account) + val accountSettings = AccountSettings(context, account) /* don't run sync if - sync conditions (e.g. "sync only in WiFi") are not met AND diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt index 9fa15544e..59e230347 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt @@ -18,14 +18,13 @@ import at.bitfire.dav4android.DavResponseCallback import at.bitfire.dav4android.Response import at.bitfire.dav4android.exception.DavException import at.bitfire.dav4android.property.* -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.SyncState import at.bitfire.davdroid.resource.LocalCalendar import at.bitfire.davdroid.resource.LocalEvent import at.bitfire.davdroid.resource.LocalResource -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.ical4android.Event import at.bitfire.ical4android.InvalidCalendarException import okhttp3.HttpUrl @@ -41,14 +40,13 @@ import java.util.logging.Level */ class CalendarSyncManager( context: Context, - settings: ISettings, account: Account, accountSettings: AccountSettings, extras: Bundle, authority: String, syncResult: SyncResult, localCalendar: LocalCalendar -): SyncManager(context, settings, account, accountSettings, extras, authority, syncResult, localCalendar) { +): SyncManager(context, account, accountSettings, extras, authority, syncResult, localCalendar) { override fun prepare(): Boolean { collectionURL = HttpUrl.parse(localCollection.name ?: return false) ?: return false diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.kt index 2691bdec9..b7c3dd9eb 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.kt @@ -12,13 +12,12 @@ import android.content.* import android.database.DatabaseUtils import android.os.Bundle import android.provider.CalendarContract -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.ServiceDB import at.bitfire.davdroid.model.ServiceDB.Collections import at.bitfire.davdroid.resource.LocalCalendar -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.ical4android.AndroidCalendar import okhttp3.HttpUrl import java.util.logging.Level @@ -32,9 +31,9 @@ class CalendarsSyncAdapterService: SyncAdapterService() { context: Context ): SyncAdapter(context) { - override fun sync(settings: ISettings, account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { try { - val accountSettings = AccountSettings(context, settings, account) + val accountSettings = AccountSettings(context, account) /* don't run sync if - sync conditions (e.g. "sync only in WiFi") are not met AND @@ -52,7 +51,7 @@ class CalendarsSyncAdapterService: SyncAdapterService() { for (calendar in AndroidCalendar.find(account, provider, LocalCalendar.Factory, "${CalendarContract.Calendars.SYNC_EVENTS}!=0", null)) { Logger.log.info("Synchronizing calendar #${calendar.id}, URL: ${calendar.name}") - CalendarSyncManager(context, settings, account, accountSettings, extras, authority, syncResult, calendar).use { + CalendarSyncManager(context, account, accountSettings, extras, authority, syncResult, calendar).use { it.performSync() } } diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.kt index d5d9b9827..c58185cf6 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.kt @@ -15,10 +15,9 @@ import android.content.Context import android.content.SyncResult import android.os.Bundle import android.provider.ContactsContract -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.resource.LocalAddressBook -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings import java.util.logging.Level class ContactsSyncAdapterService: SyncAdapterService() { @@ -34,10 +33,10 @@ class ContactsSyncAdapterService: SyncAdapterService() { context: Context ): SyncAdapter(context) { - override fun sync(settings: ISettings, account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { try { val addressBook = LocalAddressBook(context, account, provider) - val accountSettings = AccountSettings(context, settings, addressBook.mainAccount) + val accountSettings = AccountSettings(context, addressBook.mainAccount) // handle group method change val groupMethod = accountSettings.getGroupMethod().name @@ -61,7 +60,7 @@ class ContactsSyncAdapterService: SyncAdapterService() { Logger.log.info("Synchronizing address book: ${addressBook.url}") Logger.log.info("Taking settings from: ${addressBook.mainAccount}") - ContactsSyncManager(context, settings, account, accountSettings, extras, authority, syncResult, provider, addressBook).use { + ContactsSyncManager(context, account, accountSettings, extras, authority, syncResult, provider, addressBook).use { it.performSync() } } catch(e: Exception) { diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt index a074ba7c7..bef4590c9 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt @@ -20,14 +20,13 @@ import at.bitfire.dav4android.DavResponseCallback import at.bitfire.dav4android.Response import at.bitfire.dav4android.exception.DavException import at.bitfire.dav4android.property.* -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.SyncState import at.bitfire.davdroid.resource.* -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact @@ -76,7 +75,6 @@ import java.util.logging.Level */ class ContactsSyncManager( context: Context, - settings: ISettings, account: Account, accountSettings: AccountSettings, extras: Bundle, @@ -84,7 +82,7 @@ class ContactsSyncManager( syncResult: SyncResult, val provider: ContentProviderClient, localAddressBook: LocalAddressBook -): SyncManager(context, settings, account, accountSettings, extras, authority, syncResult, localAddressBook) { +): SyncManager(context, account, accountSettings, extras, authority, syncResult, localAddressBook) { companion object { infix fun Set.disjunct(other: Set) = (this - other) union (other - this) diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt index b099a1166..ecae5664d 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt @@ -21,11 +21,9 @@ import android.os.Bundle import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger -import at.bitfire.davdroid.settings.ISettings -import at.bitfire.davdroid.settings.Settings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.AccountActivity import at.bitfire.davdroid.ui.AccountSettingsActivity import at.bitfire.davdroid.ui.NotificationUtils @@ -52,7 +50,7 @@ abstract class SyncAdapterService: Service() { context: Context ): AbstractThreadedSyncAdapter(context, false) { - abstract fun sync(settings: ISettings, account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) + 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) { Logger.log.log(Level.INFO, "$authority sync of $account has been initiated", extras.keySet().joinToString(", ")) @@ -71,19 +69,8 @@ abstract class SyncAdapterService: Service() { // required for dav4android (ServiceLoader) Thread.currentThread().contextClassLoader = context.classLoader - // load app settings - Settings.getInstance(context).use { settings -> - if (settings == null) { - syncResult.databaseError = true - Logger.log.severe("Couldn't connect to Settings service, aborting sync") - return - } - - //if (runSync) { - SyncManager.cancelNotifications(NotificationManagerCompat.from(context), authority, account) - sync(settings, account, extras, authority, provider, syncResult) - //} - } + SyncManager.cancelNotifications(NotificationManagerCompat.from(context), authority, account) + sync(account, extras, authority, provider, syncResult) } finally { synchronized(runningSyncs) { runningSyncs.removeAll { it.get() == null || it.get() == currentSync } diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt index 9308e1218..c844f3ddf 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt @@ -30,7 +30,7 @@ import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.SyncState import at.bitfire.davdroid.resource.* -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.AccountSettingsActivity import at.bitfire.davdroid.ui.DebugInfoActivity import at.bitfire.davdroid.ui.NotificationUtils @@ -54,7 +54,6 @@ import javax.net.ssl.SSLHandshakeException @Suppress("MemberVisibilityCanBePrivate") abstract class SyncManager, out CollectionType: LocalCollection, RemoteType: DavCollection>( val context: Context, - val settings: ISettings, val account: Account, val accountSettings: AccountSettings, val extras: Bundle, @@ -70,8 +69,8 @@ abstract class SyncManager, out CollectionType: L companion object { - val MAX_PROCESSING_THREADS = Math.max(Runtime.getRuntime().availableProcessors(), 4) - val MAX_DOWNLOAD_THREADS = Math.max(Runtime.getRuntime().availableProcessors(), 4) + val MAX_PROCESSING_THREADS = Math.min(Runtime.getRuntime().availableProcessors()/2, 1) + val MAX_DOWNLOAD_THREADS = Math.max(Runtime.getRuntime().availableProcessors(), 2) const val MAX_MULTIGET_RESOURCES = 10 fun cancelNotifications(manager: NotificationManagerCompat, authority: String, account: Account) = @@ -90,7 +89,7 @@ abstract class SyncManager, out CollectionType: L protected val notificationManager = NotificationManagerCompat.from(context) protected val notificationTag = notificationTag(authority, mainAccount) - protected val httpClient = HttpClient.Builder(context, settings, accountSettings).build() + protected val httpClient = HttpClient.Builder(context, accountSettings).build() protected lateinit var collectionURL: HttpUrl protected lateinit var davCollection: RemoteType diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt index b8fa587d3..3c238ca9e 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt @@ -17,7 +17,6 @@ import android.net.Uri import android.os.Bundle import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.CollectionInfo @@ -25,7 +24,7 @@ import at.bitfire.davdroid.model.ServiceDB import at.bitfire.davdroid.model.ServiceDB.Collections import at.bitfire.davdroid.model.ServiceDB.Services import at.bitfire.davdroid.resource.LocalTaskList -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.ical4android.AndroidTaskList import at.bitfire.ical4android.TaskProvider @@ -45,10 +44,10 @@ class TasksSyncAdapterService: SyncAdapterService() { context: Context ): SyncAdapter(context) { - override fun sync(settings: ISettings, account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { try { val taskProvider = TaskProvider.fromProviderClient(context, provider) - val accountSettings = AccountSettings(context, settings, account) + val accountSettings = AccountSettings(context, account) /* don't run sync if - sync conditions (e.g. "sync only in WiFi") are not met AND - this is is an automatic sync (i.e. manual syncs are run regardless of sync conditions) @@ -60,7 +59,7 @@ class TasksSyncAdapterService: SyncAdapterService() { for (taskList in AndroidTaskList.find(account, taskProvider, LocalTaskList.Factory, "${TaskContract.TaskLists.SYNC_ENABLED}!=0", null)) { Logger.log.info("Synchronizing task list #${taskList.id} [${taskList.syncId}]") - TasksSyncManager(context, settings, account, accountSettings, extras, authority, syncResult, taskList).use { + TasksSyncManager(context, account, accountSettings, extras, authority, syncResult, taskList).use { it.performSync() } } diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt index d4e88f02f..39fb190ee 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt @@ -21,14 +21,13 @@ import at.bitfire.dav4android.property.CalendarData import at.bitfire.dav4android.property.GetCTag import at.bitfire.dav4android.property.GetETag import at.bitfire.dav4android.property.SyncToken -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.SyncState import at.bitfire.davdroid.resource.LocalResource import at.bitfire.davdroid.resource.LocalTask import at.bitfire.davdroid.resource.LocalTaskList -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.ical4android.InvalidCalendarException import at.bitfire.ical4android.Task import okhttp3.HttpUrl @@ -43,14 +42,13 @@ import java.util.logging.Level */ class TasksSyncManager( context: Context, - settings: ISettings, account: Account, accountSettings: AccountSettings, extras: Bundle, authority: String, syncResult: SyncResult, localCollection: LocalTaskList -): SyncManager(context, settings, account, accountSettings, extras, authority, syncResult, localCollection) { +): SyncManager(context, account, accountSettings, extras, authority, syncResult, localCollection) { override fun prepare(): Boolean { collectionURL = HttpUrl.parse(localCollection.syncId ?: return false) ?: return false diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt index 084bdcfc9..1f36a6191 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountSettingsActivity.kt @@ -10,9 +10,7 @@ package at.bitfire.davdroid.ui import android.Manifest import android.accounts.Account -import android.annotation.SuppressLint import android.content.ContentResolver -import android.content.Context import android.content.Intent import android.content.SyncStatusObserver import android.content.pm.PackageManager @@ -29,15 +27,12 @@ import androidx.core.app.ActivityCompat import androidx.core.app.NavUtils import androidx.core.content.ContextCompat import androidx.fragment.app.DialogFragment -import androidx.loader.app.LoaderManager -import androidx.loader.content.Loader import androidx.preference.* -import at.bitfire.davdroid.AccountSettings -import at.bitfire.davdroid.InvalidAccountException import at.bitfire.davdroid.R import at.bitfire.davdroid.model.Credentials import at.bitfire.davdroid.resource.LocalCalendar -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.AccountSettings +import at.bitfire.davdroid.settings.Settings import at.bitfire.ical4android.AndroidCalendar import at.bitfire.ical4android.TaskProvider import at.bitfire.vcard4android.GroupMethod @@ -76,26 +71,45 @@ class AccountSettingsActivity: AppCompatActivity() { false - class AccountSettingsFragment: PreferenceFragmentCompat(), LoaderManager.LoaderCallbacks> { + class AccountSettingsFragment: PreferenceFragmentCompat(), SyncStatusObserver, Settings.OnChangeListener { + private lateinit var settings: Settings + lateinit var account: Account + private var statusChangeListener: Any? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - + settings = Settings.getInstance(requireActivity()) account = arguments!!.getParcelable(EXTRA_ACCOUNT)!! - LoaderManager.getInstance(this).initLoader(0, arguments, this) } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.settings_account) } - override fun onCreateLoader(id: Int, args: Bundle?) = - AccountSettingsLoader(requireActivity(), args!!.getParcelable(EXTRA_ACCOUNT)!!) + override fun onResume() { + super.onResume() + + statusChangeListener = ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this) + settings.addOnChangeListener(this) + + reload() + } + + override fun onPause() { + super.onPause() + statusChangeListener?.let { + ContentResolver.removeStatusChangeListener(it) + statusChangeListener = null + } + settings.removeOnChangeListener(this) + } + + override fun onStatusChanged(which: Int) = reload() + override fun onSettingsChanged() = reload() - @SuppressLint("Recycle") - override fun onLoadFinished(loader: Loader>, result: Pair?) { - val (settings, accountSettings) = result ?: return + fun reload() { + val accountSettings = AccountSettings(requireActivity(), account) // preference group: authentication val prefUserName = findPreference("username") as EditTextPreference @@ -110,14 +124,14 @@ class AccountSettingsActivity: AppCompatActivity() { prefUserName.text = credentials.userName prefUserName.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.credentials(Credentials(newValue as String, credentials.password)) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } prefPassword.isVisible = true prefPassword.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.credentials(Credentials(credentials.userName, newValue as String)) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } @@ -133,7 +147,7 @@ class AccountSettingsActivity: AppCompatActivity() { KeyChain.choosePrivateKeyAlias(requireActivity(), { alias -> accountSettings.credentials(Credentials(certificateAlias = alias)) Handler(Looper.getMainLooper()).post { - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() } }, null, null, null, -1, credentials.certificateAlias) true @@ -157,7 +171,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.summary = getString(R.string.settings_sync_summary_periodically, syncIntervalContacts / 60) it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setSyncInterval(getString(R.string.address_books_authority), (newValue as String).toLong()) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } } else @@ -174,7 +188,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.summary = getString(R.string.settings_sync_summary_periodically, syncIntervalCalendars / 60) it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setSyncInterval(CalendarContract.AUTHORITY, (newValue as String).toLong()) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } } else @@ -191,7 +205,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.summary = getString(R.string.settings_sync_summary_periodically, syncIntervalTasks / 60) it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, (newValue as String).toLong()) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } } else @@ -203,7 +217,7 @@ class AccountSettingsActivity: AppCompatActivity() { prefWifiOnly.isChecked = accountSettings.getSyncWifiOnly() prefWifiOnly.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, wifiOnly -> accountSettings.setSyncWiFiOnly(wifiOnly as Boolean) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } @@ -216,7 +230,7 @@ class AccountSettingsActivity: AppCompatActivity() { prefWifiOnlySSIDs.setSummary(R.string.settings_sync_wifi_only_ssids_off) prefWifiOnlySSIDs.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setSyncWifiOnlySSIDs((newValue as String).split(',').mapNotNull { StringUtils.trimToNull(it) }.distinct()) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } @@ -245,7 +259,7 @@ class AccountSettingsActivity: AppCompatActivity() { .setPositiveButton(android.R.string.ok) { _, _ -> // change group method accountSettings.setGroupMethod(GroupMethod.valueOf(groupMethod as String)) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() // reload all contacts val args = Bundle(1) @@ -298,7 +312,7 @@ class AccountSettingsActivity: AppCompatActivity() { } } - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } } else @@ -312,7 +326,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.isChecked = accountSettings.getManageCalendarColors() it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> accountSettings.setManageCalendarColors(newValue as Boolean) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() false } } else @@ -327,7 +341,7 @@ class AccountSettingsActivity: AppCompatActivity() { it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> if (newValue as Boolean) { accountSettings.setEventColors(true) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() } else AlertDialog.Builder(requireActivity()) .setIcon(R.drawable.ic_error_dark) @@ -336,7 +350,7 @@ class AccountSettingsActivity: AppCompatActivity() { .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(android.R.string.ok) { _, _ -> accountSettings.setEventColors(false) - LoaderManager.getInstance(this).restartLoader(0, arguments, this) + reload() } .show() false @@ -346,52 +360,6 @@ class AccountSettingsActivity: AppCompatActivity() { } } - override fun onLoaderReset(loader: Loader>) { - } - - } - - - class AccountSettingsLoader( - context: Context, - val account: Account - ): SettingsLoader?>(context), SyncStatusObserver { - - private var listenerHandle: Any? = null - - override fun onStartLoading() { - super.onStartLoading() - - if (listenerHandle == null) - listenerHandle = ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this) - } - - override fun onReset() { - super.onReset() - - listenerHandle?.let { - ContentResolver.removeStatusChangeListener(it) - listenerHandle = null - } - } - - override fun loadInBackground(): Pair? { - settings?.let { settings -> - try { - return Pair( - settings, - AccountSettings(context, settings, account) - ) - } catch (e: InvalidAccountException) { - } - } - return null - } - - override fun onStatusChanged(which: Int) { - onContentChanged() - } - } } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt index 76614de43..df5a6ef27 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt @@ -10,7 +10,6 @@ package at.bitfire.davdroid.ui import android.accounts.AccountManager import android.content.ContentResolver -import android.content.Context import android.content.Intent import android.content.SyncStatusObserver import android.os.Bundle @@ -18,39 +17,48 @@ import android.view.MenuItem import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GravityCompat -import androidx.loader.app.LoaderManager -import androidx.loader.content.Loader -import at.bitfire.davdroid.App import at.bitfire.davdroid.R -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.Settings import at.bitfire.davdroid.ui.setup.LoginActivity import com.google.android.material.navigation.NavigationView 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.* -class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, LoaderManager.LoaderCallbacks, SyncStatusObserver { +class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, SyncStatusObserver { companion object { val accountsDrawerHandler = DefaultAccountsDrawerHandler() - private const val fragTagStartup = "startup" + const val fragTagStartup = "startup" } + private lateinit var settings: Settings + private var syncStatusSnackbar: Snackbar? = null private var syncStatusObserver: Any? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_accounts) + settings = Settings.getInstance(this) + setContentView(R.layout.activity_accounts) setSupportActionBar(toolbar) + if (supportFragmentManager.findFragmentByTag(fragTagStartup) == null) { + val ft = supportFragmentManager.beginTransaction() + StartupDialogFragment.getStartupDialogs(this).forEach { ft.add(it, fragTagStartup) } + ft.commit() + } + fab.setOnClickListener { startActivity(Intent(this, LoginActivity::class.java)) } + fab.show() + accountsDrawerHandler.initMenu(this, drawer_layout.nav_view.menu) val toggle = ActionBarDrawerToggle( this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) drawer_layout.addDrawerListener(toggle) @@ -58,38 +66,6 @@ class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSele nav_view.setNavigationItemSelectedListener(this) nav_view.itemIconTintList = null - - /* When the DAVdroid main activity is started, start a Settings service that stays in memory - for better performance. The service stops itself when memory is trimmed. */ - val settingsIntent = Intent(this, Settings::class.java) - startService(settingsIntent) - - val args = Bundle(1) - LoaderManager.getInstance(this).initLoader(0, args, this) - } - - override fun onCreateLoader(code: Int, args: Bundle?) = - SettingsLoader(this) - - override fun onLoadFinished(loader: Loader, result: Settings?) { - if (result == null) - return - - if (supportFragmentManager.findFragmentByTag(fragTagStartup) == null) { - val ft = supportFragmentManager.beginTransaction() - StartupDialogFragment.getStartupDialogs(this, result.settings).forEach { ft.add(it, fragTagStartup) } - ft.commit() - } - - nav_view?.menu?.let { - accountsDrawerHandler.onSettingsChanged(result.settings, it) - } - } - - override fun onLoaderReset(loader: Loader) { - nav_view?.menu?.let { - accountsDrawerHandler.onSettingsChanged(null, it) - } } override fun onResume() { @@ -139,27 +115,4 @@ class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSele return processed } - - class Settings( - val settings: ISettings - ) - - class SettingsLoader( - context: Context - ): at.bitfire.davdroid.ui.SettingsLoader(context) { - - override fun loadInBackground(): Settings? { - settings?.let { - val accountManager = AccountManager.get(context) - val accounts = accountManager.getAccountsByType(context.getString(R.string.account_type)) - - return Settings( - it - ) - } - return null - } - - } - } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt index fef5fdef4..72d1e81e6 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt @@ -8,26 +8,16 @@ package at.bitfire.davdroid.ui -import android.app.ActivityManager -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection import android.os.Bundle -import android.os.IBinder -import android.os.Process import androidx.appcompat.app.AppCompatActivity import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreferenceCompat import at.bitfire.cert4android.CustomCertManager -import at.bitfire.davdroid.App import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger -import at.bitfire.davdroid.settings.ISettings -import at.bitfire.davdroid.settings.ISettingsObserver import at.bitfire.davdroid.settings.Settings import com.google.android.material.snackbar.Snackbar import java.net.URI @@ -52,43 +42,11 @@ class AppSettingsActivity: AppCompatActivity() { } - class SettingsFragment: PreferenceFragmentCompat() { - - val observer = object: ISettingsObserver.Stub() { - override fun onSettingsChanged() { - loadSettings() - } - } - - var settings: ISettings? = null - var settingsSvc: ServiceConnection? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val serviceConn = object: ServiceConnection { - override fun onServiceConnected(name: ComponentName, binder: IBinder) { - settings = ISettings.Stub.asInterface(binder) - settings?.registerObserver(observer) - loadSettings() - } - - override fun onServiceDisconnected(name: ComponentName) { - settings?.unregisterObserver(observer) - settings = null - } - } - if (activity!!.bindService(Intent(activity, Settings::class.java), serviceConn, Context.BIND_AUTO_CREATE)) - settingsSvc = serviceConn - } - - override fun onDestroy() { - super.onDestroy() - settingsSvc?.let { activity!!.unbindService(it) } - } + class SettingsFragment: PreferenceFragmentCompat(), Settings.OnChangeListener { override fun onCreatePreferences(bundle: Bundle?, s: String?) { addPreferencesFromResource(R.xml.settings_app) + loadSettings() // UI settings val prefResetHints = findPreference("reset_hints") @@ -98,7 +56,7 @@ class AppSettingsActivity: AppCompatActivity() { } // security settings - val prefDistrustSystemCerts = findPreference(App.DISTRUST_SYSTEM_CERTIFICATES) + val prefDistrustSystemCerts = findPreference(Settings.DISTRUST_SYSTEM_CERTIFICATES) prefDistrustSystemCerts.isVisible = BuildConfig.customCerts prefDistrustSystemCerts.isEnabled = true @@ -114,23 +72,23 @@ class AppSettingsActivity: AppCompatActivity() { } private fun loadSettings() { - val settings = requireNotNull(settings) - + val settings = Settings.getInstance(requireActivity()) + // connection settings - val prefOverrideProxy = findPreference(App.OVERRIDE_PROXY) as SwitchPreferenceCompat - prefOverrideProxy.isChecked = settings.getBoolean(App.OVERRIDE_PROXY, false) - prefOverrideProxy.isEnabled = settings.isWritable(App.OVERRIDE_PROXY) + val prefOverrideProxy = findPreference(Settings.OVERRIDE_PROXY) as SwitchPreferenceCompat + prefOverrideProxy.isChecked = settings.getBoolean(Settings.OVERRIDE_PROXY) ?: Settings.OVERRIDE_PROXY_DEFAULT + prefOverrideProxy.isEnabled = settings.isWritable(Settings.OVERRIDE_PROXY) - val prefProxyHost = findPreference(App.OVERRIDE_PROXY_HOST) as EditTextPreference - prefProxyHost.isEnabled = settings.isWritable(App.OVERRIDE_PROXY_HOST) - val proxyHost = settings.getString(App.OVERRIDE_PROXY_HOST, App.OVERRIDE_PROXY_HOST_DEFAULT) + val prefProxyHost = findPreference(Settings.OVERRIDE_PROXY_HOST) as EditTextPreference + prefProxyHost.isEnabled = settings.isWritable(Settings.OVERRIDE_PROXY_HOST) + val proxyHost = settings.getString(Settings.OVERRIDE_PROXY_HOST) ?: Settings.OVERRIDE_PROXY_HOST_DEFAULT prefProxyHost.text = proxyHost prefProxyHost.summary = proxyHost prefProxyHost.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> val host = newValue as String try { URI(null, host, null, null) - settings.putString(App.OVERRIDE_PROXY_HOST, host) + settings.putString(Settings.OVERRIDE_PROXY_HOST, host) prefProxyHost.summary = host true } catch(e: URISyntaxException) { @@ -139,16 +97,16 @@ class AppSettingsActivity: AppCompatActivity() { } } - val prefProxyPort = findPreference(App.OVERRIDE_PROXY_PORT) as EditTextPreference - prefProxyHost.isEnabled = settings.isWritable(App.OVERRIDE_PROXY_PORT) - val proxyPort = settings.getInt(App.OVERRIDE_PROXY_PORT, App.OVERRIDE_PROXY_PORT_DEFAULT) + val prefProxyPort = findPreference(Settings.OVERRIDE_PROXY_PORT) as EditTextPreference + prefProxyHost.isEnabled = settings.isWritable(Settings.OVERRIDE_PROXY_PORT) + val proxyPort = settings.getInt(Settings.OVERRIDE_PROXY_PORT) ?: Settings.OVERRIDE_PROXY_PORT_DEFAULT prefProxyPort.text = proxyPort.toString() prefProxyPort.summary = proxyPort.toString() prefProxyPort.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> try { val port = Integer.parseInt(newValue as String) if (port in 1..65535) { - settings.putInt(App.OVERRIDE_PROXY_PORT, port) + settings.putInt(Settings.OVERRIDE_PROXY_PORT, port) prefProxyPort.text = port.toString() prefProxyPort.summary = port.toString() true @@ -160,32 +118,27 @@ class AppSettingsActivity: AppCompatActivity() { } // security settings - val prefDistrustSystemCerts = findPreference(App.DISTRUST_SYSTEM_CERTIFICATES) as SwitchPreferenceCompat - prefDistrustSystemCerts.isChecked = settings.getBoolean(App.DISTRUST_SYSTEM_CERTIFICATES, false) + val prefDistrustSystemCerts = findPreference(Settings.DISTRUST_SYSTEM_CERTIFICATES) as SwitchPreferenceCompat + prefDistrustSystemCerts.isChecked = settings.getBoolean(Settings.DISTRUST_SYSTEM_CERTIFICATES) ?: Settings.DISTRUST_SYSTEM_CERTIFICATES_DEFAULT // debugging settings val prefLogToExternalStorage = findPreference(Logger.LOG_TO_EXTERNAL_STORAGE) as SwitchPreferenceCompat prefLogToExternalStorage.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> - val context = activity!! - Logger.initialize(context) - - // kill a potential :sync process, so that the new logger settings will be used - val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - am.runningAppProcesses.forEach { - if (it.pid != Process.myPid()) { - Logger.log.info("Killing ${it.processName} process, pid = ${it.pid}") - Process.killProcess(it.pid) - } - } true } } + override fun onSettingsChanged() { + loadSettings() + } + + private fun resetHints() { - settings?.remove(StartupDialogFragment.HINT_AUTOSTART_PERMISSIONS) - settings?.remove(StartupDialogFragment.HINT_BATTERY_OPTIMIZATIONS) - settings?.remove(StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED) - settings?.remove(StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED) + val settings = Settings.getInstance(requireActivity()) + settings.remove(StartupDialogFragment.HINT_AUTOSTART_PERMISSIONS) + settings.remove(StartupDialogFragment.HINT_BATTERY_OPTIMIZATIONS) + settings.remove(StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED) + settings.remove(StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED) Snackbar.make(view!!, R.string.app_settings_reset_hints_success, Snackbar.LENGTH_LONG).show() } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CollectionInfoFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/CollectionInfoFragment.kt index e87d7bdc7..445f60409 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CollectionInfoFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/CollectionInfoFragment.kt @@ -11,8 +11,8 @@ package at.bitfire.davdroid.ui import android.annotation.SuppressLint import android.app.Dialog import android.os.Bundle -import androidx.fragment.app.DialogFragment import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment import at.bitfire.davdroid.R import at.bitfire.davdroid.model.CollectionInfo import kotlinx.android.synthetic.main.collection_properties.view.* diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.kt index 3bbb611fd..e0f460e54 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.kt @@ -13,11 +13,11 @@ import android.content.Context import android.content.Intent import android.graphics.drawable.ColorDrawable import android.os.Bundle -import androidx.core.app.NavUtils -import androidx.appcompat.app.AppCompatActivity import android.view.Menu import android.view.MenuItem import android.widget.ArrayAdapter +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.NavUtils import androidx.loader.app.LoaderManager import androidx.loader.content.AsyncTaskLoader import androidx.loader.content.Loader @@ -29,8 +29,8 @@ import kotlinx.android.synthetic.main.activity_create_calendar.* import net.fortuna.ical4j.model.Calendar import okhttp3.HttpUrl import org.apache.commons.lang3.StringUtils -import yuku.ambilwarna.AmbilWarnaDialog import java.util.* +import yuku.ambilwarna.AmbilWarnaDialog class CreateCalendarActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt index 537cbce61..74f8f8714 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.kt @@ -19,14 +19,13 @@ import androidx.loader.content.AsyncTaskLoader import androidx.loader.content.Loader import at.bitfire.dav4android.DavResource import at.bitfire.dav4android.XmlUtils -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.ServiceDB -import at.bitfire.davdroid.settings.Settings +import at.bitfire.davdroid.settings.AccountSettings import java.io.IOException import java.io.StringWriter import java.util.logging.Level @@ -184,42 +183,40 @@ class CreateCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks< Logger.log.log(Level.SEVERE, "Couldn't assemble Extended MKCOL request", e) } - Settings.getInstance(context)?.use { settings -> - HttpClient.Builder(context, settings, AccountSettings(context, settings, account)) - .setForeground(true) - .build().use { httpClient -> - try { - val collection = DavResource(httpClient.okHttpClient, info.url) - - // create collection on remote server - collection.mkCol(writer.toString()) {} - - // now insert collection into database: - ServiceDB.OpenHelper(context).use { dbHelper -> - val db = dbHelper.writableDatabase - - // 1. find service ID - val serviceType = when (info.type) { - CollectionInfo.Type.ADDRESS_BOOK -> ServiceDB.Services.SERVICE_CARDDAV - CollectionInfo.Type.CALENDAR -> ServiceDB.Services.SERVICE_CALDAV - else -> throw IllegalArgumentException("Collection must be an address book or calendar") - } - db.query(ServiceDB.Services._TABLE, arrayOf(ServiceDB.Services.ID), - "${ServiceDB.Services.ACCOUNT_NAME}=? AND ${ServiceDB.Services.SERVICE}=?", - arrayOf(account.name, serviceType), null, null, null).use { c -> - - assert(c.moveToNext()) - val serviceID = c.getLong(0) - - // 2. add collection to service - val values = info.toDB() - values.put(ServiceDB.Collections.SERVICE_ID, serviceID) - db.insert(ServiceDB.Collections._TABLE, null, values) - } + HttpClient.Builder(context, AccountSettings(context, account)) + .setForeground(true) + .build().use { httpClient -> + try { + val collection = DavResource(httpClient.okHttpClient, info.url) + + // create collection on remote server + collection.mkCol(writer.toString()) {} + + // now insert collection into database: + ServiceDB.OpenHelper(context).use { dbHelper -> + val db = dbHelper.writableDatabase + + // 1. find service ID + val serviceType = when (info.type) { + CollectionInfo.Type.ADDRESS_BOOK -> ServiceDB.Services.SERVICE_CARDDAV + CollectionInfo.Type.CALENDAR -> ServiceDB.Services.SERVICE_CALDAV + else -> throw IllegalArgumentException("Collection must be an address book or calendar") + } + db.query(ServiceDB.Services._TABLE, arrayOf(ServiceDB.Services.ID), + "${ServiceDB.Services.ACCOUNT_NAME}=? AND ${ServiceDB.Services.SERVICE}=?", + arrayOf(account.name, serviceType), null, null, null).use { c -> + + assert(c.moveToNext()) + val serviceID = c.getLong(0) + + // 2. add collection to service + val values = info.toDB() + values.put(ServiceDB.Collections.SERVICE_ID, serviceID) + db.insert(ServiceDB.Collections._TABLE, null, values) } - } catch(e: Exception) { - return e } + } catch(e: Exception) { + return e } } return null diff --git a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt index 76f27d0de..46fa11923 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.kt @@ -34,14 +34,13 @@ import androidx.loader.app.LoaderManager import androidx.loader.content.AsyncTaskLoader import androidx.loader.content.Loader import at.bitfire.dav4android.exception.HttpException -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.InvalidAccountException import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.ServiceDB import at.bitfire.davdroid.resource.LocalAddressBook -import at.bitfire.davdroid.settings.Settings +import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.ical4android.TaskProvider import kotlinx.android.synthetic.main.activity_debug_info.* import org.dmfs.tasks.contract.TaskContract @@ -260,27 +259,24 @@ class DebugInfoActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks - for (acct in accountManager.getAccountsByType(context.getString(R.string.account_type))) { - try { - val accountSettings = AccountSettings(context, settings, acct) - report.append("Account: ${acct.name}\n" + - " Address book sync. interval: ${syncStatus(accountSettings, context.getString(R.string.address_books_authority))}\n" + - " Calendar sync. interval: ${syncStatus(accountSettings, CalendarContract.AUTHORITY)}\n" + - " OpenTasks sync. interval: ${syncStatus(accountSettings, TaskProvider.ProviderName.OpenTasks.authority)}\n" + - " WiFi only: ").append(accountSettings.getSyncWifiOnly()) - accountSettings.getSyncWifiOnlySSIDs()?.let { - report.append(", SSIDs: ${accountSettings.getSyncWifiOnlySSIDs()}") - } - report.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") - } catch (e: InvalidAccountException) { - report.append("$acct is invalid (unsupported settings version) or does not exist\n") + for (acct in accountManager.getAccountsByType(context.getString(R.string.account_type))) + try { + val accountSettings = AccountSettings(context, acct) + report.append("Account: ${acct.name}\n" + + " Address book sync. interval: ${syncStatus(accountSettings, context.getString(R.string.address_books_authority))}\n" + + " Calendar sync. interval: ${syncStatus(accountSettings, CalendarContract.AUTHORITY)}\n" + + " OpenTasks sync. interval: ${syncStatus(accountSettings, TaskProvider.ProviderName.OpenTasks.authority)}\n" + + " WiFi only: ").append(accountSettings.getSyncWifiOnly()) + accountSettings.getSyncWifiOnlySSIDs()?.let { + report.append(", SSIDs: ${accountSettings.getSyncWifiOnlySSIDs()}") } + report.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") + } catch (e: InvalidAccountException) { + report.append("$acct is invalid (unsupported settings version) or does not exist\n") } - } // address book accounts for (acct in accountManager.getAccountsByType(context.getString(R.string.account_type_address_book))) try { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/DefaultAccountsDrawerHandler.kt b/app/src/main/java/at/bitfire/davdroid/ui/DefaultAccountsDrawerHandler.kt index 7d6b1e618..6fdf45cfd 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DefaultAccountsDrawerHandler.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/DefaultAccountsDrawerHandler.kt @@ -9,6 +9,7 @@ package at.bitfire.davdroid.ui import android.app.Activity +import android.content.Context import android.content.Intent import android.net.Uri import android.view.Menu @@ -16,7 +17,6 @@ import android.view.MenuItem import at.bitfire.davdroid.App import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R -import at.bitfire.davdroid.settings.ISettings class DefaultAccountsDrawerHandler: IAccountsDrawerHandler { @@ -25,7 +25,7 @@ class DefaultAccountsDrawerHandler: IAccountsDrawerHandler { } - override fun onSettingsChanged(settings: ISettings?, menu: Menu) { + 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 } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.kt index 2638b2345..63ed48521 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.kt @@ -19,12 +19,11 @@ import androidx.loader.app.LoaderManager import androidx.loader.content.AsyncTaskLoader import androidx.loader.content.Loader import at.bitfire.dav4android.DavResource -import at.bitfire.davdroid.AccountSettings import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.R import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.ServiceDB -import at.bitfire.davdroid.settings.Settings +import at.bitfire.davdroid.settings.AccountSettings class DeleteCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks { @@ -83,24 +82,22 @@ class DeleteCollectionFragment: DialogFragment(), LoaderManager.LoaderCallbacks< override fun onStartLoading() = forceLoad() override fun loadInBackground(): Exception? { - Settings.getInstance(context)?.use { settings -> - HttpClient.Builder(context, settings, AccountSettings(context, settings, account)) - .setForeground(true) - .build().use { httpClient -> - try { - val collection = DavResource(httpClient.okHttpClient, collectionInfo.url) - - // delete collection from server - collection.delete(null) {} - - // delete collection locally - ServiceDB.OpenHelper(context).use { dbHelper -> - val db = dbHelper.writableDatabase - db.delete(ServiceDB.Collections._TABLE, "${ServiceDB.Collections.ID}=?", arrayOf(collectionInfo.id.toString())) - } - } catch(e: Exception) { - return e + HttpClient.Builder(context, AccountSettings(context, account)) + .setForeground(true) + .build().use { httpClient -> + try { + val collection = DavResource(httpClient.okHttpClient, collectionInfo.url) + + // delete collection from server + collection.delete(null) {} + + // delete collection locally + ServiceDB.OpenHelper(context).use { dbHelper -> + val db = dbHelper.writableDatabase + db.delete(ServiceDB.Collections._TABLE, "${ServiceDB.Collections.ID}=?", arrayOf(collectionInfo.id.toString())) } + } catch(e: Exception) { + return e } } return null diff --git a/app/src/main/java/at/bitfire/davdroid/ui/ExceptionInfoFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/ExceptionInfoFragment.kt index 2fab8e1a7..44d28f7d1 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/ExceptionInfoFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/ExceptionInfoFragment.kt @@ -12,8 +12,8 @@ import android.accounts.Account import android.app.Dialog import android.content.Intent import android.os.Bundle -import androidx.fragment.app.DialogFragment import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment import at.bitfire.dav4android.exception.HttpException import at.bitfire.davdroid.R import java.io.IOException diff --git a/app/src/main/java/at/bitfire/davdroid/ui/IAccountsDrawerHandler.kt b/app/src/main/java/at/bitfire/davdroid/ui/IAccountsDrawerHandler.kt index b0241de0f..77eecabfe 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/IAccountsDrawerHandler.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/IAccountsDrawerHandler.kt @@ -9,13 +9,13 @@ package at.bitfire.davdroid.ui import android.app.Activity +import android.content.Context import android.view.Menu import android.view.MenuItem -import at.bitfire.davdroid.settings.ISettings interface IAccountsDrawerHandler { - fun onSettingsChanged(settings: ISettings?, menu: Menu) + fun initMenu(context: Context, menu: Menu) fun onNavigationItemSelected(activity: Activity, item: MenuItem): Boolean diff --git a/app/src/main/java/at/bitfire/davdroid/ui/SettingsLoader.kt b/app/src/main/java/at/bitfire/davdroid/ui/SettingsLoader.kt deleted file mode 100644 index c412b7751..000000000 --- a/app/src/main/java/at/bitfire/davdroid/ui/SettingsLoader.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.ui - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.os.Handler -import android.os.IBinder -import androidx.loader.content.AsyncTaskLoader -import at.bitfire.davdroid.settings.ISettings -import at.bitfire.davdroid.settings.ISettingsObserver -import at.bitfire.davdroid.settings.Settings - -abstract class SettingsLoader( - context: Context -): AsyncTaskLoader(context) { - - val handler = Handler() - val settingsObserver = object: ISettingsObserver.Stub() { - override fun onSettingsChanged() { - handler.post { - onContentChanged() - } - } - } - - private var settingsSvc: ServiceConnection? = null - var settings: ISettings? = null - - override fun onStartLoading() { - if (settingsSvc != null) - forceLoad() - else { - val serviceConn = object: ServiceConnection { - override fun onServiceConnected(name: ComponentName?, binder: IBinder) { - settings = ISettings.Stub.asInterface(binder) - settings!!.registerObserver(settingsObserver) - onContentChanged() - } - - override fun onServiceDisconnected(name: ComponentName?) { - settings!!.unregisterObserver(settingsObserver) - settings = null - } - } - if (context.bindService(Intent(context, Settings::class.java), serviceConn, Context.BIND_AUTO_CREATE)) - settingsSvc = serviceConn - } - } - - override fun onReset() { - settingsSvc?.let { - context.unbindService(it) - settingsSvc = null - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt index 1e1bdc8d7..cf53aff30 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt @@ -21,18 +21,16 @@ import android.os.Bundle import android.os.PowerManager import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment -import androidx.loader.app.LoaderManager -import androidx.loader.content.Loader import at.bitfire.davdroid.App import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.resource.LocalTaskList -import at.bitfire.davdroid.settings.ISettings +import at.bitfire.davdroid.settings.Settings import java.util.* import java.util.logging.Level -class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks { +class StartupDialogFragment: DialogFragment() { enum class Mode { AUTOSTART_PERMISSIONS, @@ -56,33 +54,34 @@ class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks { + fun getStartupDialogs(context: Context): List { val dialogs = LinkedList() + val settings = Settings.getInstance(context) - if (System.currentTimeMillis() > settings.getLong(SETTING_NEXT_DONATION_POPUP, 0)) + if (System.currentTimeMillis() > settings.getLong(SETTING_NEXT_DONATION_POPUP) ?: 0) dialogs += StartupDialogFragment.instantiate(Mode.OSE_DONATE) // store-specific information /*if (BuildConfig.FLAVOR == App.FLAVOR_GOOGLE_PLAY) { // Play store if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && // only on Android <5 - settings.getBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, true)) // and only when "Don't show again" hasn't been clicked yet + settings.getBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED) != false) // and only when "Don't show again" hasn't been clicked yet dialogs += StartupDialogFragment.instantiate(Mode.GOOGLE_PLAY_ACCOUNTS_REMOVED) }*/ // battery optimization white-listing - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && settings.getBoolean(HINT_BATTERY_OPTIMIZATIONS, true)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && settings.getBoolean(HINT_BATTERY_OPTIMIZATIONS) != false) { val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager if (!powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)) dialogs.add(StartupDialogFragment.instantiate(Mode.BATTERY_OPTIMIZATIONS)) } // vendor-specific auto-start information - if (autostartManufacturers.contains(Build.MANUFACTURER.toLowerCase()) && settings.getBoolean(HINT_AUTOSTART_PERMISSIONS, true)) + if (autostartManufacturers.contains(Build.MANUFACTURER.toLowerCase()) && settings.getBoolean(HINT_AUTOSTART_PERMISSIONS) != false) dialogs.add(StartupDialogFragment.instantiate(Mode.AUTOSTART_PERMISSIONS)) // OpenTasks information - if (!LocalTaskList.tasksProviderAvailable(context) && settings.getBoolean(HINT_OPENTASKS_NOT_INSTALLED, true)) + if (!LocalTaskList.tasksProviderAvailable(context) && settings.getBoolean(HINT_OPENTASKS_NOT_INSTALLED) != false) dialogs.add(StartupDialogFragment.instantiate(Mode.OPENTASKS_NOT_INSTALLED)) return dialogs.reversed() @@ -98,30 +97,12 @@ class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks, result: ISettings?) { - settings = result - } - - override fun onLoaderReset(loader: Loader) { - settings = null - } - - + @SuppressLint("BatteryLife") override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { isCancelable = false + val settings = Settings.getInstance(requireActivity()) val activity = requireActivity() val mode = Mode.valueOf(arguments!!.getString(ARGS_MODE)!!) return when (mode) { @@ -136,7 +117,7 @@ class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks } .setNegativeButton(R.string.startup_dont_show_again) { _, _ -> - settings?.putBoolean(HINT_AUTOSTART_PERMISSIONS, false) + settings.putBoolean(HINT_AUTOSTART_PERMISSIONS, false) } .create() @@ -151,7 +132,7 @@ class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks } .setNegativeButton(R.string.startup_dont_show_again) { _: DialogInterface, _: Int -> - settings?.putBoolean(HINT_BATTERY_OPTIMIZATIONS, false) + settings.putBoolean(HINT_BATTERY_OPTIMIZATIONS, false) } .create() @@ -172,7 +153,7 @@ class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks } .setNegativeButton(R.string.startup_dont_show_again) { _, _ -> - settings?.putBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, false) + settings.putBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, false) } .create() } @@ -191,7 +172,7 @@ class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks } .setNegativeButton(R.string.startup_dont_show_again) { _: DialogInterface, _: Int -> - settings?.putBoolean(HINT_OPENTASKS_NOT_INSTALLED, false) + settings.putBoolean(HINT_OPENTASKS_NOT_INSTALLED, false) } .create() } @@ -205,26 +186,14 @@ class StartupDialogFragment: DialogFragment(), LoaderManager.LoaderCallbacks - settings?.putLong(SETTING_NEXT_DONATION_POPUP, System.currentTimeMillis() + 14 * 86400000L) // 14 days + settings.putLong(SETTING_NEXT_DONATION_POPUP, System.currentTimeMillis() + 14 * 86400000L) // 14 days } .create() } } - - class SettingsLoader( - context: Context - ): at.bitfire.davdroid.ui.SettingsLoader(context) { - - override fun loadInBackground(): ISettings? { - settings?.let { return it } - return null - } - - } - } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt index 93b1a1eab..d35305029 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt @@ -24,15 +24,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.loader.app.LoaderManager -import androidx.loader.content.Loader -import at.bitfire.davdroid.* +import at.bitfire.davdroid.Constants +import at.bitfire.davdroid.DavService +import at.bitfire.davdroid.InvalidAccountException +import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.model.ServiceDB.* import at.bitfire.davdroid.resource.LocalTaskList -import at.bitfire.davdroid.settings.ISettings -import at.bitfire.davdroid.ui.SettingsLoader -import at.bitfire.davdroid.ui.setup.AccountDetailsFragment.CreateSettings +import at.bitfire.davdroid.settings.AccountSettings +import at.bitfire.davdroid.settings.Settings import at.bitfire.ical4android.TaskProvider import at.bitfire.vcard4android.GroupMethod import com.google.android.material.snackbar.Snackbar @@ -41,7 +41,7 @@ import kotlinx.android.synthetic.main.login_account_details.view.* import java.lang.ref.WeakReference import java.util.logging.Level -class AccountDetailsFragment: Fragment(), LoaderManager.LoaderCallbacks { +class AccountDetailsFragment: Fragment() { companion object { const val KEY_CONFIG = "config" @@ -55,9 +55,6 @@ class AccountDetailsFragment: Fragment(), LoaderManager.LoaderCallbacks val name = v.account_name.text.toString() if (name.isEmpty()) v.account_name.error = getString(R.string.login_account_name_required) - else - settings?.let { - val idx = view!!.contact_group_method.selectedItemPosition - val groupMethodName = resources.getStringArray(R.array.settings_contact_group_method_values)[idx] - - v.create_account.visibility = View.GONE - v.create_account_progress.visibility = View.VISIBLE - - CreateAccountTask(requireActivity().applicationContext, WeakReference(requireActivity()), - it, name, - args.getParcelable(KEY_CONFIG) as DavResourceFinder.Configuration, - GroupMethod.valueOf(groupMethodName)).execute() - } - } + else { + val idx = view!!.contact_group_method.selectedItemPosition + val groupMethodName = resources.getStringArray(R.array.settings_contact_group_method_values)[idx] - LoaderManager.getInstance(this).initLoader(0, null, this) + v.create_account.visibility = View.GONE + v.create_account_progress.visibility = View.VISIBLE - return v - } + CreateAccountTask(requireActivity().applicationContext, WeakReference(requireActivity()), + name, + args.getParcelable(KEY_CONFIG) as DavResourceFinder.Configuration, + GroupMethod.valueOf(groupMethodName)).execute() + } + } - override fun onCreateLoader(code: Int, args: Bundle?) = - GroupMethodLoader(requireActivity()) - - override fun onLoadFinished(loader: Loader, result: CreateSettings?) { - settings = (result ?: return).settings - groupMethod = result.groupMethod - - view?.let { view -> - if (result.groupMethod != null) { - view.contact_group_method.isEnabled = false - for ((i, method) in resources.getStringArray(R.array.settings_contact_group_method_values).withIndex()) { - if (method == result.groupMethod.name) { - view.contact_group_method.setSelection(i) - break - } + val forcedGroupMethod = settings.getString(AccountSettings.KEY_CONTACT_GROUP_METHOD)?.let { GroupMethod.valueOf(it) } + if (forcedGroupMethod != null) { + v.contact_group_method.isEnabled = false + for ((i, method) in resources.getStringArray(R.array.settings_contact_group_method_values).withIndex()) { + if (method == forcedGroupMethod.name) { + v.contact_group_method.setSelection(i) + break } - } else - view.contact_group_method.isEnabled = true - - view.create_account.isEnabled = true - } - } + } + } else + v.contact_group_method.isEnabled = true - override fun onLoaderReset(loader: Loader) { - settings = null - groupMethod = null - view?.create_account?.isEnabled = false + return v } @SuppressLint("StaticFieldLeak") // we'll only keep the application Context class CreateAccountTask( - private val applicationContext: Context, + private val appContext: Context, private val activityRef: WeakReference, - private val settings: ISettings, private val accountName: String, private val config: DavResourceFinder.Configuration, @@ -146,24 +122,24 @@ class AccountDetailsFragment: Fragment(), LoaderManager.LoaderCallbacks() { override fun doInBackground(vararg params: Void?): Boolean { - val account = Account(accountName, applicationContext.getString(R.string.account_type)) + val account = Account(accountName, appContext.getString(R.string.account_type)) // create Android account val userData = AccountSettings.initialUserData(config.credentials) Logger.log.log(Level.INFO, "Creating Android account with initial config", arrayOf(account, userData)) - val accountManager = AccountManager.get(applicationContext) + val accountManager = AccountManager.get(appContext) if (!accountManager.addAccountExplicitly(account, config.credentials.password, userData)) return false // add entries for account to service DB Logger.log.log(Level.INFO, "Writing account configuration to database", config) - OpenHelper(applicationContext).use { dbHelper -> + OpenHelper(appContext).use { dbHelper -> val db = dbHelper.writableDatabase try { - val accountSettings = AccountSettings(applicationContext, settings, account) + val accountSettings = AccountSettings(appContext, account) - val refreshIntent = Intent(applicationContext, DavService::class.java) + val refreshIntent = Intent(appContext, DavService::class.java) refreshIntent.action = DavService.ACTION_REFRESH_COLLECTIONS if (config.cardDAV != null) { @@ -172,15 +148,15 @@ class AccountDetailsFragment: Fragment(), LoaderManager.LoaderCallbacks(context) { - - override fun loadInBackground(): CreateSettings? { - settings?.let { settings -> - var groupMethod: GroupMethod? = null - settings.getString(AccountSettings.KEY_CONTACT_GROUP_METHOD, null)?.let { - try { - groupMethod = GroupMethod.valueOf(it) - } catch (e: IllegalArgumentException) { - } - } - - return CreateSettings( - settings, - groupMethod - ) - } - return null - } - - } - -} +} \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/DavResourceFinder.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/DavResourceFinder.kt index f6884a9d2..f68e43866 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/DavResourceFinder.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/DavResourceFinder.kt @@ -24,7 +24,6 @@ import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.log.StringHandler import at.bitfire.davdroid.model.CollectionInfo import at.bitfire.davdroid.model.Credentials -import at.bitfire.davdroid.settings.Settings import okhttp3.HttpUrl import org.apache.commons.lang3.builder.ReflectionToStringBuilder import org.xbill.DNS.Lookup @@ -57,14 +56,12 @@ class DavResourceFinder( log.addHandler(logBuffer) } - private val settings = Settings.getInstance(context) - private val httpClient: HttpClient = HttpClient.Builder(context, settings, logger = log) + private val httpClient: HttpClient = HttpClient.Builder(context, logger = log) .addAuthentication(null, loginInfo.credentials) .setForeground(true) .build() override fun close() { - settings?.close() httpClient.close() } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginActivity.kt index 8cbbcaa70..83015daed 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginActivity.kt @@ -9,9 +9,9 @@ package at.bitfire.davdroid.ui.setup import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity import android.view.Menu import android.view.MenuItem +import androidx.appcompat.app.AppCompatActivity import at.bitfire.davdroid.App import at.bitfire.davdroid.R import at.bitfire.davdroid.ui.UiUtils diff --git a/app/src/main/java/at/bitfire/davdroid/ui/widget/IntEditTextPreference.kt b/app/src/main/java/at/bitfire/davdroid/ui/widget/IntEditTextPreference.kt index 4e0908e57..cac0c98bb 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/widget/IntEditTextPreference.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/widget/IntEditTextPreference.kt @@ -9,8 +9,8 @@ package at.bitfire.davdroid.ui.widget import android.content.Context -import androidx.preference.EditTextPreference import android.util.AttributeSet +import androidx.preference.EditTextPreference class IntEditTextPreference( context: Context, diff --git a/app/src/main/res/layout/login_account_details.xml b/app/src/main/res/layout/login_account_details.xml index a35c3e310..09b38376f 100644 --- a/app/src/main/res/layout/login_account_details.xml +++ b/app/src/main/res/layout/login_account_details.xml @@ -85,7 +85,6 @@ android:layout_width="0dp" android:layout_weight="1" android:text="@string/login_create_account" - android:enabled="false" style="@style/stepper_nav_button"/> Date: Wed, 26 Dec 2018 13:57:34 +0100 Subject: [PATCH 017/382] Replace AmbilWarna by ColorPicker --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 7 ------ .../davdroid/ui/CreateCalendarActivity.kt | 22 +++++++++++------ app/src/main/res/values/about.xml | 24 +++++++------------ gradle.properties | 5 +--- 5 files changed, 25 insertions(+), 35 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e781dc41e..ab20ea394 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -79,7 +79,7 @@ dependencies { implementation 'androidx.preference:preference:1.0.0' implementation 'com.google.android.material:material:1.0.0' - implementation 'com.github.yukuku:ambilwarna:2.0.1' + implementation 'com.jaredrummler:colorpicker:1.0.5' implementation 'com.mikepenz:aboutlibraries:6.2.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 56d7d91ca..e60075d68 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,11 +1,4 @@ - { +class CreateCalendarActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks, ColorPickerDialogListener { companion object { const val EXTRA_ACCOUNT = "account" @@ -49,16 +50,23 @@ class CreateCalendarActivity: AppCompatActivity(), LoaderManager.LoaderCallbacks setContentView(R.layout.activity_create_calendar) color.setOnClickListener { _ -> - AmbilWarnaDialog(this, (color.background as ColorDrawable).color, true, object: AmbilWarnaDialog.OnAmbilWarnaListener { - override fun onCancel(dialog: AmbilWarnaDialog) {} - override fun onOk(dialog: AmbilWarnaDialog, rgb: Int) = - color.setBackgroundColor(rgb) - }).show() + ColorPickerDialog.newBuilder() + .setShowAlphaSlider(false) + .setColor((color.background as ColorDrawable).color) + .show(this) } LoaderManager.getInstance(this).initLoader(0, null, this) } + override fun onColorSelected(dialogId: Int, rgb: Int) { + color.setBackgroundColor(rgb) + } + + override fun onDialogDismissed(dialogId: Int) { + // color selection dismissed + } + override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.activity_create_collection, menu) return true diff --git a/app/src/main/res/values/about.xml b/app/src/main/res/values/about.xml index 158f3b2b9..0ef12caab 100644 --- a/app/src/main/res/values/about.xml +++ b/app/src/main/res/values/about.xml @@ -1,22 +1,14 @@ - - - - Randy Sugianto - https://github.com/yukuku - AmbilWarna - This is a small library for your application to enable the users to select an arbitrary color. - https://github.com/yukuku/ambilwarna - true - apache_2_0 + + Jared Rummler, Daniel Nilsson + https://github.com/jaredrummler/ + Color Picker + Yet another open source color picker for Android. + https://github.com/jaredrummler/ColorPicker + true + apache_2_0 Apache Software Foundation diff --git a/gradle.properties b/gradle.properties index 17d0c8add..ad8917e96 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,2 @@ org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true - -# AmbilWarna doesn't use AndroidX yet -android.enableJetifier=true \ No newline at end of file +android.useAndroidX=true \ No newline at end of file -- GitLab From 07c5cdc75485b56c6054f638b8bcae8712f50743 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 30 Dec 2018 11:16:23 +0100 Subject: [PATCH 018/382] =?UTF-8?q?Rename=20DAVdroid=20to=20DAVx=E2=81=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 ++++++++-------- app/src/main/res/values-ar/strings.xml | 28 +++++++++--------- app/src/main/res/values-ca/strings.xml | 8 +++--- app/src/main/res/values-cs/strings.xml | 24 ++++++++-------- app/src/main/res/values-da/strings.xml | 28 +++++++++--------- app/src/main/res/values-de/strings.xml | 26 ++++++++--------- app/src/main/res/values-es/strings.xml | 26 ++++++++--------- app/src/main/res/values-fr/strings.xml | 28 +++++++++--------- app/src/main/res/values-hu/strings.xml | 26 ++++++++--------- app/src/main/res/values-it/strings.xml | 26 ++++++++--------- app/src/main/res/values-ja/strings.xml | 28 +++++++++--------- app/src/main/res/values-nb-rNO/strings.xml | 26 ++++++++--------- app/src/main/res/values-nl/strings.xml | 24 ++++++++-------- app/src/main/res/values-pl/strings.xml | 28 +++++++++--------- app/src/main/res/values-pt-rBR/strings.xml | 28 +++++++++--------- app/src/main/res/values-ru/strings.xml | 28 +++++++++--------- app/src/main/res/values-sr/strings.xml | 6 ++-- app/src/main/res/values-tr-rTR/strings.xml | 18 ++++++------ app/src/main/res/values-uk/strings.xml | 28 +++++++++--------- app/src/main/res/values-zh-rCN/strings.xml | 26 ++++++++--------- app/src/main/res/values-zh-rTW/strings.xml | 26 ++++++++--------- app/src/main/res/values/strings.xml | 33 +++++++++++----------- 22 files changed, 271 insertions(+), 272 deletions(-) diff --git a/README.md b/README.md index 94684a5b0..09d6cd8ba 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,25 @@ -[![build status](https://gitlab.com/bitfireAT/davdroid/badges/master-ose/build.svg)](https://gitlab.com/bitfireAT/davdroid/commits/master-ose) +[![build status](https://gitlab.com/bitfireAT/davx5/badges/master-ose/build.svg)](https://gitlab.com/bitfireAT/davx5/commits/master-ose) -DAVdroid +DAVx⁵ ======== -Please see the [DAVdroid Web site](https://www.davdroid.com) for -comprehensive information about DAVdroid. +Please see the [DAVx⁵ Web site](https://www.davx5.com) for +comprehensive information about DAVx⁵. -DAVdroid is licensed under the [GPLv3 License](LICENSE). +DAVx⁵ is licensed under the [GPLv3 License](LICENSE). -News and updates: [@davdroidapp](https://twitter.com/davdroidapp) on Twitter +News and updates: [@davx5app](https://twitter.com/davx5dapp) on Twitter -Help and discussion: [DAVdroid forums](https://www.davdroid.com/forums/) +Help and discussion: [DAVx⁵ forums](https://www.davx5.com/forums/) -**If you want to support DAVdroid, please consider [donating to DAVdroid](https://www.davdroid.com/donate/) -or [purchasing it](https://www.davdroid.com/download/).** +**If you want to support DAVx⁵, please consider [donating to DAVx⁵](https://www.davx5.com/donate/) +or [purchasing it](https://www.davx5.com/download/).** -Generated KDoc: https://bitfireAT.gitlab.io/davdroid/dokka/app/ +Generated KDoc: https://bitfireAT.gitlab.io/davx5/dokka/app/ -Parts of DAVdroid have been outsourced into these libraries: +Parts of DAVx⁵ have been outsourced into these libraries: * [cert4android](https://gitlab.com/bitfireAT/cert4android) – custom certificate management * [dav4android](https://gitlab.com/bitfireAT/dav4android) – WebDAV/CalDav/CardDAV framework @@ -30,7 +30,7 @@ Parts of DAVdroid have been outsourced into these libraries: USED THIRD-PARTY LIBRARIES ========================== -Those libraries are used by DAVdroid (alphabetically): +Those libraries are used by DAVx⁵ (alphabetically): * [Ambilwarna](https://github.com/yukuku/ambilwarna) – [Apache License, Version 2.0](https://github.com/yukuku/ambilwarna/blob/master/LICENSE) * [dnsjava](http://www.xbill.org/dnsjava/) – [BSD License](http://www.xbill.org/dnsjava/dnsjava-current/LICENSE) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 56d98f2a9..c4c5a261b 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - DAVdroid دفتر عناوين + DAVx⁵ + DAVx⁵ دفتر عناوين دفاتر العناوين مساعدة إدارة الحسابات @@ -18,20 +18,20 @@ مزامنة تلقائية %s البرنامج الثابت للجهاز قد يمنع المزامنة التلقائية. في هذه الحالة أعط الإذن بالمزامنة التلقائية في إعدادات النظام لديك. مزامنة مجدولة - سيقوم جهازك بتقييد مزامنة DAVdroid . قم بإطفاء \"تحسين البطارية\" لإجبار مدد منتظمة لمزامنة DAVdroid . - التعطيل لـ DAVdroid + سيقوم جهازك بتقييد مزامنة DAVx⁵ . قم بإطفاء \"تحسين البطارية\" لإجبار مدد منتظمة لمزامنة DAVx⁵ . + التعطيل لـ DAVx⁵ لاتعرضه مرة أخرى ليس الآن معلومات المصدر المفتوح - يسعدنا كونك تستخدم DAVdroid ، البرنامج ذو المصدر المفتوح (GPLv3). تطوير DAVdroid عمل مضنٍ أخذ منا آلاف ساعات العمل ، هلّا تبرعت لنا ؟ + يسعدنا كونك تستخدم DAVx⁵ ، البرنامج ذو المصدر المفتوح (GPLv3). تطوير DAVx⁵ عمل مضنٍ أخذ منا آلاف ساعات العمل ، هلّا تبرعت لنا ؟ عرض صفحة التبرُّع ربما لاحقاً معلومات علة DRM في متجر Play - في ظروف معيّنة، DRM في متجر Play قد تتسبب في إتلاف جميع حسابات DAVdroid بعد إعادة تشغيل الجهاز أو حتى تحديث DAVdroid. فضلاً ثبّت برنامج \"DAVdroid JB Workaround\" من متجر Play إذا تأثرت بهذه المشكلة (فقط في هذه الحالة). + في ظروف معيّنة، DRM في متجر Play قد تتسبب في إتلاف جميع حسابات DAVx⁵ بعد إعادة تشغيل الجهاز أو حتى تحديث DAVx⁵. فضلاً ثبّت برنامج \"DAVx⁵ JB Workaround\" من متجر Play إذا تأثرت بهذه المشكلة (فقط في هذه الحالة). المزيد من المعلومات OpenTasks غير مثبت تطبيق OpenTasks المجاني مطلوب لمزامنة المهام. (غير مطلوب لمزامنة جهات الاتصال أو الأحداث) - بعد تثبيت OpenTasks، لابد لك من إعادة تثبيت DAVdroid وإضافة حساباتك مجدداً (علة في آندرويد). + بعد تثبيت OpenTasks، لابد لك من إعادة تثبيت DAVx⁵ وإضافة حساباتك مجدداً (علة في آندرويد). تثبيت OpenTasks المكتبات @@ -40,7 +40,7 @@ هذا الإصدار صالح للتوزيع عبر Google Play فقط. يقدَّم هذا البرنامج دون أدنى مسؤولية. إنه برنامج حر، وندعوك لإعادة توزيعه حسب أحكام محددة. - تسجيل ملفات DAVdroid + تسجيل ملفات DAVx⁵ التسجيل في التخزين الخارجي: %s لا يمكن إنشاء ملف سجل خارجي: %s لم نجِد أي تخزين خارجي @@ -58,7 +58,7 @@ الأسئلة الشائعة المساعدة / المنتدى تبرَّع - مرحباً بك في DAVdroid !\n\n يمكنك إضافة حساب CalDAV أو CardDAV الآن. + مرحباً بك في DAVx⁵ !\n\n يمكنك إضافة حساب CalDAV أو CardDAV الآن. تم تعطيل المزامنة التلقائية على مستوى النظام تفعيل @@ -197,8 +197,8 @@ الأحداث التي جرت بعد عدد الأيام هذا في الماضي سيتم تجاهلها (يمكن أن يكون 0). أتركه فارغاً لمزامنة جميع الأحداث. إدارة ألوان التقاويم - ألوان التقاويم يديرها DAVdroid - ألوان التقاويم لا يقوم DAVdroid بضبطها + ألوان التقاويم يديرها DAVx⁵ + ألوان التقاويم لا يقوم DAVx⁵ بضبطها دعم ألوان الأحداث زامن ألوان الأحداث لا تزامن ألوان الأحداث @@ -244,7 +244,7 @@ تم تجاهل تغيير في جهة الاتصال المحلية %d تم تجاهل تغيير في جهة الاتصال المحلية %d - أذونات DAVdroid + أذونات DAVx⁵ مطلوب أذونات إضافية OpenTasks قديم للغاية الإصدار المطلوب:%1$s (الموجود %2$s) @@ -255,6 +255,6 @@ إعادة المحاولة عرض العنصر - DAVdroid: أمن الاتصال - عثر DAVdroid على شهادة غير معروفة. هل تريد الوثوق بها؟ + DAVx⁵: أمن الاتصال + عثر DAVx⁵ على شهادة غير معروفة. هل تريد الوثوق بها؟ diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 4fe475d01..f2b72987e 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - DAVdroid Llibreta d’Adreces + DAVx⁵ + DAVx⁵ Llibreta d’Adreces Llibreta d’adreces Ajuda Gestiona comptes @@ -14,10 +14,10 @@ Xarxa i errors E/S Estat dels misatges - Desactiva per DAVdroid + Desactiva per DAVx⁵ No mostrar de nou Informació de Codi Obert - Som feliços de que utilitzis DAVdroid, el qual és programari lliure (GPLv3). Desenvolupar DAVdroid ens porte moltes hores i molta feina, si us plau, considera fer una donació. + Som feliços de que utilitzis DAVx⁵, el qual és programari lliure (GPLv3). Desenvolupar DAVx⁵ ens porte moltes hores i molta feina, si us plau, considera fer una donació. Mostra la pàgina de donació Més tard Informació d\'errors DRM a Play Store diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 9d155d260..5da167cac 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -1,27 +1,27 @@ - DAVdroid + DAVx⁵ Pomoc Spravovat účty Chvíli strpení … Odeslat - Vypnout pro DAVdroid + Vypnout pro DAVx⁵ Již nezobrazovat Open Source informace - Jsme velice rádi že používáte DAVdroid, software s otevřeným zdrojovým kódem (GPLv3). Vývoj této aplikace je náročný a trval již několik tisíc hodin, velice nás potěší přispějete-li na jeho vývoj. + Jsme velice rádi že používáte DAVx⁵, software s otevřeným zdrojovým kódem (GPLv3). Vývoj této aplikace je náročný a trval již několik tisíc hodin, velice nás potěší přispějete-li na jeho vývoj. Zobrazit stránku pro obdarování Možná později Informace o chybě DRM Obchodu Play - Za určitých podmínek může dojít po restartu nebo aktualizaci aplikace DAVdroid k vymazání účtů kvůli chybě DRM Obchodu Play. Pokud jste postiženi touto chybou (ale pouze v tomto případě), nainstalujte prosím z Obchodu Play aplikaci \"DAVdroid JB Workaround\". + Za určitých podmínek může dojít po restartu nebo aktualizaci aplikace DAVx⁵ k vymazání účtů kvůli chybě DRM Obchodu Play. Pokud jste postiženi touto chybou (ale pouze v tomto případě), nainstalujte prosím z Obchodu Play aplikaci \"DAVx⁵ JB Workaround\". OpenTasks není nainstalován - Po instalaci OpenTasks musíte PŘEINSTALOVAT DAVdroid a přidat znovu své účty (Android chyba). + Po instalaci OpenTasks musíte PŘEINSTALOVAT DAVx⁵ a přidat znovu své účty (Android chyba). Nainstalovat OpenTasks Tento program je distribuován BEZ JAKÉKOLIV ZÁRUKY. Je to volně dostupný software a lze jej za určitých podmínek dále distribuovat. - DAVdroid logování do souboru + DAVx⁵ logování do souboru Logování do externího úložiště: %s Nelze vytvořit externí soubor logu: %s Externí úložiště nenalezeno @@ -36,7 +36,7 @@ Webová stránka FAQ Obdarovat - Vítejte v aplikaci DAVdroid!\n\nNyní můžete přidat CalDAV/CardDAV účet. + Vítejte v aplikaci DAVx⁵!\n\nNyní můžete přidat CalDAV/CardDAV účet. Vyhledání služby selhalo Nelze obnovit seznam sbírky @@ -141,8 +141,8 @@ Události z minulosti starší než vyznačený počet dnů budou ignorovány (lze zadat 0). Ponechte prázdné pro synchronizaci všech událostí. Spravovat barvy kalendářů - Barvy kalendářů spravuje DAVdroid - Barvy kalendářů nespravuje DAVdroid + Barvy kalendářů spravuje DAVx⁵ + Barvy kalendářů nespravuje DAVx⁵ Vytvořit adresář Můj adresář @@ -171,9 +171,9 @@ Zobrazit detaily Ladící informace - DAVdroid oprávnění + DAVx⁵ oprávnění Vyžadována dodatečná oprávnění - DAVdroid: Zabezpečení připojení - DAVdroid nalezl neznámý certifikát. Chcete mu důvěřovat? + DAVx⁵: Zabezpečení připojení + DAVx⁵ nalezl neznámý certifikát. Chcete mu důvěřovat? diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index bf0df440d..7d5be5a27 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - DAVdroid adressebog + DAVx⁵ + DAVx⁵ adressebog Adressebøger Hjælp Administrere konti @@ -18,20 +18,20 @@ Automatisk synkronisering %s firmware blokerer ofte automatisk synkronisering. Tillad automatisk synkronisering i Android opsætning. Planlagt synkronisering - Enheden vil forhindre DAVdroid synkronisering. For at håndhæve regelmæssige DAVdroid synkroniseringsintervaller sluk for \"batterioptimering\". - Deaktivere DAVdroid + Enheden vil forhindre DAVx⁵ synkronisering. For at håndhæve regelmæssige DAVx⁵ synkroniseringsintervaller sluk for \"batterioptimering\". + Deaktivere DAVx⁵ Vis ikke igen Ikke nu Open-Source information - Det glæder os, at du bruger DAVdroid, som er open source-software (GPLv3). Det er hårdt arbejde at udvikle DAVdroid og taget tusindvis af arbejdstimer, så overvej at donere til projektet. + Det glæder os, at du bruger DAVx⁵, som er open source-software (GPLv3). Det er hårdt arbejde at udvikle DAVx⁵ og taget tusindvis af arbejdstimer, så overvej at donere til projektet. Vis donationsside Måske senere Play Store DRM fejl information - Under visse tekniske omstændigheder kan DRM fra Play Store bevirke, at alle DAVdroid-konti er væk efter en genstart eller opgradering af DAVdroid. Hvis du er udsat for dette problem (og ellers ikke), opfordres du til at installere DAVDroid JB Workaround\" fra Play Store. + Under visse tekniske omstændigheder kan DRM fra Play Store bevirke, at alle DAVx⁵-konti er væk efter en genstart eller opgradering af DAVx⁵. Hvis du er udsat for dette problem (og ellers ikke), opfordres du til at installere DAVDroid JB Workaround\" fra Play Store. Mere information OpenTasks ikke installeret Du er nødt til at have den gratis app OpenTasks for at kunne synkronisere opgaver. (Ikke påkrævet for kontakter/begivenheder.) - Efter at have installeret OpenTasks, vil du være nødt til at GENINSTALLERE DAVdroid og dine konti igen (en fejl i Android). + Efter at have installeret OpenTasks, vil du være nødt til at GENINSTALLERE DAVx⁵ og dine konti igen (en fejl i Android). Installere OpenTasks Biblioteker @@ -40,7 +40,7 @@ Denne version er kun berettiget til distribution via Google Play. Dette program leveres ABSOLUT UDEN GARANTI. Det er fri software, og du er velkommen til at videredistribuere det under visse betingelse. - DAVdroid fil-logning + DAVx⁵ fil-logning Logger til eksternt datalager: %s Kunne ikke oprette ekstern logfil: %s Eksternt lager ikke fundet @@ -58,7 +58,7 @@ OSS Hjælp / fora Donation - Velkommen til DAVdroid!\n\nDu kan nu tilføje en CalDAV/CardDAV konto. + Velkommen til DAVx⁵!\n\nDu kan nu tilføje en CalDAV/CardDAV konto. Automatisk synkronisering på tværs af systemet er deaktiveret Aktivere @@ -193,8 +193,8 @@ Begivenheder, som er mere end dette antal dage gamle vil blive ignoreret (kan også være 0). Hvis feltet ikke er udfyldt, vil alle begivenheder blive synkroniseret. Administrer farver for kalender - Kalenderfarver administreres af DAVdroid - Kalenderfarver sættes ikke fra DAVdroid + Kalenderfarver administreres af DAVx⁵ + Kalenderfarver sættes ikke fra DAVx⁵ Farver for begivenheder Synkroniser farver for begivenheder Synkroniser ikke farver for begivenheder @@ -236,7 +236,7 @@ Lokal ændring slettet %d lokale ændringer slettet - DAVdroid-rettigheder + DAVx⁵-rettigheder Yderligere adgang påkrævet OpenTasks-version for gammel Påkrævet version: %1$s (aktuelt%2$s) @@ -247,6 +247,6 @@ Gentag Vis element - DAVdroid: Forbindelsessikkerhed - DAVdroid er stødt på et ukendt certifikat. Vil du stole på det? + DAVx⁵: Forbindelsessikkerhed + DAVx⁵ er stødt på et ukendt certifikat. Vil du stole på det? diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index bd70c9c5f..44c1a225b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - DAVdroid-Adressbuch + DAVx⁵ + DAVx⁵-Adressbuch Adressbücher Hilfe Konten verwalten @@ -18,20 +18,20 @@ Automatische Synchronisierung %s-Firmware verhindert oftmals das automatische Synchronisieren. In diesem Fall müssen Sie das automatische Synchronisieren in den Android-Einstellung erlauben. Geplante Synchronisierung - Ihr Gerät wird die DAVdroid- Synchronisierung einschränken. Damit regelmäßig synchronisiert werden kann, muss die »Akku-Leistungsoptimierung« deaktiviert werden. - Für DAVdroid deaktivieren + Ihr Gerät wird die DAVx⁵- Synchronisierung einschränken. Damit regelmäßig synchronisiert werden kann, muss die »Akku-Leistungsoptimierung« deaktiviert werden. + Für DAVx⁵ deaktivieren Nicht mehr anzeigen Später Open-Source-Information - Es freut uns, dass Sie DAVdroid und damit Open-Source-Software (GPLv3) verwenden. Da in DAVdroid tausende Stunden harter Arbeit stecken und es immer noch weiter entwickelt wird, gibt es die Möglichkeit, uns zu spenden. + Es freut uns, dass Sie DAVx⁵ und damit Open-Source-Software (GPLv3) verwenden. Da in DAVx⁵ tausende Stunden harter Arbeit stecken und es immer noch weiter entwickelt wird, gibt es die Möglichkeit, uns zu spenden. Spendenseite anzeigen Vielleicht später Möglicher Play Store DRM-Fehler - Unter bestimmten Umständen verursacht die zwangsweise Play Store-Verschlüsselung, dass Accounts nach einem Neustart oder einem DAVdroid-Update verschwinden. Installieren Sie \"DAVdroid JB Workaround\", falls Sie von dem Problem betroffen sind. + Unter bestimmten Umständen verursacht die zwangsweise Play Store-Verschlüsselung, dass Accounts nach einem Neustart oder einem DAVx⁵-Update verschwinden. Installieren Sie \"DAVx⁵ JB Workaround\", falls Sie von dem Problem betroffen sind. Mehr Infos OpenTasks nicht installiert Für die Synchronisierung von Aufgaben wird die freie App OpenTasks benötigt. (Nicht benötigt für Kontakte/Termine.) - Nach der Installation von OpenTasks muss DAVdroid NEU INSTALLIERT und die Konten neu hinzugefügt werden (Android-Bug). + Nach der Installation von OpenTasks muss DAVx⁵ NEU INSTALLIERT und die Konten neu hinzugefügt werden (Android-Bug). OpenTasks installieren Bibliotheken @@ -40,7 +40,7 @@ Diese Version ist nur zur Verteilung über Google Play bestimmt. Dieses Programm wird OHNE JEDE GEWÄHRLEISTUNG bereitgestellt. Es ist freie Software – Sie können es also unter bestimmten Bedingungen weiterverbreiten. - DAVdroid Datei-Protokollierung + DAVx⁵ Datei-Protokollierung Protokollierung auf externen Speicher: %s Protokoll konnte nicht angelegt werden: %s Kein externer Speicher gefunden @@ -193,8 +193,8 @@ Termine, die mehr als diese Anzahl von Tagen in der Vergangenheit liegen, werden ignoriert (kann 0 sein). Feld leer lassen, um alle Termine zu synchronisieren. Kalenderfarben verwalten - Kalenderfarben werden von DAVdroid verwaltet - DAVdroid setzt keine Kalenderfarben + Kalenderfarben werden von DAVx⁵ verwaltet + DAVx⁵ setzt keine Kalenderfarben Unterstützung für Terminfarben Terminfarben werden synchronisiert Terminfarben werden nicht synchronisiert @@ -236,7 +236,7 @@ Lokale Kontakt-Änderung verworfen %d lokale Kontakt-Änderungen verworfen - DAVdroid-Berechtigungen + DAVx⁵-Berechtigungen Zusätzliche Berechtigungen benötigt OpenTasks zu alt Version %1$s benötigt (derzeit %2$s) @@ -247,6 +247,6 @@ Wiederholen Untersuchen - DAVdroid: Verbindungssicherheit - DAVdroid ist auf ein unbekanntes Zertifikat gestoßen. Ist dieses vertrauenswürdig? + DAVx⁵: Verbindungssicherheit + DAVx⁵ ist auf ein unbekanntes Zertifikat gestoßen. Ist dieses vertrauenswürdig? diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 6b29ed14d..e3547f10a 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - Agenda DAVdroid + DAVx⁵ + Agenda DAVx⁵ Agendas Ayuda Administrar cuentas @@ -15,23 +15,23 @@ Errores de Red y E/S Mensajes de estado - Apagar para DAVdroid + Apagar para DAVx⁵ No mostrar de nuevo Información de código abierto - Nos alegra que uses DAVdroid, que es software de código abierto (GPLv3). Debido al duro trabajo que supone el desarrollo de DAVdroid y los cientos de horas de trabajo, por favor, considera hacer una donación. + Nos alegra que uses DAVx⁵, que es software de código abierto (GPLv3). Debido al duro trabajo que supone el desarrollo de DAVx⁵ y los cientos de horas de trabajo, por favor, considera hacer una donación. Mostrar página de donación Quizás luego Información de error de DRM de Play Store - Bajo ciertas condiciones, el DRM de Play Store puede causar que todas las cuentas de DAVdroid se desconfiguren tras un reinicio o una actualización de DAVdroid. Si esto le afecta (y sólo en ese caso), por favor, instale \"DAVdroid JB Workaround\" desde Play Store. + Bajo ciertas condiciones, el DRM de Play Store puede causar que todas las cuentas de DAVx⁵ se desconfiguren tras un reinicio o una actualización de DAVx⁵. Si esto le afecta (y sólo en ese caso), por favor, instale \"DAVx⁵ JB Workaround\" desde Play Store. Más información OpenTasks no está instalado Para sincronizar tareas, la aplicación libre OpenTasks es requerida. (No necesaria para contactos/eventos.) - Tras instalar OpenTasks, tendrás que re-instalar DAVdroid y añadir tus cuentas de nuevo (por un error de Android). + Tras instalar OpenTasks, tendrás que re-instalar DAVx⁵ y añadir tus cuentas de nuevo (por un error de Android). Instalar OpenTasks Este programa viene sin NINGÚN TIPO DE GARANTÍA. Es software libre, y cualquier contribución es bienvenida y redistribuida bajo ciertas condiciones. - Archivo de registro de DAVdroid + Archivo de registro de DAVx⁵ Registrar en almacenamiento externo: %s No se puede crear el archivo de registro externo: %s Almacenamiento externo no encontrado @@ -49,7 +49,7 @@ Preguntas frequentes Ayuda / Foros Donar - Bienvenido a DAVdroid!\n\nAhora puedes añadir una cuenta CalDAV/CardDAV. + Bienvenido a DAVx⁵!\n\nAhora puedes añadir una cuenta CalDAV/CardDAV. Sincronización automática del sistema completo está deshabilitada Activar @@ -173,8 +173,8 @@ Los eventos anteriores a este número de días serán ignorados (puede ser 0). Deja en blanco el campo para sincronizar todos los eventos. Colores de calendario - Los colores de los calendarios son administrados por DAVdroid - Los colores de los calendarios no son establecidos por DAVdroid + Los colores de los calendarios son administrados por DAVx⁵ + Los colores de los calendarios no son establecidos por DAVx⁵ Soporte de colores en eventos Sincronizar colores de eventos No sincronizar colores de eventos @@ -213,7 +213,7 @@ Cambio de contacto local descartado %d cambios de contactos locales descartados - Permisos de DAVdroid + Permisos de DAVx⁵ Permisos adicionales requeridos OpenTasks muy viejo Versión requerida: %1$s (actual %2$s) @@ -224,6 +224,6 @@ Reintentar Ver item - DAVdroid: Seguridad de conexión - DAVdroid ha encontrado un certificado desconocido. ¿Quieres que sea válido? + DAVx⁵: Seguridad de conexión + DAVx⁵ ha encontrado un certificado desconocido. ¿Quieres que sea válido? diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6c3e0fff7..2a5e5e0ec 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - Carnet d\'adresses DAVdroid + DAVx⁵ + Carnet d\'adresses DAVx⁵ Carnets d\'adresses Aide Gestion des comptes @@ -18,20 +18,20 @@ Synchronisation automatique %s le matériel bloque souvent la synchronisation automatique. Dans ce cas, autorisez la synchronisation automatique dans vos paramètres d\'Android. Synchronisation planifiée - Votre appareil va restreindre la synchronisation DAVdroid. Pour forcer des intervalles de synchronisation DAVdroid plus réguliers, enlever l\'option \"optimisation de batterie\" - Désactiver pour DAVdroid + Votre appareil va restreindre la synchronisation DAVx⁵. Pour forcer des intervalles de synchronisation DAVx⁵ plus réguliers, enlever l\'option \"optimisation de batterie\" + Désactiver pour DAVx⁵ Ne plus afficher Pas maintenant Open-Source Information - Nous sommes heureux que vous utilisiez DAVdroid, qui est un logiciel open-source (GPLv3). Parce que développer DAVdroid est un travail difficile, qui nous a pris de nombreuses heures, nous vous invitons à faire un don. + Nous sommes heureux que vous utilisiez DAVx⁵, qui est un logiciel open-source (GPLv3). Parce que développer DAVx⁵ est un travail difficile, qui nous a pris de nombreuses heures, nous vous invitons à faire un don. Faire un don Plus tard Erreur information Play Store DRM - Dans certaines conditions, Play Store DRM peut provoquer la disparition de tous les comptes DAVdroid après un redémarrage ou après la mise à niveau de DAVdroid. Si vous êtes concerné par ce problème (et seulement dans ce cas), s\'il vous plaît installer \"DAVdroid JB Solution\" du Play Store. + Dans certaines conditions, Play Store DRM peut provoquer la disparition de tous les comptes DAVx⁵ après un redémarrage ou après la mise à niveau de DAVx⁵. Si vous êtes concerné par ce problème (et seulement dans ce cas), s\'il vous plaît installer \"DAVx⁵ JB Solution\" du Play Store. Plus d\'informations L\'application OpenTasks n\'est pas installée Pour synchroniser les tâches, l\'application gratuite OpenTasks est nécessaire (elle ne l\'est pas pour les contacts et les événements). - Après l\'installation OpenTasks, vous devez RE-INSTALLER DAVdroid et ajoutez vos comptes à nouveau (bug Android). + Après l\'installation OpenTasks, vous devez RE-INSTALLER DAVx⁵ et ajoutez vos comptes à nouveau (bug Android). Installer OpenTasks Librairies @@ -40,7 +40,7 @@ Cette version ne peut être distribuée que sur Google Play. Ce programme est fourni sans AUCUNE GARANTIE. C\'est un logiciel libre, et vous êtes en droit de le redistribuer sous certaines conditions. - DAVdroid fichier de journalisation + DAVx⁵ fichier de journalisation Se connecter au stockage externe: %s Impossible de créer le fichier journal externe: %s Stockage externe introuvable @@ -58,7 +58,7 @@ Foire aux questions Aide/Forum Faire un don - Bienvenue sur DAVdroid!\n\nVous pouvez maintenant ajouter un compte CalDAV ou CardDAV. + Bienvenue sur DAVx⁵!\n\nVous pouvez maintenant ajouter un compte CalDAV ou CardDAV. La synchronisation automatique globale est désactivée Activer @@ -193,8 +193,8 @@ Les événements antérieurs à ce nombre de jours seront ignorés (peut être 0). Laissez vide pour synchroniser tous les événements. Choisir couleur du calendrier - Les couleurs de calendrier sont gérées par DAVdroid - Les couleurs de calendrier ne sont pas gérées par DAVdroid + Les couleurs de calendrier sont gérées par DAVx⁵ + Les couleurs de calendrier ne sont pas gérées par DAVx⁵ Couleur associée aux événements Synchroniser la couleur associée aux événements Ne pas synchroniser la couleur associée aux événements @@ -232,7 +232,7 @@ Infos de débogage Carnet d\'adresse en lecture seulement - Autorisations DAVdroid + Autorisations DAVx⁵ Autorisations supplémentaires demandées Version d\'OpenTask trop ancienne Version requise %1$s (actuellement %2$s) @@ -243,6 +243,6 @@ Essayez à nouveau Voir l\'élément - DAVdroid : Sécurité de la connexion - DAVdroid a rencontré un certificat inconnu. Voulez-vous lui faire confiance? + DAVx⁵ : Sécurité de la connexion + DAVx⁵ a rencontré un certificat inconnu. Voulez-vous lui faire confiance? diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 8cdbf7179..d5fd0cb71 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - DAVdroid címjegyzék + DAVx⁵ + DAVx⁵ címjegyzék Címjegyzékek Súgó Fiókok kezelése @@ -18,20 +18,20 @@ Automatikus szinkronizálás A(z) %s gyári szoftvere gyakran blokkolja az automatikus szinkronizálást. Ebben az esetben engedélyezze a szinkronizálást az Android beállítások között. Időzített szinkronizálás - Az eszköz korlátozni fogja a DAVdroid szinkronizálást. A szokásos DAVadroid szinkronizációs ciklus biztosítása érdekében vonja ki a DAVdroid-ot az „akkumulátorfigyelés” alól. - Kikapcsolás a DAVdroid kapcsán + Az eszköz korlátozni fogja a DAVx⁵ szinkronizálást. A szokásos DAVadroid szinkronizációs ciklus biztosítása érdekében vonja ki a DAVx⁵-ot az „akkumulátorfigyelés” alól. + Kikapcsolás a DAVx⁵ kapcsán Ne jelenjen meg többet Ne most A forrás nyíltságával kapcsolatos információk - Örülünk, hogy használja a DAVdroidot. A DAVdroid nyílt forráskódú (GPLv3) szoftver, de a fejlesztése kemény munkát jelent, már eddig több ezer munkaórát emésztett fel, ezért kérjük, fontolja meg, hogy támogassa munkánkat. + Örülünk, hogy használja a DAVx⁵ot. A DAVx⁵ nyílt forráskódú (GPLv3) szoftver, de a fejlesztése kemény munkát jelent, már eddig több ezer munkaórát emésztett fel, ezért kérjük, fontolja meg, hogy támogassa munkánkat. Támogatás küldése Talán később Play Áruház DRM hibával kapcsolatos információ - Bizonyos körülmények között a Play Áruház DRM okozhatja azt, hogy az eszköz újraindítását vagy a DAVdroid frissítését követően a DAVdroid fiókok eltűnnek. Amennyiben (és csak amennyiben) érinti Önt ez a probléma, telepítse a \"DAVdroid JB Workaround\" alkalmazást Play Áruházból. + Bizonyos körülmények között a Play Áruház DRM okozhatja azt, hogy az eszköz újraindítását vagy a DAVx⁵ frissítését követően a DAVx⁵ fiókok eltűnnek. Amennyiben (és csak amennyiben) érinti Önt ez a probléma, telepítse a \"DAVx⁵ JB Workaround\" alkalmazást Play Áruházból. További információk Az OpenTasks nincs telepítve A feladatok szinkronizálásához az ingyenes OpenTasks alkalmazásra van szükség. (A névjegyek és események szinkronizálásához erre nincs szükség.) - Az OpenTasks telepítését követően újra kell telepíteni a DAVdroit alkalmazást és újra fel kell venni a fiókokat (Android hiba). + Az OpenTasks telepítését követően újra kell telepíteni a DAVdroit alkalmazást és újra fel kell venni a fiókokat (Android hiba). Az OpenTasks telepítése Könyvtárak @@ -40,7 +40,7 @@ Ennek a verziónak a terjesztése csak a Play Áruházon keresztül engedélyezett. Ehhez a program SEMMIFÉLE GARANCIA NEM JÁR. Ez a program szabad szoftver, ami a bizonyos feltételek mellett szabadon terjeszthető. - DAVdroid fájlalapú naplózás + DAVx⁵ fájlalapú naplózás Naplózás külső tárhelyre: %s Külső naplófájl létrehozása sikertelen: %s A külső tárhely nem érhető el @@ -58,7 +58,7 @@ GYIK Segítség / Fórumok Támogatás - Üdvözöljük a DAVdroid felhasználók között!\n\nMost már felvehet CalDAV/CardDav fiókokat. + Üdvözöljük a DAVx⁵ felhasználók között!\n\nMost már felvehet CalDAV/CardDav fiókokat. A rendszerszintű automatikus szinkronizálás ki van kapcsolva Bekapcsolás @@ -193,8 +193,8 @@ Az ennyi napnál (lehet 0) régebbi események figyelmen kívül lesznek hagyva. Hagyja üresen, ha minden múltbéli eseményt szinkronizálni akar. Naptárszínek kezelése - A naptárszíneket a DAVdroid kezeli - A naptárszíneket nem a DAVdroid kezeli + A naptárszíneket a DAVx⁵ kezeli + A naptárszíneket nem a DAVx⁵ kezeli Eseményszínek támogatása Eseményszínek szinkronlzálása Az eseményszínek szinkronizálásának elhagyása @@ -236,7 +236,7 @@ Helyi névjegyváltozás elvetve %d helyi névjegyváltozás elvetve - DAVdroid engedélyek + DAVx⁵ engedélyek További engedélyek szükségesek Az OpenTask verzió nem megfelelő Szükséges verzió: %1$s (jelenlegi verzió: %2$s) @@ -247,6 +247,6 @@ Újbóli próbálkozás Elem megtekintése - DAVdroid: kapcsolatbiztonság + DAVx⁵: kapcsolatbiztonság Egy eddig ismeretlen tanúsítvány érkezett. Megbízhatónak kívánja elfogadni? diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 85fa4f513..4e0eaffbd 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - Rubrica DAVdroid + DAVx⁵ + Rubrica DAVx⁵ Rubriche Aiuto Gestione account @@ -14,23 +14,23 @@ Errori di Rete e di I/O Messaggi di stato - Disabilita per DAVdroid + Disabilita per DAVx⁵ Non mostrare più Informazioni sull\'Open-Source - Siamo soddisfatti del tuo uso di DAVdroid, programma Open-Source (GPLv3). Poiché lo sviluppo è un\'iniziativa complessa che comporta molte ore di lavoro ti invitiamo a fare una donazione. + Siamo soddisfatti del tuo uso di DAVx⁵, programma Open-Source (GPLv3). Poiché lo sviluppo è un\'iniziativa complessa che comporta molte ore di lavoro ti invitiamo a fare una donazione. Mostra pagina donazioni Più tardi Informazioni sul bug del DRM di Play Store - In alcune condizioni il DRM di Play Store può causare la perdita di tutti gli account DAVdroid dopo un riavvio o dopo un aggiornamento di DAVdroid. Se verificate questo problema installate successivamente \"DAVdroid JB Workaround\" da Play Store. + In alcune condizioni il DRM di Play Store può causare la perdita di tutti gli account DAVx⁵ dopo un riavvio o dopo un aggiornamento di DAVx⁵. Se verificate questo problema installate successivamente \"DAVx⁵ JB Workaround\" da Play Store. Più informazioni OpenTasks non installata Per sincronizzare le attività è richiesta l\'app gratuita OpenTasks. (Non richiesta per contatti/eventi.) - Dopo l\'installazione di OpenTasks è necessario INSTALLARE NUOVAMENTE DAVdroid e aggiungere ancora gli account (per un bug di Android). + Dopo l\'installazione di OpenTasks è necessario INSTALLARE NUOVAMENTE DAVx⁵ e aggiungere ancora gli account (per un bug di Android). Installa OpenTasks Il programma è distribuito SENZA ALCUNA GARANZIA. È software libero e può essere redistribuito sotto alcune condizioni. - Invio del log di DAVdroid su file + Invio del log di DAVx⁵ su file Log su dispositivo esterno: %s Non riesco a creare il file di log esterno: %s Dispositivo esterno non disponibile @@ -47,7 +47,7 @@ Domande Frequenti Aiuto / Forum Donazione - Benvenuto a DAVdroid!\n\nÈ ora possibile aggiungere account CalDAV/CardDAV. + Benvenuto a DAVx⁵!\n\nÈ ora possibile aggiungere account CalDAV/CardDAV. La sincronizzazione automatica dell\'intero sistema è disabilitata Attiva @@ -173,8 +173,8 @@ Eventi più vecchi di questo numero di giorni verranno ignorati(può anche essere 0). Lasciare in bianco per sincronizzare tutti gli eventi. Cambia il colore del calendario - I colori dei calendari sono gestiti da DAVdroid - I colori dei calendari non sono gestiti da DAVdroid + I colori dei calendari sono gestiti da DAVx⁵ + I colori dei calendari non sono gestiti da DAVx⁵ Supporto colore dell\'evento Sincronizza colori eventi Non sincronizza colori eventi @@ -208,12 +208,12 @@ Informazioni di debug Rubrica in sola lettura - Autorizzazioni DAVdroid + Autorizzazioni DAVx⁵ Autorizzazioni addizionali richieste OpenTasks troppo vecchia Versione richiesta: %1$s (attualmente %2$s) Autenticazione fallita (controlla credenziali login) - DAVdroid: sicurezza della connessione - DAVdroid ha trovato un certificato sconosciuto. Ritenerlo affidabile? + DAVx⁵: sicurezza della connessione + DAVx⁵ ha trovato un certificato sconosciuto. Ritenerlo affidabile? diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 65196ccc2..dd626bf0b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - DAVdroid アドレス帳 + DAVx⁵ + DAVx⁵ アドレス帳 アドレス帳 ヘルプ アカウントの管理 @@ -18,20 +18,20 @@ 自動同期 %s ファームウェアは自動同期をブロックすることがよくあります。 この場合、Android の設定で自動同期を許可してください。 スケジュール同期 - お使いのデバイスは DAVdroid の同期を制限します。 通常の DAVdroid 同期間隔を適用するには、「バッテリ最適化」をオフにしてください。 - DAVdroid 用にオフにする + お使いのデバイスは DAVx⁵ の同期を制限します。 通常の DAVx⁵ 同期間隔を適用するには、「バッテリ最適化」をオフにしてください。 + DAVx⁵ 用にオフにする 次回から表示しない 後で オープンソース情報 - あなたがオープンソース ソフトウェア (GPLv3) の DAVdroid を使用していただくことに、私たちは満足しています。 DAVdroid の開発はハードワークで、何千もの作業時間がかかりました。寄付をご検討ください。 + あなたがオープンソース ソフトウェア (GPLv3) の DAVx⁵ を使用していただくことに、私たちは満足しています。 DAVx⁵ の開発はハードワークで、何千もの作業時間がかかりました。寄付をご検討ください。 寄付ページを表示 たぶん後で Play ストア DRM バグ情報 - 特定の条件下で、DAVdroid を再起動後またはアップグレードした後、Play ストア DRM によりすべての DAVdroid アカウントがなくなる可能性があります。この問題の影響を受けている場合 (のみ)、Play ストアから「DAVdroid JB 回避策」をインストールしてください。 + 特定の条件下で、DAVx⁵ を再起動後またはアップグレードした後、Play ストア DRM によりすべての DAVx⁵ アカウントがなくなる可能性があります。この問題の影響を受けている場合 (のみ)、Play ストアから「DAVx⁵ JB 回避策」をインストールしてください。 追加情報 OpenTasks がインストールされていません タスクを同期するために、無料アプリのOpenTasksが必要です。 (連絡先/イベントには必要ありません) - OpenTasks をインストールした後で、DAVdroidを再インストールして、再度アカウントを追加してください (Android のバグ)。 + OpenTasks をインストールした後で、DAVx⁵を再インストールして、再度アカウントを追加してください (Android のバグ)。 OpenTasks をインストール ライブラリー @@ -40,7 +40,7 @@ このバージョンは Google Play での配信にのみ対応です。 このプログラムは完全に無保証で提供されます。これはフリーソフトウェアで、特定の条件下での再頒布を歓迎します。 - DAVdroid ファイルログ + DAVx⁵ ファイルログ 外部ストレージにログ: %s 外部ログファイルを作成できませんでした: %s 外部ストレージが見つかりません @@ -58,7 +58,7 @@ FAQ ヘルプ / フォーラム 寄付 - DAVdroid にようこそ!\n\nCalDAV/CardDAV アカウントを追加できるようになりました。 + DAVx⁵ にようこそ!\n\nCalDAV/CardDAV アカウントを追加できるようになりました。 システム全体の自動同期が無効です 有効 @@ -192,8 +192,8 @@ この日数より過去のイベントは無視されます (0 も可)。すべてのイベントを同期させるには、空白のままにしてください。 カレンダーの色を管理 - カレンダーの色は DAVdroid が管理します - カレンダーの色を DAVdroid が設定しません + カレンダーの色は DAVx⁵ が管理します + カレンダーの色を DAVx⁵ が設定しません イベントカラーサポート イベントカラーを同期 イベントカラーを同期しない @@ -234,7 +234,7 @@ %d ローカル連絡先の変更が破棄されました - DAVdroid アクセス許可 + DAVx⁵ アクセス許可 追加のアクセス許可が必要です OpenTasks が古すぎます 必要なバージョン: %1$s (現在 %2$s) @@ -245,6 +245,6 @@ 再試行 アイテムを表示 - DAVdroid: 接続セキュリティ - DAVdroidは、未知の証明書を検出しました。それを信頼しますか? + DAVx⁵: 接続セキュリティ + DAVx⁵は、未知の証明書を検出しました。それを信頼しますか? diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index dd48ddfc9..09d1df91a 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -1,29 +1,29 @@ - DAVdroid - DAVdroid-adressebok + DAVx⁵ + DAVx⁵-adressebok Adressebøker Hjelp Behandle kontoer Vent… Send - Skru av for DAVdroid + Skru av for DAVx⁵ Ikke vis igjen Friprog-informasjon - Vi er glade for at du bruker DAVdroid, som er fri programvare (GPLv3). Siden utvikling av DAVdroid er hardt arbeid og har tatt tusenvis av arbeidstimer, bes du overveie en donasjon. + Vi er glade for at du bruker DAVx⁵, som er fri programvare (GPLv3). Siden utvikling av DAVx⁵ er hardt arbeid og har tatt tusenvis av arbeidstimer, bes du overveie en donasjon. Vis donasjonsside Kanskje senere DRM-feilinformasjon på Play-butikken - Under gitte forhold vil DRM fra Play-butikken forårsake at alle DAVdroid-kontoer forsvinner etter omstart eller etter oppgradering av DAVdroid. Hvis du rammes av dette problemet (og bare da), installer \"DAVdroid JB Workaround\" fra Play-butikken. + Under gitte forhold vil DRM fra Play-butikken forårsake at alle DAVx⁵-kontoer forsvinner etter omstart eller etter oppgradering av DAVx⁵. Hvis du rammes av dette problemet (og bare da), installer \"DAVx⁵ JB Workaround\" fra Play-butikken. OpenTasks er ikke installert - Etter å ha installert OpenTasks, må du reinstallere Davdroid og legge til kontoene dine igjen (Android-feil). + Etter å ha installert OpenTasks, må du reinstallere Davdroid og legge til kontoene dine igjen (Android-feil). Installer OpenTasks Dette programmet kommer uten NOEN FORM FOR GARANTI. Det er fri programvare, og du er velkommen til å redistribuere det under gitte forhold. - DAVDroid fil-logging + DAVDroid fil-logging Logger til ekstern lagringsmedium: %s Kan ikke opprette ekstern loggfil: %s Fant ingen ekstern lagringsplass @@ -40,7 +40,7 @@ O-S-S Hjelp / Forum Doner - Velkommen til DAVdroid.\n\nDu kan legge til en CalDAV/CardDAV-konto nå. + Velkommen til DAVx⁵.\n\nDu kan legge til en CalDAV/CardDAV-konto nå. Systemomspennende automatisk synkronisering avskrudd Skru på @@ -169,8 +169,8 @@ Hendelser som er mer enn dette antallet dager i fortid vil bli ignorert (kan være 0). La stå tomt for å synkronisere alle hendelser. Velg kalenderfarger - Kalenderfarger behandles av DAVdroid - Kalenderfarger settes ikke av DAVdroid + Kalenderfarger behandles av DAVx⁵ + Kalenderfarger settes ikke av DAVx⁵ Støtte for fargelegging av hendelser Synkroniser hendelsesfarger Ikke synkroniser hendelsesfarger @@ -209,9 +209,9 @@ Lokal kontaktendring forkastet %d lokale kontaktendringer forkastet - DAVdroid-tilganger + DAVx⁵-tilganger Ytterligere tilganger kreves - DAVdroid: Tilkoblingssikkerhet - DAVdroid har støtt på et ukjent sertifikat. Har du tiltro til det? + DAVx⁵: Tilkoblingssikkerhet + DAVx⁵ har støtt på et ukjent sertifikat. Har du tiltro til det? diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index eddceb434..446b1aa76 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1,29 +1,29 @@ - DAVdroid - DAVdroid Adresboek + DAVx⁵ + DAVx⁵ Adresboek Adresboeken Help Beheer accounts Een moment geduld… Verzenden - DAVdroid afsluiten + DAVx⁵ afsluiten Niet opnieuw weergeven Open-Source informatie - We zijn blij dat je DAVdroid gebruikt, wat open-source software (GPLv3) is. Omdat de ontwikkeling van DAVdroid hard werk is en duizenden uren in beslag neemt. overweeg alstublieft een donatie. + We zijn blij dat je DAVx⁵ gebruikt, wat open-source software (GPLv3) is. Omdat de ontwikkeling van DAVx⁵ hard werk is en duizenden uren in beslag neemt. overweeg alstublieft een donatie. Toon donatie pagina Misschien later Play Store DRM fout-informatie - Onder bepaalde omstandigheden, kan Play Store DRM ervoor zorgen dat accounts kwijt zijn na een herstart of na een DAVdroid update. Als dit probleem zich bij je voordoet (en alleen dan), Installeer dan \"DAVdroid JB Workaround\" vanuit de Play Store + Onder bepaalde omstandigheden, kan Play Store DRM ervoor zorgen dat accounts kwijt zijn na een herstart of na een DAVx⁵ update. Als dit probleem zich bij je voordoet (en alleen dan), Installeer dan \"DAVx⁵ JB Workaround\" vanuit de Play Store OpenTasks niet geinstalleerd - Na installatie van OpenTasks dient u DAVdroid opnieuw te installeren en de accounts toe te voegen (Android bug). + Na installatie van OpenTasks dient u DAVx⁵ opnieuw te installeren en de accounts toe te voegen (Android bug). OpenTasks installeren Dit programma kom met ABSOLUUT GEEN GARANTIE. Het is gratis software, en je bent welkom dit te herdistribueren onder bepaalde voorwaarden. - DAVDroid bestand loggen + DAVDroid bestand loggen Loggen naar externe opslag: %s Kon extern log bestand niet verwijderen: %s Externe opslag niet gevonden @@ -41,7 +41,7 @@ FAQ Help / Forums Doneren - Welkom bij DAVdroid!\n\nJe kunt nu een CalDAV/CardDAv account toevoegen. + Welkom bij DAVx⁵!\n\nJe kunt nu een CalDAV/CardDAv account toevoegen. Systeembrede automatische synchronisatie is uitgeschakeld Inschakelen @@ -157,8 +157,8 @@ Afspraken ouder dan dit aantal dagen worden genegeerd (mag 0 zijn). Laat leeg om alle afspraken te synchronizeren. Agenda kleuren beheren - Agenda kleuren worden door DAVdroid beheerd. - Agenda kleuren worden niet door DAVdroid ingesteld + Agenda kleuren worden door DAVx⁵ beheerd. + Agenda kleuren worden niet door DAVx⁵ ingesteld Evenement kleur ondersteuning Evenement kleuren synchroniseren Evenement kleuren niet synchroniseren @@ -191,11 +191,11 @@ Debug informatie Alleen-lezen adresboek - DAVdroid rechten + DAVx⁵ rechten Aanvullende rechten vereist Opnieuw Bekijk item - DAVdroid: Verbinding beveiliging + DAVx⁵: Verbinding beveiliging Davdroid is benaderd door een onbekend certificaat. Vertrouwd u dit? diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e516d3bd1..c2981c8c7 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - Książka adresowa DAVdroid + DAVx⁵ + Książka adresowa DAVx⁵ Książka adresowa Pomoc Zadządzaj kontami @@ -10,21 +10,21 @@ Wyślij Debugowanie - Wyłącz dla DAVdroid + Wyłącz dla DAVx⁵ Nie pokazuj ponownie Informacje Open-Source - Jesteśmy szczęśliwi, że używasz DAVdroid, który jest oprogramowaniem open-source (GPLv3). Ponieważ rozwijanie DAVdroid jest ciężką pracą i zajęło nam tysiące godzin pracy, prosimy o rozważenie darowizny. + Jesteśmy szczęśliwi, że używasz DAVx⁵, który jest oprogramowaniem open-source (GPLv3). Ponieważ rozwijanie DAVx⁵ jest ciężką pracą i zajęło nam tysiące godzin pracy, prosimy o rozważenie darowizny. Pokaż stronę darowizny Może później Informacje o błędzie DRM Sklepu Play - Pod pewnymi warunkami, DRM Sklepu Play może powodować, że wszystkie konta DAVdroid mogą zostać usunięte po uruchomieniu lub po uaktualnieniu DAVdroid. Jeśli jesteś dotknięty tym problemem (i tylko wtedy) należy zainstalować \"DAVdroid JB Obejście\" ze Sklepu Play. + Pod pewnymi warunkami, DRM Sklepu Play może powodować, że wszystkie konta DAVx⁵ mogą zostać usunięte po uruchomieniu lub po uaktualnieniu DAVx⁵. Jeśli jesteś dotknięty tym problemem (i tylko wtedy) należy zainstalować \"DAVx⁵ JB Obejście\" ze Sklepu Play. OpenTasks nie jest zainstalowany - Po zainstalowaniu OpenTasks konieczne jest PRZEINSTALOWANIE DAVdroid i ponowne dodanie twoich kont (błąd Androida). + Po zainstalowaniu OpenTasks konieczne jest PRZEINSTALOWANIE DAVx⁵ i ponowne dodanie twoich kont (błąd Androida). Zainstaluj OpenTasks Ten program jest ABSOLUTNIE BEZ GWARANCJI. To jest wolne oprogramowanie i mile widziane jest dalsze rozpowszechnianie go pod pewnymi warunkami. - Plik logów DAVdroid + Plik logów DAVx⁵ Logowanie do zewnątrznej pamięci: %s Nie można stworzyć zewnętrznego pliku logów: %s Zewnętrzna pamięci nie została naleziona @@ -32,7 +32,7 @@ Otwórz menu nawigacji Zamknij menu nawigacji Adapter synchronizacji CalDAV/CardDAV - O DAVdroid / Licencja + O DAVx⁵ / Licencja Przekaż opinię Ustawienia Nowości & aktualizacje @@ -42,7 +42,7 @@ Pytania i odpowiedzi Pomoc / Forum Dotacja - Witamy w DAVdroid!\n\nMożesz teraz dodać konto CalDAV/CardDAV. + Witamy w DAVx⁵!\n\nMożesz teraz dodać konto CalDAV/CardDAV. Automatyczna synchronizacja dla całego systemu jest wyłączona Włącz @@ -173,8 +173,8 @@ Wydarzenia, które są starsze niż podana liczba dni zostaną zignorowane (może być 0). Zostaw puste, aby synchronizować wszystkie wydarzenia. Zarządzaj kolorami kalendarza - Kolory kalendarza są zarządzane przez DAVdroid - Kolory kalendarze nie są ustawiane przez DAVdroid + Kolory kalendarza są zarządzane przez DAVx⁵ + Kolory kalendarze nie są ustawiane przez DAVx⁵ Obsługa kolorów wydarzeń Synchronizuj kolory zdarzeń Nie synchronizuj kolorów zdarzeń @@ -215,11 +215,11 @@ %d lokalne kontakty zostaną odrzucone %d lokalne kontakty zostaną odrzucone - Uprawnienia DAVdroid + Uprawnienia DAVx⁵ Wymagane dodatkowe uprawnienia OpenTask jest niekompatybilny Wymagana wersja: %1$s (obecna %2$s) - DAVdroid: Bezpieczeństwo połączenia - DAVdroid napotkał nieznany certyfikat. Czy chcesz go dodać? + DAVx⁵: Bezpieczeństwo połączenia + DAVx⁵ napotkał nieznany certyfikat. Czy chcesz go dodać? diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index b87d0c890..a1561c19d 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - Livro de endereços DAVdroid + DAVx⁵ + Livro de endereços DAVx⁵ Livros de endereços Ajuda Gerenciar contas @@ -18,20 +18,20 @@ Sincronização automática O firmware%s frequentemente bloqueia a sincronização automática. Nesse caso, ative a sincronização automática nas configurações do seu Android. Sincronização agendada - Seu aparelho irá restringir a sincronização do DAVdroid. Para forçar a sincronização do DAVdroid em intervalos regulares, desligue a \"otimização da bateria\". - Desligar para o DAVdroid + Seu aparelho irá restringir a sincronização do DAVx⁵. Para forçar a sincronização do DAVx⁵ em intervalos regulares, desligue a \"otimização da bateria\". + Desligar para o DAVx⁵ Não mostrar novamente Não agora Informação sobre Código Aberto - Estamos felizes que você usa o DAVdroid, um software de código aberto (GPLv3). O desenvolvimento do DAVdroid é trabalhoso e consome muitas horas de trabalho. Por esse motivo, considere fazer uma doação. + Estamos felizes que você usa o DAVx⁵, um software de código aberto (GPLv3). O desenvolvimento do DAVx⁵ é trabalhoso e consome muitas horas de trabalho. Por esse motivo, considere fazer uma doação. Mostrar a página de doações Talvez depois Informação sobre o erro de DRM da Play Store - Sob certas condições, o DRM da Play Store pode fazer com que todas as contas DAVdroid sejam perdidas depois de uma reinicialização ou atualização do DAVdroid. Se você for afetado por esse problema, instale o \"DAVdroid JB Workaround\" a partir da Play Store. + Sob certas condições, o DRM da Play Store pode fazer com que todas as contas DAVx⁵ sejam perdidas depois de uma reinicialização ou atualização do DAVx⁵. Se você for afetado por esse problema, instale o \"DAVx⁵ JB Workaround\" a partir da Play Store. Mais informações O OpenTasks não está instalado Para sincronizar tarefas é necessário instalar o aplicativo livre OpenTasks. (Não é necessário para contatos/eventos) - Depois da instalação do OpenTasks, torna-se necessário REINSTALAR o DAVdroid e adicionar suas contas novamente (erro do Android). + Depois da instalação do OpenTasks, torna-se necessário REINSTALAR o DAVx⁵ e adicionar suas contas novamente (erro do Android). Instalar o OpenTasks Bibliotecas @@ -40,7 +40,7 @@ Esta versão está disponível apenas para distribuição na Google Play. Este programa é distribuído SEM NENHUMA GARANTIA. Ele é software livre e pode ser redistribuído sob algumas condições. - Registro do arquivo do DAVdroid + Registro do arquivo do DAVx⁵ Registrando no arquivo externo: %s Não foi possível criar o arquivo de registro externo: %s Armazenamento externo não encontrado @@ -58,7 +58,7 @@ Perguntas fequentes Ajuda / Fóruns Doações - Bem-vindo ao DAVdroid!\n\nVocê pode adicionar uma conta CalDAV/CardDAV agora. + Bem-vindo ao DAVx⁵!\n\nVocê pode adicionar uma conta CalDAV/CardDAV agora. A sincronização automática pelo sistema está desativada Ativar @@ -193,8 +193,8 @@ Os eventos que ocorreram antes desse número de dias serão ignorados (pode ser 0). Deixe em branco para sincronizar todos os eventos. Gerenciar cores dos calendários - Cores dos calendários definidas pelo DAVdroid - Cores dos calendários não definidas pelo DAVdroid + Cores dos calendários definidas pelo DAVx⁵ + Cores dos calendários não definidas pelo DAVx⁵ Suporte para cor de evento Sincronizar cores de eventos Não sincronizar cores de eventos @@ -236,7 +236,7 @@ Alteração local de contato descartada %d alterações locais de contatos descartadas - Permissões do DAVdroid + Permissões do DAVx⁵ É necessário permissões adicionais A versão do OpenTasks é muito antiga Versão necessária: %1$s (atual %2$s) @@ -247,6 +247,6 @@ Repetir Ver item - DAVdroid: Segurança da conexão - O DAVdroid encontrou um certificado desconhecido. Deseja torná-lo confiável? + DAVx⁵: Segurança da conexão + O DAVx⁵ encontrou um certificado desconhecido. Deseja torná-lo confiável? diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 87ad9b5cb..d1defccbd 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - Адресная книга DAVdroid + DAVx⁵ + Адресная книга DAVx⁵ Адресные книги Помощь Управление аккаунтами @@ -18,20 +18,20 @@ Автоматическая синхронизация %s ПО устройства часто блокирует автоматическую синхронизацию. В этом случае разрешите автоматическую синхронизацию в настройках Android. Синхронизация по расписанию - Ваше устройство будет блокировать синхронизацию DAVdroid. Чтобы обеспечить регулярные интервалы синхронизации DAVdroid, отключите оптимизацию энергопотребления. - Отключить для DAVdroid + Ваше устройство будет блокировать синхронизацию DAVx⁵. Чтобы обеспечить регулярные интервалы синхронизации DAVx⁵, отключите оптимизацию энергопотребления. + Отключить для DAVx⁵ Не показывать снова Не сейчас Open-Source информация - Мы рады, что вы используете DAVdroid, который является программным обеспечением с открытым исходным кодом (GPLv3). Поскольку разработка DAVdroid - тяжелая работа и заняла у нас нас очень много времени, пожалуйста, рассмотрите возможность поддержать проект. + Мы рады, что вы используете DAVx⁵, который является программным обеспечением с открытым исходным кодом (GPLv3). Поскольку разработка DAVx⁵ - тяжелая работа и заняла у нас нас очень много времени, пожалуйста, рассмотрите возможность поддержать проект. Показать страницу пожертвований Возможно, позже Информация об ошибке в Play Store DRM - При определенных условиях Play Store DRM может стать причиной потери всех DAVdroid аккаунтов после перезагрузки устройства или после обновления DAVdroid. Если Вы столкнулись с этой проблемой (и только в этом случае), установите \"DAVdroid JB Workaround\" из Play Store. + При определенных условиях Play Store DRM может стать причиной потери всех DAVx⁵ аккаунтов после перезагрузки устройства или после обновления DAVx⁵. Если Вы столкнулись с этой проблемой (и только в этом случае), установите \"DAVx⁵ JB Workaround\" из Play Store. Дополнительная информация OpenTasks не установлен Для синхронизации задач требуется бесплатное приложение OpenTasks. (Не требуется для контактов/событий.) - После установки OpenTasks необходимо ПЕРЕУСТАНОВИТЬ DAVdroid и повторно добавить ваши аккаунты (проблема Android). + После установки OpenTasks необходимо ПЕРЕУСТАНОВИТЬ DAVx⁵ и повторно добавить ваши аккаунты (проблема Android). Установить OpenTasks Библиотеки @@ -40,7 +40,7 @@ Эта версия распространяется только через Google Play. Эта программа поставляется БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ. Это свободное программное обеспечение и вы можете распространять его при соблюдении определенных условий. - Файл журнала DAVdroid + Файл журнала DAVx⁵ Сохранение логов во внешнем хранилище: %s Не удалось создать внешний файл журнала: %s Внешнее хранилище не найдено @@ -58,7 +58,7 @@ FAQ Помощь / Форумы Пожертвовать - Добро пожаловать в DAVdroid\n\nТеперь вы можете добавить аккаунт CalDAV/CardDAV. + Добро пожаловать в DAVx⁵\n\nТеперь вы можете добавить аккаунт CalDAV/CardDAV. Синхронизация отключена на уровне устройства Включить @@ -195,8 +195,8 @@ События старше указанного количества дней будут игнорироваться (может быть 0). Оставьте пустым для синхронизации всех событий. Управление цветами календаря - Цвета календаря устанавливаются DAVdroid - Цвета календаря не устанавливаются DAVdroid + Цвета календаря устанавливаются DAVx⁵ + Цвета календаря не устанавливаются DAVx⁵ Поддержка цвета событий Синхронизация цветов событий Не синхронизировать цвета событий @@ -240,7 +240,7 @@ %d локальных изменений контакта отменены %d локальных изменений контакта отменены - Разрешения DAVdroid + Разрешения DAVx⁵ Требуются дополнительные разрешения OpenTasks устарел Требуется версия: %1$s (текущая %2$s) @@ -251,6 +251,6 @@ Повторить Просмотр элемента - DAVdroid: безопасность подключения - DAVdroid обнаружил неизвестный сертификат. Вы хотите доверять ему? + DAVx⁵: безопасность подключения + DAVx⁵ обнаружил неизвестный сертификат. Вы хотите доверять ему? diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index ec24d79b1..8dd420113 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -27,11 +27,11 @@ Прикажи страницу за донације Можда касније Грешка ДРМ-а Плеј продавнице - Под одређеним околностима ДРМ Плеј продавнице може да узрокује да сви налози ДАВдроида нестане након рестарта или ажурирања ДАВдроида. Ако и само ако имате овај проблем, инсталирајте „DAVdroid JB Workaround“ са Плеј продавнице. + Под одређеним околностима ДРМ Плеј продавнице може да узрокује да сви налози ДАВдроида нестане након рестарта или ажурирања ДАВдроида. Ако и само ако имате овај проблем, инсталирајте „DAVx⁵ JB Workaround“ са Плеј продавнице. Још информација Отворени задаци нису инсталирани За синхронизацију листе задатака бесплатна апликација „Отворени задаци“ је потребна. (Није потребна за контакте/догађаје.) - Након инсталирања Отворених задатака, морате поново да инсталирате ДАВдроид и поново додате ваше налоге (због грешке у Андроиду). + Након инсталирања Отворених задатака, морате поново да инсталирате ДАВдроид и поново додате ваше налоге (због грешке у Андроиду). Инсталирај Отворене задатке Библиотеке @@ -40,7 +40,7 @@ Ово издање је доступно за дистрибуцију само преко Гугловог Плеја. Овај програм НЕМА НИКАКВЕ ГАРАНЦИЈЕ. Бесплатан је софтвер којег можете слободно да делите под одређеним условима. - ДАВдроид евиденција + ДАВдроид евиденција Уписивање евиденције у спољашње складиште: %s Не могох да направим спољашњи фајл записа: %s Спољашње складиште није нађено diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index 26a2d3520..161789175 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -1,7 +1,7 @@ - DAVdroid + DAVx⁵ Yardım Hesapları yönet Lütfen bekle … @@ -9,18 +9,18 @@ Bir daha gösterme Açık-Kaynak Bilgisi - Açık kaynaklı yazılım (GPLv3) olan DAVdroid\'i kullandığına çok mutluyuz. DAVdroid\'i geliştirmek zor bir iş ve üzerinde binlerce saat çalıştığımızdan, lütfen bir bağışta bulunmayı düşün. + Açık kaynaklı yazılım (GPLv3) olan DAVx⁵\'i kullandığına çok mutluyuz. DAVx⁵\'i geliştirmek zor bir iş ve üzerinde binlerce saat çalıştığımızdan, lütfen bir bağışta bulunmayı düşün. Bağış sayfasını göster Belki sonra Play Store DRM hata bilgisi - Bazı durumlarda Play Store DRM\'i, cihazı yeniden başlatınca veya DAVdroid\'i yükseltince DAVdroid hesaplarının yokolmasına sebep olabiliyor. Bu sorundan etkileniyorsan (ve sadece bu durumda), lütfen Play Store\'daki \"DAVdroid JB Workaround\" uygulamasını kur. + Bazı durumlarda Play Store DRM\'i, cihazı yeniden başlatınca veya DAVx⁵\'i yükseltince DAVx⁵ hesaplarının yokolmasına sebep olabiliyor. Bu sorundan etkileniyorsan (ve sadece bu durumda), lütfen Play Store\'daki \"DAVx⁵ JB Workaround\" uygulamasını kur. OpenTasks kurulu değil - OpenTasks\'i kurduktan sonra, DAVdroid\'i YENİDEN KURMAN ve hesaplarını yeniden eklemen gerek. (Android hatası). + OpenTasks\'i kurduktan sonra, DAVx⁵\'i YENİDEN KURMAN ve hesaplarını yeniden eklemen gerek. (Android hatası). OpenTasks kur Bu uygulama HİÇ BİR GARANTİ ile gelmemektedir. Bedava bir yazılımdır ve belli koşullar altında dağıtabilirsiniz. - DAVdroid dosya jurnallemesi + DAVx⁵ dosya jurnallemesi Harici depolamaya jurnalleniyor: %s Harici jurnal dosyası yaratılamadı: %s Harici depolama alanı bulunamadı @@ -35,7 +35,7 @@ Web sitesi SSS Bağış yap - DAVdroid\'e hoşgeldin!\n\nŞimdi bir CalDAV/CardDAV hesabı ekleyebilirsin. + DAVx⁵\'e hoşgeldin!\n\nŞimdi bir CalDAV/CardDAV hesabı ekleyebilirsin. Servis keşfi başarısız Kolleksiyon listesi yenilenemedi @@ -112,8 +112,8 @@ Bu sayıdan daha eski olan olaylar yok sayılacaktır (0 olabilir). Tüm olayları senkronize etmek için boş bırak. Takvim renklerini yönet - Takvim renkleri DAVdroid tarafından yönetilmekte - Takvim renkleri DAVdroid tarafından ayarlanmadı + Takvim renkleri DAVx⁵ tarafından yönetilmekte + Takvim renkleri DAVx⁵ tarafından ayarlanmadı Rehber yarat Benim Rehberim @@ -143,7 +143,7 @@ Detayları göster Hata ayıklama bilgisi - DAVdroid izinleri + DAVx⁵ izinleri Ek izinler zorunludur diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2d49a209c..1be601ae5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - Адресна книга DAVdroid + DAVx⁵ + Адресна книга DAVx⁵ Адресні книги Допомога Керування обліковими записами @@ -18,20 +18,20 @@ Автоматична синхронізація %s прошивка часто блокує автоматичну синхронізацію. У цьому випадку дозвольте автоматичну синхронізацію в налаштуваннях Android. Запланована синхронізація - Ваш пристрій буде обмежувати синхронізацію DAVdroid. Щоб забезпечити регулярні інтервали синхронізації DAVdroid, вимкніть \"оптимізація батареї\". - Вимкнути для DAVdroid + Ваш пристрій буде обмежувати синхронізацію DAVx⁵. Щоб забезпечити регулярні інтервали синхронізації DAVx⁵, вимкніть \"оптимізація батареї\". + Вимкнути для DAVx⁵ Не показувати знову Не зараз Інформація Open-Source - Ми раді, що Ви використовуєте DAVdroid, який є програмним засобом з відкритим джерельним кодом (GPLv3). Розробка DAVdroid є досить складним завданням і потребує від нас тисячі годин роботи. Будь ласка, розгляньте можливість підтримати проект. + Ми раді, що Ви використовуєте DAVx⁵, який є програмним засобом з відкритим джерельним кодом (GPLv3). Розробка DAVx⁵ є досить складним завданням і потребує від нас тисячі годин роботи. Будь ласка, розгляньте можливість підтримати проект. Показати сторінку пожертви Можливо пізніше Інформація про ваду в Play Store DRM - При деяких обставинах Play Store DRM може стати причиною втрати всіх облікових записів DAVdroid після перезавантаження пристрою чи оновлення DAVdroid. Якщо ви зіткнулися із цим (і лише у цьому випадку), будь ласка, встановіть \"DAVdroid JB Workaround\" з Play Store. + При деяких обставинах Play Store DRM може стати причиною втрати всіх облікових записів DAVx⁵ після перезавантаження пристрою чи оновлення DAVx⁵. Якщо ви зіткнулися із цим (і лише у цьому випадку), будь ласка, встановіть \"DAVx⁵ JB Workaround\" з Play Store. Додаткова інформація OpenTasks не встановлено Для синхронізації завдань необхідно встановити додаток OpenTasks. (Не має потреби для контактів/подій.) - Після встановлення OpenTasks, необхідно перевстановити DAVdroid та додати облікові записи знову (Вада системи Android). + Після встановлення OpenTasks, необхідно перевстановити DAVx⁵ та додати облікові записи знову (Вада системи Android). Встановити OpenTasks Бібліотеки @@ -40,7 +40,7 @@ Ця версія доступна лише для розповсюдження через Google Play. Цей програмний засіб постачається АБСОЛЮТНО БЕЗ БУДЬ-ЯКИХ ГАРАНТІЙ. Це вільне програмне забезпечення, і ви можете поширювати її, за деякими умовами. - Файл звітування DAVdroid + Файл звітування DAVx⁵ Звітування до зовнішнього сховища: %s Не вдалося створити файл зовнішнього звіту: %s Не знайдено зовнішнього сховища @@ -58,7 +58,7 @@ Питання/Відповіді Допомога / Форуми Підтримка - Вітаємо у DAVdroid!\n\nТепер можете додавати облікові записи CalDAV/CardDAV. + Вітаємо у DAVx⁵!\n\nТепер можете додавати облікові записи CalDAV/CardDAV. Автоматичну синхронізацію вимкнено зі сторони системи Увімкнути @@ -195,8 +195,8 @@ Події старші вказаного часу будуть проігноровані (може бути 0). Залиште порожнім, аби синхронізувати всі події. Керування кольорами календаря - Кольори календаря керуються DAVdroid - Кольори календаря не керуються DAVdroid + Кольори календаря керуються DAVx⁵ + Кольори календаря не керуються DAVx⁵ Підтримка кольорів подій Синхронізувати кольори подій Не синхронізувати кольори подій @@ -240,7 +240,7 @@ %d локальних змін контакту відхилено %d локальних змін контакту відхилено - Дозволи DAVdroid + Дозволи DAVx⁵ Потребує додаткові дозволи OpenTasks застарів Необхідна версія: %1$s (поточна %2$s) @@ -251,6 +251,6 @@ Повторити Перегляд елементу - DAVdroid: Безпека з\'єднання - DAVdroid зіткнувся з невідомим сертифікатом. Чи довіряти йому? + DAVx⁵: Безпека з\'єднання + DAVx⁵ зіткнувся з невідомим сертифікатом. Чи довіряти йому? diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4f42520df..1ad58f401 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1,8 +1,8 @@ - DAVdroid - DAVdroid 通讯录 + DAVx⁵ + DAVx⁵ 通讯录 通讯录 帮助 管理账户 @@ -18,20 +18,20 @@ 自动同步 %s 的系统通常会禁止自动同步。在 Android 的系统设置中手工打开自动同步即可恢复。 定时同步 - 你的设备会限制 DAVdroid 的同步频率,如需恢复正常同步频率,请关闭电池优化设置。 + 你的设备会限制 DAVx⁵ 的同步频率,如需恢复正常同步频率,请关闭电池优化设置。 禁用电池优化 不再显示 暂不 开源信息 - 欢迎使用 DAVdroid,这是一款开源软件 (GPLv3)。开发 DAVdroid 的工作花费了数千小时,请您考虑捐助我们。 + 欢迎使用 DAVx⁵,这是一款开源软件 (GPLv3)。开发 DAVx⁵ 的工作花费了数千小时,请您考虑捐助我们。 显示捐助页面 稍后提示 Play 商店 DRM 问题提醒 - 在某些情况下,Play 商店的 DRM 可能会导致所有 DAVdroid 账户在设备重启或升级 DAVdroid 后消失。如果你遇到了该问题,请从 Play 商店安装“DAVdroid JB Workaround”,否则请不要安装修复程序。 + 在某些情况下,Play 商店的 DRM 可能会导致所有 DAVx⁵ 账户在设备重启或升级 DAVx⁵ 后消失。如果你遇到了该问题,请从 Play 商店安装“DAVx⁵ JB Workaround”,否则请不要安装修复程序。 更多信息 OpenTasks 未安装 同步任务需安装 OpenTasks 免费应用。(如只需同步通讯录、事件,则不用安装) - 安装 OpenTasks 后,由于 Android 的限制,请重新安装 DAVdroid 并重新创建账户。 + 安装 OpenTasks 后,由于 Android 的限制,请重新安装 DAVx⁵ 并重新创建账户。 安装 OpenTasks 程序库 @@ -40,7 +40,7 @@ 此版本只允许在 Google Play 上发行。 本程序不附带任何担保。这是一款自由软件,你可以有条件地传播它。 - DAVdroid 文件日志 + DAVx⁵ 文件日志 记录日志到外部存储 %s 无法创建外部日志文件 %s 找不到外部存储 @@ -58,7 +58,7 @@ 常见问题 帮助 / 论坛 捐助 - 欢迎使用 DAVdroid!\n\n请开始增加 CalDAV/CardDAV 账户。 + 欢迎使用 DAVx⁵!\n\n请开始增加 CalDAV/CardDAV 账户。 系统全局自动同步已禁用 启用 @@ -192,8 +192,8 @@ 超过这个数字的天数的旧日程将会被忽略(可以为 0)。留空则同步所有日程。 管理日历颜色 - 日历颜色由 DAVdroid 设置 - 日历颜色不由 DAVdroid 设置 + 日历颜色由 DAVx⁵ 设置 + 日历颜色不由 DAVx⁵ 设置 事件日历颜色支持 同步日历事件颜色 不同步日历事件颜色 @@ -234,7 +234,7 @@ %d 本地联系人修改被撤销 - DAVdroid 权限 + DAVx⁵ 权限 需要额外权限 OpenTasks 版本太旧 最低版本 %1$s (当前 %2$s) @@ -245,6 +245,6 @@ 重试 显示项目 - DAVdroid: 连接安全性 - DAVdroid 遇到了未知证书。你是否要信任该证书? + DAVx⁵: 连接安全性 + DAVx⁵ 遇到了未知证书。你是否要信任该证书? diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index cc493ba95..478447359 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1,29 +1,29 @@ - DAVdroid - DAVdroid 通訊錄 + DAVx⁵ + DAVx⁵ 通訊錄 通訊錄 幫助 管理帳號 請稍待 … 送出 - 關閉 DAVdroid 的電池最佳化 + 關閉 DAVx⁵ 的電池最佳化 不要再顯示此訊息 開源資訊 - 很高興您使用 DAVdroid,這是個開源軟體 (GPLv3授權)。因為開發 DAVdroid 是艱難的工作,需要上千個小時,請考慮捐款支持我們。 + 很高興您使用 DAVx⁵,這是個開源軟體 (GPLv3授權)。因為開發 DAVx⁵ 是艱難的工作,需要上千個小時,請考慮捐款支持我們。 開啟捐款頁面 下次再說 Play商店數位權利管理錯誤訊息 - 在某些情況下,Play商店 的數位權利管理可能導致在重開機後或更新 DAVdroid 後,DAVdroid 全部帳號消失。如果您遇到此問題 (且只有在遇到此問題時),請到 Play商店 安裝 \"DAVdroid JB Workaround\"。 + 在某些情況下,Play商店 的數位權利管理可能導致在重開機後或更新 DAVx⁵ 後,DAVx⁵ 全部帳號消失。如果您遇到此問題 (且只有在遇到此問題時),請到 Play商店 安裝 \"DAVx⁵ JB Workaround\"。 OpenTasks 未安裝 - 安裝 OpenTasks 後,您必須「重新安裝」 DAVdroid 並且重新加入要同步的帳號 (這是 Android 的設計問題) + 安裝 OpenTasks 後,您必須「重新安裝」 DAVx⁵ 並且重新加入要同步的帳號 (這是 Android 的設計問題) 安裝 OpenTasks 我們「完全不保證」本程式無瑕疵。這是個自由軟體,歡迎您在符合公用授權條款的情況下任意散布它。 - DAVdroid 正在記錄除錯訊息 + DAVx⁵ 正在記錄除錯訊息 正在將除錯訊息存到外部檔案: %s 無法新增除錯訊息檔案: %s 找不到外部儲存空間 @@ -38,7 +38,7 @@ 我們的網站 常見問答 贊助我們 - 歡迎使用 DAVdroid!\n\n您現在可以新增 CalDAV/CardDAV 帳號 + 歡迎使用 DAVx⁵!\n\n您現在可以新增 CalDAV/CardDAV 帳號 啟用 未發現遠端服務 @@ -141,8 +141,8 @@ 這個天數之前的項目將被忽略 (可設為0)。留白,則所有項目都會同步。 管理行事曆的顏色 - 行事曆顏色由 DAVdroid 管理 - 行事曆顏色不由 DAVdroid 管理 + 行事曆顏色由 DAVx⁵ 管理 + 行事曆顏色不由 DAVx⁵ 管理 建立通訊錄 我的通訊錄 @@ -171,9 +171,9 @@ 顯示細節 除錯訊息 - DAVdroid 權限 + DAVx⁵ 權限 需要額外的權限 - DAVdroid: 連線安全性 - DAVdroid 發現未知的憑證,您要信任它嗎? + DAVx⁵: 連線安全性 + DAVx⁵ 發現未知的憑證,您要信任它嗎? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a20b62a0..7ebb40444 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,18 +10,17 @@ - DAVdroid + DAVx⁵ bitfire.at.davdroid at.bitfire.davdroid.address_book - DAVdroid Address book + DAVx⁵ Address book at.bitfire.davdroid.addressbooks Address books Help Manage accounts Please wait … Send - https://www.davdroid.com/ - https://forums.bitfire.at/category/9/beta-test-discussion?pk_campaign=davdroid-app + https://www.davx5.com/ Debugging Other important messages @@ -34,20 +33,20 @@ Automatic synchronization %s firmware often blocks automatic synchronization. In this case, allow automatic synchronization in your Android settings. Scheduled synchronization - Your device will restrain DAVdroid synchronization. To enforce regular DAVdroid sync intervals, turn off \"battery optimization\". - Turn off for DAVdroid + Your device will restrain DAVx⁵ synchronization. To enforce regular DAVx⁵ sync intervals, turn off \"battery optimization\". + Turn off for DAVx⁵ Don\'t show again Not now Open-Source Information - We\'re happy that you use DAVdroid, which is open-source software (GPLv3). Because developing DAVdroid is hard work and took us thousands of working hours, please consider a donation. + We\'re happy that you use DAVx⁵, which is open-source software (GPLv3). Because developing DAVx⁵ is hard work and took us thousands of working hours, please consider a donation. Show donation page Maybe later Play Store DRM bug information - Under certain conditions, Play Store DRM may cause all DAVdroid accounts to be gone after a reboot or after upgrading DAVdroid. If you\'re affected by this problem (and only then), please install \"DAVdroid JB Workaround\" from Play Store. + Under certain conditions, Play Store DRM may cause all DAVx⁵ accounts to be gone after a reboot or after upgrading DAVx⁵. If you\'re affected by this problem (and only then), please install \"DAVx⁵ JB Workaround\" from Play Store. More information OpenTasks not installed To synchronize tasks, the free app OpenTasks is required. (Not required for contacts/events.) - After installing OpenTasks, you have to RE-INSTALL DAVdroid and add your accounts again (Android bug). + After installing OpenTasks, you have to RE-INSTALL DAVx⁵ and add your accounts again (Android bug). Install OpenTasks @@ -59,7 +58,7 @@ This program comes with ABSOLUTELY NO WARRANTY. It is free software, and you are welcome to redistribute it under certain conditions. - DAVdroid file logging + DAVx⁵ file logging Logging to external storage: %s Couldn\'t create external log file: %s External storage not found @@ -79,7 +78,7 @@ Help / Forums Donate - Welcome to DAVdroid!\n\nYou can add a CalDAV/CardDAV account now. + Welcome to DAVx⁵!\n\nYou can add a CalDAV/CardDAV account now. System-wide automatic synchronization is disabled Enable @@ -229,8 +228,8 @@ Events which are more than this number of days in the past will be ignored (may be 0). Leave blank to synchronize all events. Manage calendar colors - Calendar colors are managed by DAVdroid - Calendar colors are not set by DAVdroid + Calendar colors are managed by DAVx⁵ + Calendar colors are not set by DAVx⁵ Event color support Sync event colors Do not sync event colors @@ -269,14 +268,14 @@ Show details - at.bitfire.davdroid.log + at.bitfire.davdroid.log Debug info Read-only address book Local contact change discarded %d local contact changes discarded - DAVdroid permissions + DAVx⁵ permissions Additional permissions required OpenTasks too old Required version: %1$s (currently %2$s) @@ -288,7 +287,7 @@ View item - DAVdroid: Connection security - DAVdroid has encountered an unknown certificate. Do you want to trust it? + DAVx⁵: Connection security + DAVx⁵ has encountered an unknown certificate. Do you want to trust it? -- GitLab From 8b12cb7a29af50496bc98e7f9a352f0f8e458d64 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 30 Dec 2018 11:59:11 +0100 Subject: [PATCH 019/382] Fetch translations from Transifex --- app/src/main/res/values-fa/strings.xml | 32 ++++++++++++++++++++++ app/src/main/res/values-sl-rSI/strings.xml | 27 ++++++++++++++++++ scripts/fetch-translations.sh | 6 ++-- 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/values-fa/strings.xml create mode 100644 app/src/main/res/values-sl-rSI/strings.xml diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml new file mode 100644 index 000000000..6661c49e0 --- /dev/null +++ b/app/src/main/res/values-fa/strings.xml @@ -0,0 +1,32 @@ + + + + دفترچه‌های آدرس + راهنما + مدیریت حساب‌ها + کمی صبر کنید … + ارسال + عیب‌یابی + سایر پیام‌های مهم + همگام‌سازی + خطاهای همگام‌سازی + خطاهای شبکه + + + + + + + + + + + ساخت دفترچه آدرس + دفترچه آدرس من + تقویم من + ساخته شدن + آیا مطمءن هستین؟ + + + + diff --git a/app/src/main/res/values-sl-rSI/strings.xml b/app/src/main/res/values-sl-rSI/strings.xml new file mode 100644 index 000000000..cfd22a7c4 --- /dev/null +++ b/app/src/main/res/values-sl-rSI/strings.xml @@ -0,0 +1,27 @@ + + + + Seznami s stiki + Pomoč + Urejanje prijav + Počakajte … + Pošlji + + Ne pokaži več + Odprtokodne informacije + Pojdi na stran donacij + Morda kasneje + Napaka DRM trgovine Google Play + + + + + + + + + + + + + diff --git a/scripts/fetch-translations.sh b/scripts/fetch-translations.sh index fbf3c7f4d..3f5ce0c3f 100755 --- a/scripts/fetch-translations.sh +++ b/scripts/fetch-translations.sh @@ -1,7 +1,7 @@ #!/bin/bash declare -A android -android=([ar_SA]=ar [ca]=ca [cs]=cs [da]=da [de]=de [es]=es [fr]=fr [hu]=hu [it]=it [ja]=ja [nl]=nl [nb_NO]=nb-rNO [pl]=pl [pt]=pt [pt_BR]=pt-rBR [ru]=ru [sr]=sr [tr_TR]=tr-rTR [uk]=uk [zh_CN]=zh-rCN [zh_TW]=zh-rTW) +android=([ar_SA]=ar [ca]=ca [cs]=cs [da]=da [de]=de [es]=es [fa]=fa [fr]=fr [hu]=hu [it]=it [ja]=ja [nl]=nl [nb_NO]=nb-rNO [pl]=pl [pt]=pt [pt_BR]=pt-rBR [ru]=ru [sl_SI]=sl-rSI [sr]=sr [tr_TR]=tr-rTR [uk]=uk [zh_CN]=zh-rCN [zh_TW]=zh-rTW) for lang in ${!android[@]} do @@ -9,9 +9,9 @@ do target_cert4android=../cert4android/src/main/res/values-${android[$lang]} mkdir -p $target_app - curl -n "https://www.transifex.com/api/2/project/davdroid/resource/app/translation/$lang?file" | + curl -n "https://www.transifex.com/api/2/project/davx5/resource/app/translation/$lang?file" | sed 's/\.\.\./…/g' > $target_app/strings.xml #mkdir -p $target_cert4android - #curl -n "https://www.transifex.com/api/2/project/davdroid/resource/cert4android/translation/$lang?file" >$target_cert4android/strings.xml + #curl -n "https://www.transifex.com/api/2/project/davx5/resource/cert4android/translation/$lang?file" >$target_cert4android/strings.xml done -- GitLab From 556843df4fb91ac4a3f3d74a128d4a1b2f8c979a Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 30 Dec 2018 12:02:22 +0100 Subject: [PATCH 020/382] Remove "Accounts may be gone after rebooting" startup dialog --- .../at/bitfire/davdroid/model/ServiceDB.kt | 4 +-- .../davdroid/ui/AppSettingsActivity.kt | 1 - .../davdroid/ui/StartupDialogFragment.kt | 35 ------------------- 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt b/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt index 634258e42..f870c4f20 100644 --- a/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt +++ b/app/src/main/java/at/bitfire/davdroid/model/ServiceDB.kt @@ -161,8 +161,8 @@ class ServiceDB { "overrideProxyHost" -> edit.putString(Settings.OVERRIDE_PROXY_HOST, cursor.getString(1)) "overrideProxyPort" -> edit.putInt(Settings.OVERRIDE_PROXY_PORT, cursor.getInt(1)) - StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED -> - edit.putBoolean(StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, cursor.getInt(1) != 0) + /*StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED -> + edit.putBoolean(StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, cursor.getInt(1) != 0)*/ StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED -> edit.putBoolean(StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED, cursor.getInt(1) != 0) } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt index 72d1e81e6..830ff853f 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AppSettingsActivity.kt @@ -137,7 +137,6 @@ class AppSettingsActivity: AppCompatActivity() { val settings = Settings.getInstance(requireActivity()) settings.remove(StartupDialogFragment.HINT_AUTOSTART_PERMISSIONS) settings.remove(StartupDialogFragment.HINT_BATTERY_OPTIMIZATIONS) - settings.remove(StartupDialogFragment.HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED) settings.remove(StartupDialogFragment.HINT_OPENTASKS_NOT_INSTALLED) Snackbar.make(view!!, R.string.app_settings_reset_hints_success, Snackbar.LENGTH_LONG).show() } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt index cf53aff30..ccaaf7bd4 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt @@ -13,8 +13,6 @@ import android.annotation.TargetApi import android.app.Dialog import android.content.Context import android.content.DialogInterface -import android.content.pm.PackageManager -import android.graphics.drawable.Drawable import android.net.Uri import android.os.Build import android.os.Bundle @@ -28,14 +26,12 @@ import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.resource.LocalTaskList import at.bitfire.davdroid.settings.Settings import java.util.* -import java.util.logging.Level class StartupDialogFragment: DialogFragment() { enum class Mode { AUTOSTART_PERMISSIONS, BATTERY_OPTIMIZATIONS, - GOOGLE_PLAY_ACCOUNTS_REMOVED, OPENTASKS_NOT_INSTALLED, OSE_DONATE } @@ -49,7 +45,6 @@ class StartupDialogFragment: DialogFragment() { private val autostartManufacturers = arrayOf("huawei", "letv", "oneplus", "vivo", "xiaomi", "zte") const val HINT_BATTERY_OPTIMIZATIONS = "hint_BatteryOptimizations" - const val HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED = "hint_GooglePlayAccountsRemoved" const val HINT_OPENTASKS_NOT_INSTALLED = "hint_OpenTasksNotInstalled" const val ARGS_MODE = "mode" @@ -61,14 +56,6 @@ class StartupDialogFragment: DialogFragment() { if (System.currentTimeMillis() > settings.getLong(SETTING_NEXT_DONATION_POPUP) ?: 0) dialogs += StartupDialogFragment.instantiate(Mode.OSE_DONATE) - // store-specific information - /*if (BuildConfig.FLAVOR == App.FLAVOR_GOOGLE_PLAY) { - // Play store - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && // only on Android <5 - settings.getBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED) != false) // and only when "Don't show again" hasn't been clicked yet - dialogs += StartupDialogFragment.instantiate(Mode.GOOGLE_PLAY_ACCOUNTS_REMOVED) - }*/ - // battery optimization white-listing if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && settings.getBoolean(HINT_BATTERY_OPTIMIZATIONS) != false) { val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager @@ -136,28 +123,6 @@ class StartupDialogFragment: DialogFragment() { } .create() - Mode.GOOGLE_PLAY_ACCOUNTS_REMOVED -> { - var icon: Drawable? = null - try { - icon = activity.packageManager.getApplicationIcon("com.android.vending").current - } catch(e: PackageManager.NameNotFoundException) { - Logger.log.log(Level.WARNING, "Can't load Play Store icon", e) - } - return AlertDialog.Builder(activity) - .setIcon(icon) - .setTitle(R.string.startup_google_play_accounts_removed) - .setMessage(R.string.startup_google_play_accounts_removed_message) - .setPositiveButton(R.string.startup_more_info) { _, _ -> - UiUtils.launchUri(requireActivity(), App.homepageUrl(requireActivity()).buildUpon() - .appendPath("faq").appendEncodedPath("accounts-gone-after-reboot-or-update/").build()) - } - .setNeutralButton(R.string.startup_not_now) { _, _ -> } - .setNegativeButton(R.string.startup_dont_show_again) { _, _ -> - settings.putBoolean(HINT_GOOGLE_PLAY_ACCOUNTS_REMOVED, false) - } - .create() - } - Mode.OPENTASKS_NOT_INSTALLED -> { val builder = StringBuilder(getString(R.string.startup_opentasks_not_installed_message)) if (Build.VERSION.SDK_INT < 23) -- GitLab From 0d75cf0cdcf420d8813b4b5e56a017f901fbb665 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 30 Dec 2018 12:37:33 +0100 Subject: [PATCH 021/382] Update strings --- app/src/main/res/values/strings.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7ebb40444..b7b6e5d38 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,8 +41,6 @@ We\'re happy that you use DAVx⁵, which is open-source software (GPLv3). Because developing DAVx⁵ is hard work and took us thousands of working hours, please consider a donation. Show donation page Maybe later - Play Store DRM bug information - Under certain conditions, Play Store DRM may cause all DAVx⁵ accounts to be gone after a reboot or after upgrading DAVx⁵. If you\'re affected by this problem (and only then), please install \"DAVx⁵ JB Workaround\" from Play Store. More information OpenTasks not installed To synchronize tasks, the free app OpenTasks is required. (Not required for contacts/events.) @@ -54,7 +52,7 @@ Version %1s (%2d) Compiled on %s © Ricki Hirner, Bernhard Stockmann (bitfire web engineering) - This version is only eligible for distribution over Google Play. + This version is only eligible for distribution over Google Play™. This program comes with ABSOLUTELY NO WARRANTY. It is free software, and you are welcome to redistribute it under certain conditions. -- GitLab From f8f60f4b52e27ad521d825006429885b49e8fb7a Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 30 Dec 2018 12:56:58 +0100 Subject: [PATCH 022/382] Update CI script --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9cffdd9c..1aa1b69e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: registry.gitlab.com/bitfireat/davdroid:latest +image: registry.gitlab.com/bitfireat/davx5-ose:latest before_script: - git submodule update --init --recursive -- GitLab From 3dea33e100fa3fd3243386a3a236cb18637a1abb Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 30 Dec 2018 14:00:25 +0100 Subject: [PATCH 023/382] Fix string references --- README.md | 4 ++-- app/src/main/java/at/bitfire/davdroid/log/Logger.kt | 2 +- .../main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 09d6cd8ba..c971fa096 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![build status](https://gitlab.com/bitfireAT/davx5/badges/master-ose/build.svg)](https://gitlab.com/bitfireAT/davx5/commits/master-ose) +[![build status](https://gitlab.com/bitfireAT/davx5-ose/badges/master-ose/build.svg)](https://gitlab.com/bitfireAT/davx5-ose/commits/master-ose) DAVx⁵ @@ -17,7 +17,7 @@ Help and discussion: [DAVx⁵ forums](https://www.davx5.com/forums/) **If you want to support DAVx⁵, please consider [donating to DAVx⁵](https://www.davx5.com/donate/) or [purchasing it](https://www.davx5.com/download/).** -Generated KDoc: https://bitfireAT.gitlab.io/davx5/dokka/app/ +Generated KDoc: https://bitfireAT.gitlab.io/davx5-ose/dokka/app/ Parts of DAVx⁵ have been outsourced into these libraries: diff --git a/app/src/main/java/at/bitfire/davdroid/log/Logger.kt b/app/src/main/java/at/bitfire/davdroid/log/Logger.kt index b3d857083..44f70f39b 100644 --- a/app/src/main/java/at/bitfire/davdroid/log/Logger.kt +++ b/app/src/main/java/at/bitfire/davdroid/log/Logger.kt @@ -65,7 +65,7 @@ object Logger { if (logToFile) { val builder = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_DEBUG) builder .setSmallIcon(R.drawable.ic_sd_storage_notification) - .setContentTitle(context.getString(R.string.logging_davdroid_file_logging)) + .setContentTitle(context.getString(R.string.logging_davx5_file_logging)) .setLocalOnly(true) val dir = context.getExternalFilesDir(null) diff --git a/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt index ccaaf7bd4..d3e784ecf 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/StartupDialogFragment.kt @@ -126,7 +126,7 @@ class StartupDialogFragment: DialogFragment() { Mode.OPENTASKS_NOT_INSTALLED -> { val builder = StringBuilder(getString(R.string.startup_opentasks_not_installed_message)) if (Build.VERSION.SDK_INT < 23) - builder.append("\n\n").append(getString(R.string.startup_opentasks_reinstall_davdroid)) + builder.append("\n\n").append(getString(R.string.startup_opentasks_reinstall_davx5)) return AlertDialog.Builder(activity) .setIcon(R.drawable.ic_playlist_add_check_dark) .setTitle(R.string.startup_opentasks_not_installed) -- GitLab From 241957f2dd1c2da87d39e2e894b7799514a517b8 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 30 Dec 2018 15:25:54 +0100 Subject: [PATCH 024/382] New launcher icons --- README.md | 2 + app/src/main/ic_launcher-web.png | Bin 24163 -> 23286 bytes .../drawable-v26/ic_launcher_foreground.xml | 36 ++++++++++++++++++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 2 +- app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3555 -> 3410 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2310 -> 2239 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4552 -> 4391 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 6801 -> 6521 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 8995 -> 8597 bytes app/src/main/res/mipmap/ic_launcher.png | Bin 46090 -> 0 bytes .../res/mipmap/ic_launcher_foreground.png | Bin 9025 -> 0 bytes 11 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable-v26/ic_launcher_foreground.xml delete mode 100644 app/src/main/res/mipmap/ic_launcher.png delete mode 100644 app/src/main/res/mipmap/ic_launcher_foreground.png diff --git a/README.md b/README.md index c971fa096..eb489affc 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![build status](https://gitlab.com/bitfireAT/davx5-ose/badges/master-ose/build.svg)](https://gitlab.com/bitfireAT/davx5-ose/commits/master-ose) +![DAVx⁵ logo](app/src/main/res/mipmap-xxhdpi/ic_launcher.png) + DAVx⁵ ======== diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png index a531285cdfab8e8e637906f659400892297b3484..4cc81409080c7754124235d4d31c11ce7a8434f0 100644 GIT binary patch literal 23286 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