Is it possible to query CalendarContract.Instances with a custom where clause?
The normal syntax for using this URI is:
Uri.Builder builder = CalendarContract.Instances.CONTENT_URI.buildUpon();
ContentUris.appendId (builder, startDate);
ContentUris.appendId (builder, endDate);
But I want to manipulate the start date comparison by providing my own where clause and omitting the start and stop times from the URI. For sake of a simple example, like this ("projection" defined elsewhere):
final Uri uri = CalendarContract.Instances.CONTENT_URI;
String selection = Instances.BEGIN + " >= " + startTime + " and " +
Instances.END + " <= " + endTime;
Cursor cursor = context.getContentResolver().query (
uri,
projection,
selection,
null,
null);
This is resulting in a DatabaseUtils exception:
03-16 09:57:06.041 W/System.err: java.lang.IllegalArgumentException: Unknown URL content://com.android.calendar/instances/when
03-16 09:57:06.041 W/System.err: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:167)
My real code is more complicated. I need the custom where clause so that I can adjust the start time of all day events, which are stored in UTC time rather than local time. Without an adjustment, the comparison of all day events fails in the where clause and in sorting.
Is there a different URI that I can use with Instances to do what I want?
Related
I'm a newbie with Android Studio so please be patient... This forum often leads me with suggestions and examples (as a reader), but today I decided to ask for help:
Since hours, I try to build an SQLite statement in Android Studio: There is a column COLUMN_LAST_ATTEMPT with date and time as String, e.g. 2020-01-09 17:23, see screenshot, and I want to get the newest date (without time) from the table, e.g. 2020-09-01. I tried various options but I can't get it to run.
What I need is an Android SQLite Statement for
SELECT MAX(SUBSTR(last_attempt,11,20)) FROM quiz_questions
(which runs on DBBrowser), where 'last attempt' is a column of table 'quiz_questions', screenshot of that column in table 'quiz_questions'
I tried the following rawQueries, none of them works:
In QuizDBHelper-Class
//...
final QuizDbHelper dbHelper = QuizDbHelper.getInstance(this);
//...
public String newestQuiz(){
db = getReadableDatabase();
String result = null;
Cursor cursor = db.rawQuery("SELECT MAX(" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + ") FROM "
+ QuizContract.QuestionsTable.TABLE_NAME, null);
//Cursor cursor = db.rawQuery("SELECT MAX(SUBSTR(" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT +
// ",11,20)) FROM " + QuizContract.QuestionsTable.TABLE_NAME, null);
//Cursor cursor = db.rawQuery("SELECT " + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + " FROM " +
// QuizContract.QuestionsTable.TABLE_NAME, null);
if(cursor.moveToFirst()){
do {
result = cursor.getString(c.getColumnIndex(QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT));
} while (cursor.moveToNext());
}
cursor.close();
return result;
}
In Statistics-Class
String LastUse = dbHelper.newestQuiz();
LastUsage.setText("Letzte Challenge: " + LastUse);
//LastUsage is a TextView in activity_Statistics.xml
//attached with LastUsage = findViewById(R.id.text_lastUsage);
Either the SQLite statements are totally wrong or I make (basic?) mistakes in statistics class. I need ...newbie help!
I need something like Select column from table where substring of date-Entry == newest
Your issue appear to be column names. That is a Cursor only contains the columns extracted, not all the columns from the table. Although you are basing your query on the column as per QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT that will not be the column name in the cursor.
Rather it will will MAX(SUBSTR(" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT +
// ",11,20))
The simplest way of managing this is to give the column in the Cursor a specific name using AS. As such perhaps use :-
Cursor cursor = db.rawQuery("SELECT MAX(" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + ") AS " + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + " FROM "
+ QuizContract.QuestionsTable.TABLE_NAME, null);
However, you may prefere to use a column name (AS ????) specififc to the situation e.g.
........ AS max_" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + ........
You would then have to use :-
result = cursor.getString(c.getColumnIndex("max_" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT));
Alternately, as it's just a single value/column that is returned in the cursor you could use the column offset of 0, in which case the column name is irrelevant as long as it is valid. However, using offsets is not typically recommended due to the lack of validation of the column being accessed.
re the comment :-
I just need the date part
As the date is a recognised DateTime format (and also that such formats are directly sortable/orderable), use max(date(column_name)) or even max(column_name).
Events for the holidays and birthdays calendars apparently cannot be modified. In general, how do I tell if a calendar's events are read-only? I'm guessing I can look at the CALENDAR_ACCESS_LEVEL column. Anyone know for sure, and what value(s) to check for?
Yes, you need to check the value of CALENDAR_ACCESS_LEVEL.
You should get all the calendar with the following access level:
CAL_ACCESS_OWNER
Full access to the calendar
CAL_ACCESS_EDITOR
Full access to modify the calendar, but not the
access control settings
CAL_ACCESS_CONTRIBUTOR
Full access to modify the calendar, but not the
access control settings
CAL_ACCESS_ROOT
Domain admin
This is an example that returns all the writable calendars:
String selection = "(" + CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + " = ? OR "
+ CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + " = ? OR "
+ CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + " = ? OR "
+ CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + " = ?)";
String[] selectionArgs = new String[]{
Integer.toString(CalendarContract.Calendars.CAL_ACCESS_OWNER),
Integer.toString(CalendarContract.Calendars.CAL_ACCESS_EDITOR),
Integer.toString(CalendarContract.Calendars.CAL_ACCESS_CONTRIBUTOR),
Integer.toString(CalendarContract.Calendars.CAL_ACCESS_ROOT)};
Cursor cursor = getContentResolver().query(CalendarContract.Calendars.CONTENT_URI, null, selection, selectionArgs, null);
I'll consider the following to be the correct answer unless someone corrects me.
Via
Cursor calCursor = context.getContentResolver().query(
Calendars.CONTENT_URI,
projection, // whatever cols you want
(Calendars.VISIBLE + " = 1 and " +
Calendars.CALENDAR_ACCESS_LEVEL + " >= " + Calendars.CAL_ACCESS_CONTRIBUTOR),
null,
Calendars._ID + " ASC");
I'm getting a list that does not include the holidays or birthdays calendars. So, I'm assuming that any calender in the list I get is writeable and, thus, my app can easily make holidays and birthdays read only. So far that assumption has been working for me.
How can I query CalendcarContract.Instances with a LIMIT clause?
I would like to query starting with a particular start date for a LIMIT of "n" rows.
What I've tried is:
final Uri uri = Uri.parse(CalendarContract.Instances.CONTENT_URI + "/" +
Long.toString(startDate) + "/" +
Long.MAX_VALUE);
final String sortOrder = Instances.BEGIN;
String selection = " limit " + rows;
Cursor cursor = context.getContentResolver().query (
uri,
projection,
selection,
null,
sortOrder);
This generates an error, reported in the log file:
...while compiling: SELECT Instances._id...WHERE (begin<=? AND end>=? AND (limit 1)...
I believe the error is the "AND" before (limit 1). The service is adding that, not me. So, is there another URI I can use or another technique?
NB: I specifically want the Instances versions, which joins single events with recurring events.
Thanks.
Ok, never mind unless you have a better answer.
I realized this is a more general URI problem, not specifically related to CalendarContract. In searching for other results, I found one suggestion to append LIMIT n to the sort clause, e.g.
final String sortOrder = Instances.BEGIN + " limit " + 10;
Credit to
How to add limit clause using content provider
I'm querying the content provider for appointments.
Uri.Builder builder = CalendarContract.Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);
Uri uri = builder.build();
String[] event_projection= new String[] {
Instances.TITLE,
Instances.ALL_DAY,
Instances.CALENDAR_COLOR,
Instances.EVENT_ID,
Instances.BEGIN,
Instances.END,
};
String selection = CalendarContract.Instances.VISIBLE + "='1'";
String sortBy = CalendarContract.Instances.BEGIN + " ASC, " +
CalendarContract.Instances.TITLE + " ASC";
Cursor mCursor = cr.query(uri, event_projection, selection, null, sortBy);
Some users send the following crash report:
java.lang.IllegalArgumentException: allDay is true but sec, min, hour are not 0.
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:167)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:384)
at android.content.ContentResolver.query(ContentResolver.java:372)
at android.content.ContentResolver.query(ContentResolver.java:315)
This happens mostly on Sony Xperia devices, but also happened on Google Nexus10.
How is it possible to get this exception when querying the data?
Shouldn't this have been avoided when inserting the data in the first place?
Does it mean that I have to surround the query with try/catch?
Doesn't this slow down the query?
Yes, it does sound like the data is inserted badly. If it shortcuts the query processing the exception could speed up the query actually, but wont return the data you are looking for.
The exception appears to be from Time.compare http://developer.android.com/reference/android/text/format/Time.html#compare(android.text.format.Time,%20android.text.format.Time)
This would probably result from the sortBy argument of the query. Try it with null and see if it stops using the comparator. If that works then maybe when ALL_DAY is true, it is incompatible with sorting by BEGIN on your data set.
You could either sort in a collection after reading the rows from the cursor and updating the values in memory to make the comparator happy or use your own comparator, or you could fix the data (set sec, min, hour to 0 where ALL_DAY is true) before running your query.
Maybe a good practice would be to repair the data upon exception and retry.
If allDay is set to 1 eventTimezone must be TIMEZONE_UTC and the time must correspond to a midnight boundary, means hour , minute , second should be zero of calendar object.
refer following link..
http://developer.android.com/reference/android/provider/CalendarContract.Events.html
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.set(2014, 03, 18, 0, 0,0);
When I update the CalendarContract.Events DTEND column, why doesn't the change show up in the CalendarContract.Instances END column?
My app allows the user to view and change calendar events using the CalendarContract.Events APIs. The code performs an update to the Events table and then reads it back (later) using the Instances table. Changes to TITLE, for example, work fine (that is, I update Events and can read back the change in Instances). Changes to Events.DTEND do show up in Instances.DTEND, but how can I get that update to also show up in Instances.END?
This is important since, evidently, the Android calendar app (and my app, too) uses Instances.BEGIN and Instances.END to determine what to display in the calendar.
Here is my update code:
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put (Events.CALENDAR_ID, calendarId);
values.put (Events.TITLE, title);
values.put (Events.DTEND, eventEnd.getTimeInMillis());
String where = "_id =" + eventId +
" and " + CALENDAR_ID + "=" + calendarId;
int count = cr.update (Events.CONTENT_URI, values, where, null);
if (count != 1)
throw new IllegalStateException ("more than one row updated");
Thanks.
The solution turns out to be adding the start date:
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put (Events.CALENDAR_ID, calendarId);
values.put (Events.TITLE, title);
values.put (Events.DTSTART, eventStart.getTimeInMillis());
values.put (Events.DTEND, eventEnd.getTimeInMillis());
String where = "_id =" + eventId +
" and " + CALENDAR_ID + "=" + calendarId;
int count = cr.update (Events.CONTENT_URI, values, where, null);
if (count != 1)
throw new IllegalStateException ("more than one row updated");
A word of caution: this case only shows how to update nonrecurring events. Nonrecurring events have a null RRULE.
I suspect what the provider code is doing is using only the values you provide instead of refetching the start date itself (obviously if the user changes the start date you would have to provide it anyway). This makes sense from the perspective of reducing db accesses. Too bad Google didn't document any of this.