Java tutorial
/* * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file. */ package com.microsoft.office365.starter.models; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.microsoft.outlookservices.Attendee; import com.microsoft.outlookservices.BodyType; import com.microsoft.outlookservices.EmailAddress; import com.microsoft.outlookservices.Event; import com.microsoft.outlookservices.ItemBody; import com.microsoft.outlookservices.Location; import com.microsoft.office365.starter.O365APIsStart_Application; import com.microsoft.office365.starter.helpers.Constants; import com.microsoft.office365.starter.interfaces.OnEventsAddedListener; import com.microsoft.office365.starter.interfaces.OnOperationCompleteListener; import com.microsoft.office365.starter.interfaces.OnEventsAddedListener.setEventCollection; import com.microsoft.office365.starter.interfaces.OnOperationCompleteListener.OperationResult; /** * This model class encapsulates all of the Outlook service API calendar operations that create * read, update, and delete calendar events. The O365CalendarModel class contains several methods * that post changes made to the calendar event (com.microsoft.office365.OutlookServices.Event) in * the local cache. The CalendarEvents class exposes a list and hash table to be consumed by an * arrayAdapter on the UI calendar event list The O365Calendar_Event class encapsulates * com.microsoft.office365.OutlookServices.Event and exposes event properties as simple strings that * the UI fragments consume. */ public class O365CalendarModel implements Parcelable { private CalendarEvents mCalendarEvents; private O365APIsStart_Application mApplication; private OnEventsAddedListener mEventSelectionListener; private OnOperationCompleteListener mEventOperationCompleteListener; private UUID tempNewEventId; public static final Parcelable.Creator<O365CalendarModel> CREATOR = new Parcelable.Creator<O365CalendarModel>() { public O365CalendarModel createFromParcel(Parcel in) { return new O365CalendarModel(in); } @Override public O365CalendarModel[] newArray(int size) { return new O365CalendarModel[size]; } }; public void setEventSelectionListener(OnEventsAddedListener eventSelectionListener) { this.mEventSelectionListener = eventSelectionListener; } public void setEventOperationCompleteListener(OnOperationCompleteListener eventOperationCompleteListener) { this.mEventOperationCompleteListener = eventOperationCompleteListener; } // Returns an instance of the CalendarEvents class after constructing it if necessary public CalendarEvents getCalendar() { if (mCalendarEvents == null) mCalendarEvents = new CalendarEvents(); return mCalendarEvents; } // This overload is called when a user is creating a new event. public O365CalendarModel.O365Calendar_Event createEvent(String subject) { // Create a temporary unique event Id for the new event. The // temporary Id is used by the ListView to uniquely id the new event // when it is added to the local cache before posting to the Outlook service UUID ID = java.util.UUID.randomUUID(); // Cache the temp Id in the calendar model so the model can retrieve the // Event out of the ITEMS_MAP map and update with the Id assigned by // Outlook service upon successful add tempNewEventId = ID; // The com.microsoft.office365.OutlookServices.Event is created // and cached in the event model Event newEvent = new Event(); O365CalendarModel.O365Calendar_Event newEventModel = new O365CalendarModel.O365Calendar_Event(subject, newEvent); newEventModel.setID(ID.toString()); return newEventModel; } // This overload is called when Event objects are retrieved from the OutlookServices // endpoint. public O365CalendarModel.O365Calendar_Event createEvent(String id, Event event) { // The event model caches an existing com.microsoft.office365.OutlookServices.Event return new O365CalendarModel.O365Calendar_Event(id, event); } // Posts changes made to an existing event. public void postUpdatedEvent(final Activity activity, final O365CalendarModel.O365Calendar_Event eventToUpdate) { if (eventToUpdate == null) return; Event event = eventToUpdate.getEvent(); if (event.getEnd().before(event.getStart())) { OperationResult opResult = new OperationResult("Update event", "Event was not updated. End cannot be before start.", "-1"); mEventOperationCompleteListener.onOperationComplete(opResult); return; } ListenableFuture<Event> updatedEvent = mApplication.getCalendarClient().getMe().getEvents() .getById(event.getId()).update(event); Futures.addCallback(updatedEvent, new FutureCallback<Event>() { @Override public void onSuccess(final Event result) { // Notify caller that the Event update operation is complete and succeeded OperationResult opResult = new OperationResult("Update event", "Event updated", eventToUpdate.id); eventToUpdate.thisEvent = result; sortInDateTimeOrder(mCalendarEvents.ITEMS); mEventOperationCompleteListener.onOperationComplete(opResult); } @Override public void onFailure(final Throwable t) { Log.e(t.getMessage(), "Update event"); // Notify caller that the operation failed OperationResult opResult = new OperationResult("Update event", "Event was not updated: " + getErrorMessage(t.getMessage()), "-1"); mEventOperationCompleteListener.onOperationComplete(opResult); } }); } // Posts an event deletion @SuppressWarnings("unchecked") public ListenableFuture<Event> postDeletedEvent(final Activity activity, final O365CalendarModel.O365Calendar_Event eventToDelete) { if (eventToDelete == null) { OperationResult opResult = new OperationResult("Remove event", "Select an event before clicking the Delete event button ", "-1"); mEventOperationCompleteListener.onOperationComplete(opResult); return null; } String eventId = eventToDelete.getEvent().getId(); ListenableFuture<Event> deletedEvent = mApplication.getCalendarClient().getMe().getEvents().getById(eventId) .delete(); Futures.addCallback(deletedEvent, new FutureCallback<Event>() { @Override public void onSuccess(final Event result) { // Remove event from calendar events collection. This collection is // the source of the ArrayAdapter attached to the event list in the UI mCalendarEvents.ITEMS.remove(eventToDelete); mCalendarEvents.ITEM_MAP.remove(eventToDelete.id); OperationResult opResult = new OperationResult("Remove event", "Removed event", eventToDelete.id); sortInDateTimeOrder(mCalendarEvents.ITEMS); mEventOperationCompleteListener.onOperationComplete(opResult); } @Override public void onFailure(final Throwable t) { Log.e(t.getMessage(), "Delete event"); OperationResult opResult = new OperationResult("Remove event", "Remove event failed: " + getErrorMessage(t.getMessage()), "-1"); mEventOperationCompleteListener.onOperationComplete(opResult); } }); return deletedEvent; } // Posts a new event public void postCreatedEvent(final Activity activity, final O365CalendarModel.O365Calendar_Event eventToAdd) { try { Event newEvent = eventToAdd.getEvent(); if (newEvent.getEnd().before(newEvent.getStart())) { OperationResult opResult = new OperationResult("Add event", "Event was not added. End cannot be before start.", "-1"); mEventOperationCompleteListener.onOperationComplete(opResult); return; } // This request returns the user's primary calendar. if you want to get // a different calendar in the user's calendar collection in Office 365, // ListenableFuture<Event> addedEvent = mApplication.getCalendarClient().getMe().getCalendars() .getById(Constants.CALENDER_ID).getEvents().add(newEvent); // addedEvent. Futures.addCallback(addedEvent, new FutureCallback<Event>() { @Override public void onSuccess(final Event result) { OperationResult opResult = new OperationResult("Add event", "Added event", result.getId()); eventToAdd.setEvent(result); // Update event collection.ITEM_MAP with updated Event.ID if (mCalendarEvents.ITEM_MAP.containsKey(tempNewEventId.toString())) mCalendarEvents.ITEM_MAP.remove(tempNewEventId.toString()); tempNewEventId = null; mCalendarEvents.ITEM_MAP.put(result.getId(), eventToAdd); sortInDateTimeOrder(mCalendarEvents.ITEMS); mEventOperationCompleteListener.onOperationComplete(opResult); } @Override public void onFailure(final Throwable t) { Log.e(t.getMessage(), "Create event"); OperationResult opResult = new OperationResult("Add event", "Error on add event: " + getErrorMessage(t.getMessage()), "-1"); tempNewEventId = null; mEventOperationCompleteListener.onOperationComplete(opResult); } }); } catch (NullPointerException npe) { Log.e("Null pointer on add new event in O365CalendarModel.postCreatedEvent : " + npe.getMessage(), "null pointer"); OperationResult opResult = new OperationResult("Add event", "Error on add event - null pointer", "-1"); mEventOperationCompleteListener.onOperationComplete(opResult); } } //Get a set of calendar events, starting with the event at skipToEventNumber //Size of calendar event set is set by pageSize public void getEventList(int pageSize, int skipToEventNumber) { if (mCalendarEvents == null) mCalendarEvents = new CalendarEvents(); // retrieve a page of primary calendar events asynchronously ListenableFuture<List<Event>> results = mApplication.getCalendarClient().getMe().getCalendars() .getById(Constants.CALENDER_ID).getEvents().top(pageSize).skip(skipToEventNumber).read(); Futures.addCallback(results, new FutureCallback<List<Event>>() { @Override public void onSuccess(final List<Event> result) { loadEventsIntoModel(result); sortInDateTimeOrder(mCalendarEvents.ITEMS); setEventCollection eventData = new setEventCollection(mCalendarEvents.ITEMS); mEventSelectionListener.OnEventsAdded(eventData); } @Override public void onFailure(final Throwable t) { Log.e("Failed to get events: " + getErrorMessage(t.getMessage()), "O365CalendarModel.getEventList"); setEventCollection eventData = new setEventCollection(mCalendarEvents.ITEMS); mEventSelectionListener.OnEventsAdded(eventData); } }); return; } private void sortInDateTimeOrder(List<O365Calendar_Event> events) { // Sort returned events in ascending date/time order Collections.sort(events, new Comparator<O365Calendar_Event>() { @Override public int compare(O365Calendar_Event event, O365Calendar_Event event2) { if (event.thisEvent.getStart().getTime().getTime() < event2.thisEvent.getStart().getTime() .getTime()) return -1; else if (event.thisEvent.getStart().getTime().getTime() > event2.thisEvent.getStart().getTime() .getTime()) return 1; else return 0; } }); } // Takes the string returned from Outlook service in the // onFailure event, parses for the JSON object, and gets // the actual error message private String getErrorMessage(String result) { String errorMessage = ""; try { // Gets the JSON object out of the result string String responsejSON = result.substring(result.indexOf("{"), result.length()); JSONObject jObject = new JSONObject(responsejSON); JSONObject error = (JSONObject) jObject.get("error"); errorMessage = error.getString("message"); } catch (JSONException e) { e.printStackTrace(); errorMessage = e.getMessage(); } return errorMessage; } private void loadEventsIntoModel(List<Event> events) { try { this.getCalendar().ITEMS.clear(); this.getCalendar().ITEM_MAP.clear(); for (Event e : events) { O365Calendar_Event calendarEvent = this.createEvent(e.getId(), e); ItemBody itemBody = e.getBody(); if (itemBody != null) calendarEvent.setItemBody(e.getBody()); Location location = e.getLocation(); if (location != null) calendarEvent.setLocation(e.getLocation()); java.util.Calendar startDate = e.getStart(); java.util.Calendar endDate = e.getEnd(); calendarEvent.setStartDate(startDate.get(Calendar.YEAR), startDate.get(Calendar.MONTH), startDate.get(Calendar.DAY_OF_MONTH), startDate.get(Calendar.HOUR_OF_DAY), startDate.get(Calendar.MINUTE)); calendarEvent.setEndDate(endDate.get(Calendar.YEAR), endDate.get(Calendar.MONTH), endDate.get(Calendar.DAY_OF_MONTH), endDate.get(Calendar.HOUR_OF_DAY), endDate.get(Calendar.MINUTE)); calendarEvent.setSubject(e.getSubject()); addItem(calendarEvent); } } catch (Exception ex) { String exceptionMessage = ex.getMessage(); Log.e("RetrieveEventsTask", exceptionMessage); } } private void addItem(O365Calendar_Event item) { this.getCalendar().ITEMS.add(item); this.getCalendar().ITEM_MAP.put(item.id, item); } public void setActivity(Activity activity) { mApplication = (O365APIsStart_Application) activity.getApplication(); } public O365CalendarModel(Parcel in) { } public O365CalendarModel(Activity activity) { mApplication = (O365APIsStart_Application) activity.getApplication(); } /** * Helper class for providing content for user interfaces created by Android template wizards. */ public class CalendarEvents { public List<O365Calendar_Event> ITEMS = new ArrayList<O365Calendar_Event>(); public Map<String, O365Calendar_Event> ITEM_MAP = new HashMap<String, O365Calendar_Event>(); } /** * A single calendar event. The class exposes event properties as simple strings */ public class O365Calendar_Event { private String id; private String subject = " "; private String attendees = ""; private String locationString = ""; private ItemBody itemBody; private String itemBodyString = ""; private Location location; private Event thisEvent; // Sets the subject property of an event and // sets the event item body (content) with the // same subject string public void setSubject(String Subject) { subject = Subject; if (this.itemBody != null) { this.itemBody.setContent(Subject); this.itemBody.setContentType(BodyType.Text); thisEvent.setSubject(Subject); } } public void setEvent(Event event) { thisEvent = event; this.id = event.getId(); } // Updates the subject of the event public void updateSubject(String Subject) { subject = Subject; if (thisEvent != null) thisEvent.setSubject(Subject); } // Returns the subject of the event public String getSubject() { String returnValue = ""; if (thisEvent != null) { returnValue = subject; } else { returnValue = thisEvent.getSubject(); } return returnValue; } public String getID() { return this.id; } // Returns a comma delimited list of attendee // email addresses public String getAttendees() { try { // Get any previously invited attendees if (thisEvent.getAttendees() != null) { attendees = ""; List<Attendee> attendeeList = thisEvent.getAttendees(); for (Attendee a : attendeeList) { String charSeparator = ""; String attendeeName = a.getEmailAddress().getAddress(); if (attendeeList.size() > 1) charSeparator = "; "; String newName = attendeeName; attendees += newName + charSeparator; } // Trim off trailing space and the comma that trails the invitee list attendees = attendees.trim(); if (attendees.endsWith(";")) { attendees = attendees.substring(0, attendees.length() - 1); } } } catch (Exception ex) { Log.e("Exception on get attendees: " + ex.getMessage(), ""); } return attendees; } public void setID(String newId) { id = newId; } // Add an attendee to the event attendee collection if it is not already there private void checkAddAttendeeToEvent(String attendeeToAdd, List<Attendee> attendeeList) { attendeeToAdd = attendeeToAdd.trim(); Boolean inList = false; // Iterate on attendee list for (Attendee a : attendeeList) { if (a.getEmailAddress().getAddress().equals(attendeeToAdd)) { inList = true; break; } } if (inList == false) { makeAnAttendee(attendeeToAdd); } } // Add new attendees to the existing list of event attendees public void setAttendees(String anAttendee) { if (thisEvent.getAttendees() != null) { thisEvent.getAttendees().clear(); } Pattern pattern; Matcher matcher; String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; pattern = Pattern.compile(EMAIL_PATTERN); String[] attendeeArray = anAttendee.split(";"); for (String attendeeString : attendeeArray) { // Add attendee if attendeeString is an email address matcher = pattern.matcher(attendeeString); if (matcher.matches()) makeAnAttendee(attendeeString.trim()); } } private void makeAnAttendee(String anAttendee) { // Works for new attendee added to event this.attendees = anAttendee; Attendee attendee1 = new Attendee(); EmailAddress email = new EmailAddress(); email.setAddress(anAttendee); attendee1.setEmailAddress(email); // Get the current list of event attendees and add the new attendee // to the list List<Attendee> listAttendees = thisEvent.getAttendees(); if (listAttendees == null) listAttendees = new ArrayList<Attendee>(); listAttendees.add(attendee1); thisEvent.setAttendees(listAttendees); } // Sets the location of an event public void setLocation(Location location) { locationString = location.getDisplayName(); this.location = location; } // Sets the location in the OutlookServices event object public void setLocation(String Location) { locationString = Location; if (this.location != null) { this.location.setDisplayName(Location); thisEvent.setLocation(this.location); } else { Location newLocation = new Location(); newLocation.setDisplayName(Location); thisEvent.setLocation(newLocation); } } public void setItemBody(ItemBody body) { this.itemBody = body; this.itemBodyString = body.getContent(); } public String getItemBody() { return itemBodyString; } public String getLocation() { return locationString; } public void setStartDate(int yearValue, int monthValue, int dayValue, int hourValue, int minuteValue) { Calendar startDate = thisEvent.getStart(); if (startDate == null) { startDate = new GregorianCalendar(yearValue, monthValue, dayValue); } startDate.setTimeZone(TimeZone.getDefault()); startDate.set(yearValue, monthValue, dayValue, hourValue, minuteValue); thisEvent.setStart(startDate); } public void setEndDate(int yearValue, int monthValue, int dayValue, int hourValue, int minuteValue) { Calendar endDate = thisEvent.getEnd(); if (endDate == null) { endDate = new GregorianCalendar(yearValue, monthValue, dayValue); } endDate.setTimeZone(TimeZone.getDefault()); endDate.set(yearValue, monthValue, dayValue, hourValue, minuteValue); thisEvent.setEnd(endDate); } public Calendar getStartDateTime() { return thisEvent.getStart(); } public Calendar getEndDateTime() { return thisEvent.getEnd(); } public Event getEvent() { return thisEvent; } public O365Calendar_Event(String id, Event event) { this.id = id; thisEvent = event; } public O365Calendar_Event(String id) { this.id = id; thisEvent = new Event(); thisEvent.setId(this.id); } // the toString override is called by the two-pane list box to show // calendar event details in the list. @Override public String toString() { String amPm = "AM "; if (thisEvent.getStart().get(Calendar.AM_PM) == 1) amPm = "PM "; int startHour = thisEvent.getStart().get(Calendar.HOUR); String hourString = Integer.toString(startHour); int startMinute = thisEvent.getStart().get(Calendar.MINUTE); String minuteString = Integer.toString(startMinute); if (startMinute < 10) minuteString = "0" + minuteString; return (thisEvent.getStart().get(Calendar.MONTH) + 1) + "/" + thisEvent.getStart().get(Calendar.DAY_OF_MONTH) + "/" + thisEvent.getStart().get(Calendar.YEAR) + " " + hourString + ":" + minuteString + " " + amPm + subject; } } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel arg0, int arg1) { } } // ********************************************************* // // O365-Android-Start, https://github.com/OfficeDev/O365-Android-Start // // Copyright (c) Microsoft Corporation // All rights reserved. // // MIT License: // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // *********************************************************