Someone know if there's a programmatically way to use a specific defined APN on the device which is not the default one?
Thanks.
You can programmatically query and set the preferred APN using the uri content://telephony/carriers/preferapn. To set a new preferred APN you have to pass in the database ID of an existing APN entry. The following function can do this if you pass in the display name of the APN (eg: setPreferredApn(context, "Giffgaff");)
public static final Uri APN_TABLE_URI = Uri.parse("content://telephony/carriers");
public static final Uri APN_PREFER_URI = Uri.parse("content://telephony/carriers/preferapn");
public static boolean setPreferredApn(Context context, String name) {
boolean changed = false;
String columns[] = new String[] { Carriers._ID, Carriers.NAME };
String where = "name = ?";
String wargs[] = new String[] {name};
String sortOrder = null;
Cursor cur = context.getContentResolver().query(APN_TABLE_URI, columns, where, wargs, sortOrder);
if (cur != null) {
if (cur.moveToFirst()) {
ContentValues values = new ContentValues(1);
values.put("apn_id", cur.getLong(0));
if (context.getContentResolver().update(APN_PREFER_URI, values, null, null) == 1)
changed = true;
}
cur.close();
}
return changed;
}
I guess I should add that you need WRITE_APN_SETTINGS permission and need to import android.provider.Telephony and android.provider.Telephony.Carriers
UPDATE FOR 4.0+
This facility became disabled with the release of Android 4.0 (ICS). Enabling the WRITE_APN_SETTINGS permission has no effect on allowing you to set the APN any more. See this question for some relevant links. On the API page it now states explicitly this permission is not for external use and this is enforced internally.
I dont think there is a way, even if there is one, the carrier could wipe it out with a software update. Also, for some carriers like AT&T in US, using a specific APN provides specific functionality, like getting the Subscriber number of that user (its a unique ID, not the phone number). So it may not be a good idea to force this change, as it will impact numerous other apps installed on handset.
Related
I would like to know if anyone can point to a good source that explain how can you can change phone carrier related settings such APN, E911 CMAS using the Android SDK. I noticed the Telephony Manager class as CarrierConfigManager, but not sure which will give read/write access or which is the best or if there any better ways to do it.
You can change APN by using content provider provided by Telephony.Carriers.
Following is the code I used to create a new APN.
public void saveApn(Apn newApn) {
String name = checkNotSet(newApn.getName());
String apn = checkNotSet(newApn.getApn());
String mcc = checkNotSet(newApn.getMcc());
String mnc = checkNotSet(newApn.getMnc());
ContentValues values = new ContentValues();
values.put(Telephony.Carriers.NAME, name);
values.put(Telephony.Carriers.APN, apn);
values.put(Telephony.Carriers.MCC, mcc);
values.put(Telephony.Carriers.MNC, mnc);
values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
mContext.getContentResolver().insert(Telephony.Carriers.CONTENT_URI, values)
}
And to set as prefered APN
private void setAsPreferedApn(int apnId) {
ContentValues values = new ContentValues();
values.put("apn_id", String.valueOf(apnId));
getContentResolver().update(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn"), values, null, null);
}
In my android application I want to send MMS from background service without user interaction, I tried to use SmsManager.sendMultimediaMessage but this method requires API-21 atleast and I am working on API-19 (Android KitKat). So please tell how can I send MMS from background service without user interaction in Android API level 19 (KitKat)?
Use this page.
since i shouldn't answer with just a link. I copied some content from there.
Settings sendSettings = new Settings();
sendSettings.setMmsc(mmsc);
sendSettings.setProxy(proxy);
sendSettings.setPort(port);
you can get them something like (found at Set APN programmatically on Android - answear by vincent091):
Cursor cursor = null;
if (Utils.hasICS()){
cursor =SqliteWrapper.query(activity, activity.getContentResolver(),
Uri.withAppendedPath(Carriers.CONTENT_URI, "current"), APN_PROJECTION, null, null, null);
} else {
cursor = activity.getContentResolver().query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"),
null, null, null, null);
}
cursor.moveToLast();
String type = cursor.getString(cursor.getColumnIndex(Telephony.Carriers.TYPE));
String mmsc = cursor.getString(cursor.getColumnIndex(Telephony.Carriers.MMSC));
String proxy = cursor.getString(cursor.getColumnIndex(Telephony.Carriers.MMSPROXY));
String port = cursor.getString(cursor.getColumnIndex(Telephony.Carriers.MMSPORT));
Writing this on the fly, so I apologize for the code sample. This is NOT real code, it's something I wrote in a plain text editor on the fly. No compile checking, couldn't remember all the exact class and method names, etc. It's just a written concept of what I'm trying to do, I'm looking for feedback on the broader concepts.
I'm working on retrieving a list of contacts from the content provider. I want to be able to filter the results based on the contact's account name. the user will be presented with all available accounts, and will select which ones are to be used, and then that will be used in the retrieval method.
The thing is, the account name is in RawContacts, and the rest of the info I want (display name, lookupID) is in Contacts. I know that ContactsContract.Contacts.Entity is the shortcut to access all of this, so this code sample is what I'm planning to do.
Again, this is written on the fly with no IDE or looking up methods or anything. I'm sure my syntax is bad in many places, but this shows the concept I'm trying to do.
private static final URI URI = ContactsContract.Contacts.URI;
private static final String[] FIRST_PROJECTION = new String[]{
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.LOOKUP_KEY
};
private String[] acceptedAccountNames = {Accepted Account Names Will Go Here (dynamic)};
private static final String[] SECOND_PROJECTION = new String[]{
ContactsContract.Contacts.Entity.ACCOUNT_NAME //This is whatever the entity -> RawContacts field name would be
};
public List<Contact> loadContacts(Context context){
List<Contact> contacts = new ArrayList<>();
ContentProvider provider = context.getContentProvider();
Cursor contactsCursor = provider.query(URI, FIRST_PROJECTION, null, null);
contactsCursor.movetoFirst();
while(!contactsCursor.isAtLast()){
String name = contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
long lookupKey = contactsCursor.getLong(contactsCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
Uri idUri = Uri.makeWithId(URI, lookupKey);
Uri entityUri = Uri.makeWithTableName(idUri, "entity");
Cursor contactEntityCursor = provider.query(entityUri, SECOND_PROJECTION, null, null);
contactEntityCursor.moveToFirst();
String accountName = contactEntityCursor.getString(contactEntityCursor.getColumnIndex(ContactsContract.Contacts.Entity.ACCOUNT_NAME));
if(Arrays.asList(acceptedAccountNames).contains(accountName)){
Contact contact = new Contact(lookupKey, name);
contacts.add(contact);
}
contactsCursor.moveToNext();
}
return contacts;
}
As you can see, I create a cursor while looping over another cursor. I'm essentially creating a new cursor for each contact in the list.
My question is twofold:
1) What would be the performance implications of this? With a large enough list, would this severely hurt app performance?
2) Is there a better way to do this? As in, a way to do this in a single query, getting all the data I'm looking for in the cursor.
Thanks so much in advance.
I'm attempting to update a calendar's event on my phone from my code, but context.getContentResolver().update keeps returning 0, and of course there are no changes made to the event when I look at it in the Calendar app.
I'm getting the event ID, start time, etc with context.getContentResolver().query, and I'm getting unique numbers like 431, 4, 233, etc, so I'm presuming the event IDs I'm using are real.
I understand the official way to do this is to go through Google's servers instead of using update(), but for my implementation it doesn't make sense to do it that way (or even in general, but I digress).
Am I doing something wrong, or am I trying to do something that Android simply isn't going to allow?
Uri updateEventUri = ContentUris.withAppendedId(Uri.parse("content://com.android.calendar/events"), id);
ContentValues cv = new ContentValues();
begin.set(Calendar.HOUR_OF_DAY, arg0.getCurrentHour()); //begin is a java.util.Calendar object
begin.set(Calendar.MINUTE, arg0.getCurrentMinute());
//cv.put("_id", id);
//cv.put("title", "yeahyeahyeah!");
cv.put("dtstart", begin.getTimeInMillis());
int updatedrowcount = context.getContentResolver().update(updateEventUri, cv, null, null);
System.out.println("updated "+updatedrowcount+" rows with id "+id);
A related question was posted here with no replies https://stackoverflow.com/questions/5636350/update-android-calendar-event
Let me know if I can clarify anything; I would really appreciate any input you guys and dolls could provide!
i had tried a lot and finally ended up with solution (Unreliable though).. but works fine..
public static boolean updateCalendar(Context context,String cal_Id,String eventId)
{
try{
Uri CALENDAR_URI = Uri.parse(CAL_URI+"events");
Cursor c = context.getContentResolver().query(CALENDAR_URI, null, null, null, null);
String[] s = c.getColumnNames();
if (c.moveToFirst())
{
while (c.moveToNext())
{
String _id = c.getString(c.getColumnIndex("_id"));
String CalId = c.getString(c.getColumnIndex("calendar_id"));
if ((_id==null) && (CalId == null))
{
return false;
}
else
{
if (_id.equals(eventId) && CalId.equals(cal_Id))
{
Uri uri = ContentUris.withAppendedId(CALENDAR_URI, Integer.parseInt(_id));
context.getContentResolver().update(uri, null, null, null);// need to give your data here
return true;
}
}
}
}
}
finally
{
return true;
}
}
and finally i'm not sure if it works with every device.
Ok, so, the problem was that I was using different URIs between fetching the events and editing them. I used the code sample from here and was using the URI "content://com.android.calendar/instances/when" to fetch the events and display them on the screen. When I had made a change I was using "content://com.android.calendar/events" to edit by id as in my example above.
What I found, thanks to your response, ntc, was that the ids for events between the two URIs were different, and therefore I couldn't edit the events consistently with the information each was giving me. I was presuming the event ids I was getting were system ids and universal to the phone.
I guess I'll have to do some testing and see what hardware isn't compatible with this method. I am using an HTC Evo for testing and so far so good.
When querying the Instances table, use Instances.EVENT_ID to get the identifier for the event you want to edit, instead of Instances._ID.
Is it possible to get the calendar's entries from the phone offline? It seem the only way is to use gdata-java-client.
Josef and Isaac's solutions for accessing the calendar only work in Android 2.1 and earlier. Google have changed the base content URI in 2.2 from "content://calendar" to "content://com.android.calendar". This change means the best approach is to attempt to obtain a cursor using the old base URI, and if the returned cursor is null, then try the new base URI.
Please note that I got this approach from the open source test code that Shane Conder and Lauren Darcey provide with their Working With The Android Calendar article.
private final static String BASE_CALENDAR_URI_PRE_2_2 = "content://calendar";
private final static String BASE_CALENDAR_URI_2_2 = "content://com.android.calendar";
/*
* Determines if we need to use a pre 2.2 calendar Uri, or a 2.2 calendar Uri, and returns the base Uri
*/
private String getCalendarUriBase() {
Uri calendars = Uri.parse(BASE_CALENDAR_URI_PRE_2_2 + "/calendars");
try {
Cursor managedCursor = managedQuery(calendars, null, null, null, null);
if (managedCursor != null) {
return BASE_CALENDAR_URI_PRE_2_2;
}
else {
calendars = Uri.parse(BASE_CALENDAR_URI_2_2 + "/calendars");
managedCursor = managedQuery(calendars, null, null, null, null);
if (managedCursor != null) {
return BASE_CALENDAR_URI_2_2;
}
}
} catch (Exception e) { /* eat any exceptions */ }
return null; // No working calendar URI found
}
These answers are good, but they all involve hard-coding the Calendar URI (which I've seen in three different incarnations across different Android devices).
A better way to get that URI (which hard-codes the name of a class and a field instead) would be something like this:
Class<?> calendarProviderClass = Class.forName("android.provider.Calendar");
Field uriField = calendarProviderClass.getField("CONTENT_URI");
Uri calendarUri = (Uri) uriField.get(null);
This isn't perfect (it will break if they ever remove the android.provider.Calendar class or the CONTENT_URI field) but it works on more platforms than any single URI hard-code.
Note that these reflection methods will throw exceptions which will need to be caught or re-thrown by the calling method.
Currently, this is not possible without using private APIs (see Josef's post.) There is a Calendar provider, but it is not public yet. It could change anytime and break your app.
Though, it probably will not change (I don't think they will change it from "calendar"), so you might be able to use it. But my recommendation is to use a separate class like this:
public class CalendarProvider {
public static final Uri CONTENT_URI = Uri.parse("content://calendar");
public static final String TITLE = "title";
public static final String ....
And use those instead of the strings directly. This will let you change it very easily if/when the API changes or it is made public.
You can use the calendar content provider (com.android.providers.calendar.CalendarProvider). Example:
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse("content://calendar/events"), null, null, null, null);
while(cursor.moveToNext()) {
String eventTitle = cursor.getString(cursor.getColumnIndex("title"));
Date eventStart = new Date(cursor.getLong(cursor.getColumnIndex("dtstart")));
// etc.
}
edit: you might want to put this in a wrapper (see Isaac's post) as it's currently a private API.
You can use the CalendarContract from here: https://github.com/dschuermann/android-calendar-compatibility
It is the same API class as available on Android 4, but made to work with Android >= 2.2.
About the API that can change... The whole ContentProvider approach won't change that quickly so can already overcome a lot of problems by only updating the strings. Therefor create constants you reuse over the whole project.
public static final String URI_CONTENT_CALENDAR_EVENTS = "content://calendar/events";
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse(URI_CONTENT_CALENDAR_EVENTS), null, null, null, null);
//etc
If you want a proper private API you'll have to create a pojo and some services like this:
public class CalendarEvent {
private long id;
private long date;
//etc...
}
public interface CalendarService {
public Set<CalendarEvent> getAllCalendarEvents();
public CalendarEvent findCalendarEventById(long id);
public CalendarEvent findCalendarEventByDate(long date);
}
and so on. This way you'll only have to update the CalendarEvent object and this service in case the API changes.
Nick's solution involves managedQuery, which is not defined in the Context class. Many times when you are running things in the background you would want to use a context object. Here's a modified version:
public String getCalendarUriBase() {
return (android.os.Build.VERSION.SDK_INT>=8)?
"content://com.android.calendar":
"content://calendar";
}
The catch for null should not be carried out here since there might be more exceptions even if the managedQuery succeeded earlier.