From 5de54008e58ff63d388e4d448b50a47950990e22 Mon Sep 17 00:00:00 2001 From: Marc Blank Date: Mon, 25 Jan 2010 12:38:32 -0800 Subject: [PATCH] Handle Exchange meeting invitation responses * Includes some refactoring of internal "request" code in SyncManager * Adds Message flags to tag meeting invites and cancellations * Adds meetingResponse method in EmailService * Hooks into Controller and MessageView UI included Change-Id: I4c5e10bccc4b41956b94d9dfa55925e5af030939 --- src/com/android/email/Controller.java | 25 +++++- .../android/email/activity/MessageView.java | 12 ++- .../android/email/provider/EmailContent.java | 8 +- .../email/service/EmailServiceProxy.java | 12 +++ .../android/exchange/AbstractSyncService.java | 51 +++---------- .../exchange/EasAuthenticationException.java | 31 ++++++++ src/com/android/exchange/EasSyncService.java | 76 +++++++++++++++---- .../exchange/EmailServiceConstants.java | 23 ++++++ src/com/android/exchange/IEmailService.aidl | 2 + .../exchange/MeetingResponseRequest.java | 29 +++++++ src/com/android/exchange/PartRequest.java | 23 +++--- src/com/android/exchange/Request.java | 27 +++++++ src/com/android/exchange/SyncManager.java | 47 +++--------- .../exchange/adapter/EmailSyncAdapter.java | 8 ++ .../adapter/MeetingResponseParser.java | 65 ++++++++++++++++ src/com/android/exchange/adapter/Tags.java | 14 ++++ 16 files changed, 346 insertions(+), 107 deletions(-) create mode 100644 src/com/android/exchange/EasAuthenticationException.java create mode 100644 src/com/android/exchange/EmailServiceConstants.java create mode 100644 src/com/android/exchange/MeetingResponseRequest.java create mode 100644 src/com/android/exchange/Request.java create mode 100644 src/com/android/exchange/adapter/MeetingResponseParser.java diff --git a/src/com/android/email/Controller.java b/src/com/android/email/Controller.java index cd55a0d88..1180912bd 100644 --- a/src/com/android/email/Controller.java +++ b/src/com/android/email/Controller.java @@ -551,7 +551,7 @@ public class Controller { /** * Delete a single attachment entry from the DB given its id. - * Does not delete any eventual associated files. + * Does not delete any eventual associated files. */ public void deleteAttachment(long attachmentId) { ContentResolver resolver = mProviderContext.getContentResolver(); @@ -684,6 +684,29 @@ public class Controller { } } + /** + * Respond to a meeting invitation. + * + * @param messageId the id of the invitation being responded to + * @param response the code representing the response to the invitation + * @callback the Controller callback by which results will be reported (currently not defined) + */ + public void sendMeetingResponse(final long messageId, final int response, + final Result callback) { + // Split here for target type (Service or MessagingController) + IEmailService service = getServiceForMessage(messageId); + if (service != null) { + // Service implementation + try { + service.sendMeetingResponse(messageId, response); + } catch (RemoteException e) { + // TODO Change exception handling to be consistent with however this method + // is implemented for other protocols + Log.e("onDownloadAttachment", "RemoteException", e); + } + } + } + /** * Request that an attachment be loaded. It will be stored at a location controlled * by the AttachmentProvider. diff --git a/src/com/android/email/activity/MessageView.java b/src/com/android/email/activity/MessageView.java index 336e2863f..9a4e25bf7 100644 --- a/src/com/android/email/activity/MessageView.java +++ b/src/com/android/email/activity/MessageView.java @@ -31,6 +31,7 @@ import com.android.email.provider.EmailContent.Attachment; import com.android.email.provider.EmailContent.Body; import com.android.email.provider.EmailContent.BodyColumns; import com.android.email.provider.EmailContent.Message; +import com.android.exchange.EmailServiceConstants; import org.apache.commons.io.IOUtils; @@ -64,7 +65,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.Window; import android.view.View.OnClickListener; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -676,6 +676,16 @@ public class MessageView extends Activity implements OnClickListener { return null; } + // NOTE + // This is a placeholder for code used to accept a meeting invitation, and would presumably + // be called in response to a button press or menu selection + // The appropriate EmailServiceConstant would be changed to implement "decline" and + // "tentative" responses + private void onAccept() { + mController.sendMeetingResponse(mMessageId, EmailServiceConstants.MEETING_REQUEST_ACCEPTED, + mControllerCallback); + } + private void onDownloadAttachment(AttachmentInfo attachment) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { /* diff --git a/src/com/android/email/provider/EmailContent.java b/src/com/android/email/provider/EmailContent.java index 67f56365d..62ff388da 100644 --- a/src/com/android/email/provider/EmailContent.java +++ b/src/com/android/email/provider/EmailContent.java @@ -542,12 +542,18 @@ public abstract class EmailContent { public static final int FLAG_LOADED_DELETED = 3; // Bits used in mFlags - // These three states are mutually exclusive, and indicate whether the message is an + // The following three states are mutually exclusive, and indicate whether the message is an // original, a reply, or a forward public static final int FLAG_TYPE_ORIGINAL = 0; public static final int FLAG_TYPE_REPLY = 1<<0; public static final int FLAG_TYPE_FORWARD = 1<<1; public static final int FLAG_TYPE_MASK = FLAG_TYPE_REPLY | FLAG_TYPE_FORWARD; + // The following flags indicate messages that are determined to be meeting related + // (e.g. invites) + public static final int FLAG_MEETING_INVITE = 1<<2; + public static final int FLAG_MEETING_CANCEL_NOTICE = 1<<3; + public static final int FLAG_MEETING_MASK = + FLAG_MEETING_INVITE | FLAG_MEETING_CANCEL_NOTICE; public Message() { mBaseUri = CONTENT_URI; diff --git a/src/com/android/email/service/EmailServiceProxy.java b/src/com/android/email/service/EmailServiceProxy.java index 1bc4f9c40..d5174fdac 100644 --- a/src/com/android/email/service/EmailServiceProxy.java +++ b/src/com/android/email/service/EmailServiceProxy.java @@ -287,6 +287,18 @@ public class EmailServiceProxy implements IEmailService { }); } + public void sendMeetingResponse(final long messageId, final int response) throws RemoteException { + setTask(new Runnable () { + public void run() { + try { + if (mCallback != null) mService.setCallback(mCallback); + mService.sendMeetingResponse(messageId, response); + } catch (RemoteException e) { + } + } + }); + } + public void loadMore(long messageId) throws RemoteException { // TODO Auto-generated method stub } diff --git a/src/com/android/exchange/AbstractSyncService.java b/src/com/android/exchange/AbstractSyncService.java index 425424dad..08583ecf8 100644 --- a/src/com/android/exchange/AbstractSyncService.java +++ b/src/com/android/exchange/AbstractSyncService.java @@ -76,8 +76,8 @@ public abstract class AbstractSyncService implements Runnable { protected Object mSynchronizer = new Object(); protected volatile long mRequestTime = 0; - protected ArrayList mPartRequests = new ArrayList(); - protected PartRequest mPendingPartRequest = null; + protected ArrayList mRequests = new ArrayList(); + protected PartRequest mPendingRequest = null; /** * Sent by SyncManager to request that the service stop itself cleanly @@ -282,54 +282,23 @@ public abstract class AbstractSyncService implements Runnable { } /** - * PartRequest handling (common functionality) - * Can be overridden if desired, but IMAP/EAS both use the next three methods as-is + * Request handling (common functionality) + * Can be overridden if desired */ - public void addPartRequest(PartRequest req) { - synchronized (mPartRequests) { - mPartRequests.add(req); + public void addRequest(Request req) { + synchronized (mRequests) { + mRequests.add(req); mRequestTime = System.currentTimeMillis(); } } - public void removePartRequest(PartRequest req) { - synchronized (mPartRequests) { - mPartRequests.remove(req); + public void removeRequest(Request req) { + synchronized (mRequests) { + mRequests.remove(req); } } - public PartRequest hasPartRequest(long emailId, String part) { - synchronized (mPartRequests) { - for (PartRequest pr : mPartRequests) { - if (pr.emailId == emailId && pr.loc.equals(part)) - return pr; - } - } - return null; - } - - // cancelPartRequest is sent in response to user input to stop an attachment load - // that is in progress. This will almost certainly require code overriding the base - // functionality, as sockets may need to be closed, etc. and this functionality will be - // service dependent. This returns the canceled PartRequest or null - public PartRequest cancelPartRequest(long emailId, String part) { - synchronized (mPartRequests) { - PartRequest p = null; - for (PartRequest pr : mPartRequests) { - if (pr.emailId == emailId && pr.loc.equals(part)) { - p = pr; - break; - } - } - if (p != null) { - mPartRequests.remove(p); - return p; - } - } - return null; - } - /** * Convenience method wrapping calls to retrieve columns from a single row, via EmailProvider. * The arguments are exactly the same as to contentResolver.query(). Results are returned in diff --git a/src/com/android/exchange/EasAuthenticationException.java b/src/com/android/exchange/EasAuthenticationException.java new file mode 100644 index 000000000..f5b14b9d6 --- /dev/null +++ b/src/com/android/exchange/EasAuthenticationException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.exchange; + +import java.io.IOException; + +/** + * Use this to be able to distinguish login (authentication) failures from other I/O + * exceptions during a sync, as they are handled very differently. + */ +public class EasAuthenticationException extends IOException { + private static final long serialVersionUID = 1L; + + EasAuthenticationException() { + super(); + } +} diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java index e8a35eb2f..8b9b424f7 100644 --- a/src/com/android/exchange/EasSyncService.java +++ b/src/com/android/exchange/EasSyncService.java @@ -34,6 +34,7 @@ import com.android.exchange.adapter.AccountSyncAdapter; import com.android.exchange.adapter.ContactsSyncAdapter; import com.android.exchange.adapter.EmailSyncAdapter; import com.android.exchange.adapter.FolderSyncParser; +import com.android.exchange.adapter.MeetingResponseParser; import com.android.exchange.adapter.PingParser; import com.android.exchange.adapter.Serializer; import com.android.exchange.adapter.Tags; @@ -628,7 +629,7 @@ public class EasSyncService extends AbstractSyncService { * @throws IOException */ protected void getAttachment(PartRequest req) throws IOException { - Attachment att = req.att; + Attachment att = req.mAttachment; Message msg = Message.restoreMessageWithId(mContext, att.mMessageKey); doProgressCallback(msg.mId, att.mId, 0); @@ -640,9 +641,9 @@ public class EasSyncService extends AbstractSyncService { HttpEntity e = res.getEntity(); int len = (int)e.getContentLength(); InputStream is = res.getEntity().getContent(); - File f = (req.destination != null) - ? new File(req.destination) - : createUniqueFileInternal(req.destination, att.mFileName); + File f = (req.mDestination != null) + ? new File(req.mDestination) + : createUniqueFileInternal(req.mDestination, att.mFileName); if (f != null) { // Ensure that the target directory exists File destDir = f.getParentFile(); @@ -654,7 +655,7 @@ public class EasSyncService extends AbstractSyncService { // len < 0 means "chunked" transfer-encoding if (len != 0) { try { - mPendingPartRequest = req; + mPendingRequest = req; byte[] bytes = new byte[CHUNK_SIZE]; int length = len; // Loop terminates 1) when EOF is reached or 2) if an IOException occurs @@ -689,7 +690,7 @@ public class EasSyncService extends AbstractSyncService { } } } finally { - mPendingPartRequest = null; + mPendingRequest = null; } } os.flush(); @@ -697,8 +698,8 @@ public class EasSyncService extends AbstractSyncService { // EmailProvider will throw an exception if we try to update an unsaved attachment if (att.isSaved()) { - String contentUriString = (req.contentUriString != null) - ? req.contentUriString + String contentUriString = (req.mContentUriString != null) + ? req.mContentUriString : "file://" + f.getAbsolutePath(); ContentValues cv = new ContentValues(); cv.put(AttachmentColumns.CONTENT_URI, contentUriString); @@ -711,6 +712,37 @@ public class EasSyncService extends AbstractSyncService { } } + /** + * Responds to a meeting request. The MeetingResponseRequest is basically our + * wrapper for the meetingResponse service call + * @param req the request (message id and response code) + * @throws IOException + */ + protected void sendMeetingResponse(MeetingResponseRequest req) throws IOException { + Message msg = Message.restoreMessageWithId(mContext, req.mMessageId); + Serializer s = new Serializer(); + s.start(Tags.MREQ_MEETING_RESPONSE).start(Tags.MREQ_REQUEST); + s.data(Tags.MREQ_USER_RESPONSE, Integer.toString(req.mResponse)); + s.data(Tags.MREQ_COLLECTION_ID, Long.toString(msg.mMailboxKey)); + s.data(Tags.MREQ_REQ_ID, msg.mServerId); + s.end().end().done(); + HttpResponse res = sendHttpClientPost("MeetingResponse", s.toByteArray()); + int status = res.getStatusLine().getStatusCode(); + if (status == HttpStatus.SC_OK) { + HttpEntity e = res.getEntity(); + int len = (int)e.getContentLength(); + InputStream is = res.getEntity().getContent(); + if (len != 0) { + new MeetingResponseParser(is, this).parse(); + } + } else if (isAuthError(status)) { + throw new EasAuthenticationException(); + } else { + userLog("Meeting response request failed, code: " + status); + throw new IOException(); + } + } + @SuppressWarnings("deprecation") private String makeUriString(String cmd, String extra) throws IOException { // Cache the authentication string and the command string @@ -1323,18 +1355,29 @@ public class EasSyncService extends AbstractSyncService { return; } + // Now, handle various requests while (true) { - PartRequest req = null; - synchronized (mPartRequests) { - if (mPartRequests.isEmpty()) { + Request req = null; + synchronized (mRequests) { + if (mRequests.isEmpty()) { break; } else { - req = mPartRequests.get(0); + req = mRequests.get(0); } } - getAttachment(req); - synchronized(mPartRequests) { - mPartRequests.remove(req); + + // Our two request types are PartRequest (loading attachment) and + // MeetingResponseRequest (respond to a meeting request) + if (req instanceof PartRequest) { + getAttachment((PartRequest)req); + } else if (req instanceof MeetingResponseRequest) { + sendMeetingResponse((MeetingResponseRequest)req); + } + + // If there's an exception handling the request, we'll throw it + // Otherwise, we remove the request + synchronized(mRequests) { + mRequests.remove(req); } } @@ -1465,6 +1508,9 @@ public class EasSyncService extends AbstractSyncService { sync(target); } while (mRequestTime != 0); } + } catch (EasAuthenticationException e) { + userLog("Caught authentication error"); + mExitStatus = EXIT_LOGIN_FAILURE; } catch (IOException e) { String message = e.getMessage(); userLog("Caught IOException: ", (message == null) ? "No message" : message); diff --git a/src/com/android/exchange/EmailServiceConstants.java b/src/com/android/exchange/EmailServiceConstants.java new file mode 100644 index 000000000..80d310faa --- /dev/null +++ b/src/com/android/exchange/EmailServiceConstants.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010 The Android Open Source Project. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.exchange; + +public class EmailServiceConstants { + public static final int MEETING_REQUEST_ACCEPTED = 1; + public static final int MEETING_REQUEST_TENTATIVE = 2; + public static final int MEETING_REQUEST_DECLINED = 3; +} diff --git a/src/com/android/exchange/IEmailService.aidl b/src/com/android/exchange/IEmailService.aidl index 81181eb90..a790645d2 100644 --- a/src/com/android/exchange/IEmailService.aidl +++ b/src/com/android/exchange/IEmailService.aidl @@ -43,4 +43,6 @@ interface IEmailService { void hostChanged(long accountId); Bundle autoDiscover(String userName, String password); + + void sendMeetingResponse(long messageId, int response); } \ No newline at end of file diff --git a/src/com/android/exchange/MeetingResponseRequest.java b/src/com/android/exchange/MeetingResponseRequest.java new file mode 100644 index 000000000..1d69a6cbe --- /dev/null +++ b/src/com/android/exchange/MeetingResponseRequest.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.exchange; + +/** + * MeetingResponseRequest is the EAS wrapper for responding to meeting requests. + */ +public class MeetingResponseRequest extends Request { + public int mResponse; + + MeetingResponseRequest(long messageId, int response) { + mMessageId = messageId; + mResponse = response; + } +} diff --git a/src/com/android/exchange/PartRequest.java b/src/com/android/exchange/PartRequest.java index 72b79f4e1..b6b281d06 100644 --- a/src/com/android/exchange/PartRequest.java +++ b/src/com/android/exchange/PartRequest.java @@ -24,24 +24,21 @@ import com.android.email.provider.EmailContent.Attachment; * the attachment to be loaded, it also contains the callback to be used for status/progress * updates to the UI. */ -public class PartRequest { - public long timeStamp; - public long emailId; - public Attachment att; - public String destination; - public String contentUriString; - public String loc; +public class PartRequest extends Request { + public Attachment mAttachment; + public String mDestination; + public String mContentUriString; + public String mLocation; public PartRequest(Attachment _att) { - timeStamp = System.currentTimeMillis(); - emailId = _att.mMessageKey; - att = _att; - loc = att.mLocation; + mMessageId = _att.mMessageKey; + mAttachment = _att; + mLocation = mAttachment.mLocation; } public PartRequest(Attachment _att, String _destination, String _contentUriString) { this(_att); - destination = _destination; - contentUriString = _contentUriString; + mDestination = _destination; + mContentUriString = _contentUriString; } } diff --git a/src/com/android/exchange/Request.java b/src/com/android/exchange/Request.java new file mode 100644 index 000000000..185dd7f60 --- /dev/null +++ b/src/com/android/exchange/Request.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.exchange; + +/** + * Requests for mailbox actions are handled by subclasses of this abstract class. + * Two subclasses are now defined: PartRequest (attachment load) and MeetingResponseRequest + * (respond to a meeting invitation) + */ +public abstract class Request { + public long mTimeStamp = System.currentTimeMillis(); + public long mMessageId; +} diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java index 2648b8cdc..648fc8006 100644 --- a/src/com/android/exchange/SyncManager.java +++ b/src/com/android/exchange/SyncManager.java @@ -312,7 +312,7 @@ public class SyncManager extends Service implements Runnable { public void loadAttachment(long attachmentId, String destinationFile, String contentUriString) throws RemoteException { Attachment att = Attachment.restoreAttachmentWithId(SyncManager.this, attachmentId); - partRequest(new PartRequest(att, destinationFile, contentUriString)); + sendMessageRequest(new PartRequest(att, destinationFile, contentUriString)); } public void updateFolderList(long accountId) throws RemoteException { @@ -348,8 +348,11 @@ public class SyncManager extends Service implements Runnable { Eas.setUserDebug(on); } + public void sendMeetingResponse(long messageId, int response) throws RemoteException { + sendMessageRequest(new MeetingResponseRequest(messageId, response)); + } + public void loadMore(long messageId) throws RemoteException { - // TODO Auto-generated method stub } // The following three methods are not implemented in this version @@ -1338,7 +1341,7 @@ public class SyncManager extends Service implements Runnable { } } - private void startService(Mailbox m, int reason, PartRequest req) { + private void startService(Mailbox m, int reason, Request req) { // Don't sync if there's no connectivity if (sConnectivityHold) return; synchronized (sSyncToken) { @@ -1351,7 +1354,7 @@ public class SyncManager extends Service implements Runnable { if (!((EasSyncService)service).mIsValid) return; service.mSyncReason = reason; if (req != null) { - service.addPartRequest(req); + service.addRequest(req); } startService(service, m); } @@ -1726,9 +1729,9 @@ public class SyncManager extends Service implements Runnable { } } - static public void partRequest(PartRequest req) { + static public void sendMessageRequest(Request req) { if (INSTANCE == null) return; - Message msg = Message.restoreMessageWithId(INSTANCE, req.emailId); + Message msg = Message.restoreMessageWithId(INSTANCE, req.mMessageId); if (msg == null) { return; } @@ -1739,33 +1742,7 @@ public class SyncManager extends Service implements Runnable { service = startManualSync(mailboxId, SYNC_SERVICE_PART_REQUEST, req); kick("part request"); } else { - service.addPartRequest(req); - } - } - - static public PartRequest hasPartRequest(long emailId, String part) { - if (INSTANCE == null) return null; - Message msg = Message.restoreMessageWithId(INSTANCE, emailId); - if (msg == null) { - return null; - } - long mailboxId = msg.mMailboxKey; - AbstractSyncService service = INSTANCE.mServiceMap.get(mailboxId); - if (service != null) { - return service.hasPartRequest(emailId, part); - } - return null; - } - - static public void cancelPartRequest(long emailId, String part) { - Message msg = Message.restoreMessageWithId(INSTANCE, emailId); - if (msg == null) { - return; - } - long mailboxId = msg.mMailboxKey; - AbstractSyncService service = INSTANCE.mServiceMap.get(mailboxId); - if (service != null) { - service.cancelPartRequest(emailId, part); + service.addRequest(req); } } @@ -1793,7 +1770,7 @@ public class SyncManager extends Service implements Runnable { return PING_STATUS_OK; } - static public AbstractSyncService startManualSync(long mailboxId, int reason, PartRequest req) { + static public AbstractSyncService startManualSync(long mailboxId, int reason, Request req) { if (INSTANCE == null || INSTANCE.mServiceMap == null) return null; synchronized (sSyncToken) { if (INSTANCE.mServiceMap.get(mailboxId) == null) { @@ -1866,7 +1843,7 @@ public class SyncManager extends Service implements Runnable { int exitStatus = svc.mExitStatus; switch (exitStatus) { case AbstractSyncService.EXIT_DONE: - if (!svc.mPartRequests.isEmpty()) { + if (!svc.mRequests.isEmpty()) { // TODO Handle this case } errorMap.remove(mailboxId); diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java index 94c5ef0ee..c835f856e 100644 --- a/src/com/android/exchange/adapter/EmailSyncAdapter.java +++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java @@ -166,6 +166,14 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { String text = getValue(); msg.mText = text; break; + case Tags.EMAIL_MESSAGE_CLASS: + String messageClass = getValue(); + if (messageClass.equals("IPM.Schedule.Meeting.Request")) { + msg.mFlags |= Message.FLAG_MEETING_INVITE; + } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) { + msg.mFlags |= Message.FLAG_MEETING_CANCEL_NOTICE; + } + break; default: skipTag(); } diff --git a/src/com/android/exchange/adapter/MeetingResponseParser.java b/src/com/android/exchange/adapter/MeetingResponseParser.java new file mode 100644 index 000000000..142c41919 --- /dev/null +++ b/src/com/android/exchange/adapter/MeetingResponseParser.java @@ -0,0 +1,65 @@ +/* Copyright (C) 2010 The Android Open Source Project. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.exchange.adapter; + +import com.android.exchange.EasSyncService; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Parse the result of a MeetingRequest command. + */ +public class MeetingResponseParser extends Parser { + private EasSyncService mService; + + public MeetingResponseParser(InputStream in, EasSyncService service) throws IOException { + super(in); + mService = service; + } + + public void parseResult() throws IOException { + while (nextTag(Tags.MREQ_RESULT) != END) { + if (tag == Tags.MREQ_STATUS) { + int status = getValueInt(); + if (status != 1) { + mService.userLog("Error in meeting response: " + status); + } + } else if (tag == Tags.MREQ_CAL_ID) { + mService.userLog("Meeting response calendar id: " + getValue()); + } else { + skipTag(); + } + } + } + + @Override + public boolean parse() throws IOException { + boolean res = false; + if (nextTag(START_DOCUMENT) != Tags.MREQ_MEETING_RESPONSE) { + throw new IOException(); + } + while (nextTag(START_DOCUMENT) != END_DOCUMENT) { + if (tag == Tags.MREQ_RESULT) { + parseResult(); + } else { + skipTag(); + } + } + return res; + } +} + diff --git a/src/com/android/exchange/adapter/Tags.java b/src/com/android/exchange/adapter/Tags.java index d8221813c..2c5c97154 100644 --- a/src/com/android/exchange/adapter/Tags.java +++ b/src/com/android/exchange/adapter/Tags.java @@ -39,6 +39,7 @@ public class Tags { public static final int MOVE = 0x05; public static final int GIE = 0x06; public static final int FOLDER = 0x07; + public static final int MREQ = 0x08; public static final int TASK = 0x09; public static final int CONTACTS2 = 0x0C; public static final int PING = 0x0D; @@ -218,6 +219,17 @@ public class Tags { public static final int FOLDER_COUNT = FOLDER_PAGE + 0x17; public static final int FOLDER_VERSION = FOLDER_PAGE + 0x18; + public static final int MREQ_PAGE = MREQ << PAGE_SHIFT; + public static final int MREQ_CAL_ID = MREQ_PAGE + 5; + public static final int MREQ_COLLECTION_ID = MREQ_PAGE + 6; + public static final int MREQ_MEETING_RESPONSE = MREQ_PAGE + 7; + public static final int MREQ_REQ_ID = MREQ_PAGE + 8; + public static final int MREQ_REQUEST = MREQ_PAGE + 9; + public static final int MREQ_RESULT = MREQ_PAGE + 0xA; + public static final int MREQ_STATUS = MREQ_PAGE + 0xB; + public static final int MREQ_USER_RESPONSE = MREQ_PAGE + 0xC; + public static final int MREQ_VERSION = MREQ_PAGE + 0xD; + public static final int EMAIL_PAGE = EMAIL << PAGE_SHIFT; public static final int EMAIL_ATTACHMENT = EMAIL_PAGE + 5; public static final int EMAIL_ATTACHMENTS = EMAIL_PAGE + 6; @@ -441,6 +453,8 @@ public class Tags { }, { // 0x08 MeetingResponse + "CalId", "CollectionId", "MeetingResponse", "ReqId", "Request", + "Result", "Status", "UserResponse", "Version" }, { // 0x09 Tasks