I have a custom content provider for sharing files. For columns (via query method) i support only OpenableColumns.DISPLAY_NAME and OpenableColumns.SIZE.
While testing i noticed that Photos app (Google+) have problems (NullPointerException) opening images through my content provider, because they are retrieving _data column, which is not supported in my content provider, and for it i return null (for column names and values).
Later i updated this part of code, so now it returns in columns array _data column, but it's value as null.
It seems that this works now ok in Photos app, but i'm curious... Does anybody know if this is in general specified/standardized, what we should return in case of unsupported column?
Part of confusion is: should we on side of content provider handle such scenarios as i did, or should apps that uses content providers do additional checking? Since some apps can check if column exists, then do something with value (but value will be null). On other side, other apps can expect column, and assume if column exists, then value should also exists.
I know that the best approach is to cover all situations, but initial question remains: is such handling specified somewhere in the documentation?
Here is also section of code for better imagination:
String cols[] = new String[projection.length];
Object vals[] = new Object[projection.length];
for(int i=0; i<projection.length; i++){
//projection is passed to query() by 3rd party app
String column = projection[i];
if ( OpenableColumns.DISPLAY_NAME.equals(column) ) {
cols[i] = OpenableColumns.DISPLAY_NAME;
vals[i] = name;
}else if ( OpenableColumns.SIZE.equals(column) ) {
cols[i] = OpenableColumns.SIZE;
vals[i] = file.length();
}else{
//THIS PART OF CODE WAS LATER ADDED
//for unsupported column return column name
//but it's value will be null
cols[i] = column;
vals[i] = null;
}
}
MatrixCursor mc = new MatrixCursor(cols, 1);
mc.addRow(vals);
Related
For example, let's say that a local contact have multiple Japanese contacts with the name たなか, つなき, and てるてる. In the local contact these names will be sorted under the section index letter た.
For what I've read, it is possible to sort them via collate, but how do you sort them to index た or even know that they should go to the た index?
Thank you.
The API has a trick for returning that list of alphabet letters, and optionally also including counts per character.
See official docs for EXTRA_ADDRESS_BOOK_INDEX:
https://developer.android.com/reference/android/provider/ContactsContract.Contacts.html#EXTRA_ADDRESS_BOOK_INDEX
Example code:
Uri uri = Contacts.CONTENT_URI.buildUpon()
.appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true")
.build();
Cursor cursor = getContentResolver().query(uri,
new String[] {Contacts.DISPLAY_NAME},
null, null, null);
Bundle bundle = cursor.getExtras();
if (bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES) && bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS)) {
String sections[] = bundle.getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
// sections will now contain an array of characters that represent the first letter of each available contact name
// optionally - if you need counts per letter - int counts[] = bundle.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
}
Good day all, I have a list of Objects (Let's call them ContactObject for simplicity). This object contains 2 Strings, Name and Email.
This list of objects will number somewhere around 2000 in size. The goal here is to filter that list as the user types letters and display it on the screen (IE in a recyclerview) if they match. Ideally, It would filter where the objects with a not-null name would be above an object with a null name.
As of right now, the steps I am taking are:
1) Create 2 lists to start and get the String the user is typing to compare to
List<ContactObject> nameContactList = new ArrayList<>();
List<ContactObject> emailContactList = new ArrayList<>();
String compareTo; //Passed in as an argument
2) Loop though the master list of ContactObjects via an enhanced for loop
3) Get the name and email Strings
String name = contactObject.getName();
String email = contactObject.getEmail();
4) If the name matches, add it to the list. Intentionally skip this loop if the name is not null and it gets added to the list to prevent doubling.
if(name != null){
if(name.toLowerCase().contains(compareTo)){
nameContactList.add(contactObject);
continue;
}
}
if(email != null){
if(email.toLowerCase().contains(compareTo)){
emailContactList.add(contactObject);
}
}
5) Outside of the for loop now as the object lists are build, use a comparator to sort the ones with names (I do not care about sorting the ones with emails at the moment)
Collections.sort(nameContactList, new Comparator<ContactObject>() {
public int compare(ContactObject v1, ContactObject v2) {
String fName1, fName2;
try {
fName1 = v1.getName();
fName2 = v2.getName();
return fName1.compareTo(fName2);
} catch (Exception e) {
return -1;
}
}
});
6) Loop through the built lists (one sorted) and then add them to the master list that will be used to set into the adapter for the recyclerview:
for(ContactObject contactObject: nameContactList){
masterList.add(contactObject);
}
for(ContactObject contactObject: emailContactList){
masterList.add(contactObject);
}
7) And then we are all done.
Herein lies the problem, this code works just fine, but it is quite slow. When I am filtering through the list of 2000 in size, it can take 1-3 seconds each time the user types a letter.
My goal here is to emulate apps that allow you to search the contact list of the phone, but seem to always to it quicker than I am able to replicate.
Does anyone have any recommendations as to how I can speed this process up at all?
Is there some hidden Android secret I don't know of that only allows you to query a small section of the contacts in quicker succession?
How to check if table is empty using parse , I'm having a problem with the code below :
private String[] getMaxDateMessage() throws ParseException {
final String[] msgData = new String[3];
ParseObject ob = null;
String[] userIds = {currentUserId, recipientId};
ParseQuery<ParseObject> query = ParseQuery.getQuery("ParseMessage");
query.whereContainedIn("senderId", Arrays.asList(userIds));
query.whereContainedIn("recipientId", Arrays.asList(userIds));
query.orderByDescending("createdAt");
if(query.hasCachedResult())
{
ob = query.getFirst();
if (ob.isDataAvailable()) {
//for (int i = 0; i < 1; i++) {
//createdDate[0] = messageList.get(i).get("createdAt").toString();
msgData[0] = ob.getCreatedAt().toString();
msgData[1] = ob.get("senderId").toString();
msgData[2] = ob.get("recipientId").toString();
// }
}
}
The thing is that the table is empty , so the query should return null , but no exception is been throwed , it just crashes the app .
So how can I check if the table is empty before trying to fetch any data ?
Update : The solution that I have found is to use query.count().
If the count returns a value that is not 0 then the table is not empty .
Using query.count() to determine if the table is empty is not an optimal solution. While this is perfectly fine when actually run against an empty table, using query.count() will almost always result in a sub-optimal query when there's more than one object in the table. The reason for this is quite clear: you only care about the first object matched by this query, yet a query.count() will scan the whole table in order to return the total of objects that match your query.
Therefore, the ideal solution is to simply use query.getFirst() and check if you get any results. You should be able to handle the case where ob is not a ParseObject, e.g. the collection is either empty or no objects match your query.
I'm trying to:
Display a list of contacts
Let the user search through them by typing a query
Limit search results only to a specific Google/Gmail account.
This is how I build the URI for the cursor:
// User is searching for 'jo'
String query = "jo";
Uri uri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(query));
// Restrict the query to contacts from 'example#gmail.com'
Uri.Builder builder = uri.buildUpon();
builder.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(ContactsContract.Directory.DEFAULT));
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, "example#gmail.com");
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google");
uri = builder.build();
This is the final URI:
content://com.android.contacts/contacts/filter/jo?directory=0&account_name=example%40gmail.com&account_type=com.google
Currently, this shows search results from all accounts on the phone.
NOTE: If I use Contacts.CONTENT_URI instead of Contacts.CONTENT_FILTER_URI, then specifying the directory/account works as expected, but I can no longer use 'type-to-filter' style search.
The documentation does state:
The most important use case for Directories is search. A Directory
provider is expected to support at least Contacts.CONTENT_FILTER_URI.
Could anyone help point out what I might be doing wrong?
I added your code in Google's example for contact retrieving, and with a couple of changes it worked perfectly with my Google for Work account.
The changes I made were:
remove the line with DIRECTORY_PARAM_KEY, as I didn't find it to make any difference
removed ContactsQuery.SELECTION from the return statement, because that constant prevents "invisible" contacts from being displayed.
The changes were made to ContactsListFragment.java
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// If this is the loader for finding contacts in the Contacts Provider
// (the only one supported)
if (id == ContactsQuery.QUERY_ID) {
Uri contentUri;
// There are two types of searches, one which displays all contacts and
// one which filters contacts by a search query. If mSearchTerm is set
// then a search query has been entered and the latter should be used.
if (mSearchTerm == null) {
// Since there's no search string, use the content URI that searches the entire
// Contacts table
contentUri = ContactsQuery.CONTENT_URI;
} else {
// Since there's a search string, use the special content Uri that searches the
// Contacts table. The URI consists of a base Uri and the search string.
contentUri = Uri.withAppendedPath(ContactsQuery.FILTER_URI, Uri.encode(mSearchTerm));
}
// HERE COMES YOUR CODE (except the DIRECTORY_PARAM_KEY line)
Uri.Builder builder = contentUri.buildUpon();
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, "example#mycompany.com");
builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google");
contentUri = builder.build();
// Returns a new CursorLoader for querying the Contacts table. No arguments are used
// for the selection clause. The search string is either encoded onto the content URI,
// or no contacts search string is used. The other search criteria are constants. See
// the ContactsQuery interface.
return new CursorLoader(getActivity(),
contentUri,
ContactsQuery.PROJECTION,
null, // I REMOVED SELECTION HERE
null,
ContactsQuery.SORT_ORDER);
}
Log.e(TAG, "onCreateLoader - incorrect ID provided (" + id + ")");
return null;
}
So I'm still building a Database to support a project of mine. There are two different things to be saved: first, attribute values of some player objects and second, simple values stored in a java class.
ATM my problem lies in the process of loading values of a player object and writing it in the respective class.
Now let's see some code:
Following you see the method I want to use for saving the values in the database.
That works fine atm, but I just realized I'm still passing the contentValues object an extra value for the 'ID' , which I did set - and planned to keep that way - as autoincrement.
Any Idea how to work this in accordingly?
public void savePlayer(Player player[]) {
for (int i = 0; i <= 3; i++) {
playerValues.put("ID", i);
playerValues.put("Name", player[i].getName());
playerValues.put("HP", player[i].getHp());
playerValues.put("Satisfaction", player[i].getsatisfaction());
playerValues.put("Hygiene", player[i].isHygieneInt());
playerValues.put("IsAlive", player[i].isAliveInt());
}
db.insert("playertable", null, playerValues);
}
Okay, hold on to your hats because this might look a bit like spaghetti - the load-method:
public void loadPlayer() {
String[] namecolumn = { "Name" };
String[] intcolumn = { "ID, HP, Satisfaction, Hygiene, IsAlive" };
String[] namesToString = new String[4];
for (int j = 0; j <= 3; j++) {
Cursor playerCursorName = db.query("playertable", namecolumn, "ID="
+ j, null, null, null, null);
namesToString = cursorToString(playerCursorName);
Resource.playerArray[j].setName(namesToString[j]);
}
for (int i = 0; i < 3; i++) {
int[] restToInt;
Cursor playerCursorInt = db.query("playertable", intcolumn, "ID="
+ i, null, null, null, null);
restToInt = cursorToInt(playerCursorInt, 4);
Resource.playerArray[i].setHp(restToInt[i]);
Resource.playerArray[i].setsatisfaction(restToInt[i]);
Resource.playerArray[i].setHygieneInt(restToInt[i]);
Resource.playerArray[i].setAliveInt(restToInt[i]);
}
}
Yeah, I know this looks pretty ugly but let me explain it:
Because there are 4 player objects I planned on iterating through the database entries by using the ID as identifier to get exactly one row at a time and writing the name and the other values of this object in the java class where I want to manage them within my project.
Note: same problem with autoincrement here than in the save method
In addition, I get a CursorIndexOutOfBoundsException when calling loadPlayer because
Index -1 is being requested - isn't that the result of an operation on the database resulting in an error?
Yeah that's pretty much it, I'll provide you with additional code if requested, hope someone can help me
You are using Cursors in a slightly odd way here.
The point of a Cursor is to ask SQLite to do the hard work of fetching data for you, and your job is simply to use the cursor to iterate through the returned values.
Firstly, I would change the query here to ask for all values in the table (and perhaps put some condition to constrain what you get back), to make sure your cursor then contains all your values.
Then, I would loop through the cursor's values by using a while loop, (with cursor.moveToPosition(-1) before the loop) moving along the cursor by using cursor.moveToNext().
See the API for more information:
http://developer.android.com/reference/android/database/Cursor.html
With regard to the autoincrement problem, as far as I can remember you can leave out the ID and use db.insert() without that field and the database will provide an ID for you.
You shouldn't have the same issue in your load method because it doesn't make sense to autoincrement when loading, you just get back what's in the database.