bitfireat / vcard4android Goto Github PK
View Code? Open in Web Editor NEWAllows usage of vCard resources with the Android contacts provider
License: GNU General Public License v3.0
Allows usage of vCard resources with the Android contacts provider
License: GNU General Public License v3.0
KIND:group
is working, but KIND:GROUP
not. RFC 6350 specifies "group" and values are case-insensitive, but for better compatibility, the check should be case-insensitive.
Depends on #27
Running ./gradlew check
on Windows results in
> Task :vcard4android:testDebugUnitTest
at.bitfire.vcard4android.ContactTest > testVCard3FieldsAsVCard3 FAILED
org.junit.ComparisonFailure at ContactTest.kt:161
121 tests completed, 1 failed
> Task :vcard4android:testDebugUnitTest FAILED
> Task :vcard4android:testReleaseUnitTest FAILED
at.bitfire.vcard4android.ContactTest > testVCard3FieldsAsVCard3 FAILED
org.junit.ComparisonFailure at ContactTest.kt:161
121 tests completed, 1 failed
Appears to be a CR LF issue
Originally posted by fwiep August 22, 2023
Hello,
Using DAVx5 v4.3.5.2 from F-Droid on several devices, both Android 12.1 (2x custom built LineageOS) and Android 13 (1x stock Samsung UI).
Since about one week, all contacts no longer have their birthdays set on the Android device. In the Nextcloud, all is well and all the dates are present and shown on screen.
I have tried to clear the Contacts-app cache, even installed an extra (official Google) Contacts app to compare the result. On all three devices, not a single birthday is shown. The Contact app allows me to add a birthday to the contact, but via DAVx5, no birthday gets synchronized.
When I edit, say the 'Notes' field of a contact in the Nextcloud, then sync through DAVx5, this modification shows up instantly.
How can I get the birthdays back into my Android contacts?
How should I debug this issue? I have collected a sync's verbose logfile, but no error is shown.
Thanks for your help,
FWieP
Currently, we have support for the RAW_CONTACT_IS_READ_ONLY flag.
However Contacts apps can still change the data rows of read-only contacts (and quite everything is a data row, for instance the name). So for native contacts read-only support, we need
Similar to bitfireAT/cert4android#13 and bitfireAT/ical4android#101
I think vcard4android is easier than ical4android, so we should do it first.
I also hope that it won't be more difficult to debug / test real-world problems. But if it is, we can think of a good solution then.
Recently I get multiple reports from t-online.de customers that used to sync contacts with DAVx5. They seem to have changed or added something in their backend and DAVx5 receives an unsupported field that seems to stop the sync:
05-09 10:26:11.087 3181 3239 I davx5 : [syncadapter.ContactsSyncManager] Downloading 1 vCard(s): [https://spica.t-online.de/spica-contacts/carddav/principals/[email protected]/addressBooks/default/120049010000000015465723-3496076699.vcf]
05-09 10:26:11.486 3181 3239 I davx5 : [syncadapter.ContactsSyncManager] Processing CardDAV resource 120049010000000015465723-3496076699.vcf
05-09 10:26:11.683 3181 3239 W vcard4android: [ContactReader] checkPartialDate: unsupported DateOrTimeProperty
05-09 10:26:11.683 3181 3239 W vcard4android: PARAMETER #0 = ezvcard.property.Birthday [ group=null | parameters={} | text=null | date=1964-05-07T00:00:00Z | partialDate=null ]
05-09 10:26:11.756 3181 3239 I davx5 : [syncadapter.ContactsSyncManager] Updating 120049010000000015465723-3496076699.vcf in local address book
05-09 10:26:11.756 3181 3239 I davx5 : PARAMETER #0 = at.bitfire.vcard4android.Contact@fe63c60[addresses=[LabeledProperty(property=ezvcard.property.Address [ group=null | parameters={TYPE=[HOME]} | poBoxes=[] | extendedAddresses=[] | streetAddresses=[Schwemmweg 14] | localities=[Bergrheinfeld] | regions=[] | postalCodes=[97493] | countries=[Deutschland] ], label=null)],anniversary=<null>,birthDay=ezvcard.property.Birthday [ group=null | parameters={} | text=null | date=1964-05-07T00:00:00Z | partialDate=null ],categories=[],customDates=[],displayName=Thomas Herbert,emails=[LabeledProperty(property=ezvcard.property.Email [ group=null | parameters={TYPE=[HOME, PREF]} | [email protected] ], label=null)],familyName=Herbert,givenName=Thomas,group=false,impps=[],jobDescription=<null>,jobTitle=<null>,members=[],middleName=<null>,nickName=<null>,note=<null>,organization=<null>,phoneNumbers=[LabeledProperty(property=ezvcard.property.Telephone [ group=null | parameters={TYPE=[VOICE, HOME]} | uri=null | text=09721-69417 ], label=null), LabeledProperty(property=ezvcard.property.Telephone [ group=null | parameters={TYPE=[VOICE, CELL]} | uri=null | text=+49 174 7862230 ], label=null)],phoneticFamilyName=<null>,phoneticGivenName=<null>,phoneticMiddleName=<null>,prefix=<null>,relations=[],suffix=<null>,uid=3496076699,unknownProperties=<null>,urls=[]]
05-09 10:26:11.825 3181 3239 E davx5 : [syncadapter.SyncManager] Unclassified sync error
05-09 10:26:11.825 3181 3239 E davx5 : EXCEPTION j$.time.temporal.p: Unsupported field: YearOfEra
05-09 10:26:11.825 3181 3239 E davx5 : at j$.time.Instant.p(SourceFile:0)
05-09 10:26:11.825 3181 3239 E davx5 : at j$.time.format.y.e(Unknown Source:16)
05-09 10:26:11.825 3181 3239 E davx5 : at j$.time.format.k.e(Unknown Source:2)
05-09 10:26:11.825 3181 3239 E davx5 : at j$.time.format.f.e(Unknown Source:20)
05-09 10:26:11.825 3181 3239 E davx5 : at j$.time.format.DateTimeFormatter.format(SourceFile:0)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.vcard4android.contactrow.EventBuilder.buildEvent(EventBuilder.kt:23)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.vcard4android.contactrow.EventBuilder.buildEvent$default(EventBuilder.kt:6)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.vcard4android.contactrow.EventBuilder.build(EventBuilder.kt:19)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.vcard4android.contactrow.ContactProcessor.insertDataRows(ContactProcessor.kt:38)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.vcard4android.AndroidContact.insertDataRows(AndroidContact.kt:27)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.vcard4android.AndroidContact.update(AndroidContact.kt:101)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.resource.LocalContact.update(LocalContact.kt:3)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$processCard$1.invoke(ContactsSyncManager.kt:14)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$processCard$1.invoke(ContactsSyncManager.kt:1)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager.localExceptionContext(SyncManager.kt:8)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager.processCard(ContactsSyncManager.kt:88)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager.access$processCard(ContactsSyncManager.kt:1)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1$1$1.invoke(ContactsSyncManager.kt:10)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1$1$1.invoke(ContactsSyncManager.kt:1)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager.responseExceptionContext(SyncManager.kt:13)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1.invoke$lambda$0(ContactsSyncManager.kt:21)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1.$r8$lambda$aTmx16DT01n3ZmMyYWepKV6Txjc(ContactsSyncManager.kt:1)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1$$ExternalSyntheticLambda0.onResponse(R8$$SyntheticClass:3)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.dav4jvm.Response$Companion.parse(Response.kt:306)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.dav4jvm.DavResource.processMultiStatus$parseMultiStatus(DavResource.kt:51)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.dav4jvm.DavResource.processMultiStatus(DavResource.kt:16)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.dav4jvm.DavResource.processMultiStatus(DavResource.kt:9)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.dav4jvm.DavAddressBook.multiget(DavAddressBook.kt:67)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1.invoke(ContactsSyncManager.kt:10)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1.invoke(ContactsSyncManager.kt:1)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager.remoteExceptionContext(SyncManager.kt:1)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager.remoteExceptionContext(SyncManager.kt:5)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncManager.downloadRemote(ContactsSyncManager.kt:46)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager
05-09 10:26:11.825 3181 3239 E davx5 : $syncRemote$1$download$1.invokeSuspend(SyncManager.kt:12)
05-09 10:26:11.825 3181 3239 E davx5 : at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9)
05-09 10:26:11.825 3181 3239 E davx5 : at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:107)
05-09 10:26:11.825 3181 3239 E davx5 : at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:154)
05-09 10:26:11.825 3181 3239 E davx5 : at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:103)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager.syncRemote(SyncManager.kt:41)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager$performSync$1.invoke(SyncManager.kt:46)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager$performSync$1.invoke(SyncManager.kt:1)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager.unwrapExceptions(SyncManager.kt:2)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncManager.performSync(SyncManager.kt:20)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.ContactsSyncAdapterService$ContactsSyncAdapter.sync(ContactsSyncAdapterService.kt:246)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncAdapterService$SyncAdapter$onPerformSync$1.invoke(SyncAdapterService.kt:8)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncAdapterService$SyncAdapter$onPerformSync$1.invoke(SyncAdapterService.kt:1)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.util.ConcurrentUtils.runSingle(ConcurrentUtils.kt:21)
05-09 10:26:11.825 3181 3239 E davx5 : at at.bitfire.davdroid.syncadapter.SyncAdapterService$SyncAdapter.onPerformSync(SyncAdapterService.kt:108)
05-09 10:26:11.825 3181 3239 E davx5 : at android.content.AbstractThreadedSyncAdapter$SyncThread.run(AbstractThreadedSyncAdapter.java:354)
05-09 10:26:11.890 3181 3239 I davx5 : [syncadapter.ContactsSyncAdapterService] Contacts sync complete
I think we should handle this field somehow but we don't have a test account for t-online and we'd need a German number to create a free account for this, which I don't have :-/ I can ask for a test account from the customer, otherwise we can only "work-into-the-blue" on this.
Here are the logs for it:
year-of-era-debug-info.txt
year-of-era-logcat.txt
References:
Zammad ticket #1: https://bitfire-at.zammad.com/#ticket/zoom/986
Zammad ticket #2: https://bitfire-at.zammad.com/#ticket/zoom/987
Email by Heribert Geller: [email protected] from May 10th 14:59 โ [email protected]
Raw-contact photos can be saved either
Because of its advantages, method 2 should be used when possible. There is however a problem which has forced us to use method 1 until now:
caller_is_syncadapter
or not.We should give another try to use method 2 if any possible. Useful information:
ContactsProvider2
uses a write lock when updating the photo. Maybe an update with DIRTY=0 some time after inserting the photo would block until the photo is actually inserted.ContactsProvider2Test
and some other projects simply wait until the photo data row is available. However then we need a timeout, and what for instance if photo insertion failed?Like #31, but also for other possibilities of a FN that is generated from data just for the purpose of having a FN in the vCard
We could start with the libraries to use the gradle version catalog for dependencies.
Steps to reproduce:
FN:Only A Company
ORG:Only A Company
because vCard always requires a FN
.
Result:
If the downloaded contact is opened in a contacts app, it shows the correct organization but also First name: Only, Middle name: A, Last name: Company.
Expected result:
Contacts app should only contain the organization, without first/last name.
Hi, I am running into an exception while syncing contacts with Davx5.
As a result the sync fails Soft error (max retries reached)
and a lot of contacts are actually missing.
Probaly just the faulty Contactimage should be skipped, but other data and other contacts should still be synced?
Not sure if this issue should rather be put into the davx5 repo...
The StackTrace leads me to the Photobuilder.kt from reading the source it looks like it should throw an ContactsStorageException. But the uncaught/unchecked IO exception seems to prevent that.
Here is the StackTrace:
--- BEGIN DEBUG INFO ---
SYNCHRONIZATION INFO Account: Account {name=......), type=at.bitfire.davdroid.address_book} Authority: com.android.contacts EXCEPTION
java.io.IOException: write failed: EPIPE (Broken pipe) at libcore.io.IoBridge.write(IoBridge.java:651) at
java.io.FileOutputStream.write(FileOutputStream.java:406) at java.io.FileOutputStream.write(FileOutputStream.java:384) at
at.bitfire.vcard4android.contactrow.PhotoBuilder$Companion.insertPhoto(PhotoBuilder.kt:146) at
at.bitfire.vcard4android.AndroidContact.update(AndroidContact.kt:138) at
at.bitfire.davdroid.resource.LocalContact.update(LocalContact.kt:3) at
at.bitfire.davdroid.syncadapter.ContactsSyncManager$processCard$1.invoke(ContactsSyncManager.kt:14) at
at.bitfire.davdroid.syncadapter.ContactsSyncManager$processCard$1.invoke(ContactsSyncManager.kt:1) at
at.bitfire.davdroid.syncadapter.SyncManager.localExceptionContext(SyncManager.kt:8) at
at.bitfire.davdroid.syncadapter.ContactsSyncManager.processCard(ContactsSyncManager.kt:88) at
at.bitfire.davdroid.syncadapter.ContactsSyncManager.access$processCard(ContactsSyncManager.kt:1) at
at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1$1$1.invoke(ContactsSyncManager.kt:10) at
at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1$1$1.invoke(ContactsSyncManager.kt:1) at
at.bitfire.davdroid.syncadapter.SyncManager.responseExceptionContext(SyncManager.kt:13) at
at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1.invoke$lambda$0(ContactsSyncManager.kt:22) at
at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1.$r8$lambda$aTmx16DT01n3ZmMyYWepKV6Txjc(ContactsSyncManager.kt:1) at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1$$ExternalSyntheticLambda0.onResponse(R8$$SyntheticClass:3) at at.bitfire.dav4jvm.Response$Companion.parse(Response.kt:307) at at.bitfire.dav4jvm.DavResource.processMultiStatus$parseMultiStatus(DavResource.kt:51) at at.bitfire.dav4jvm.DavResource.processMultiStatus(DavResource.kt:16) at at.bitfire.dav4jvm.DavResource.processMultiStatus(DavResource.kt:9) at at.bitfire.dav4jvm.DavAddressBook.multiget(DavAddressBook.kt:69) at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1.invoke(ContactsSyncManager.kt:10) at at.bitfire.davdroid.syncadapter.ContactsSyncManager$downloadRemote$1.invoke(ContactsSyncManager.kt:1) at at.bitfire.davdroid.syncadapter.SyncManager.remoteExceptionContext(SyncManager.kt:1) at at.bitfire.davdroid.syncadapter.SyncManager.remoteExceptionContext(SyncManager.kt:5) at at.bitfire.davdroid.syncadapter.ContactsSyncManager.downloadRemote(ContactsSyncManager.kt:46) at at.bitfire.davdroid.syncadapter.SyncManager$syncRemote$1$download$1.invokeSuspend(SyncManager.kt:14) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:112) at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:163) at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:103) at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source:3) at at.bitfire.davdroid.syncadapter.SyncManager.syncRemote(SyncManager.kt:39) at at.bitfire.davdroid.syncadapter.SyncManager$performSync$1.invoke(SyncManager.kt:29) at at.bitfire.davdroid.syncadapter.SyncManager$performSync$1.invoke(SyncManager.kt:1) at at.bitfire.davdroid.syncadapter.SyncManager.unwrapExceptions(SyncManager.kt:2) at at.bitfire.davdroid.syncadapter.SyncManager.performSync(SyncManager.kt:20) at at.bitfire.davdroid.syncadapter.ContactSyncer.sync(ContactSyncer.kt:218) at at.bitfire.davdroid.syncadapter.Syncer.onPerformSync(Syncer.kt:119) at at.bitfire.davdroid.syncadapter.SyncWorker$doWork$2$1$1.invoke(SyncWorker.kt:4) at at.bitfire.davdroid.syncadapter.SyncWorker$doWork$2$1$1.invoke(SyncWorker.kt:1) at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invokeSuspend(Interruptible.kt:59) at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invoke(Interruptible.kt:13) at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:5) at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:41) at kotlinx.coroutines.InterruptibleKt.runInterruptible$default(Interruptible.kt:9) at at.bitfire.davdroid.syncadapter.SyncWorker$doWork$2.invokeSuspend(SyncWorker.kt:503) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:9) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:112) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644) at java.lang.Thread.run(Thread.java:1012) Caused by: android.system.ErrnoException: write failed: EPIPE (Broken pipe) at libcore.io.Linux.writeBytes(Native Method) at libcore.io.Linux.write(Linux.java:296) at libcore.io.ForwardingOs.write(ForwardingOs.java:943) at libcore.io.BlockGuardOs.write(BlockGuardOs.java:448) at libcore.io.ForwardingOs.write(ForwardingOs.java:943) at libcore.io.IoBridge.write(IoBridge.java:646) ... 52
Locale(s): [en_US] Time zone: Europe/Berlin
4.3.12.1-ose
Android version: 14 (UQ1A.240105.002) Device: Google Pixel 6a (bluejay)
but happens also also on older versions and other phones
This test
Was commented until mangstadt/ez-vcard#113 is merged, which now it is. I don't know in which version it was fixed, so maybe we have to wait until #11 is closed.
This is a carry over from: etesync/android#244
There is an issue where Android contact list apps when interacting with Etesync (and thus this library) are creating odd behavior not seen otherwise.
Specifically using Etesync (which uses this library) and its interaction with Android contact list apps (ie. Google Contacts and Apple contacts; another user has confirmed similar behavior of sorts on linux).
Basically, when a contact is created or imported that does not contain a first/middle/last name and only contains a Organization name (with other contact details). Basically, a vCard not associated with a person, but a business entity.
Example vCard (that doesn't have a problem, not associated with Etesync or this library):
BEGIN:VCARD
VERSION:4.0
FN:
N:;;;;
ORG:Test Company
TITLE:
TEL;TYPE=work:123.456.7890
ADR;TYPE=HOME:;;123 Walnut St;NoWhere;CO;12345;United States
END:VCARD
But this same vCard when assiociated with Etesync (and thus this library) will copy the Org
attribute to the FN
attribute (Android -->> Etesync). But then when it comes back (Etesync -->> Android), it is copy the FN
attribute to the N
attribute and screwS up organization.
The Org
attribute getting copied to the FN
atrribute doesn't seem to be a big problem, from my reading that is required (though Google, Outlook, etc don't seem to care and ignore that requirement). It is when the N
attribute gets populated with the same data stored in the Org
attribute. But maybe it is bothv I don't know.
This does not occur when syncing to GMail, Outlook, or using DecSync (though DecSync seems to just store, not process, for syncing). I have not found another that does this.
This issue screws my contact list organization up pretty bad.
Another issue that goes along with this is how the address fields get messed up. In the apps, the address is divided into separate (sub) fields. When Etesync (& this library) interacts with a contact, it will copy the entire address into a single field (some times the PO Box, others into the street address along with what is already there). This screws addresses up pretty bad, will take me hours to fix, but no reason to fix it when it just get undone on the next sync.
Please see the other issue ticket on Etesync for further details: etesync/android#244
SIP (Android: SipAddress) is primarily used for VoIP, which should be saved as TEL
property. Additionally an IMPP
entry could be created. However the IMPP
entry should not be the only/primary one.
ez-vcard 0.12.0 requires Java 8, for instance the java.nio classes seem to require Android SDK level 26. On the other side, we don't use file access by ez-vcard (only streams) and there's desugaring, so we should check whether using ez-vcard 0.12.0 is possible with minSdkVersion 19 or 21 (DAVx5 currently has 21).
If yes, upgrade, otherwise we maybe have to raise the minimum SDK level for vcard4android and DAVx5 at some time.
On Android <12, the pre-defined protocols for IM can still be used.
On Android โฅ 12 there are still open questions.
See https://forums.bitfire.at/topic/2690/jabber-im-entry-sync-as-xmpp/.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.