Contacts Provider

Der Kontaktdatenanbieter ist eine leistungsstarke und flexible Android-Komponente, die das zentrale Repository des Geräts mit Daten zu Personen verwaltet. Der Contacts Provider ist die Datenquelle die Sie in der Kontakte App auf Ihrem Gerät sehen. Sie können auch in Ihrem eigenen Anwendung ausführen und Daten zwischen dem Gerät und den Onlinediensten übertragen. Der Anbieter berücksichtigt verschiedene Datenquellen arbeiten und versucht, so viele Daten wie möglich für jede Person zu verwalten, dass die Organisation komplex ist. Daher enthält die API des Anbieters ein an Vertragsklassen und Benutzeroberflächen, die sowohl den Datenabruf als auch Änderung.

In diesem Leitfaden wird Folgendes beschrieben:

  • Die grundlegende Struktur des Anbieters.
  • So rufen Sie Daten vom Anbieter ab.
  • So ändern Sie Daten beim Anbieter.
  • So schreiben Sie einen Synchronisierungsadapter zum Synchronisieren von Daten von Ihrem Server mit dem Kontaktdatenanbieter.

In diesem Leitfaden wird davon ausgegangen, dass Sie mit den Grundlagen von Android-Inhaltsanbietern vertraut sind. Weitere Informationen zu Android-Contentanbietern Leitfaden zu den Grundlagen des Contentanbieters.

Organisation des Kontaktanbieters

Der Contacts Provider ist eine Android-Contentanbieterkomponente. Es werden drei Arten von Daten zu einer Person gespeichert, die jeweils einer vom Anbieter angebotenen Tabelle entsprechen, wie in Abbildung 1 dargestellt:

Abbildung 1. Tabellenstruktur des Kontaktanbieters

Auf die drei Tabellen wird im Allgemeinen mit den Namen ihrer Vertragsklassen verwiesen. Die Klassen definieren Konstanten für Inhalts-URIs, Spaltennamen und Spaltenwerte, die in den Tabellen verwendet werden:

Tabelle ContactsContract.Contacts
Zeilen, die verschiedene Personen darstellen, basierend auf Aggregationen unverarbeiteter Kontaktzeilen
Tabelle ContactsContract.RawContacts
Zeilen mit einer Zusammenfassung der Daten einer Person speziell für das jeweilige Nutzerkonto und den jeweiligen Nutzertyp.
Tabelle ContactsContract.Data
Zeilen mit den Details zu unformatierten Kontakten, z. B. E-Mail-Adressen oder Telefonnummern.

Die anderen Tabellen, die in ContactsContract durch Vertragsklassen dargestellt werden Hilfstabellen sind, die der Contacts Provider für die Verwaltung seines Betriebs oder des Supports verwendet Funktionen in den Kontakten oder Telefonieanwendungen des Geräts.

Rohkontakte

Ein unformatierter Kontakt steht für die Daten einer Person, die von einem einzelnen Kontotyp und einem einzigen Konto stammen. Namen. Da der Contacts Provider mehr als einen Onlinedienst als Quelle von Daten für eine Person enthält, lässt der Contacts Provider mehrere unbearbeitete Kontakte für dieselbe Person zu. Mit mehreren unformatierten Kontakten kann ein Nutzer auch die Daten einer Person aus mehreren Konten zusammenführen desselben Kontotyps erstellen.

Die meisten Daten für einen Rohkontakt werden nicht in der Tabelle ContactsContract.RawContacts gespeichert. Stattdessen werden sie in einer oder mehreren Zeilen in der Tabelle ContactsContract.Data gespeichert. Jede Datenzeile hat eine Spalte Data.RAW_CONTACT_ID, die den RawContacts._ID-Wert der übergeordneten Zeile ContactsContract.RawContacts enthält.

Wichtige Spalten für Rohkontakte

Die wichtigsten Spalten in der Tabelle ContactsContract.RawContacts sind in Tabelle 1 aufgeführt. Bitte lesen Sie die Hinweise nach der Tabelle:

Tabelle 1 Wichtige Spalten mit unverarbeiteten Kontaktdaten.

Spaltenname Verwenden Hinweise
ACCOUNT_NAME Der Kontoname für den Kontotyp, der die Quelle dieses Rohkontakts ist. Der Kontoname eines Google-Kontos ist beispielsweise eine der Gmail-Adressen des Geräteeigentümers. Den nächsten Eintrag zu Weitere Informationen: ACCOUNT_TYPE Informationen. Das Format dieses Namens ist vom Kontotyp abhängig. Es muss sich nicht unbedingt um eine E-Mail-Adresse handeln.
ACCOUNT_TYPE Der Kontotyp, der die Quelle dieses unformatierten Kontakts ist. Der Kontotyp eines Google-Kontos ist beispielsweise com.google. Kontotyp immer qualifizieren durch eine Domain-ID für eine Domain, die Ihnen gehört oder die Sie kontrollieren. Dadurch wird sichergestellt, Kontotyp eindeutig ist. Ein Kontotyp, der Kontaktdaten anbietet, hat normalerweise einen zugehörigen Synchronisierungsadapter, der wird mit dem Contacts Provider synchronisiert.
DELETED Das Flag „deleted“ (gelöscht) für einen Rohkontakt. Mit diesem Flag kann der Kontaktanbieter die Zeile bis zur Synchronisierung intern beibehalten Adapter können die Zeile von ihren Servern löschen und anschließend die Zeile löschen. aus dem Repository.

Hinweise

Im Folgenden finden Sie wichtige Hinweise zur Tabelle ContactsContract.RawContacts:

  • Der Name eines Rohkontakts wird nicht in seiner Zeile in ContactsContract.RawContacts gespeichert. Stattdessen wird sie in der Tabelle ContactsContract.Data in einer Zeile vom Typ ContactsContract.CommonDataKinds.StructuredName gespeichert. Ein unverarbeiteter Kontakt enthält nur eine Zeile dieses Typs in der Tabelle ContactsContract.Data.
  • Achtung: Wenn Sie Ihre eigenen Kontodaten in einer Zeile mit Rohkontakten verwenden möchten, müssen sie zuerst bei der AccountManager registriert werden. Fordern Sie dazu Nutzer, um den Kontotyp und ihren Kontonamen der Liste der Konten hinzuzufügen. Andernfalls wird die Zeile mit den Rohkontakten vom Kontaktdatenanbieter automatisch gelöscht.

    Wenn Sie beispielsweise möchten, dass Ihre App Kontaktdaten für Ihren webbasierten Dienst verwaltet mit der Domain com.example.dataservice und dem Nutzerkonto für Ihren Dienst becky.sharp@dataservice.example.com ist, muss der Nutzer zuerst das Konto hinzufügen "type" (com.example.dataservice) und Konto „name“ (becky.smart@dataservice.example.com), bevor Ihre App unbearbeitete Kontaktzeilen hinzufügen kann. Sie können diese Anforderung in der Dokumentation erläutern oder den Nutzer auffordern, den Typ und den Namen hinzuzufügen. Kontotypen und Kontonamen werden im nächsten Abschnitt ausführlicher beschrieben.

Quellen für Rohkontaktdaten

Um zu verstehen, wie Rohkontakte funktionieren, nehmen wir an, dass die Nutzerin „Emily Dickinson“ die folgenden drei Nutzerkonten auf ihrem Gerät definiert hat:

  • emily.dickinson@gmail.com
  • emilyd@gmail.com
  • Twitter-Konto „belle_of_amherst“

Dieser Nutzer hat in den Einstellungen unter Konten die Option Kontakte synchronisieren für alle drei Konten aktiviert.

Angenommen, Emily Dickinson öffnet ein Browserfenster und meldet sich mit der emily.dickinson@gmail.com, geöffnet Kontakte und fügt "Thomas Higginson" hinzu. Später meldet sie sich als emilyd@gmail.com in Gmail an und sendet eine E-Mail an „Thomas Higginson“. Dadurch wird er automatisch als Kontakt hinzugefügt. Sie folgt auch „colnel_tom“ (Twitter-ID von Thomas Higginson) auf Twitter

Im Rahmen dieser Arbeit erstellt der Contacts Provider drei unbearbeitete Kontakte:

  1. Ein Rohkontakt für „Thomas Higginson“, der mit emily.dickinson@gmail.com verknüpft ist. Der Nutzerkontotyp ist „Google“.
  2. Ein zweiter Rohkontakt für „Thomas Higginson“, der mit emilyd@gmail.com verknüpft ist. Der Typ des Nutzerkontos ist ebenfalls „Google“. Es gibt einen zweiten Rohkontakt, obwohl der Name mit einem früheren Namen identisch ist, weil die Person für ein anderes Nutzerkonto hinzugefügt wurde.
  3. Ein dritter Rohkontakt für „Thomas Higginson“, der mit „belle_of_amherst“ verknüpft ist. Der Nutzer den Kontotyp „Twitter“ ist.

Daten

Wie bereits erwähnt, werden die Daten für einen Rohkontakt in einer ContactsContract.Data-Zeile gespeichert, die mit dem _ID-Wert des Rohkontakts verknüpft ist. So kann ein einzelner Rohkontakt mehrere Instanzen desselben Datentyps wie E-Mail-Adressen oder Telefonnummern haben. Wenn „Thomas Higginson“ für emilyd@gmail.com (die Zeile mit den Rohdaten für Thomas Higginson, die mit dem Google-Konto emilyd@gmail.com verknüpft ist) beispielsweise die private E-Mail-Adresse thigg@gmail.com und die geschäftliche E-Mail-Adresse thomas.higginson@gmail.com hat, speichert der Kontaktdatenanbieter die beiden E-Mail-Adressenzeilen und verknüpft sie beide mit dem Rohkontakt.

Beachten Sie, dass in dieser einzelnen Tabelle verschiedene Datentypen gespeichert sind. Anzeigename, Telefonnummer, E-Mail-Adresse, Postanschrift, Foto und Website-Detailzeilen finden Sie alle in der Tabelle „ContactsContract.Data“. Die Funktion Die Tabelle „ContactsContract.Data“ enthält einige Spalten mit aussagekräftigen Namen. und andere mit allgemeinen Namen. Der Inhalt einer Spalte für beschreibenden Namen hat dieselbe Bedeutung unabhängig vom Datentyp in der Zeile, während der Inhalt einer Spalte mit generischen Namen verschiedene Bedeutungen haben.

Beschreibende Spaltennamen

Hier einige Beispiele für beschreibende Spaltennamen:

RAW_CONTACT_ID
Der Wert der Spalte _ID des Rohkontakts für diese Daten.
MIMETYPE
Der Datentyp, der in dieser Zeile gespeichert ist, als benutzerdefinierter MIME-Typ. Kontakte-Anbieter verwendet die MIME-Typen, die in den abgeleiteten Klassen von ContactsContract.CommonDataKinds Diese MIME-Typen sind Open Source, und kann von jeder Anwendung oder jedem Synchronisierungsadapter verwendet werden, der mit dem Contacts Provider funktioniert.
IS_PRIMARY
Wenn diese Art von Datenzeile für einen Rohkontakt mehrmals vorkommen kann, wird in der Spalte IS_PRIMARY die Datenzeile mit den primären Daten für den Typ gekennzeichnet. Beispiel: Der Nutzer drückt lange auf die Telefonnummer eines Kontakts und wählt Als Standard festlegen aus. dann ist die Zeile ContactsContract.Data mit dieser Zahl für die Spalte IS_PRIMARY auf einen Wert ungleich null haben.

Generische Spaltennamen

Es gibt 15 allgemeine Spalten mit den Namen DATA1 bis DATA15, die allgemein verfügbar sind, sowie vier weitere allgemeine Spalten SYNC1 bis SYNC4, die nur für die Synchronisierung verwendet werden sollen Adapter. Die Konstanten für generische Spaltennamen funktionieren immer, unabhängig vom Typ der die die Zeile enthält.

Die Spalte DATA1 ist indexiert. Der Kontaktanbieter verwendet diese Spalte immer für Die vom Anbieter erwarteten Daten sind das häufigste Ziel einer Abfrage. Beispiel: in einer E-Mail-Zeile enthält, enthält diese Spalte die eigentliche E-Mail-Adresse.

Die Spalte DATA15 ist standardmäßig für das Speichern von BLOB-Daten (Binary Large Object) wie Foto-Thumbnails reserviert.

Typspezifische Spaltennamen

Um die Arbeit mit den Spalten für einen bestimmten Zeilentyp zu erleichtern, stellt der Kontaktdatenanbieter auch typspezifische Konstanten für Spaltennamen bereit, die in Unterklassen von ContactsContract.CommonDataKinds definiert sind. Die Konstanten geben einfach ein verschiedenen Konstantennamen auf denselben Spaltennamen. Dies erleichtert den Zugriff auf Daten in einer Zeile einer bestimmten Typs.

Die Klasse ContactsContract.CommonDataKinds.Email definiert beispielsweise typspezifische Spaltennamenkonstanten für eine ContactsContract.Data-Zeile mit dem MIME-Typ Email.CONTENT_ITEM_TYPE. Die Klasse enthält die Konstante ADDRESS für die E-Mail-Adresse Spalte. Der tatsächliche Wert von ADDRESS ist „data1“, was dem generischen Namen der Spalte entspricht.

Achtung: Fügen Sie der Tabelle ContactsContract.Data keine benutzerdefinierten Daten hinzu, indem Sie eine Zeile mit einem der vordefinierten MIME-Typen des Anbieters verwenden. Andernfalls können die Daten verloren gehen oder der Anbieter kann nicht mehr ordnungsgemäß funktionieren. Sie sollten beispielsweise keine Zeile mit dem MIME-Typ hinzufügen. Email.CONTENT_ITEM_TYPE, die einen Nutzernamen anstelle einer E-Mail-Adresse in der Spalte DATA1. Wenn Sie für die Zeile einen benutzerdefinierten MIME-Typ verwenden, können Sie eigene typspezifische Spaltennamen definieren und die Spalten nach Belieben verwenden.

Abbildung 2 zeigt, wie Beschreibungsspalten und Datenspalten in einer Zeile vom Typ ContactsContract.Data angezeigt werden und wie sich typspezifische Spaltennamen über die generischen Spaltennamen legen.

Zuordnung von typspezifischen Spaltennamen zu generischen Spaltennamen

Abbildung 2. Typspezifische und generische Spaltennamen.

Typspezifische Klassen für Spaltennamen

In Tabelle 2 sind die am häufigsten verwendeten typspezifischen Klassen für Spaltennamen aufgeführt:

Tabelle 2 Typspezifische Klassen für Spaltennamen

Mapping-Klasse Datentyp Hinweise
ContactsContract.CommonDataKinds.StructuredName Die Namensdaten für den Rohkontakt, der mit dieser Datenzeile verknüpft ist. Ein Rohkontakt hat nur eine dieser Zeilen.
ContactsContract.CommonDataKinds.Photo Das Hauptfoto für den Rohkontakt, der mit dieser Datenzeile verknüpft ist. Ein Rohkontakt hat nur eine dieser Zeilen.
ContactsContract.CommonDataKinds.Email Eine E-Mail-Adresse des unformatierten Kontakts, der mit dieser Datenzeile verknüpft ist. Ein unformatierter Kontakt kann mehrere E-Mail-Adressen haben.
ContactsContract.CommonDataKinds.StructuredPostal Eine Postadresse für den Rohkontakt, der mit dieser Datenzeile verknüpft ist. Ein Rohkontakt kann mehrere Postadressen haben.
ContactsContract.CommonDataKinds.GroupMembership Eine Kennung, die den Rohkontakt mit einer der Gruppen im Kontaktdatenanbieter verknüpft. Gruppen sind eine optionale Funktion eines Kontotyps und Kontonamens. Sie werden im Abschnitt Kontaktgruppen ausführlicher beschrieben.

Kontakte

Der Kontaktdatenanbieter kombiniert die Rohzeilen der Kontakte aller Kontotypen und Kontonamen zu einem Kontakt. Dies vereinfacht die Anzeige und Änderung aller Daten die ein Nutzer für eine Person erfasst hat. Der Kontaktanbieter verwaltet die Erstellung neuer Kontakte Zeilen und die Aggregation der unbearbeiteten Kontakte mit einer vorhandenen Kontaktzeile. Weder Anwendungen noch Synchronisierungsadapter dürfen Kontakte hinzufügen. Einige Spalten in einer Kontaktzeile sind schreibgeschützt.

Hinweis: Wenn Sie versuchen, dem Kontaktdatenanbieter einen Kontakt mit einer insert() hinzuzufügen, erhalten Sie eine UnsupportedOperationException-Ausnahme. Wenn Sie versuchen, eine Spalte zu aktualisieren, die als „schreibgeschützt“ aufgeführt ist, wird die Aktualisierung ignoriert.

Der Kontaktdatenanbieter erstellt einen neuen Kontakt, wenn ein neuer Rohkontakt hinzugefügt wird, der keinem vorhandenen Kontakt entspricht. Dies geschieht auch, wenn sich die Daten eines vorhandenen Kontakts so ändern, dass sie nicht mehr mit dem Kontakt übereinstimmen, mit dem sie zuvor verknüpft waren. Wenn eine Anwendung oder ein Synchronisierungsadapter einen neuen Rohkontakt erstellt, nicht mit einem vorhandenen Kontakt übereinstimmt, wird der neue Rohkontakt mit dem vorhandenen Kontakt.

Der Kontaktdatenanbieter verknüpft eine Kontaktdatenzeile mit ihren Rohdatenzeilen über die Spalte _ID der Kontaktdatenzeile in der Tabelle Contacts. Spalte CONTACT_ID der Tabelle mit unformatierten Kontakten ContactsContract.RawContacts enthält _ID Werte für die Zeile mit Kontakten, die mit jeder Zeile mit unformatierten Kontakten verknüpft ist.

Die Tabelle ContactsContract.Contacts enthält auch die Spalte LOOKUP_KEY ist "dauerhaft" auf die Kontaktzeile. Da der Kontaktdatenanbieter Kontakte automatisch verwaltet, kann er den Wert _ID einer Kontaktzeile als Reaktion auf eine Aggregation oder Synchronisierung ändern. In diesem Fall verweist der Inhalts-URI CONTENT_LOOKUP_URI in Kombination mit der LOOKUP_KEY des Kontakts weiterhin auf die Kontaktzeile. Sie können also weiterhin LOOKUP_KEY verwenden, um Links zu „Favoriten“-Kontakten usw. zu speichern. Diese Spalte hat ein eigenes Format, das sich vom Format der Spalte _ID unterscheidet.

Abbildung 3 zeigt, wie die drei Haupttabellen zueinander in Beziehung stehen.

Haupttabellen des Kontakteanbieters

Abbildung 3: Tabellenbeziehungen mit Kontakten, Rohkontakten und Details

Achtung: Wenn Sie Ihre App im Google Play Store veröffentlichen oder Ihre App auf einem Gerät mit Android 10 (API-Level 29) oder höher ausgeführt wird, beachten Sie, dass einige Datenfelder und Methoden für Kontaktdaten veraltet sind.

Unter den genannten Bedingungen löscht das System regelmäßig alle Werte, die in diese Datenfelder geschrieben wurden:

Auch die APIs, mit denen die oben genannten Datenfelder festgelegt wurden, sind nicht mehr verfügbar:

Außerdem werden in den folgenden Feldern keine häufigen Kontakte mehr zurückgegeben. Einige dieser Felder wirken sich nur dann auf das Ranking von Kontakten aus, wenn die Kontakte zu einer bestimmten Datenart gehören.

Wenn Ihre Apps auf diese Felder oder APIs zugreifen oder sie aktualisieren, verwenden Sie alternative Methoden. Sie können beispielsweise bestimmte Anwendungsfälle erfüllen, indem Sie privat Contentanbieter oder andere in Ihrer App oder Ihrem Backend gespeicherte Daten Systeme.

Sie können diese Datenfelder manuell löschen, um zu prüfen, ob sich die Funktionalität Ihrer App durch diese Änderung nicht beeinträchtigt. Führen Sie dazu auf einem Gerät mit Android 4.1 (API-Ebene 16) oder höher den folgenden ADB-Befehl aus:

adb shell content delete \
--uri content://com.android.contacts/contacts/delete_usage

Daten von Synchronisierungsadaptern

Nutzer geben Kontaktdaten direkt auf dem Gerät ein. Daten werden aber auch über Synchronadapter aus Webdiensten an den Kontaktdatenanbieter gesendet, wodurch die Übertragung von Daten zwischen dem Gerät und den Diensten automatisiert wird. Synchronisierungsadapter werden im Hintergrund ausgeführt unter der Kontrolle des Systems und rufen ContentResolver-Methoden auf um Daten zu verwalten.

Unter Android wird der Webdienst, mit dem ein Synchronisierungsadapter zusammenarbeitet, durch einen Kontotyp identifiziert. Jeder Synchronisierungsadapter funktioniert mit einem Kontotyp, kann aber mehrere Kontonamen für diesen Typ unterstützen. Die Kontotypen und Kontonamen werden im Abschnitt Quellen von unformatierten Kontaktdaten. In den folgenden Definitionen finden Sie weitere Informationen dazu, wie sich der Kontotyp und der Name auf Synchronadapter und ‑dienste beziehen.

Kontotyp
Kennzeichnet einen Dienst, in dem der Nutzer Daten gespeichert hat. Meistens müssen die Nutzenden sich beim Dienst zu authentifizieren. Google Kontakte ist beispielsweise ein Kontotyp, der durch den Code google.com gekennzeichnet ist. Dieser Wert entspricht dem Kontotyp, der von AccountManager verwendet wird.
Kontoname
Kennzeichnet ein bestimmtes Konto oder Login für einen Kontotyp. Google Kontakte-Konten sind identisch mit Google-Konten, bei denen eine E-Mail-Adresse als Kontoname verwendet wird. Andere Dienste verwenden möglicherweise einen aus einem Wort bestehenden Nutzernamen oder eine numerische ID.

Kontotypen müssen nicht eindeutig sein. Ein Nutzer kann mehrere Google Kontakte-Konten konfigurieren. und ihre Daten beim Contacts Provider herunterladen; kann dies passieren, wenn der Nutzer persönliche Kontakte für ein privates Konto und eine weitere für die Arbeit. Kontonamen sind in der Regel eindeutig. Gemeinsam identifizieren sie einen bestimmten Datenfluss zwischen dem Contacts Provider und einem externen Dienst.

Wenn Sie die Daten Ihres Dienstes an den Contacts Provider übertragen möchten, müssen Sie Ihre Daten in den eigenen Synchronisierungsadapter verwenden. Dies wird im Abschnitt ausführlicher beschrieben. Synchronisierungsadapter für Kontakte-Anbieter

Abbildung 4 zeigt, wie der Kontaktdatenanbieter in den Datenfluss zu Personen passt. Im Feld „Synchronisierungsadapter“ ist jeder Adapter mit seinem Kontotyp gekennzeichnet.

Datenfluss zu Personen

Abbildung 4: Der Datenfluss des Contacts Providers.

Erforderliche Berechtigungen

Anwendungen, die auf den Contacts Provider zugreifen möchten, müssen Folgendes anfordern: Berechtigungen:

Lesezugriff auf eine oder mehrere Tabellen
READ_CONTACTS, angegeben in AndroidManifest.xml mit dem <uses-permission>-Element als <uses-permission android:name="android.permission.READ_CONTACTS">
Schreibzugriff auf eine oder mehrere Tabellen
WRITE_CONTACTS, angegeben in AndroidManifest.xml mit dem <uses-permission>-Element als <uses-permission android:name="android.permission.WRITE_CONTACTS">

Diese Berechtigungen gelten nicht für die Nutzerprofildaten. Das Nutzerprofil und seine die erforderlichen Berechtigungen werden im folgenden Abschnitt erläutert. Nutzerprofil:

Denken Sie daran, dass die Kontaktdaten des Nutzers personenbezogen und vertraulich sind. Nutzer legen großen Wert auf den Datenschutz und möchten nicht, dass Apps Daten über sie oder ihre Kontakte erheben. Wenn nicht klar ist, warum Sie die Berechtigung zum Zugriff auf die Kontaktdaten benötigen, geben Nutzer Ihrer App möglicherweise eine schlechte Bewertung oder installieren sie gar nicht.

Das Nutzerprofil

Die Tabelle ContactsContract.Contacts enthält eine einzelne Zeile mit Profildaten für den Gerätenutzer. Diese Daten beschreiben den user des Geräts und nicht als ein Kontakt des Nutzers. Die Zeile „Profile Contacts“ ist für jedes System, das ein Profil verwendet, mit einer Zeile mit Rohkontakten verknüpft. Jede Zeile mit Rohkontaktdaten des Profils kann mehrere Datenzeilen enthalten. Konstanten für den Zugriff auf das Nutzerprofil sind in der Klasse ContactsContract.Profile verfügbar.

Für den Zugriff auf das Nutzerprofil sind spezielle Berechtigungen erforderlich. Zusätzlich zu den READ_CONTACTS und WRITE_CONTACTS Berechtigungen für Lese-, Schreib- und Zugriff erforderlich für das Nutzerprofil die Berechtigung „android.Manifest.permission#READ_PROFILE“ und android.Manifest.permission#WRITE_PROFILE für den Lese- und Schreibzugriff .

Denken Sie daran, dass das Profil eines Nutzers vertraulich ist. Die Berechtigung Mit der Berechtigung „android.Manifest.permission#READ_PROFILE“ kannst du auf die personenbezogenen Daten. Erläutern Sie in der Beschreibung Ihrer App, warum Sie Berechtigungen für den Zugriff auf Nutzerprofile benötigen.

Rufen Sie ContentResolver.query() auf, um die Kontaktzeile mit dem Profil des Nutzers abzurufen. Inhalts-URI festlegen auf CONTENT_URI und keine Auswahlkriterien. Sie können diesen Inhalts-URI auch als Basis-URI zum Abrufen der Rohdaten Kontakte oder Daten für das Profil. Dieses Snippet ruft beispielsweise Daten für das Profil ab:

Kotlin

// Sets the columns to retrieve for the user profile
projection = arrayOf(
        ContactsContract.Profile._ID,
        ContactsContract.Profile.DISPLAY_NAME_PRIMARY,
        ContactsContract.Profile.LOOKUP_KEY,
        ContactsContract.Profile.PHOTO_THUMBNAIL_URI
)

// Retrieves the profile from the Contacts Provider
profileCursor = contentResolver.query(
        ContactsContract.Profile.CONTENT_URI,
        projection,
        null,
        null,
        null
)

Java

// Sets the columns to retrieve for the user profile
projection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };

// Retrieves the profile from the Contacts Provider
profileCursor =
        getContentResolver().query(
                Profile.CONTENT_URI,
                projection ,
                null,
                null,
                null);

Hinweis: Wenn Sie mehrere Kontaktzeilen abrufen und feststellen möchten, ob eine davon das Nutzerprofil ist, prüfen Sie die Spalte IS_USER_PROFILE der Zeile. Diese Spalte ist auf „1“ festgelegt wenn der Kontakt das Nutzerprofil ist.

Metadaten des Kontaktanbieters

Der Contact Provider verwaltet Daten, anhand derer der Status der Kontaktdaten im zu erstellen. Diese Metadaten zum Repository werden an verschiedenen Orten gespeichert, z. B. im In den Tabellenzeilen mit den Rohdaten "Kontakte", "Daten" und "Kontakte" werden die Tabelle ContactsContract.Settings und die Tabelle ContactsContract.SyncState. In der folgenden Tabelle sehen Sie die Auswirkungen dieser Metadaten:

Tabelle 3 Metadaten im Contacts Provider

Tabelle Spalte Werte Bedeutung
ContactsContract.RawContacts DIRTY „0“ – seit der letzten Synchronisierung nicht geändert Markiert unformatierte Kontakte, die auf dem Gerät geändert wurden und mit dem Server. Der Wert wird automatisch vom Contacts Provider festgelegt, wenn Android eine Zeile aktualisieren.

Synchronisierungsadapter, die die unbearbeiteten Kontakte oder Datentabellen ändern, sollten immer den Parameter String CALLER_IS_SYNCADAPTER in den verwendet wird. Dadurch wird verhindert, dass der Anbieter Zeilen als fehlerhaft kennzeichnet. Andernfalls werden Änderungen am Synchronisierungsadapter als lokale Änderungen betrachtet und an den Server gesendet, auch wenn der Server die Quelle der Änderung war.

„1“ - seit der letzten Synchronisierung geändert, muss wieder mit dem Server synchronisiert werden.
ContactsContract.RawContacts VERSION Die Versionsnummer dieser Zeile. Der Kontaktanbieter erhöht diesen Wert automatisch, wenn die Zeile oder Änderungen der zugehörigen Daten.
ContactsContract.Data DATA_VERSION Die Versionsnummer dieser Zeile. Der Kontaktdatenanbieter erhöht diesen Wert automatisch, wenn sich die Datenzeile ändert.
ContactsContract.RawContacts SOURCE_ID Ein Stringwert, der diesen unbearbeiteten Kontakt für das Konto in als er erstellt wurde. Wenn ein Synchronisierungsadapter einen neuen Rohkontakt erstellt, sollte diese Spalte auf die eindeutige ID des Servers für den Rohkontakt festgelegt werden. Wenn eine Android-Anwendung einen neuen Rohkontakt erstellt, sollte diese Spalte leer bleiben. Dies signalisiert dem Synchronisierungsadapter, dass er einen neuen Rohkontakt auf dem Server erstellen und einen Wert für die SOURCE_ID abrufen soll.

Insbesondere muss die Quell-ID für jeden Kontotyp eindeutig sein und bei jeder Synchronisierung gleich bleiben:

  • Eindeutig: Jeder Rohkontakt für ein Konto muss eine eigene Quellen-ID haben. Wenn Sie nicht erzwingen, werden Probleme in der Kontakte-Anwendung verursacht. Zwei unbearbeitete Kontakte für denselben Kontotyp dieselbe Quellen-ID. Beispiel: Der Rohkontakt „Thomas Higginson“ für das Konto emily.dickinson@gmail.com darf dieselbe Quell-ID haben wie der Rohkontakt „Thomas Higginson“ für das Konto emilyd@gmail.com.
  • Stabil: Quell-IDs sind ein fester Bestandteil der Daten des Onlinedienstes für den Rohkontakt. Wenn der Nutzer beispielsweise den Speicher für Kontakte in den App-Einstellungen löscht und die Synchronisierung noch einmal durchführt, sollten die wiederhergestellten Rohkontakte dieselben Quell-IDs wie zuvor haben. Wenn du diese Richtlinie nicht erzwingst, werden Tastenkombinationen beendet funktionieren.
ContactsContract.Groups GROUP_VISIBLE „0“ - Kontakte in dieser Gruppe sollten in den Benutzeroberflächen von Android-Anwendungen nicht sichtbar sein. Diese Spalte dient der Kompatibilität mit Servern, auf denen Nutzer Kontakte in bestimmten Gruppen ausblenden können.
„1“ - Kontakte in dieser Gruppe dürfen in Anwendungs-UIs sichtbar sein.
ContactsContract.Settings UNGROUPED_VISIBLE „0“: Für dieses Konto und diesen Kontotyp sind Kontakte, die keiner Gruppe angehören, für die Benutzeroberfläche von Android-Apps nicht sichtbar. Standardmäßig sind Kontakte unsichtbar, wenn keiner ihrer Rohkontakte zu einer Gruppe gehört. Die Gruppenzugehörigkeit eines Rohkontakts wird durch eine oder mehrere ContactsContract.CommonDataKinds.GroupMembership-Zeilen in der ContactsContract.Data-Tabelle angezeigt. Durch Festlegen dieses Flags in der Tabellenzeile ContactsContract.Settings für einen Kontotyp und ein Konto können Sie erzwingen, dass Kontakte ohne Gruppen sichtbar sind. Dieses Flag kann beispielsweise verwendet werden, um Kontakte von Servern anzuzeigen, die keine Gruppen verwenden.
„1“: Für dieses Konto und diesen Kontotyp sind Kontakte, die keiner Gruppe angehören, für die Benutzeroberfläche der Anwendung sichtbar.
ContactsContract.SyncState (alle) Verwenden Sie diese Tabelle, um Metadaten für Ihren Synchronisierungsadapter zu speichern. Mit dieser Tabelle können Sie den Synchronisierungsstatus und andere synchronisierungsbezogene Daten dauerhaft auf dem Gerät speichern.

Zugriff des Anbieters für Kontakte

In diesem Abschnitt werden Richtlinien für den Zugriff auf Daten vom Contacts Provider beschrieben, wobei der Schwerpunkt auf Folgendes:

  • Entitätsabfragen:
  • Batch-Änderung
  • Abrufen und Ändern mit Intents
  • Datenintegrität

Das Vornehmen von Änderungen über einen Synchronisierungsadapter wird in diesem Abschnitt ausführlicher behandelt. Synchronisierungsadapter des Kontakteanbieters

Entitäten abfragen

Da die Tabellen der Kontaktdatenanbieter hierarchisch organisiert sind, ist es oft nützlich, eine Zeile und alle zugehörigen untergeordneten Zeilen abzurufen. Um beispielsweise Informationen über eine Person enthält, möchten Sie vielleicht alle ContactsContract.RawContacts Zeilen für eine einzelne ContactsContract.Contacts Zeile oder alle ContactsContract.CommonDataKinds.Email Zeilen für eine einzelne ContactsContract.RawContacts Zeile. Um dies zu erleichtern, Der Anbieter bietet Entitätskonstrukte an, die wie Datenbankverknüpfungen zwischen Tabellen.

Eine Entität ist wie eine Tabelle, die sich aus ausgewählten Spalten aus einer übergeordneten und einer untergeordneten Tabelle zusammensetzt. Wenn Sie eine Entität abfragen, geben Sie eine Projektion und Suchkriterien an, die auf den Spalten basieren, die für die Entität verfügbar sind. Das Ergebnis ist eine Cursor, die eine Zeile für jede Zeile der untergeordneten Tabelle enthält, die abgerufen wurde. Wenn Sie beispielsweise ContactsContract.Contacts.Entity für den Namen eines Kontakts und alle ContactsContract.CommonDataKinds.Email-Zeilen für alle keine unformatierten Kontakte für diesen Namen enthält, erhalten Sie eine Cursor mit einer Zeile für jede ContactsContract.CommonDataKinds.Email-Zeile.

Entitäten vereinfachen Abfragen. Mit einem Entitätsobjekt können Sie alle Kontaktdaten für einen Kontakt oder einen Rohkontakt auf einmal abrufen, anstatt zuerst die übergeordnete Tabelle abfragen zu müssen, um eine ID zu erhalten, und dann die untergeordnete Tabelle mit dieser ID abfragen zu müssen. Außerdem verarbeitet der Kontaktdatenanbieter eine Abfrage für ein Entitätsobjekt in einer einzelnen Transaktion, wodurch sichergestellt wird, dass die abgerufenen Daten intern konsistent sind.

Hinweis: Eine Entität enthält normalerweise nicht alle Spalten der Spalten untergeordneten Tabelle. Wenn Sie versuchen, mit einem Spaltennamen zu arbeiten, der nicht in der Liste der Spaltennamenkonstanten für das Entitätsobjekt enthalten ist, wird Exception zurückgegeben.

Das folgende Snippet zeigt, wie alle Rohzeilen für einen Kontakt abgerufen werden. Das Snippet ist Teil einer größeren Anwendung mit zwei Aktivitäten, „main“ und „detail“. Die Hauptaktivität zeigt eine Liste von Kontaktzeilen an. Wenn der Nutzer eine auswählt, sendet die Aktivität ihre ID an die Detailaktivität. Bei der Detailaktivität werden mithilfe von ContactsContract.Contacts.Entity alle Datenzeilen aus allen Rohkontakten angezeigt, die mit dem ausgewählten Kontakt verknüpft sind.

Dieses Snippet stammt aus der Aktivität „detail“:

Kotlin

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY
    )

    // Initializes the loader identified by LOADER_ID.
    loaderManager.initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this        // The context of the activity
    )

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = SimpleCursorAdapter(
            this,                       // the context of the activity
            R.layout.detail_list_item,  // the view item containing the detail widgets
            mCursor,                    // the backing cursor
            fromColumns,               // the columns in the cursor that provide the data
            toViews,                   // the views in the view item that display the data
            0)                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.adapter = cursorAdapter
...
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    val projection: Array<String> = arrayOf(
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
    )

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    val sortOrder = "${ContactsContract.Contacts.Entity.RAW_CONTACT_ID} ASC"

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return CursorLoader(
            applicationContext, // The activity's context
            contactUri,        // The entity content URI for a single contact
            projection,         // The columns to retrieve
            null,               // Retrieve all the raw contacts and their data rows.
            null,               //
            sortOrder           // Sort by the raw contact ID.
    )
}

Java

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            fromColumns,                // the columns in the cursor that provide the data
            toViews,                    // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.setAdapter(cursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    String[] projection =
        {
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
        };

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    String sortOrder =
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
            " ASC";

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return new CursorLoader(
            getApplicationContext(),  // The activity's context
            contactUri,              // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,                     // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder);               // Sort by the raw contact ID.
}

Nach Abschluss der Datenübertragung ruft LoaderManager einen Rückruf an onLoadFinished() auf. Eines der eingehenden Argumente für diese Methode ist ein Cursor durch die Ergebnisse der Abfrage. In Ihrer eigenen App können Sie die Daten aus diesem Cursor abrufen, um sie anzuzeigen oder weiterzuverarbeiten.

Batch-Änderung

Wenn möglich, sollten Sie Daten beim Contacts Provider in "Batchmodus", indem ein ArrayList von ContentProviderOperation-Objekte und Aufrufe applyBatch() Da der Kontaktdatenanbieter alle Vorgänge in einer applyBatch() in einer einzigen Transaktion ausführt, wird das Kontaktdatenverzeichnis durch Ihre Änderungen nie in einen inkonsistenten Zustand versetzt. Eine Batch-Änderung erleichtert auch das gleichzeitige Einfügen eines Rohkontakts und seiner Detaildaten.

Hinweis: Wenn Sie einen einzelnen Rohkontakt ändern möchten, sollten Sie eine Intent an die Kontakte-App des Geräts senden, anstatt die Änderung in Ihrer App vorzunehmen. Weitere Informationen dazu finden Sie im Abschnitt Abrufen und Ändern mit Intents.

Ertragspunkte

Eine Batch-Änderung mit einer großen Anzahl von Vorgängen kann andere Prozesse blockieren, was zu einer schlechten Nutzererfahrung führt. Um alle Änderungen zu organisieren, Listen mit möglichst wenigen separaten Listen führen und gleichzeitig verhindern, das System blockieren, sollten Sie Ertragspunkte für einen oder mehrere Vorgänge festlegen. Ein Ertragspunkt ist ein ContentProviderOperation-Objekt mit isYieldAllowed()-Wert festgelegt auf true Wenn der Contacts Provider auf einen Ertragspunkt stößt, unterbricht er seine Arbeit, um andere Prozesse ausführen und die aktuelle Transaktion schließen. Wenn der Anbieter neu gestartet wird, fährt er mit dem nächsten Vorgang in der ArrayList fort und startet eine neue Transaktion.

Ertragspunkte führen zu mehr als einer Transaktion pro Aufruf von applyBatch(). Aus sollten Sie einen Ertragspunkt für den letzten Vorgang für einen Satz zusammengehöriger Zeilen festlegen. Sie sollten beispielsweise einen Ertragspunkt für den letzten Vorgang in einem Satz festlegen, mit dem Rohkontaktzeilen und die zugehörigen Datenzeilen hinzugefügt werden, oder für den letzten Vorgang für einen Satz von Zeilen, die sich auf einen einzelnen Kontakt beziehen.

Yield-Punkte sind auch eine Einheit für atomaren Betrieb. Alle Zugriffe zwischen zwei Yield-Punkten verlaufen entweder als Einheit erfolgreich oder schlagen als solche fehl. Wenn Sie keine Ertragspunkte festlegen, Ein „atomarer Vorgang“ ist der gesamte Batch von Vorgängen. Wenn Sie Yield-Punkte verwenden, verhindern Sie, dass Vorgänge die Systemleistung beeinträchtigen, und sorgen gleichzeitig dafür, dass ein Teil der Vorgänge atomar ist.

Rückverweise auf Änderungen

Wenn Sie eine neue Zeile mit Rohkontaktdaten und die zugehörigen Datenzeilen als ContentProviderOperation-Objekten haben, müssen Sie die Datenzeilen mit der Zeile mit den Rohkontakten, indem wir _ID-Wert als RAW_CONTACT_ID-Wert. Dieses Der Wert ist beim Erstellen der ContentProviderOperation nicht verfügbar da Sie das Attribut ContentProviderOperation für die Zeile mit den Rohkontakten. Um dieses Problem zu umgehen, hat die Klasse ContentProviderOperation.Builder die Methode withValueBackReference(). Mit dieser Methode können Sie eine Spalte mit dem Ergebnis eines vorherigen Vorgangs.

Die Methode withValueBackReference() hat zwei Argumente:

key
Der Schlüssel eines Schlüssel/Wert-Paars. Der Wert dieses Arguments sollte der Name einer Spalte in der Tabelle sein, die Sie ändern.
previousResult
Der Index (ab 0) eines Werts im Array der ContentProviderResult-Objekte aus applyBatch(). Während die Batchvorgänge angewendet werden, wird das Ergebnis jeder Operation in einem Zwischenergebnisarray gespeichert. Der previousResult-Wert ist der Index eines dieser Ergebnisse, der abgerufen und mit dem key-Wert gespeichert wird. So können Sie einen neuen Raw-Kontakteintrag einfügen und den _ID-Wert zurückgeben. Anschließend können Sie beim Hinzufügen einer ContactsContract.Data-Zeile einen Verweis auf den Wert herstellen.

Das gesamte Ergebnisarray wird beim ersten Aufruf erstellt. applyBatch(), mit einer Größe, die der Größe von ArrayList von ContentProviderOperation-Objekte, die Sie angeben. Jedoch alle sind die Elemente im Ergebnis-Array auf null gesetzt. Wenn Sie versuchen, um einen Rückverweis auf ein Ergebnis für einen Vorgang zu erstellen, der noch nicht angewendet wurde. withValueBackReference() wirft Exception.

In den folgenden Snippets wird gezeigt, wie Sie einen neuen Rohkontakt und Daten im Batch einfügen. Sie Einschließen-Code, der einen Ertragspunkt festlegt und einen Rückverweis verwendet.

Im ersten Snippet werden Kontaktdaten aus der Benutzeroberfläche abgerufen. Zu diesem Zeitpunkt hat die nutzende Person bereits das Konto ausgewählt, für das der neue unbearbeitete Kontakt hinzugefügt werden soll.

Kotlin

// Creates a contact entry from the current UI values, using the currently-selected account.
private fun createContactEntry() {
    /*
     * Gets values from the UI
     */
    val name = contactNameEditText.text.toString()
    val phone = contactPhoneEditText.text.toString()
    val email = contactEmailEditText.text.toString()

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

// Creates a contact entry from the current UI values, using the currently-selected account.
protected void createContactEntry() {
    /*
     * Gets values from the UI
     */
    String name = contactNameEditText.getText().toString();
    String phone = contactPhoneEditText.getText().toString();
    String email = contactEmailEditText.getText().toString();

    int phoneType = contactPhoneTypes.get(
            contactPhoneTypeSpinner.getSelectedItemPosition());

    int emailType = contactEmailTypes.get(
            contactEmailTypeSpinner.getSelectedItemPosition());

Das nächste Snippet erstellt einen Vorgang zum Einfügen der Zeile mit den Rohkontakten in die Tabelle ContactsContract.RawContacts:

Kotlin

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

    // Creates a new array of ContentProviderOperation objects.
    val ops = arrayListOf<ContentProviderOperation>()

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    var op: ContentProviderOperation.Builder =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

     // Creates a new array of ContentProviderOperation objects.
    ArrayList<ContentProviderOperation> ops =
            new ArrayList<ContentProviderOperation>();

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    ContentProviderOperation.Builder op =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

Als Nächstes erstellt der Code Datenzeilen für die Zeilen „Anzeigename“, „Telefonnummer“ und „E-Mail-Adresse“.

Jedes Objekt vom Typ „Operation Builder“ verwendet withValueBackReference(), um den RAW_CONTACT_ID abzurufen. Die Referenzpunkte verweisen auf das ContentProviderResult-Objekt aus dem ersten Vorgang, wodurch die Zeile mit den Rohkontakten hinzugefügt und der neue _ID-Wert zurückgegeben wird. Daher wird jede Datenzeile automatisch durch ihre RAW_CONTACT_ID in die neue Zeile ContactsContract.RawContacts ein, zu der sie gehört.

Das ContentProviderOperation.Builder-Objekt, mit dem die E-Mail-Zeile hinzugefügt wird, ist gekennzeichnet mit withYieldAllowed(), wodurch ein Ertragspunkt festgelegt wird:

Kotlin

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified phone number and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified email and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified phone number and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified email and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

Im letzten Snippet ist der Aufruf von applyBatch() zu sehen, über den die neuen Zeilen für Rohkontakte und Daten eingefügt werden.

Kotlin

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})")
    Log.d(TAG, "Creating contact: $name")

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {
        contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
    } catch (e: Exception) {
        // Display a warning
        val txt: String = getString(R.string.contactCreationFailure)
        Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show()

        // Log exception
        Log.e(TAG, "Exception encountered while inserting contact: $e")
    }
}

Java

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" +
            selectedAccount.getType() + ")");
    Log.d(TAG,"Creating contact: " + name);

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {

            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch (Exception e) {

            // Display a warning
            Context ctx = getApplicationContext();

            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();

            // Log exception
            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    }
}

Mit Batch-Vorgängen können Sie auch die optimistische Parallelitätssteuerung implementieren, eine Methode zum Anwenden von Änderungstransaktionen, ohne das zugrunde liegende Repository sperren zu müssen. Bei dieser Methode wenden Sie die Transaktion an und prüfen dann, ob gleichzeitig andere Änderungen vorgenommen wurden. Wenn Sie feststellen, dass eine inkonsistente Änderung aufgetreten ist, machen Sie ein Rollback der Transaktion und versuchen Sie es noch einmal.

Die optimistische Gleichzeitigkeitserkennung ist nützlich für Mobilgeräte, Gleichzeitige Zugriffe auf ein Daten-Repository sind selten. Da keine Sperre verwendet wird, Sie müssen keine Zeit damit verschwenden, Sperren einzurichten oder darauf zu warten, dass andere Transaktionen ihre Sperren aufheben.

Um eine optimistische Gleichzeitigkeitserkennung bei der Aktualisierung eines einzelnen ContactsContract.RawContacts Zeile verwenden, gehen Sie so vor:

  1. Rufen Sie die Spalte VERSION des Rohkontakts zusammen mit den anderen abgerufenen Daten ab.
  2. Erstellen Sie mit der Methode newAssertQuery(Uri) ein ContentProviderOperation.Builder-Objekt, das zum Erzwingen einer Einschränkung geeignet ist. Für den Inhalts-URI RawContacts.CONTENT_URI verwenden wobei der _ID des unformatierten Kontakts angehängt ist.
  3. Rufen Sie für das ContentProviderOperation.Builder-Objekt auf: withValue() zum Vergleichen von VERSION in die soeben abgerufene Versionsnummer ein.
  4. Rufen Sie für dieselbe ContentProviderOperation.Builder withExpectedCount() auf, damit nur eine Zeile durch diese Behauptung getestet wird.
  5. Rufen Sie build() auf, um die ContentProviderOperation-Objekt und fügen Sie dieses Objekt erstes Objekt im ArrayList, das Sie an applyBatch().
  6. Wenden Sie die Batch-Transaktion an.

Wenn die Zeile mit den Rohkontakten zwischen dem Lesen der Zeile und dem Versuch, sie zu ändern, durch einen anderen Vorgang aktualisiert wird, schlägt die „assert“-Anweisung ContentProviderOperation fehl und die gesamte Batch-Operation wird rückgängig gemacht. Sie können dann den Batch noch einmal versuchen oder eine andere Aktion ausführen.

Im folgenden Snippet wird gezeigt, wie Sie eine ContentProviderOperation vom Typ „assert“ erstellen, nachdem Sie mit einer CursorLoader nach einem einzelnen Rohkontakt gesucht haben:

Kotlin

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID))
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION))
}

...

// Sets up a Uri for the assert operation
val rawContactUri: Uri = ContentUris.withAppendedId(
        ContactsContract.RawContacts.CONTENT_URI,
        rawContactID
)

// Creates a builder for the assert operation
val assertOp: ContentProviderOperation.Builder =
        ContentProviderOperation.newAssertQuery(rawContactUri).apply {
            // Adds the assertions to the assert operation: checks the version
            withValue(SyncColumns.VERSION, mVersion)

            // and count of rows tested
            withExpectedCount(1)
        }

// Creates an ArrayList to hold the ContentProviderOperation objects
val ops = arrayListOf<ContentProviderOperation>()

ops.add(assertOp.build())

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try {
    val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops)
} catch (e: OperationApplicationException) {
    // Actions you want to take if the assert operation fails go here
}

Java

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}

...

// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri);

// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);

// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperation>;

ops.add(assertOp.build());

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try
    {
        ContentProviderResult[] results =
                getContentResolver().applyBatch(AUTHORITY, ops);

    } catch (OperationApplicationException e) {

        // Actions you want to take if the assert operation fails go here
    }

Abrufen und Ändern mit Intents

Wenn Sie eine Intent an die Kontaktanwendung des Geräts senden, können Sie indirekt auf den Kontaktdatenanbieter zugreifen. Der Intent startet die Benutzeroberfläche der Kontaktanwendung des Geräts, in der Nutzer kontaktbezogene Aktionen ausführen können. Mit dieser Art von Zugriff haben Nutzer folgende Möglichkeiten:

  • Wählen Sie einen Kontakt aus einer Liste aus und lassen Sie ihn zur weiteren Bearbeitung an Ihre App zurückgeben.
  • Bearbeiten Sie die Daten eines vorhandenen Kontakts.
  • Fügen Sie einen neuen Rohkontakt für eines der Konten ein.
  • Daten eines Kontakts oder mehrerer Kontakte löschen

Wenn der Nutzer Daten einfügt oder aktualisiert, können Sie die Daten zuerst erfassen und als Teil der Intent-Aktion senden.

Wenn Sie Intents verwenden, um über die Kontakte-App des Geräts auf den Contacts Provider zuzugreifen, keine eigene UI oder Code schreiben müssen, um auf den Anbieter zuzugreifen. Außerdem müssen Sie keine Lese- oder Schreibberechtigung für den Anbieter anfordern. Die Kontakte-App des Geräts kann Ihnen die Leseberechtigung für einen Kontakt delegieren. Da Sie Änderungen am Anbieter über eine andere App vornehmen, benötigen Sie keine Schreibberechtigungen.

Das allgemeine Verfahren zum Senden eines Intents, um auf einen Anbieter zuzugreifen, wird ausführlich in der Grundlagen des Contentanbieters im Abschnitt „Datenzugriff über Intents“. Die Aktion, Der MIME-Typ und die Datenwerte, die Sie für die verfügbaren Aufgaben verwenden, sind in Tabelle 4 zusammengefasst. Die zusätzliche Werte, die Sie mit putExtra() sind aufgeführt in Referenzdokumentation zu ContactsContract.Intents.Insert:

Tabelle 4 Kontakte-Anbieter-Intents.

Aufgabe Aktion Daten MIME-Typ Hinweise
Kontakt aus einer Liste auswählen ACTION_PICK Eine der folgenden Möglichkeiten: Nicht verwendet Je nach angegebenem Inhalts-URI-Typ wird eine Liste der Rohkontakte oder eine Liste der Daten aus einem Rohkontakt angezeigt.

Anruf startActivityForResult(), gibt den Inhalts-URI der ausgewählten Zeile zurück. Der URI hat das Format der Inhalts-URI der Tabelle, an die die LOOKUP_ID der Zeile angehängt ist. Die Kontakte-App des Geräts delegiert Lese- und Schreibberechtigungen für die Dauer Ihrer Aktivität an diesen Inhalts-URI. Weitere Informationen findest du im Leitfaden Grundlagen für Contentanbieter.

Neuen Rohkontakt einfügen Insert.ACTION RawContacts.CONTENT_TYPE, MIME-Typ für eine Reihe von Rohkontakten. Der Bildschirm Kontakt hinzufügen der Kontakte App des Geräts wird angezeigt. Die zusätzlichen Werte, die Sie dem Intent hinzufügen, werden angezeigt. Bei Versand mit startActivityForResult(), wird der Inhalts-URI des neu hinzugefügten unformatierten Kontakts an den onActivityResult() im Argument Intent im „Daten“ ein. Rufen Sie getData() auf, um den Wert zu erhalten.
Kontakt bearbeiten ACTION_EDIT CONTENT_LOOKUP_URI für den Kontakt. Mit der Bearbeitungsaktivität kann der Nutzer alle mit diesem Kontakt verknüpften Daten bearbeiten. Contacts.CONTENT_ITEM_TYPE, einen einzelnen Kontakt. Zeigt den Bildschirm "Kontakt bearbeiten" in der Kontakte-Anwendung an. Die zusätzlichen Werte, die Sie hinzufügen zum Intent angezeigt. Wenn der Nutzer auf Fertig klickt, um die Änderungen vornehmen, wird die Aktivität wieder im Vordergrund angezeigt.
Auswahl anzeigen, über die auch Daten hinzugefügt werden können ACTION_INSERT_OR_EDIT CONTENT_ITEM_TYPE Dieser Intent zeigt immer den Auswahlbildschirm der Kontakte-App an. Der Nutzer kann entweder Wählen Sie einen Kontakt zum Bearbeiten aus oder fügen Sie einen neuen Kontakt hinzu. Je nach Auswahl des Nutzers wird entweder der Bearbeitungs- oder der Hinzufügungsbildschirm angezeigt. Die zusätzlichen Daten, die Sie in der Intent-Anfrage übergeben, werden angezeigt. Wenn Ihre App Kontaktdaten wie eine E-Mail-Adresse oder Telefonnummer anzeigt, verwenden Sie damit der Nutzer die Daten einem vorhandenen Kontakt hinzufügen kann. Kontakt,

Hinweis: Sie müssen in den Extras dieses Intents keinen Namens-Wert senden, da der Nutzer immer einen vorhandenen Namen auswählt oder einen neuen hinzufügt. Außerdem Wenn Sie einen Namen senden und der Nutzer sich für eine Änderung entscheidet, wird die Kontakte App zeigt den von Ihnen gesendeten Namen an und überschreibt damit den vorherigen Wert. Wenn der Nutzer dies nicht bemerkt und die Änderung speichert, geht der alte Wert verloren.

Die Kontakte-App auf dem Gerät erlaubt es Ihnen nicht, einen unbearbeiteten Kontakt oder die dazugehörigen Daten mit einem die Nutzerabsicht verstehen. Um einen unformatierten Kontakt zu löschen, verwenden Sie ContentResolver.delete() oder ContentProviderOperation.newDelete().

Im folgenden Snippet wird gezeigt, wie ein Intent erstellt und gesendet wird, mit dem ein neuer Rohkontakt und Daten eingefügt werden:

Kotlin

// Gets values from the UI
val name = contactNameEditText.text.toString()
val phone = contactPhoneEditText.text.toString()
val email = contactEmailEditText.text.toString()

val company = companyName.text.toString()
val jobtitle = jobTitle.text.toString()

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
val contactData = arrayListOf<ContentValues>()

/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
val rawContactRow = ContentValues().apply {
    // Adds the account type and name to the row
    put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type)
    put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name)
}

// Adds the row to the array
contactData.add(rawContactRow)

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
val phoneRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

    // Adds the phone number and its type to the row
    put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
}

// Adds the row to the array
contactData.add(phoneRow)

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
val emailRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

    // Adds the email address and its type to the row
    put(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
}

// Adds the row to the array
contactData.add(emailRow)

// Creates a new intent for sending to the device's contacts application
val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply {
    // Sets the MIME type to the one expected by the insertion activity
    type = ContactsContract.RawContacts.CONTENT_TYPE

    // Sets the new contact name
    putExtra(ContactsContract.Intents.Insert.NAME, name)

    // Sets the new company and job title
    putExtra(ContactsContract.Intents.Insert.COMPANY, company)
    putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle)

    /*
    * Adds the array to the intent's extras. It must be a parcelable object in order to
    * travel between processes. The device's contacts app expects its key to be
    * Intents.Insert.DATA
    */
    putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData)
}

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent)

Java

// Gets values from the UI
String name = contactNameEditText.getText().toString();
String phone = contactPhoneEditText.getText().toString();
String email = contactEmailEditText.getText().toString();

String company = companyName.getText().toString();
String jobtitle = jobTitle.getText().toString();

// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);

// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);

// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);

// Sets the new company and job title
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();


/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();

// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

// Adds the row to the array
contactData.add(rawContactRow);

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
ContentValues phoneRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
phoneRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);

// Adds the phone number and its type to the row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

// Adds the row to the array
contactData.add(phoneRow);

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
ContentValues emailRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
emailRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);

// Adds the email address and its type to the row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);

// Adds the row to the array
contactData.add(emailRow);

/*
 * Adds the array to the intent's extras. It must be a parcelable object in order to
 * travel between processes. The device's contacts app expects its key to be
 * Intents.Insert.DATA
 */
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent);

Datenintegrität

Weil das Kontakt-Repository wichtige und sensible Daten enthält, die Nutzer erwarten korrekt und aktuell sind, verfügt der Contacts Provider über klar definierte Regeln für die Datenintegrität. Es ist sind Sie verpflichtet, sich beim Ändern von Kontaktdaten an diese Regeln zu halten. Die wichtigsten Regeln sind hier aufgeführt:

Immer eine ContactsContract.CommonDataKinds.StructuredName-Zeile hinzufügen für jede ContactsContract.RawContacts Zeile, die Sie hinzufügen.
Eine ContactsContract.RawContacts-Zeile ohne ContactsContract.CommonDataKinds.StructuredName-Zeile in der ContactsContract.Data-Tabelle kann bei der Aggregation zu Problemen führen.
Verknüpfen Sie neue ContactsContract.Data-Zeilen immer mit der übergeordneten ContactsContract.RawContacts-Zeile.
Eine ContactsContract.Data-Zeile, die nicht mit einer ContactsContract.RawContacts verknüpft ist, wird in der Kontakte-App des Geräts nicht angezeigt und kann Probleme mit Synchronisatoren verursachen.
Ändern Sie nur Daten für die unbearbeiteten Kontakte, deren Inhaber Sie sind.
Denken Sie daran, dass der Kontaktanbieter in der Regel Daten aus mehreren verschiedenen Kontotypen/Onlinediensten verwaltet. Sie müssen sicherstellen, dass Ihre Anwendung nur Änderungen Daten für Zeilen gelöscht, die Ihnen gehören, und dass nur Daten mit einem Kontotyp und -name selbst verwalten.
Verwenden Sie immer die in ContactsContract definierten Konstanten und die zugehörigen für Zertifizierungsstellen, Inhalts-URIs, URI-Pfade, Spaltennamen, MIME-Typen und TYPE-Werte.
Die Verwendung dieser Konstanten hilft Ihnen, Fehler zu vermeiden. Du wirst auch vom Compiler benachrichtigt Warnungen, wenn eine der Konstanten veraltet ist.

Benutzerdefinierte Datenzeilen

Wenn Sie benutzerdefinierte MIME-Typen erstellen und verwenden, können Sie eigene Datenzeilen in der Tabelle ContactsContract.Data erstellen. Ihre Zeilen nur die Spalte verwenden, die in ContactsContract.DataColumns, obwohl Sie eigene Karten erstellen können typspezifischen Spaltennamen den Standardspaltennamen zu. In der Kontakte App auf dem Gerät werden die Daten für die Zeilen angezeigt, können aber weder bearbeitet noch gelöscht werden. Nutzer können keine zusätzliche Daten. Damit Nutzer Ihre benutzerdefinierten Datenzeilen ändern können, müssen Sie einen Editor bereitstellen in Ihrer eigenen Anwendung.

Wenn du deine benutzerdefinierten Daten anzeigen lassen möchtest, stelle eine contacts.xml-Datei mit einem <ContactsAccountType>-Element und mindestens eines seiner <ContactsDataKind> untergeordnete Elemente. Weitere Informationen finden Sie im Abschnitt <ContactsDataKind> element.

Weitere Informationen zu benutzerdefinierten MIME-Typen findest du im Leitfaden Inhaltsanbieter erstellen.

Synchronisierungsadapter für Kontakte-Anbieter

Der Kontaktanbieter wurde speziell für die Synchronisierung entwickelt. von Kontaktdaten zwischen einem Gerät und einem Onlinedienst. Damit können Nutzende vorhandene Daten auf ein neues Gerät übertragen und vorhandene Daten in ein neues Konto hochladen. Durch die Synchronisierung haben Nutzer unabhängig von der Quelle der Ergänzungen und Änderungen immer die neuesten Daten zur Hand. Ein weiterer Vorteil der Synchronisierung besteht darin, Kontaktdaten sind auch dann verfügbar, wenn das Gerät nicht mit dem Netzwerk verbunden ist.

Obwohl Sie die Synchronisierung auf verschiedene Arten implementieren können, bietet das Android-System ein Plug-in-Synchronisierungs-Framework, das die folgenden Aufgaben automatisiert:

  • Netzwerkverfügbarkeit wird geprüft.
  • Die Synchronisierung wird basierend auf den Nutzereinstellungen geplant und ausgeführt.
  • Beendete Synchronisierungen werden neu gestartet.

Wenn Sie dieses Framework verwenden möchten, müssen Sie ein Synchronisierungsadapter-Plug-in bereitstellen. Jeder Synchronisierungsadapter ist für einen Dienst und einen Inhaltsanbieter eindeutig, kann aber mehrere Kontonamen für denselben Dienst verarbeiten. Das Framework ermöglicht auch mehrere Synchronisierungsadapter für denselben Dienst und Anbieter.

Adapterklassen und Dateien synchronisieren

Sie implementieren einen Synchronisierungsadapter als Unterklasse von AbstractThreadedSyncAdapter und installieren ihn als Teil einer Android-Anwendung. Das System erfährt über Elemente im Anwendungsmanifest und über eine spezielle XML-Datei, auf die das Manifest verweist, von dem Synchronisierungsadapter. Die XML-Datei definiert den Kontotyp für den Onlinedienst und die Autorität für den Inhaltsanbieter, die zusammen den Adapter eindeutig identifizieren. Der Synchronisierungsadapter wird erst aktiv, wenn der Nutzer ein Konto für den Kontotyp des Synchronisierungsadapters hinzufügt und die Synchronisierung für den Inhaltsanbieter aktiviert, mit dem der Synchronisierungsadapter synchronisiert wird. Das System beginnt dann, den Adapter zu verwalten und ihn bei Bedarf aufzurufen, um die Synchronisierung zwischen dem Contentanbieter und dem Server vorzunehmen.

Hinweis: Wenn Sie einen Kontotyp als Teil der Identifizierung des Synchronisierungsadapters verwenden, kann das System Synchronisierungsadapter erkennen und gruppieren, die auf verschiedene Dienste derselben Organisation zugreifen. Synchronisierungsadapter für Google-Onlinedienste haben beispielsweise alle Kontotyp com.google. Wenn Nutzer ihren Geräten ein Google-Konto hinzufügen, der installierten Synchronisierungsadapter für Google-Dienste sind zusammen aufgeführt. jeder Synchronisierungsadapter wird mit einem anderen Contentanbieter auf dem Gerät synchronisiert.

Weil Nutzer bei den meisten Diensten ihre Identität bestätigen müssen, bevor sie auf sie zugreifen können bietet das Android-System ein Authentifizierungs-Framework, das dem wird in Verbindung mit dem Synchronisierungsadapter-Framework verwendet. Das Authentifizierungs-Framework verwendet Plug-in-Authentifikatoren, die Unterklassen von AbstractAccountAuthenticator sind. Ein Authenticator bestätigt, Identität des Nutzers in den folgenden Schritten ermitteln:

  1. Erfasst den Namen, das Passwort oder ähnliche Informationen des Nutzers (die Anmeldedaten)
  2. Sendet die Anmeldedaten an den Dienst
  3. Prüft die Antwort des Dienstes.

Wenn der Dienst die Anmeldedaten akzeptiert, kann der Authenticator die Anmeldedaten zur späteren Verwendung speichern. Aufgrund des Plug-in-Authenticator-Frameworks AccountManager kann Zugriff auf alle Authentifizierungstokens eines Authenticators gewähren unterstützt und wählt aus, z. B. OAuth2-Authentifizierungstokens.

Die Authentifizierung ist zwar nicht erforderlich, wird aber von den meisten Kontaktdiensten verwendet. Sie müssen jedoch nicht das Android-Authentifizierungsframework für die Authentifizierung verwenden.

Implementierung des Synchronisierungsadapters

Wenn Sie einen Synchronisierungsadapter für den Kontaktdatenanbieter implementieren möchten, erstellen Sie zuerst eine Android-Anwendung mit den folgenden Elementen:

Eine Service-Komponente, die auf Anfragen des Systems reagiert, um eine Bindung an den Synchronisierungsadapter vorzunehmen.
Wenn das System eine Synchronisierung ausführen möchte, ruft es die Methode onBind()-Methode, um eine IBinder für den Synchronisierungsadapter. Dadurch kann das System prozessübergreifende Aufrufe der Methoden des Adapters.
Der tatsächliche Synchronisierungsadapter, implementiert als konkrete Unterklasse von AbstractThreadedSyncAdapter
In dieser Klasse werden Daten vom Server heruntergeladen, und Konflikte lösen. Die Hauptarbeit des Adapters wird in der Methode onPerformSync() ausgeführt. Diese Klasse muss als Singleton instanziiert werden.
Eine abgeleitete Klasse von Application.
Diese Klasse dient als Fabrik für das Sync-Adapter-Singleton. Verwenden Sie die Methode onCreate(), um den Synchronisierungsadapter zu instanziieren, und geben Sie eine statische „Getter“-Methode an, um das Singleton an die onBind()-Methode des Synchronisierungsadapter-Dienstes zurückzugeben.
Optional: Eine Service-Komponente, die auf Anfragen des Systems zur Nutzerauthentifizierung reagiert.
AccountManager startet diesen Dienst, um den Authentifizierungsprozess zu starten. Die Methode onCreate() des Dienstes instanziiert ein Authenticator-Objekt enthält. Wenn das System ein Nutzerkonto für den Synchronisierungsadapter der Anwendung installiert, ruft er die onBind()-Methode zum Abrufen eines IBinder für den Authenticator. Dadurch kann das System prozessübergreifende Aufrufe der Methoden des Authenticators.
Optional: Eine konkrete abgeleitete Klasse von AbstractAccountAuthenticator, die Anfragen für Authentifizierung.
Diese Klasse bietet Methoden, die vom AccountManager aufgerufen werden, um die Anmeldedaten des Nutzers beim Server zu authentifizieren. Die Details des Authentifizierungsverfahrens variieren je nach verwendeter Servertechnologie stark. Sie sollten Weitere Informationen zur Authentifizierung finden Sie in der Dokumentation Ihrer Serversoftware.
XML-Dateien, die den Synchronisierungsadapter und den Authenticator für das System definieren.
Die zuvor beschriebenen Komponenten des Synchronisierungsadapters und des Authentifizierungsdienstes sind definiert in <service> im App-Manifest ein. Diese Elemente enthalten untergeordnete <meta-data>-Elemente, die dem System bestimmte Daten zur Verfügung stellen:
  • Das Element <meta-data> für den Synchronisierungsadapterdienst verweist auf die XML-Datei res/xml/syncadapter.xml. Diese Datei gibt wiederum einen URI für den Webdienst an, der mit dem Kontaktdatenanbieter synchronisiert wird, sowie einen Kontotyp für den Webdienst.
  • Optional: Das Feld <meta-data> -Element für den Authenticator verweist auf die XML-Datei res/xml/authenticator.xml. Diese Datei gibt wiederum die Kontotyp, den dieser Authenticator unterstützt, sowie UI-Ressourcen, die werden während des Authentifizierungsvorgangs angezeigt. Der in diesem Element angegebene Kontotyp muss mit dem für den Synchronisierungsadapter angegebenen Kontotyp übereinstimmen.

Daten aus sozialen Netzwerken

In den Tabellen „android.provider.ContactsContract.StreamItems“ und „android.provider.ContactsContract.StreamItemPhotos“ werden eingehende Daten aus sozialen Netzwerken verwaltet. Sie können einen Synchronisierungsadapter schreiben, der diesen Tabellen Streamdaten aus Ihrem eigenen Netzwerk hinzufügt, oder Streamdaten aus diesen Tabellen lesen und in Ihrer eigenen Anwendung anzeigen. Mit diesen Funktionen können Ihre Dienste und Anwendungen für soziale Netzwerke in die sozialen Netzwerke von Android eingebunden werden.

Text im Stream für soziale Netzwerke

Streamelemente sind immer mit einem Rohkontakt verknüpft. Die android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID verweist auf die _ID-Wert für den Rohkontakt. Der Kontotyp und der Kontoname der unbearbeiteten Kontakte werden auch in der Zeile des Streamelements gespeichert.

Speichern Sie die Daten aus Ihrem Stream in den folgenden Spalten:

android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
Erforderlich Der Kontotyp des Nutzers für den Rohkontakt, der mit diesem Streamelement verknüpft ist. Denken Sie daran, diesen Wert festzulegen, wenn Sie ein Streamelement einfügen.
android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
Erforderlich Der Kontoname des Nutzers für den unverarbeiteten Kontakt, der mit diesem Streamelement. Achten Sie darauf, diesen Wert festzulegen, wenn Sie ein Streamelement einfügen.
ID-Spalten
Erforderlich. Wenn du ein Streamelement einfügst, musst du die folgenden Spalten mit IDs einfügen:
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: Die android.provider.BaseColumns#_ID-Wert des Kontakts, den dieser Stream enthält mit dem das Element verknüpft ist.
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: Der Wert von android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY des Kontakts, mit dem dieses Streamelement verknüpft ist.
  • android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: Der Wert von android.provider.BaseColumns#_ID des Rohkontakts, mit dem dieses Streamelement verknüpft ist.
android.provider.ContactsContract.StreamItemsColumns#KOMMENTARE
Optional. Hier werden Zusammenfassungsinformationen gespeichert, die am Anfang eines Streamelements angezeigt werden können.
android.provider.ContactsContract.StreamItemsColumns#TEXT
Der Text des Streamelements, d. h. der Inhalt, der von der Quelle des Elements gepostet wurde, oder eine Beschreibung einer Aktion, die das Streamelement generiert hat. Diese Spalte kann folgende Informationen enthalten: alle Formatierungen und eingebetteten Ressourcenbilder, die von fromHtml() Der Anbieter kann lange Inhalte abschneiden oder aussparen, versucht aber, Trenn-Tags zu vermeiden.
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
Ein Textstring, der die Zeit enthält, zu der das Streamelement eingefügt oder aktualisiert wurde, in Form von Millisekunden seit der Epoche. Anwendungen, die Streamelemente einfügen oder aktualisieren, sind für die Pflege dieser Spalte verantwortlich; nicht automatisch vom Kontaktanbieter.

Verwenden Sie zum Anzeigen von Identifizierungsinformationen für Ihre Stream-Elemente die android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL und android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE des zum Verknüpfen mit Ressourcen in Ihrer Anwendung.

Die Tabelle „android.provider.ContactsContract.StreamItems“ enthält außerdem die Spalten „android.provider.ContactsContract.StreamItemsColumns#SYNC1“ bis „android.provider.ContactsContract.StreamItemsColumns#SYNC4“, die ausschließlich für Synchronadapter verwendet werden.

Fotos aus sozialen Netzwerken

In der Tabelle „android.provider.ContactsContract.StreamItemPhotos“ werden Fotos gespeichert, die mit mit einem Streamelement. Die Spalte „android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID“ der Tabelle verweist auf Werte in der Spalte _ID der Tabelle „android.provider.ContactsContract.StreamItems“. Fotoreferenzen werden im in diese Spalten einfügen:

Spalte „PHOTO“ der Tabelle „android.provider.ContactsContract.StreamItemPhotos“ (BLOB)
Eine binäre Darstellung des Fotos, die vom Anbieter für die Speicherung und Anzeige neu skaliert wurde. Diese Spalte ist für die Abwärtskompatibilität mit früheren Versionen des Kontaktdatenanbieters verfügbar, in denen sie zum Speichern von Fotos verwendet wurde. In der aktuellen Version sollten Sie diese Spalte jedoch nicht zum Speichern von Fotos verwenden. Verwenden Sie stattdessen entweder android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID oder android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (beide werden in den folgenden Punkten beschrieben), um Fotos in einer Datei zu speichern. Diese Spalte enthält eine Miniaturansicht des Fotos, die zum Lesen verfügbar ist.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
Eine numerische Kennung eines Fotos für einen Rohkontakt. Diesen Wert an die Konstante anhängen DisplayPhoto.CONTENT_URI um einen Inhalts-URI abzurufen, der auf eine einzelne Fotodatei verweist, und rufen Sie dann openAssetFileDescriptor(), um einen Alias für die Fotodatei abzurufen.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
Ein Inhalts-URI, der direkt auf die Fotodatei für das Foto verweist, das durch diese Zeile dargestellt wird. Rufen Sie openAssetFileDescriptor() mit diesem URI auf, um einen Handle für die Fotodatei zu erhalten.

Tabellen für den Social Stream verwenden

Diese Tabellen funktionieren bis auf die folgenden Ausnahmen genau wie die anderen Haupttabellen im Contacts Provider:

  • Diese Tabellen erfordern zusätzliche Zugriffsberechtigungen. Um daraus zu lesen, muss Ihre Anwendung muss die Berechtigung „android.Manifest.permission#READ_SOCIAL_STREAM“ haben. Wenn Sie sie ändern möchten, muss Ihre Anwendung die Berechtigung „android.Manifest.permission#WRITE_SOCIAL_STREAM“ haben.
  • Die Anzahl der Zeilen für die Tabelle „android.provider.ContactsContract.StreamItems“ für jeden Rohkontakt begrenzt ist. Sobald dieses Limit erreicht ist, Kontakteanbieter macht Platz für neue Streamelementzeilen, indem er automatisch die Zeilen mit den ältesten android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP. Um das Limit abzurufen, stellen Sie eine Abfrage an den Inhalts-URI android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. Du kannst alle Argumente außer dem Inhalts-URI auf null belassen. Die Abfrage gibt einen Cursor mit einer einzelnen Zeile und der einzelnen Spalte „android.provider.ContactsContract.StreamItems#MAX_ITEMS“ zurück.

Die Klasse android.provider.ContactsContract.StreamItems.StreamItemPhotos definiert eine untergeordnete Tabelle von android.provider.ContactsContract.StreamItemPhotos, die die Fotozeilen für ein einzelnes Streamelement enthält.

Interaktionen im Social Stream

Die vom Kontaktdatenanbieter verwalteten Daten des Social Streams in Verbindung mit der Kontaktanwendung des Geräts bieten eine leistungsstarke Möglichkeit, Ihr Social-Networking-System mit vorhandenen Kontakten zu verknüpfen. Folgende Funktionen sind verfügbar:

  • Durch die Synchronisierung Ihres sozialen Netzwerks mit dem Contacts Provider können Sie die letzten Aktivitäten für die Kontakte eines Nutzers abrufen und im „android.provider.ContactsContract.StreamItems“ und android.provider.ContactsContract.StreamItemPhotos-Tabellen zur späteren Verwendung.
  • Neben der regelmäßigen Synchronisierung können Sie Ihren Synchronisierungsadapter so konfigurieren, dass zusätzliche Daten abgerufen werden, wenn der Nutzer einen Kontakt zum Ansehen auswählt. Dadurch kann der Synchronisierungsadapter um Fotos mit hoher Auflösung und die aktuellsten Stream-Elemente für den Kontakt abzurufen.
  • Wenn Sie eine Benachrichtigung in der Kontaktanwendung des Geräts und beim Kontaktanbieter registrieren, können Sie eine Intent erhalten, wenn ein Kontakt aufgerufen wird, und den Status des Kontakts dann über Ihren Dienst aktualisieren. Dieser Ansatz ist möglicherweise schneller und benötigt weniger Bandbreite als eine vollständige Synchronisierung mit einem Synchronisierungsadapter.
  • Die Nutzer können Ihrem sozialen Netzwerk einen Kontakt hinzufügen, während sie den Kontakt ansehen. in der Kontakte App auf dem Gerät. Sie aktivieren diese Funktion mit der Funktion „Kontakt einladen“. Dazu kombinieren Sie eine Aktivität, durch die ein vorhandener Kontakt zu Ihrem Netzwerk hinzugefügt wird, mit einer XML-Datei, die der Kontaktanwendung des Geräts und dem Kontaktdatenanbieter die Details Ihrer Anwendung zur Verfügung stellt.

Die regelmäßige Synchronisierung von Streamelementen mit dem Kontaktdatenanbieter erfolgt wie bei anderen Synchronisierungen. Weitere Informationen zur Synchronisierung finden Sie im Abschnitt Synchronadapter für Kontaktdatenanbieter. In den nächsten beiden Abschnitten erfahren Sie, wie Sie Benachrichtigungen registrieren und Kontakte einladen.

Registrierung für die Verarbeitung von Aufrufen in sozialen Netzwerken

So registrieren Sie Ihren Synchronisierungsadapter, um Benachrichtigungen zu erhalten, wenn der Nutzer einen Kontakt aufruft, der von Ihrem Synchronisierungsadapter verwaltet wird:

  1. Erstellen Sie in der Datei res/xml/ Ihres Projekts eine Datei mit dem Namen contacts.xml. -Verzeichnis. Wenn Sie diese Datei bereits haben, können Sie diesen Schritt überspringen.
  2. Fügen Sie in dieser Datei das Element <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> hinzu. Wenn dieses Element bereits vorhanden ist, können Sie diesen Schritt überspringen.
  3. Wenn Sie einen Dienst registrieren möchten, der benachrichtigt wird, wenn der Nutzer die Detailseite eines Kontakts in der Kontaktanwendung des Geräts öffnet, fügen Sie dem Element das Attribut viewContactNotifyService="serviceclass" hinzu. Dabei ist serviceclass die vollqualifizierte Klassenname des Dienstes, der die Intent-Anfrage von der Kontaktanwendung des Geräts erhalten soll. Für die benachrichtigende Person verwenden Sie eine Klasse, die IntentService erweitert, damit der Dienst Intents empfangen. Die Daten im eingehenden Intent enthalten den Inhalts-URI der Rohdaten auf den der Nutzer geklickt hat. Vom Notifier-Dienst können Sie eine Bindung an Ihren Sync-Adapter, um die Daten für den Rohkontakt zu aktualisieren.

So registrierst du eine Aktivität, die aufgerufen werden soll, wenn der Nutzer auf ein Streamelement, ein Foto oder beides klickt:

  1. Erstellen Sie im Verzeichnis res/xml/ Ihres Projekts eine Datei mit dem Namen contacts.xml. Wenn Sie diese Datei bereits haben, können Sie diesen Schritt überspringen.
  2. Fügen Sie in dieser Datei das Element <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> Wenn dieses Element bereits vorhanden ist, können Sie diesen Schritt überspringen.
  3. Um eine Ihrer Aktivitäten zu registrieren, damit der Nutzer, der auf ein Stream-Element in der in der Kontakte App auf Ihrem Gerät das Attribut viewStreamItemActivity="activityclass" zum Element, wobei activityclass ist der voll qualifizierte Klassenname der Aktivität das den Intent von der Kontakte App auf dem Gerät empfangen soll.
  4. Zum Registrieren einer Ihrer Aktivitäten, damit der Nutzer, der auf ein Stream-Foto in der in der Kontakte App auf Ihrem Gerät das Attribut viewStreamItemPhotoActivity="activityclass" zum Element, wobei activityclass ist der voll qualifizierte Klassenname der Aktivität das den Intent von der Kontakte App auf dem Gerät empfangen soll.

Das <ContactsAccountType>-Element wird ausführlicher beschrieben in der Abschnitt <ContactsAccountType> -Element.

Die eingehende Intent-Nachricht enthält den Inhalts-URI des Artikels oder Fotos, auf das der Nutzer geklickt hat. Wenn Sie separate Aktivitäten für Textelemente und Fotos haben möchten, verwenden Sie beide Attribute in derselben Datei.

Interaktion mit Ihrem sozialen Netzwerk

Nutzer müssen die Kontakte-App des Geräts nicht verlassen, um einen Kontakt zu Ihrer Social-Networking-Website einzuladen. Stattdessen können Sie die Kontakte-App des Geräts so konfigurieren, dass sie eine Intent sendet, um den Kontakt zu einer Ihrer Aktivitäten einzuladen. So richtest du ein Konto mit Elternaufsicht ein:

  1. Erstellen Sie in der Datei res/xml/ Ihres Projekts eine Datei mit dem Namen contacts.xml. -Verzeichnis. Wenn Sie diese Datei bereits haben, können Sie diesen Schritt überspringen.
  2. Fügen Sie in dieser Datei das Element <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> Wenn dieses Element bereits vorhanden ist, können Sie diesen Schritt überspringen.
  3. Fügen Sie die folgenden Attribute hinzu:
    • inviteContactActivity="activityclass"
    • inviteContactActionLabel="@string/invite_action_label"
    Der Wert von activityclass ist der voll qualifizierte Klassenname der Aktivität, die die Absicht erhalten soll. Der Wert invite_action_label ist ein Textstring, der in der Kontakte-App des Geräts im Menü Verbindung hinzufügen angezeigt wird.

Hinweis:ContactsSource ist ein nicht mehr unterstützter Tag-Name für ContactsAccountType.

Referenz zu contacts.xml

Die Datei contacts.xml enthält XML-Elemente, die die Interaktion deines Adapter und Anwendung mit der Kontakte-Anwendung und dem Contacts Provider synchronisieren. Diese -Elemente werden in den folgenden Abschnitten beschrieben.

<ContactsAccountType>-Element

Das Element <ContactsAccountType> steuert die Interaktion deines mit der Kontakte App. Die Syntax lautet so:

<ContactsAccountType
        xmlns:android="http://schemas.android.com/apk/res/android"
        inviteContactActivity="activity_name"
        inviteContactActionLabel="invite_command_text"
        viewContactNotifyService="view_notify_service"
        viewGroupActivity="group_view_activity"
        viewGroupActionLabel="group_action_text"
        viewStreamItemActivity="viewstream_activity_name"
        viewStreamItemPhotoActivity="viewphotostream_activity_name">

enthalten in:

res/xml/contacts.xml

kann Folgendes enthalten:

<ContactsDataKind>

Beschreibung:

Deklariert Android-Komponenten und UI-Labels, mit denen Nutzer einen ihrer Kontakte einladen können zu in einem sozialen Netzwerk, Nutzer benachrichtigen, wenn einer ihrer Streams in sozialen Netzwerken aktualisiert wird, und so weiter.

Beachten Sie, dass das Attributpräfix android: für die Attribute von <ContactsAccountType>.

Attribute:

inviteContactActivity
Der vollqualifizierte Klassenname der Aktivität in Ihrer Anwendung, die aktiviert werden soll, wenn der Nutzer in der Kontaktanwendung des Geräts Verbindung hinzufügen auswählt.
inviteContactActionLabel
Eine Textzeichenfolge, die für die in angegebene Aktivität angezeigt wird. inviteContactActivity im Menü Verbindung hinzufügen. Sie können beispielsweise den String „In meinem Netzwerk folgen“ verwenden. Sie können eine String-Ressource verwenden ID für dieses Label.
viewContactNotifyService
Der vollständig qualifizierte Klassenname eines Dienstes in Ihrer Anwendung, der Benachrichtigungen erhalten soll, wenn sich der Nutzer einen Kontakt ansieht. Diese Benachrichtigung wird vom Kontakte-Anwendung; Es ermöglicht Ihrer Anwendung, datenintensive Vorgänge zu verschieben. bis sie gebraucht werden. Ihre Anwendung kann beispielsweise auf diese Benachrichtigung reagieren indem Sie das hochauflösende Foto des Kontakts und Elemente aus sozialen Streams. Diese Funktion wird im Abschnitt ausführlicher beschrieben. Interaktionen über Streams in sozialen Netzwerken:
viewGroupActivity
Der voll qualifizierte Klassenname einer Aktivität in Ihrer Anwendung, die angezeigt werden kann Gruppeninformationen. Wenn der Nutzer in der Kontakte-App des Geräts auf das Gruppenlabel klickt, wird die Benutzeroberfläche für diese Aktivität angezeigt.
viewGroupActionLabel
Das Label, das die Kontaktanwendung für ein Steuerelement der Benutzeroberfläche anzeigt, mit dem sich Nutzer Gruppen in Ihrer Anwendung ansehen können.

Für dieses Attribut ist eine Stringressourcen-ID zulässig.

viewStreamItemActivity
Der voll qualifizierte Klassenname einer Aktivität in Ihrer App, die vom Gerät wird gestartet, wenn der Nutzer auf ein Streamelement für einen unbearbeiteten Kontakt klickt.
viewStreamItemPhotoActivity
Der voll qualifizierte Klassenname einer Aktivität in Ihrer Anwendung, die die Kontakte-App des Geräts startet, wenn der Nutzer auf ein Foto im Streamelement für einen Rohkontakt klickt.

<ContactsDataKind> Unterelement

Mit dem Element <ContactsDataKind> wird die Anzeige der benutzerdefinierten Datenzeilen Ihrer Anwendung in der Benutzeroberfläche der Kontakte-App gesteuert. Sie hat die folgende Syntax:

<ContactsDataKind
        android:mimeType="MIMEtype"
        android:icon="icon_resources"
        android:summaryColumn="column_name"
        android:detailColumn="column_name">

enthalten in:

<ContactsAccountType>

Beschreibung:

Verwenden Sie dieses Element, damit die Kontakte-Anwendung den Inhalt einer benutzerdefinierten Datenzeile als Teil der Details eines unbearbeiteten Kontakts. Jedes untergeordnete <ContactsDataKind>-Element <ContactsAccountType> steht für eine Zeile mit benutzerdefinierten Daten, die bei der Synchronisierung Adapter zur Tabelle ContactsContract.Data hinzugefügt. Hinzufügen <ContactsDataKind>-Element für jeden verwendeten benutzerdefinierten MIME-Typ. Sie müssen das Element nicht hinzufügen, wenn Sie eine benutzerdefinierte Datenzeile haben, für die keine Daten angezeigt werden sollen.

Attribute:

android:mimeType
Der benutzerdefinierte MIME-Typ, den Sie für einen Ihrer benutzerdefinierten Datenzeilentypen in der Tabelle ContactsContract.Data definiert haben. Der Wert vnd.android.cursor.item/vnd.example.locationstatus kann beispielsweise ein benutzerdefinierter MIME-Typ für eine Datenzeile sein, in der der letzte bekannte Standort eines Kontakts gespeichert ist.
android:icon
Ein Android-Gerät Drawable-Ressource die die Kontakte App neben Ihren Daten anzeigt. Hiermit weisen Sie den Nutzer darauf hin, dass die Daten aus Ihrem Dienst stammen.
android:summaryColumn
Der Spaltenname für den ersten von zwei aus der Datenzeile abgerufenen Werten. Die wird als erste Zeile des Eintrags für diese Datenzeile angezeigt. Die erste Zeile ist die als Zusammenfassung der Daten dienen sollen, aber optional sind. Siehe auch android:detailColumn einen Wert zuweisen.
android:detailColumn
Der Name der Spalte für den zweiten von zwei Werten, die aus der Datenzeile abgerufen wurden. Der Wert wird als zweite Zeile des Eintrags für diese Datenzeile angezeigt. Siehe auch android:summaryColumn.

Zusätzliche Funktionen für Kontakte-Anbieter

Neben den in den vorherigen Abschnitten beschriebenen Hauptfunktionen bietet der Contacts Provider diese nützlichen Funktionen für die Arbeit mit Kontaktdaten:

  • Kontaktgruppen
  • Fotofunktionen

Kontaktgruppen

Der Kontaktanbieter kann Sammlungen ähnlicher Kontakte mit group-Daten. Wenn der mit einem Nutzerkonto verknüpfte Server Gruppen verwalten möchte, sollte der Synchronisierungsadapter für den Kontotyp des Kontos Gruppendaten zwischen dem Kontaktdatenanbieter und dem Server übertragen. Wenn Nutzer dem Server einen neuen Kontakt hinzufügen und diesen Kontakt dann einer neuen Gruppe zuweisen, muss der Synchronisierungsadapter die neue Gruppe der Tabelle ContactsContract.Groups hinzufügen. Die Gruppe oder Gruppierung Kontakte, zu denen der Kontakt gehört, werden in der Tabelle ContactsContract.Data gespeichert. Dabei werden mithilfe den MIME-Typ ContactsContract.CommonDataKinds.GroupMembership.

Wenn Sie einen Synchronisierungsadapter entwerfen, der Rohdaten aus und Sie keine Gruppen verwenden, müssen Sie dem Kontaktanbieter Dienstleister, der Ihre Daten sichtbar macht. In dem Code, der ausgeführt wird, wenn ein Nutzer ein Konto hinzufügt auf dem Gerät, aktualisiere ContactsContract.Settings Zeile, die der Kontaktanbieter für das Konto hinzufügt. Legen Sie in dieser Zeile den Wert der Settings.UNGROUPED_VISIBLE auf 1 setzen. In diesem Fall macht der Kontaktanbieter Ihre Kontaktdaten immer sichtbar, auch wenn Sie keine Gruppen verwenden.

Kontaktfotos

In der Tabelle ContactsContract.Data werden Fotos als Zeilen mit MIME-Typ gespeichert Photo.CONTENT_ITEM_TYPE. Die Spalte CONTACT_ID der Zeile ist mit der Spalte _ID des Rohkontakts verknüpft, zu dem sie gehört. Die Klasse ContactsContract.Contacts.Photo definiert eine untergeordnete Tabelle von ContactsContract.Contacts, die Fotoinformationen für das Hauptfoto eines Kontakts enthält. Das ist das Hauptfoto des primären Rohkontakts des Kontakts. Ähnlich definiert die Klasse ContactsContract.RawContacts.DisplayPhoto eine untergeordnete Tabelle von ContactsContract.RawContacts, die Fotoinformationen für das Hauptfoto eines Rohkontakts enthält.

Die Referenzdokumentation zu ContactsContract.Contacts.Photo und ContactsContract.RawContacts.DisplayPhoto enthält Beispiele zum Abrufen von Fotoinformationen. Es gibt keine praktische Klasse zum Abrufen des primären Thumbnails für einen Rohkontakt. Sie können jedoch eine Abfrage an die Tabelle ContactsContract.Data senden und die Spalten _ID, Photo.CONTENT_ITEM_TYPE und IS_PRIMARY auswählen, um die Zeile mit dem primären Foto des Rohkontakts zu finden.

Social-Stream-Daten für eine Person können auch Fotos enthalten. Diese werden im android.provider.ContactsContract.StreamItemPhotos, die in weiteren im Abschnitt Fotos aus sozialen Netzwerken.