简体   繁体   English

cordova-plugin-contacts - 在Android M上的联系人选择应用程序崩溃

[英]cordova-plugin-contacts - On contact pick app crash on android M

I an using cordova-plugin-contacts to pick a contact from contacts. 我使用cordova-plugin-contacts从联系人中选择一个联系人。 App is working fine on Android 5(Lollipop) and prior versions. 应用程序在Android 5(Lollipop)和之前版本上运行良好。 But on Android 6(Marshmallow) app crashes when I pick a contact. 但是当我选择一个联系人时,Android 6(Marshmallow)应用程序崩溃了。

Here is my javascript: 这是我的javascript:

navigator.contacts.pickContact(function(contact){
            $scope.contact.name=contact.displayName;
            if(contact.phoneNumbers) {
                msgToastService.toastMsgAlert('Number picked: ' + contact.phoneNumbers,'Contact',"SC");
            } else {
                msgToastService.toastMsgAlert('Choose Valid Mobile Number!','Contact',"SC");
            }
            $scope.$apply();
        },function(err){
        });

ContactManager.java ContactManager.java

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 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;

public static int instanceCounter = 0;
/**
 * Constructor.
 */
public ContactManager() {
    instanceCounter++;
    Log.e(this.getClass().getName(), "Instance created: " + instanceCounter);
}


protected void getReadPermission(int requestCode)
{
    cordova.requestPermission(this, requestCode, READ);
}


protected void getWritePermission(int requestCode)
{
    cordova.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(cordova.hasPermission(READ)) {
            search(executeArgs);
        }
        else
        {
            getReadPermission(SEARCH_REQ_CODE);
        }
    }
    else if (action.equals("save")) {
        if(cordova.hasPermission(WRITE))
        {
            save(executeArgs);
        }
        else
        {
            getWritePermission(SAVE_REQ_CODE);
        }
    }
    else if (action.equals("remove")) {
        if(cordova.hasPermission(WRITE))
        {
            remove(executeArgs);
        }
        else
        {
            getWritePermission(REMOVE_REQ_CODE);
        }
    }
    else if (action.equals("pickContact")) {
        if(cordova.hasPermission(READ)) {
            pickContactAsync();
            PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
            r.setKeepCallback(true);
            this.callbackContext.sendPluginResult(r);
        }
        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;
    plugin.cordova.setActivityResultCallback(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.getActivity().runOnUiThread(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);//Exception in this line
                this.callbackContext.success(contact);
                return;
            } catch (JSONException e) {
                Log.e(LOG_TAG, "JSON fail.", e);
            }
        } else if (resultCode == Activity.RESULT_CANCELED){
            this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.NO_RESULT, UNKNOWN_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;
    }
}
}

Here is an exception shown in android logcat: 这是android logcat中显示的异常:

 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.paymepaisa.in, PID: 28620
java.lang.RuntimeException: Unable to resume activity {com.paymepaisa.in/com.paymepaisa.in.MainActivity}: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1000, result=-1, data=Intent { dat=content://com.android.contacts/contacts/lookup/1483r1353-2951434F593D2941394339.3789r1354-2951434F593D2941394339/1504191 flg=0x1 }} to activity {com.paymepaisa.in/com.paymepaisa.in.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'org.json.JSONObject org.apache.cordova.contacts.ContactAccessor.getContactById(java.lang.String)' on a null object reference
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3103)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3134)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2481)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
    Caused by: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1000, result=-1, data=Intent { dat=content://com.android.contacts/contacts/lookup/1483r1353-2951434F593D2941394339.3789r1354-2951434F593D2941394339/1504191 flg=0x1 }} to activity {com.paymepaisa.in/com.paymepaisa.in.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'org.json.JSONObject org.apache.cordova.contacts.ContactAccessor.getContactById(java.lang.String)' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:3699)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3089)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3134) 
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2481) 
    at android.app.ActivityThread.-wrap11(ActivityThread.java) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5417) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'org.json.JSONObject org.apache.cordova.contacts.ContactAccessor.getContactById(java.lang.String)' on a null object reference
    at org.apache.cordova.contacts.ContactManager.onActivityResult(ContactManager.java:246)
    at org.apache.cordova.CordovaInterfaceImpl.onActivityResult(CordovaInterfaceImpl.java:126)
    at org.apache.cordova.CordovaActivity.onActivityResult(CordovaActivity.java:348)
    at android.app.Activity.dispatchActivityResult(Activity.java:6428)
    at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3089) 
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3134) 
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2481) 
    at android.app.ActivityThread.-wrap11(ActivityThread.java) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5417) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

As the exception says, there is a NullPointerException in ContactManager.java:246. 正如异常所说,ContactManager.java:246中存在NullPointerException。 So I have checked the plugin code and found that the object of ContactManager is re-created and onActivityResult is called on that. 所以我检查了插件代码,发现重新创建了ContactManager的对象,并在其上调用了onActivityResult。 There for all member variables of ContactManager becomes null. 因为ContactManager的所有成员变量都为空。

I can't find out why is this happening. 我不明白为什么会这样。 Please look at the cordova-plugin-contacts git repository for plugin code. 请查看cordova-plugin-contacts git存储库以获取插件代码。

My Cordova version: 5.3.1 我的Cordova版本:5.3.1

Android SDK: Marshmallow(6) API Level-23 Android SDK:Marshmallow(6)API Level-23

Please let me know if you want any more information. 如果您想了解更多信息,请与我们联系。 Thanks in advance. 提前致谢。

Thats exactly because of the changes in the marshmallow for runtime permission. 这正是因为棉花糖的运行时许可的变化。 @Murtaza Khursheed Hussain is right @Murtaza Khursheed Hussain是对的

Here is how to fix: Before accessing any module of your mobile you have to make sure user allowed that permission. 以下是修复方法:在访问移动设备的任何模块之前,您必须确保用户允许该权限。 In your case it's contacts, this also applies to other cases like location, file storage, camera, sensors etc. 在你的情况下它是联系人,这也适用于其他情况,如位置,文件存储,相机,传感器等。

You can ask for runtime permissions using cordova.plugins.diagnostic plugin 您可以使用cordova.plugins.diagnostic插件来请求运行时权限

Install Plugin (cordova.plugins.diagnostic): $ cordova plugin add cordova.plugins.diagnostic 安装插件(cordova.plugins.diagnostic): $ cordova plugin add cordova.plugins.diagnostic

Try wrapping your code as follows: 尝试按如下方式包装代码:

  cordova.plugins.diagnostic.getPermissionAuthorizationStatus(function(status){
    //Check for contact permission status
    if(cordova.plugins.diagnostic.runtimePermissionStatus.GRANTED !== status){
      //request for permission at runtime
      cordova.plugins.diagnostic.requestRuntimePermission(function(statusAfterRequest){
        if(cordova.plugins.diagnostic.runtimePermissionStatus.GRANTED === statusAfterRequest){
          //Your code here..
          //navigator.contacts.pickContact(....
        }
      }, function(error){
        console.error("error while requesting permission: "+error);
      },);
    }

  }, function(error){
      console.error("The following error occurred: "+error);
  }, cordova.plugins.diagnostic.runtimePermission.READ_CONTACTS);

Here is the plugin page for more information. 这是插件页面以获取更多信息。 cordova.plugins.diagnostic cordova.plugins.diagnostic

Hope it helps! 希望能帮助到你! :) Have a great day :) 祝你有美好的一天

Try this code 试试这个代码

      cordova.plugins.diagnostic.requestRuntimePermission(function(status){
    switch(status){
        case cordova.plugins.diagnostic.permissionStatus.GRANTED:
            console.log("Permission granted to use the camera");
            $scope.callChargeDetailLoad();
            break;
        case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED:
            console.log("Permission to use the camera has not been requested yet");
            break;
        case cordova.plugins.diagnostic.permissionStatus.DENIED:
            console.log("Permission denied to use the camera - ask again?");
            break;
        case cordova.plugins.diagnostic.permissionStatus.DENIED_ALWAYS:
            console.log("Permission permanently denied to use the camera - guess we won't be using it then!");
            break;
    }
}, function(error){
    console.error("The following error occurred: "+error);
}, cordova.plugins.diagnostic.permission.READ_CONTACTS);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM