I'm looking to create a plugin for setting ringtones to contacts using Cordova / Android, but I haven't had any luck after messing around with the following code for a while:
package com.mypackage.contactringtone;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.CommonDataKinds.*;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.LOG;
import org.json.JSONArray;
import org.json.JSONException;
import java.lang.Object;
import android.net.Uri;
import java.io.File;
import android.content.Context;
import android.content.ContentValues;
import android.database.Cursor;
import android.provider.MediaStore;
public class ContactRingtone extends CordovaPlugin {
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if ("set_ringtone".equals(action)) {
String contact_id = args.getString(0);
String phone_number = args.getString(1);
String ringtone_path = args.getString(2);
int my_contact_id = Integer.parseInt(contact_id);
Context context = cordova.getActivity();
Uri contact_data = ContactsContract.Contacts.CONTENT_URI;
File ringtone_file = new File(ringtone_path);
String[] projection = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER
};
String selection = ContactsContract.CommonDataKinds.Phone._ID + "='" + my_contact_id + "'";
Cursor local_cursor = context.getContentResolver().query(contact_data, projection, selection, null, null);
local_cursor.move(1);
String this_id = local_cursor.getString(local_cursor.getColumnIndexOrThrow("_id"));
Uri local_uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, this_id);
ContentValues local_content_values = new ContentValues();
local_content_values.put(ContactsContract.Data.RAW_CONTACT_ID, my_contact_id);
local_content_values.put(ContactsContract.Data.CUSTOM_RINGTONE, ringtone_file.getAbsolutePath() + "/" + contact_id + ".ogg");
context.getContentResolver().update(local_uri, local_content_values, null, null);
callbackContext.success("Ringtone set for: " + my_contact_id + " - " + phone_number + " - " + ringtone_path);
return true;
}
callbackContext.error("Invalid action: " + action);
return false;
}
}
Basically, I'm using the following plugins to get contact data and ringtone path:
cordova-plugin-contacts cordova-plugin-RingtoneSelector
Once I get the contact ID and ringtone path, I send them to my custom plugin as arguments. Contact ID example is "1". Ringtone path example is "content://media/internal/audio/media/34".
I am not seeing any errors returned and the success callback returns the data from the Java function correctly, so I'm assuming I'm just not querying something correctly.
I'm fairly new to Java, so this code is becoming a mess. What needs to be updated in the above code to be able to set a ringtone to a contact properly? Thanks in advance.
Few issues I find here:
Integer.parseInt(contact_id)
is wrong.CommonDataKinds.Phone._ID
is not the contact-id, it's the id of the Data row of a phone number, so selection
is wrong.new File(ringtone_path)
make sure you have permission to access this path, especially if you're using Runtime-permissions on Marshmallow+ devices. Data.RAW_CONTACT_ID
is the ID of a RawContact
, not Contact
.Here's the part of the Contacts update code that should work:
Long myContactId = Long.parseLong(contact_id);
final String ringtoneUri = Uri.fromFile(ringtone_file).toString();
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Contacts.CONTENT_URI)
.withSelection(ContactsContract.Contacts._ID + " = '" + myContactId + "'")
.withValue(ContactsContract.Contacts.CUSTOM_RINGTONE, ringtoneUri)
.build());
try {
resolver.applyBatch(ContactsContract.AUTHORITY, ops);
}