Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TIMOB-17450] Android: Ti.Calendar.Event should expose the Attendees (Parity with iOS) #9325

Merged
merged 6 commits into from Aug 21, 2017

Conversation

ypbnv
Copy link
Contributor

@ypbnv ypbnv commented Aug 16, 2017

JIRA: https://jira.appcelerator.org/browse/TIMOB-17450

Description:
Add AttendeesProxy and add it as a result for getAttendees() method.

Test case:

var win = Ti.UI.createWindow({backgroundColor:'gray'});
var pickerCalendar = Ti.UI.createPicker({
    top: 50,
});
var pickerEvents = Ti.UI.createPicker({
    top: 100
});
var buttonGetAttendeesForEvent = Ti.UI.createButton({
    top: 150,
    title: 'Get Attendees'
});
var events = [];
var selectedEventIndex;
buttonGetAttendeesForEvent.addEventListener('click', function () {
    Ti.API.info('----------------------');
    Ti.API.info(events[selectedEventIndex].getAttendees());
    Ti.API.info('----------------------');
    events[selectedEventIndex].getAttendees().forEach(function(attendee){
        Ti.API.info('----------------------');
        Ti.API.info(JSON.stringify(attendee));
        Ti.API.info('----------------------');
    });
});

var listEvents = function (calendarSource) {
    pickerEvents = Ti.UI.createPicker({
        top: 100
    });
    var i=0;
    var eventsRows = [];
    events = [];
    Ti.Calendar.getCalendarById(calendarSource).getEventsInYear(2017).forEach(function(event){
        //create row for each calendar
        events[i] = event;
        eventsRows[i++]=Ti.UI.createPickerRow({title:event.getTitle()});
    });
    if (eventsRows.length > 0) {
        pickerEvents.add(eventsRows);
        pickerEvents.addEventListener('change', function(e){
            selectedEventIndex = e.rowIndex;
        })
        win.add(pickerEvents);
        selectedEventIndex = 0;
    }
}

var getCalendars = function() {
    var calendarRows = [];
    var i=0;
    Ti.Calendar.getAllCalendars().forEach(function(calendar){
        //create row for each calendar
        calendarRows[i++]=Ti.UI.createPickerRow({title:calendar.getName(), calendar: calendar});
    });
    if (calendarRows.length >0 ) {
        pickerCalendar.add(calendarRows);
            pickerCalendar.addEventListener('change', function(e){
                win.remove(pickerEvents);
                listEvents(e.rowIndex+1);
            })
            win.add(pickerCalendar);
            listEvents(1);
               win.add(buttonGetAttendeesForEvent);
    }
}

win.addEventListener('open', function(){
    if (Ti.Calendar.hasCalendarPermissions()) {
        getCalendars();
    } else {
        Ti.Calendar.requestCalendarPermissions(function(){
            getCalendars();
        })
    }
});

win.open();

Note: Device should have a valid calendar with an event with at least one attendee.
Also these permissions need to be added in tiapp.xml:

<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission android:name="android.permission.READ_CALENDAR"/>

@ypbnv ypbnv added this to the 7.0.0 milestone Aug 16, 2017
@Kroll.constant public static final int ATTENDEE_TYPE_NONE = CalendarContract.Attendees.TYPE_NONE;
@Kroll.constant public static final int ATTENDEE_TYPE_OPTIONAL = CalendarContract.Attendees.TYPE_OPTIONAL;
@Kroll.constant public static final int ATTENDEE_TYPE_RESOURCE = CalendarContract.Attendees.TYPE_RESOURCE;
@Kroll.constant public static final int ATTENDEE_TYPE_REQUIERED = CalendarContract.Attendees.TYPE_REQUIRED;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo...
Change "ATTENDEE_TYPE_REQUIERED" to "ATTENDEE_TYPE_REQUIRED".

//add the proxy to the result array
result[index++] = proxyForRow;
}
return result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return result; line is indented 1 tab too far.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we need to return an empty array if we've failed to find any attendees or if an error occurs. This is to match iOS' behavior as documented here...
http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Calendar.Event-property-attendees

final String query = "(" + CalendarContract.Attendees.EVENT_ID + " = ?)";
final String[] args = new String[]{id};
ContentResolver contentResolver = TiApplication.getInstance().getContentResolver();
final Cursor cursor = contentResolver.query(CalendarContract.Attendees.CONTENT_URI, attendeeProjection, query, args, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ContentResolver.query() method can return null. We need to do a null check on cursor before using it below.

cursor.getString(cursor.getColumnIndex(CalendarContract.Attendees.ATTENDEE_NAME)),
cursor.getInt(cursor.getColumnIndex(CalendarContract.Attendees.ATTENDEE_TYPE)),
cursor.getInt(cursor.getColumnIndex(CalendarContract.Attendees.ATTENDEE_STATUS)),
cursor.getInt(cursor.getColumnIndex(CalendarContract.Attendees.ATTENDEE_RELATIONSHIP))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cursor.getColumnIndex() call can return -1 which would trigger an out-of-bounds exception when passed through to the cursor getter methods. Also, the cursor getter methods can throw an exception if the column type does not match the type requested (ie: it stores a string when we're requesting an int).

To be safe, we should make sure the column index is valid before using it. And wrap each cursor getter in separate try/catch blocks. We can default fields not found to constants such as NONE.

Also, should we define our own "UNKNOWN" constant? The idea is to future-proof this code for when Google adds new constants that our internal code doesn't support yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked up our docs. We do define UNKNOWN types in iOS. We need to do the same on Android for types we don't recognize as well. Perhaps that should be determined in the AttendeeProxy? I'll leave that up to you.
http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Calendar

@ypbnv
Copy link
Contributor Author

ypbnv commented Aug 17, 2017

@jquick-axway Updated.
Can we test the guarding for the columns' index and value type somehow, because I was not able on a few devices here?
Currently I have added the UNKNOWN type, status and relationship with the same value, which should do, but we can change it in case we need it to differ.

@jquick-axway
Copy link
Contributor

Can we test the guarding for the columns' index and value type somehow?

@ypbnv, you would have to use a 3rd party contacts app instead of Google's, because they don't have to follow the rules. This is something we have to worry about if a Titanium app is distributed via a different Android app store such as Amazon. For example, an Amazon device will have an Amazon made contacts app instead of the one made by Google.
(From looking at your code though, it looks like it'll handle it fine.)

Copy link
Contributor

@jquick-axway jquick-axway left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CR: Pass

@lokeshchdhry
Copy link
Contributor

FR Passed.

The attendees info for the event can be successfully fetched like "relationship, type, status & email.

Studio Ver: 4.9.1.201707200100
SDK Ver: 7.0.0 local build
OS Ver: 10.12.3
Xcode Ver: Xcode 8.3.3
Appc NPM: 4.2.9
Appc CLI: 6.2.3
Ti CLI Ver: 5.0.14
Alloy Ver: 1.9.13
Node Ver: 6.10.1
Java Ver: 1.8.0_101
Devices: ⇨ google Nexus 5 --- Android 6.0.1
⇨ google Pixel --- Android 7.1.1
⇨ MotoG2 --- Android 4.4.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants