diff --git a/js/ui/cordova/plugins/cordova-plugin-contacts/src/android/ContactManager.java b/js/ui/cordova/plugins/cordova-plugin-contacts/src/android/ContactManager.java new file mode 100644 index 0000000..448302d --- /dev/null +++ b/js/ui/cordova/plugins/cordova-plugin-contacts/src/android/ContactManager.java @@ -0,0 +1,306 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 org.apache.cordova.contacts; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaActivity; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.os.Build; +import android.os.Bundle; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.RawContacts; +import android.util.Log; + +import java.lang.reflect.Method; + +public class ContactManager extends CordovaPlugin { + + private ContactAccessor contactAccessor; + private CallbackContext callbackContext; // The callback context from which we were invoked. + private JSONArray executeArgs; + + 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 OPERATION_CANCELLED_ERROR = 6; + public static final int PERMISSION_DENIED_ERROR = 20; + private static final int CONTACT_PICKER_RESULT = 1000; + public static String [] permissions; + + + //Request code for the permissions picker (Pick is async and uses intents) + public static final int SEARCH_REQ_CODE = 0; + public static final int SAVE_REQ_CODE = 1; + public static final int REMOVE_REQ_CODE = 2; + public static final int PICK_REQ_CODE = 3; + + public static final String READ = Manifest.permission.READ_CONTACTS; + public static final String WRITE = Manifest.permission.WRITE_CONTACTS; + + + /** + * Constructor. + */ + public ContactManager() { + + } + + + protected void getReadPermission(int requestCode) + { + PermissionHelper.requestPermission(this, requestCode, READ); + } + + + protected void getWritePermission(int requestCode) + { + PermissionHelper.requestPermission(this, requestCode, WRITE); + } + + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArray of arguments for the plugin. + * @param callbackContext The callback context used when calling back into JavaScript. + * @return True if the action was valid, false otherwise. + */ + public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { + + this.callbackContext = callbackContext; + this.executeArgs = args; + + /** + * 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 Cordova 1.0. + */ + if (android.os.Build.VERSION.RELEASE.startsWith("1.")) { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, ContactManager.NOT_SUPPORTED_ERROR)); + return true; + } + + /** + * 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.cordova); + } + + if (action.equals("search")) { + if(PermissionHelper.hasPermission(this, READ)) { + search(executeArgs); + } + else + { + getReadPermission(SEARCH_REQ_CODE); + } + } + else if (action.equals("save")) { + if(PermissionHelper.hasPermission(this, WRITE)) + { + save(executeArgs); + } + else + { + getWritePermission(SAVE_REQ_CODE); + } + } + else if (action.equals("remove")) { + if(PermissionHelper.hasPermission(this, WRITE)) + { + remove(executeArgs); + } + else + { + getWritePermission(REMOVE_REQ_CODE); + } + } + else if (action.equals("pickContact")) { + if(PermissionHelper.hasPermission(this, READ)) + { + pickContactAsync(); + } + else + { + getReadPermission(PICK_REQ_CODE); + } + } + else { + return false; + } + return true; + } + + private void remove(JSONArray args) throws JSONException { + final String contactId = args.getString(0); + this.cordova.getThreadPool().execute(new Runnable() { + public void run() { + if (contactAccessor.remove(contactId)) { + callbackContext.success(); + } else { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR)); + } + } + }); + } + + private void save(JSONArray args) throws JSONException { + final JSONObject contact = args.getJSONObject(0); + this.cordova.getThreadPool().execute(new Runnable(){ + public void run() { + JSONObject res = null; + String id = contactAccessor.save(contact); + if (id != null) { + try { + res = contactAccessor.getContactById(id); + } catch (JSONException e) { + Log.e(LOG_TAG, "JSON fail.", e); + } + } + if (res != null) { + callbackContext.success(res); + } else { + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR)); + } + } + }); + } + + private void search(JSONArray args) throws JSONException + { + final JSONArray filter = args.getJSONArray(0); + final JSONObject options = args.get(1) == null ? null : args.getJSONObject(1); + this.cordova.getThreadPool().execute(new Runnable() { + public void run() { + JSONArray res = contactAccessor.search(filter, options); + callbackContext.success(res); + } + }); + } + + + /** + * Launches the Contact Picker to select a single contact. + */ + private void pickContactAsync() { + final CordovaPlugin plugin = (CordovaPlugin) this; + Runnable worker = new Runnable() { + public void run() { + Intent contactPickerIntent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); + plugin.cordova.startActivityForResult(plugin, contactPickerIntent, CONTACT_PICKER_RESULT); + } + }; + this.cordova.getThreadPool().execute(worker); + } + + /** + * Called when user picks contact. + * @param requestCode The request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + * @throws JSONException + */ + public void onActivityResult(int requestCode, int resultCode, final Intent intent) { + if (requestCode == CONTACT_PICKER_RESULT) { + if (resultCode == Activity.RESULT_OK) { + String contactId = intent.getData().getLastPathSegment(); + // to populate contact data we require Raw Contact ID + // so we do look up for contact raw id first + Cursor c = this.cordova.getActivity().getContentResolver().query(RawContacts.CONTENT_URI, + new String[] {RawContacts._ID}, RawContacts.CONTACT_ID + " = " + contactId, null, null); + if (!c.moveToFirst()) { + this.callbackContext.error("Error occured while retrieving contact raw id"); + return; + } + String id = c.getString(c.getColumnIndex(RawContacts._ID)); + c.close(); + + try { + JSONObject contact = contactAccessor.getContactById(id); + this.callbackContext.success(contact); + return; + } catch (JSONException e) { + Log.e(LOG_TAG, "JSON fail.", e); + } + } else if (resultCode == Activity.RESULT_CANCELED) { + callbackContext.error(OPERATION_CANCELLED_ERROR); + return; + } + + this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR)); + } + } + + public void onRequestPermissionResult(int requestCode, String[] permissions, + int[] grantResults) throws JSONException + { + for(int r:grantResults) + { + if(r == PackageManager.PERMISSION_DENIED) + { + this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR)); + return; + } + } + switch(requestCode) + { + case SEARCH_REQ_CODE: + search(executeArgs); + break; + case SAVE_REQ_CODE: + save(executeArgs); + break; + case REMOVE_REQ_CODE: + remove(executeArgs); + break; + case PICK_REQ_CODE: + pickContactAsync(); + break; + } + } + + /** + * This plugin launches an external Activity when a contact is picked, so we + * need to implement the save/restore API in case the Activity gets killed + * by the OS while it's in the background. We don't actually save anything + * because picking a contact doesn't take in any arguments. + */ + public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) { + this.callbackContext = callbackContext; + this.contactAccessor = new ContactAccessorSdk5(this.cordova); + } +}