From c80ddc1b22b499111bb3a7d939dcc3107260a3ff Mon Sep 17 00:00:00 2001 From: macdonst Date: Wed, 13 Jul 2011 00:14:15 +0800 Subject: [PATCH] Issue #149: Deprecate support for Android 1.X devices Right now we are just removing the code for Contacts on 1.5/1.6 devices. We still need to keep around our implementation of Geolocation and Storage for older devices since some versions of Android have broken implementations of these features. Android 3.0 I'm looking at you! --- .../src/com/phonegap/ContactAccessor.java | 45 - .../com/phonegap/ContactAccessorSdk3_4.java | 833 ------------------ .../src/com/phonegap/ContactManager.java | 47 +- 3 files changed, 39 insertions(+), 886 deletions(-) delete mode 100644 framework/src/com/phonegap/ContactAccessorSdk3_4.java diff --git a/framework/src/com/phonegap/ContactAccessor.java b/framework/src/com/phonegap/ContactAccessor.java index dcf6839f..d17528ac 100644 --- a/framework/src/com/phonegap/ContactAccessor.java +++ b/framework/src/com/phonegap/ContactAccessor.java @@ -24,7 +24,6 @@ package com.phonegap; -import java.lang.reflect.Constructor; import java.util.HashMap; import android.app.Activity; @@ -44,53 +43,9 @@ import org.json.JSONObject; */ public abstract class ContactAccessor { - /** - * Static singleton instance of {@link ContactAccessor} holding the - * SDK-specific implementation of the class. - */ - private static ContactAccessor sInstance; protected final String LOG_TAG = "ContactsAccessor"; protected Activity mApp; protected WebView mView; - - public static ContactAccessor getInstance(WebView view, Activity app) { - if (sInstance == null) { - String className; - - /* - * Check the version of the SDK we are running on. Choose an - * implementation class designed for that version of the SDK. - * - * Unfortunately we have to use strings to represent the class - * names. If we used the conventional ContactAccessorSdk5.class.getName() - * syntax, we would get a ClassNotFoundException at runtime on pre-Eclair SDKs. - * Using the above syntax would force Dalvik to load the class and try to - * resolve references to all other classes it uses. Since the pre-Eclair - * does not have those classes, the loading of ContactAccessorSdk5 would fail. - */ - - if (android.os.Build.VERSION.RELEASE.startsWith("1.")) { - className = "com.phonegap.ContactAccessorSdk3_4"; - } else { - className = "com.phonegap.ContactAccessorSdk5"; - } - - /* - * Find the required class by name and instantiate it. - */ - try { - Class clazz = - Class.forName(className).asSubclass(ContactAccessor.class); - // Grab constructor for contactsmanager class dynamically. - Constructor classConstructor = clazz.getConstructor(Class.forName("android.webkit.WebView"), Class.forName("android.app.Activity")); - sInstance = classConstructor.newInstance(view, app); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - return sInstance; - } /** * Check to see if the data associated with the key is required to diff --git a/framework/src/com/phonegap/ContactAccessorSdk3_4.java b/framework/src/com/phonegap/ContactAccessorSdk3_4.java deleted file mode 100644 index b482051f..00000000 --- a/framework/src/com/phonegap/ContactAccessorSdk3_4.java +++ /dev/null @@ -1,833 +0,0 @@ -// Taken from Android tutorials -/* - * PhoneGap is available under *either* the terms of the modified BSD license *or* the - * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. - * - * Copyright (c) 2005-2010, Nitobi Software Inc. - * Copyright (c) 2010, IBM Corporation - */ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.phonegap; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.app.Activity; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; -import android.provider.Contacts; -import android.provider.Contacts.ContactMethods; -import android.provider.Contacts.ContactMethodsColumns; -import android.provider.Contacts.Organizations; -import android.provider.Contacts.People; -import android.provider.Contacts.Phones; -import android.util.Log; -import android.webkit.WebView; - -/** - * An implementation of {@link ContactAccessor} that uses legacy Contacts API. - * These APIs are deprecated and should not be used unless we are running on a - * pre-Eclair SDK. - *

- * There are several reasons why we wouldn't want to use this class on an Eclair device: - *

- */ -@SuppressWarnings("deprecation") -public class ContactAccessorSdk3_4 extends ContactAccessor { - private static final String PEOPLE_ID_EQUALS = "people._id = ?"; - /** - * A static map that converts the JavaScript property name to Android database column name. - */ - private static final Map dbMap = new HashMap(); - static { - dbMap.put("id", People._ID); - dbMap.put("displayName", People.DISPLAY_NAME); - dbMap.put("phoneNumbers", Phones.NUMBER); - dbMap.put("phoneNumbers.value", Phones.NUMBER); - dbMap.put("emails", ContactMethods.DATA); - dbMap.put("emails.value", ContactMethods.DATA); - dbMap.put("addresses", ContactMethodsColumns.DATA); - dbMap.put("addresses.formatted", ContactMethodsColumns.DATA); - dbMap.put("ims", ContactMethodsColumns.DATA); - dbMap.put("ims.value", ContactMethodsColumns.DATA); - dbMap.put("organizations", Organizations.COMPANY); - dbMap.put("organizations.name", Organizations.COMPANY); - dbMap.put("organizations.title", Organizations.TITLE); - dbMap.put("note", People.NOTES); - } - - /** - * Create an contact accessor. - */ - public ContactAccessorSdk3_4(WebView view, Activity app) - { - mApp = app; - mView = view; - } - - @Override - /** - * This method takes the fields required and search options in order to produce an - * array of contacts that matches the criteria provided. - * @param fields an array of items to be used as search criteria - * @param options that can be applied to contact searching - * @return an array of contacts - */ - public JSONArray search(JSONArray fields, JSONObject options) { - String searchTerm = ""; - int limit = Integer.MAX_VALUE; - boolean multiple = true; - - if (options != null) { - searchTerm = options.optString("filter"); - if (searchTerm.length()==0) { - searchTerm = "%"; - } - else { - searchTerm = "%" + searchTerm + "%"; - } - try { - multiple = options.getBoolean("multiple"); - if (!multiple) { - limit = 1; - } - } catch (JSONException e) { - // Multiple was not specified so we assume the default is true. - } - } - else { - searchTerm = "%"; - } - - ContentResolver cr = mApp.getContentResolver(); - - Set contactIds = buildSetOfContactIds(fields, searchTerm); - HashMap populate = buildPopulationSet(fields); - - Iterator it = contactIds.iterator(); - - JSONArray contacts = new JSONArray(); - JSONObject contact; - String contactId; - int pos = 0; - while (it.hasNext() && (pos < limit)) { - contact = new JSONObject(); - try { - contactId = it.next(); - contact.put("id", contactId); - - // Do query for name and note - Cursor cur = cr.query(People.CONTENT_URI, - new String[] {People.DISPLAY_NAME, People.NOTES}, - PEOPLE_ID_EQUALS, - new String[] {contactId}, - null); - cur.moveToFirst(); - - if (isRequired("displayName",populate)) { - contact.put("displayName", cur.getString(cur.getColumnIndex(People.DISPLAY_NAME))); - } - if (isRequired("phoneNumbers",populate)) { - contact.put("phoneNumbers", phoneQuery(cr, contactId)); - } - if (isRequired("emails",populate)) { - contact.put("emails", emailQuery(cr, contactId)); - } - if (isRequired("addresses",populate)) { - contact.put("addresses", addressQuery(cr, contactId)); - } - if (isRequired("organizations",populate)) { - contact.put("organizations", organizationQuery(cr, contactId)); - } - if (isRequired("ims",populate)) { - contact.put("ims", imQuery(cr, contactId)); - } - if (isRequired("note",populate)) { - contact.put("note", cur.getString(cur.getColumnIndex(People.NOTES))); - } - // nickname - // urls - // relationship - // birthdays - // anniversary - - pos++; - cur.close(); - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - contacts.put(contact); - } - return contacts; - } - - /** - * Query the database using the search term to build up a list of contact ID's - * matching the search term - * @param fields - * @param searchTerm - * @return a set of contact ID's - */ - private Set buildSetOfContactIds(JSONArray fields, String searchTerm) { - Set contactIds = new HashSet(); - - String key; - try { - for (int i=0; i contactIds, - Uri uri, String projection, String selection, String[] selectionArgs) { - ContentResolver cr = mApp.getContentResolver(); - - Cursor cursor = cr.query( - uri, - null, - selection, - selectionArgs, - null); - - while (cursor.moveToNext()) { - contactIds.add(cursor.getString(cursor.getColumnIndex(projection))); - } - cursor.close(); - } - - /** - * Create a ContactField JSONArray - * @param cr database access object - * @param contactId the ID to search the database for - * @return a JSONArray representing a set of ContactFields - */ - private JSONArray imQuery(ContentResolver cr, String contactId) { - String imWhere = ContactMethods.PERSON_ID - + " = ? AND " + ContactMethods.KIND + " = ?"; - String[] imWhereParams = new String[]{contactId, ContactMethods.CONTENT_IM_ITEM_TYPE}; - Cursor cursor = cr.query(ContactMethods.CONTENT_URI, - null, imWhere, imWhereParams, null); - JSONArray ims = new JSONArray(); - JSONObject im; - while (cursor.moveToNext()) { - im = new JSONObject(); - try{ - im.put("id", cursor.getString( - cursor.getColumnIndex(ContactMethods._ID))); - im.put("perf", false); - im.put("value", cursor.getString( - cursor.getColumnIndex(ContactMethodsColumns.DATA))); - im.put("type", getContactType(cursor.getInt( - cursor.getColumnIndex(ContactMethodsColumns.TYPE)))); - ims.put(im); - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - } - cursor.close(); - return null; - } - - /** - * Create a ContactOrganization JSONArray - * @param cr database access object - * @param contactId the ID to search the database for - * @return a JSONArray representing a set of ContactOrganization - */ - private JSONArray organizationQuery(ContentResolver cr, String contactId) { - String orgWhere = ContactMethods.PERSON_ID + " = ?"; - String[] orgWhereParams = new String[]{contactId}; - Cursor cursor = cr.query(Organizations.CONTENT_URI, - null, orgWhere, orgWhereParams, null); - JSONArray organizations = new JSONArray(); - JSONObject organization; - while (cursor.moveToNext()) { - organization = new JSONObject(); - try{ - organization.put("id", cursor.getString(cursor.getColumnIndex(Organizations._ID))); - organization.put("name", cursor.getString(cursor.getColumnIndex(Organizations.COMPANY))); - organization.put("title", cursor.getString(cursor.getColumnIndex(Organizations.TITLE))); - // organization.put("department", cursor.getString(cursor.getColumnIndex(Organizations))); - organizations.put(organization); - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - } - return organizations; - } - - /** - * Create a ContactAddress JSONArray - * @param cr database access object - * @param contactId the ID to search the database for - * @return a JSONArray representing a set of ContactAddress - */ - private JSONArray addressQuery(ContentResolver cr, String contactId) { - String addrWhere = ContactMethods.PERSON_ID - + " = ? AND " + ContactMethods.KIND + " = ?"; - String[] addrWhereParams = new String[]{contactId, - ContactMethods.CONTENT_POSTAL_ITEM_TYPE}; - Cursor cursor = cr.query(ContactMethods.CONTENT_URI, - null, addrWhere, addrWhereParams, null); - JSONArray addresses = new JSONArray(); - JSONObject address; - while (cursor.moveToNext()) { - address = new JSONObject(); - try{ - address.put("id", cursor.getString(cursor.getColumnIndex(ContactMethods._ID))); - address.put("formatted", cursor.getString(cursor.getColumnIndex(ContactMethodsColumns.DATA))); - addresses.put(address); - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - } - return addresses; - } - - /** - * Create a ContactField JSONArray - * @param cr database access object - * @param contactId the ID to search the database for - * @return a JSONArray representing a set of ContactFields - */ - private JSONArray phoneQuery(ContentResolver cr, String contactId) { - Cursor cursor = cr.query( - Phones.CONTENT_URI, - null, - Phones.PERSON_ID +" = ?", - new String[]{contactId}, null); - JSONArray phones = new JSONArray(); - JSONObject phone; - while (cursor.moveToNext()) { - phone = new JSONObject(); - try{ - phone.put("id", cursor.getString(cursor.getColumnIndex(Phones._ID))); - phone.put("perf", false); - phone.put("value", cursor.getString(cursor.getColumnIndex(Phones.NUMBER))); - phone.put("type", getPhoneType(cursor.getInt(cursor.getColumnIndex(Phones.TYPE)))); - phones.put(phone); - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - } - return phones; - } - - /** - * Create a ContactField JSONArray - * @param cr database access object - * @param contactId the ID to search the database for - * @return a JSONArray representing a set of ContactFields - */ - private JSONArray emailQuery(ContentResolver cr, String contactId) { - Cursor cursor = cr.query( - ContactMethods.CONTENT_EMAIL_URI, - null, - ContactMethods.PERSON_ID +" = ?", - new String[]{contactId}, null); - JSONArray emails = new JSONArray(); - JSONObject email; - while (cursor.moveToNext()) { - email = new JSONObject(); - try{ - email.put("id", cursor.getString(cursor.getColumnIndex(ContactMethods._ID))); - email.put("perf", false); - email.put("value", cursor.getString(cursor.getColumnIndex(ContactMethods.DATA))); - // TODO Find out why adding an email type throws and exception - //email.put("type", cursor.getString(cursor.getColumnIndex(ContactMethods.TYPE))); - emails.put(email); - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - } - return emails; - } - - /** - * This method will save a contact object into the devices contacts database. - * - * @param contact the contact to be saved. - * @returns true if the contact is successfully saved, false otherwise. - */ - @Override - public String save(JSONObject contact) { - ContentValues personValues = new ContentValues(); - - String id = getJsonString(contact, "id"); - - String name = getJsonString(contact, "displayName"); - if (name != null) { - personValues.put(Contacts.People.NAME, name); - } - String note = getJsonString(contact, "note"); - if (note != null) { - personValues.put(Contacts.People.NOTES, note); - } - - /* STARRED 0 = Contacts, 1 = Favorites */ - personValues.put(Contacts.People.STARRED, 0); - - Uri newPersonUri; - // Add new contact - if (id == null) { - newPersonUri = Contacts.People.createPersonInMyContactsGroup(mApp.getContentResolver(), personValues); - } - // modify existing contact - else { - newPersonUri = Uri.withAppendedPath(Contacts.People.CONTENT_URI, id); - mApp.getContentResolver().update(newPersonUri, personValues, PEOPLE_ID_EQUALS, new String[]{id}); - } - - if (newPersonUri != null) { - // phoneNumbers - savePhoneNumbers(contact, newPersonUri); - // emails - saveEntries(contact, newPersonUri, "emails", Contacts.KIND_EMAIL); - // addresses - saveAddresses(contact, newPersonUri); - // organizations - saveOrganizations(contact, newPersonUri); - // ims - saveEntries(contact, newPersonUri, "ims", Contacts.KIND_IM); - - // Successfully create a Contact - return id; - } - return null; - } - - /** - * Takes a JSON contact object and loops through the available organizations. If the - * organization has an id that is not equal to null the organization will be updated in the database. - * If the id is null then we treat it as a new organization. - * - * @param contact the contact to extract the organizations from - * @param uri the base URI for this contact. - */ - private void saveOrganizations(JSONObject contact, Uri newPersonUri) { - ContentValues values = new ContentValues(); - Uri orgUri = Uri.withAppendedPath(newPersonUri, - Contacts.Organizations.CONTENT_DIRECTORY); - String id = null; - try { - JSONArray orgs = contact.getJSONArray("organizations"); - if (orgs != null && orgs.length() > 0) { - JSONObject org; - for (int i=0; i 0) { - JSONObject entry; - values.put(Contacts.ContactMethods.KIND, Contacts.KIND_POSTAL); - for (int i=0; i 0 ) { - buffer.append(", "); - } - buffer.append(getJsonString(entry, "region")); - } - if (getJsonString(entry, "postalCode") != null ) { - if (buffer.length() > 0 ) { - buffer.append(", "); - } - buffer.append(getJsonString(entry, "postalCode")); - } - if (getJsonString(entry, "country") != null ) { - if (buffer.length() > 0 ) { - buffer.append(", "); - } - buffer.append(getJsonString(entry, "country")); - } - return buffer.toString(); - } - - /** - * Takes a JSON contact object and loops through the available entries (Emails/IM's). If the - * entry has an id that is not equal to null the entry will be updated in the database. - * If the id is null then we treat it as a new entry. - * - * @param contact the contact to extract the entries from - * @param uri the base URI for this contact. - */ - private void saveEntries(JSONObject contact, Uri uri, String dataType, int contactKind) { - ContentValues values = new ContentValues(); - Uri newUri = Uri.withAppendedPath(uri, - Contacts.People.ContactMethods.CONTENT_DIRECTORY); - String id = null; - - try { - JSONArray entries = contact.getJSONArray(dataType); - if (entries != null && entries.length() > 0) { - JSONObject entry; - values.put(Contacts.ContactMethods.KIND, contactKind); - for (int i=0; i 0) { - JSONObject phone; - for (int i=0; i 0) ? true : false; - } - - @Override - public JSONObject getContactById(String id) throws JSONException { - // TODO Auto-generated method stub - return null; - } -} \ No newline at end of file diff --git a/framework/src/com/phonegap/ContactManager.java b/framework/src/com/phonegap/ContactManager.java index 60edd545..6e41f0f2 100755 --- a/framework/src/com/phonegap/ContactManager.java +++ b/framework/src/com/phonegap/ContactManager.java @@ -16,9 +16,18 @@ import android.util.Log; public class ContactManager extends Plugin { - private static ContactAccessor contactAccessor; + private ContactAccessor contactAccessor; private static final String LOG_TAG = "Contact Query"; + public static final int UNKNOWN_ERROR = 0; + public static final int INVALID_ARGUMENT_ERROR = 1; + public static final int TIMEOUT_ERROR = 2; + public static final int PENDING_OPERATION_ERROR = 3; + public static final int IO_ERROR = 4; + public static final int NOT_SUPPORTED_ERROR = 5; + public static final int PERMISSION_DENIED_ERROR = 20; + + /** * Constructor. */ @@ -34,12 +43,34 @@ public class ContactManager extends Plugin { * @return A PluginResult object with a status and message. */ public PluginResult execute(String action, JSONArray args, String callbackId) { - if (contactAccessor == null) { - contactAccessor = ContactAccessor.getInstance(webView, ctx); - } - PluginResult.Status status = PluginResult.Status.OK; - String result = ""; - + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + /** + * Check to see if we are on an Android 1.X device. If we are return an error as we + * do not support this as of PhoneGap 1.0. + */ + if (android.os.Build.VERSION.RELEASE.startsWith("1.")) { + JSONObject res = null; + try { + res = new JSONObject(); + res.put("code", NOT_SUPPORTED_ERROR); + res.put("message", "Contacts are not supported in Android 1.X devices"); + } catch (JSONException e) { + // This should never happen + Log.e(LOG_TAG, e.getMessage(), e); + } + return new PluginResult(PluginResult.Status.ERROR, res); + } + + /** + * Only create the contactAccessor after we check the Android version or the program will crash + * older phones. + */ + if (this.contactAccessor == null) { + this.contactAccessor = new ContactAccessorSdk5(this.webView, this.ctx); + } + try { if (action.equals("search")) { JSONArray res = contactAccessor.search(args.getJSONArray(0), args.optJSONObject(1)); @@ -61,7 +92,7 @@ public class ContactManager extends Plugin { } // If we get to this point an error has occurred JSONObject r = new JSONObject(); - r.put("code", 0); + r.put("code", UNKNOWN_ERROR); return new PluginResult(PluginResult.Status.ERROR, r); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e);