2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-25 11:27:22 +00:00

259 lines
9.3 KiB
Java
Raw Normal View History

Add contacts-reading plugin (Android side) Summary: Add a plugin to the KDE Connect Android application which supports reading the Android contacts databases and sending the requested data as vcards - Android automatically has support for exporting vcards with all the fields you would expect (phone, email, photo, etc.) - I add two custom fields, one for the modification timestamp and another for the NAME_RAW_CONTACT_ID so that the contacts can be correlated back to the Android database This does not (yet) support writing contacts back to the phone nor does it automatically listen to the phone's contacts database to change BUG: 367999 Test Plan: Connect the device to the desktop and verify that vcards are created in QStandardPaths::GenericDataLocation / kpeoplevcard". On my system this is ~/.local/share/kpeoplevcard Create a dummy contact on the device and verify it is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Modify the dummy contact and verify the modifications are synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Delete the dummy contact and verify the deletion is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Reviewers: #kde_connect, mtijink, nicolasfella Reviewed By: #kde_connect, nicolasfella Subscribers: MatMaul, philipc, kdeconnect, nicolasfella, andyholmes, mtijink Tags: #kde_connect Maniphest Tasks: T8283 Differential Revision: https://phabricator.kde.org/D9690
2018-05-30 18:33:23 -06:00
/*
* ContactsPlugin.java - This file is part of KDE Connect's Android App
* Implement a way to request and send contact information
*
* Copyright 2018 Simon Redman <simon@ergotech.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
2018-09-29 20:28:47 +02:00
*/
Add contacts-reading plugin (Android side) Summary: Add a plugin to the KDE Connect Android application which supports reading the Android contacts databases and sending the requested data as vcards - Android automatically has support for exporting vcards with all the fields you would expect (phone, email, photo, etc.) - I add two custom fields, one for the modification timestamp and another for the NAME_RAW_CONTACT_ID so that the contacts can be correlated back to the Android database This does not (yet) support writing contacts back to the phone nor does it automatically listen to the phone's contacts database to change BUG: 367999 Test Plan: Connect the device to the desktop and verify that vcards are created in QStandardPaths::GenericDataLocation / kpeoplevcard". On my system this is ~/.local/share/kpeoplevcard Create a dummy contact on the device and verify it is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Modify the dummy contact and verify the modifications are synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Delete the dummy contact and verify the deletion is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Reviewers: #kde_connect, mtijink, nicolasfella Reviewed By: #kde_connect, nicolasfella Subscribers: MatMaul, philipc, kdeconnect, nicolasfella, andyholmes, mtijink Tags: #kde_connect Maniphest Tasks: T8283 Differential Revision: https://phabricator.kde.org/D9690
2018-05-30 18:33:23 -06:00
package org.kde.kdeconnect.Plugins.ContactsPlugin;
import android.Manifest;
import android.annotation.TargetApi;
import android.os.Build;
import android.provider.ContactsContract;
import android.util.Log;
import org.kde.kdeconnect.Helpers.ContactsHelper;
import org.kde.kdeconnect.Helpers.ContactsHelper.VCardBuilder;
import org.kde.kdeconnect.Helpers.ContactsHelper.uID;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class ContactsPlugin extends Plugin {
/**
* Used to request the device send the unique ID of every contact
*/
2018-09-29 20:28:47 +02:00
private static final String PACKET_TYPE_CONTACTS_REQUEST_ALL_UIDS_TIMESTAMPS = "kdeconnect.contacts.request_all_uids_timestamps";
Add contacts-reading plugin (Android side) Summary: Add a plugin to the KDE Connect Android application which supports reading the Android contacts databases and sending the requested data as vcards - Android automatically has support for exporting vcards with all the fields you would expect (phone, email, photo, etc.) - I add two custom fields, one for the modification timestamp and another for the NAME_RAW_CONTACT_ID so that the contacts can be correlated back to the Android database This does not (yet) support writing contacts back to the phone nor does it automatically listen to the phone's contacts database to change BUG: 367999 Test Plan: Connect the device to the desktop and verify that vcards are created in QStandardPaths::GenericDataLocation / kpeoplevcard". On my system this is ~/.local/share/kpeoplevcard Create a dummy contact on the device and verify it is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Modify the dummy contact and verify the modifications are synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Delete the dummy contact and verify the deletion is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Reviewers: #kde_connect, mtijink, nicolasfella Reviewed By: #kde_connect, nicolasfella Subscribers: MatMaul, philipc, kdeconnect, nicolasfella, andyholmes, mtijink Tags: #kde_connect Maniphest Tasks: T8283 Differential Revision: https://phabricator.kde.org/D9690
2018-05-30 18:33:23 -06:00
/**
* Used to request the names for the contacts corresponding to a list of UIDs
* <p>
* It shall contain the key "uids", which will have a list of uIDs (long int, as string)
*/
2018-09-29 20:28:47 +02:00
private static final String PACKET_TYPE_CONTACTS_REQUEST_VCARDS_BY_UIDS = "kdeconnect.contacts.request_vcards_by_uid";
Add contacts-reading plugin (Android side) Summary: Add a plugin to the KDE Connect Android application which supports reading the Android contacts databases and sending the requested data as vcards - Android automatically has support for exporting vcards with all the fields you would expect (phone, email, photo, etc.) - I add two custom fields, one for the modification timestamp and another for the NAME_RAW_CONTACT_ID so that the contacts can be correlated back to the Android database This does not (yet) support writing contacts back to the phone nor does it automatically listen to the phone's contacts database to change BUG: 367999 Test Plan: Connect the device to the desktop and verify that vcards are created in QStandardPaths::GenericDataLocation / kpeoplevcard". On my system this is ~/.local/share/kpeoplevcard Create a dummy contact on the device and verify it is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Modify the dummy contact and verify the modifications are synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Delete the dummy contact and verify the deletion is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Reviewers: #kde_connect, mtijink, nicolasfella Reviewed By: #kde_connect, nicolasfella Subscribers: MatMaul, philipc, kdeconnect, nicolasfella, andyholmes, mtijink Tags: #kde_connect Maniphest Tasks: T8283 Differential Revision: https://phabricator.kde.org/D9690
2018-05-30 18:33:23 -06:00
/**
* Response indicating the packet contains a list of contact uIDs
* <p>
* It shall contain the key "uids", which will mark a list of uIDs (long int, as string)
* The returned IDs can be used in future requests for more information about the contact
*/
2018-09-29 20:28:47 +02:00
private static final String PACKET_TYPE_CONTACTS_RESPONSE_UIDS_TIMESTAMPS = "kdeconnect.contacts.response_uids_timestamps";
Add contacts-reading plugin (Android side) Summary: Add a plugin to the KDE Connect Android application which supports reading the Android contacts databases and sending the requested data as vcards - Android automatically has support for exporting vcards with all the fields you would expect (phone, email, photo, etc.) - I add two custom fields, one for the modification timestamp and another for the NAME_RAW_CONTACT_ID so that the contacts can be correlated back to the Android database This does not (yet) support writing contacts back to the phone nor does it automatically listen to the phone's contacts database to change BUG: 367999 Test Plan: Connect the device to the desktop and verify that vcards are created in QStandardPaths::GenericDataLocation / kpeoplevcard". On my system this is ~/.local/share/kpeoplevcard Create a dummy contact on the device and verify it is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Modify the dummy contact and verify the modifications are synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Delete the dummy contact and verify the deletion is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Reviewers: #kde_connect, mtijink, nicolasfella Reviewed By: #kde_connect, nicolasfella Subscribers: MatMaul, philipc, kdeconnect, nicolasfella, andyholmes, mtijink Tags: #kde_connect Maniphest Tasks: T8283 Differential Revision: https://phabricator.kde.org/D9690
2018-05-30 18:33:23 -06:00
/**
* Response indicating the packet contains a list of contact names
* <p>
* It shall contain the key "uids", which will mark a list of uIDs (long int, as string)
* then, for each UID, there shall be a field with the key of that UID and the value of the name of the contact
* <p>
* For example:
* ( 'uids' : ['1', '3', '15'],
2018-09-29 20:28:47 +02:00
* '1' : 'John Smith',
* '3' : 'Abe Lincoln',
* '15' : 'Mom' )
Add contacts-reading plugin (Android side) Summary: Add a plugin to the KDE Connect Android application which supports reading the Android contacts databases and sending the requested data as vcards - Android automatically has support for exporting vcards with all the fields you would expect (phone, email, photo, etc.) - I add two custom fields, one for the modification timestamp and another for the NAME_RAW_CONTACT_ID so that the contacts can be correlated back to the Android database This does not (yet) support writing contacts back to the phone nor does it automatically listen to the phone's contacts database to change BUG: 367999 Test Plan: Connect the device to the desktop and verify that vcards are created in QStandardPaths::GenericDataLocation / kpeoplevcard". On my system this is ~/.local/share/kpeoplevcard Create a dummy contact on the device and verify it is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Modify the dummy contact and verify the modifications are synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Delete the dummy contact and verify the deletion is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Reviewers: #kde_connect, mtijink, nicolasfella Reviewed By: #kde_connect, nicolasfella Subscribers: MatMaul, philipc, kdeconnect, nicolasfella, andyholmes, mtijink Tags: #kde_connect Maniphest Tasks: T8283 Differential Revision: https://phabricator.kde.org/D9690
2018-05-30 18:33:23 -06:00
*/
2018-09-29 20:28:47 +02:00
private static final String PACKET_TYPE_CONTACTS_RESPONSE_VCARDS = "kdeconnect.contacts.response_vcards";
Add contacts-reading plugin (Android side) Summary: Add a plugin to the KDE Connect Android application which supports reading the Android contacts databases and sending the requested data as vcards - Android automatically has support for exporting vcards with all the fields you would expect (phone, email, photo, etc.) - I add two custom fields, one for the modification timestamp and another for the NAME_RAW_CONTACT_ID so that the contacts can be correlated back to the Android database This does not (yet) support writing contacts back to the phone nor does it automatically listen to the phone's contacts database to change BUG: 367999 Test Plan: Connect the device to the desktop and verify that vcards are created in QStandardPaths::GenericDataLocation / kpeoplevcard". On my system this is ~/.local/share/kpeoplevcard Create a dummy contact on the device and verify it is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Modify the dummy contact and verify the modifications are synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Delete the dummy contact and verify the deletion is synchronized (Currently not automatic, have to disconnect and reconnect or use dbus) Reviewers: #kde_connect, mtijink, nicolasfella Reviewed By: #kde_connect, nicolasfella Subscribers: MatMaul, philipc, kdeconnect, nicolasfella, andyholmes, mtijink Tags: #kde_connect Maniphest Tasks: T8283 Differential Revision: https://phabricator.kde.org/D9690
2018-05-30 18:33:23 -06:00
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_contacts);
}
@Override
public String getDescription() {
return context.getResources().getString(R.string.pref_plugin_contacts_desc);
}
@Override
public String[] getSupportedPacketTypes() {
return new String[]{
PACKET_TYPE_CONTACTS_REQUEST_ALL_UIDS_TIMESTAMPS,
PACKET_TYPE_CONTACTS_REQUEST_VCARDS_BY_UIDS
};
}
@Override
public String[] getOutgoingPacketTypes() {
return new String[]{
PACKET_TYPE_CONTACTS_RESPONSE_UIDS_TIMESTAMPS,
PACKET_TYPE_CONTACTS_RESPONSE_VCARDS
};
}
@Override
public boolean onCreate() {
permissionExplanation = R.string.contacts_permission_explanation;
return true;
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@Override
public String[] getRequiredPermissions() {
return new String[]{Manifest.permission.READ_CONTACTS};
// One day maybe we will also support WRITE_CONTACTS, but not yet
}
@Override
public int getMinSdk() {
// Need API 18 for contact timestamps
return Build.VERSION_CODES.JELLY_BEAN_MR2;
}
/**
* Add custom fields to the vcard to keep track of KDE Connect-specific fields
* <p>
* These include the local device's uID as well as last-changed timestamp
* <p>
* This might be extended in the future to include more fields
*
* @param vcard vcard to apply metadata to
* @param uID uID to which the vcard corresponds
* @return The same VCard as was passed in, but now with KDE Connect-specific fields
*/
protected VCardBuilder addVCardMetadata(VCardBuilder vcard, uID uID) {
// Append the device ID line
// Unclear if the deviceID forms a valid name per the vcard spec. Worry about that later..
vcard.appendLine("X-KDECONNECT-ID-DEV-" + device.getDeviceId(),
uID.toString());
// Build the timestamp line
// Maybe one day this should be changed into the vcard-standard REV key
List<uID> uIDs = new ArrayList<>();
uIDs.add(uID);
final String[] contactsProjection = new String[]{
ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP
};
Map<uID, Map<String, Object>> timestamp = ContactsHelper.getColumnsFromContactsForIDs(context, uIDs, contactsProjection);
vcard.appendLine("X-KDECONNECT-TIMESTAMP",
timestamp.get(uID).get(ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP).toString());
return vcard;
}
/**
* Return a unique identifier (Contacts.LOOKUP_KEY) for all contacts in the Contacts database
* <p>
* The identifiers returned can be used in future requests to get more information
* about the contact
*
* @param np The package containing the request
* @return true if successfully handled, false otherwise
*/
@SuppressWarnings("SameReturnValue")
protected boolean handleRequestAllUIDsTimestamps(@SuppressWarnings("unused") NetworkPacket np) {
NetworkPacket reply = new NetworkPacket(PACKET_TYPE_CONTACTS_RESPONSE_UIDS_TIMESTAMPS);
List<uID> uIDs = ContactsHelper.getAllContactContactIDs(context);
List<String> uIDsAsStrings = new ArrayList<>(uIDs.size());
for (uID uID : uIDs) {
uIDsAsStrings.add(uID.toString());
}
final String[] contactsProjection = new String[]{
ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP
};
reply.set("uids", uIDsAsStrings);
// Add last-modified timestamps
Map<uID, Map<String, Object>> uIDsToTimestamps = ContactsHelper.getColumnsFromContactsForIDs(context, uIDs, contactsProjection);
for (uID ID : uIDsToTimestamps.keySet()) {
Map<String, Object> data = uIDsToTimestamps.get(ID);
reply.set(ID.toString(), (Integer) data.get(ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP));
}
device.sendPacket(reply);
return true;
}
protected boolean handleRequestVCardsByUIDs(NetworkPacket np) {
if (!np.has("uids")) {
Log.e("ContactsPlugin", "handleRequestNamesByUIDs received a malformed packet with no uids key");
return false;
}
List<String> uIDsAsStrings = np.getStringList("uids");
// Convert to Collection<uIDs> to call getVCardsForContactIDs
Set<uID> uIDs = new HashSet<>(uIDsAsStrings.size());
for (String uID : uIDsAsStrings) {
uIDs.add(new uID(uID));
}
Map<uID, VCardBuilder> uIDsToVCards = ContactsHelper.getVCardsForContactIDs(context, uIDs);
// ContactsHelper.getVCardsForContactIDs(..) is allowed to reply without
// some of the requested uIDs if they were not in the database, so update our list
uIDsAsStrings = new ArrayList<>(uIDsToVCards.size());
NetworkPacket reply = new NetworkPacket(PACKET_TYPE_CONTACTS_RESPONSE_VCARDS);
// Add the vcards to the packet
for (uID uID : uIDsToVCards.keySet()) {
VCardBuilder vcard = uIDsToVCards.get(uID);
vcard = this.addVCardMetadata(vcard, uID);
// Store this as a valid uID
uIDsAsStrings.add(uID.toString());
// Add the uid -> vcard pairing to the packet
reply.set(uID.toString(), vcard.toString());
}
// Add the valid uIDs to the packet
reply.set("uids", uIDsAsStrings);
device.sendPacket(reply);
return true;
}
@Override
public boolean onPacketReceived(NetworkPacket np) {
switch (np.getType()) {
case PACKET_TYPE_CONTACTS_REQUEST_ALL_UIDS_TIMESTAMPS:
return this.handleRequestAllUIDsTimestamps(np);
case PACKET_TYPE_CONTACTS_REQUEST_VCARDS_BY_UIDS:
return this.handleRequestVCardsByUIDs(np);
default:
Log.e("ContactsPlugin", "Contacts plugin received an unexpected packet!");
return false;
}
}
}